summaryrefslogtreecommitdiff
path: root/frontends
diff options
context:
space:
mode:
authorVincent Sanders <vince@kyllikki.org>2016-05-05 22:28:51 +0100
committerVincent Sanders <vince@kyllikki.org>2016-05-15 13:44:34 +0100
commitd21447d096a320a08b3efb2b8768fad0dcdcfd64 (patch)
tree1a83814b7c9e94b2f13c473261f23dd3a17dee64 /frontends
parent2cbb337756d9af5bda4d594964d446439f602551 (diff)
downloadnetsurf-d21447d096a320a08b3efb2b8768fad0dcdcfd64.tar.gz
netsurf-d21447d096a320a08b3efb2b8768fad0dcdcfd64.tar.bz2
move frontends into sub directory
Diffstat (limited to 'frontends')
-rw-r--r--frontends/Makefile9
-rw-r--r--frontends/amiga/Makefile122
-rw-r--r--frontends/amiga/Makefile.defaults42
-rw-r--r--frontends/amiga/agclass/amigaguide_class.c394
-rwxr-xr-xfrontends/amiga/agclass/amigaguide_class.h43
-rw-r--r--frontends/amiga/arexx.c636
-rwxr-xr-xfrontends/amiga/arexx.h32
-rw-r--r--frontends/amiga/bitmap.c706
-rwxr-xr-xfrontends/amiga/bitmap.h192
-rw-r--r--frontends/amiga/clipboard.c374
-rwxr-xr-xfrontends/amiga/clipboard.h42
-rwxr-xr-xfrontends/amiga/cookies.c39
-rwxr-xr-xfrontends/amiga/cookies.h28
-rw-r--r--frontends/amiga/ctxmenu.c607
-rw-r--r--frontends/amiga/ctxmenu.h92
-rw-r--r--frontends/amiga/datatypes.c40
-rw-r--r--frontends/amiga/datatypes.h41
-rwxr-xr-xfrontends/amiga/dist/Install397
-rw-r--r--frontends/amiga/dist/Install.infobin0 -> 8892 bytes
-rwxr-xr-xfrontends/amiga/dist/NetSurf.guide411
-rw-r--r--frontends/amiga/dist/NetSurf.guide.infobin0 -> 6741 bytes
-rw-r--r--frontends/amiga/dist/Rexx.infobin0 -> 6206 bytes
-rw-r--r--frontends/amiga/dist/Rexx/CloseTabs.nsrx47
-rw-r--r--frontends/amiga/dist/Rexx/SMTube.nsrx29
-rw-r--r--frontends/amiga/dist/Rexx/ShowTitles.nsrx18
-rwxr-xr-xfrontends/amiga/dist/Rexx/viewsource.nsrx24
-rw-r--r--frontends/amiga/download.c451
-rwxr-xr-xfrontends/amiga/download.h44
-rw-r--r--frontends/amiga/drag.c352
-rw-r--r--frontends/amiga/drag.h45
-rw-r--r--frontends/amiga/dt_anim.c365
-rw-r--r--frontends/amiga/dt_picture.c294
-rw-r--r--frontends/amiga/dt_sound.c278
-rw-r--r--frontends/amiga/file.c293
-rw-r--r--frontends/amiga/file.h45
-rw-r--r--frontends/amiga/filetype.c647
-rw-r--r--frontends/amiga/filetype.h51
-rw-r--r--frontends/amiga/font.c152
-rwxr-xr-xfrontends/amiga/font.h63
-rw-r--r--frontends/amiga/font_bullet.c895
-rw-r--r--frontends/amiga/font_bullet.h32
-rw-r--r--frontends/amiga/font_cache.c206
-rw-r--r--frontends/amiga/font_cache.h54
-rw-r--r--frontends/amiga/font_diskfont.c292
-rw-r--r--frontends/amiga/font_diskfont.h23
-rw-r--r--frontends/amiga/font_scan.c522
-rwxr-xr-xfrontends/amiga/font_scan.h39
-rw-r--r--frontends/amiga/gui.c5740
-rw-r--r--frontends/amiga/gui.h246
-rwxr-xr-xfrontends/amiga/gui_options.c2316
-rwxr-xr-xfrontends/amiga/gui_options.h32
-rw-r--r--frontends/amiga/hash/xxhash.c962
-rw-r--r--frontends/amiga/hash/xxhash.h192
-rwxr-xr-xfrontends/amiga/help.c85
-rwxr-xr-xfrontends/amiga/help.h38
-rwxr-xr-xfrontends/amiga/history.c41
-rwxr-xr-xfrontends/amiga/history.h29
-rwxr-xr-xfrontends/amiga/history_local.c341
-rwxr-xr-xfrontends/amiga/history_local.h45
-rwxr-xr-xfrontends/amiga/hotlist.c119
-rwxr-xr-xfrontends/amiga/hotlist.h34
-rw-r--r--frontends/amiga/icon.c545
-rw-r--r--frontends/amiga/icon.h47
-rwxr-xr-xfrontends/amiga/iff_cset.h33
-rw-r--r--frontends/amiga/iff_dr2d.c422
-rw-r--r--frontends/amiga/iff_dr2d.h107
-rwxr-xr-xfrontends/amiga/launch.c179
-rwxr-xr-xfrontends/amiga/launch.h33
-rw-r--r--frontends/amiga/libs.c326
-rw-r--r--frontends/amiga/libs.h82
-rwxr-xr-xfrontends/amiga/login.c238
-rwxr-xr-xfrontends/amiga/login.h31
-rw-r--r--frontends/amiga/menu.c1146
-rwxr-xr-xfrontends/amiga/menu.h150
-rwxr-xr-xfrontends/amiga/misc.c506
-rw-r--r--frontends/amiga/misc.h52
-rwxr-xr-xfrontends/amiga/object.c136
-rwxr-xr-xfrontends/amiga/object.h65
-rw-r--r--frontends/amiga/options.h95
-rw-r--r--frontends/amiga/os3support.c431
-rw-r--r--frontends/amiga/os3support.h254
-rwxr-xr-xfrontends/amiga/pkg/AutoInstall3
-rw-r--r--frontends/amiga/pkg/drawer.infobin0 -> 17208 bytes
-rwxr-xr-xfrontends/amiga/pkg/makepackage34
-rwxr-xr-xfrontends/amiga/pkg/makereslinks28
-rwxr-xr-xfrontends/amiga/pkg/netsurf.readme25
-rw-r--r--frontends/amiga/pkg/netsurf.readme.infobin0 -> 6238 bytes
-rw-r--r--frontends/amiga/pkg/netsurf_os3.readme28
-rw-r--r--frontends/amiga/pkg/netsurf_os3.readme.infobin0 -> 6238 bytes
-rw-r--r--frontends/amiga/plotters.c920
-rw-r--r--frontends/amiga/plotters.h55
-rw-r--r--frontends/amiga/plugin_hack.c274
-rw-r--r--frontends/amiga/plugin_hack.h28
-rw-r--r--frontends/amiga/print.c589
-rwxr-xr-xfrontends/amiga/print.h40
l---------frontends/amiga/resources/AdBlock.css1
-rw-r--r--frontends/amiga/resources/LangNames74
-rwxr-xr-xfrontends/amiga/resources/Pointers/Blank33
-rw-r--r--frontends/amiga/resources/Pointers/Blank.infobin0 -> 1719 bytes
-rwxr-xr-xfrontends/amiga/resources/Pointers/Caret33
-rw-r--r--frontends/amiga/resources/Pointers/Caret.infobin0 -> 1605 bytes
-rw-r--r--frontends/amiga/resources/Pointers/Cross.infobin0 -> 2046 bytes
-rwxr-xr-xfrontends/amiga/resources/Pointers/Default33
-rw-r--r--frontends/amiga/resources/Pointers/Default.infobin0 -> 2045 bytes
-rw-r--r--frontends/amiga/resources/Pointers/Down.infobin0 -> 1719 bytes
-rw-r--r--frontends/amiga/resources/Pointers/Drag.infobin0 -> 2302 bytes
-rw-r--r--frontends/amiga/resources/Pointers/Help.infobin0 -> 2775 bytes
-rw-r--r--frontends/amiga/resources/Pointers/Left.infobin0 -> 1587 bytes
-rw-r--r--frontends/amiga/resources/Pointers/LeftDown.infobin0 -> 1765 bytes
-rw-r--r--frontends/amiga/resources/Pointers/LeftUp.infobin0 -> 1695 bytes
-rwxr-xr-xfrontends/amiga/resources/Pointers/Menu33
-rw-r--r--frontends/amiga/resources/Pointers/Menu.infobin0 -> 2577 bytes
-rwxr-xr-xfrontends/amiga/resources/Pointers/Move33
-rw-r--r--frontends/amiga/resources/Pointers/Move.infobin0 -> 2840 bytes
-rw-r--r--frontends/amiga/resources/Pointers/NoDrop.infobin0 -> 2801 bytes
-rw-r--r--frontends/amiga/resources/Pointers/NotAllowed.infobin0 -> 2533 bytes
-rwxr-xr-xfrontends/amiga/resources/Pointers/Point33
-rw-r--r--frontends/amiga/resources/Pointers/Point.infobin0 -> 2755 bytes
-rw-r--r--frontends/amiga/resources/Pointers/Progress.infobin0 -> 2851 bytes
-rw-r--r--frontends/amiga/resources/Pointers/Right.infobin0 -> 1609 bytes
-rw-r--r--frontends/amiga/resources/Pointers/RightDown.infobin0 -> 1669 bytes
-rw-r--r--frontends/amiga/resources/Pointers/RightUp.infobin0 -> 1687 bytes
-rw-r--r--frontends/amiga/resources/Pointers/Up.infobin0 -> 1617 bytes
-rwxr-xr-xfrontends/amiga/resources/Pointers/Wait33
-rw-r--r--frontends/amiga/resources/Pointers/Wait.infobin0 -> 2449 bytes
-rw-r--r--frontends/amiga/resources/Resource.map4
-rw-r--r--frontends/amiga/resources/SearchEngines22
-rw-r--r--frontends/amiga/resources/Themes/AISS/NetSurf.infobin0 -> 23778 bytes
-rw-r--r--frontends/amiga/resources/Themes/AISS/Resource.map8
-rwxr-xr-xfrontends/amiga/resources/Themes/AISS/Theme75
-rw-r--r--frontends/amiga/resources/Themes/AISS/Throbberbin0 -> 5443 bytes
-rw-r--r--frontends/amiga/resources/Themes/Default/NetSurf.infobin0 -> 14818 bytes
-rwxr-xr-xfrontends/amiga/resources/Themes/Default/Theme88
-rw-r--r--frontends/amiga/resources/Themes/Default/Throbberbin0 -> 6479 bytes
-rwxr-xr-xfrontends/amiga/resources/Themes/Default/back.pngbin0 -> 653 bytes
-rw-r--r--frontends/amiga/resources/Themes/Default/back_g.pngbin0 -> 306 bytes
-rw-r--r--frontends/amiga/resources/Themes/Default/back_h.pngbin0 -> 607 bytes
-rw-r--r--frontends/amiga/resources/Themes/Default/closetab.pngbin0 -> 266 bytes
-rw-r--r--frontends/amiga/resources/Themes/Default/closetab_g.pngbin0 -> 169 bytes
-rwxr-xr-xfrontends/amiga/resources/Themes/Default/forward.pngbin0 -> 697 bytes
-rw-r--r--frontends/amiga/resources/Themes/Default/forward_g.pngbin0 -> 538 bytes
-rw-r--r--frontends/amiga/resources/Themes/Default/forward_h.pngbin0 -> 635 bytes
-rwxr-xr-xfrontends/amiga/resources/Themes/Default/home.pngbin0 -> 745 bytes
-rw-r--r--frontends/amiga/resources/Themes/Default/home_g.pngbin0 -> 576 bytes
-rw-r--r--frontends/amiga/resources/Themes/Default/home_h.pngbin0 -> 751 bytes
-rwxr-xr-xfrontends/amiga/resources/Themes/Default/reload.pngbin0 -> 1062 bytes
-rw-r--r--frontends/amiga/resources/Themes/Default/reload_g.pngbin0 -> 814 bytes
-rw-r--r--frontends/amiga/resources/Themes/Default/reload_h.pngbin0 -> 1046 bytes
-rw-r--r--frontends/amiga/resources/Themes/Default/search.pngbin0 -> 662 bytes
-rwxr-xr-xfrontends/amiga/resources/Themes/Default/stop.pngbin0 -> 1135 bytes
-rw-r--r--frontends/amiga/resources/Themes/Default/stop_g.pngbin0 -> 850 bytes
-rw-r--r--frontends/amiga/resources/Themes/Default/stop_h.pngbin0 -> 1119 bytes
-rw-r--r--frontends/amiga/resources/blankspace.pngbin0 -> 85 bytes
l---------frontends/amiga/resources/ca-bundle1
l---------frontends/amiga/resources/de1
-rw-r--r--frontends/amiga/resources/default.css7
-rw-r--r--frontends/amiga/resources/default.css.infobin0 -> 5082 bytes
l---------frontends/amiga/resources/en1
-rw-r--r--frontends/amiga/resources/favicon.pngbin0 -> 685 bytes
l---------frontends/amiga/resources/fr1
l---------frontends/amiga/resources/it1
-rw-r--r--frontends/amiga/resources/mimetypes112
l---------frontends/amiga/resources/nl1
l---------frontends/amiga/resources/nsdefault.css1
l---------frontends/amiga/resources/quirks.css1
-rw-r--r--frontends/amiga/resources/splash.pngbin0 -> 29458 bytes
-rw-r--r--frontends/amiga/rtg.c72
-rw-r--r--frontends/amiga/rtg.h37
-rw-r--r--frontends/amiga/save_pdf.c59
-rw-r--r--frontends/amiga/save_pdf.h31
-rwxr-xr-xfrontends/amiga/schedule.c353
-rwxr-xr-xfrontends/amiga/schedule.h57
-rwxr-xr-xfrontends/amiga/search.c339
-rwxr-xr-xfrontends/amiga/search.h37
-rw-r--r--frontends/amiga/selectmenu.c200
-rwxr-xr-xfrontends/amiga/selectmenu.h39
-rw-r--r--frontends/amiga/sslcert.c50
-rw-r--r--frontends/amiga/sslcert.h28
-rwxr-xr-xfrontends/amiga/stringview/stringview.c870
-rwxr-xr-xfrontends/amiga/stringview/stringview.h60
-rw-r--r--frontends/amiga/stringview/urlhistory.c124
-rw-r--r--frontends/amiga/stringview/urlhistory.h33
-rw-r--r--frontends/amiga/theme.c513
-rw-r--r--frontends/amiga/theme.h50
-rw-r--r--frontends/amiga/tree.c1473
-rwxr-xr-xfrontends/amiga/tree.h48
-rwxr-xr-xfrontends/amiga/utf8.c104
-rwxr-xr-xfrontends/amiga/utf8.h31
-rw-r--r--frontends/amiga/version.c39
-rw-r--r--frontends/atari/Makefile208
-rw-r--r--frontends/atari/Makefile.defaults58
-rw-r--r--frontends/atari/about.c206
-rw-r--r--frontends/atari/about.h24
-rw-r--r--frontends/atari/bitmap.c455
-rw-r--r--frontends/atari/bitmap.h137
-rw-r--r--frontends/atari/certview.c296
-rw-r--r--frontends/atari/certview.h51
-rw-r--r--frontends/atari/clipboard.c88
-rw-r--r--frontends/atari/clipboard.h25
-rw-r--r--frontends/atari/cookies.c220
-rw-r--r--frontends/atari/cookies.h39
-rw-r--r--frontends/atari/ctxmenu.c321
-rw-r--r--frontends/atari/ctxmenu.h24
-rw-r--r--frontends/atari/deskmenu.c828
-rw-r--r--frontends/atari/deskmenu.h11
-rwxr-xr-xfrontends/atari/doc/DejaVu.txt99
-rwxr-xr-xfrontends/atari/doc/bugs4
-rwxr-xr-xfrontends/atari/doc/changes.txt53
-rwxr-xr-xfrontends/atari/doc/faq.txt74
-rwxr-xr-xfrontends/atari/doc/readme.txt125
-rwxr-xr-xfrontends/atari/doc/todo.txt18
-rw-r--r--frontends/atari/download.c453
-rw-r--r--frontends/atari/download.h63
-rw-r--r--frontends/atari/encoding.c81
-rw-r--r--frontends/atari/encoding.h35
-rwxr-xr-xfrontends/atari/extract.php10
-rw-r--r--frontends/atari/file.c294
-rw-r--r--frontends/atari/file.h27
-rw-r--r--frontends/atari/filetype.c91
-rw-r--r--frontends/atari/filetype.h24
-rw-r--r--frontends/atari/findfile.c144
-rw-r--r--frontends/atari/findfile.h26
-rw-r--r--frontends/atari/font.c156
-rw-r--r--frontends/atari/font.h25
-rw-r--r--frontends/atari/gemtk/aestabs.c191
-rw-r--r--frontends/atari/gemtk/aestabs.h56
-rwxr-xr-xfrontends/atari/gemtk/dragdrop.c515
-rwxr-xr-xfrontends/atari/gemtk/dragdrop.h4
-rw-r--r--frontends/atari/gemtk/gemtk.h298
-rw-r--r--frontends/atari/gemtk/guiwin.c1430
-rw-r--r--frontends/atari/gemtk/guiwin.h4
-rw-r--r--frontends/atari/gemtk/msgbox.c110
-rw-r--r--frontends/atari/gemtk/msgbox.h5
-rw-r--r--frontends/atari/gemtk/objc.c522
-rw-r--r--frontends/atari/gemtk/objc.h7
-rw-r--r--frontends/atari/gemtk/redrawslots.c123
-rw-r--r--frontends/atari/gemtk/redrawslots.h25
-rw-r--r--frontends/atari/gemtk/utils.c144
-rw-r--r--frontends/atari/gemtk/utils.h5
-rw-r--r--frontends/atari/gemtk/vaproto.c170
-rw-r--r--frontends/atari/gemtk/vaproto.h121
-rw-r--r--frontends/atari/gui.c1217
-rw-r--r--frontends/atari/gui.h183
-rw-r--r--frontends/atari/history.c191
-rw-r--r--frontends/atari/history.h39
-rw-r--r--frontends/atari/hotlist.c315
-rw-r--r--frontends/atari/hotlist.h52
-rw-r--r--frontends/atari/login.c71
-rw-r--r--frontends/atari/login.h26
-rw-r--r--frontends/atari/misc.c408
-rw-r--r--frontends/atari/misc.h106
-rw-r--r--frontends/atari/options.h48
-rw-r--r--frontends/atari/osspec.c132
-rw-r--r--frontends/atari/osspec.h45
-rw-r--r--frontends/atari/plot/eddi.h54
-rw-r--r--frontends/atari/plot/eddi.s42
-rw-r--r--frontends/atari/plot/font_freetype.c714
-rw-r--r--frontends/atari/plot/font_freetype.h52
-rw-r--r--frontends/atari/plot/font_internal.c2386
-rw-r--r--frontends/atari/plot/font_internal.h37
-rw-r--r--frontends/atari/plot/font_vdi.c310
-rw-r--r--frontends/atari/plot/font_vdi.h26
-rw-r--r--frontends/atari/plot/fontplot.c132
-rw-r--r--frontends/atari/plot/fontplot.h86
-rw-r--r--frontends/atari/plot/plot.c2247
-rw-r--r--frontends/atari/plot/plot.h158
-rw-r--r--frontends/atari/redrawslots.c125
-rw-r--r--frontends/atari/redrawslots.h54
-rw-r--r--frontends/atari/res/blank39
-rw-r--r--frontends/atari/res/favicon.icobin0 -> 9862 bytes
-rw-r--r--frontends/atari/res/icons/toolbar/atfact/main.pngbin0 -> 12555 bytes
-rw-r--r--frontends/atari/res/icons/toolbar/atfact/throbber.pngbin0 -> 6135 bytes
-rw-r--r--frontends/atari/res/icons/toolbar/default/main.pngbin0 -> 46417 bytes
-rw-r--r--frontends/atari/res/icons/toolbar/default/main.xcfbin0 -> 16404 bytes
-rw-r--r--frontends/atari/res/icons/toolbar/default/throbber.pngbin0 -> 20960 bytes
-rw-r--r--frontends/atari/res/icons/toolbar/default/throbber.xcfbin0 -> 10231 bytes
-rw-r--r--frontends/atari/res/languages261
-rwxr-xr-xfrontends/atari/res/netsurf.rscbin0 -> 38896 bytes
-rwxr-xr-xfrontends/atari/res/netsurf.rsh241
-rwxr-xr-xfrontends/atari/res/netsurf.rsm225
-rw-r--r--frontends/atari/rootwin.c1540
-rw-r--r--frontends/atari/rootwin.h97
-rw-r--r--frontends/atari/save.h22
-rw-r--r--frontends/atari/schedule.c228
-rw-r--r--frontends/atari/schedule.h48
-rwxr-xr-xfrontends/atari/scripts/env-v4e.sh7
-rwxr-xr-xfrontends/atari/scripts/env-x86.sh9
-rw-r--r--frontends/atari/search.c303
-rw-r--r--frontends/atari/search.h53
-rw-r--r--frontends/atari/settings.c990
-rw-r--r--frontends/atari/settings.h26
-rw-r--r--frontends/atari/statusbar.c232
-rw-r--r--frontends/atari/statusbar.h40
-rw-r--r--frontends/atari/toolbar.c1041
-rw-r--r--frontends/atari/toolbar.h96
-rw-r--r--frontends/atari/treeview.c794
-rw-r--r--frontends/atari/treeview.h124
-rw-r--r--frontends/atari/verify_ssl.c269
-rw-r--r--frontends/atari/verify_ssl.h24
-rw-r--r--frontends/beos/Makefile102
-rw-r--r--frontends/beos/Makefile.defaults30
-rw-r--r--frontends/beos/WindowStack.h45
-rw-r--r--frontends/beos/about.cpp57
-rw-r--r--frontends/beos/about.h24
-rw-r--r--frontends/beos/beos_res.rsrcbin0 -> 11584 bytes
-rw-r--r--frontends/beos/bitmap.cpp556
-rw-r--r--frontends/beos/bitmap.h33
-rw-r--r--frontends/beos/cookies.cpp417
-rw-r--r--frontends/beos/cookies.h24
-rw-r--r--frontends/beos/download.cpp264
-rw-r--r--frontends/beos/download.h19
-rw-r--r--frontends/beos/fetch_rsrc.cpp396
-rw-r--r--frontends/beos/fetch_rsrc.h35
-rw-r--r--frontends/beos/filetype.cpp138
-rw-r--r--frontends/beos/filetype.h21
-rw-r--r--frontends/beos/font.cpp374
-rw-r--r--frontends/beos/font.h37
-rw-r--r--frontends/beos/gui.cpp1109
-rw-r--r--frontends/beos/gui.h88
-rw-r--r--frontends/beos/gui_options.cpp50
-rw-r--r--frontends/beos/gui_options.h31
-rw-r--r--frontends/beos/login.cpp196
-rw-r--r--frontends/beos/options.h30
-rw-r--r--frontends/beos/plotters.cpp659
-rw-r--r--frontends/beos/plotters.h48
-rw-r--r--frontends/beos/res.h18
-rw-r--r--frontends/beos/res.rdef242
l---------frontends/beos/res/SearchEngines1
l---------frontends/beos/res/adblock.css1
-rw-r--r--frontends/beos/res/beosdefault.css22
l---------frontends/beos/res/ca-bundle.txt1
l---------frontends/beos/res/credits.html1
l---------frontends/beos/res/de/welcome.html1
l---------frontends/beos/res/default.css1
l---------frontends/beos/res/en/credits.html1
l---------frontends/beos/res/en/licence.html1
l---------frontends/beos/res/en/maps.html1
l---------frontends/beos/res/en/welcome.html1
l---------frontends/beos/res/favicon.png1
l---------frontends/beos/res/icons1
l---------frontends/beos/res/internal.css1
l---------frontends/beos/res/it/credits.html1
l---------frontends/beos/res/it/licence.html1
l---------frontends/beos/res/it/welcome.html1
l---------frontends/beos/res/ja/welcome.html1
l---------frontends/beos/res/licence.html1
-rw-r--r--frontends/beos/res/license332
l---------frontends/beos/res/maps.html1
l---------frontends/beos/res/netsurf.png1
l---------frontends/beos/res/quirks.css1
-rw-r--r--frontends/beos/res/throbber/throbber0.pngbin0 -> 521 bytes
-rw-r--r--frontends/beos/res/throbber/throbber1.pngbin0 -> 820 bytes
-rw-r--r--frontends/beos/res/throbber/throbber2.pngbin0 -> 812 bytes
-rw-r--r--frontends/beos/res/throbber/throbber3.pngbin0 -> 826 bytes
-rw-r--r--frontends/beos/res/throbber/throbber4.pngbin0 -> 818 bytes
-rw-r--r--frontends/beos/res/throbber/throbber5.pngbin0 -> 815 bytes
-rw-r--r--frontends/beos/res/throbber/throbber6.pngbin0 -> 839 bytes
-rw-r--r--frontends/beos/res/throbber/throbber7.pngbin0 -> 811 bytes
-rw-r--r--frontends/beos/res/throbber/throbber8.pngbin0 -> 833 bytes
l---------frontends/beos/res/welcome.html1
-rw-r--r--frontends/beos/scaffolding.cpp2332
-rw-r--r--frontends/beos/scaffolding.h210
-rw-r--r--frontends/beos/schedule.cpp142
-rw-r--r--frontends/beos/schedule.h29
-rw-r--r--frontends/beos/search.cpp76
-rw-r--r--frontends/beos/throbber.cpp118
-rw-r--r--frontends/beos/throbber.h36
-rw-r--r--frontends/beos/window.cpp1382
-rw-r--r--frontends/beos/window.h83
-rw-r--r--frontends/cocoa/ArrowBox.h34
-rw-r--r--frontends/cocoa/ArrowBox.m163
-rw-r--r--frontends/cocoa/ArrowWindow.h32
-rw-r--r--frontends/cocoa/ArrowWindow.m239
-rw-r--r--frontends/cocoa/BlackScroller.h20
-rw-r--r--frontends/cocoa/BlackScroller.m154
-rw-r--r--frontends/cocoa/BookmarksController.h41
-rw-r--r--frontends/cocoa/BookmarksController.m223
-rw-r--r--frontends/cocoa/BrowserView.h53
-rw-r--r--frontends/cocoa/BrowserView.m757
-rw-r--r--frontends/cocoa/BrowserViewController.h75
-rw-r--r--frontends/cocoa/BrowserViewController.m378
-rw-r--r--frontends/cocoa/BrowserWindow.h26
-rw-r--r--frontends/cocoa/BrowserWindow.m29
-rw-r--r--frontends/cocoa/BrowserWindowController.h57
-rw-r--r--frontends/cocoa/BrowserWindowController.m266
-rw-r--r--frontends/cocoa/DownloadWindowController.h53
-rw-r--r--frontends/cocoa/DownloadWindowController.m415
-rw-r--r--frontends/cocoa/FormSelectMenu.h32
-rw-r--r--frontends/cocoa/FormSelectMenu.m113
-rw-r--r--frontends/cocoa/HistoryView.h35
-rw-r--r--frontends/cocoa/HistoryView.m151
-rw-r--r--frontends/cocoa/HistoryWindowController.h31
-rw-r--r--frontends/cocoa/HistoryWindowController.m52
-rw-r--r--frontends/cocoa/LocalHistoryController.h40
-rw-r--r--frontends/cocoa/LocalHistoryController.m119
-rw-r--r--frontends/cocoa/Makefile249
-rw-r--r--frontends/cocoa/Makefile.defaults30
-rw-r--r--frontends/cocoa/NetSurf.xcodeproj/project.pbxproj1023
-rw-r--r--frontends/cocoa/NetSurfAppDelegate.h42
-rw-r--r--frontends/cocoa/NetSurfAppDelegate.m200
-rw-r--r--frontends/cocoa/NetsurfApp.h31
-rw-r--r--frontends/cocoa/NetsurfApp.m302
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front.pngbin0 -> 292 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front_Pressed.pngbin0 -> 292 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front_Rollover.pngbin0 -> 297 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front.pngbin0 -> 307 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front_Pressed.pngbin0 -> 310 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front_Rollover.pngbin0 -> 317 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/AquaTabNew.pngbin0 -> 371 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/AquaTabNewPressed.pngbin0 -> 380 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/AquaTabNewRollover.pngbin0 -> 380 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/overflowImage.pngbin0 -> 256 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/overflowImagePressed.pngbin0 -> 250 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/pi.pngbin0 -> 564 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/NSBezierPath_AMShading.h23
-rw-r--r--frontends/cocoa/PSMTabBarControl/NSBezierPath_AMShading.m119
-rw-r--r--frontends/cocoa/PSMTabBarControl/NSString_AITruncation.h12
-rw-r--r--frontends/cocoa/PSMTabBarControl/NSString_AITruncation.m32
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMOverflowPopUpButton.h28
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMOverflowPopUpButton.m152
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMProgressIndicator.h15
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMProgressIndicator.m40
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMRolloverButton.h28
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMRolloverButton.m170
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabBarCell.h116
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabBarCell.m489
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabBarControl.h241
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabBarControl.m1995
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabBarController.h38
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabBarController.m643
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabDragAssistant.h101
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabDragAssistant.m834
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabDragView.h21
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabDragView.m62
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabDragWindow.h20
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabDragWindow.m48
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabDragWindowController.h33
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabDragWindowController.m111
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabStyle.h57
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMUnifiedTabStyle.h29
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMUnifiedTabStyle.m573
-rw-r--r--frontends/cocoa/PSMTabBarControl/ReadMe.rtfd/TXT.rtf186
-rw-r--r--frontends/cocoa/PSMTabBarControl/ReadMe.rtfd/startpage.gifbin0 -> 11246 bytes
-rw-r--r--frontends/cocoa/PreferencesWindowController.h29
-rw-r--r--frontends/cocoa/PreferencesWindowController.m57
-rw-r--r--frontends/cocoa/Prefix.pch11
-rw-r--r--frontends/cocoa/ScrollableView.h31
-rw-r--r--frontends/cocoa/ScrollableView.m71
-rw-r--r--frontends/cocoa/SearchWindowController.h54
-rw-r--r--frontends/cocoa/SearchWindowController.m119
-rw-r--r--frontends/cocoa/Tree.h56
-rw-r--r--frontends/cocoa/Tree.m147
-rw-r--r--frontends/cocoa/TreeView.h34
-rw-r--r--frontends/cocoa/TreeView.m249
-rw-r--r--frontends/cocoa/URLFieldCell.h31
-rw-r--r--frontends/cocoa/URLFieldCell.m208
-rw-r--r--frontends/cocoa/apple_image.h39
-rw-r--r--frontends/cocoa/apple_image.m255
-rw-r--r--frontends/cocoa/bitmap.h28
-rw-r--r--frontends/cocoa/bitmap.m282
-rwxr-xr-xfrontends/cocoa/compile-xib.sh20
-rw-r--r--frontends/cocoa/coordinates.h108
-rwxr-xr-xfrontends/cocoa/extract-strings.sh11
-rw-r--r--frontends/cocoa/fetch.h19
-rw-r--r--frontends/cocoa/fetch.m113
-rw-r--r--frontends/cocoa/font.h28
-rw-r--r--frontends/cocoa/font.m243
-rw-r--r--frontends/cocoa/gui.h35
-rw-r--r--frontends/cocoa/gui.m335
-rw-r--r--frontends/cocoa/plotter.h34
-rw-r--r--frontends/cocoa/plotter.m335
-rw-r--r--frontends/cocoa/res/BookmarksWindow.xib610
-rw-r--r--frontends/cocoa/res/Browser.xib399
-rw-r--r--frontends/cocoa/res/BrowserWindow.xib1395
-rw-r--r--frontends/cocoa/res/DownloadWindow.xib493
-rw-r--r--frontends/cocoa/res/HistoryWindow.xib338
-rw-r--r--frontends/cocoa/res/HomeTemplate.pdf106
l---------frontends/cocoa/res/Icons1
-rw-r--r--frontends/cocoa/res/LocalHistoryPanel.xib357
-rw-r--r--frontends/cocoa/res/MainMenu.xib2369
-rw-r--r--frontends/cocoa/res/NetSurf-Info.plist111
-rw-r--r--frontends/cocoa/res/NetSurf.icnsbin0 -> 203268 bytes
-rw-r--r--frontends/cocoa/res/PreferencesWindow.xib512
-rw-r--r--frontends/cocoa/res/SearchWindow.xib614
l---------frontends/cocoa/res/adblock.css1
l---------frontends/cocoa/res/ca-bundle1
-rw-r--r--frontends/cocoa/res/de.lproj/BookmarksWindow.xib.stringsbin0 -> 366 bytes
-rw-r--r--frontends/cocoa/res/de.lproj/BrowserWindow.xib.stringsbin0 -> 1892 bytes
-rw-r--r--frontends/cocoa/res/de.lproj/DownloadWindow.xib.stringsbin0 -> 536 bytes
-rw-r--r--frontends/cocoa/res/de.lproj/HistoryWindow.xib.stringsbin0 -> 172 bytes
-rw-r--r--frontends/cocoa/res/de.lproj/Localizable.strings78
-rw-r--r--frontends/cocoa/res/de.lproj/MainMenu.xib.stringsbin0 -> 11982 bytes
l---------frontends/cocoa/res/de.lproj/Messages1
-rw-r--r--frontends/cocoa/res/de.lproj/PreferencesWindow.xib.stringsbin0 -> 1206 bytes
-rw-r--r--frontends/cocoa/res/de.lproj/SearchWindow.xib.stringsbin0 -> 1148 bytes
l---------frontends/cocoa/res/default.css1
-rw-r--r--frontends/cocoa/res/en.lproj/Localizable.stringsbin0 -> 3322 bytes
l---------frontends/cocoa/res/en.lproj/Messages1
-rw-r--r--frontends/cocoa/res/fr.lproj/Localizable.stringsbin0 -> 3322 bytes
l---------frontends/cocoa/res/fr.lproj/Messages1
l---------frontends/cocoa/res/internal.css1
-rw-r--r--frontends/cocoa/res/it.lproj/Localizable.stringsbin0 -> 3496 bytes
l---------frontends/cocoa/res/it.lproj/Messages1
l---------frontends/cocoa/res/netsurf.png1
-rw-r--r--frontends/cocoa/res/nl.lproj/Localizable.stringsbin0 -> 3322 bytes
l---------frontends/cocoa/res/nl.lproj/Messages1
l---------frontends/cocoa/res/quirks.css1
-rw-r--r--frontends/cocoa/schedule.h19
-rw-r--r--frontends/cocoa/schedule.m90
-rw-r--r--frontends/cocoa/selection.h19
-rw-r--r--frontends/cocoa/selection.m104
-rw-r--r--frontends/framebuffer/Makefile184
-rw-r--r--frontends/framebuffer/Makefile.defaults47
-rw-r--r--frontends/framebuffer/bitmap.c352
-rw-r--r--frontends/framebuffer/bitmap.h26
-rw-r--r--frontends/framebuffer/clipboard.c105
-rw-r--r--frontends/framebuffer/clipboard.h24
-rw-r--r--frontends/framebuffer/convert_font.c1215
-rw-r--r--frontends/framebuffer/convert_image.c315
-rw-r--r--frontends/framebuffer/fb_search.c74
-rw-r--r--frontends/framebuffer/fbtk.h616
-rw-r--r--frontends/framebuffer/fbtk/bitmap.c136
-rw-r--r--frontends/framebuffer/fbtk/event.c349
-rw-r--r--frontends/framebuffer/fbtk/fbtk.c830
-rw-r--r--frontends/framebuffer/fbtk/fill.c81
-rw-r--r--frontends/framebuffer/fbtk/osk.c199
-rw-r--r--frontends/framebuffer/fbtk/scroll.c589
-rw-r--r--frontends/framebuffer/fbtk/text.c640
-rw-r--r--frontends/framebuffer/fbtk/user.c64
-rw-r--r--frontends/framebuffer/fbtk/widget.h259
-rw-r--r--frontends/framebuffer/fbtk/window.c91
-rw-r--r--frontends/framebuffer/fetch.c98
-rw-r--r--frontends/framebuffer/fetch.h25
-rw-r--r--frontends/framebuffer/findfile.c56
-rw-r--r--frontends/framebuffer/findfile.h32
-rw-r--r--frontends/framebuffer/font.h68
-rw-r--r--frontends/framebuffer/font_freetype.c566
-rw-r--r--frontends/framebuffer/font_freetype.h30
-rw-r--r--frontends/framebuffer/font_internal.c491
-rw-r--r--frontends/framebuffer/font_internal.h49
-rw-r--r--frontends/framebuffer/framebuffer.c467
-rw-r--r--frontends/framebuffer/framebuffer.h40
-rw-r--r--frontends/framebuffer/gui.c2226
-rw-r--r--frontends/framebuffer/gui.h84
-rw-r--r--frontends/framebuffer/image_data.h60
-rw-r--r--frontends/framebuffer/localhistory.c144
-rw-r--r--frontends/framebuffer/options.h70
l---------frontends/framebuffer/res/Messages1
l---------frontends/framebuffer/res/adblock.css1
l---------frontends/framebuffer/res/credits.html1
l---------frontends/framebuffer/res/default.css1
l---------frontends/framebuffer/res/favicon.png1
-rw-r--r--frontends/framebuffer/res/fonts/glyph_data9508
-rw-r--r--frontends/framebuffer/res/icons/back.pngbin0 -> 741 bytes
-rw-r--r--frontends/framebuffer/res/icons/back_g.pngbin0 -> 701 bytes
-rw-r--r--frontends/framebuffer/res/icons/forward.pngbin0 -> 672 bytes
-rw-r--r--frontends/framebuffer/res/icons/forward_g.pngbin0 -> 702 bytes
-rw-r--r--frontends/framebuffer/res/icons/history.pngbin0 -> 884 bytes
-rw-r--r--frontends/framebuffer/res/icons/history_g.pngbin0 -> 798 bytes
-rw-r--r--frontends/framebuffer/res/icons/home.pngbin0 -> 818 bytes
-rw-r--r--frontends/framebuffer/res/icons/home_g.pngbin0 -> 798 bytes
-rw-r--r--frontends/framebuffer/res/icons/osk.pngbin0 -> 262 bytes
-rw-r--r--frontends/framebuffer/res/icons/reload.pngbin0 -> 1075 bytes
-rw-r--r--frontends/framebuffer/res/icons/reload_g.pngbin0 -> 1109 bytes
-rw-r--r--frontends/framebuffer/res/icons/scrolld.pngbin0 -> 294 bytes
-rw-r--r--frontends/framebuffer/res/icons/scrolll.pngbin0 -> 307 bytes
-rw-r--r--frontends/framebuffer/res/icons/scrollr.pngbin0 -> 286 bytes
-rw-r--r--frontends/framebuffer/res/icons/scrollu.pngbin0 -> 280 bytes
-rw-r--r--frontends/framebuffer/res/icons/stop.pngbin0 -> 1023 bytes
-rw-r--r--frontends/framebuffer/res/icons/stop_g.pngbin0 -> 1050 bytes
l---------frontends/framebuffer/res/internal.css1
l---------frontends/framebuffer/res/licence.html1
l---------frontends/framebuffer/res/maps.html1
l---------frontends/framebuffer/res/netsurf.png1
-rw-r--r--frontends/framebuffer/res/pointers/caret.pngbin0 -> 146 bytes
-rw-r--r--frontends/framebuffer/res/pointers/cross.pngbin0 -> 144 bytes
-rw-r--r--frontends/framebuffer/res/pointers/default.pngbin0 -> 179 bytes
-rw-r--r--frontends/framebuffer/res/pointers/help.pngbin0 -> 209 bytes
-rw-r--r--frontends/framebuffer/res/pointers/left-right.pngbin0 -> 163 bytes
-rw-r--r--frontends/framebuffer/res/pointers/lu-rd.pngbin0 -> 170 bytes
-rw-r--r--frontends/framebuffer/res/pointers/menu.pngbin0 -> 220 bytes
-rw-r--r--frontends/framebuffer/res/pointers/move.pngbin0 -> 183 bytes
-rw-r--r--frontends/framebuffer/res/pointers/no_drop.pngbin0 -> 224 bytes
-rw-r--r--frontends/framebuffer/res/pointers/not_allowed.pngbin0 -> 197 bytes
-rw-r--r--frontends/framebuffer/res/pointers/point.pngbin0 -> 181 bytes
-rw-r--r--frontends/framebuffer/res/pointers/progress.pngbin0 -> 213 bytes
-rw-r--r--frontends/framebuffer/res/pointers/ru-ld.pngbin0 -> 171 bytes
-rw-r--r--frontends/framebuffer/res/pointers/up-down.pngbin0 -> 167 bytes
-rw-r--r--frontends/framebuffer/res/pointers/wait.pngbin0 -> 211 bytes
l---------frontends/framebuffer/res/quirks.css1
l---------frontends/framebuffer/res/throbber1
l---------frontends/framebuffer/res/welcome.html1
-rw-r--r--frontends/framebuffer/schedule.c221
-rw-r--r--frontends/framebuffer/schedule.h45
-rw-r--r--frontends/gtk/Makefile205
-rw-r--r--frontends/gtk/Makefile.defaults42
-rw-r--r--frontends/gtk/about.c157
-rw-r--r--frontends/gtk/about.h24
-rw-r--r--frontends/gtk/bitmap.c540
-rw-r--r--frontends/gtk/bitmap.h35
-rw-r--r--frontends/gtk/compat.c640
-rw-r--r--frontends/gtk/compat.h296
-rw-r--r--frontends/gtk/completion.c157
-rw-r--r--frontends/gtk/completion.h46
-rw-r--r--frontends/gtk/cookies.c219
-rw-r--r--frontends/gtk/cookies.h37
-rw-r--r--frontends/gtk/download.c865
-rw-r--r--frontends/gtk/download.h41
-rw-r--r--frontends/gtk/fetch.c260
-rw-r--r--frontends/gtk/fetch.h28
-rw-r--r--frontends/gtk/gdk.c124
-rw-r--r--frontends/gtk/gdk.h35
-rw-r--r--frontends/gtk/gettext.c43
-rw-r--r--frontends/gtk/gettext.h32
-rw-r--r--frontends/gtk/gui.c1161
-rw-r--r--frontends/gtk/gui.h45
-rw-r--r--frontends/gtk/history.c273
-rw-r--r--frontends/gtk/history.h39
-rw-r--r--frontends/gtk/hotlist.c280
-rw-r--r--frontends/gtk/hotlist.h40
-rw-r--r--frontends/gtk/layout_pango.c309
-rw-r--r--frontends/gtk/layout_pango.h43
-rw-r--r--frontends/gtk/login.c232
-rw-r--r--frontends/gtk/login.h31
-rw-r--r--frontends/gtk/menu.c585
-rw-r--r--frontends/gtk/menu.h227
-rw-r--r--frontends/gtk/options.h72
-rw-r--r--frontends/gtk/plotters.c538
-rw-r--r--frontends/gtk/plotters.h40
-rw-r--r--frontends/gtk/preferences.c1030
-rw-r--r--frontends/gtk/preferences.h28
-rw-r--r--frontends/gtk/print.c613
-rw-r--r--frontends/gtk/print.h50
l---------frontends/gtk/res/Messages1
l---------frontends/gtk/res/SearchEngines1
l---------frontends/gtk/res/adblock.css1
-rw-r--r--frontends/gtk/res/arrow_down_8x32.pngbin0 -> 206 bytes
l---------frontends/gtk/res/ca-bundle.txt1
-rw-r--r--frontends/gtk/res/cookies.gtk2.ui174
-rw-r--r--frontends/gtk/res/cookies.gtk3.ui206
l---------frontends/gtk/res/credits.html1
l---------frontends/gtk/res/de/welcome.html1
l---------frontends/gtk/res/default.css1
-rw-r--r--frontends/gtk/res/default.icobin0 -> 1406 bytes
-rw-r--r--frontends/gtk/res/downloads.gtk2.ui175
-rw-r--r--frontends/gtk/res/downloads.gtk3.ui175
l---------frontends/gtk/res/en/credits.html1
l---------frontends/gtk/res/en/licence.html1
l---------frontends/gtk/res/en/maps.html1
l---------frontends/gtk/res/en/welcome.html1
l---------frontends/gtk/res/favicon.png1
-rw-r--r--frontends/gtk/res/history.gtk2.ui242
-rw-r--r--frontends/gtk/res/history.gtk3.ui238
-rw-r--r--frontends/gtk/res/hotlist.gtk2.ui217
-rw-r--r--frontends/gtk/res/hotlist.gtk3.ui255
l---------frontends/gtk/res/icons1
l---------frontends/gtk/res/internal.css1
l---------frontends/gtk/res/it/credits.html1
l---------frontends/gtk/res/it/licence.html1
l---------frontends/gtk/res/it/welcome.html1
l---------frontends/gtk/res/ja/welcome.html1
-rw-r--r--frontends/gtk/res/languages261
l---------frontends/gtk/res/licence.html1
-rw-r--r--frontends/gtk/res/login.gtk2.ui223
-rw-r--r--frontends/gtk/res/login.gtk3.ui223
l---------frontends/gtk/res/maps.html1
-rw-r--r--frontends/gtk/res/menu_cursor.pngbin0 -> 255 bytes
-rw-r--r--frontends/gtk/res/menu_cursor.xbm6
-rw-r--r--frontends/gtk/res/menu_cursor_mask.xbm6
-rw-r--r--frontends/gtk/res/menu_cursor_mask.xpm22
-rw-r--r--frontends/gtk/res/messages.gresource.xml10
-rw-r--r--frontends/gtk/res/netsurf-16x16.xpm211
-rw-r--r--frontends/gtk/res/netsurf-gtk.desktop68
-rw-r--r--frontends/gtk/res/netsurf.gresource.xml70
-rw-r--r--frontends/gtk/res/netsurf.gtk2.ui212
-rw-r--r--frontends/gtk/res/netsurf.gtk3.ui207
l---------frontends/gtk/res/netsurf.png1
-rw-r--r--frontends/gtk/res/netsurf.xpm317
l---------frontends/gtk/res/nl/credits.html1
l---------frontends/gtk/res/nl/licence.html1
l---------frontends/gtk/res/nl/welcome.html1
-rw-r--r--frontends/gtk/res/options.gtk2.ui3004
-rw-r--r--frontends/gtk/res/options.gtk3.ui3057
-rw-r--r--frontends/gtk/res/password.gtk2.ui415
-rw-r--r--frontends/gtk/res/password.gtk3.ui415
l---------frontends/gtk/res/quirks.css1
-rw-r--r--frontends/gtk/res/ssl.gtk2.ui202
-rw-r--r--frontends/gtk/res/ssl.gtk3.ui181
-rw-r--r--frontends/gtk/res/tabcontents.gtk2.ui92
-rw-r--r--frontends/gtk/res/tabcontents.gtk3.ui92
-rw-r--r--frontends/gtk/res/throbber/throbber0.pngbin0 -> 507 bytes
-rw-r--r--frontends/gtk/res/throbber/throbber1.pngbin0 -> 802 bytes
-rw-r--r--frontends/gtk/res/throbber/throbber2.pngbin0 -> 790 bytes
-rw-r--r--frontends/gtk/res/throbber/throbber3.pngbin0 -> 808 bytes
-rw-r--r--frontends/gtk/res/throbber/throbber4.pngbin0 -> 797 bytes
-rw-r--r--frontends/gtk/res/throbber/throbber5.pngbin0 -> 797 bytes
-rw-r--r--frontends/gtk/res/throbber/throbber6.pngbin0 -> 819 bytes
-rw-r--r--frontends/gtk/res/throbber/throbber7.pngbin0 -> 792 bytes
-rw-r--r--frontends/gtk/res/throbber/throbber8.pngbin0 -> 814 bytes
-rw-r--r--frontends/gtk/res/toolbar.gtk2.ui189
-rw-r--r--frontends/gtk/res/toolbar.gtk3.ui189
-rw-r--r--frontends/gtk/res/viewdata.gtk2.ui204
-rw-r--r--frontends/gtk/res/viewdata.gtk3.ui239
-rw-r--r--frontends/gtk/res/warning.gtk2.ui77
-rw-r--r--frontends/gtk/res/warning.gtk3.ui77
l---------frontends/gtk/res/welcome.html1
-rw-r--r--frontends/gtk/resources.c600
-rw-r--r--frontends/gtk/resources.h109
-rw-r--r--frontends/gtk/scaffolding.c2811
-rw-r--r--frontends/gtk/scaffolding.h252
-rw-r--r--frontends/gtk/schedule.c136
-rw-r--r--frontends/gtk/schedule.h26
-rw-r--r--frontends/gtk/search.c245
-rw-r--r--frontends/gtk/search.h36
-rw-r--r--frontends/gtk/selection.c96
-rw-r--r--frontends/gtk/selection.h24
-rw-r--r--frontends/gtk/sexy_icon_entry.c982
-rw-r--r--frontends/gtk/sexy_icon_entry.h100
-rw-r--r--frontends/gtk/ssl_cert.c135
-rw-r--r--frontends/gtk/ssl_cert.h36
-rw-r--r--frontends/gtk/tabs.c416
-rw-r--r--frontends/gtk/tabs.h42
-rw-r--r--frontends/gtk/throbber.c91
-rw-r--r--frontends/gtk/throbber.h35
-rw-r--r--frontends/gtk/toolbar.c1416
-rw-r--r--frontends/gtk/toolbar.h96
-rw-r--r--frontends/gtk/treeview.c586
-rw-r--r--frontends/gtk/treeview.h38
-rw-r--r--frontends/gtk/viewdata.c990
-rw-r--r--frontends/gtk/viewdata.h48
-rw-r--r--frontends/gtk/viewsource.c82
-rw-r--r--frontends/gtk/viewsource.h25
-rw-r--r--frontends/gtk/warn.h32
-rw-r--r--frontends/gtk/window.c1337
-rw-r--r--frontends/gtk/window.h48
-rw-r--r--frontends/monkey/401login.c55
-rw-r--r--frontends/monkey/401login.h9
-rw-r--r--frontends/monkey/Makefile55
-rw-r--r--frontends/monkey/Makefile.defaults13
-rw-r--r--frontends/monkey/bitmap.c150
-rw-r--r--frontends/monkey/bitmap.h24
-rw-r--r--frontends/monkey/browser.c527
-rw-r--r--frontends/monkey/browser.h48
-rw-r--r--frontends/monkey/cert.c58
-rw-r--r--frontends/monkey/cert.h28
-rw-r--r--frontends/monkey/dispatch.c105
-rw-r--r--frontends/monkey/dispatch.h28
-rw-r--r--frontends/monkey/download.c94
-rw-r--r--frontends/monkey/fetch.c53
-rw-r--r--frontends/monkey/fetch.h24
-rw-r--r--frontends/monkey/filetype.c215
-rw-r--r--frontends/monkey/filetype.h22
-rw-r--r--frontends/monkey/layout.c117
-rw-r--r--frontends/monkey/layout.h24
-rw-r--r--frontends/monkey/main.c398
-rw-r--r--frontends/monkey/options.h43
-rw-r--r--frontends/monkey/plot.c111
-rw-r--r--frontends/monkey/plot.h26
l---------frontends/monkey/res1
-rw-r--r--frontends/monkey/schedule.c223
-rw-r--r--frontends/monkey/schedule.h48
-rw-r--r--frontends/riscos/401login.c236
-rw-r--r--frontends/riscos/Makefile153
-rw-r--r--frontends/riscos/Makefile.defaults37
-rw-r--r--frontends/riscos/assert.c44
-rw-r--r--frontends/riscos/bitmap.c876
-rw-r--r--frontends/riscos/bitmap.h116
-rw-r--r--frontends/riscos/buffer.c396
-rw-r--r--frontends/riscos/buffer.h31
-rw-r--r--frontends/riscos/configure.c410
-rw-r--r--frontends/riscos/configure.h34
-rw-r--r--frontends/riscos/configure/con_cache.c107
-rw-r--r--frontends/riscos/configure/con_connect.c220
-rw-r--r--frontends/riscos/configure/con_content.c107
-rw-r--r--frontends/riscos/configure/con_fonts.c209
-rw-r--r--frontends/riscos/configure/con_home.c118
-rw-r--r--frontends/riscos/configure/con_image.c269
-rw-r--r--frontends/riscos/configure/con_inter.c145
-rw-r--r--frontends/riscos/configure/con_language.c139
-rw-r--r--frontends/riscos/configure/con_secure.c83
-rw-r--r--frontends/riscos/configure/con_theme.c420
-rw-r--r--frontends/riscos/configure/configure.h43
-rw-r--r--frontends/riscos/content-handlers/artworks.c435
-rw-r--r--frontends/riscos/content-handlers/artworks.h42
-rw-r--r--frontends/riscos/content-handlers/awrender.s390
-rw-r--r--frontends/riscos/content-handlers/draw.c254
-rw-r--r--frontends/riscos/content-handlers/draw.h42
-rw-r--r--frontends/riscos/content-handlers/sprite.c266
-rw-r--r--frontends/riscos/content-handlers/sprite.h44
-rw-r--r--frontends/riscos/cookies.c385
-rw-r--r--frontends/riscos/cookies.h38
-rw-r--r--frontends/riscos/dialog.c816
-rw-r--r--frontends/riscos/dialog.h55
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Cache/!Boot,feb17
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Cache/!Help,feb2
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Cache/!Run,feb9
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Cache/!RunImage,ffbbin0 -> 2985 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Cache/!Sprites,ff9bin0 -> 864 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Cache/!Sprites22,ff9bin0 -> 2504 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Cache/Caches/Blank1
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/MultiError,ffbbin0 -> 24031 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/ResFind,ffbbin0 -> 1930 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/UK/!Meta9
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/UK/Help60
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/UK/Messages8
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/UK/Templates,fecbin0 -> 541 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/!Boot,feb5
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/!Help1
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/!Run,feb5
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/!Sprites,ff9bin0 -> 5780 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/!Sprites11,ff9bin0 -> 11132 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/!Sprites22,ff9bin0 -> 7324 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Acorn/Latin1bin0 -> 256 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Apple/CentEurobin0 -> 256 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Apple/Cyrillicbin0 -> 256 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Apple/Romanbin0 -> 256 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Apple/Ukrainianbin0 -> 256 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/BigFivebin0 -> 29516 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/C0/40[ISO646]bin0 -> 64 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/C1/43[IS6429]bin0 -> 64 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/40[646old]bin0 -> 188 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/41[646-GB]bin0 -> 188 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/42[646IRV]bin0 -> 188 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/43[FinSwe]bin0 -> 188 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/47[646-SE]bin0 -> 188 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/48[646-SE]bin0 -> 188 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/49[JS201K]1
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/4A[JS201R]bin0 -> 188 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/4B[646-DE]bin0 -> 188 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/4C[646-PT]bin0 -> 188 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/54[GB1988]bin0 -> 188 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/56[Teltxt]bin0 -> 188 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/59[646-IT]bin0 -> 188 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/5A[646-ES]bin0 -> 188 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/60[646-NO]bin0 -> 188 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/66[646-FR]bin0 -> 188 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/69[646-HU]bin0 -> 188 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/6B[Arabic]bin0 -> 188 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/6C[IS6937]bin0 -> 188 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/7A[SerbCr]bin0 -> 188 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/40[JS6226]bin0 -> 17672 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/41[GB2312]bin0 -> 17672 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/42[JIS208]bin0 -> 17672 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/43[KS1001]bin0 -> 17672 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/44[JIS212]bin0 -> 17672 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/47[CNS1]bin0 -> 17672 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/48[CNS2]bin0 -> 17672 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/49[CNS3]bin0 -> 17672 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/4A[CNS4]bin0 -> 17672 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/4B[CNS5]bin0 -> 17672 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/4C[CNS6]bin0 -> 17672 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/4D[CNS7]2
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/41[Lat1]bin0 -> 192 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/42[Lat2]bin0 -> 192 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/43[Lat3]bin0 -> 192 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/44[Lat4]bin0 -> 192 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/46[Greek]bin0 -> 192 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/47[Arabic]bin0 -> 192 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/48[Hebrew]bin0 -> 192 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/4C[Cyrill]bin0 -> 192 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/4D[Lat5]bin0 -> 192 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/50[LatSup]bin0 -> 192 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/52[IS6937]bin0 -> 192 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/54[Thai]bin0 -> 192 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/56[Lat6]bin0 -> 192 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/58[L6Sami]bin0 -> 192 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/59[Lat7]bin0 -> 192 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/5C[Welsh]bin0 -> 192 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/5D[Sami]bin0 -> 192 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/5E[Hebrew]bin0 -> 192 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/5F[Lat8]bin0 -> 192 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/62[Lat9]bin0 -> 192 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/66[Lat10]bin0 -> 192 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/KOI8-Rbin0 -> 256 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1250bin0 -> 256 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1251bin0 -> 256 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1252bin0 -> 256 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1253bin0 -> 256 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1254bin0 -> 256 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1256bin0 -> 256 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP866bin0 -> 256 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP874bin0 -> 256 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP932bin0 -> 996 bytes
-rw-r--r--frontends/riscos/distribution/!Boot/Resources/!Unicode/Files/Aliases303
-rw-r--r--frontends/riscos/distribution/!System/310/Modules/CryptRand,ffabin0 -> 15584 bytes
-rw-r--r--frontends/riscos/distribution/!System/310/Modules/Iconv,ffabin0 -> 66240 bytes
-rw-r--r--frontends/riscos/distribution/!System/310/Modules/Network/URI,ffabin0 -> 9188 bytes
-rwxr-xr-xfrontends/riscos/distribution/!System/310/Modules/SharedULib,ffabin0 -> 3116 bytes
-rw-r--r--frontends/riscos/distribution/!System/310/Modules/Tinct,ffabin0 -> 25680 bytes
-rw-r--r--frontends/riscos/distribution/3rdParty/AcornURI/!ReadMe34
-rw-r--r--frontends/riscos/distribution/3rdParty/AcornURI/Copying504
-rw-r--r--frontends/riscos/distribution/3rdParty/CryptRand/Copyright46
-rw-r--r--frontends/riscos/distribution/3rdParty/Iconv/ReadMe45
-rw-r--r--frontends/riscos/distribution/3rdParty/Iconv/doc/API132
-rw-r--r--frontends/riscos/distribution/3rdParty/Iconv/doc/ChangeLog114
-rw-r--r--frontends/riscos/distribution/3rdParty/Iconv/doc/Uni-iconv204
-rw-r--r--frontends/riscos/distribution/3rdParty/SharedULib/Copyright761
-rw-r--r--frontends/riscos/distribution/3rdParty/Tinct/!Help304
-rw-r--r--frontends/riscos/distribution/LeesMij73
-rw-r--r--frontends/riscos/distribution/ReadMe61
-rw-r--r--frontends/riscos/download.c1629
-rw-r--r--frontends/riscos/filetype.c352
-rw-r--r--frontends/riscos/filetype.h128
-rw-r--r--frontends/riscos/font.c598
-rw-r--r--frontends/riscos/font.h45
-rw-r--r--frontends/riscos/global_history.c413
-rw-r--r--frontends/riscos/global_history.h38
-rw-r--r--frontends/riscos/gui.c2522
-rw-r--r--frontends/riscos/gui.h271
-rw-r--r--frontends/riscos/gui/button_bar.c1229
-rw-r--r--frontends/riscos/gui/button_bar.h309
-rw-r--r--frontends/riscos/gui/progress_bar.c535
-rw-r--r--frontends/riscos/gui/progress_bar.h44
-rw-r--r--frontends/riscos/gui/status_bar.c625
-rw-r--r--frontends/riscos/gui/status_bar.h45
-rw-r--r--frontends/riscos/gui/throbber.c418
-rw-r--r--frontends/riscos/gui/throbber.h147
-rw-r--r--frontends/riscos/gui/url_bar.c1344
-rw-r--r--frontends/riscos/gui/url_bar.h329
-rw-r--r--frontends/riscos/help.c361
-rw-r--r--frontends/riscos/help.h33
-rw-r--r--frontends/riscos/history.c336
-rw-r--r--frontends/riscos/hotlist.c737
-rw-r--r--frontends/riscos/hotlist.h53
-rw-r--r--frontends/riscos/iconbar.c261
-rw-r--r--frontends/riscos/iconbar.h32
-rw-r--r--frontends/riscos/image.c219
-rw-r--r--frontends/riscos/image.h39
-rw-r--r--frontends/riscos/menus.c958
-rw-r--r--frontends/riscos/menus.h183
-rw-r--r--frontends/riscos/message.c247
-rw-r--r--frontends/riscos/message.h42
-rw-r--r--frontends/riscos/mouse.c282
-rw-r--r--frontends/riscos/mouse.h110
-rw-r--r--frontends/riscos/options.h68
-rw-r--r--frontends/riscos/oslib_pre7.h42
-rw-r--r--frontends/riscos/palettes.c321
-rw-r--r--frontends/riscos/palettes.h33
-rw-r--r--frontends/riscos/plotters.c525
-rw-r--r--frontends/riscos/print.c976
-rw-r--r--frontends/riscos/print.h33
-rw-r--r--frontends/riscos/query.c381
-rw-r--r--frontends/riscos/query.h53
-rw-r--r--frontends/riscos/save.c1401
-rw-r--r--frontends/riscos/save.h50
-rw-r--r--frontends/riscos/save_draw.c474
-rw-r--r--frontends/riscos/save_draw.h31
-rw-r--r--frontends/riscos/save_pdf.c60
-rw-r--r--frontends/riscos/save_pdf.h31
-rw-r--r--frontends/riscos/schedule.c163
-rw-r--r--frontends/riscos/scripts/Help12
-rw-r--r--frontends/riscos/scripts/Run109
-rw-r--r--frontends/riscos/search.c475
-rw-r--r--frontends/riscos/searchweb.c18
-rw-r--r--frontends/riscos/sslcert.c348
-rw-r--r--frontends/riscos/sslcert.h34
-rw-r--r--frontends/riscos/templates/de3845
-rw-r--r--frontends/riscos/templates/en3837
-rw-r--r--frontends/riscos/templates/fr3862
-rw-r--r--frontends/riscos/templates/nl3887
-rw-r--r--frontends/riscos/textarea.c1160
-rw-r--r--frontends/riscos/textarea.h50
-rw-r--r--frontends/riscos/textselection.c657
-rw-r--r--frontends/riscos/textselection.h53
-rw-r--r--frontends/riscos/theme.c741
-rw-r--r--frontends/riscos/theme.h109
-rw-r--r--frontends/riscos/theme_install.c237
-rw-r--r--frontends/riscos/tinct.h154
-rw-r--r--frontends/riscos/toolbar.c1788
-rw-r--r--frontends/riscos/toolbar.h542
-rw-r--r--frontends/riscos/treeview.c1279
-rw-r--r--frontends/riscos/treeview.h55
-rw-r--r--frontends/riscos/ucstables.c697
-rw-r--r--frontends/riscos/ucstables.h29
-rw-r--r--frontends/riscos/uri.c139
-rw-r--r--frontends/riscos/uri.h30
-rw-r--r--frontends/riscos/url_complete.c740
-rw-r--r--frontends/riscos/url_complete.h100
-rw-r--r--frontends/riscos/url_protocol.c226
-rw-r--r--frontends/riscos/url_protocol.h35
-rw-r--r--frontends/riscos/url_suggest.c239
-rw-r--r--frontends/riscos/url_suggest.h38
-rw-r--r--frontends/riscos/wimp.c1134
-rw-r--r--frontends/riscos/wimp.h83
-rw-r--r--frontends/riscos/wimp_event.c1814
-rw-r--r--frontends/riscos/wimp_event.h116
-rw-r--r--frontends/riscos/wimputils.h65
-rw-r--r--frontends/riscos/window.c5025
-rw-r--r--frontends/riscos/window.h46
-rw-r--r--frontends/windows/Makefile75
-rw-r--r--frontends/windows/Makefile.defaults25
-rw-r--r--frontends/windows/about.c151
-rw-r--r--frontends/windows/about.h24
-rw-r--r--frontends/windows/bitmap.c387
-rw-r--r--frontends/windows/bitmap.h40
-rw-r--r--frontends/windows/download.c346
-rw-r--r--frontends/windows/download.h37
-rw-r--r--frontends/windows/drawable.c625
-rw-r--r--frontends/windows/drawable.h25
-rw-r--r--frontends/windows/file.c302
-rw-r--r--frontends/windows/file.h29
-rw-r--r--frontends/windows/filetype.c60
-rw-r--r--frontends/windows/filetype.h24
-rw-r--r--frontends/windows/findfile.c156
-rw-r--r--frontends/windows/findfile.h26
-rw-r--r--frontends/windows/font.c298
-rw-r--r--frontends/windows/font.h53
-rw-r--r--frontends/windows/gui.c170
-rw-r--r--frontends/windows/gui.h58
-rw-r--r--frontends/windows/localhistory.c401
-rw-r--r--frontends/windows/localhistory.h32
-rw-r--r--frontends/windows/main.c271
-rw-r--r--frontends/windows/plot.c868
-rw-r--r--frontends/windows/plot.h24
-rw-r--r--frontends/windows/pointers.c128
-rw-r--r--frontends/windows/pointers.h40
-rw-r--r--frontends/windows/prefs.c680
-rw-r--r--frontends/windows/prefs.h24
-rw-r--r--frontends/windows/res/NetSurf.icobin0 -> 18614 bytes
l---------frontends/windows/res/adblock.css1
-rw-r--r--frontends/windows/res/banner.bmpbin0 -> 79518 bytes
l---------frontends/windows/res/ca-bundle.crt1
l---------frontends/windows/res/credits.html1
l---------frontends/windows/res/default.css1
-rw-r--r--frontends/windows/res/home.bmpbin0 -> 2358 bytes
-rw-r--r--frontends/windows/res/icons/back.pngbin0 -> 653 bytes
-rw-r--r--frontends/windows/res/icons/back_g.pngbin0 -> 306 bytes
-rw-r--r--frontends/windows/res/icons/back_h.pngbin0 -> 607 bytes
-rw-r--r--frontends/windows/res/icons/forward.pngbin0 -> 697 bytes
-rw-r--r--frontends/windows/res/icons/forward_g.pngbin0 -> 538 bytes
-rw-r--r--frontends/windows/res/icons/forward_h.pngbin0 -> 635 bytes
-rw-r--r--frontends/windows/res/icons/home.pngbin0 -> 745 bytes
-rw-r--r--frontends/windows/res/icons/home_g.pngbin0 -> 576 bytes
-rw-r--r--frontends/windows/res/icons/home_h.pngbin0 -> 751 bytes
-rw-r--r--frontends/windows/res/icons/reload.pngbin0 -> 1062 bytes
-rw-r--r--frontends/windows/res/icons/reload_g.pngbin0 -> 814 bytes
-rw-r--r--frontends/windows/res/icons/reload_h.pngbin0 -> 1046 bytes
-rw-r--r--frontends/windows/res/icons/stop.pngbin0 -> 1135 bytes
-rw-r--r--frontends/windows/res/icons/stop_g.pngbin0 -> 850 bytes
-rw-r--r--frontends/windows/res/icons/stop_h.pngbin0 -> 1119 bytes
-rw-r--r--frontends/windows/res/installer.nsi141
l---------frontends/windows/res/internal.css1
l---------frontends/windows/res/licence.html1
-rw-r--r--frontends/windows/res/netsurf.gifbin0 -> 14119 bytes
l---------frontends/windows/res/netsurf.png1
l---------frontends/windows/res/quirks.css1
-rw-r--r--frontends/windows/res/resource.rc265
-rw-r--r--frontends/windows/res/throbber.avibin0 -> 23980 bytes
-rw-r--r--frontends/windows/res/throbber/throbber0.bmpbin0 -> 1866 bytes
-rwxr-xr-xfrontends/windows/res/throbber/throbber0.pngbin0 -> 730 bytes
-rw-r--r--frontends/windows/res/throbber/throbber1.bmpbin0 -> 1866 bytes
-rwxr-xr-xfrontends/windows/res/throbber/throbber1.pngbin0 -> 928 bytes
-rw-r--r--frontends/windows/res/throbber/throbber2.bmpbin0 -> 1866 bytes
-rwxr-xr-xfrontends/windows/res/throbber/throbber2.pngbin0 -> 906 bytes
-rw-r--r--frontends/windows/res/throbber/throbber3.bmpbin0 -> 1866 bytes
-rwxr-xr-xfrontends/windows/res/throbber/throbber3.pngbin0 -> 917 bytes
-rw-r--r--frontends/windows/res/throbber/throbber4.bmpbin0 -> 1866 bytes
-rwxr-xr-xfrontends/windows/res/throbber/throbber4.pngbin0 -> 927 bytes
-rw-r--r--frontends/windows/res/throbber/throbber5.bmpbin0 -> 1866 bytes
-rwxr-xr-xfrontends/windows/res/throbber/throbber5.pngbin0 -> 923 bytes
-rw-r--r--frontends/windows/res/throbber/throbber6.bmpbin0 -> 1866 bytes
-rwxr-xr-xfrontends/windows/res/throbber/throbber6.pngbin0 -> 904 bytes
-rw-r--r--frontends/windows/res/throbber/throbber7.bmpbin0 -> 1866 bytes
-rwxr-xr-xfrontends/windows/res/throbber/throbber7.pngbin0 -> 940 bytes
-rwxr-xr-xfrontends/windows/res/throbber/throbber8.pngbin0 -> 921 bytes
-rw-r--r--frontends/windows/res/toolbar.bmpbin0 -> 8694 bytes
-rw-r--r--frontends/windows/res/toolbarg.bmpbin0 -> 8694 bytes
-rw-r--r--frontends/windows/res/toolbarh.bmpbin0 -> 8694 bytes
l---------frontends/windows/res/welcome.html1
-rw-r--r--frontends/windows/resourceid.h138
-rw-r--r--frontends/windows/schedule.c235
-rw-r--r--frontends/windows/schedule.h65
-rw-r--r--frontends/windows/windbg.c663
-rw-r--r--frontends/windows/windbg.h34
-rw-r--r--frontends/windows/window.c1741
-rw-r--r--frontends/windows/window.h109
1076 files changed, 215611 insertions, 0 deletions
diff --git a/frontends/Makefile b/frontends/Makefile
new file mode 100644
index 000000000..da6fe2019
--- /dev/null
+++ b/frontends/Makefile
@@ -0,0 +1,9 @@
+# NetSurf Frontend sources
+
+FRONTEND_SOURCE_DIR := frontends/$(TARGET)
+FRONTEND_RESOURCES_DIR := frontends/$(TARGET)/res
+
+# Target Specific setup
+include frontends/$(TARGET)/Makefile
+
+S_FRONTEND := $(addprefix frontends/$(TARGET)/,$(S_FRONTEND))
diff --git a/frontends/amiga/Makefile b/frontends/amiga/Makefile
new file mode 100644
index 000000000..a5e9d1c1f
--- /dev/null
+++ b/frontends/amiga/Makefile
@@ -0,0 +1,122 @@
+# ----------------------------------------------------------------------------
+# Amiga target setup
+# ----------------------------------------------------------------------------
+
+CFLAGS += -std=c99 -Dnsamiga
+
+ifneq ($(SUBTARGET),os3)
+ CFLAGS += -O2 -finline-functions -U__STRICT_ANSI__ -D__USE_INLINE__ -D__USE_BASETYPE__
+else
+ CFLAGS += -msoft-float -m68020-60 -O2 -DPATH_MAX=1024 -D__m68k__
+endif
+
+NETSURF_FEATURE_ROSPRITE_CFLAGS := -DWITH_NSSPRITE
+NETSURF_FEATURE_BMP_CFLAGS := -DWITH_BMP
+NETSURF_FEATURE_GIF_CFLAGS := -DWITH_GIF
+NETSURF_FEATURE_PNG_CFLAGS := -DWITH_PNG
+NETSURF_FEATURE_NSSVG_CFLAGS := -DWITH_NS_SVG
+NETSURF_FEATURE_VIDEO_CFLAGS := -DWITH_VIDEO
+
+ifeq ($(HOST),amiga)
+ $(eval $(call feature_enabled,ROSPRITE,-DWITH_NSSPRITE,-lrosprite,Sprite (librosprite)))
+ $(eval $(call feature_enabled,BMP,-DWITH_BMP,-lnsbmp,BMP (libnsbmp)))
+ $(eval $(call feature_enabled,GIF,-DWITH_GIF,-lnsgif,GIF (libnsgif)))
+ $(eval $(call feature_enabled,PNG,-DWITH_PNG,-lpng,PNG (libpng) ))
+ $(eval $(call feature_enabled,NSSVG,-DWITH_NS_SVG,-lsvgtiny,SVG (libsvgtiny)))
+ $(eval $(call feature_enabled,VIDEO,-DWITH_VIDEO -I /SDK/local/newlib/include/glib-2.0,-lgstreamer-0.10 -lglib-2.0 -lgobject-2.0 -lgmodule-2.0 -lgthread-2.0 -lintl -lffi,Video (libgstreamer)))
+ $(eval $(call feature_enabled,JS,-DXP_UNIX -DWITH_JS -DJS_VERSION=185,-lstdc++ -lmozjs185,JavaScript))
+ $(eval $(call feature_enabled,MOZJS,-DXP_AMIGA -DWITH_MOZJS -DJS_VERSION=170 -DJSVERSION_LATEST=170 -DJSOPTION_JIT=0 -DJSCLASS_GLOBAL_FLAGS=0,-ljs -lfdlibm,JavaScript))
+ $(eval $(call feature_enabled,AMIGA_ICON,-DWITH_AMIGA_ICON,,Amiga icon))
+ $(eval $(call feature_enabled,AMIGA_DATATYPES,-DWITH_AMIGA_DATATYPES,,DataTypes))
+
+ CFLAGS += -I /SDK/local/common/include/libpng12
+ LDFLAGS += -lcurl -lrtmp -lpthread -ltre -lintl -lpbl
+ LDFLAGS += -lssl -lcrypto -ldom -lhubbub -lcss -lparserutils -lwapcaplet
+else
+ $(eval $(call feature_enabled,AMIGA_ICON,-DWITH_AMIGA_ICON,,Amiga icon))
+ $(eval $(call feature_enabled,AMIGA_DATATYPES,-DWITH_AMIGA_DATATYPES,,DataTypes))
+
+ CFLAGS += -I$(GCCSDK_INSTALL_ENV)/include
+ CFLAGS += $(shell $(PKG_CONFIG) --cflags tre)
+
+ LDFLAGS += $(shell $(PKG_CONFIG) --static --libs libcurl openssl)
+ LDFLAGS += $(shell $(PKG_CONFIG) --libs tre)
+ LDFLAGS += -L$(GCCSDK_INSTALL_ENV)/lib
+
+ LDFLAGS += -lpbl -liconv
+
+ ifeq ($(SUBTARGET),os3)
+ LDFLAGS += -lamiga -lm
+ endif
+endif
+
+EXETARGET := NetSurf
+
+# The filter and target for split messages
+MESSAGES_FILTER=ami
+
+# ----------------------------------------------------------------------------
+# Source file setup
+# ----------------------------------------------------------------------------
+
+# sources purely for the Amiga build
+S_FRONTEND := gui.c tree.c history.c hotlist.c schedule.c file.c \
+ misc.c bitmap.c font.c filetype.c utf8.c login.c \
+ plotters.c object.c menu.c save_pdf.c arexx.c version.c \
+ cookies.c ctxmenu.c clipboard.c help.c font_scan.c \
+ launch.c search.c history_local.c download.c iff_dr2d.c \
+ sslcert.c gui_options.c print.c theme.c drag.c icon.c libs.c \
+ datatypes.c dt_picture.c dt_anim.c dt_sound.c plugin_hack.c \
+ stringview/stringview.c stringview/urlhistory.c rtg.c \
+ agclass/amigaguide_class.c os3support.c font_diskfont.c \
+ selectmenu.c hash/xxhash.c font_cache.c font_bullet.c
+
+# This is the final source build list
+# Note this is deliberately *not* expanded here as common and image
+# are not yet available
+SOURCES = $(S_COMMON) $(S_IMAGE) $(S_BROWSER) $(S_FRONTEND)
+
+# ----------------------------------------------------------------------------
+# Install target
+# ----------------------------------------------------------------------------
+
+install-amiga:
+
+# ----------------------------------------------------------------------------
+# Package target
+# ----------------------------------------------------------------------------
+
+package-amiga: netsurf.lha
+
+AMIGA_LANGUAGES := de en it ja nl
+AMIGA_PLATFORM_RESOURCES := Pointers Themes default.css default.css.info favicon.png LangNames mimetypes Resource.map SearchEngines splash.png
+AMIGA_GENERIC_RESOURCES := $(AMIGA_LANGUAGES) ca-bundle Icons
+AMIGA_RESOURCES := $(addprefix $(FRONTEND_SOURCE_DIR)/resources/,$(AMIGA_PLATFORM_RESOURCES)) $(addprefix \!NetSurf/Resources/,$(AMIGA_GENERIC_RESOURCES))
+AMIGA_DISTRIBUTION_FILES := $(FRONTEND_SOURCE_DIR)/dist/*
+AMIGA_INSTALL_TARGET_DIR := NetSurf_Amiga
+
+netsurf.lha: $(EXETARGET)
+ $(VQ)echo Creating netsurf.lha
+ $(Q)rm -rf $(AMIGA_INSTALL_TARGET_DIR)
+ $(Q)mkdir -p $(AMIGA_INSTALL_TARGET_DIR)/NetSurf
+ $(Q)cp -p $(EXETARGET) $(AMIGA_INSTALL_TARGET_DIR)/NetSurf
+ $(Q)mkdir $(AMIGA_INSTALL_TARGET_DIR)/NetSurf/Resources
+ $(Q)cp -r $(AMIGA_RESOURCES) $(AMIGA_INSTALL_TARGET_DIR)/NetSurf/Resources
+ $(Q)cp -r $(AMIGA_DISTRIBUTION_FILES) $(AMIGA_INSTALL_TARGET_DIR)/NetSurf
+ $(Q)cp \!NetSurf/Resources/AdBlock,f79 $(AMIGA_INSTALL_TARGET_DIR)/NetSurf/Resources/adblock.css
+ $(Q)cp \!NetSurf/Resources/CSS,f79 $(AMIGA_INSTALL_TARGET_DIR)/NetSurf/Resources/nsdefault.css
+ $(Q)cp \!NetSurf/Resources/internal.css,f79 $(AMIGA_INSTALL_TARGET_DIR)/NetSurf/Resources/internal.css
+ $(Q)cp \!NetSurf/Resources/Quirks,f79 $(AMIGA_INSTALL_TARGET_DIR)/NetSurf/Resources/quirks.css
+ $(Q)cp \!NetSurf/Resources/netsurf.png,b60 $(AMIGA_INSTALL_TARGET_DIR)/NetSurf/Resources/netsurf.png
+ $(Q)cp amiga/pkg/drawer.info $(AMIGA_INSTALL_TARGET_DIR)/NetSurf.info
+ $(Q)cp amiga/pkg/AutoInstall $(AMIGA_INSTALL_TARGET_DIR)
+ ifeq ($(SUBTARGET),os3)
+ $(Q)cp amiga/pkg/netsurf_os3.readme $(AMIGA_INSTALL_TARGET_DIR)/NetSurf
+ $(Q)cp amiga/pkg/netsurf_os3.readme.info $(AMIGA_INSTALL_TARGET_DIR)/NetSurf
+ else
+ $(Q)cp amiga/pkg/netsurf.readme $(AMIGA_INSTALL_TARGET_DIR)/NetSurf
+ $(Q)cp amiga/pkg/netsurf.readme.info $(AMIGA_INSTALL_TARGET_DIR)/NetSurf
+ endif
+ $(Q)cd $(AMIGA_INSTALL_TARGET_DIR); \
+ lha a netsurf.lha NetSurf NetSurf.info AutoInstall
+
diff --git a/frontends/amiga/Makefile.defaults b/frontends/amiga/Makefile.defaults
new file mode 100644
index 000000000..0674a9de2
--- /dev/null
+++ b/frontends/amiga/Makefile.defaults
@@ -0,0 +1,42 @@
+# ----------------------------------------------------------------------------
+# Amiga-specific options
+# ----------------------------------------------------------------------------
+
+# Force using glibc internal iconv implementation instead of external libiconv
+# Valid options: YES, NO
+ifneq ($(SUBTARGET),os3)
+ NETSURF_USE_LIBICONV_PLUG := YES
+else
+ NETSURF_USE_LIBICONV_PLUG := NO
+endif
+
+# Enable NetSurf's use of librosprite for displaying RISC OS Sprites
+# Valid options: YES, NO, AUTO
+NETSURF_USE_ROSPRITE := NO
+
+# Enable NetSurf to display Amiga icons
+# Valid options: YES, NO (recommended)
+NETSURF_USE_AMIGA_ICON := YES
+
+# Enable NetSurf's use of DataTypes for unknown filetypes
+# Valid options: YES, NO
+NETSURF_USE_AMIGA_DATATYPES := YES
+
+# Enable NetSurf's use of libsvgtiny for displaying SVGs
+# Valid options: YES, NO
+NETSURF_USE_NSSVG := YES
+
+# Enable NetSurf's use of Spidermonkey 1.80+
+# Only here to stop the build complaining;
+# enable NETSURF_USE_MOZJS instead for JavaScript support
+# Valid options: NO
+NETSURF_USE_JS := NO
+
+# Enable building the source object cache filesystem based backing store.
+# implementation.
+# Valid options: YES, NO
+NETSURF_FS_BACKING_STORE := YES
+
+# Optimisation levels
+CFLAGS += -fomit-frame-pointer -gstabs
+
diff --git a/frontends/amiga/agclass/amigaguide_class.c b/frontends/amiga/agclass/amigaguide_class.c
new file mode 100644
index 000000000..2b80223ca
--- /dev/null
+++ b/frontends/amiga/agclass/amigaguide_class.c
@@ -0,0 +1,394 @@
+/*
+ * AmigaGuide Class
+ * A BOOPSI class for displaying AmigaGuide files.
+ * by Daniel "Trixie" Jedlicka
+ */
+
+#include "amiga/os3support.h"
+#include "amigaguide_class.h"
+
+#ifdef __amigaos4__
+#define DISPATCHHOOK(func) static uint32 func(Class *cl, Object *o, Msg msg)
+#else
+#define DISPATCHHOOK(func) static ASM uint32 func(REG(a0, Class *cl),REG(a2, Object *o), REG(a1, Msg msg))
+#endif
+
+struct localObjectData
+{
+ struct NewAmigaGuide nag;
+ struct AmigaGuideMsg *agm;
+ AMIGAGUIDECONTEXT agHandle;
+ uint32 agContextID;
+ uint32 agSignal;
+};
+
+struct Library *AmigaGuideBase = NULL;
+struct AmigaGuideIFace *IAmigaGuide = NULL;
+
+
+/* ********************************** function prototypes ************************************ */
+
+DISPATCHHOOK(dispatchAGClass);
+
+
+// class methods
+uint32 om_new(Class *, Object *, struct opSet *);
+uint32 om_dispose(Class *, Object *, Msg);
+uint32 om_set(Class *, Object *, struct opSet *);
+uint32 om_get(Class *, Object *, struct opGet *);
+uint32 agm_open(Class *, Object *, Msg);
+uint32 agm_close(Class *, Object *, Msg);
+uint32 agm_process(Class *, Object *, Msg);
+
+
+/* *************************** class initialization and disposal ***************************** */
+
+
+Class *initAGClass(void)
+{
+ Class *cl = NULL;
+
+
+ // Open amigaguide.library and its interface.
+ if ( (AmigaGuideBase = OpenLibrary("amigaguide.library", 40)) )
+ {
+#ifdef __amigaos4__
+ if ( (IAmigaGuide = (struct AmigaGuideIFace *)GetInterface(AmigaGuideBase, "main", 1L, NULL)) )
+ {
+#endif
+ if ( (cl = MakeClass(NULL, "rootclass", NULL, sizeof(struct localObjectData), 0)) )
+ {
+ cl->cl_Dispatcher.h_Entry = (HOOKFUNC)dispatchAGClass;
+ AddClass(cl);
+ }
+ else freeAGClass(NULL);
+#ifdef __amigaos4__
+ }
+ else freeAGClass(NULL);
+#endif
+ }
+
+ return cl;
+
+}
+
+
+
+BOOL freeAGClass(Class *cl)
+{
+ BOOL retVal = FALSE;
+
+
+ // Close amigaguide.library and free the class.
+#ifdef __amigaos4__
+ if (IAmigaGuide) DropInterface((struct Interface *)IAmigaGuide);
+#endif
+ if (AmigaGuideBase) CloseLibrary(AmigaGuideBase);
+ if (cl) retVal = FreeClass(cl);
+
+ return retVal;
+}
+
+
+
+/* ************************************** class dispatcher ************************************ */
+
+
+DISPATCHHOOK(dispatchAGClass)
+{
+
+ switch (msg->MethodID)
+ {
+ case OM_NEW:
+ return om_new(cl, o, (struct opSet *)msg);
+
+ case OM_DISPOSE:
+ return om_dispose(cl, o, msg);
+
+ case OM_UPDATE:
+ case OM_SET:
+ return om_set(cl, o, (struct opSet *)msg);
+
+ case OM_GET:
+ return om_get(cl, o, (struct opGet *)msg);
+
+ case AGM_OPEN:
+ return agm_open(cl, o, msg);
+
+ case AGM_CLOSE:
+ return agm_close(cl, o, msg);
+
+ case AGM_PROCESS:
+ return agm_process(cl, o, msg);
+
+ default:
+ return IDoSuperMethodA(cl, o, msg);
+ }
+
+}
+
+
+/* *************************************** class methods ************************************** */
+
+uint32 om_new(Class *cl, Object *o, struct opSet *msg)
+{
+ struct localObjectData *lod = NULL;
+ uint32 retVal = 0L;
+
+
+ if ( (retVal = IDoSuperMethodA(cl, o, (Msg)msg)) )
+ {
+ // Obtain pointer to our object's local instance data.
+ if ( (lod = (struct localObjectData *)INST_DATA(cl, retVal)) )
+ {
+ // Initialize values.
+ lod->agHandle = NULL;
+ lod->agContextID = 0;
+ lod->nag.nag_Name = NULL;
+ lod->nag.nag_Screen = NULL;
+ lod->nag.nag_PubScreen = NULL;
+ lod->nag.nag_BaseName = NULL;
+ lod->nag.nag_Context = NULL;
+ lod->nag.nag_Client = NULL; // private, must be NULL!
+
+ // Set initial object attributes based on the tags from NewObject().
+ om_set(cl, (Object *)retVal, msg);
+ }
+ }
+
+ return retVal;
+
+}
+
+
+
+
+
+uint32 om_dispose(Class *cl, Object *o, Msg msg)
+{
+
+ // Close the document, should it still be opened.
+ agm_close(cl, o, msg);
+
+ // Let superclass dispose of the object.
+ return IDoSuperMethodA(cl, o, msg);
+
+}
+
+
+
+
+
+uint32 om_set(Class *cl, Object *o, struct opSet *msg)
+{
+ struct localObjectData *lod = (struct localObjectData *)INST_DATA(cl, o);
+ struct TagItem *ti = NULL, *tags = msg->ops_AttrList;
+ uint32 retVal = 0L;
+
+
+ while ((ti = NextTagItem (&tags)))
+ {
+ switch (ti->ti_Tag)
+ {
+ case AMIGAGUIDE_Name:
+ lod->nag.nag_Name = (STRPTR)ti->ti_Data;
+ retVal++;
+ break;
+
+ case AMIGAGUIDE_Screen:
+ lod->nag.nag_Screen = (struct Screen *)ti->ti_Data;
+ retVal++;
+ break;
+
+ case AMIGAGUIDE_PubScreen:
+ lod->nag.nag_PubScreen = (STRPTR)ti->ti_Data;
+ retVal++;
+ break;
+
+ case AMIGAGUIDE_BaseName:
+ lod->nag.nag_BaseName = (STRPTR)ti->ti_Data;
+ retVal++;
+ break;
+
+ case AMIGAGUIDE_ContextArray:
+ lod->nag.nag_Context = (STRPTR *)ti->ti_Data;
+ retVal++;
+ break;
+
+ case AMIGAGUIDE_ContextID:
+ lod->agContextID = (uint32)ti->ti_Data;
+ retVal++;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return retVal;
+
+}
+
+
+
+
+
+uint32 om_get(Class *cl, Object *o, struct opGet *msg)
+{
+ struct localObjectData *lod = (struct localObjectData *)INST_DATA(cl, o);
+ uint32 retVal = 0L;
+
+
+ switch (msg->opg_AttrID)
+ {
+ case AMIGAGUIDE_Name:
+ *(msg->opg_Storage) = (uint32)lod->nag.nag_Name;
+ retVal = 1L;
+ break;
+
+ case AMIGAGUIDE_Screen:
+ *(msg->opg_Storage) = (uint32)lod->nag.nag_Screen;
+ retVal = 1L;
+ break;
+
+ case AMIGAGUIDE_PubScreen:
+ *(msg->opg_Storage) = (uint32)lod->nag.nag_PubScreen;
+ retVal = 1L;
+ break;
+
+ case AMIGAGUIDE_BaseName:
+ *(msg->opg_Storage) = (uint32)lod->nag.nag_BaseName;
+ retVal = 1L;
+ break;
+
+ case AMIGAGUIDE_ContextArray:
+ *(msg->opg_Storage) = (uint32)lod->nag.nag_Context;
+ retVal = 1L;
+ break;
+
+ case AMIGAGUIDE_ContextID:
+ *(msg->opg_Storage) = (uint32)lod->agContextID;
+ retVal = 1L;
+ break;
+
+ case AMIGAGUIDE_Signal:
+ *(msg->opg_Storage) = (uint32)lod->agSignal;
+ retVal = 1L;
+ break;
+
+ default:
+ retVal = IDoSuperMethodA(cl, o, (Msg)msg);
+ }
+
+ return retVal;
+
+}
+
+
+
+
+
+uint32 agm_open(Class *cl, Object *o, Msg msg)
+{
+ struct localObjectData *lod = (struct localObjectData *)INST_DATA(cl, o);
+ BOOL agActive = FALSE;
+ uint32 retVal = 0L;
+
+
+ // Close a previous instance.
+ if ( lod->agHandle ) agm_close(cl, o, msg);
+
+ // (Re)establish the AmigaGuide context and open the database asynchronously.
+ if ( (lod->agHandle = OpenAmigaGuideAsync(&(lod->nag), NULL)) )
+ {
+ if ( (lod->agSignal = AmigaGuideSignal(lod->agHandle)) )
+ {
+ // Wait until the database is displayed and ready.
+ Wait(lod->agSignal);
+ while ( agActive == FALSE )
+ {
+ while ( (lod->agm = GetAmigaGuideMsg(lod->agHandle)) )
+ {
+ // The AmigaGuide process started OK.
+ if ( lod->agm->agm_Type == ActiveToolID ) agActive = TRUE;
+
+ // Opening the guide file failed for some reason, continue as usual.
+ if ( lod->agm->agm_Type == ToolStatusID && lod->agm->agm_Pri_Ret ) agActive = TRUE;
+
+ ReplyAmigaGuideMsg(lod->agm);
+ }
+ }
+ if ( lod->nag.nag_Context )
+ {
+ // A context node array is provided = open the current context node.
+ SetAmigaGuideContext(lod->agHandle, lod->agContextID, NULL);
+ retVal = SendAmigaGuideContext(lod->agHandle, NULL);
+ }
+ else
+ {
+ // No context array is provided = open the main node.
+ retVal = SendAmigaGuideCmd(lod->agHandle, "LINK MAIN", TAG_DONE);
+ }
+ }
+ }
+
+
+ return retVal;
+}
+
+
+
+
+
+uint32 agm_close(Class *cl, Object *o, Msg msg)
+{
+ struct localObjectData *lod = (struct localObjectData *)INST_DATA(cl, o);
+ uint32 retVal = 0L;
+
+
+ if ( lod->agHandle )
+ {
+ CloseAmigaGuide(lod->agHandle);
+ lod->agHandle = NULL;
+ lod->agSignal = 0;
+ retVal = 1L;
+ }
+
+ return retVal;
+
+}
+
+
+
+
+
+
+uint32 agm_process(Class *cl, Object *o, Msg msg)
+{
+ struct localObjectData *lod = (struct localObjectData *)INST_DATA(cl, o);
+ uint32 retVal = 0L;
+
+
+ if (lod->agHandle)
+ {
+ while ( (lod->agm = GetAmigaGuideMsg(lod->agHandle)) )
+ {
+ switch (lod->agm->agm_Type)
+ {
+ case ShutdownMsgID:
+ agm_close(cl, o, msg);
+ retVal = 1L;
+ break;
+
+ default:
+ //printf("%d\n", lod->agm->agm_Type);
+ break;
+ }
+ ReplyAmigaGuideMsg(lod->agm);
+ }
+ }
+
+ return retVal;
+
+}
+
diff --git a/frontends/amiga/agclass/amigaguide_class.h b/frontends/amiga/agclass/amigaguide_class.h
new file mode 100755
index 000000000..ee7e55e60
--- /dev/null
+++ b/frontends/amiga/agclass/amigaguide_class.h
@@ -0,0 +1,43 @@
+/*
+ * AmigaGuide Class
+ *
+ */
+
+#ifndef AMIGAGUIDE_CLASS_H
+#define AMIGAGUIDE_CLASS_H
+
+#include <exec/types.h>
+#include <intuition/classes.h>
+
+#include <classes/window.h>
+
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <proto/amigaguide.h>
+#include <proto/utility.h>
+
+
+
+// tag definitions
+#define AMIGAGUIDE_Dummy (TAG_USER+0x05000000)
+
+#define AMIGAGUIDE_Name (AMIGAGUIDE_Dummy + 1) // Name of the AmigaGuide database.
+#define AMIGAGUIDE_Screen (AMIGAGUIDE_Dummy + 2) // Pointer of the screen to open on.
+#define AMIGAGUIDE_PubScreen (AMIGAGUIDE_Dummy + 3) // Name of the public screen to open on.
+#define AMIGAGUIDE_BaseName (AMIGAGUIDE_Dummy + 4) // Basename of the application that opens the help file.
+#define AMIGAGUIDE_ContextArray (AMIGAGUIDE_Dummy + 5) // Context node array (must be NULL-terminated).
+#define AMIGAGUIDE_ContextID (AMIGAGUIDE_Dummy + 6) // Index value of the node to display.
+#define AMIGAGUIDE_Signal (AMIGAGUIDE_Dummy + 7) // Signal mask to wait on
+
+// method definition
+#define AGM_Dummy AMIGAGUIDE_Dummy + 100
+#define AGM_OPEN AGM_Dummy + 1
+#define AGM_CLOSE AGM_Dummy + 2
+#define AGM_PROCESS AGM_Dummy + 3
+
+// function prototypes
+Class *initAGClass(void);
+BOOL freeAGClass(Class *);
+
+#endif // AMIGAGUIDE_CLASS_H
+
diff --git a/frontends/amiga/arexx.c b/frontends/amiga/arexx.c
new file mode 100644
index 000000000..c5b5ab42d
--- /dev/null
+++ b/frontends/amiga/arexx.c
@@ -0,0 +1,636 @@
+/*
+ * Copyright 2008-2010 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "amiga/os3support.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <proto/intuition.h>
+#include <proto/dos.h>
+#include <proto/exec.h>
+#include <proto/clicktab.h>
+#include <gadgets/clicktab.h>
+#include <reaction/reaction_macros.h>
+
+#include "utils/utils.h"
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "utils/nsurl.h"
+#include "content/hlcache.h"
+#include "desktop/browser.h"
+#include "desktop/gui_window.h"
+#include "desktop/version.h"
+
+#include "amiga/arexx.h"
+#include "amiga/download.h"
+#include "amiga/gui.h"
+#include "amiga/hotlist.h"
+#include "amiga/libs.h"
+#include "amiga/misc.h"
+#include "amiga/theme.h"
+
+extern const char * const verarexx;
+extern const char * const wt_revid;
+
+enum
+{
+ RX_OPEN=0,
+ RX_QUIT,
+ RX_TOFRONT,
+ RX_GETURL,
+ RX_GETTITLE,
+ RX_VERSION,
+ RX_SAVE,
+ RX_PUBSCREEN,
+ RX_BACK,
+ RX_FORWARD,
+ RX_HOME,
+ RX_RELOAD,
+ RX_WINDOWS,
+ RX_ACTIVE,
+ RX_CLOSE,
+ RX_HOTLIST
+};
+
+STATIC char result[100];
+
+STATIC VOID rx_open(struct ARexxCmd *, struct RexxMsg *);
+STATIC VOID rx_quit(struct ARexxCmd *, struct RexxMsg *);
+STATIC VOID rx_tofront(struct ARexxCmd *, struct RexxMsg *);
+STATIC VOID rx_geturl(struct ARexxCmd *, struct RexxMsg *);
+STATIC VOID rx_gettitle(struct ARexxCmd *, struct RexxMsg *);
+STATIC VOID rx_version(struct ARexxCmd *, struct RexxMsg *);
+STATIC VOID rx_save(struct ARexxCmd *, struct RexxMsg *);
+STATIC VOID rx_pubscreen(struct ARexxCmd *, struct RexxMsg *);
+STATIC VOID rx_back(struct ARexxCmd *, struct RexxMsg *);
+STATIC VOID rx_forward(struct ARexxCmd *, struct RexxMsg *);
+STATIC VOID rx_home(struct ARexxCmd *, struct RexxMsg *);
+STATIC VOID rx_reload(struct ARexxCmd *, struct RexxMsg *);
+STATIC VOID rx_windows(struct ARexxCmd *, struct RexxMsg *);
+STATIC VOID rx_active(struct ARexxCmd *, struct RexxMsg *);
+STATIC VOID rx_close(struct ARexxCmd *, struct RexxMsg *);
+STATIC VOID rx_hotlist(struct ARexxCmd *, struct RexxMsg *);
+
+STATIC struct ARexxCmd Commands[] =
+{
+ {"OPEN",RX_OPEN,rx_open,"URL/A,NEW=NEWWINDOW/S,NEWTAB/S,SAVEAS/K,W=WINDOW/K/N,T=TAB/K/N", 0, NULL, 0, 0, NULL },
+ {"QUIT",RX_QUIT,rx_quit,NULL, 0, NULL, 0, 0, NULL },
+ {"TOFRONT",RX_TOFRONT,rx_tofront,NULL, 0, NULL, 0, 0, NULL },
+ {"GETURL",RX_GETURL,rx_geturl, "W=WINDOW/K/N,T=TAB/K/N", 0, NULL, 0, 0, NULL },
+ {"GETTITLE",RX_GETTITLE,rx_gettitle, "W=WINDOW/K/N,T=TAB/K/N", 0, NULL, 0, 0, NULL },
+ {"VERSION",RX_VERSION,rx_version,"VERSION/N,SVN=REVISION/N,RELEASE/S", 0, NULL, 0, 0, NULL },
+ {"SAVE",RX_SAVE,rx_save,"FILENAME/A,W=WINDOW/K/N,T=TAB/K/N", 0, NULL, 0, 0, NULL },
+ {"GETSCREENNAME",RX_PUBSCREEN,rx_pubscreen,NULL, 0, NULL, 0, 0, NULL },
+ {"BACK", RX_BACK, rx_back, "W=WINDOW/K/N,T=TAB/K/N", 0, NULL, 0, 0, NULL },
+ {"FORWARD", RX_FORWARD, rx_forward, "W=WINDOW/K/N,T=TAB/K/N", 0, NULL, 0, 0, NULL },
+ {"HOME", RX_HOME, rx_home, "W=WINDOW/K/N,T=TAB/K/N", 0, NULL, 0, 0, NULL },
+ {"RELOAD", RX_RELOAD, rx_reload, "FORCE/S,W=WINDOW/K/N,T=TAB/K/N", 0, NULL, 0, 0, NULL },
+ {"WINDOWS", RX_WINDOWS, rx_windows, "W=WINDOW/K/N", 0, NULL, 0, 0, NULL },
+ {"ACTIVE", RX_ACTIVE, rx_active, "T=TAB/S", 0, NULL, 0, 0, NULL },
+ {"CLOSE", RX_CLOSE, rx_close, "W=WINDOW/K/N,T=TAB/K/N", 0, NULL, 0, 0, NULL },
+ {"HOTLIST", RX_HOTLIST, rx_hotlist, "A=ACTION/A", 0, NULL, 0, 0, NULL },
+ { NULL, 0, NULL, NULL, 0, NULL, 0, 0, NULL }
+};
+
+BOOL ami_arexx_init(void)
+{
+ if((arexx_obj = ARexxObj,
+ AREXX_HostName,"NETSURF",
+ AREXX_Commands,Commands,
+ AREXX_NoSlot,TRUE,
+ AREXX_ReplyHook,NULL,
+ AREXX_DefExtension,"nsrx",
+ End))
+ {
+ GetAttr(AREXX_SigMask, arexx_obj, &rxsig);
+ return true;
+ }
+ else
+ {
+/* Create a temporary ARexx port so we can send commands to the NetSurf which
+ * is already running */
+ arexx_obj = ARexxObj,
+ AREXX_HostName,"NETSURF",
+ AREXX_Commands,Commands,
+ AREXX_NoSlot,FALSE,
+ AREXX_ReplyHook,NULL,
+ AREXX_DefExtension,"nsrx",
+ End;
+ return false;
+ }
+}
+
+void ami_arexx_handle(void)
+{
+ RA_HandleRexx(arexx_obj);
+}
+
+void ami_arexx_execute(char *script)
+{
+ char full_script_path[1025];
+ BPTR lock;
+
+ if((lock = Lock(script, ACCESS_READ))) {
+ DevNameFromLock(lock, full_script_path, 1024, DN_FULLPATH);
+ LOG("Executing script: %s", full_script_path);
+ IDoMethod(arexx_obj, AM_EXECUTE, full_script_path, NULL, NULL, NULL, NULL, NULL);
+ UnLock(lock);
+ }
+}
+
+void ami_arexx_cleanup(void)
+{
+ if(arexx_obj) DisposeObject(arexx_obj);
+}
+
+static struct gui_window *ami_find_tab_gwin(struct gui_window_2 *gwin, int tab)
+{
+ int tabs = 0;
+ struct Node *ctab;
+ struct Node *ntab;
+ struct gui_window *gw;
+
+ if((tab == 0) || (gwin->tabs == 0)) return gwin->gw;
+
+ ctab = GetHead(&gwin->tab_list);
+
+ do
+ {
+ tabs++;
+ ntab=GetSucc(ctab);
+ GetClickTabNodeAttrs(ctab,
+ TNA_UserData, &gw,
+ TAG_DONE);
+ if(tabs == tab) return gw;
+ } while((ctab=ntab));
+
+ return NULL;
+}
+
+static int ami_find_tab_bw(struct gui_window_2 *gwin, struct browser_window *bw)
+{
+ int tabs = 0;
+ struct Node *ctab;
+ struct Node *ntab;
+ struct gui_window *tgw = NULL;
+
+ if((bw == NULL) || (gwin->tabs == 0)) return 1;
+
+ ctab = GetHead(&gwin->tab_list);
+
+ do
+ {
+ tabs++;
+ ntab=GetSucc(ctab);
+ GetClickTabNodeAttrs(ctab,
+ TNA_UserData, &tgw,
+ TAG_DONE);
+ if(tgw->bw == bw) return tabs;
+ } while((ctab=ntab));
+
+ return 0;
+}
+
+static struct gui_window *ami_find_tab(int window, int tab)
+{
+ struct nsObject *node, *nnode;
+
+ if(!IsMinListEmpty(window_list))
+ {
+ int windows = 0;
+
+ node = (struct nsObject *)GetHead((struct List *)window_list);
+
+ do
+ {
+ nnode=(struct nsObject *)GetSucc((struct Node *)node);
+
+ if(node->Type == AMINS_WINDOW)
+ {
+ windows++;
+ if(windows == window)
+ return ami_find_tab_gwin(node->objstruct, tab);
+ }
+ } while((node = nnode));
+ }
+ return NULL;
+}
+
+STATIC VOID rx_open(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unused)))
+{
+ struct dlnode *dln;
+ struct gui_window *gw = cur_gw;
+ nsurl *url;
+
+ cmd->ac_RC = 0;
+
+ if((cmd->ac_ArgList[4]) && (cmd->ac_ArgList[5]))
+ gw = ami_find_tab(*(ULONG *)cmd->ac_ArgList[4], *(ULONG *)cmd->ac_ArgList[5]);
+
+ if (nsurl_create((char *)cmd->ac_ArgList[0], &url) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", 0);
+ return;
+ }
+
+ if(cmd->ac_ArgList[3])
+ {
+ if(!gw) return;
+
+ dln = ami_misc_allocvec_clear(sizeof(struct dlnode), 0);
+ dln->filename = strdup((char *)cmd->ac_ArgList[3]);
+ dln->node.ln_Name = strdup((char *)cmd->ac_ArgList[0]);
+ dln->node.ln_Type = NT_USER;
+ AddTail(&gw->dllist, (struct Node *)dln);
+ browser_window_navigate(gw->bw,
+ url,
+ NULL,
+ BW_NAVIGATE_DOWNLOAD,
+ NULL,
+ NULL,
+ NULL);
+ }
+ else if(cmd->ac_ArgList[2])
+ {
+ browser_window_create(BW_CREATE_HISTORY |
+ BW_CREATE_TAB,
+ url,
+ NULL,
+ gw->bw,
+ NULL);
+ }
+ else if(cmd->ac_ArgList[1])
+ {
+ browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ }
+ else
+ {
+ if(gw)
+ {
+ browser_window_navigate(gw->bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ }
+ else
+ {
+ browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ }
+ }
+ nsurl_unref(url);
+}
+
+STATIC VOID rx_save(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unused)))
+{
+ BPTR fh = 0;
+ ULONG source_size;
+ const char *source_data;
+ struct gui_window *gw = cur_gw;
+
+ cmd->ac_RC = 0;
+
+ if((cmd->ac_ArgList[1]) && (cmd->ac_ArgList[2]))
+ gw = ami_find_tab(*(ULONG *)cmd->ac_ArgList[1], *(ULONG *)cmd->ac_ArgList[2]);
+
+ if(!gw) return;
+
+ ami_set_pointer(gw->shared, GUI_POINTER_WAIT, false);
+
+ if((fh = FOpen((char *)cmd->ac_ArgList[0], MODE_NEWFILE, 0)))
+ {
+ hlcache_handle *h = browser_window_get_content(gw->bw);
+ if((source_data = content_get_source_data(h, &source_size)))
+ FWrite(fh, source_data, 1, source_size);
+
+ FClose(fh);
+ SetComment((char *)cmd->ac_ArgList[0], nsurl_access(browser_window_get_url(gw->bw)));
+ }
+
+ ami_reset_pointer(gw->shared);
+}
+
+STATIC VOID rx_quit(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unused)))
+{
+ cmd->ac_RC = 0;
+ ami_quit_netsurf();
+}
+
+STATIC VOID rx_tofront(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unused)))
+{
+ cmd->ac_RC = 0;
+ ScreenToFront(scrn);
+}
+
+STATIC VOID rx_geturl(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unused)))
+{
+ struct gui_window *gw = cur_gw;
+
+ cmd->ac_RC = 0;
+
+ if((cmd->ac_ArgList[0]) && (cmd->ac_ArgList[1]))
+ gw = ami_find_tab(*(ULONG *)cmd->ac_ArgList[0], *(ULONG *)cmd->ac_ArgList[1]);
+
+ if(gw && gw->bw)
+ {
+ strcpy(result, nsurl_access(browser_window_get_url(gw->bw)));
+ }
+ else
+ {
+ strcpy(result,"");
+ }
+
+ cmd->ac_Result = result;
+}
+
+STATIC VOID rx_gettitle(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unused)))
+{
+ struct gui_window *gw = cur_gw;
+
+ cmd->ac_RC = 0;
+
+ if((cmd->ac_ArgList[0]) && (cmd->ac_ArgList[1]))
+ gw = ami_find_tab(*(ULONG *)cmd->ac_ArgList[0], *(ULONG *)cmd->ac_ArgList[1]);
+
+ if(gw)
+ {
+ if(gw->shared->tabs > 1)
+ strcpy(result, gw->tabtitle);
+ else
+ strcpy(result, gw->shared->wintitle);
+ }
+ else
+ {
+ strcpy(result,"");
+ }
+
+ cmd->ac_Result = result;
+}
+
+STATIC VOID rx_version(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unused)))
+{
+ cmd->ac_RC = 0;
+
+ if(cmd->ac_ArgList[2])
+ {
+ if(cmd->ac_ArgList[1])
+ {
+ if((netsurf_version_major > *(int *)cmd->ac_ArgList[0]) || ((netsurf_version_minor >= *(int *)cmd->ac_ArgList[1]) && (netsurf_version_major == *(int *)cmd->ac_ArgList[0])))
+ {
+ strcpy(result,"1");
+ }
+ else
+ {
+ strcpy(result,"0");
+ }
+ }
+ else if(cmd->ac_ArgList[0])
+ {
+ if((netsurf_version_major >= *(int *)cmd->ac_ArgList[0]))
+ {
+ strcpy(result,"1");
+ }
+ else
+ {
+ strcpy(result,"0");
+ }
+ }
+ else
+ {
+ strcpy(result,netsurf_version);
+ }
+ }
+ else
+ {
+ if(cmd->ac_ArgList[1])
+ {
+ if((netsurf_version_major > *(int *)cmd->ac_ArgList[0]) || ((atoi(wt_revid) >= *(int *)cmd->ac_ArgList[1]) && (netsurf_version_major == *(int *)cmd->ac_ArgList[0])))
+ {
+ strcpy(result,"1");
+ }
+ else
+ {
+ strcpy(result,"0");
+ }
+ }
+ else if(cmd->ac_ArgList[0])
+ {
+ if((netsurf_version_major >= *(int *)cmd->ac_ArgList[0]))
+ {
+ strcpy(result,"1");
+ }
+ else
+ {
+ strcpy(result,"0");
+ }
+ }
+ else
+ {
+ strcpy(result,verarexx);
+ }
+ }
+
+ cmd->ac_Result = result;
+}
+
+STATIC VOID rx_pubscreen(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unused)))
+{
+ cmd->ac_RC = 0;
+
+ if(nsoption_charp(pubscreen_name) == NULL)
+ {
+ strcpy(result,"NetSurf");
+ }
+ else
+ {
+ strcpy(result, nsoption_charp(pubscreen_name));
+ }
+
+ cmd->ac_Result = result;
+}
+
+STATIC VOID rx_back(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unused)))
+{
+ struct gui_window *gw = cur_gw;
+
+ cmd->ac_RC = 0;
+
+ if((cmd->ac_ArgList[0]) && (cmd->ac_ArgList[1]))
+ gw = ami_find_tab(*(ULONG *)cmd->ac_ArgList[0], *(ULONG *)cmd->ac_ArgList[1]);
+
+ if(gw) ami_gui_history(gw->shared, true);
+}
+
+STATIC VOID rx_forward(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unused)))
+{
+ struct gui_window *gw = cur_gw;
+
+ cmd->ac_RC = 0;
+
+ if((cmd->ac_ArgList[0]) && (cmd->ac_ArgList[1]))
+ gw = ami_find_tab(*(ULONG *)cmd->ac_ArgList[0], *(ULONG *)cmd->ac_ArgList[1]);
+
+ if(gw) ami_gui_history(gw->shared, false);
+
+}
+
+STATIC VOID rx_home(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unused)))
+{
+ struct gui_window *gw = cur_gw;
+ nsurl *url;
+
+ cmd->ac_RC = 0;
+
+ if((cmd->ac_ArgList[0]) && (cmd->ac_ArgList[1]))
+ gw = ami_find_tab(*(ULONG *)cmd->ac_ArgList[0], *(ULONG *)cmd->ac_ArgList[1]);
+
+ if(gw == NULL) return;
+
+ if (nsurl_create(nsoption_charp(homepage_url), &url) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", 0);
+ } else {
+ browser_window_navigate(gw->bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+}
+
+STATIC VOID rx_reload(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unused)))
+{
+ struct gui_window *gw = cur_gw;
+
+ cmd->ac_RC = 0;
+
+ if((cmd->ac_ArgList[1]) && (cmd->ac_ArgList[2]))
+ gw = ami_find_tab(*(ULONG *)cmd->ac_ArgList[1], *(ULONG *)cmd->ac_ArgList[2]);
+
+ if(gw)
+ {
+ if(cmd->ac_ArgList[0]) /* FORCE */
+ {
+ browser_window_reload(gw->bw, true);
+ }
+ else
+ {
+ browser_window_reload(gw->bw, false);
+ }
+ }
+}
+
+STATIC VOID rx_windows(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unused)))
+{
+ int windows = 0, tabs = 0;
+ int window = 0;
+
+ if(cmd->ac_ArgList[0]) window = *(ULONG *)cmd->ac_ArgList[0];
+ cmd->ac_RC = 0;
+
+ windows = ami_gui_count_windows(window, &tabs);
+
+ if(cmd->ac_ArgList[0]) sprintf(result, "%d", tabs);
+ else sprintf(result, "%d", windows);
+ cmd->ac_Result = result;
+}
+
+STATIC VOID rx_active(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unused)))
+{
+ int window = 0, tab = 0;
+ struct gui_window *gw = cur_gw;
+ struct nsObject *node, *nnode;
+ struct gui_window_2 *gwin = NULL;
+
+ cmd->ac_RC = 0;
+
+ if(!IsMinListEmpty(window_list))
+ {
+ int windows = 0;
+
+ node = (struct nsObject *)GetHead((struct List *)window_list);
+
+ do
+ {
+ nnode=(struct nsObject *)GetSucc((struct Node *)node);
+
+ gwin = node->objstruct;
+
+ if(node->Type == AMINS_WINDOW)
+ {
+ windows++;
+ if(gwin->gw == gw)
+ {
+ window = windows;
+ break;
+ }
+ }
+ } while((node = nnode));
+ }
+
+ if(cmd->ac_ArgList[0])
+ {
+ tab = ami_find_tab_bw(gwin, gw->bw);
+ }
+
+ if(cmd->ac_ArgList[0]) sprintf(result, "%d", tab);
+ else sprintf(result, "%d", window);
+ cmd->ac_Result = result;
+}
+
+STATIC VOID rx_close(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unused)))
+{
+ struct gui_window *gw = cur_gw;
+
+ cmd->ac_RC = 0;
+
+ if((cmd->ac_ArgList[0]) && (cmd->ac_ArgList[1]))
+ gw = ami_find_tab(*(ULONG *)cmd->ac_ArgList[0], *(ULONG *)cmd->ac_ArgList[1]);
+ else if(cmd->ac_ArgList[0])
+ {
+ ami_gui_close_window(gw->shared);
+ return;
+ }
+
+ if(gw) browser_window_destroy(gw->bw);
+}
+
+STATIC VOID rx_hotlist(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unused)))
+{
+ cmd->ac_RC = 0;
+
+ if(strcasecmp((char *)cmd->ac_ArgList[0], "OPEN") == 0) {
+ ami_tree_open(hotlist_window, AMI_TREE_HOTLIST);
+ } else if(strcasecmp((char *)cmd->ac_ArgList[0], "CLOSE") == 0) {
+ ami_tree_close(hotlist_window);
+ }
+}
+
diff --git a/frontends/amiga/arexx.h b/frontends/amiga/arexx.h
new file mode 100755
index 000000000..358d174f3
--- /dev/null
+++ b/frontends/amiga/arexx.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2008 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_AREXX_H
+#define AMIGA_AREXX_H
+
+#include <proto/arexx.h>
+#include <classes/arexx.h>
+
+BOOL ami_arexx_init(void);
+void ami_arexx_handle(void);
+void ami_arexx_execute(char *);
+void ami_arexx_cleanup(void);
+
+Object *arexx_obj;
+ULONG rxsig;
+#endif
diff --git a/frontends/amiga/bitmap.c b/frontends/amiga/bitmap.c
new file mode 100644
index 000000000..d04857262
--- /dev/null
+++ b/frontends/amiga/bitmap.c
@@ -0,0 +1,706 @@
+/*
+ * Copyright 2008, 2009, 2012, 2016 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "amiga/os3support.h"
+
+#include <stdlib.h>
+#include <proto/exec.h>
+#ifdef __amigaos4__
+#include <graphics/blitattr.h>
+#include <graphics/composite.h>
+#endif
+#include <graphics/gfxbase.h>
+#include <proto/datatypes.h>
+#include <datatypes/pictureclass.h>
+#include <proto/dos.h>
+#include <proto/intuition.h>
+#include <proto/utility.h>
+
+#include <proto/guigfx.h>
+#include <guigfx/guigfx.h>
+#include <render/render.h>
+#ifndef __amigaos4__
+#include <inline/guigfx.h>
+#endif
+
+#ifdef __amigaos4__
+#include <sys/param.h>
+#endif
+#include "assert.h"
+
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "utils/nsurl.h"
+#include "utils/messages.h"
+#include "content/hlcache.h"
+#include "desktop/mouse.h"
+#include "desktop/gui_window.h"
+#include "image/bitmap.h"
+
+#include "amiga/gui.h"
+#include "amiga/bitmap.h"
+#include "amiga/download.h"
+#include "amiga/misc.h"
+#include "amiga/rtg.h"
+
+struct bitmap {
+ int width;
+ int height;
+ UBYTE *pixdata;
+ bool opaque;
+ int native;
+ struct BitMap *nativebm;
+ int nativebmwidth;
+ int nativebmheight;
+ PLANEPTR native_mask;
+ Object *dto;
+ APTR drawhandle;
+ struct nsurl *url; /* temporary storage space */
+ char *title; /* temporary storage space */
+ ULONG *icondata; /* for appicons */
+};
+
+enum {
+ AMI_NSBM_NONE = 0,
+ AMI_NSBM_TRUECOLOUR,
+ AMI_NSBM_PALETTEMAPPED
+};
+
+APTR pool_bitmap = NULL;
+
+/* exported function documented in amiga/bitmap.h */
+void *amiga_bitmap_create(int width, int height, unsigned int state)
+{
+ struct bitmap *bitmap;
+
+ if(pool_bitmap == NULL) pool_bitmap = ami_misc_itempool_create(sizeof(struct bitmap));
+
+ bitmap = ami_misc_itempool_alloc(pool_bitmap, sizeof(struct bitmap));
+ if(bitmap == NULL) return NULL;
+
+ bitmap->pixdata = ami_misc_allocvec_clear(width*height*4, 0xff);
+ bitmap->width = width;
+ bitmap->height = height;
+
+ if(state & BITMAP_OPAQUE) bitmap->opaque = true;
+ else bitmap->opaque = false;
+
+ bitmap->nativebm = NULL;
+ bitmap->nativebmwidth = 0;
+ bitmap->nativebmheight = 0;
+ bitmap->native_mask = NULL;
+ bitmap->drawhandle = NULL;
+ bitmap->url = NULL;
+ bitmap->title = NULL;
+ bitmap->icondata = NULL;
+ bitmap->native = AMI_NSBM_NONE;
+
+ return bitmap;
+}
+
+
+/* exported function documented in amiga/bitmap.h */
+unsigned char *amiga_bitmap_get_buffer(void *bitmap)
+{
+ struct bitmap *bm = bitmap;
+ return bm->pixdata;
+}
+
+/* exported function documented in amiga/bitmap.h */
+size_t amiga_bitmap_get_rowstride(void *bitmap)
+{
+ struct bitmap *bm = bitmap;
+
+ if(bm)
+ {
+ return ((bm->width)*4);
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+/* exported function documented in amiga/bitmap.h */
+void amiga_bitmap_destroy(void *bitmap)
+{
+ struct bitmap *bm = bitmap;
+
+ if(bm)
+ {
+ if((bm->nativebm)) { // && (bm->native == AMI_NSBM_TRUECOLOUR)) {
+ ami_rtg_freebitmap(bm->nativebm);
+ }
+
+ if(bm->native_mask) FreeRaster(bm->native_mask, bm->width, bm->height);
+ if(bm->drawhandle) ReleaseDrawHandle(bm->drawhandle);
+ FreeVec(bm->pixdata);
+
+ if(bm->url) nsurl_unref(bm->url);
+ if(bm->title) free(bm->title);
+
+ bm->pixdata = NULL;
+ bm->nativebm = NULL;
+ bm->native_mask = NULL;
+ bm->drawhandle = NULL;
+ bm->url = NULL;
+ bm->title = NULL;
+
+ ami_misc_itempool_free(pool_bitmap, bm, sizeof(struct bitmap));
+ bm = NULL;
+ }
+}
+
+
+/* exported function documented in amiga/bitmap.h */
+bool amiga_bitmap_save(void *bitmap, const char *path, unsigned flags)
+{
+ int err = 0;
+ Object *dto = NULL;
+
+ if((dto = ami_datatype_object_from_bitmap(bitmap)))
+ {
+ if (flags & AMI_BITMAP_SCALE_ICON) {
+ IDoMethod(dto, PDTM_SCALE, 16, 16, 0);
+
+ if((DoDTMethod(dto, 0, 0, DTM_PROCLAYOUT, 0, 1)) == 0) {
+ return false;
+ }
+ }
+
+ err = SaveDTObjectA(dto, NULL, NULL, path, DTWM_IFF, FALSE, NULL);
+ DisposeDTObject(dto);
+ }
+
+ if(err == 0) return false;
+ else return true;
+}
+
+
+/* exported function documented in amiga/bitmap.h */
+void amiga_bitmap_modified(void *bitmap)
+{
+ struct bitmap *bm = bitmap;
+
+ if((bm->nativebm)) // && (bm->native == AMI_NSBM_TRUECOLOUR))
+ ami_rtg_freebitmap(bm->nativebm);
+
+ if(bm->drawhandle) ReleaseDrawHandle(bm->drawhandle);
+ if(bm->native_mask) FreeRaster(bm->native_mask, bm->width, bm->height);
+ bm->nativebm = NULL;
+ bm->drawhandle = NULL;
+ bm->native_mask = NULL;
+ bm->native = AMI_NSBM_NONE;
+}
+
+
+/* exported function documented in amiga/bitmap.h */
+void amiga_bitmap_set_opaque(void *bitmap, bool opaque)
+{
+ struct bitmap *bm = bitmap;
+ assert(bitmap);
+ bm->opaque = opaque;
+}
+
+
+/* exported function documented in amiga/bitmap.h */
+bool amiga_bitmap_test_opaque(void *bitmap)
+{
+ struct bitmap *bm = bitmap;
+ uint32 p = bm->width * bm->height;
+ uint32 a = 0;
+ uint32 *bmi = (uint32 *)amiga_bitmap_get_buffer(bm);
+
+ assert(bitmap);
+
+ for(a=0;a<p;a+=4)
+ {
+ if ((*bmi & 0x000000ffU) != 0x000000ffU) return false;
+ bmi++;
+ }
+ return true;
+}
+
+
+/* exported function documented in amiga/bitmap.h */
+bool amiga_bitmap_get_opaque(void *bitmap)
+{
+ struct bitmap *bm = bitmap;
+ assert(bitmap);
+ return bm->opaque;
+}
+
+/**
+ * get width of a bitmap.
+ */
+int bitmap_get_width(void *bitmap)
+{
+ struct bitmap *bm = bitmap;
+
+ if(bm)
+ {
+ return(bm->width);
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/**
+ * get height of a bitmap.
+ */
+int bitmap_get_height(void *bitmap)
+{
+ struct bitmap *bm = bitmap;
+
+ if(bm)
+ {
+ return(bm->height);
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+/**
+ * Find the bytes per pixel of a bitmap
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \return bytes per pixel
+ */
+static size_t bitmap_get_bpp(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *)vbitmap;
+ assert(bitmap);
+ return 4;
+}
+
+static void ami_bitmap_argb_to_rgba(struct bitmap *bm)
+{
+ if(bm == NULL) return;
+
+ ULONG *data = (ULONG *)amiga_bitmap_get_buffer(bm);
+ for(int i = 0; i < (bm->width * bm->height); i++) {
+ data[i] = (data[i] << 8) | (data[i] >> 24);
+ }
+}
+
+static void ami_bitmap_rgba_to_argb(struct bitmap *bm)
+{
+ if(bm == NULL) return;
+
+ ULONG *data = (ULONG *)amiga_bitmap_get_buffer(bm);
+ for(int i = 0; i < (bm->width * bm->height); i++) {
+ data[i] = (data[i] >> 8) | (data[i] << 24);
+ }
+}
+
+#ifdef BITMAP_DUMP
+void bitmap_dump(struct bitmap *bitmap)
+{
+ int x,y;
+ ULONG *bm = (ULONG *)amiga_bitmap_get_buffer(bitmap);
+
+ printf("Width=%ld, Height=%ld, Opaque=%s\nnativebm=%lx, width=%ld, height=%ld\n",
+ bitmap->width, bitmap->height, bitmap->opaque ? "true" : "false",
+ bitmap->nativebm, bitmap->nativebmwidth, bitmap->nativebmheight);
+
+ for(y = 0; y < bitmap->height; y++) {
+ for(x = 0; x < bitmap->width; x++) {
+ printf("%lx ", bm[(y*bitmap->width) + x]);
+ }
+ printf("\n");
+ }
+}
+#endif
+
+Object *ami_datatype_object_from_bitmap(struct bitmap *bitmap)
+{
+ Object *dto;
+ struct BitMapHeader *bmhd;
+
+ if((dto = NewDTObject(NULL,
+ DTA_SourceType,DTST_RAM,
+ DTA_GroupID,GID_PICTURE,
+ //DTA_BaseName,"ilbm",
+ PDTA_DestMode,PMODE_V43,
+ TAG_DONE)))
+ {
+ if(GetDTAttrs(dto,PDTA_BitMapHeader,&bmhd,TAG_DONE))
+ {
+ bmhd->bmh_Width = (UWORD)bitmap_get_width(bitmap);
+ bmhd->bmh_Height = (UWORD)bitmap_get_height(bitmap);
+ bmhd->bmh_Depth = (UBYTE)bitmap_get_bpp(bitmap) * 8;
+ if(!amiga_bitmap_get_opaque(bitmap)) bmhd->bmh_Masking = mskHasAlpha;
+ }
+
+ SetDTAttrs(dto,NULL,NULL,
+ DTA_ObjName, bitmap->url ? nsurl_access(bitmap->url) : "",
+ DTA_ObjAnnotation,bitmap->title,
+ DTA_ObjAuthor,messages_get("NetSurf"),
+ DTA_NominalHoriz,bitmap_get_width(bitmap),
+ DTA_NominalVert,bitmap_get_height(bitmap),
+ PDTA_SourceMode,PMODE_V43,
+ TAG_DONE);
+
+ IDoMethod(dto, PDTM_WRITEPIXELARRAY, amiga_bitmap_get_buffer(bitmap),
+ PBPAFMT_RGBA, amiga_bitmap_get_rowstride(bitmap), 0, 0,
+ bitmap_get_width(bitmap), bitmap_get_height(bitmap));
+ }
+
+ return dto;
+}
+
+/* Quick way to get an object on disk into a struct bitmap */
+struct bitmap *ami_bitmap_from_datatype(char *filename)
+{
+ Object *dto;
+ struct bitmap *bm = NULL;
+
+ if((dto = NewDTObject(filename,
+ DTA_GroupID, GID_PICTURE,
+ PDTA_DestMode, PMODE_V43,
+ PDTA_PromoteMask, TRUE,
+ TAG_DONE))) {
+ struct BitMapHeader *bmh;
+
+ if(GetDTAttrs(dto, PDTA_BitMapHeader, &bmh, TAG_DONE))
+ {
+ bm = amiga_bitmap_create(bmh->bmh_Width, bmh->bmh_Height, 0);
+
+ IDoMethod(dto, PDTM_READPIXELARRAY, amiga_bitmap_get_buffer(bm),
+ PBPAFMT_RGBA, amiga_bitmap_get_rowstride(bm), 0, 0,
+ bmh->bmh_Width, bmh->bmh_Height);
+
+ amiga_bitmap_set_opaque(bm, amiga_bitmap_test_opaque(bm));
+ }
+ DisposeDTObject(dto);
+ }
+
+ return bm;
+}
+
+static inline struct BitMap *ami_bitmap_get_generic(struct bitmap *bitmap, int width, int height, struct BitMap *friendbm, int type)
+{
+ struct BitMap *tbm = NULL;
+
+ if(bitmap->nativebm)
+ {
+ if((bitmap->nativebmwidth == width) && (bitmap->nativebmheight == height)) {
+ tbm = bitmap->nativebm;
+ return tbm;
+ } else if((bitmap->nativebmwidth == bitmap->width) &&
+ (bitmap->nativebmheight == bitmap->height)) { // >= width/height ?
+ tbm = bitmap->nativebm;
+ } else {
+ if(bitmap->nativebm) amiga_bitmap_modified(bitmap);
+ }
+ }
+
+ if(tbm == NULL) {
+ if(type == AMI_NSBM_TRUECOLOUR) {
+ if((tbm = ami_rtg_allocbitmap(bitmap->width, bitmap->height, 32, 0,
+ friendbm, AMI_BITMAP_FORMAT))) {
+ ami_rtg_writepixelarray(amiga_bitmap_get_buffer(bitmap),
+ tbm, bitmap->width, bitmap->height,
+ bitmap->width * 4, AMI_BITMAP_FORMAT);
+ }
+ } else {
+ if((tbm = ami_rtg_allocbitmap(bitmap->width, bitmap->height,
+ 8, 0, friendbm, AMI_BITMAP_FORMAT))) {
+
+ struct RastPort rp;
+ InitRastPort(&rp);
+ rp.BitMap = tbm;
+ ULONG dithermode = DITHERMODE_NONE;
+
+ if(nsoption_int(dither_quality) == 1) {
+ dithermode = DITHERMODE_EDD;
+ } else if(nsoption_int(dither_quality) == 2) {
+ dithermode = DITHERMODE_FS;
+ }
+
+ ami_bitmap_rgba_to_argb(bitmap);
+ bitmap->drawhandle = ObtainDrawHandle(NULL,
+ &rp, scrn->ViewPort.ColorMap,
+ GGFX_DitherMode, dithermode,
+ TAG_DONE);
+
+ APTR ddh = CreateDirectDrawHandle(bitmap->drawhandle,
+ bitmap->width, bitmap->height,
+ bitmap->width, bitmap->height, NULL);
+
+ DirectDrawTrueColor(ddh, (ULONG *)amiga_bitmap_get_buffer(bitmap), 0, 0, TAG_DONE);
+ DeleteDirectDrawHandle(ddh);
+ ami_bitmap_argb_to_rgba(bitmap);
+ }
+ }
+
+ if(nsoption_int(cache_bitmaps) == 2)
+ {
+ bitmap->nativebm = tbm;
+ bitmap->nativebmwidth = bitmap->width;
+ bitmap->nativebmheight = bitmap->height;
+ bitmap->native = type;
+ }
+ }
+
+ if((bitmap->width != width) || (bitmap->height != height))
+ {
+ struct BitMap *scaledbm;
+ struct BitScaleArgs bsa;
+ int depth = 32;
+ if(type == AMI_NSBM_PALETTEMAPPED) depth = 8;
+
+ scaledbm = ami_rtg_allocbitmap(width, height, depth, 0,
+ friendbm, AMI_BITMAP_FORMAT);
+#ifdef __amigaos4__
+ if(__builtin_expect(((GfxBase->LibNode.lib_Version >= 53) &&
+ (type == AMI_NSBM_TRUECOLOUR)), 1)) {
+ /* AutoDoc says v52, but this function isn't in OS4.0, so checking for v53 (OS4.1)
+ * Additionally, when we use friend BitMaps in non 32-bit modes it freezes the OS */
+ uint32 flags = 0;
+ if(nsoption_bool(scale_quality)) flags |= COMPFLAG_SrcFilter;
+
+ CompositeTags(COMPOSITE_Src, tbm, scaledbm,
+ COMPTAG_ScaleX, COMP_FLOAT_TO_FIX((float)width/bitmap->width),
+ COMPTAG_ScaleY, COMP_FLOAT_TO_FIX((float)height/bitmap->height),
+ COMPTAG_Flags, flags,
+ COMPTAG_DestX, 0,
+ COMPTAG_DestY, 0,
+ COMPTAG_DestWidth, width,
+ COMPTAG_DestHeight, height,
+ COMPTAG_OffsetX, 0,
+ COMPTAG_OffsetY, 0,
+ COMPTAG_FriendBitMap, scrn->RastPort.BitMap,
+ TAG_DONE);
+ } else /* Do it the old-fashioned way. This is pretty slow, even on OS4.1 */
+#endif
+ {
+ bsa.bsa_SrcX = 0;
+ bsa.bsa_SrcY = 0;
+ bsa.bsa_SrcWidth = bitmap->width;
+ bsa.bsa_SrcHeight = bitmap->height;
+ bsa.bsa_DestX = 0;
+ bsa.bsa_DestY = 0;
+ bsa.bsa_XSrcFactor = bitmap->width;
+ bsa.bsa_XDestFactor = width;
+ bsa.bsa_YSrcFactor = bitmap->height;
+ bsa.bsa_YDestFactor = height;
+ bsa.bsa_SrcBitMap = tbm;
+ bsa.bsa_DestBitMap = scaledbm;
+ bsa.bsa_Flags = 0;
+
+ BitMapScale(&bsa);
+ }
+
+ if(bitmap->nativebm != tbm) ami_rtg_freebitmap(bitmap->nativebm);
+ ami_rtg_freebitmap(tbm);
+ tbm = scaledbm;
+ bitmap->nativebm = NULL;
+ bitmap->native = AMI_NSBM_NONE;
+
+ if(nsoption_int(cache_bitmaps) >= 1)
+ {
+ bitmap->nativebm = tbm;
+ bitmap->nativebmwidth = width;
+ bitmap->nativebmheight = height;
+ bitmap->native = type;
+ }
+ }
+
+ return tbm;
+}
+
+
+static inline struct BitMap *ami_bitmap_get_truecolour(struct bitmap *bitmap,int width,int height,struct BitMap *friendbm)
+{
+ if((bitmap->native != AMI_NSBM_NONE) && (bitmap->native != AMI_NSBM_TRUECOLOUR)) {
+ amiga_bitmap_modified(bitmap);
+ }
+
+ return ami_bitmap_get_generic(bitmap, width, height, friendbm, AMI_NSBM_TRUECOLOUR);
+}
+
+PLANEPTR ami_bitmap_get_mask(struct bitmap *bitmap, int width,
+ int height, struct BitMap *n_bm)
+{
+ uint32 *bmi = (uint32 *) amiga_bitmap_get_buffer(bitmap);
+ UBYTE maskbit = 0;
+ ULONG bm_width;
+ int y, x, bpr;
+
+ if((height != bitmap->height) || (width != bitmap->width)) return NULL;
+ if(amiga_bitmap_get_opaque(bitmap) == true) return NULL;
+ if(bitmap->native_mask) return bitmap->native_mask;
+
+ bm_width = GetBitMapAttr(n_bm, BMA_WIDTH);
+ bpr = RASSIZE(bm_width, 1);
+ bitmap->native_mask = AllocRaster(bm_width, height);
+ SetMem(bitmap->native_mask, 0, bpr * height);
+
+ for(y=0; y<height; y++) {
+ for(x=0; x<width; x++) {
+ if ((*bmi & 0x000000ffU) <= (ULONG)nsoption_int(mask_alpha)) maskbit = 0;
+ else maskbit = 1;
+ bmi++;
+ bitmap->native_mask[(y*bpr) + (x/8)] |=
+ maskbit << (7 - (x % 8));
+ }
+ }
+
+ return bitmap->native_mask;
+}
+
+static inline struct BitMap *ami_bitmap_get_palettemapped(struct bitmap *bitmap,
+ int width, int height, struct BitMap *friendbm)
+{
+ if((bitmap->native != AMI_NSBM_NONE) && (bitmap->native != AMI_NSBM_PALETTEMAPPED)) {
+ amiga_bitmap_modified(bitmap);
+ }
+
+ return ami_bitmap_get_generic(bitmap, width, height, friendbm, AMI_NSBM_PALETTEMAPPED);
+}
+
+struct BitMap *ami_bitmap_get_native(struct bitmap *bitmap,
+ int width, int height, struct BitMap *friendbm)
+{
+ if(bitmap == NULL) return NULL;
+
+ if(__builtin_expect(ami_plot_screen_is_palettemapped() == true, 0)) {
+ return ami_bitmap_get_palettemapped(bitmap, width, height, friendbm);
+ } else {
+ return ami_bitmap_get_truecolour(bitmap, width, height, friendbm);
+ }
+}
+
+void ami_bitmap_fini(void)
+{
+ if(pool_bitmap) ami_misc_itempool_delete(pool_bitmap);
+ pool_bitmap = NULL;
+}
+
+static nserror bitmap_render(struct bitmap *bitmap, hlcache_handle *content)
+{
+#ifdef __amigaos4__
+ struct redraw_context ctx = {
+ .interactive = false,
+ .background_images = true,
+ .plot = &amiplot
+ };
+
+ int plot_width;
+ int plot_height;
+ struct gui_globals bm_globals;
+ struct gui_globals *temp_gg = glob;
+
+ plot_width = MIN(content_get_width(content), bitmap->width);
+ plot_height = ((plot_width * bitmap->height) + (bitmap->width / 2)) /
+ bitmap->width;
+
+ ami_init_layers(&bm_globals, bitmap->width, bitmap->height, true);
+ bm_globals.shared_pens = NULL;
+
+ glob = &bm_globals;
+ ami_clearclipreg(&bm_globals);
+
+ content_scaled_redraw(content, plot_width, plot_height, &ctx);
+
+ BltBitMapTags( BLITA_SrcX, 0,
+ BLITA_SrcY, 0,
+ BLITA_Width, bitmap->width,
+ BLITA_Height, bitmap->height,
+ BLITA_Source, bm_globals.bm,
+ BLITA_SrcType, BLITT_BITMAP,
+ BLITA_Dest, amiga_bitmap_get_buffer(bitmap),
+ BLITA_DestType, BLITT_ARGB32,
+ BLITA_DestBytesPerRow, 4 * bitmap->width,
+ BLITA_DestX, 0,
+ BLITA_DestY, 0,
+ TAG_DONE);
+
+ ami_bitmap_argb_to_rgba(bitmap);
+
+ /**\todo In theory we should be able to move the bitmap to our native area
+ to try to avoid re-conversion (at the expense of memory) */
+
+ ami_free_layers(&bm_globals);
+ amiga_bitmap_set_opaque(bitmap, true);
+
+ /* Restore previous render area. This is set when plotting starts,
+ * but if bitmap_render is called *during* a browser render then
+ * having an invalid pointer here causes NetSurf to crash.
+ */
+ glob = temp_gg;
+#else
+#warning FIXME for OS3 (in current state none of bitmap_render can work!)
+#endif
+
+ return NSERROR_OK;
+}
+
+void ami_bitmap_set_url(struct bitmap *bm, struct nsurl *url)
+{
+ if(bm->url != NULL) return;
+ bm->url = nsurl_ref(url);
+}
+
+void ami_bitmap_set_title(struct bitmap *bm, const char *title)
+{
+ if(bm->title != NULL) return;
+ bm->title = strdup(title);
+}
+
+ULONG *ami_bitmap_get_icondata(struct bitmap *bm)
+{
+ return bm->icondata;
+}
+
+void ami_bitmap_set_icondata(struct bitmap *bm, ULONG *icondata)
+{
+ bm->icondata = icondata;
+}
+
+bool ami_bitmap_is_nativebm(struct bitmap *bm, struct BitMap *nbm)
+{
+ if(bm->nativebm == nbm) return true;
+ else return false;
+}
+
+
+static struct gui_bitmap_table bitmap_table = {
+ .create = amiga_bitmap_create,
+ .destroy = amiga_bitmap_destroy,
+ .set_opaque = amiga_bitmap_set_opaque,
+ .get_opaque = amiga_bitmap_get_opaque,
+ .test_opaque = amiga_bitmap_test_opaque,
+ .get_buffer = amiga_bitmap_get_buffer,
+ .get_rowstride = amiga_bitmap_get_rowstride,
+ .get_width = bitmap_get_width,
+ .get_height = bitmap_get_height,
+ .get_bpp = bitmap_get_bpp,
+ .save = amiga_bitmap_save,
+ .modified = amiga_bitmap_modified,
+ .render = bitmap_render,
+};
+
+struct gui_bitmap_table *amiga_bitmap_table = &bitmap_table;
diff --git a/frontends/amiga/bitmap.h b/frontends/amiga/bitmap.h
new file mode 100755
index 000000000..d7dd70bce
--- /dev/null
+++ b/frontends/amiga/bitmap.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2008,2009 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_BITMAP_H
+#define AMIGA_BITMAP_H
+
+#include <stdbool.h>
+#include <exec/types.h>
+#include <proto/graphics.h>
+#include <intuition/classusr.h>
+#include <libraries/Picasso96.h>
+
+#define AMI_BITMAP_FORMAT RGBFB_R8G8B8A8
+#define AMI_BITMAP_SCALE_ICON 0xFF
+
+struct gui_bitmap_table *amiga_bitmap_table;
+struct bitmap;
+struct nsurl;
+
+struct BitMap *ami_bitmap_get_native(struct bitmap *bitmap,
+ int width, int height, struct BitMap *friendbm);
+PLANEPTR ami_bitmap_get_mask(struct bitmap *bitmap, int width,
+ int height, struct BitMap *n_bm);
+
+Object *ami_datatype_object_from_bitmap(struct bitmap *bitmap);
+struct bitmap *ami_bitmap_from_datatype(char *filename);
+
+/**
+ * Set bitmap URL
+ *
+ * \param bm a bitmap, as returned by bitmap_create()
+ * \param title a pointer to a title string
+ *
+ * A reference will be kept by the bitmap object.
+ * The URL can only ever be set once for a bitmap.
+ */
+void ami_bitmap_set_url(struct bitmap *bm, struct nsurl *url);
+
+/**
+ * Set bitmap title
+ *
+ * \param bm a bitmap, as returned by bitmap_create()
+ * \param title a pointer to a title string
+ *
+ * This is copied by the bitmap object.
+ * The title can only ever be set once for a bitmap.
+ */
+void ami_bitmap_set_title(struct bitmap *bm, const char *title);
+
+/**
+ * Get an icondata pointer
+ *
+ * \param bm a bitmap, as returned by bitmap_create()
+ * \return pointer to the icondata area
+ *
+ * This function probably shouldn't be here!
+ */
+ULONG *ami_bitmap_get_icondata(struct bitmap *bm);
+
+/**
+ * Set an icondata pointer
+ *
+ * \param bm a bitmap, as returned by bitmap_create()
+ * \param icondata a pointer to memory
+ *
+ * This function probably shouldn't be here!
+ */
+void ami_bitmap_set_icondata(struct bitmap *bm, ULONG *icondata);
+
+/**
+ * Test if a BitMap is owned by a bitmap.
+ *
+ * \param bm a bitmap, as returned by bitmap_create()
+ * \param nbm a BitMap, as created by AllocBitMap()
+ * \return true if the BitMap is owned by the bitmap
+ */
+bool ami_bitmap_is_nativebm(struct bitmap *bm, struct BitMap *nbm);
+
+/**
+ * Cleanup bitmap allocations
+ */
+void ami_bitmap_fini(void);
+
+/**
+ * Create a bitmap.
+ *
+ * \param width width of image in pixels
+ * \param height width of image in pixels
+ * \param state a flag word indicating the initial state
+ * \return an opaque struct bitmap, or NULL on memory exhaustion
+ */
+void *amiga_bitmap_create(int width, int height, unsigned int state);
+
+/**
+ * Return a pointer to the pixel data in a bitmap.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ * \return pointer to the pixel buffer
+ *
+ * The pixel data is packed as BITMAP_FORMAT, possibly with padding at the end
+ * of rows. The width of a row in bytes is given by bitmap_get_rowstride().
+ */
+unsigned char *amiga_bitmap_get_buffer(void *bitmap);
+
+/**
+ * Find the width of a pixel row in bytes.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ * \return width of a pixel row in the bitmap
+ */
+size_t amiga_bitmap_get_rowstride(void *bitmap);
+
+/**
+ * Return the width of a bitmap.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ * \return width in pixels
+ */
+int bitmap_get_width(void *bitmap);
+
+/**
+ * Return the height of a bitmap.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ * \return height in pixels
+ */
+int bitmap_get_height(void *bitmap);
+
+/**
+ * Free a bitmap.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ */
+void amiga_bitmap_destroy(void *bitmap);
+
+/**
+ * Save a bitmap in the platform's native format.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ * \param path pathname for file
+ * \param flags flags controlling how the bitmap is saved.
+ * \return true on success, false on error and error reported
+ */
+bool amiga_bitmap_save(void *bitmap, const char *path, unsigned flags);
+
+/**
+ * The bitmap image has changed, so flush any persistant cache.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ */
+void amiga_bitmap_modified(void *bitmap);
+
+/**
+ * Sets whether a bitmap should be plotted opaque
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ * \param opaque whether the bitmap should be plotted opaque
+ */
+void amiga_bitmap_set_opaque(void *bitmap, bool opaque);
+
+/**
+ * Tests whether a bitmap has an opaque alpha channel
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ * \return whether the bitmap is opaque
+ */
+bool amiga_bitmap_test_opaque(void *bitmap);
+
+/**
+ * Gets whether a bitmap should be plotted opaque
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ */
+bool amiga_bitmap_get_opaque(void *bitmap);
+
+
+#endif
diff --git a/frontends/amiga/clipboard.c b/frontends/amiga/clipboard.c
new file mode 100644
index 000000000..12ceb0f09
--- /dev/null
+++ b/frontends/amiga/clipboard.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright 2008-2012 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <proto/iffparse.h>
+#include <proto/intuition.h>
+#include <proto/exec.h>
+#include <proto/datatypes.h>
+#include <proto/diskfont.h>
+
+#include <diskfont/diskfonttag.h>
+#include <datatypes/textclass.h>
+#include <datatypes/pictureclass.h>
+
+#include "utils/nsoption.h"
+#include "utils/utf8.h"
+#include "utils/nsurl.h"
+#include "content/hlcache.h"
+#include "desktop/browser.h"
+#include "desktop/plotters.h"
+#include "desktop/textinput.h"
+#include "desktop/gui_window.h"
+#include "desktop/gui_clipboard.h"
+
+#include "amiga/bitmap.h"
+#include "amiga/clipboard.h"
+#include "amiga/drag.h"
+#include "amiga/filetype.h"
+#include "amiga/gui.h"
+#include "amiga/iff_cset.h"
+#include "amiga/iff_dr2d.h"
+#include "amiga/menu.h"
+#include "amiga/misc.h"
+#include "amiga/utf8.h"
+
+#define ID_UTF8 MAKE_ID('U','T','F','8')
+
+struct IFFHandle *iffh = NULL;
+
+static struct IFFHandle *ami_clipboard_init_internal(int unit)
+{
+ struct IFFHandle *iffhandle = NULL;
+
+ if((iffhandle = AllocIFF()))
+ {
+ if((iffhandle->iff_Stream = (ULONG)OpenClipboard(unit)))
+ {
+ InitIFFasClip(iffhandle);
+ }
+ }
+
+ return iffhandle;
+}
+
+void ami_clipboard_init(void)
+{
+ iffh = ami_clipboard_init_internal(0);
+}
+
+static void ami_clipboard_free_internal(struct IFFHandle *iffhandle)
+{
+ if(iffhandle == NULL) return;
+ if(iffhandle->iff_Stream) CloseClipboard((struct ClipboardHandle *)iffhandle->iff_Stream);
+ FreeIFF(iffhandle);
+}
+
+void ami_clipboard_free(void)
+{
+ ami_clipboard_free_internal(iffh);
+}
+
+void gui_start_selection(struct gui_window *g)
+{
+ if(!g) return;
+ if(!g->shared->win) return;
+ if(nsoption_bool(kiosk_mode) == true) return;
+
+ OnMenu(g->shared->win, AMI_MENU_CLEAR);
+ OnMenu(g->shared->win, AMI_MENU_COPY);
+
+ if (browser_window_get_editor_flags(g->bw) & BW_EDITOR_CAN_CUT)
+ OnMenu(g->shared->win, AMI_MENU_CUT);
+}
+
+static char *ami_clipboard_cat_collection(struct CollectionItem *ci, LONG codeset, size_t *text_length)
+{
+ struct CollectionItem *ci_new = NULL, *ci_next = NULL, *ci_curr = ci;
+ size_t len = 0;
+ char *text = NULL, *p;
+
+ /* Scan the collected chunks to find out the total size.
+ * If they are not in UTF-8, convert the chunks first and create a new CollectionItem list.
+ */
+ do {
+ switch(codeset) {
+ case 106:
+ len += ci_curr->ci_Size;
+ break;
+
+ case 0:
+ if(ci_new) {
+ ci_next->ci_Next = ami_misc_allocvec_clear(sizeof(struct CollectionItem), 0);
+ ci_next = ci_next->ci_Next;
+ } else {
+ ci_new = ami_misc_allocvec_clear(sizeof(struct CollectionItem), 0);
+ ci_next = ci_new;
+ }
+
+ utf8_from_local_encoding(ci_curr->ci_Data, ci_curr->ci_Size, (char **)&ci_next->ci_Data);
+ ci_next->ci_Size = strlen(ci_next->ci_Data);
+ len += ci_next->ci_Size;
+ break;
+
+ default:
+ if(ci_new) {
+ ci_next->ci_Next = ami_misc_allocvec_clear(sizeof(struct CollectionItem), 0);
+ ci_next = ci_next->ci_Next;
+ } else {
+ ci_new = ami_misc_allocvec_clear(sizeof(struct CollectionItem), 0);
+ ci_next = ci_new;
+ }
+
+ utf8_from_enc(ci_curr->ci_Data,
+ (const char *)ObtainCharsetInfo(DFCS_NUMBER,
+ codeset, DFCS_MIMENAME),
+ ci_curr->ci_Size, (char **)&ci_next->ci_Data, NULL);
+ ci_next->ci_Size = strlen(ci_next->ci_Data);
+ len += ci_next->ci_Size;
+ break;
+ }
+ } while ((ci_curr = ci_curr->ci_Next));
+
+ text = malloc(len);
+
+ if(text == NULL) return NULL;
+
+ /* p points to the end of the buffer. This is because the chunks are
+ * in the list in reverse order. */
+ p = text + len;
+
+ if(ci_new) {
+ ci_curr = ci_new;
+ } else {
+ ci_curr = ci;
+ }
+
+ do {
+ p -= ci_curr->ci_Size;
+ memcpy(p, ci_curr->ci_Data, ci_curr->ci_Size);
+ ci_next = ci_curr->ci_Next;
+
+ if(ci_new) {
+ free(ci_curr->ci_Data);
+ FreeVec(ci_curr);
+ }
+ } while ((ci_curr = ci_next));
+
+ *text_length = len;
+ return text;
+}
+
+static void gui_get_clipboard(char **buffer, size_t *length)
+{
+ struct CollectionItem *ci = NULL;
+ struct StoredProperty *sp = NULL;
+ struct CSet *cset;
+
+ if(OpenIFF(iffh,IFFF_READ)) return;
+
+ if(CollectionChunk(iffh,ID_FTXT,ID_CHRS)) return;
+ if(PropChunk(iffh,ID_FTXT,ID_CSET)) return;
+ if(CollectionChunk(iffh,ID_FTXT,ID_UTF8)) return;
+ if(StopOnExit(iffh, ID_FTXT, ID_FORM)) return;
+
+ ParseIFF(iffh,IFFPARSE_SCAN);
+
+ if((ci = FindCollection(iffh, ID_FTXT, ID_UTF8))) {
+ *buffer = ami_clipboard_cat_collection(ci, 106, length);
+ } else if((ci = FindCollection(iffh, ID_FTXT, ID_CHRS))) {
+ LONG codeset = 0;
+ if((sp = FindProp(iffh, ID_FTXT, ID_CSET))) {
+ cset = (struct CSet *)sp->sp_Data;
+ codeset = cset->CodeSet;
+ }
+ *buffer = ami_clipboard_cat_collection(ci, codeset, length);
+ }
+
+ CloseIFF(iffh);
+}
+
+static void gui_set_clipboard(const char *buffer, size_t length,
+ nsclipboard_styles styles[], int n_styles)
+{
+ char *text;
+ struct CSet cset = {0, {0}};
+
+ if(buffer == NULL) return;
+
+ if(!(OpenIFF(iffh, IFFF_WRITE)))
+ {
+ if(!(PushChunk(iffh, ID_FTXT, ID_FORM, IFFSIZE_UNKNOWN)))
+ {
+ if(nsoption_bool(clipboard_write_utf8))
+ {
+ if(!(PushChunk(iffh, 0, ID_CSET, 32)))
+ {
+ cset.CodeSet = 106; // UTF-8
+ WriteChunkBytes(iffh, &cset, 32);
+ PopChunk(iffh);
+ }
+ }
+ }
+ else
+ {
+ PopChunk(iffh);
+ }
+
+ if(!(PushChunk(iffh, 0, ID_CHRS, IFFSIZE_UNKNOWN))) {
+ if(nsoption_bool(clipboard_write_utf8)) {
+ WriteChunkBytes(iffh, buffer, length);
+ } else {
+ if(utf8_to_local_encoding(buffer, length, &text) == NSERROR_OK) {
+ char *p;
+
+ p = text;
+
+ while(*p != '\0') {
+ if(*p == 0xa0) *p = 0x20;
+ p++;
+ }
+ WriteChunkBytes(iffh, text, strlen(text));
+ ami_utf8_free(text);
+ }
+ }
+
+ PopChunk(iffh);
+ } else {
+ PopChunk(iffh);
+ }
+
+ if(!(PushChunk(iffh, 0, ID_UTF8, IFFSIZE_UNKNOWN))) {
+ WriteChunkBytes(iffh, buffer, length);
+ PopChunk(iffh);
+ } else {
+ PopChunk(iffh);
+ }
+ CloseIFF(iffh);
+ }
+}
+
+void ami_drag_selection(struct gui_window *g)
+{
+ int x;
+ int y;
+ char *utf8text;
+ char *sel;
+ struct IFFHandle *old_iffh = iffh;
+ struct gui_window_2 *gwin = ami_window_at_pointer(AMINS_WINDOW);
+
+ /* NB: 'gwin' is at the drop point, 'g' is where the selection was dragged from.
+ * These may be different if the selection has been dragged between windows. */
+
+ if(!gwin)
+ {
+ DisplayBeep(scrn);
+ return;
+ }
+
+ x = gwin->win->MouseX;
+ y = gwin->win->MouseY;
+
+ if(ami_text_box_at_point(gwin, (ULONG *)&x, (ULONG *)&y))
+ {
+ iffh = ami_clipboard_init_internal(1);
+
+ browser_window_key_press(g->bw, NS_KEY_COPY_SELECTION);
+ browser_window_mouse_click(gwin->gw->bw, BROWSER_MOUSE_PRESS_1, x, y);
+ browser_window_key_press(gwin->gw->bw, NS_KEY_PASTE);
+
+ ami_clipboard_free_internal(iffh);
+ iffh = old_iffh;
+ }
+ else
+ {
+ x = gwin->win->MouseX;
+ y = gwin->win->MouseY;
+
+ if(ami_gadget_hit(gwin->objects[GID_URL], x, y))
+ {
+ if((sel = browser_window_get_selection(g->bw)))
+ {
+ utf8text = ami_utf8_easy(sel);
+ RefreshSetGadgetAttrs((struct Gadget *)gwin->objects[GID_URL],
+ gwin->win, NULL, STRINGA_TextVal, utf8text, TAG_DONE);
+ free(sel);
+ ami_utf8_free(utf8text);
+ }
+ }
+ else if(ami_gadget_hit(gwin->objects[GID_SEARCHSTRING], x, y))
+ {
+ if((sel = browser_window_get_selection(g->bw)))
+ {
+ utf8text = ami_utf8_easy(sel);
+ RefreshSetGadgetAttrs((struct Gadget *)gwin->objects[GID_SEARCHSTRING],
+ gwin->win, NULL, STRINGA_TextVal, utf8text, TAG_DONE);
+ free(sel);
+ ami_utf8_free(utf8text);
+ }
+ }
+ else
+ {
+ DisplayBeep(scrn);
+ }
+ }
+}
+
+bool ami_easy_clipboard(const char *text)
+{
+ gui_set_clipboard(text, strlen(text), NULL, 0);
+ return true;
+}
+
+bool ami_easy_clipboard_bitmap(struct bitmap *bitmap)
+{
+ Object *dto = NULL;
+
+ if((dto = ami_datatype_object_from_bitmap(bitmap)))
+ {
+ DoDTMethod(dto,NULL,NULL,DTM_COPY,NULL);
+ DisposeDTObject(dto);
+ }
+ return true;
+}
+
+#ifdef WITH_NS_SVG
+bool ami_easy_clipboard_svg(struct hlcache_handle *c)
+{
+ const char *source_data;
+ ULONG source_size;
+
+ if(ami_mime_compare(c, "svg") == false) return false;
+ if((source_data = content_get_source_data(c, &source_size)) == NULL) return false;
+
+ if(!(OpenIFF(iffh,IFFF_WRITE)))
+ {
+ ami_svg_to_dr2d(iffh, source_data, source_size, nsurl_access(hlcache_handle_get_url(c)));
+ CloseIFF(iffh);
+ }
+
+ return true;
+}
+#endif
+
+static struct gui_clipboard_table clipboard_table = {
+ .get = gui_get_clipboard,
+ .set = gui_set_clipboard,
+};
+
+struct gui_clipboard_table *amiga_clipboard_table = &clipboard_table;
diff --git a/frontends/amiga/clipboard.h b/frontends/amiga/clipboard.h
new file mode 100755
index 000000000..bc5b779ef
--- /dev/null
+++ b/frontends/amiga/clipboard.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2008-2009, 2011 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_CLIPBOARD_H
+#define AMIGA_CLIPBOARD_H
+#include <stdbool.h>
+
+struct bitmap;
+struct hlcache_handle;
+struct selection;
+struct gui_window;
+struct gui_window_2;
+struct gui_clipboard_table;
+
+extern struct gui_clipboard_table *amiga_clipboard_table;
+
+void gui_start_selection(struct gui_window *g);
+
+void ami_clipboard_init(void);
+void ami_clipboard_free(void);
+void ami_drag_selection(struct gui_window *g);
+bool ami_easy_clipboard(const char *text);
+bool ami_easy_clipboard_bitmap(struct bitmap *bitmap);
+#ifdef WITH_NS_SVG
+bool ami_easy_clipboard_svg(struct hlcache_handle *c);
+#endif
+#endif
diff --git a/frontends/amiga/cookies.c b/frontends/amiga/cookies.c
new file mode 100755
index 000000000..0ca17413e
--- /dev/null
+++ b/frontends/amiga/cookies.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2008, 2009 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <proto/exec.h>
+
+#include "desktop/cookie_manager.h"
+#include "desktop/mouse.h"
+#include "desktop/gui_window.h"
+
+#include "amiga/cookies.h"
+#include "amiga/tree.h"
+
+void ami_cookies_initialise(void)
+{
+ cookies_window = ami_tree_create(TREE_COOKIES, NULL);
+
+ if(!cookies_window) return;
+}
+
+void ami_cookies_free()
+{
+ ami_tree_destroy(cookies_window);
+ cookies_window = NULL;
+}
diff --git a/frontends/amiga/cookies.h b/frontends/amiga/cookies.h
new file mode 100755
index 000000000..d6922750c
--- /dev/null
+++ b/frontends/amiga/cookies.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2008, 2009 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_COOKIES_H
+#define AMIGA_COOKIES_H
+#include "desktop/tree.h"
+#include "amiga/tree.h"
+
+void ami_cookies_initialise(void);
+void ami_cookies_free(void);
+
+struct treeview_window *cookies_window;
+#endif
diff --git a/frontends/amiga/ctxmenu.c b/frontends/amiga/ctxmenu.c
new file mode 100644
index 000000000..cc612da24
--- /dev/null
+++ b/frontends/amiga/ctxmenu.c
@@ -0,0 +1,607 @@
+/*
+ * Copyright 2015 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Intuition-based context menu operations
+ */
+
+#ifdef __amigaos4__
+#include <string.h>
+
+#include <stdlib.h>
+#include <proto/exec.h>
+#include <proto/intuition.h>
+
+#include <proto/bitmap.h>
+#include <images/bitmap.h>
+#include <proto/window.h>
+#include <classes/window.h>
+
+#include <intuition/menuclass.h>
+#include <reaction/reaction_macros.h>
+
+#include "utils/utils.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/nsoption.h"
+#include "utils/nsurl.h"
+#include "content/hlcache.h"
+#include "desktop/browser.h"
+#include "desktop/browser_history.h"
+#include "desktop/mouse.h"
+#include "desktop/searchweb.h"
+#include "desktop/textinput.h"
+
+#include "amiga/bitmap.h"
+#include "amiga/clipboard.h"
+#include "amiga/ctxmenu.h"
+#include "amiga/filetype.h"
+#include "amiga/gui.h"
+#include "amiga/libs.h"
+#include "amiga/plugin_hack.h"
+#include "amiga/theme.h"
+#include "amiga/utf8.h"
+
+
+enum {
+ AMI_CTX_ID_NONE = 0,
+
+ /* Text selection */
+ AMI_CTX_ID_SELCOPY,
+ AMI_CTX_ID_WEBSEARCH,
+
+ /* Links */
+ AMI_CTX_ID_URLOPENTAB,
+ AMI_CTX_ID_URLOPENWIN,
+ AMI_CTX_ID_URLDOWNLOAD,
+ AMI_CTX_ID_URLCOPY,
+
+ /* Objects */
+ AMI_CTX_ID_OBJSHOW,
+ AMI_CTX_ID_OBJCOPY,
+ AMI_CTX_ID_OBJCMD,
+
+ /* Frames */
+ AMI_CTX_ID_FRAMESHOW,
+
+ /* History */
+ AMI_CTX_ID_HISTORY,
+ AMI_CTX_ID_HISTORY0,
+ AMI_CTX_ID_HISTORY9F = AMI_CTX_ID_HISTORY0 + 19,
+
+ /* Tabs */
+ AMI_CTX_ID_TABNEW,
+ AMI_CTX_ID_TABCLOSE_OTHER,
+
+ AMI_CTX_ID_MAX
+};
+
+static Object *ctxmenu_obj = NULL;
+
+static struct Hook ctxmenu_item_hook[AMI_CTX_ID_MAX];
+static char *ctxmenu_item_label[AMI_CTX_ID_MAX];
+static char *ctxmenu_item_shortcut[AMI_CTX_ID_MAX];
+static Object *ctxmenu_item_image[AMI_CTX_ID_MAX];
+
+/****************************
+ * Menu item hook functions *
+ ****************************/
+
+/** Menu functions - called automatically by RA_HandleInput **/
+HOOKF(void, ami_ctxmenu_item_selcopy, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin = (struct gui_window_2 *)hook->h_Data;
+
+ browser_window_key_press(gwin->gw->bw, NS_KEY_COPY_SELECTION);
+ browser_window_key_press(gwin->gw->bw, NS_KEY_CLEAR_SELECTION);
+}
+
+HOOKF(void, ami_ctxmenu_item_websearch, APTR, window, struct IntuiMessage *)
+{
+ nserror ret = NSERROR_OK;
+ nsurl *url;
+
+ struct gui_window_2 *gwin = (struct gui_window_2 *)hook->h_Data;
+ char *sel = browser_window_get_selection(gwin->gw->bw);
+
+ ret = search_web_omni(sel, SEARCH_WEB_OMNI_SEARCHONLY, &url);
+ if (ret == NSERROR_OK) {
+ browser_window_navigate(gwin->gw->bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (ret != NSERROR_OK) {
+ amiga_warn_user(messages_get_errorcode(ret), 0);
+ }
+
+ free(sel);
+}
+
+HOOKF(void, ami_ctxmenu_item_urlopentab, APTR, window, struct IntuiMessage *)
+{
+ struct browser_window *bw;
+ nsurl *url = (nsurl *)hook->h_Data;
+ struct gui_window_2 *gwin;
+
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+ nserror error = browser_window_create(BW_CREATE_CLONE | BW_CREATE_HISTORY | BW_CREATE_TAB,
+ url,
+ browser_window_get_url(gwin->gw->bw),
+ gwin->gw->bw,
+ &bw);
+
+ if (error != NSERROR_OK)
+ amiga_warn_user(messages_get_errorcode(error), 0);
+}
+
+HOOKF(void, ami_ctxmenu_item_urlopenwin, APTR, window, struct IntuiMessage *)
+{
+ struct browser_window *bw;
+ nsurl *url = (nsurl *)hook->h_Data;
+ struct gui_window_2 *gwin;
+
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+ nserror error = browser_window_create(BW_CREATE_CLONE | BW_CREATE_HISTORY,
+ url,
+ browser_window_get_url(gwin->gw->bw),
+ gwin->gw->bw,
+ &bw);
+
+ if (error != NSERROR_OK)
+ amiga_warn_user(messages_get_errorcode(error), 0);
+}
+
+HOOKF(void, ami_ctxmenu_item_urldownload, APTR, window, struct IntuiMessage *)
+{
+ nsurl *url = (nsurl *)hook->h_Data;
+ struct gui_window_2 *gwin;
+
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ browser_window_navigate(gwin->gw->bw,
+ url,
+ browser_window_get_url(gwin->gw->bw),
+ BW_NAVIGATE_DOWNLOAD,
+ NULL,
+ NULL,
+ NULL);
+}
+
+HOOKF(void, ami_ctxmenu_item_urlcopy, APTR, window, struct IntuiMessage *)
+{
+ nsurl *url = (nsurl *)hook->h_Data;
+ ami_easy_clipboard(nsurl_access(url));
+}
+
+HOOKF(void, ami_ctxmenu_item_objshow, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ browser_window_navigate(gwin->gw->bw,
+ hlcache_handle_get_url(hook->h_Data),
+ browser_window_get_url(gwin->gw->bw),
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+}
+
+HOOKF(void, ami_ctxmenu_item_objcopy, APTR, window, struct IntuiMessage *)
+{
+ struct bitmap *bm;
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ struct hlcache_handle *object = (struct hlcache_handle *)hook->h_Data;
+ if((bm = content_get_bitmap(object)))
+ {
+ ami_bitmap_set_url(bm, hlcache_handle_get_url(object));
+ ami_bitmap_set_title(bm, content_get_title(object));
+ ami_easy_clipboard_bitmap(bm);
+ }
+#ifdef WITH_NS_SVG
+ else if(ami_mime_compare(object, "svg") == true)
+ {
+ ami_easy_clipboard_svg(object);
+ }
+#endif
+}
+
+HOOKF(void, ami_ctxmenu_item_objcmd, APTR, window, struct IntuiMessage *)
+{
+ amiga_plugin_hack_execute((struct hlcache_handle *)hook->h_Data);
+}
+
+HOOKF(void, ami_ctxmenu_item_frameshow, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ browser_window_navigate(gwin->gw->bw,
+ hlcache_handle_get_url(hook->h_Data),
+ browser_window_get_url(gwin->gw->bw),
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+}
+
+/** Hooks for clicktab context menu entries **/
+HOOKF(void, ami_ctxmenu_item_tabnew, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+ ami_gui_new_blank_tab(gwin);
+}
+
+HOOKF(void, ami_ctxmenu_item_tabclose_other, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+ ami_gui_close_inactive_tabs(gwin);
+}
+
+/** Hook for history context menu entries **/
+HOOKF(void, ami_ctxmenu_item_history, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ browser_window_history_go(gwin->gw->bw, (struct history_entry *)hook->h_Data, false);
+}
+
+
+/*************************
+ * Browser context menus *
+ *************************/
+
+/** Add an initialised item to a context menu **/
+static void ami_ctxmenu_add_item(Object *root_menu, int id, APTR data)
+{
+ ctxmenu_item_hook[id].h_Data = data;
+
+ IDoMethod(root_menu, OM_ADDMEMBER, MStrip,
+ MA_Type, T_ITEM,
+ MA_ID, id,
+ MA_Label, ctxmenu_item_label[id],
+ MA_Key, ctxmenu_item_shortcut[id],
+ MA_Image, ctxmenu_item_image[id],
+ MA_UserData, &ctxmenu_item_hook[id],
+ MEnd);
+}
+
+/** Hook function called by Intuition, creates context menu structure **/
+static uint32 ami_ctxmenu_hook_func(struct Hook *hook, struct Window *window, struct ContextMenuMsg *msg)
+{
+ Object *root_menu;
+ bool ctxmenu_has_content = false;
+ struct gui_window_2 *gwin = hook->h_Data;
+ struct hlcache_handle *cc = browser_window_get_content(gwin->gw->bw);
+ struct browser_window_features ccdata;
+ int mx = window->MouseX;
+ int my = window->MouseY;
+ int x, y;
+ char *sel;
+
+ if(msg->State != CM_QUERY) return 0;
+ if(nsoption_bool(kiosk_mode) == true) return 0;
+// check window is active
+
+ if(ctxmenu_obj != NULL) DisposeObject(ctxmenu_obj);
+
+ ctxmenu_obj = MStrip,
+ MA_Type, T_ROOT,
+ MA_AddChild, root_menu = MStrip,
+ MA_Type, T_MENU,
+ MA_Label, NULL, //"NetSurf",
+ MA_EmbeddedKey, FALSE,
+ MA_FreeImage, FALSE,
+ MEnd,
+ MEnd;
+
+ if(ami_mouse_to_ns_coords(gwin, &x, &y, mx, my) == false) {
+ /* Outside browser render area */
+ return 0;
+ }
+
+ browser_window_get_features(gwin->gw->bw, x, y, &ccdata);
+
+ if((browser_window_can_select(gwin->gw->bw)) &&
+ ((browser_window_get_editor_flags(gwin->gw->bw) & BW_EDITOR_CAN_COPY)) &&
+ (sel = browser_window_get_selection(gwin->gw->bw))) {
+
+ ami_ctxmenu_add_item(root_menu, AMI_CTX_ID_SELCOPY, gwin);
+ ami_ctxmenu_add_item(root_menu, AMI_CTX_ID_WEBSEARCH, gwin);
+
+ ctxmenu_has_content = true;
+ free(sel);
+ }
+
+ if(ccdata.link) {
+ if(ctxmenu_has_content == true)
+ ami_ctxmenu_add_item(root_menu, AMI_CTX_ID_NONE, NULL);
+
+ ami_ctxmenu_add_item(root_menu, AMI_CTX_ID_URLOPENTAB, ccdata.link);
+ ami_ctxmenu_add_item(root_menu, AMI_CTX_ID_URLOPENWIN, ccdata.link);
+ ami_ctxmenu_add_item(root_menu, AMI_CTX_ID_URLDOWNLOAD, ccdata.link);
+ ami_ctxmenu_add_item(root_menu, AMI_CTX_ID_URLCOPY, ccdata.link);
+ ctxmenu_has_content = true;
+ }
+
+ if(ccdata.object) {
+ if(ctxmenu_has_content == true)
+ ami_ctxmenu_add_item(root_menu, AMI_CTX_ID_NONE, NULL);
+
+ ami_ctxmenu_add_item(root_menu, AMI_CTX_ID_OBJSHOW, ccdata.object);
+
+ if(content_get_type(ccdata.object) == CONTENT_IMAGE)
+ ami_ctxmenu_add_item(root_menu, AMI_CTX_ID_OBJCOPY, ccdata.object);
+
+ if(ami_mime_content_to_cmd(ccdata.object))
+ ami_ctxmenu_add_item(root_menu, AMI_CTX_ID_OBJCMD, ccdata.object);
+
+ ctxmenu_has_content = true;
+ }
+
+ if(ccdata.main && (ccdata.main != cc)) {
+ if(ctxmenu_has_content == true)
+ ami_ctxmenu_add_item(root_menu, AMI_CTX_ID_NONE, NULL);
+
+ ami_ctxmenu_add_item(root_menu, AMI_CTX_ID_FRAMESHOW, ccdata.main);
+
+ ctxmenu_has_content = true;
+ }
+
+ if(ctxmenu_has_content == true) {
+ msg->Menu = ctxmenu_obj;
+ ami_set_pointer(gwin, GUI_POINTER_DEFAULT, false);
+ }
+
+ return 0;
+}
+
+/** Initial menu item creation **/
+static void ami_ctxmenu_alloc_item(int id, const char *label, const char *key, const char *image, void *func)
+{
+ if(label == ML_SEPARATOR) {
+ ctxmenu_item_label[id] = ML_SEPARATOR;
+ } else {
+ ctxmenu_item_label[id] = ami_utf8_easy(messages_get(label));
+ }
+
+ if(key != NULL) {
+ ctxmenu_item_shortcut[id] = strdup(key);
+ } else {
+ ctxmenu_item_shortcut[id] = NULL;
+ }
+
+ if(image != NULL) {
+ ctxmenu_item_image[id] = BitMapObj,
+ BITMAP_Screen, scrn,
+ BITMAP_SourceFile, image,
+ BITMAP_Masking, TRUE,
+ BitMapEnd;
+
+ SetAttrs(ctxmenu_item_image[id],
+ BITMAP_Width, 16,
+ BITMAP_Height, 16,
+ TAG_DONE);
+ }
+
+ ctxmenu_item_hook[id].h_Entry = func;
+ ctxmenu_item_hook[id].h_Data = 0;
+}
+
+/** Exported interface documented in ctxmenu.h **/
+struct Hook *ami_ctxmenu_get_hook(APTR data)
+{
+ return AllocSysObjectTags(ASOT_HOOK,
+ ASOHOOK_Entry, (HOOKFUNC)ami_ctxmenu_hook_func,
+ ASOHOOK_Data, data,
+ TAG_DONE);
+}
+
+/** Exported interface documented in ctxmenu.h **/
+void ami_ctxmenu_release_hook(struct Hook *hook)
+{
+ FreeSysObject(ASOT_HOOK, hook);
+}
+
+/** Exported interface documented in ctxmenu.h **/
+void ami_ctxmenu_free(void)
+{
+ for(int i = 1; i < AMI_CTX_ID_MAX; i++) {
+ if((ctxmenu_item_label[i] != NULL) && (ctxmenu_item_label[i] != ML_SEPARATOR)) {
+ ami_utf8_free(ctxmenu_item_label[i]);
+ }
+ ctxmenu_item_label[i] = NULL;
+
+ if(ctxmenu_item_shortcut[i] != NULL) {
+ free(ctxmenu_item_shortcut[i]);
+ ctxmenu_item_shortcut[i] = NULL;
+ }
+
+ if(ctxmenu_item_image[i] != NULL) {
+ DisposeObject(ctxmenu_item_image[i]);
+ ctxmenu_item_image[i] = NULL;
+ }
+ }
+
+ if(ctxmenu_obj != NULL) DisposeObject(ctxmenu_obj);
+ ctxmenu_obj = NULL;
+}
+
+/** Exported interface documented in ctxmenu.h **/
+void ami_ctxmenu_init(void)
+{
+ ami_ctxmenu_alloc_item(AMI_CTX_ID_NONE, ML_SEPARATOR, NULL, NULL, NULL);
+
+ ami_ctxmenu_alloc_item(AMI_CTX_ID_SELCOPY, "CopyNS", "C", "TBImages:list_copy",
+ ami_ctxmenu_item_selcopy);
+ ami_ctxmenu_alloc_item(AMI_CTX_ID_WEBSEARCH, "SearchWeb", NULL, "TBImages:list_search",
+ ami_ctxmenu_item_websearch);
+
+ ami_ctxmenu_alloc_item(AMI_CTX_ID_URLOPENTAB, "LinkNewTab", NULL, "TBImages:list_tab",
+ ami_ctxmenu_item_urlopentab);
+ ami_ctxmenu_alloc_item(AMI_CTX_ID_URLOPENWIN, "LinkNewWin", NULL, "TBImages:list_app",
+ ami_ctxmenu_item_urlopenwin);
+ ami_ctxmenu_alloc_item(AMI_CTX_ID_URLDOWNLOAD, "LinkDload", NULL, "TBImages:list_save",
+ ami_ctxmenu_item_urldownload);
+ ami_ctxmenu_alloc_item(AMI_CTX_ID_URLCOPY, "CopyURL", NULL, "TBImages:list_copy",
+ ami_ctxmenu_item_urlcopy);
+
+ ami_ctxmenu_alloc_item(AMI_CTX_ID_OBJSHOW, "ObjShow", NULL, "TBImages:list_preview",
+ ami_ctxmenu_item_objshow);
+ ami_ctxmenu_alloc_item(AMI_CTX_ID_OBJCOPY, "CopyClip", NULL, "TBImages:list_copy",
+ ami_ctxmenu_item_objcopy);
+ ami_ctxmenu_alloc_item(AMI_CTX_ID_OBJCMD, "ExternalApp", NULL, "TBImages:list_tool",
+ ami_ctxmenu_item_objcmd);
+
+ ami_ctxmenu_alloc_item(AMI_CTX_ID_FRAMESHOW, "FrameOnly", NULL, "TBImages:list_preview",
+ ami_ctxmenu_item_frameshow);
+
+ ami_ctxmenu_alloc_item(AMI_CTX_ID_TABNEW, "NewTab", "T", "TBImages:list_tab",
+ ami_ctxmenu_item_tabnew);
+ ami_ctxmenu_alloc_item(AMI_CTX_ID_TABCLOSE_OTHER, "CloseInactive", "K", "TBImages:list_cancel",
+ ami_ctxmenu_item_tabclose_other);
+}
+
+/********************************
+ * History button context menus *
+ ********************************/
+
+/** Create menu entries from browser history **/
+static bool ami_ctxmenu_history(int direction, struct gui_window_2 *gwin, const struct history_entry *entry)
+{
+ Object *history_root;
+ int id = AMI_CTX_ID_HISTORY0 + gwin->temp;
+ if(direction == AMI_CTXMENU_HISTORY_FORWARD) id += 10;
+
+ if(gwin->temp >= 10) return false;
+
+ ctxmenu_item_hook[id].h_Entry = (HOOKFUNC)ami_ctxmenu_item_history;
+ ctxmenu_item_hook[id].h_Data = (APTR)entry;
+
+ history_root = (Object *)IDoMethod(gwin->history_ctxmenu[direction], MM_FINDID, 0, AMI_CTX_ID_HISTORY);
+
+ IDoMethod(history_root, OM_ADDMEMBER, MStrip,
+ MA_Type, T_ITEM,
+ MA_Label, browser_window_history_entry_get_title(entry),
+ MA_ID, id,
+ MA_Image, NULL,
+ MA_UserData, &ctxmenu_item_hook[id],
+ MEnd);
+
+ gwin->temp++;
+
+ return true;
+}
+
+/** Callback for browser_window_history_enumerate **/
+static bool ami_ctxmenu_history_back(const struct browser_window *bw,
+ int x0, int y0, int x1, int y1,
+ const struct history_entry *entry, void *user_data)
+{
+ return ami_ctxmenu_history(AMI_CTXMENU_HISTORY_BACK, (struct gui_window_2 *)user_data, entry);
+}
+
+/** Callback for browser_window_history_enumerate **/
+static bool ami_ctxmenu_history_forward(const struct browser_window *bw,
+ int x0, int y0, int x1, int y1,
+ const struct history_entry *entry, void *user_data)
+{
+ return ami_ctxmenu_history(AMI_CTXMENU_HISTORY_FORWARD, (struct gui_window_2 *)user_data, entry);
+}
+
+/** Exported interface documented in ctxmenu.h **/
+struct Menu *ami_ctxmenu_history_create(int direction, struct gui_window_2 *gwin)
+{
+ Object *obj;
+
+ if(gwin->history_ctxmenu[direction] == NULL) {
+ if(ctxmenu_item_label[AMI_CTX_ID_HISTORY] == NULL)
+ ctxmenu_item_label[AMI_CTX_ID_HISTORY] = ami_utf8_easy(messages_get("History"));
+
+ gwin->history_ctxmenu[direction] = MStrip,
+ MA_Type, T_ROOT,
+ MA_AddChild, MStrip,
+ MA_Type, T_MENU,
+ MA_ID, AMI_CTX_ID_HISTORY,
+ MA_Label, ctxmenu_item_label[AMI_CTX_ID_HISTORY],
+ MA_EmbeddedKey, FALSE,
+ //MA_FreeImage, FALSE,
+ MEnd,
+ MEnd;
+ } else {
+ for (int i = 0; i < 20; i++) {
+ obj = (Object *)IDoMethod(gwin->history_ctxmenu[direction],
+ MM_FINDID, 0, AMI_CTX_ID_HISTORY0 + i);
+ if(obj != NULL) IDoMethod(gwin->history_ctxmenu[direction], OM_REMMEMBER, obj);
+ }
+
+ gwin->temp = 0;
+
+ if(direction == AMI_CTXMENU_HISTORY_BACK) {
+ browser_window_history_enumerate_back(gwin->gw->bw, ami_ctxmenu_history_back, gwin);
+ } else {
+ browser_window_history_enumerate_forward(gwin->gw->bw, ami_ctxmenu_history_forward, gwin);
+ }
+ }
+
+ return (struct Menu *)gwin->history_ctxmenu[direction];
+}
+
+
+/**************************
+ * ClickTab context menus *
+ **************************/
+
+/** Exported interface documented in ctxmenu.h **/
+struct Menu *ami_ctxmenu_clicktab_create(struct gui_window_2 *gwin)
+{
+ Object *root_menu;
+
+ if(gwin->clicktab_ctxmenu != NULL) return (struct Menu *)gwin->clicktab_ctxmenu;
+
+ gwin->clicktab_ctxmenu = MStrip,
+ MA_Type, T_ROOT,
+ MA_AddChild, root_menu = MStrip,
+ MA_Type, T_MENU,
+ MA_Label, NULL,
+ MA_EmbeddedKey, FALSE,
+ MEnd,
+ MEnd;
+
+ ami_ctxmenu_add_item(root_menu, AMI_CTX_ID_TABNEW, gwin);
+ ami_ctxmenu_add_item(root_menu, AMI_CTX_ID_TABCLOSE_OTHER, gwin);
+
+ return (struct Menu *)gwin->clicktab_ctxmenu;
+}
+
+
+#endif
+
diff --git a/frontends/amiga/ctxmenu.h b/frontends/amiga/ctxmenu.h
new file mode 100644
index 000000000..08a5fe582
--- /dev/null
+++ b/frontends/amiga/ctxmenu.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2015 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Interface to Intuition-based context menu operations
+ */
+
+#ifndef AMIGA_CTXMENU_H
+#define AMIGA_CTXMENU_H 1
+struct Hook;
+struct Menu;
+struct gui_window_2;
+
+enum {
+ AMI_CTXMENU_HISTORY_BACK = 0,
+ AMI_CTXMENU_HISTORY_FORWARD = 1
+};
+
+#ifdef __amigaos4__
+/**
+ * Initialise context menus code (allocate label text, etc)
+ * Must be called *after* NetSurf's screen pointer is obtained.
+ */
+void ami_ctxmenu_init(void);
+
+/**
+ * Cleanup context menus code
+ */
+void ami_ctxmenu_free(void);
+
+/**
+ * Get a Hook for WA_ContextMenuHook
+ *
+ * \param data ptr for the hook to use (struct gui_window_2 *)
+ * \returns pointer to a struct Hook
+ */
+struct Hook *ami_ctxmenu_get_hook(APTR data);
+
+/**
+ * Release a Hook for WA_ContextMenuHook
+ *
+ * \param hook ptr to hook
+ */
+void ami_ctxmenu_release_hook(struct Hook *hook);
+
+/**
+ * Create history context menu
+ * The first time this is run it will create an empty menu,
+ * Subsequent runs will (re-)populate with the history.
+ * This is to allow the pointer to be obtained before the browser_window is opened.
+ *
+ * \param direction AMI_CTXMENU_HISTORY_(BACK|FORWARD)
+ * \param gwin struct gui_window_2 *
+ * \returns pointer to menu (for convenience, is also stored in gwin structure)
+ * The returned pointer MUST be disposed of with DisposeObject before program exit.
+ */
+struct Menu *ami_ctxmenu_history_create(int direction, struct gui_window_2 *gwin);
+
+/**
+ * Create ClickTab context menu
+ *
+ * \param gwin struct gui_window_2 *
+ * \returns pointer to menu (for convenience, is also stored in gwin structure)
+ * The returned pointer MUST be disposed of with DisposeObject before program exit.
+ */
+struct Menu *ami_ctxmenu_clicktab_create(struct gui_window_2 *gwin);
+
+#else //__amigaos4__
+inline void ami_ctxmenu_init(void) {}
+inline void ami_ctxmenu_free(void) {}
+inline struct Hook *ami_ctxmenu_get_hook(APTR data) {return NULL;}
+inline void ami_ctxmenu_release_hook(struct Hook *hook) {}
+inline struct Menu *ami_ctxmenu_history_create(int direction, struct gui_window_2 *gwin) {return NULL;}
+inline struct Menu *ami_ctxmenu_clicktab_create(struct gui_window_2 *gwin) {return NULL;}
+#endif //__amigaos4__
+#endif //AMIGA_CTXMENU_H
+
diff --git a/frontends/amiga/datatypes.c b/frontends/amiga/datatypes.c
new file mode 100644
index 000000000..6018b107f
--- /dev/null
+++ b/frontends/amiga/datatypes.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2011 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#ifdef WITH_AMIGA_DATATYPES
+#include "amiga/datatypes.h"
+
+nserror amiga_datatypes_init(void)
+{
+ nserror err = NSERROR_OK;
+
+ err = amiga_dt_picture_init();
+ if(err != NSERROR_OK) return err;
+
+#ifdef WITH_DTANIM
+ err = amiga_dt_anim_init();
+ if(err != NSERROR_OK) return err;
+#endif
+
+ err = amiga_dt_sound_init();
+ if(err != NSERROR_OK) return err;
+
+ return NSERROR_OK;
+}
+
+#endif
diff --git a/frontends/amiga/datatypes.h b/frontends/amiga/datatypes.h
new file mode 100644
index 000000000..00a9b4e5a
--- /dev/null
+++ b/frontends/amiga/datatypes.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2011 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_AMIGA_DATATYPES_H_
+#define NETSURF_AMIGA_DATATYPES_H_
+
+#include "utils/config.h"
+#include "utils/errors.h"
+
+#ifdef WITH_AMIGA_DATATYPES
+
+nserror amiga_datatypes_init(void);
+
+nserror amiga_dt_picture_init(void);
+
+nserror amiga_dt_anim_init(void);
+
+nserror amiga_dt_sound_init(void);
+
+#else
+
+#define amiga_datatypes_init() NSERROR_OK
+
+#endif /* WITH_AMIGA_DATATYPES */
+
+#endif
diff --git a/frontends/amiga/dist/Install b/frontends/amiga/dist/Install
new file mode 100755
index 000000000..e8d9d0ed7
--- /dev/null
+++ b/frontends/amiga/dist/Install
@@ -0,0 +1,397 @@
+; Installation script for NetSurf
+
+(procedure p_append #filename #text
+ (set #file-path (tackon "ENVARC:launch-handler/URL" #protocol))
+ (set #def-file-path (tackon "ENVARC:launch-handler/URL/defaults" #protocol))
+
+ (if (exists #file-path)
+ (
+ (set #inc #file-path)
+ )
+ ;else
+ (
+ (set #inc #def-file-path)
+ )
+ )
+
+ (textfile
+ (dest #file-path)
+ (include #inc)
+ (append #text)
+ )
+)
+
+(procedure p_chk_launch-handler #protocol
+ (if (exists (tackon "ENVARC:launch-handler/URL" #protocol))
+ (
+ (run "C:Search >T:NS_Install.tmp " (tackon "ENVARC:launch-handler/URL" #protocol) " NetSurf" (safe))
+ )
+ ;else
+ (
+ (run "C:Search >T:NS_Install.tmp " (tackon "ENVARC:launch-handler/URL/defaults" #protocol) " NetSurf" (safe))
+ )
+ )
+
+ (set #has_entry (getsize "T:NS_Install.tmp"))
+)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Script execution starts here ;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(set osver (getversion "libs:version.library"))
+(set osver (/ osver 65536))
+
+(if (= @app-name "NetSurfAutoInstall") (set #AutoInstall 1))
+(set @app-name "NetSurf")
+
+(set #netsurf-readme "NetSurf.readme")
+(if (= (exists #netsurf-readme) 0)
+ (
+ (set #netsurf-readme "NetSurf_os3.readme")
+ (if (OR (>= osver 50) (< osver 44))
+ (abort "This archive is for AmigaOS 3.5 or 3.9 ONLY.")
+ )
+ )
+ ;else
+ (
+ (if (< osver 50)
+ (abort "This archive is for AmigaOS 4.0 and higher ONLY.")
+ )
+ )
+)
+
+(if (<> #AutoInstall 1) (welcome))
+
+; (hopefully temporary) workaround for a bug in Installer:
+(if (= @language "dutch") (set @askdir-help ""))
+
+(complete 0)
+
+(set @default-dest (getenv "AppPaths/NetSurf"))
+
+; If env-var did not exist, this is a first time install.
+; For novice users we must take care to put the files somewhere sensible.
+(if (= @default-dest "")
+ (
+ ; Workaround for Installer bug picking TEXTCLIP: as a sensible location
+ (if (= @default-dest "TextClip:") (set @default-dest "SYS:Internet"))
+
+ (if (= @user-level 0)
+ (
+ (makedir (tackon @default-dest "NetSurf") (infos))
+ (set @default-dest (tackon @default-dest "NetSurf"))
+ )
+ )
+ )
+)
+
+(set @default-dest
+ (askdir
+ (prompt "Where would you like to install NetSurf?\n"
+ "(a drawer WILL NOT be created)")
+ (help @askdir-help)
+ (default @default-dest)
+ )
+)
+
+(complete 5)
+
+(working "Checking existing installation...")
+(set #icon-exists (exists (tackon @default-dest "NetSurf.info")))
+
+(complete 10)
+
+(set #user (getenv "user"))
+(if (= #user "") (set #user "Default"))
+
+(set #user-dir (tackon (tackon @default-dest "Users") #user))
+(set #user-options (tackon #user-dir "Choices"))
+(set #options-exist (exists #user-options))
+(set #searchengines-exist (exists (tackon @default-dest "Resources/SearchEngines")))
+(set #user-hotlist (tackon #user-dir "Hotlist"))
+(set #hotlist-exist (exists #user-hotlist))
+(set #old-hotlist (tackon @default-dest "Resources/Hotlist"))
+(set #old-hotlist-exist (exists #old-hotlist))
+(set #aiss-theme "")
+
+(if (= #options-exist 0)
+ (
+ (if (exists "TBimages:" (noreq))
+ (set #aiss-theme "AISS")
+ )
+
+ (set #themename
+ (askchoice
+ (prompt "Please select theme")
+ (help "AISS theme requires AISS (and def_pointers for 32-bit "
+ "pointers), and will only be shown as an option if "
+ "AISS is installed.\n\n"
+ @askchoice-help)
+ (choices "Default" #aiss-theme)
+ (default 0)
+ )
+ )
+
+ (select #themename
+ (set #themeshort "Default")
+ (set #themeshort "AISS")
+ )
+
+ (set #theme (tackon "PROGDIR:Resources/Themes/" #themeshort))
+ )
+)
+
+(complete 15)
+
+(if (>= osver 53)
+ (
+ (if (= #AutoInstall 1)
+ (
+ (set #addlaunchhandler 0)
+ )
+ ;else
+ (
+ (set #addlaunchhandler
+ (askbool
+ (prompt "Add NetSurf to launch-handler? (recommended)")
+ (help "launch-handler is part of OS4.1 which opens URLs "
+ " by launching a web browser.\n\n"
+ "The installation will update the configuration of "
+ "OS4.1 to allow URLs to be opened by NetSurf.")
+ (default 1)
+ )
+ )
+ )
+ )
+ )
+ ;else
+ (
+ (set #addlaunchhandler 0)
+ )
+)
+
+(complete 20)
+
+(set #runfixfonts
+ (askbool
+ (prompt "Installer will run FixFonts after NetSurf is installed. "
+ "Unless you have a *very* good reason you should not skip this step.")
+ (help "FixFonts corrects inconsistencies in the Amiga FONTS: structure. "
+ "Running it will prevent NetSurf hitting problems when the fonts are scanned.")
+ (default 1)
+ (choices "Run FixFonts" "Skip")
+ )
+)
+
+(complete 25)
+
+(working "Installing NetSurf")
+
+(if (= #AutoInstall 0)
+ (
+ (copyfiles
+ (prompt "Copying NetSurf...")
+ (help @copyfiles-help)
+ (source "NetSurf")
+ (dest @default-dest)
+ (newname "NetSurf")
+ (optional "askuser" "force" "oknodelete")
+ (confirm "expert")
+ )
+ )
+;else
+ (
+ (run "CopyStore NetSurf" @default-dest)
+ )
+)
+
+(complete 40)
+
+(if #searchengines-exist
+ (rename (tackon @default-dest "Resources/SearchEngines") (tackon @default-dest "Resources/SearchEngines.backup"))
+)
+
+(run "c:filenote Rexx/CloseTabs.nsrx \"Close other tabs\"")
+(run "c:filenote Rexx/ViewSource.nsrx \"View source\"")
+
+(complete 50)
+
+(copyfiles
+ (prompt "Copying files")
+ (source "")
+ (choices "Resources" "Rexx" "NetSurf.guide" #netsurf-readme)
+ (help @copyfiles-help)
+ (dest @default-dest)
+ (infos)
+ (optional "nofail")
+; (all)
+)
+
+(complete 65)
+
+(copyfiles
+ (prompt "Copying additional documentation")
+ (source "")
+ (pattern "(COPYING|ChangeLog)")
+ (help @copyfiles-help)
+ (dest @default-dest)
+ (infos)
+ (optional "nofail")
+)
+
+(complete 70)
+
+(if #themeshort
+ (
+ (set #theme-icon
+ (tackon "Resources/Themes"
+ (tackon #themeshort "NetSurf.info")
+ )
+ )
+
+ (if (exists #theme-icon)
+ (
+ (copyfiles
+ (prompt "Copying theme icon")
+ (help @copyfiles-help)
+ (source #theme-icon)
+ (dest @default-dest)
+ )
+ )
+ )
+
+ (makedir #user-dir)
+
+ (textfile
+ (prompt "Setting default options")
+ (help @textfile-help)
+ (dest #user-options)
+ (append "theme:" #theme "\n")
+ (append "pubscreen_name:Workbench\n")
+ )
+
+ (if (= #hotlist-exist 0)
+ (if (= #old-hotlist-exist 1)
+ (
+ (copyfiles
+ (prompt "Migrating NetSurf 2.x Hotlist")
+ (help @copyfiles-help)
+ (source #old-hotlist)
+ (dest #user-dir)
+ (optional "askuser" "force" "oknodelete")
+ )
+ )
+ )
+ )
+ )
+)
+
+(complete 75)
+
+(if (= (exists "ENVARC:Sys/def_css.info") 0)
+ (copyfiles
+ (prompt "Copying default CSS icon")
+ (source "Resources/default.css.info")
+ (newname "def_css.info")
+ (help @copyfiles-help)
+ (dest "ENVARC:Sys")
+ (optional "nofail")
+ )
+)
+
+(complete 80)
+
+(if (>= osver 53)
+ (if (= (exists "Rexx:NetSurf") 0)
+ (
+ (textfile
+ (prompt "Creating NetSurf launch helper")
+ (help @textfile-help)
+ (dest "Rexx:NetSurf")
+ (append "/*\n"
+" * Copyright 2013 Chris Young <chris@unsatisfactorysoftware.co.uk>\n"
+" *\n"
+" * This file is part of NetSurf, http://www.netsurf-browser.org/\n"
+" *\n"
+" * NetSurf is free software; you can redistribute it and/or modify\n"
+" * it under the terms of the GNU General Public License as published by\n"
+" * the Free Software Foundation; version 2 of the License.\n"
+" *\n"
+" * NetSurf is distributed in the hope that it will be useful,\n"
+" * but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+" * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
+" * GNU General Public License for more details.\n"
+" *\n"
+" * You should have received a copy of the GNU General Public License\n"
+" * along with this program. If not, see <http://www.gnu.org/licenses/>.\n"
+" */\n"
+"\n"
+"/* This is a convenience script for launching NetSurf from the Shell.\n"
+" * If NetSurf is already running it will open the supplied URL in a new tab.\n"
+" * This can be used in URL Prefs in place of directly calling APPDIR:NetSurf.\n"
+" */\n"
+"\n"
+"options results\n"
+"parse arg url\n"
+"\n"
+"if show('P', 'NETSURF') then do\n"
+" address NETSURF 'OPEN' url 'NEWTAB'\n"
+" address NETSURF 'TOFRONT'\n"
+"end\n"
+"else do\n"
+" address command 'APPDIR:NetSurf URL' url\n"
+"end\n")
+ )
+ (protect "Rexx:NetSurf" "+se")
+ )
+ )
+)
+
+(complete 85)
+
+(if (= #addlaunchhandler 1)
+ (
+ (working "Adding NetSurf to launch-handler config")
+ (if (= (p_chk_launch-handler "FILE.LH") 0)
+ (p_append "FILE.LH" "ClientName=\"NETSURF\" ClientPath=\"Rexx:NetSurf\" CMDFORMAT=\"*\"file:///%s*\"\"")
+ )
+
+ (if (= (p_chk_launch-handler "HTTP.LH") 0)
+ (p_append "HTTP.LH" "ClientName=\"NETSURF\" ClientPath=\"Rexx:NetSurf\" CMDFORMAT=\"*\"http://%s*\"\"")
+ )
+
+ (if (= (p_chk_launch-handler "HTTPS.LH") 0)
+ (p_append "HTTPS.LH" "ClientName=\"NETSURF\" ClientPath=\"Rexx:NetSurf\" CMDFORMAT=\"*\"https://%s*\"\"")
+ )
+
+ (if (= (p_chk_launch-handler "WWW.LH") 0)
+ (p_append "WWW.LH" "ClientName=\"NETSURF\" ClientPath=\"Rexx:NetSurf\" CMDFORMAT=\"*\"http://www.%s*\"\"")
+ )
+
+; (if (= (p_chk_launch-handler "FTP.LH") 0)
+; (p_append "FTP.LH" "ClientName=\"NETSURF\" ClientPath=\"Rexx:NetSurf\" CMDFORMAT=\"*\"ftp://%s*\"\"")
+; )
+ )
+)
+
+(complete 90)
+
+(working "Running FixFonts")
+
+(if #runfixfonts
+ (
+ (run "SYS:System/FixFonts")
+ )
+)
+
+(complete 100)
+
+(if (= #AutoInstall 1)
+ (
+ (exit (quiet))
+ )
+ (
+ (exit)
+ )
+)
diff --git a/frontends/amiga/dist/Install.info b/frontends/amiga/dist/Install.info
new file mode 100644
index 000000000..944fcc69d
--- /dev/null
+++ b/frontends/amiga/dist/Install.info
Binary files differ
diff --git a/frontends/amiga/dist/NetSurf.guide b/frontends/amiga/dist/NetSurf.guide
new file mode 100755
index 000000000..a1c3941df
--- /dev/null
+++ b/frontends/amiga/dist/NetSurf.guide
@@ -0,0 +1,411 @@
+@database NetSurf.guide
+@author Chris Young
+@wordwrap
+
+@node Main "NetSurf"
+@{b}NetSurf@{ub} Amiga-specific documentation
+http://www.netsurf-browser.org
+
+@{" GUI " link GUI}
+@{" Preferences GUI " link Prefs}
+
+@{" Command line options " link CLI}
+@{" Options file " link Options}
+@{" Fonts " link Fonts}
+@{" ARexx port " link ARexx}
+@{" OpenURL/URL Prefs " link OpenURL}
+@{" Hotlist menu " link Hotlist}
+@{" Hotlist toolbar " link HotlistToolbar}
+@{" Local MIME types " link MIMETypes}
+@{" Keyboard controls " link Keyboard}
+
+@{" Optimising for speed " link Speed}
+
+@{" Credits " link Contact}
+@endnode
+
+@node GUI "Main window"
+NetSurf's main GUI consists of a toolbar across the top, an (optional) tab bar, an (optional) hotlist toolbar and the main browser area. It also encompasses a pull-down @{"menu" link Menu}.
+
+The toolbar buttons are, from left to right:
+@{b}Back@{ub} Go back one page in history. Right-clicking gives a menu showing the last ten pages visited.
+@{b}Forward@{ub} Go forward one page in history. Right-clicking gives a menu showing up to ten pages available.
+@{b}Stop@{ub} Stop loading the page.
+@{b}Reload@{ub} Reload the current page. Shift-clicking will reload all elements on the page.
+@{b}Home@{ub} Load the home page.
+
+The gadget to the right of the buttons is the URL bar to type web addresses into. On the far right is the search bar. Typing into here will search for the text using your default search provider (see preferences @{"Advanced tab" link Prefs-Advanced}.
+
+Below these is the (optional) @{"hotlist toolbar" link HotlistToolbar}.
+Below this is the (optional) tab bar. This usually only displays when more than one tab is open, but can be set in @{"preferences" link Prefs-Tabs} to be always present. At the far right is a button to open a new tab. On OS4.0 the current tab can be closed using the button on the far left. On OS4.1 the close gadget for the tab is embedded in the tab itself.
+
+The rest of the window is taken up with the @{"browser rendering area" link Browser}. This is where the pages visited will be displayed.
+@endnode
+
+@node Browser
+@toc GUI
+This is the main browser rendering area.
+
+Drag and drop is supported throughout, so files can be dropped onto text fields, text on pages can be highlighted and dragged to Workbench or text fields. Other elements can be saved by dragging them to Workbench with Ctrl or Shift held down.
+
+Note that dragging to Workbench only works when NetSurf is running on the Workbench screen.
+@endnode
+
+@node Menu "Menu"
+@toc GUI
+Project Browser Edit @{"Hotlist" link Hotlist} @{"ARexx" link arexx}
+
+@endnode
+
+@node Prefs "Preferences GUI"
+@{"General" link Prefs-General} @{"Display" link Prefs-Display} @{"Connection" link Prefs-Connection} @{"Rendering" link Prefs-Rendering} @{"Fonts" link Prefs-Fonts} @{"Cache" link Prefs-Cache} @{"Tabs" link Prefs-Tabs} @{"Advanced" link Prefs-Advanced} @{"Export" link Prefs-Export}
+@endnode
+
+@node Prefs-General "Prefs - General"
+@toc Prefs
+@remark todo
+@endnode
+
+@node Prefs-Display "Prefs - Display"
+@toc Prefs
+@remark todo
+@{"Themes" link Themes}
+@endnode
+
+@node Prefs-Connection "Prefs - Connection"
+@toc Prefs
+@remark todo
+@endnode
+
+@node Prefs-Rendering "Prefs - Rendering"
+@toc Prefs
+@remark todo
+@endnode
+
+@node Prefs-Fonts "Prefs - Fonts"
+@toc Prefs
+@remark todo
+See @{"Fonts" link Fonts}
+@endnode
+
+@node Prefs-Cache "Prefs - Cache"
+@toc Prefs
+@remark todo
+@endnode
+
+@node Prefs-Tabs "Prefs - Tabs"
+@toc Prefs
+@remark todo
+@endnode
+
+@node Prefs-Advanced "Prefs - Advanced"
+@toc Prefs
+@remark todo
+@endnode
+
+@node Prefs-Export "Prefs - Export"
+@toc Prefs
+This section contains options for exporting to PDF. It is not enabled in current builds of NetSurf.
+@endnode
+
+@node cli "Command line options"
+NetSurf NSOPTS/M,URL/K,USERSDIR/K,FORCE/S
+
+Where:
+URL = Address of page to open on startup
+USERSDIR = Locations of user directories (see @{"tooltypes" link tooltypes})
+FORCE = Force new instance of NetSurf to open (has some limitations, use for debugging only)
+NSOPTS = Catches everything else and passes it to the NetSurf core command line parser
+@endnode
+
+@node tooltypes "ToolTypes"
+Supported tooltypes are:
+
+@{b}USERSDIR@{ub}
+Location of the Users directory. This should contain the "Users" part of the path, NetSurf will append the username. Defaults to PROGDIR:Users.
+The user directories contain user-specific preferences and cache data. Always locate them on fast, non-volatile, writeable media.
+
+@{b}USER@{ub}
+Current user. Defaults to the value of the USER env-var, or Default.
+@endnode
+
+@node options "Options file"
+The options file is stored in @{"Users/Default/Choices" link Users/Default/Choices/Main} by default. Most of the settings can be changed from within NetSurf by selecting Edit preferences from the Settings menu.
+
+There are a couple of Amiga-specific options which can only be changed directly in the file. These are:
+
+@{b}kiosk_mode@{ub} No gadgets
+@{b}printer_unit@{ub} Specifies which printer.device unit to print to
+@{b}drag_save_icons@{ub} Enables displaying Workbench-style transparent icons under the pointer when performing drag saves (ctrl-drag of objects available if NetSurf is running on the Workbench screen) and text selection drags. If set to 0 the pointer style will change instead. OS 4.0 users may want to set this to 0 as icons will appear opaque and obscure the drop position.
+@{b}monitor_aspect_x@{ub}/@{b}monitor_aspect_y@{ub} Correct aspect ratio for displays (default of 0 means "assume square pixels").
+@{b}screen_compositing@{ub} Use compositing on NetSurf's own screen. 0=disable, 1=enable, 2=default (NB: This is indirectly modified by changing the "simple refresh" option in the GUI)
+@{b}resize_with_contents@{ub} Set to 1 to respect GUI prefs' "resize with contents" option. Default is to use old-style "resize on release"
+@{b}reformat_delay@{ub} Sets a delay on performing content reformats (eg. if the window has been resized). Set to a higher value to make "resize with contents" more responsive. Defaults to 0 (immediate).
+@{b}redraw_tile_size_x@{ub}/@{b}redraw_tile_size_y@{ub} Specify the size of the off-screen bitmap. Higher will speed up redraws at the expense of memory. 0 disables tiling (will use a bitmap at least the size of the screen NetSurf is running on)
+@{b}web_search_width@{ub} Defaults to 0. Larger values will increase the size of the web search gadget next to the URL bar.
+
+@{b}mask_alpha@{ub} Threshold to use when determining which alpha values to convert to full transparency (0 - 255, where 255 will convert even opaque pixels to transparent). Defaults to 50 (0x32). This is only used in palette-mapped modes where alpha blending is not currently supported.
+
+@{b}url_file@{ub} Path to URL database file
+@{b}hotlist_file@{ub} Path to Hotlist file
+@{b}arexx_dir@{ub} Path to ARexx scripts dir
+@{b}arexx_startup@{ub} ARexx script to run at startup (in above dir)
+@{b}arexx_shutdown@{ub} ARexx script to run on quit (in above dir)
+@endnode
+
+@node Fonts
+The font to use for each font type can be defined in NetSurf's options. OS4 NetSurf supports soft styles for bold and italic, however designed fonts look better and it is highly recommended to set them as follows:
+
+Within @{"TypeManager" system "SYS:System/TypeManager"} select a font being used by NetSurf and click on Modify.
+On the Files tab, Font family section, choose the @{b}bold@{ub}, @{i}italic@{ui} and @{b}@{i}bold-italic@{ui}@{ub} version of the font.
+Click Save.
+
+@{b}Unicode fallback font@{ub}
+
+If the font NetSurf is trying to use does not contain a specific character used on a web page, it will try the Unicode/fallback font. It is recommended to use a font which either:
+(a) Contains as complete a Unicode character set as possible or
+(b) Is complete for the pages you are likely to visit (eg. if you visit a lot of Japanese pages, set the Unicode fallback font to one with a complete Japanese character set)
+
+For most users, installing and selecting @{"Code2000" rxs "address netsurf 'open http://code2000.sf.net'"} or @{"Bitstream Cyberbit" rxs "address netsurf 'open http://ftp.netscape.com/pub/communicator/extras/fonts/windows/'"} is the best option.
+
+Additional fall-back fonts can be provided since NetSurf 3.0. These need to go into Users/user/Choices as a comma-separated list, for the font_unicode option.
+
+NB: Since NetSurf 3.0, NetSurf will scan the provided Unicode fonts, and the rest of the system fonts, on first startup. Setting font_unicode_only:1 will prevent fonts not in the preferred Unicode fonts list from being scanned or used as fallback fonts. If the system fonts or NetSurf's fallback fonts list changes, this cache will need to be re-generated. This can be forced by deleting the font glyph cache (which defaults to Users/user/FontGlyphCache).
+
+Since NetSurf 3.4, Unicode glyphs above 0xFFFF are supported. These are mainly used for Emoji. The option to specify a fallback font for this range is font_surrogate - there is no scanning of system fonts. If @{"Symbola" rxs "address netsurf 'open http://users.teilar.gr/~g1951d/'"} font is installed it will be selected automatically.
+
+@{b}Font sizes@{ub}
+
+The default and minimum font sizes can also be set.
+NB: The resolution setting on the "Rendering" tab in NetSurf's preferences affects how big text appears on screen (the conversion between point and pixel sizes) amongst other things. To find the correct value, divide the number of pixels on the screen vertically by the physical height of the screen in inches (horizontal resolution is calculated automatically if NetSurf is running on a custom screen, square pixels are assumed on all other screens).
+If the monitor is widescreen, monitor_aspect_x and monitor_aspect_y values in Users/Default/Choices may also need modifying.
+@endnode
+
+@node Themes
+AmigaOS NetSurf supports basic theming of gadget imagery. Themes live in the Resources/Themes directory, and two are included by default:
+
+Default - default theme with no external dependencies.
+AISS - theme using Mason's AISS images for buttons, and the def_pointers package for 32-bit pointer images.
+
+The file "Theme" in the theme directory contains the files used for that theme. See @{"Default Theme file" link Resources/Themes/Default/Theme/Main} for more details.
+
+Theme directories should be copied and renamed if themes are modified, as update installation of NetSurf will overwrite any changes.
+AmigaOS NetSurf themes are not compatible with NetSurf for other platforms.
+@endnode
+
+@node arexx "ARexx port"
+NetSurf's ARexx port is called NETSURF.
+
+Commands are:
+
+@{b}OPEN URL/A,NEW=NEWWINDOW/S,NEWTAB/S,SAVEAS/K,W=WINDOW/K/N,T=TAB/K/N@{ub}
+Opens URL in current window or a new window/tab if NEWWINDOW/NEWTAB is specified. Saves the location without displaying if SAVEAS and a filename is specified (SAVEAS available in 2.6325)
+Note that if the URL is non-ASCII it is expected to be encoded in UTF-8 (file: references should always be in local charset due to filesystem limitations). Usually this is not relevant, as all normalised URLs will be in their ASCII form.
+
+@{b}SAVE FILENAME/A,W=WINDOW/K/N,T=TAB/K/N@{ub} (2.6027)
+Saves current page source to FILENAME
+
+@{b}QUIT@{ub}
+Quits NetSurf
+
+@{b}TOFRONT@{ub}
+Brings NetSurf's screen to the front
+
+@{b}GETURL W=WINDOW/K/N,T=TAB/K/N@{ub}
+Puts the URL displayed in the current window/tab into RESULT
+
+@{b}GETTITLE W=WINDOW/K/N,T=TAB/K/N@{ub}
+Puts the title of the page displayed in the current window/tab into RESULT
+
+@{b}GETSCREENNAME@{ub} (2.8303)
+Puts the name of the screen NetSurf is running on into RESULT.
+
+@{b}BACK W=WINDOW/K/N,T=TAB/K/N@{ub} (2.10626)
+Move back one page in history.
+
+@{b}FORWARD W=WINDOW/K/N,T=TAB/K/N@{ub} (2.10626)
+Move forward one page in history.
+
+@{b}HOME W=WINDOW/K/N,T=TAB/K/N@{ub} (2.10626)
+Move back to the home page.
+
+@{b}RELOAD FORCE/S,W=WINDOW/K/N,T=TAB/K/N@{ub} (2.10626)
+Reload the current page, FORCE will do a full reload.
+
+@{b}CLOSE W=WINDOW/K/N,T=TAB/K/N@{ub} (2.10718)
+Close the current page. A window or window and tab can be specified. Note that when a tab is closed, the tab number of tabs to the right of it will change, and the currently active tab may also change. If the last tab or window is closed, NetSurf will usually exit. Make sure you account for these situations in your code.
+
+@{b}VERSION VERSION/N REVISION/N RELEASE/S@{ub}
+Returns the current version of NetSurf in RESULT. You can also do version checking by supplying a VERSION and optional REVISION to check against. If the version of NetSurf is the same or higher 1 will be returned, if it is older 0. If RELEASE is specified, the command operates on the release version rather than the internal version number.
+
+@{b}ACTIVE T=TAB/S@{ub} (2.10718)
+Returns the active window (or tab if TAB is specified). Commands automatically operate on the active window/tab so you do not normally need to use this.
+
+@{b}WINDOWS W=WINDOW/K/N@{ub} (2.10656)
+Puts the number of windows into RESULT. If the WINDOW keyword is specified, will put the number of tabs in that window into RESULT.
+
+@{b}HOTLIST A=ACTION/A@{ub} (3.1 - CI build #471)
+Possible actions for the hotlist window are OPEN and CLOSE.
+
+The W=WINDOW/K/N,T=TAB/K/N parameters were added in 2.10656 and allow targetting a window other than the current one. Both WINDOW and TAB must be specified (TAB=1 for tabless window) except in the special case of the CLOSE command.
+
+The ARexx menu will be populated with scripts named #?.nsrx in @{"arexx_dir" link options 12}, up to a maximum of 20 entries. The titles of these entries will be the comments field of the file (or the filename if comments field is empty).
+
+Special scripts @{"arexx_startup" link options 13} and @{"arexx_shutdown" link options 14} will be run at startup and shutdown of NetSurf. These will execute after NetSurf has fully initialised with the initial window and before NetSurf frees resources (ie. at the last stage of startup, and the first stage of shutdown)
+@endnode
+
+@node OpenURL
+@{b}OpenURL configuration@{ub}
+
+Click Add on the Browsers page and fill in the details as follows:
+Name: NetSurf
+Path: NetSurf URL="%u"
+ARexx port: NETSURF
+Show:
+To front: TOFRONT
+Open URL: OPEN "%u"
+New window: OPEN "%u" NEW
+
+On the Misc tab, please ensure "Send mailto: URLs to email application" is set.
+
+@{b}URL Prefs (OS4.1)@{ub}
+Under OS4.1 Update 1, launch-handler is used in preference to OpenURL. The
+Installer script can add the relevant configuration to launch URLs in NetSurf.
+Please ensure your email application is configured in URL Prefs for mailto:
+links clicked within NetSurf.
+
+Note that a helper script is installed in S:ARexx which can be used instead of
+the main executable, to stop the NetSurf executable from being loaded again if
+it is already running.
+@endnode
+
+@node hotlist "Hotlist menu"
+Items from the hotlist can be added to the Hotlist menu as follows:
+
+Select Hotlist => Show hotlist...
+
+Items in the "Hotlist menu" folder node, up to a maximum (currently) of 40 items, will be added to the Hotlist menu, within the limits of the Intuition menu system.
+
+Items in folders within the Menu folder node will be converted to subitems in the menu.
+
+Folders more than one level down in the heirarchy will become menu items with no action. Items deeper will not be included in the menu at all (until we switch to using menuclass).
+
+Folders with no items in them will show up disabled in the menu. If they are named "--" they will be displayed as separator bars.
+
+eg.
+
+- Hotlist Menu
+ |
+ +- Netsurf
+ | |
+ | +- NetSurf Homepage
+ | |
+ | +- More NetSurf links
+ | |
+ | +- NetSurf bugtracker
+ |
+ +- --
+ |
+ +- Google
+
+Will look something like the following within the menu:
+
+|NetSurf »| - |NetSurf Homepage |
+|---------| |More NetSurf links|
+|Google |
+
+@endnode
+
+@node HotlistToolbar "Hotlist toolbar"
+A toolbar for frequently-accessed sites can be added to the main window. To do show, follow these steps:
+
+* Select Show Hotlist from the Hotlist menu
+The Hotlist window will be displayed.
+
+* Locate the "Hotlist toolbar" folder in the tree (NetSurf creates this when it starts up)
+
+* Move or create entries in this folder. Any entries directly inside the Hotlist toolbar folder will appear on the toolbar when the hotlist window is closed. If it is empty the toolbar will disappear to save space.
+
+Note that sub-folders are not currently supported on the toolbar.
+
+@endnode
+
+@node mimetypes "Local MIME Types"
+NetSurf determines the MIME types of local files primarily by checking the icon of the file. If the icon is not found it will check the default icon for the file type.
+
+It looks for a tooltype MIMETYPE and, if found, will use the contents as the filetype of the file. If not found it will use datatypes.library and do a reverse lookup based on the contents of Resources/mimetypes[.user]
+
+The Installer script will set the MIMETYPE tooltype on basic relevant default filetype icons. If you get problems:
+* If the file has a real icon, add MIMETYPE=<MIME type of file> to the tooltypes.
+* If the file does not have an icon, check:
+1. The file type is showing in DefIcons Prefs editor
+2. The icon ENVARC:Sys/def_<filetype> contains the MIMETYPE tooltype.
+@endnode
+
+@node Keyboard "Keyboard Controls"
+This is a list of the keyboard shortcuts used in NetSurf
+
+- RAmiga + R or F5 (reload the current page)
+- PageUp/Down/Space (scroll up/down to one page)
+- Home/End (go to top/bottom of the page)
+- Left/Right/Up/Down (scroll the page)
+- RAmiga + U (activate the URL bar)
+- Alt + Left/Right (keyboard navigation of tabs)
+- Backspace (go back to one page in history)
+
+Keyboard shortcuts used in NetSurf menu
+
+- RAmiga + N (open a new window)
+- RAmiga + T (open a new tab)
+- RAmiga + O (open a local file)
+- RAmiga + S (save source page)
+- RAmiga + K (close current tab)
+- RAmiga + P (print a page)
+- RAmiga + ? (about NetSurf)
+- RAmiga + Q (quit NetSurf)
+- RAmiga + X (cut)
+- RAmiga + C (copy)
+- RAmiga + V (paste)
+- RAmiga + A (select all)
+- RAmiga + Z (undo)
+- RAmiga + Y (redo)
+- RAmiga + F (find a string/text)
+- RAmiga + - or F9 (decrease scale)
+- RAmiga + = or F8 (normal scale)
+- RAmiga + + or F10 (increase scale)
+- RAmiga + B (add link to bookmark)
+- RAmiga + H (show bookmarks)
+- RAmiga + E (execute script)
+
+@endnode
+
+@node Speed "Optimising for speed"
+There are a number of options which can be changed that will affect the speed of NetSurf's rendering. Here are a list of the fastest settings which may help decrease rendering time on slower platforms:
+
+@{lindent 2}* Ensure NetSurf is running on a @{b}32-bit screen@{ub} if possible. NetSurf down-converts from 32-bit ARGB for display, which can impact performance.@{lindent 0}
+
+@{lindent 2}* In preferences, General tab, enable @{b}Fast scrolling@{ub}.@{lindent 0}
+
+@{lindent 2}* In preferences, Rendering tab set:
+@{b}Cache native versions@{ub} to @{b}Scaled@{ub} (or preferably @{b}All@{ub}, but this will use more graphics mem, and scaling images is a bigger performance hit)
+Deselect @{b}Higher quality scaling@{ub}, this will be very slow if not done in hardware.@{lindent 0}
+
+@{lindent 2}* In @{"Options" link Options}, increase redraw_tile_size_x/y (increasing this value uses more graphics mem)@{lindent 0}
+
+@{lindent 2}* In @{"Options" link Options}, set font_antialiasing:0@{lindent 0}
+@endnode
+
+@node contact "Credits"
+NetSurf was ported to AmigaOS 4 by Chris Young
+chris\@unsatisfactorysoftware.co.uk
+
+The pointer images, AISS theme icon and Throbber were drawn by Martin 'Mason' Merz.
+http://www.masonicons.de
+
+The default theme icon was adapted from the NetSurf logo by Marko K. Seppänen.
+
+All other code and files are the same for all platforms and credited in the files and/or on the NetSurf website.
+http://www.netsurf-browser.org
+
+The source code can be obtained from http://source.netsurf-browser.org or (in the event the service is unavailable) chris\@unsatisfactorysoftware.co.uk or any other of the NetSurf developers.
+@endnode
diff --git a/frontends/amiga/dist/NetSurf.guide.info b/frontends/amiga/dist/NetSurf.guide.info
new file mode 100644
index 000000000..d6d529d31
--- /dev/null
+++ b/frontends/amiga/dist/NetSurf.guide.info
Binary files differ
diff --git a/frontends/amiga/dist/Rexx.info b/frontends/amiga/dist/Rexx.info
new file mode 100644
index 000000000..908b01843
--- /dev/null
+++ b/frontends/amiga/dist/Rexx.info
Binary files differ
diff --git a/frontends/amiga/dist/Rexx/CloseTabs.nsrx b/frontends/amiga/dist/Rexx/CloseTabs.nsrx
new file mode 100644
index 000000000..276ae11a3
--- /dev/null
+++ b/frontends/amiga/dist/Rexx/CloseTabs.nsrx
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2010 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 script closes all tabs other than the current one */
+
+options results
+address netsurf
+
+/* Find the currently active window and tab */
+active
+awin = result
+active tab
+atab = result
+
+/* Find out how many tabs are in the active window */
+windows window awin
+tabs = result
+
+if tabs=1 then exit
+
+/* Close all tabs above the current one */
+do t=tabs to atab+1 by -1
+ close window awin tab t
+end
+
+/* Close all tabs below the current one
+ * NB: The active tab number will change when lower-numbered tabs are closed!
+ */
+
+do t=1 to atab-1
+ close window awin tab 1
+end
diff --git a/frontends/amiga/dist/Rexx/SMTube.nsrx b/frontends/amiga/dist/Rexx/SMTube.nsrx
new file mode 100644
index 000000000..5f1ab209a
--- /dev/null
+++ b/frontends/amiga/dist/Rexx/SMTube.nsrx
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 script launches SMTube (OS4Depot:video/play/smtube) */
+
+options results
+geturl
+
+if open('smtube','APPDIR:SMTube','R') then do
+ close('smtube')
+ address COMMAND 'APPDIR:SMTube' result
+end
+else
+ address command 'requestchoice >NIL: "NetSurf" "SMTube must be installed for this script to function.*n*nIt can be downloaded from OS4Depot:video/play/smtube" "OK"'
diff --git a/frontends/amiga/dist/Rexx/ShowTitles.nsrx b/frontends/amiga/dist/Rexx/ShowTitles.nsrx
new file mode 100644
index 000000000..27de74122
--- /dev/null
+++ b/frontends/amiga/dist/Rexx/ShowTitles.nsrx
@@ -0,0 +1,18 @@
+/* Show all NetSurf windows and tabs open */
+
+options results
+address netsurf
+
+windows
+wins = result
+
+do w=1 to wins
+ windows window w
+ tabs = result
+ say "Window" w "(" || tabs "tabs)"
+
+ do t=1 to tabs
+ gettitle window w tab t
+ say " Tab" t || ":" result
+ end
+end
diff --git a/frontends/amiga/dist/Rexx/viewsource.nsrx b/frontends/amiga/dist/Rexx/viewsource.nsrx
new file mode 100755
index 000000000..755e7a3b6
--- /dev/null
+++ b/frontends/amiga/dist/Rexx/viewsource.nsrx
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2009 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 scripts shows the source of the current page in Notepad */
+options results
+
+'save t:nstmp'
+getscreenname
+address command 'sys:utilities/notepad t:nstmp pubscreen' result
diff --git a/frontends/amiga/download.c b/frontends/amiga/download.c
new file mode 100644
index 000000000..a0bc5c47b
--- /dev/null
+++ b/frontends/amiga/download.c
@@ -0,0 +1,451 @@
+/*
+ * Copyright 2008-2010 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <string.h>
+
+#include <proto/wb.h>
+#include <proto/asl.h>
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/intuition.h>
+#include <proto/utility.h>
+#include <proto/icon.h>
+#ifdef __amigaos4__
+#include <proto/application.h>
+#endif
+
+#include <workbench/icon.h>
+
+#include <proto/window.h>
+#include <proto/layout.h>
+
+#include <proto/fuelgauge.h>
+#include <classes/window.h>
+#include <gadgets/fuelgauge.h>
+#include <gadgets/layout.h>
+
+#include <reaction/reaction_macros.h>
+
+#include "utils/errors.h"
+#include "utils/nsurl.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "utils/nsoption.h"
+#include "utils/string.h"
+#include "desktop/download.h"
+#include "desktop/save_complete.h"
+#include "desktop/browser.h"
+#include "desktop/mouse.h"
+#include "desktop/gui_window.h"
+#include "desktop/gui_download.h"
+#include "image/ico.h"
+
+#include "amiga/gui.h"
+#include "amiga/download.h"
+#include "amiga/object.h"
+#include "amiga/bitmap.h"
+#include "amiga/icon.h"
+#include "amiga/file.h"
+#include "amiga/drag.h"
+#include "amiga/iff_dr2d.h"
+#include "amiga/libs.h"
+#include "amiga/misc.h"
+#include "amiga/theme.h"
+#include "amiga/utf8.h"
+
+struct gui_download_window {
+ struct nsObject *node;
+ struct Window *win;
+ Object *objects[GID_LAST];
+ BPTR fh;
+ uint32 size;
+ uint32 downloaded;
+ struct dlnode *dln;
+ struct browser_window *bw;
+ struct download_context *ctx;
+ const char *url;
+ char fname[1024];
+ int result;
+};
+
+enum {
+ AMINS_DLOAD_OK = 0,
+ AMINS_DLOAD_ERROR,
+ AMINS_DLOAD_ABORT,
+};
+
+int downloads_in_progress = 0;
+
+static struct gui_download_window *gui_download_window_create(download_context *ctx,
+ struct gui_window *gui)
+{
+ const char *url = nsurl_access(download_context_get_url(ctx));
+ unsigned long total_size = download_context_get_total_length(ctx);
+ struct gui_download_window *dw;
+ char *dl_filename = ami_utf8_easy(download_context_get_filename(ctx));
+ APTR va[3];
+
+ dw = ami_misc_allocvec_clear(sizeof(struct gui_download_window), 0);
+
+ if(gui && (!IsListEmpty(&gui->dllist)) && (dw->dln = (struct dlnode *)FindName(&gui->dllist,url)))
+ {
+ strcpy(dw->fname, dw->dln->filename);
+ free(dw->dln->node.ln_Name);
+ dw->dln->node.ln_Name = NULL;
+ }
+ else
+ {
+ if(AslRequestTags(savereq,
+ ASLFR_Window, gui->shared->win,
+ ASLFR_SleepWindow, TRUE,
+ ASLFR_TitleText, messages_get("NetSurf"),
+ ASLFR_Screen, scrn,
+ ASLFR_InitialFile, dl_filename,
+ TAG_DONE))
+ {
+ strlcpy(dw->fname, savereq->fr_Drawer, 1024);
+ AddPart((STRPTR)&dw->fname,savereq->fr_File,1024);
+ if(!ami_download_check_overwrite(dw->fname, gui->shared->win, total_size))
+ {
+ FreeVec(dw);
+ return NULL;
+ }
+ }
+ else
+ {
+ FreeVec(dw);
+ return NULL;
+ }
+ }
+
+ if(dl_filename) ami_utf8_free(dl_filename);
+ dw->size = total_size;
+ dw->downloaded = 0;
+ if(gui) dw->bw = gui->bw;
+ dw->url = url;
+
+ va[0] = (APTR)dw->downloaded;
+ va[1] = (APTR)dw->size;
+ va[2] = 0;
+
+ if(!(dw->fh = FOpen((STRPTR)&dw->fname,MODE_NEWFILE,0)))
+ {
+ FreeVec(dw);
+ return NULL;
+ }
+
+ dw->objects[OID_MAIN] = WindowObj,
+ WA_ScreenTitle, ami_gui_get_screen_title(),
+ WA_Title, dw->url,
+ WA_Activate, TRUE,
+ WA_DepthGadget, TRUE,
+ WA_DragBar, TRUE,
+ WA_CloseGadget, FALSE,
+ WA_SizeGadget, TRUE,
+ WA_PubScreen,scrn,
+ WINDOW_SharedPort,sport,
+ WINDOW_UserData,dw,
+ WINDOW_IconifyGadget, FALSE,
+ WINDOW_LockHeight,TRUE,
+ WINDOW_Position, WPOS_CENTERSCREEN,
+ WINDOW_ParentGroup, dw->objects[GID_MAIN] = LayoutVObj,
+ LAYOUT_AddChild, dw->objects[GID_STATUS] = FuelGaugeObj,
+ GA_ID,GID_STATUS,
+ GA_Text,messages_get("amiDownload"),
+ FUELGAUGE_Min,0,
+ FUELGAUGE_Max,total_size,
+ FUELGAUGE_Level,0,
+ FUELGAUGE_Ticks,11,
+ FUELGAUGE_ShortTicks,TRUE,
+ FUELGAUGE_VarArgs,va,
+ FUELGAUGE_Percent,FALSE,
+ FUELGAUGE_Justification,FGJ_CENTER,
+ FuelGaugeEnd,
+ CHILD_NominalSize,TRUE,
+ CHILD_WeightedHeight,0,
+ LAYOUT_AddChild, dw->objects[GID_CANCEL] = ButtonObj,
+ GA_ID,GID_CANCEL,
+ GA_RelVerify,TRUE,
+ GA_Text,messages_get("Abort"),
+ GA_TabCycle,TRUE,
+ ButtonEnd,
+ EndGroup,
+ EndWindow;
+
+ dw->win = (struct Window *)RA_OpenWindow(dw->objects[OID_MAIN]);
+ dw->ctx = ctx;
+
+ dw->node = AddObject(window_list,AMINS_DLWINDOW);
+ dw->node->objstruct = dw;
+
+ downloads_in_progress++;
+
+ return dw;
+}
+
+static nserror gui_download_window_data(struct gui_download_window *dw,
+ const char *data, unsigned int size)
+{
+ APTR va[3];
+ if(!dw) return NSERROR_SAVE_FAILED;
+
+ FWrite(dw->fh,data,1,size);
+
+ dw->downloaded = dw->downloaded + size;
+
+ va[0] = (APTR)dw->downloaded;
+ va[1] = (APTR)dw->size;
+ va[2] = 0;
+
+ if(dw->size)
+ {
+ RefreshSetGadgetAttrs((struct Gadget *)dw->objects[GID_STATUS], dw->win, NULL,
+ FUELGAUGE_Level, dw->downloaded,
+ GA_Text, messages_get("amiDownload"),
+ FUELGAUGE_VarArgs, va,
+ TAG_DONE);
+ }
+ else
+ {
+ RefreshSetGadgetAttrs((struct Gadget *)dw->objects[GID_STATUS], dw->win, NULL,
+ FUELGAUGE_Level, dw->downloaded,
+ GA_Text, messages_get("amiDownloadU"),
+ FUELGAUGE_VarArgs, va,
+ TAG_DONE);
+ }
+
+ return NSERROR_OK;
+}
+
+static void gui_download_window_done(struct gui_download_window *dw)
+{
+ struct dlnode *dln,*dln2 = NULL;
+ struct browser_window *bw;
+ bool queuedl = false;
+
+ if(!dw) return;
+ bw = dw->bw;
+
+ if((nsoption_bool(download_notify)) && (dw->result == AMINS_DLOAD_OK))
+ {
+ Notify(ami_gui_get_app_id(), APPNOTIFY_Title, messages_get("amiDownloadComplete"),
+ APPNOTIFY_PubScreenName, "FRONT",
+ APPNOTIFY_BackMsg, dw->fname,
+ APPNOTIFY_CloseOnDC, TRUE,
+ APPNOTIFY_Text, dw->fname,
+ TAG_DONE);
+ }
+
+ download_context_destroy(dw->ctx);
+
+ if((dln = dw->dln))
+ {
+ dln2 = (struct dlnode *)GetSucc((struct Node *)dln);
+ if((dln!=dln2) && (dln2)) queuedl = true;
+
+ free(dln->filename);
+ Remove((struct Node *)dln);
+ FreeVec(dln);
+ }
+
+ FClose(dw->fh);
+ SetComment(dw->fname, dw->url);
+
+ downloads_in_progress--;
+
+ DisposeObject(dw->objects[OID_MAIN]);
+ DelObject(dw->node);
+ if(queuedl) {
+ nsurl *url;
+ if (nsurl_create(dln2->node.ln_Name, &url) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", 0);
+ } else {
+ browser_window_navigate(bw,
+ url,
+ NULL,
+ BW_NAVIGATE_DOWNLOAD,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ }
+ ami_try_quit(); /* In case the only window open was this download */
+}
+
+static void gui_download_window_error(struct gui_download_window *dw,
+ const char *error_msg)
+{
+ amiga_warn_user("Unwritten","");
+ dw->result = AMINS_DLOAD_ERROR;
+ gui_download_window_done(dw);
+}
+
+void ami_download_window_abort(struct gui_download_window *dw)
+{
+ download_context_abort(dw->ctx);
+ dw->result = AMINS_DLOAD_ABORT;
+ gui_download_window_done(dw);
+}
+
+BOOL ami_download_window_event(struct gui_download_window *dw)
+{
+ /* return TRUE if window destroyed */
+ ULONG result;
+ uint16 code;
+
+ while((result = RA_HandleInput(dw->objects[OID_MAIN], &code)) != WMHI_LASTMSG)
+ {
+ switch(result & WMHI_CLASSMASK) // class
+ {
+ case WMHI_GADGETUP:
+ switch(result & WMHI_GADGETMASK)
+ {
+ case GID_CANCEL:
+ ami_download_window_abort(dw);
+ return TRUE;
+ break;
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
+
+void ami_free_download_list(struct List *dllist)
+{
+ struct dlnode *node;
+ struct dlnode *nnode;
+
+ if(!dllist) return;
+ if(IsListEmpty(dllist)) return;
+
+ node = (struct dlnode *)GetHead((struct List *)dllist);
+
+ do
+ {
+ nnode=(struct dlnode *)GetSucc((struct Node *)node);
+ free(node->node.ln_Name);
+ free(node->filename);
+ Remove((struct Node *)node);
+ FreeVec((struct Node *)node);
+ }while((node=nnode));
+}
+
+nserror
+gui_window_save_link(struct gui_window *g, nsurl *url, const char *title)
+{
+ char fname[1024];
+ STRPTR openurlstring,linkname;
+ struct DiskObject *dobj = NULL;
+
+ linkname = ASPrintf("Link_to_%s",FilePart(nsurl_access(url)));
+
+ if(AslRequestTags(savereq,
+ ASLFR_Window, g->shared->win,
+ ASLFR_SleepWindow, TRUE,
+ ASLFR_TitleText,messages_get("NetSurf"),
+ ASLFR_Screen,scrn,
+ ASLFR_InitialFile,linkname,
+ TAG_DONE))
+ {
+ strlcpy(fname, savereq->fr_Drawer, 1024);
+ AddPart(fname,savereq->fr_File,1024);
+
+ ami_set_pointer(g->shared, GUI_POINTER_WAIT, false);
+
+ if(ami_download_check_overwrite(fname, g->shared->win, 0))
+ {
+ BPTR fh;
+
+ if((fh = FOpen(fname,MODE_NEWFILE,0)))
+ {
+ /* \todo Should be URLOpen on OS4.1 */
+ openurlstring = ASPrintf("openurl \"%s\"\n",nsurl_access(url));
+ FWrite(fh,openurlstring,1,strlen(openurlstring));
+ FClose(fh);
+ FreeVec(openurlstring);
+ SetComment(fname, nsurl_access(url));
+
+ dobj = GetIconTags(NULL,ICONGETA_GetDefaultName,"url",
+ ICONGETA_GetDefaultType,WBPROJECT,
+ TAG_DONE);
+
+ dobj->do_DefaultTool = "IconX";
+
+ PutIconTags(fname,dobj,
+ ICONPUTA_NotifyWorkbench,TRUE,
+ TAG_DONE);
+
+ FreeDiskObject(dobj);
+ }
+ FreeVec(linkname);
+ }
+ ami_reset_pointer(g->shared);
+ }
+ return NSERROR_OK;
+}
+
+BOOL ami_download_check_overwrite(const char *file, struct Window *win, ULONG size)
+{
+ /* Return TRUE if file can be (over-)written */
+ int32 res = 0;
+ BPTR lock = 0;
+ char *overwritetext;
+
+ if(nsoption_bool(ask_overwrite) == false) return TRUE;
+
+ lock = Lock(file, ACCESS_READ);
+
+ if(lock)
+ {
+ if(size) {
+ BPTR fh;
+ int64 oldsize = 0;
+
+ if((fh = OpenFromLock(lock))) {
+ oldsize = GetFileSize(fh);
+ Close(fh);
+ }
+ overwritetext = ASPrintf("%s\n\n%s %s\n%s %s",
+ messages_get("OverwriteFile"),
+ messages_get("amiSizeExisting"), human_friendly_bytesize((ULONG)oldsize),
+ messages_get("amiSizeNew"), human_friendly_bytesize(size));
+ } else {
+ UnLock(lock);
+ overwritetext = ASPrintf(messages_get("OverwriteFile"));
+ }
+
+ res = amiga_warn_user_multi(overwritetext, "Replace", "DontReplace", win);
+ FreeVec(overwritetext);
+ }
+ else return TRUE;
+
+ if(res == 1) return TRUE;
+ else return FALSE;
+}
+
+static struct gui_download_table download_table = {
+ .create = gui_download_window_create,
+ .data = gui_download_window_data,
+ .error = gui_download_window_error,
+ .done = gui_download_window_done,
+};
+
+struct gui_download_table *amiga_download_table = &download_table;
diff --git a/frontends/amiga/download.h b/frontends/amiga/download.h
new file mode 100755
index 000000000..51981ede8
--- /dev/null
+++ b/frontends/amiga/download.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2008-9 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_DOWNLOAD_H
+#define AMIGA_DOWNLOAD_H
+
+#include "amiga/os3support.h"
+
+extern struct gui_download_table *amiga_download_table;
+
+struct download_context;
+struct gui_download_window;
+struct gui_window;
+struct nsurl;
+
+struct dlnode
+{
+ struct Node node;
+ char *filename;
+};
+
+void ami_download_window_abort(struct gui_download_window *dw);
+BOOL ami_download_window_event(struct gui_download_window *dw);
+void ami_free_download_list(struct List *dllist);
+BOOL ami_download_check_overwrite(const char *file, struct Window *win, ULONG size);
+
+nserror gui_window_save_link(struct gui_window *g, struct nsurl *url, const char *title);
+
+#endif
diff --git a/frontends/amiga/drag.c b/frontends/amiga/drag.c
new file mode 100644
index 000000000..67d17223e
--- /dev/null
+++ b/frontends/amiga/drag.c
@@ -0,0 +1,352 @@
+/*
+ * Copyright 2010 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+#ifdef __amigaos4__
+#include <string.h>
+
+#include <proto/wb.h>
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/intuition.h>
+#include <proto/utility.h>
+#include <proto/icon.h>
+#include <proto/layers.h>
+
+#include <graphics/blitattr.h>
+#include <workbench/icon.h>
+
+#include "utils/errors.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "utils/nsoption.h"
+#include "content/hlcache.h"
+#include "desktop/mouse.h"
+
+#include "amiga/bitmap.h"
+#include "amiga/clipboard.h"
+#include "amiga/download.h"
+#include "amiga/drag.h"
+#include "amiga/file.h"
+#include "amiga/filetype.h"
+#include "amiga/gui.h"
+#include "amiga/theme.h"
+
+
+struct Window *drag_icon = NULL;
+ULONG drag_icon_width;
+ULONG drag_icon_height;
+BOOL drag_in_progress = FALSE;
+
+void gui_drag_save_object(struct gui_window *g, struct hlcache_handle *c,
+ gui_save_type type)
+{
+ const char *filetype = NULL;
+
+ /* Check we are running on Workbench */
+ if(nsoption_charp(pubscreen_name) == NULL) return;
+ if(strcmp(nsoption_charp(pubscreen_name), "Workbench")) return;
+
+ switch(type) {
+ case GUI_SAVE_OBJECT_ORIG: // object
+ case GUI_SAVE_SOURCE:
+ filetype = ami_mime_content_to_filetype(c);
+ break;
+ case GUI_SAVE_COMPLETE:
+ filetype = "drawer";
+ break;
+ case GUI_SAVE_OBJECT_NATIVE:
+#ifdef WITH_NS_SVG
+ if(ami_mime_compare(c, "svg") == true)
+ {
+ filetype = "dr2d";
+ }
+ else
+#endif
+ {
+ filetype = "ilbm";
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ ami_drag_icon_show(g->shared->win, filetype);
+
+ drag_save_data = c;
+ drag_save_gui = g;
+ drag_save = type;
+}
+
+void gui_drag_save_selection(struct gui_window *g, const char *selection)
+{
+ ami_drag_icon_show(g->shared->win, "ascii");
+
+ ami_autoscroll = TRUE;
+ drag_save_data = g;
+ drag_save = GUI_SAVE_TEXT_SELECTION;
+}
+
+void ami_drag_save(struct Window *win)
+{
+ ULONG which = WBO_NONE, type;
+ char path[1025], dpath[1025];
+
+ ami_drag_icon_close(NULL);
+ ami_autoscroll = FALSE;
+
+ if(nsoption_charp(pubscreen_name) && (strcmp(nsoption_charp(pubscreen_name),"Workbench") == 0))
+ {
+ which = WhichWorkbenchObject(NULL,scrn->MouseX,scrn->MouseY,
+ WBOBJA_Type,&type,
+ WBOBJA_FullPath,&path,
+ WBOBJA_FullPathSize,1024,
+ WBOBJA_DrawerPath,&dpath,
+ WBOBJA_DrawerPathSize,1024,
+ TAG_DONE);
+ }
+
+ if((which == WBO_DRAWER) || ((which == WBO_ICON) && (type > WBDRAWER)))
+ {
+ strcpy(path,dpath);
+ }
+ else if(which == WBO_NONE)
+ {
+ if(drag_save == GUI_SAVE_TEXT_SELECTION)
+ ami_drag_selection((struct gui_window *)drag_save_data);
+ else DisplayBeep(scrn);
+
+ drag_save = 0;
+ drag_save_data = NULL;
+ return;
+ }
+
+ if(path[0] == '\0')
+ {
+ DisplayBeep(scrn);
+ drag_save = 0;
+ drag_save_data = NULL;
+ return;
+ }
+
+ ami_update_pointer(win, GUI_POINTER_WAIT);
+
+ switch(drag_save)
+ {
+ case GUI_SAVE_OBJECT_ORIG: // object
+ case GUI_SAVE_SOURCE:
+ {
+ struct hlcache_handle *c = drag_save_data;
+
+ AddPart(path, content_get_title(c), 1024);
+ ami_file_save(AMINS_SAVE_SOURCE, path, win, c, NULL, NULL);
+ }
+ break;
+
+ case GUI_SAVE_TEXT_SELECTION: // selection
+ AddPart(path,"netsurf_text_selection",1024);
+ struct gui_window *g = (struct gui_window *) drag_save_data;
+ ami_file_save(AMINS_SAVE_SELECTION, path, win, NULL, NULL, g->bw);
+ break;
+
+ case GUI_SAVE_COMPLETE:
+ {
+ struct hlcache_handle *c = drag_save_data;
+
+ AddPart(path, content_get_title(c), 1024);
+ ami_file_save(AMINS_SAVE_COMPLETE, path, win, c, drag_save_gui->favicon, NULL);
+ }
+ break;
+
+ case GUI_SAVE_OBJECT_NATIVE:
+ {
+ hlcache_handle *c = drag_save_data;
+ AddPart(path, content_get_title(c), 1024);
+
+ ami_file_save(AMINS_SAVE_IFF, path, win, c, NULL, NULL);
+ }
+ break;
+
+ default:
+ LOG("Unsupported drag save operation %d", drag_save);
+ break;
+ }
+
+ drag_save = 0;
+ drag_save_data = NULL;
+
+ ami_update_pointer(win, GUI_POINTER_DEFAULT);
+}
+
+void ami_drag_icon_show(struct Window *win, const char *type)
+{
+ struct DiskObject *dobj = NULL;
+ ULONG width, height;
+ int deftype = WBPROJECT;
+
+ drag_in_progress = TRUE;
+
+ if(nsoption_bool(drag_save_icons) == false)
+ {
+ ami_update_pointer(win, AMI_GUI_POINTER_DRAG);
+ return;
+ }
+ else
+ {
+ ami_update_pointer(win, GUI_POINTER_DEFAULT);
+ }
+
+ if(!strcmp(type, "drawer")) deftype = WBDRAWER;
+
+ dobj = GetIconTags(NULL, ICONGETA_GetDefaultName, type,
+ ICONGETA_GetDefaultType, deftype,
+ TAG_DONE);
+
+ IconControl(dobj,
+ ICONCTRLA_GetWidth,&width,
+ ICONCTRLA_GetHeight,&height,
+ TAG_DONE);
+
+ drag_icon_width = width;
+ drag_icon_height = height;
+
+ drag_icon = OpenWindowTags(NULL,
+ WA_Left, scrn->MouseX - (width/2),
+ WA_Top, scrn->MouseY - (height/2),
+ WA_Width, width,
+ WA_Height, height,
+ WA_PubScreen, scrn,
+ WA_Borderless, TRUE,
+ WA_ToolBox, TRUE,
+ WA_StayTop, TRUE,
+ WA_Opaqueness, 128,
+ WA_OverrideOpaqueness, TRUE,
+ TAG_DONE);
+
+/* probably need layouticon and drawinfo stuff too */
+
+ DrawIconState(drag_icon->RPort, dobj, NULL, 0, 0, IDS_NORMAL,
+ ICONDRAWA_Frameless, TRUE,
+ ICONDRAWA_Borderless, TRUE,
+ TAG_DONE);
+}
+
+void ami_drag_icon_move(void)
+{
+ if(drag_icon == NULL) return;
+
+ ChangeWindowBox(drag_icon, scrn->MouseX - (drag_icon_width / 2),
+ scrn->MouseY - (drag_icon_height / 2),
+ drag_icon_width, drag_icon_height);
+}
+
+/**
+ * Close the drag icon (invisible) window if it is open
+ *
+ * \param win pointer to window to clear drag pointer
+ */
+
+void ami_drag_icon_close(struct Window *win)
+{
+ if(drag_icon) CloseWindow(drag_icon);
+ if(win) ami_update_pointer(win, GUI_POINTER_DEFAULT);
+ drag_icon = NULL;
+ drag_in_progress = FALSE;
+}
+
+BOOL ami_drag_in_progress(void)
+{
+ return drag_in_progress;
+}
+
+static void *ami_find_gwin_by_id(struct Window *win, uint32 type)
+{
+ struct nsObject *node, *nnode;
+ struct gui_window_2 *gwin;
+
+ if(!IsMinListEmpty(window_list))
+ {
+ node = (struct nsObject *)GetHead((struct List *)window_list);
+
+ do
+ {
+ nnode=(struct nsObject *)GetSucc((struct Node *)node);
+
+ if(node->Type == type)
+ {
+ gwin = node->objstruct;
+ if(win == gwin->win) return gwin;
+ }
+ } while((node = nnode));
+ }
+ return NULL;
+}
+
+void *ami_window_at_pointer(int type)
+{
+ struct Layer *layer;
+
+ LockLayerInfo(&scrn->LayerInfo);
+
+ layer = WhichLayer(&scrn->LayerInfo, scrn->MouseX, scrn->MouseY);
+
+ UnlockLayerInfo(&scrn->LayerInfo);
+
+ if(layer) return ami_find_gwin_by_id(layer->Window, type);
+ else return NULL;
+}
+
+#else
+#include "amiga/drag.h"
+
+void gui_drag_save_object(struct gui_window *g, struct hlcache_handle *c,
+ gui_save_type type)
+{
+}
+
+void gui_drag_save_selection(struct gui_window *g, const char *selection)
+{
+}
+
+void ami_drag_save(struct Window *win)
+{
+}
+
+void ami_drag_icon_show(struct Window *win, const char *type)
+{
+}
+
+void ami_drag_icon_close(struct Window *win)
+{
+}
+
+void ami_drag_icon_move(void)
+{
+}
+
+BOOL ami_drag_in_progress(void)
+{
+ return FALSE;
+}
+
+void *ami_window_at_pointer(int type)
+{
+ return NULL;
+}
+#endif
+
diff --git a/frontends/amiga/drag.h b/frontends/amiga/drag.h
new file mode 100644
index 000000000..59a1a8467
--- /dev/null
+++ b/frontends/amiga/drag.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2010 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_DRAG_H
+#define AMIGA_DRAG_H
+#include <exec/types.h>
+#include "desktop/browser.h"
+#include "desktop/gui_window.h"
+
+#define AMI_DRAG_THRESHOLD 10
+
+struct hlcache_handle;
+struct Window;
+
+int drag_save;
+void *drag_save_data;
+struct gui_window *drag_save_gui;
+
+void gui_drag_save_selection(struct gui_window *g, const char *selection);
+void gui_drag_save_object(struct gui_window *g, struct hlcache_handle *c, gui_save_type type);
+
+void ami_drag_save(struct Window *win);
+void ami_drag_icon_show(struct Window *win, const char *type);
+void ami_drag_icon_close(struct Window *win);
+void ami_drag_icon_move(void);
+BOOL ami_drag_in_progress(void);
+
+void *ami_window_at_pointer(int type);
+#endif
+
diff --git a/frontends/amiga/dt_anim.c b/frontends/amiga/dt_anim.c
new file mode 100644
index 000000000..a3bfb1d66
--- /dev/null
+++ b/frontends/amiga/dt_anim.c
@@ -0,0 +1,365 @@
+/*
+ * Copyright 2011 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * DataTypes animation handler (implementation)
+*/
+
+#ifdef WITH_AMIGA_DATATYPES
+#include "amiga/os3support.h"
+
+#include <stdlib.h>
+#include <proto/datatypes.h>
+#include <proto/dos.h>
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <datatypes/animationclass.h>
+#include <datatypes/pictureclass.h>
+#ifdef __amigaos4__
+#include <graphics/blitattr.h>
+#endif
+#include <intuition/classusr.h>
+
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "content/content_protected.h"
+#include "desktop/plotters.h"
+#include "image/bitmap.h"
+
+#include "amiga/bitmap.h"
+#include "amiga/filetype.h"
+#include "amiga/datatypes.h"
+#include "amiga/misc.h"
+#include "amiga/plotters.h"
+
+typedef struct amiga_dt_anim_content {
+ struct content base;
+
+ struct bitmap *bitmap; /**< Created NetSurf bitmap */
+
+ Object *dto;
+ int x;
+ int y;
+ int w;
+ int h;
+} amiga_dt_anim_content;
+
+APTR ami_colormap_to_clut(struct ColorMap *cmap);
+
+static nserror amiga_dt_anim_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c);
+static bool amiga_dt_anim_convert(struct content *c);
+static void amiga_dt_anim_reformat(struct content *c, int width, int height);
+static void amiga_dt_anim_destroy(struct content *c);
+static bool amiga_dt_anim_redraw(struct content *c,
+ struct content_redraw_data *data, const struct rect *clip,
+ const struct redraw_context *ctx);
+static void amiga_dt_anim_open(struct content *c, struct browser_window *bw,
+ struct content *page, struct object_params *params);
+static void amiga_dt_anim_close(struct content *c);
+static nserror amiga_dt_anim_clone(const struct content *old, struct content **newc);
+static content_type amiga_dt_anim_content_type(void);
+
+static void *amiga_dt_anim_get_internal(const struct content *c, void *context)
+{
+ amiga_dt_anim_content *adta_c = (amiga_dt_anim_content *)c;
+
+ return adta_c->bitmap;
+}
+
+static const content_handler amiga_dt_anim_content_handler = {
+ .create = amiga_dt_anim_create,
+ .data_complete = amiga_dt_anim_convert,
+ .reformat = amiga_dt_anim_reformat,
+ .destroy = amiga_dt_anim_destroy,
+ .redraw = amiga_dt_anim_redraw,
+ .open = amiga_dt_anim_open,
+ .close = amiga_dt_anim_close,
+ .clone = amiga_dt_anim_clone,
+ .get_internal = amiga_dt_anim_get_internal,
+ .type = amiga_dt_anim_content_type,
+ .no_share = false,
+};
+
+nserror amiga_dt_anim_init(void)
+{
+ struct DataType *dt, *prevdt = NULL;
+ lwc_string *type;
+ nserror error;
+ struct Node *node = NULL;
+
+ while((dt = ObtainDataType(DTST_RAM, NULL,
+ DTA_DataType, prevdt,
+ DTA_GroupID, GID_ANIMATION,
+ TAG_DONE)) != NULL)
+ {
+ ReleaseDataType(prevdt);
+ prevdt = dt;
+
+ do {
+ node = ami_mime_from_datatype(dt, &type, node);
+
+ if(node)
+ {
+ error = content_factory_register_handler(
+ lwc_string_data(type),
+ &amiga_dt_anim_content_handler);
+
+ if (error != NSERROR_OK)
+ return error;
+ }
+
+ }while (node != NULL);
+
+ }
+
+ ReleaseDataType(prevdt);
+
+ return NSERROR_OK;
+}
+
+nserror amiga_dt_anim_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c)
+{
+ amiga_dt_anim_content *plugin;
+ nserror error;
+
+ plugin = calloc(1, sizeof(amiga_dt_anim_content));
+ if (plugin == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__init(&plugin->base, handler, imime_type, params,
+ llcache, fallback_charset, quirks);
+ if (error != NSERROR_OK) {
+ free(plugin);
+ return error;
+ }
+
+ *c = (struct content *) plugin;
+
+ return NSERROR_OK;
+}
+
+bool amiga_dt_anim_convert(struct content *c)
+{
+ LOG("amiga_dt_anim_convert");
+
+ amiga_dt_anim_content *plugin = (amiga_dt_anim_content *) c;
+ union content_msg_data msg_data;
+ int width, height;
+ const uint8 *data;
+ UBYTE *bm_buffer;
+ ULONG size;
+ struct BitMapHeader *bmh;
+ unsigned int bm_flags = BITMAP_NEW | BITMAP_OPAQUE;
+ struct adtFrame adt_frame;
+ APTR clut;
+
+ data = (uint8 *)content__get_source_data(c, &size);
+
+ if((plugin->dto = NewDTObject(NULL,
+ DTA_SourceType, DTST_MEMORY,
+ DTA_SourceAddress, data,
+ DTA_SourceSize, size,
+ DTA_GroupID, GID_ANIMATION,
+ TAG_DONE))) {
+ if(GetDTAttrs(plugin->dto, PDTA_BitMapHeader, &bmh, TAG_DONE)) {
+ width = (int)bmh->bmh_Width;
+ height = (int)bmh->bmh_Height;
+
+ plugin->bitmap = amiga_bitmap_create(width, height, bm_flags);
+ if (!plugin->bitmap) {
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ bm_buffer = amiga_bitmap_get_buffer(plugin->bitmap);
+
+ adt_frame.MethodID = ADTM_LOADFRAME;
+ adt_frame.alf_TimeStamp = 0;
+ IDoMethodA(plugin->dto, (Msg)&adt_frame);
+
+ clut = ami_colormap_to_clut(adt_frame.alf_CMap);
+#ifdef __amigaos4__
+ BltBitMapTags(
+ BLITA_Width, width,
+ BLITA_Height, height,
+ BLITA_Source, adt_frame.alf_BitMap,
+ BLITA_SrcType, BLITT_BITMAP,
+ BLITA_Dest, bm_buffer,
+ BLITA_DestType, BLITT_RGB24,
+ BLITA_DestBytesPerRow, width,
+ BLITA_CLUT, clut,
+ TAG_DONE);
+#else
+#warning FIXME: Need to use a different blitter function for OS3!
+#endif
+ FreeVec(clut);
+
+ adt_frame.MethodID = ADTM_UNLOADFRAME;
+ IDoMethodA(plugin->dto, (Msg)&adt_frame);
+ }
+ else return false;
+ }
+ else return false;
+
+ c->width = width;
+ c->height = height;
+
+/*
+ snprintf(title, sizeof(title), "image (%lux%lu, %lu bytes)",
+ width, height, size);
+ content__set_title(c, title);
+*/
+
+ amiga_bitmap_modified(plugin->bitmap);
+
+ content_set_ready(c);
+ content_set_done(c);
+
+ content_set_status(c, "");
+ return true;
+}
+
+void amiga_dt_anim_destroy(struct content *c)
+{
+ amiga_dt_anim_content *plugin = (amiga_dt_anim_content *) c;
+
+ LOG("amiga_dt_anim_destroy");
+
+ if (plugin->bitmap != NULL)
+ amiga_bitmap_destroy(plugin->bitmap);
+
+ DisposeDTObject(plugin->dto);
+
+ return;
+}
+
+bool amiga_dt_anim_redraw(struct content *c,
+ struct content_redraw_data *data, const struct rect *clip,
+ const struct redraw_context *ctx)
+{
+ amiga_dt_anim_content *plugin = (amiga_dt_anim_content *) c;
+ bitmap_flags_t flags = BITMAPF_NONE;
+
+ LOG("amiga_dt_anim_redraw");
+
+ if (data->repeat_x)
+ flags |= BITMAPF_REPEAT_X;
+ if (data->repeat_y)
+ flags |= BITMAPF_REPEAT_Y;
+
+ return ctx->plot->bitmap(data->x, data->y, data->width, data->height,
+ plugin->bitmap, data->background_colour, flags);
+}
+
+/**
+ * Handle a window containing a CONTENT_PLUGIN being opened.
+ *
+ * \param c content that has been opened
+ * \param bw browser window containing the content
+ * \param page content of type CONTENT_HTML containing c, or 0 if not an
+ * object within a page
+ * \param box box containing c, or 0 if not an object
+ * \param params object parameters, or 0 if not an object
+ */
+void amiga_dt_anim_open(struct content *c, struct browser_window *bw,
+ struct content *page, struct object_params *params)
+{
+ LOG("amiga_dt_anim_open");
+
+ return;
+}
+
+void amiga_dt_anim_close(struct content *c)
+{
+ LOG("amiga_dt_anim_close");
+ return;
+}
+
+void amiga_dt_anim_reformat(struct content *c, int width, int height)
+{
+ LOG("amiga_dt_anim_reformat");
+ return;
+}
+
+nserror amiga_dt_anim_clone(const struct content *old, struct content **newc)
+{
+ amiga_dt_anim_content *plugin;
+ nserror error;
+
+ LOG("amiga_dt_anim_clone");
+
+ plugin = calloc(1, sizeof(amiga_dt_anim_content));
+ if (plugin == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__clone(old, &plugin->base);
+ if (error != NSERROR_OK) {
+ content_destroy(&plugin->base);
+ return error;
+ }
+
+ /* We "clone" the old content by replaying conversion */
+ if (old->status == CONTENT_STATUS_READY ||
+ old->status == CONTENT_STATUS_DONE) {
+ if (amiga_dt_anim_convert(&plugin->base) == false) {
+ content_destroy(&plugin->base);
+ return NSERROR_CLONE_FAILED;
+ }
+ }
+
+ *newc = (struct content *) plugin;
+
+ return NSERROR_OK;
+}
+
+content_type amiga_dt_anim_content_type(void)
+{
+ return CONTENT_IMAGE;
+}
+
+APTR ami_colormap_to_clut(struct ColorMap *cmap)
+{
+ int i;
+ UBYTE *clut = ami_misc_allocvec_clear(256 * 4, 0); /* NB: Was not MEMF_PRIVATE */
+ ULONG colr[256 * 4];
+
+ if(!clut) return NULL;
+
+ /* Get the palette from the ColorMap */
+ GetRGB32(cmap, 0, 256, (ULONG *)&colr);
+
+ /* convert it to a table of ARGB values */
+ for(i = 0; i < 1024; i += 4)
+ {
+ clut[i] = (0xff << 24) |
+ ((colr[i] & 0xff000000) >> 8) |
+ ((colr[i + 1] & 0xff000000) >> 16) |
+ ((colr[i + 2] & 0xff000000) >> 24);
+ }
+
+ return clut;
+}
+
+#endif
diff --git a/frontends/amiga/dt_picture.c b/frontends/amiga/dt_picture.c
new file mode 100644
index 000000000..aa2418dca
--- /dev/null
+++ b/frontends/amiga/dt_picture.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2011 - 2012 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * DataTypes picture handler (implementation)
+*/
+
+#ifdef WITH_AMIGA_DATATYPES
+#include "amiga/os3support.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <proto/datatypes.h>
+#include <proto/dos.h>
+#include <proto/intuition.h>
+#include <datatypes/pictureclass.h>
+#include <intuition/classusr.h>
+
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "content/content_protected.h"
+#include "desktop/plotters.h"
+#include "image/bitmap.h"
+#include "image/image_cache.h"
+
+#include "amiga/bitmap.h"
+#include "amiga/filetype.h"
+#include "amiga/datatypes.h"
+
+
+static nserror amiga_dt_picture_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c);
+static bool amiga_dt_picture_convert(struct content *c);
+static nserror amiga_dt_picture_clone(const struct content *old, struct content **newc);
+static void amiga_dt_picture_destroy(struct content *c);
+
+static const content_handler amiga_dt_picture_content_handler = {
+ .create = amiga_dt_picture_create,
+ .data_complete = amiga_dt_picture_convert,
+ .destroy = amiga_dt_picture_destroy,
+ .redraw = image_cache_redraw,
+ .clone = amiga_dt_picture_clone,
+ .get_internal = image_cache_get_internal,
+ .type = image_cache_content_type,
+ .no_share = false,
+};
+
+struct amiga_dt_picture_content {
+ struct content c;
+ Object *dto;
+};
+
+nserror amiga_dt_picture_init(void)
+{
+ struct DataType *dt, *prevdt = NULL;
+ lwc_string *type;
+ nserror error;
+ struct Node *node = NULL;
+
+ while((dt = ObtainDataType(DTST_RAM, NULL,
+ DTA_DataType, prevdt,
+ DTA_GroupID, GID_PICTURE, // we only support images for now
+ TAG_DONE)) != NULL)
+ {
+ if(prevdt) ReleaseDataType(prevdt);
+ prevdt = dt;
+
+ do {
+ node = ami_mime_from_datatype(dt, &type, node);
+
+ if(node)
+ {
+ error = content_factory_register_handler(
+ lwc_string_data(type),
+ &amiga_dt_picture_content_handler);
+
+ if (error != NSERROR_OK)
+ return error;
+ }
+
+ }while (node != NULL);
+
+ }
+
+ ReleaseDataType(prevdt);
+
+ return NSERROR_OK;
+}
+
+nserror amiga_dt_picture_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c)
+{
+ struct amiga_dt_picture_content *adt;
+ nserror error;
+
+ adt = calloc(1, sizeof(struct amiga_dt_picture_content));
+ if (adt == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__init((struct content *)adt, handler, imime_type, params,
+ llcache, fallback_charset, quirks);
+ if (error != NSERROR_OK) {
+ free(adt);
+ return error;
+ }
+
+ *c = (struct content *)adt;
+
+ return NSERROR_OK;
+}
+
+static Object *amiga_dt_picture_newdtobject(struct amiga_dt_picture_content *adt)
+{
+ const uint8 *data;
+ ULONG size;
+
+ if(adt->dto == NULL) {
+ data = (uint8 *)content__get_source_data((struct content *)adt, &size);
+
+ adt->dto = NewDTObject(NULL,
+ DTA_SourceType, DTST_MEMORY,
+ DTA_SourceAddress, data,
+ DTA_SourceSize, size,
+ DTA_GroupID, GID_PICTURE,
+ PDTA_DestMode, PMODE_V43,
+ PDTA_PromoteMask, TRUE,
+ TAG_DONE);
+ }
+
+ return adt->dto;
+}
+
+static char *amiga_dt_picture_datatype(struct content *c)
+{
+ const uint8 *data;
+ ULONG size;
+ struct DataType *dt;
+ char *filetype = NULL;
+
+ data = (uint8 *)content__get_source_data(c, &size);
+
+ if((dt = ObtainDataType(DTST_MEMORY, NULL,
+ DTA_SourceAddress, data,
+ DTA_SourceSize, size,
+ DTA_GroupID, GID_PICTURE,
+ TAG_DONE))) {
+ filetype = strdup(dt->dtn_Header->dth_Name);
+ ReleaseDataType(dt);
+ }
+
+ if(filetype == NULL) filetype = strdup("DataTypes");
+ return filetype;
+}
+
+static struct bitmap *amiga_dt_picture_cache_convert(struct content *c)
+{
+ LOG("amiga_dt_picture_cache_convert");
+
+ union content_msg_data msg_data;
+ UBYTE *bm_buffer;
+ Object *dto;
+ struct bitmap *bitmap;
+ struct amiga_dt_picture_content *adt = (struct amiga_dt_picture_content *)c;
+
+ if((dto = amiga_dt_picture_newdtobject(adt)))
+ {
+ bitmap = amiga_bitmap_create(c->width, c->height, BITMAP_NEW);
+ if (!bitmap) {
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return NULL;
+ }
+
+ bm_buffer = amiga_bitmap_get_buffer(bitmap);
+
+ IDoMethod(dto, PDTM_READPIXELARRAY,
+ bm_buffer, PBPAFMT_RGBA,
+ amiga_bitmap_get_rowstride(bitmap),
+ 0, 0, c->width, c->height);
+
+ amiga_bitmap_set_opaque(bitmap, amiga_bitmap_test_opaque(bitmap));
+
+ DisposeDTObject(dto);
+ adt->dto = NULL;
+ }
+ else return NULL;
+
+ return bitmap;
+}
+
+bool amiga_dt_picture_convert(struct content *c)
+{
+ LOG("amiga_dt_picture_convert");
+
+ int width, height;
+ char *title;
+ Object *dto;
+ struct BitMapHeader *bmh;
+ char *filetype;
+
+ if((dto = amiga_dt_picture_newdtobject((struct amiga_dt_picture_content *)c))) {
+ if(GetDTAttrs(dto, PDTA_BitMapHeader, &bmh, TAG_DONE)) {
+ width = (int)bmh->bmh_Width;
+ height = (int)bmh->bmh_Height;
+ }
+ else return false;
+ }
+ else return false;
+
+ c->width = width;
+ c->height = height;
+ c->size = width * height * 4;
+
+ /* set title text */
+ if((filetype = amiga_dt_picture_datatype(c))) {
+ title = messages_get_buff("DataTypesTitle",
+ nsurl_access_leaf(llcache_handle_get_url(c->llcache)),
+ filetype, c->width, c->height);
+ if (title != NULL) {
+ content__set_title(c, title);
+ free(title);
+ }
+ free(filetype);
+ }
+
+ image_cache_add(c, NULL, amiga_dt_picture_cache_convert);
+
+ content_set_ready(c);
+ content_set_done(c);
+ content_set_status(c, "");
+ return true;
+}
+
+nserror amiga_dt_picture_clone(const struct content *old, struct content **newc)
+{
+ struct content *adt;
+ nserror error;
+
+ LOG("amiga_dt_picture_clone");
+
+ adt = calloc(1, sizeof(struct content));
+ if (adt == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__clone(old, adt);
+ if (error != NSERROR_OK) {
+ content_destroy(adt);
+ return error;
+ }
+
+ /* We "clone" the old content by replaying conversion */
+ if ((old->status == CONTENT_STATUS_READY) ||
+ (old->status == CONTENT_STATUS_DONE)) {
+ if (amiga_dt_picture_convert(adt) == false) {
+ content_destroy(adt);
+ return NSERROR_CLONE_FAILED;
+ }
+ }
+
+ *newc = adt;
+
+ return NSERROR_OK;
+}
+
+static void amiga_dt_picture_destroy(struct content *c)
+{
+ struct amiga_dt_picture_content *adt = (struct amiga_dt_picture_content *)c;
+
+ DisposeDTObject(adt->dto);
+ adt->dto = NULL;
+
+ image_cache_destroy(c);
+}
+
+#endif
diff --git a/frontends/amiga/dt_sound.c b/frontends/amiga/dt_sound.c
new file mode 100644
index 000000000..fe1b1fc43
--- /dev/null
+++ b/frontends/amiga/dt_sound.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2011 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * DataTypes sound handler (implementation)
+*/
+
+#ifdef WITH_AMIGA_DATATYPES
+#include "amiga/os3support.h"
+
+#include "amiga/filetype.h"
+#include "amiga/datatypes.h"
+#include "content/content_protected.h"
+#include "desktop/plotters.h"
+#include "render/box.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+
+#include <proto/datatypes.h>
+#include <proto/dos.h>
+#include <proto/intuition.h>
+#include <datatypes/soundclass.h>
+#include <intuition/classusr.h>
+
+typedef struct amiga_dt_sound_content {
+ struct content base;
+
+ Object *dto;
+ bool immediate;
+} amiga_dt_sound_content;
+
+static nserror amiga_dt_sound_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c);
+static bool amiga_dt_sound_convert(struct content *c);
+static void amiga_dt_sound_destroy(struct content *c);
+static bool amiga_dt_sound_redraw(struct content *c,
+ struct content_redraw_data *data, const struct rect *clip,
+ const struct redraw_context *ctx);
+static void amiga_dt_sound_open(struct content *c, struct browser_window *bw,
+ struct content *page, struct object_params *params);
+static nserror amiga_dt_sound_clone(const struct content *old, struct content **newc);
+static content_type amiga_dt_sound_content_type(void);
+
+static const content_handler amiga_dt_sound_content_handler = {
+ .create = amiga_dt_sound_create,
+ .data_complete = amiga_dt_sound_convert,
+ .destroy = amiga_dt_sound_destroy,
+ .redraw = amiga_dt_sound_redraw,
+ .open = amiga_dt_sound_open,
+ .clone = amiga_dt_sound_clone,
+ .type = amiga_dt_sound_content_type,
+ .no_share = false,
+};
+
+
+static void amiga_dt_sound_play(Object *dto)
+{
+ LOG("Playing...");
+ IDoMethod(dto, DTM_TRIGGER, NULL, STM_PLAY, NULL);
+}
+
+
+nserror amiga_dt_sound_init(void)
+{
+ struct DataType *dt, *prevdt = NULL;
+ lwc_string *type;
+ nserror error;
+ struct Node *node = NULL;
+
+ while((dt = ObtainDataType(DTST_RAM, NULL,
+ DTA_DataType, prevdt,
+ DTA_GroupID, GID_SOUND,
+ TAG_DONE)) != NULL)
+ {
+ ReleaseDataType(prevdt);
+ prevdt = dt;
+
+ do {
+ node = ami_mime_from_datatype(dt, &type, node);
+
+ if(node)
+ {
+ error = content_factory_register_handler(
+ lwc_string_data(type),
+ &amiga_dt_sound_content_handler);
+
+ if (error != NSERROR_OK)
+ return error;
+ }
+
+ }while (node != NULL);
+
+ }
+
+ ReleaseDataType(prevdt);
+
+ return NSERROR_OK;
+}
+
+nserror amiga_dt_sound_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c)
+{
+ amiga_dt_sound_content *plugin;
+ nserror error;
+
+ LOG("amiga_dt_sound_create");
+
+ plugin = calloc(1, sizeof(amiga_dt_sound_content));
+ if (plugin == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__init(&plugin->base, handler, imime_type, params,
+ llcache, fallback_charset, quirks);
+ if (error != NSERROR_OK) {
+ free(plugin);
+ return error;
+ }
+
+ *c = (struct content *) plugin;
+
+ return NSERROR_OK;
+}
+
+bool amiga_dt_sound_convert(struct content *c)
+{
+ LOG("amiga_dt_sound_convert");
+
+ amiga_dt_sound_content *plugin = (amiga_dt_sound_content *) c;
+ int width = 50, height = 50;
+ const uint8 *data;
+ ULONG size;
+
+ data = (uint8 *)content__get_source_data(c, &size);
+
+ plugin->dto = NewDTObject(NULL,
+ DTA_SourceType, DTST_MEMORY,
+ DTA_SourceAddress, data,
+ DTA_SourceSize, size,
+ DTA_GroupID, GID_SOUND,
+ TAG_DONE);
+
+ if(plugin->dto == NULL) return false;
+
+ c->width = width;
+ c->height = height;
+
+ if(plugin->immediate == true) amiga_dt_sound_play(plugin->dto);
+
+ content_set_ready(c);
+ content_set_done(c);
+
+ content_set_status(c, "");
+ return true;
+}
+
+void amiga_dt_sound_destroy(struct content *c)
+{
+ amiga_dt_sound_content *plugin = (amiga_dt_sound_content *) c;
+
+ LOG("amiga_dt_sound_destroy");
+
+ DisposeDTObject(plugin->dto);
+
+ return;
+}
+
+bool amiga_dt_sound_redraw(struct content *c,
+ struct content_redraw_data *data, const struct rect *clip,
+ const struct redraw_context *ctx)
+{
+ plot_style_t pstyle = {
+ .fill_type = PLOT_OP_TYPE_SOLID,
+ .fill_colour = 0xffffff,
+ .stroke_colour = 0x000000,
+ .stroke_width = 1,
+ };
+
+ LOG("amiga_dt_sound_redraw");
+
+ /* this should be some sort of play/stop control */
+
+ ctx->plot->rectangle(data->x, data->y, data->x + data->width,
+ data->y + data->height, &pstyle);
+
+ return ctx->plot->text(data->x, data->y+20,
+ lwc_string_data(content__get_mime_type(c)),
+ lwc_string_length(content__get_mime_type(c)),
+ plot_style_font);
+
+}
+
+
+void amiga_dt_sound_open(struct content *c, struct browser_window *bw,
+ struct content *page, struct object_params *params)
+{
+ amiga_dt_sound_content *plugin = (amiga_dt_sound_content *) c;
+ struct object_param *param;
+
+ LOG("amiga_dt_sound_open");
+
+ plugin->immediate = false;
+
+ if(params && (param = params->params))
+ {
+ do
+ {
+ LOG("%s = %s", param->name, param->value);
+ if((strcmp(param->name, "autoplay") == 0) &&
+ (strcmp(param->value, "true") == 0)) plugin->immediate = true;
+ if((strcmp(param->name, "autoStart") == 0) &&
+ (strcmp(param->value, "1") == 0)) plugin->immediate = true;
+ param = param->next;
+ } while(param != NULL);
+ }
+
+ if(plugin->dto && (plugin->immediate == true))
+ amiga_dt_sound_play(plugin->dto);
+
+ return;
+}
+
+
+nserror amiga_dt_sound_clone(const struct content *old, struct content **newc)
+{
+ amiga_dt_sound_content *plugin;
+ nserror error;
+
+ LOG("amiga_dt_sound_clone");
+
+ plugin = calloc(1, sizeof(amiga_dt_sound_content));
+ if (plugin == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__clone(old, &plugin->base);
+ if (error != NSERROR_OK) {
+ content_destroy(&plugin->base);
+ return error;
+ }
+
+ /* We "clone" the old content by replaying conversion */
+ if (old->status == CONTENT_STATUS_READY ||
+ old->status == CONTENT_STATUS_DONE) {
+ if (amiga_dt_sound_convert(&plugin->base) == false) {
+ content_destroy(&plugin->base);
+ return NSERROR_CLONE_FAILED;
+ }
+ }
+
+ *newc = (struct content *) plugin;
+
+ return NSERROR_OK;
+}
+
+content_type amiga_dt_sound_content_type(void)
+{
+ return CONTENT_PLUGIN;
+}
+
+#endif
diff --git a/frontends/amiga/file.c b/frontends/amiga/file.c
new file mode 100644
index 000000000..8957ecfe6
--- /dev/null
+++ b/frontends/amiga/file.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2011 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <proto/asl.h>
+#include <proto/dos.h>
+#include <proto/exec.h>
+#include <proto/icon.h>
+#include <workbench/icon.h>
+
+#include "utils/utils.h"
+#include "utils/nsoption.h"
+#include "utils/file.h"
+#include "utils/messages.h"
+#include "content/hlcache.h"
+#include "content/content.h"
+#include "content/fetch.h"
+#include "desktop/browser.h"
+#include "desktop/save_complete.h"
+#include "desktop/save_pdf.h"
+#include "desktop/save_text.h"
+#include "desktop/gui_window.h"
+
+#include "amiga/gui.h"
+#include "amiga/bitmap.h"
+#include "amiga/download.h"
+#include "amiga/file.h"
+#include "amiga/filetype.h"
+#include "amiga/icon.h"
+#include "amiga/iff_dr2d.h"
+#include "amiga/misc.h"
+#include "amiga/save_pdf.h"
+#include "amiga/theme.h"
+
+static struct Hook aslhookfunc;
+
+HOOKF(ULONG, ami_file_asl_mime_hook, struct FileRequester *, fr, struct AnchorPathOld *)
+{
+ char fname[1024];
+ BOOL ret = FALSE;
+ char *mt = NULL;
+ lwc_string *lwc_mt = NULL;
+ lwc_error lerror;
+ content_type ct;
+
+ if(msg->ap_Info.fib_DirEntryType > 0) return(TRUE);
+
+ strcpy(fname,fr->fr_Drawer);
+ AddPart(fname, msg->ap_Info.fib_FileName,1024);
+
+ mt = strdup(fetch_filetype(fname));
+ lerror = lwc_intern_string(mt, strlen(mt), &lwc_mt);
+ if (lerror != lwc_error_ok)
+ return FALSE;
+
+ ct = content_factory_type_from_mime_type(lwc_mt);
+ lwc_string_unref(lwc_mt);
+
+ if(ct != CONTENT_NONE) ret = TRUE;
+
+ free(mt);
+ return ret;
+}
+
+void ami_file_open(struct gui_window_2 *gwin)
+{
+ char *temp;
+ nsurl *url;
+
+ if(AslRequestTags(filereq,
+ ASLFR_TitleText, messages_get("NetSurf"),
+ ASLFR_Window, gwin->win,
+ ASLFR_SleepWindow, TRUE,
+ ASLFR_Screen, scrn,
+ ASLFR_DoSaveMode, FALSE,
+ ASLFR_RejectIcons, TRUE,
+ ASLFR_FilterFunc, &aslhookfunc,
+ TAG_DONE))
+ {
+ if((temp = AllocVecTagList(1024, NULL)))
+ {
+ strlcpy(temp, filereq->fr_Drawer, 1024);
+ AddPart(temp, filereq->fr_File, 1024);
+
+ if (netsurf_path_to_nsurl(temp, &url) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", 0);
+ } else {
+ browser_window_navigate(gwin->gw->bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+
+ FreeVec(temp);
+ }
+ }
+}
+
+static void ami_file_set_type(const char *path, lwc_string *mime_type)
+{
+ content_type type = content_factory_type_from_mime_type(mime_type);
+ const char *default_type;
+
+ switch(type) {
+ case CONTENT_HTML:
+ default_type = "html";
+ break;
+ default:
+ default_type = NULL;
+ break;
+ }
+
+ if (default_type != NULL) {
+ struct DiskObject *dobj = NULL;
+
+ dobj = GetIconTags(NULL,ICONGETA_GetDefaultName,default_type,
+ ICONGETA_GetDefaultType,WBPROJECT,
+ TAG_DONE);
+
+ PutIconTags(path, dobj,
+ ICONPUTA_NotifyWorkbench, TRUE, TAG_DONE);
+ }
+}
+
+void ami_file_save(int type, char *fname, struct Window *win,
+ struct hlcache_handle *object, struct hlcache_handle *favicon,
+ struct browser_window *bw)
+{
+ BPTR lock, fh;
+ const char *source_data;
+ ULONG source_size;
+ struct bitmap *bm;
+
+ ami_update_pointer(win, GUI_POINTER_WAIT);
+
+ if(ami_download_check_overwrite(fname, win, 0)) {
+ switch(type) {
+ case AMINS_SAVE_SOURCE:
+ if((source_data = content_get_source_data(object, &source_size))) {
+ BPTR fh;
+ if((fh = FOpen(fname, MODE_NEWFILE,0))) {
+ FWrite(fh, source_data, 1, source_size);
+ FClose(fh);
+ }
+ }
+ break;
+
+ case AMINS_SAVE_TEXT:
+ save_as_text(object, fname);
+ break;
+
+ case AMINS_SAVE_COMPLETE:
+ if((lock = CreateDir(fname))) {
+ UnLock(lock);
+ save_complete(object, fname, ami_file_set_type);
+ amiga_icon_superimpose_favicon(fname, favicon, NULL);
+ }
+ break;
+
+ case AMINS_SAVE_PDF:
+#ifdef WITH_PDF_EXPORT
+ if(save_as_pdf(object, fname))
+ amiga_icon_superimpose_favicon(fname, favicon, "pdf");
+#endif
+ break;
+
+ case AMINS_SAVE_IFF:
+ if((bm = content_get_bitmap(object))) {
+ ami_bitmap_set_url(bm, hlcache_handle_get_url(object));
+ ami_bitmap_set_title(bm, content_get_title(object));
+ amiga_bitmap_save(bm, fname, 0);
+ }
+#ifdef WITH_NS_SVG
+ else if(ami_mime_compare(object, "svg") == true) {
+ ami_save_svg(object, fname);
+ }
+#endif
+ break;
+
+ case AMINS_SAVE_SELECTION:
+ if((source_data = browser_window_get_selection(bw))) {
+ if((fh = FOpen(fname, MODE_NEWFILE,0))) {
+ FWrite(fh, source_data, 1, strlen(source_data));
+ FClose(fh);
+ }
+ free((void *)source_data);
+ }
+ break;
+ }
+ if(object) SetComment(fname, nsurl_access(hlcache_handle_get_url(object)));
+ }
+
+ ami_update_pointer(win, GUI_POINTER_DEFAULT);
+}
+
+void ami_file_save_req(int type, struct gui_window_2 *gwin,
+ struct hlcache_handle *object)
+{
+ char *fname = AllocVecTagList(1024, NULL);
+ char *initial_fname = NULL;
+ char *fname_with_ext = NULL;
+ bool strip_ext = true;
+
+ if(object) {
+ if(type == AMINS_SAVE_SOURCE) strip_ext = false;
+ nsurl_nice(hlcache_handle_get_url(object), &initial_fname, strip_ext);
+ }
+
+ if(initial_fname != NULL) {
+ fname_with_ext = AllocVecTagList(strlen(initial_fname) + 5, NULL); /* 5 = .ext\0 */
+
+ strcpy(fname_with_ext, initial_fname);
+
+ switch(type)
+ {
+ case AMINS_SAVE_TEXT:
+ case AMINS_SAVE_SELECTION:
+ strcat(fname_with_ext, ".txt");
+ break;
+ case AMINS_SAVE_IFF:
+ strcat(fname_with_ext, ".iff");
+ break;
+ case AMINS_SAVE_PDF:
+ strcat(fname_with_ext, ".pdf");
+ break;
+ default:
+ break;
+ }
+
+ if(initial_fname) free(initial_fname);
+ }
+
+ if(AslRequestTags(savereq,
+ ASLFR_Window, gwin->win,
+ ASLFR_SleepWindow, TRUE,
+ ASLFR_TitleText, messages_get("NetSurf"),
+ ASLFR_Screen, scrn,
+ ASLFR_InitialFile, fname_with_ext ? fname_with_ext : "",
+ TAG_DONE))
+ {
+ strlcpy(fname, savereq->fr_Drawer, 1024);
+ AddPart(fname, savereq->fr_File, 1024);
+
+ ami_file_save(type, fname, gwin->win, object, gwin->gw->favicon, gwin->gw->bw);
+ }
+
+ if(fname) FreeVec(fname);
+ if(fname_with_ext) FreeVec(fname_with_ext);
+}
+
+void ami_file_req_init(void)
+{
+ const char *initial_dir = nsoption_charp(download_dir);
+ Tag initial_dir_tag = ASLFR_InitialDrawer;
+
+ if(initial_dir == NULL) initial_dir_tag = TAG_IGNORE;
+
+ filereq = (struct FileRequester *)AllocAslRequest(ASL_FileRequest, NULL);
+ savereq = (struct FileRequester *)AllocAslRequestTags(ASL_FileRequest,
+ ASLFR_DoSaveMode, TRUE,
+ ASLFR_RejectIcons, TRUE,
+ initial_dir_tag, initial_dir,
+ TAG_DONE);
+
+ aslhookfunc.h_Entry = (void *)&ami_file_asl_mime_hook;
+ aslhookfunc.h_SubEntry = NULL;
+ aslhookfunc.h_Data = NULL;
+}
+
+void ami_file_req_free(void)
+{
+ FreeAslRequest(filereq);
+ FreeAslRequest(savereq);
+}
+
diff --git a/frontends/amiga/file.h b/frontends/amiga/file.h
new file mode 100644
index 000000000..29a76c477
--- /dev/null
+++ b/frontends/amiga/file.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2011 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+
+struct hlcache_object;
+struct selection;
+struct gui_window_2;
+
+struct FileRequester *filereq;
+struct FileRequester *savereq;
+
+enum {
+ AMINS_SAVE_SOURCE,
+ AMINS_SAVE_TEXT,
+ AMINS_SAVE_COMPLETE,
+ AMINS_SAVE_PDF,
+ AMINS_SAVE_IFF,
+ AMINS_SAVE_SELECTION,
+};
+
+
+void ami_file_req_init(void);
+void ami_file_req_free(void);
+
+void ami_file_open(struct gui_window_2 *gwin);
+void ami_file_save_req(int type, struct gui_window_2 *gwin,
+ struct hlcache_handle *object);
+void ami_file_save(int type, char *fname, struct Window *win,
+ struct hlcache_handle *object, struct hlcache_handle *favicon,
+ struct browser_window *bw);
diff --git a/frontends/amiga/filetype.c b/frontends/amiga/filetype.c
new file mode 100644
index 000000000..39058b41c
--- /dev/null
+++ b/frontends/amiga/filetype.c
@@ -0,0 +1,647 @@
+/*
+ * Copyright 2008, 2011 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "amiga/os3support.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include "amiga/filetype.h"
+#include "amiga/misc.h"
+#include "amiga/object.h"
+#include "content/fetch.h"
+#include "content/content.h"
+#include "utils/log.h"
+#include "utils/utils.h"
+#include <proto/icon.h>
+#include <proto/dos.h>
+#include <proto/datatypes.h>
+#include <proto/exec.h>
+#include <workbench/icon.h>
+
+/**
+ * filetype -- determine the MIME type of a local file
+ */
+
+struct MinList *ami_mime_list = NULL;
+
+struct ami_mime_entry
+{
+ lwc_string *mimetype;
+ lwc_string *datatype;
+ lwc_string *filetype;
+ lwc_string *plugincmd;
+};
+
+enum
+{
+ AMI_MIME_MIMETYPE,
+ AMI_MIME_DATATYPE,
+ AMI_MIME_FILETYPE,
+ AMI_MIME_PLUGINCMD
+};
+
+const char *fetch_filetype(const char *unix_path)
+{
+ static char mimetype[50];
+ struct DiskObject *dobj = NULL;
+ struct DataType *dtn;
+ BOOL found = FALSE;
+ lwc_string *lwc_mimetype;
+
+ /* First, check if we appear to have an icon.
+ We'll just do a filename check here for quickness, although the
+ first word ought to be checked against WB_DISKMAGIC really. */
+
+ if(strncmp(unix_path + strlen(unix_path) - 5, ".info", 5) == 0) {
+ strcpy(mimetype,"image/x-amiga-icon");
+ found = TRUE;
+ }
+
+
+ /* Secondly try getting a tooltype "MIMETYPE" and use that as the MIME type.
+ Will fail over to default icons if the file doesn't have a real icon. */
+
+ if(!found) {
+ if((dobj = GetIconTags(unix_path,ICONGETA_FailIfUnavailable,FALSE,
+ TAG_DONE))) {
+ STRPTR ttype = NULL;
+ ttype = FindToolType(dobj->do_ToolTypes, "MIMETYPE");
+ if(ttype) {
+ strcpy(mimetype,ttype);
+ found = TRUE;
+ }
+ FreeDiskObject(dobj);
+ }
+ }
+
+ /* If that didn't work, use the MIME file and DataTypes */
+
+ if(!found) {
+ BPTR lock;
+ if ((lock = Lock(unix_path, ACCESS_READ))) {
+ if ((dtn = ObtainDataTypeA (DTST_FILE, (APTR)lock, NULL))) {
+ if(ami_mime_from_datatype(dtn, &lwc_mimetype, NULL)) {
+ strcpy(mimetype, lwc_string_data(lwc_mimetype));
+ found = TRUE;
+ ReleaseDataType(dtn);
+ }
+ }
+ UnLock(lock);
+ }
+ }
+
+ /* Have a quick check for file extensions (inc RISC OS filetype).
+ * Makes detection a little more robust, and some of the redirects
+ * caused by links in the SVN tree prevent NetSurf from reading the
+ * MIME type from the icon (step two, above).
+ */
+
+ if((!found) || (strcmp("text/plain", mimetype) == 0))
+ {
+ if((strncmp(unix_path + strlen(unix_path) - 4, ".css", 4) == 0) ||
+ (strncmp(unix_path + strlen(unix_path) - 4, ",f79", 4) == 0))
+ {
+ strcpy(mimetype,"text/css");
+ found = TRUE;
+ }
+
+ if((strncmp(unix_path + strlen(unix_path) - 4, ".htm", 4) == 0) ||
+ (strncmp(unix_path + strlen(unix_path) - 5, ".html", 5) == 0) ||
+ (strncmp(unix_path + strlen(unix_path) - 4, ",faf", 4) == 0))
+ {
+ strcpy(mimetype,"text/html");
+ found = TRUE;
+ }
+ if(strncmp(unix_path + strlen(unix_path) - 3, ".js", 3) == 0) {
+ strcpy(mimetype,"application/javascript");
+ found = TRUE;
+ }
+ }
+
+ if(!found) strcpy(mimetype,"text/plain"); /* If all else fails */
+
+ return mimetype;
+}
+
+const char *ami_content_type_to_file_type(content_type type)
+{
+ switch(type)
+ {
+ case CONTENT_HTML:
+ return "html";
+ break;
+
+ case CONTENT_TEXTPLAIN:
+ return "ascii";
+ break;
+
+ case CONTENT_CSS:
+ return "css";
+ break;
+
+ case CONTENT_IMAGE:
+ return "picture";
+ break;
+
+ default:
+ return "project";
+ break;
+ }
+}
+
+static void ami_mime_entry_free(void *nso)
+{
+ struct ami_mime_entry *mimeentry = (struct ami_mime_entry *)nso;
+
+ if(mimeentry->mimetype) lwc_string_unref(mimeentry->mimetype);
+ if(mimeentry->datatype) lwc_string_unref(mimeentry->datatype);
+ if(mimeentry->filetype) lwc_string_unref(mimeentry->filetype);
+ if(mimeentry->plugincmd) lwc_string_unref(mimeentry->plugincmd);
+}
+
+nserror ami_mime_init(const char *mimefile)
+{
+ lwc_error lerror;
+ char buffer[256];
+ BPTR fh = 0;
+ struct RDArgs *rargs = NULL;
+ CONST_STRPTR template = "MIMETYPE/A,DT=DATATYPE/K,TYPE=DEFICON/K,CMD=PLUGINCMD/K";
+ long rarray[] = {0,0,0,0};
+ struct nsObject *node;
+ struct ami_mime_entry *mimeentry;
+
+ LOG("mimetypes file: %s", mimefile);
+
+ if(ami_mime_list == NULL)
+ ami_mime_list = NewObjList();
+
+ rargs = AllocDosObjectTags(DOS_RDARGS,TAG_DONE);
+ if(rargs == NULL) return NSERROR_NOMEM;
+
+ if((fh = FOpen(mimefile, MODE_OLDFILE, 0)))
+ {
+ while((FGets(fh, (STRPTR)&buffer, 256) != 0))
+ {
+ rargs->RDA_Source.CS_Buffer = (char *)&buffer;
+ rargs->RDA_Source.CS_Length = 256;
+ rargs->RDA_Source.CS_CurChr = 0;
+
+ rargs->RDA_DAList = NULL;
+ rargs->RDA_Buffer = NULL;
+ rargs->RDA_BufSiz = 0;
+ rargs->RDA_ExtHelp = NULL;
+ rargs->RDA_Flags = 0;
+
+ rarray[AMI_MIME_MIMETYPE] = 0;
+ rarray[AMI_MIME_DATATYPE] = 0;
+ rarray[AMI_MIME_FILETYPE] = 0;
+ rarray[AMI_MIME_PLUGINCMD] = 0;
+
+ if(ReadArgs(template, rarray, rargs))
+ {
+ if ((node = AddObject(ami_mime_list, AMINS_MIME))) {
+ ObjectCallback(node, ami_mime_entry_free);
+ mimeentry = ami_misc_allocvec_clear(sizeof(struct ami_mime_entry), 0);
+ node->objstruct = mimeentry;
+
+ if(rarray[AMI_MIME_MIMETYPE])
+ {
+ lerror = lwc_intern_string((char *)rarray[AMI_MIME_MIMETYPE],
+ strlen((char *)rarray[AMI_MIME_MIMETYPE]), &mimeentry->mimetype);
+ if (lerror != lwc_error_ok)
+ return NSERROR_NOMEM;
+ }
+
+ if(rarray[AMI_MIME_DATATYPE])
+ {
+ lerror = lwc_intern_string((char *)rarray[AMI_MIME_DATATYPE],
+ strlen((char *)rarray[AMI_MIME_DATATYPE]), &mimeentry->datatype);
+ if (lerror != lwc_error_ok)
+ return NSERROR_NOMEM;
+ }
+
+ if(rarray[AMI_MIME_FILETYPE])
+ {
+ lerror = lwc_intern_string((char *)rarray[AMI_MIME_FILETYPE],
+ strlen((char *)rarray[AMI_MIME_FILETYPE]), &mimeentry->filetype);
+ if (lerror != lwc_error_ok)
+ return NSERROR_NOMEM;
+ }
+
+ if(rarray[AMI_MIME_PLUGINCMD])
+ {
+ lerror = lwc_intern_string((char *)rarray[AMI_MIME_PLUGINCMD],
+ strlen((char *)rarray[AMI_MIME_PLUGINCMD]), &mimeentry->plugincmd);
+ if (lerror != lwc_error_ok)
+ return NSERROR_NOMEM;
+ }
+ }
+ FreeArgs(rargs);
+ }
+ }
+ FClose(fh);
+ }
+ FreeDosObject(DOS_RDARGS, rargs);
+
+ return NSERROR_OK;
+}
+
+void ami_mime_free(void)
+{
+ ami_mime_dump();
+ FreeObjList(ami_mime_list);
+}
+
+/**
+ * Return next matching MIME entry
+ *
+ * \param search lwc_string to search for (or NULL for all)
+ * \param type of value being searched for (AMI_MIME_#?)
+ * \param start_node node to continue search (updated on exit)
+ * \return entry or NULL if no match
+ */
+
+static struct ami_mime_entry *ami_mime_entry_locate(lwc_string *search,
+ int type, struct Node **start_node)
+{
+ struct nsObject *node;
+ struct nsObject *nnode;
+ struct ami_mime_entry *mimeentry;
+ lwc_error lerror;
+ bool ret = false;
+
+ if(IsMinListEmpty(ami_mime_list)) return NULL;
+
+ if(*start_node)
+ {
+ node = (struct nsObject *)GetSucc(*start_node);
+ if(node == NULL) return NULL;
+ }
+ else
+ {
+ node = (struct nsObject *)GetHead((struct List *)ami_mime_list);
+ }
+
+ do
+ {
+ nnode=(struct nsObject *)GetSucc((struct Node *)node);
+ mimeentry = node->objstruct;
+
+ lerror = lwc_error_ok;
+
+ switch(type)
+ {
+ case AMI_MIME_MIMETYPE:
+ if(search != NULL)
+ lerror = lwc_string_isequal(mimeentry->mimetype, search, &ret);
+ else if(mimeentry->mimetype != NULL)
+ ret = true;
+ break;
+
+ case AMI_MIME_DATATYPE:
+ if(search != NULL)
+ lerror = lwc_string_isequal(mimeentry->datatype, search, &ret);
+ else if(mimeentry->datatype != NULL)
+ ret = true;
+ break;
+
+ case AMI_MIME_FILETYPE:
+ if(search != NULL)
+ lerror = lwc_string_isequal(mimeentry->filetype, search, &ret);
+ else if(mimeentry->filetype != NULL)
+ ret = true;
+ break;
+
+ case AMI_MIME_PLUGINCMD:
+ if(search != NULL)
+ lerror = lwc_string_isequal(mimeentry->plugincmd, search, &ret);
+ else if(mimeentry->plugincmd != NULL)
+ ret = true;
+ break;
+ }
+
+ if((lerror == lwc_error_ok) && (ret == true))
+ break;
+
+ } while((node=nnode));
+
+ *start_node = (struct Node *)node;
+
+ if(ret == true) return mimeentry;
+ else return NULL;
+}
+
+
+static APTR ami_mime_guess_add_datatype(struct DataType *dt, lwc_string **lwc_mimetype)
+{
+ struct nsObject *node;
+ char mimetype[100];
+ char *dt_name_lwr;
+ struct ami_mime_entry *mimeentry;
+ lwc_error lerror;
+ struct DataTypeHeader *dth = dt->dtn_Header;
+ char *p;
+
+ node = AddObject(ami_mime_list, AMINS_MIME);
+ if(node == NULL) return NULL;
+
+ mimeentry = ami_misc_allocvec_clear(sizeof(struct ami_mime_entry), 0);
+ if(mimeentry == NULL) return NULL;
+
+ node->objstruct = mimeentry;
+ ObjectCallback(node, ami_mime_entry_free);
+
+ lerror = lwc_intern_string(dth->dth_Name, strlen(dth->dth_Name), &mimeentry->datatype);
+ if (lerror != lwc_error_ok)
+ return NULL;
+
+ dt_name_lwr = strdup(dth->dth_Name);
+ if(dt_name_lwr == NULL) return NULL;
+
+ strlwr(dt_name_lwr);
+ p = dt_name_lwr;
+
+ while(*p != '\0')
+ {
+ if(*p == ' ') *p = '-';
+ if(*p == '/') *p = '-';
+ p++;
+ }
+
+ switch(dth->dth_GroupID)
+ {
+ case GID_TEXT:
+ case GID_DOCUMENT:
+ if(strcmp("ascii", dt_name_lwr)==0)
+ {
+ strcpy(mimetype,"text/plain");
+ }
+ else
+ {
+ sprintf(mimetype,"text/%s", dt_name_lwr);
+ }
+ break;
+ case GID_SOUND:
+ case GID_INSTRUMENT:
+ case GID_MUSIC:
+ sprintf(mimetype,"audio/%s", dt_name_lwr);
+ break;
+ case GID_PICTURE:
+ if(strcmp("sprite", dt_name_lwr)==0)
+ {
+ strcpy(mimetype,"image/x-riscos-sprite");
+ }
+ else
+ {
+ sprintf(mimetype,"image/%s", dt_name_lwr);
+ }
+ break;
+ case GID_ANIMATION:
+ case GID_MOVIE:
+ sprintf(mimetype,"video/%s", dt_name_lwr);
+ break;
+ case GID_SYSTEM:
+ default:
+ if(strcmp("directory", dt_name_lwr)==0)
+ {
+ strcpy(mimetype,"application/x-netsurf-directory");
+ }
+ else if(strcmp("binary", dt_name_lwr)==0)
+ {
+ strcpy(mimetype,"application/octet-stream");
+ }
+ else sprintf(mimetype,"application/%s", dt_name_lwr);
+ break;
+ }
+
+ lerror = lwc_intern_string(mimetype, strlen(mimetype), &mimeentry->mimetype);
+ if (lerror != lwc_error_ok)
+ return NULL;
+
+ *lwc_mimetype = mimeentry->mimetype;
+
+ lerror = lwc_intern_string(dt_name_lwr, strlen(dt_name_lwr), &mimeentry->filetype);
+ if (lerror != lwc_error_ok)
+ return NULL;
+
+ free(dt_name_lwr);
+ return node;
+}
+
+/**
+ * Return a MIME Type matching a DataType
+ *
+ * \param dt a DataType structure
+ * \param mimetype lwc_string to hold the MIME type
+ * \param start_node node to feed back in to continue search
+ * \return node or NULL if no match
+ */
+
+struct Node *ami_mime_from_datatype(struct DataType *dt,
+ lwc_string **mimetype, struct Node *start_node)
+{
+ struct DataTypeHeader *dth;
+ struct Node *node;
+ struct ami_mime_entry *mimeentry;
+ lwc_string *dt_name;
+ lwc_error lerror;
+
+ if(dt == NULL) return NULL;
+
+ dth = dt->dtn_Header;
+ lerror = lwc_intern_string(dth->dth_Name, strlen(dth->dth_Name), &dt_name);
+ if (lerror != lwc_error_ok)
+ return NULL;
+
+ node = start_node;
+ mimeentry = ami_mime_entry_locate(dt_name, AMI_MIME_DATATYPE, &node);
+ lwc_string_unref(dt_name);
+
+ if(mimeentry != NULL)
+ {
+ *mimetype = mimeentry->mimetype;
+ return (struct Node *)node;
+ }
+ else
+ {
+ if(start_node == NULL)
+ {
+ /* If there are no matching entries in the file, guess */
+ return ami_mime_guess_add_datatype(dt, mimetype);
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+}
+
+/**
+ * Return the DefIcons type matching a MIME type
+ *
+ * \param mimetype lwc_string MIME type
+ * \param filetype ptr to lwc_string to hold DefIcons type
+ * \param start_node node to feed back in to continue search
+ * \return node or NULL if no match
+ */
+
+struct Node *ami_mime_to_filetype(lwc_string *mimetype,
+ lwc_string **filetype, struct Node *start_node)
+{
+ struct Node *node;
+ struct ami_mime_entry *mimeentry;
+
+ node = start_node;
+ mimeentry = ami_mime_entry_locate(mimetype, AMI_MIME_MIMETYPE, &node);
+
+ if(mimeentry != NULL)
+ {
+ *filetype = mimeentry->filetype;
+ return (struct Node *)node;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+const char *ami_mime_content_to_filetype(struct hlcache_handle *c)
+{
+ struct Node *node;
+ lwc_string *filetype;
+ lwc_string *mimetype;
+
+ mimetype = content_get_mime_type(c);
+
+ node = ami_mime_to_filetype(mimetype, &filetype, NULL);
+
+ if(node && (filetype != NULL))
+ return lwc_string_data(filetype);
+ else
+ return ami_content_type_to_file_type(content_get_type(c));
+}
+
+/**
+ * Return all MIME types containing a plugincmd
+ *
+ * \param mimetype ptr to lwc_string MIME type
+ * \param start_node node to feed back in to continue search
+ * \return node or NULL if no match
+ */
+
+struct Node *ami_mime_has_cmd(lwc_string **mimetype, struct Node *start_node)
+{
+ struct Node *node;
+ struct ami_mime_entry *mimeentry;
+
+ node = start_node;
+ mimeentry = ami_mime_entry_locate(NULL, AMI_MIME_PLUGINCMD, &node);
+
+ if(mimeentry != NULL)
+ {
+ *mimetype = mimeentry->mimetype;
+ return (struct Node *)node;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+/**
+ * Return the plugincmd matching a MIME type
+ *
+ * \param mimetype lwc_string MIME type
+ * \param plugincmd ptr to lwc_string to hold plugincmd
+ * \param start_node node to feed back in to continue search
+ * \return node or NULL if no match
+ */
+
+static struct Node *ami_mime_to_plugincmd(lwc_string *mimetype,
+ lwc_string **plugincmd, struct Node *start_node)
+{
+ struct Node *node;
+ struct ami_mime_entry *mimeentry;
+
+ node = start_node;
+ mimeentry = ami_mime_entry_locate(mimetype, AMI_MIME_MIMETYPE, &node);
+
+ if(mimeentry != NULL)
+ {
+ *plugincmd = mimeentry->plugincmd;
+ return (struct Node *)node;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+lwc_string *ami_mime_content_to_cmd(struct hlcache_handle *c)
+{
+ struct Node *node;
+ lwc_string *plugincmd;
+ lwc_string *mimetype;
+
+ mimetype = content_get_mime_type(c);
+
+ node = ami_mime_to_plugincmd(mimetype,
+ &plugincmd, NULL);
+
+ if(node && (plugincmd != NULL)) return plugincmd;
+ else return NULL;
+}
+
+/**
+ * Compare the MIME type of an hlcache_handle to a DefIcons type
+ */
+
+bool ami_mime_compare(struct hlcache_handle *c, const char *type)
+{
+ bool ret = false;
+ lwc_error lerror;
+ lwc_string *filetype;
+ lwc_string *mime_filetype;
+ lwc_string *mime = content_get_mime_type(c);
+
+ if(ami_mime_to_filetype(mime, &mime_filetype, NULL) == NULL)
+ return false;
+
+ lerror = lwc_intern_string(type, strlen(type), &filetype);
+ if (lerror != lwc_error_ok)
+ return false;
+
+ lerror = lwc_string_isequal(filetype, mime_filetype, &ret);
+ if (lerror != lwc_error_ok)
+ return false;
+
+ lwc_string_unref(filetype);
+
+ return ret;
+}
+
+
+void ami_mime_dump(void)
+{
+ struct Node *node = NULL;
+ struct ami_mime_entry *mimeentry;
+
+ while((mimeentry = ami_mime_entry_locate(NULL, AMI_MIME_MIMETYPE, &node))) {
+ LOG("%s DT=\"%s\" TYPE=\"%s\" CMD=\"%s\"", mimeentry->mimetype ? lwc_string_data(mimeentry->mimetype) : "", mimeentry->datatype ? lwc_string_data(mimeentry->datatype) : "", mimeentry->filetype ? lwc_string_data(mimeentry->filetype) : "", mimeentry->plugincmd ? lwc_string_data(mimeentry->plugincmd) : "");
+ };
+}
diff --git a/frontends/amiga/filetype.h b/frontends/amiga/filetype.h
new file mode 100644
index 000000000..fc27b1df2
--- /dev/null
+++ b/frontends/amiga/filetype.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2010 - 2011 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_FILETYPE_H
+#define AMIGA_FILETYPE_H
+#include <stdbool.h>
+#include <libwapcaplet/libwapcaplet.h>
+#include "content/content_type.h"
+#include "utils/errors.h"
+#include <datatypes/datatypes.h>
+
+struct hlcache_handle;
+struct ami_mime_entry;
+
+const char *fetch_filetype(const char *unix_path);
+
+nserror ami_mime_init(const char *mimefile);
+void ami_mime_free(void);
+void ami_mime_dump(void);
+
+struct Node *ami_mime_from_datatype(struct DataType *dt,
+ lwc_string **mimetype, struct Node *start_node);
+struct Node *ami_mime_to_filetype(lwc_string *mimetype,
+ lwc_string **filetype, struct Node *start_node);
+
+const char *ami_mime_content_to_filetype(struct hlcache_handle *c);
+lwc_string *ami_mime_content_to_cmd(struct hlcache_handle *c);
+
+struct Node *ami_mime_has_cmd(lwc_string **mimetype, struct Node *start_node);
+
+bool ami_mime_compare(struct hlcache_handle *c, const char *type);
+
+/* deprecated */
+const char *ami_content_type_to_file_type(content_type type);
+
+#endif
diff --git a/frontends/amiga/font.c b/frontends/amiga/font.c
new file mode 100644
index 000000000..1c9702af2
--- /dev/null
+++ b/frontends/amiga/font.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2008 - 2016 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "amiga/os3support.h"
+
+#include <proto/diskfont.h>
+#include <proto/exec.h>
+#include <proto/graphics.h>
+
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "desktop/browser.h"
+#include "desktop/gui_layout.h"
+
+#include "amiga/font.h"
+#include "amiga/font_bullet.h"
+#include "amiga/font_diskfont.h"
+#include "amiga/font_scan.h"
+
+static ULONG ami_devicedpi = 72;
+static ULONG ami_xdpi = 72;
+
+ULONG ami_font_dpi_get_devicedpi(void)
+{
+ return ami_devicedpi;
+}
+
+ULONG ami_font_dpi_get_xdpi(void)
+{
+ return ami_xdpi;
+}
+
+void ami_font_setdevicedpi(int id)
+{
+ DisplayInfoHandle dih;
+ struct DisplayInfo dinfo;
+
+ if(nsoption_bool(bitmap_fonts) == true) {
+ LOG("WARNING: Using diskfont.library for text. Forcing DPI to 72.");
+ nsoption_set_int(screen_ydpi, 72);
+ }
+
+ ULONG ydpi = nsoption_int(screen_ydpi);
+ ULONG xdpi = nsoption_int(screen_ydpi);
+ browser_set_dpi(nsoption_int(screen_ydpi));
+
+ if(id && (nsoption_int(monitor_aspect_x) != 0) && (nsoption_int(monitor_aspect_y) != 0))
+ {
+ if((dih = FindDisplayInfo(id)))
+ {
+ if(GetDisplayInfoData(dih, &dinfo, sizeof(struct DisplayInfo),
+ DTAG_DISP, 0))
+ {
+ int xres = dinfo.Resolution.x;
+ int yres = dinfo.Resolution.y;
+
+ if((nsoption_int(monitor_aspect_x) != 4) || (nsoption_int(monitor_aspect_y) != 3))
+ {
+ /* AmigaOS sees 4:3 modes as square in the DisplayInfo database,
+ * so we correct other modes to "4:3 equiv" here. */
+ xres = (xres * nsoption_int(monitor_aspect_x)) / 4;
+ yres = (yres * nsoption_int(monitor_aspect_y)) / 3;
+ }
+
+ xdpi = (yres * ydpi) / xres;
+
+ LOG("XDPI = %ld, YDPI = %ld (DisplayInfo resolution %d x %d, corrected %d x %d)", xdpi, ydpi, dinfo.Resolution.x, dinfo.Resolution.y, xres, yres);
+ }
+ }
+ }
+
+ ami_xdpi = xdpi;
+ ami_devicedpi = (xdpi << 16) | ydpi;
+}
+
+/* The below are simple font routines which should not be used for page rendering */
+
+struct TextFont *ami_font_open_disk_font(struct TextAttr *tattr)
+{
+ struct TextFont *tfont = OpenDiskFont(tattr);
+ return tfont;
+}
+
+void ami_font_close_disk_font(struct TextFont *tfont)
+{
+ CloseFont(tfont);
+}
+
+/* Font initialisation */
+void ami_font_init(void)
+{
+ if(nsoption_bool(bitmap_fonts) == false) {
+ ami_font_bullet_init();
+ } else {
+ ami_font_diskfont_init();
+ }
+}
+
+void ami_font_fini(void)
+{
+ if(nsoption_bool(bitmap_fonts) == false) {
+ ami_font_bullet_fini();
+ }
+}
+
+/* Stub entry points */
+static nserror ami_font_width(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int *width)
+{
+ if(__builtin_expect(ami_nsfont == NULL, 0)) return false;
+ return ami_nsfont->width(fstyle, string, length, width);
+}
+
+static nserror ami_font_position(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x)
+{
+ if(__builtin_expect(ami_nsfont == NULL, 0)) return false;
+ return ami_nsfont->posn(fstyle, string, length, x, char_offset, actual_x);
+}
+
+static nserror ami_font_split(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x)
+{
+ if(__builtin_expect(ami_nsfont == NULL, 0)) return false;
+ return ami_nsfont->split(fstyle, string, length, x, char_offset, actual_x);
+}
+
+static struct gui_layout_table layout_table = {
+ .width = ami_font_width,
+ .position = ami_font_position,
+ .split = ami_font_split,
+};
+
+struct gui_layout_table *ami_layout_table = &layout_table;
diff --git a/frontends/amiga/font.h b/frontends/amiga/font.h
new file mode 100755
index 000000000..cd526057f
--- /dev/null
+++ b/frontends/amiga/font.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2008, 2009, 2012, 2016 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_FONT_H
+#define AMIGA_FONT_H
+
+#include "desktop/plotters.h"
+#include "utils/errors.h"
+#include <graphics/rastport.h>
+#include <graphics/text.h>
+
+void ami_font_init(void);
+void ami_font_fini(void);
+
+/* DPI stuff */
+void ami_font_setdevicedpi(int id);
+ULONG ami_font_dpi_get_devicedpi(void);
+ULONG ami_font_dpi_get_xdpi(void);
+
+/* Simple diskfont functions for graphics.library use (not page rendering) */
+struct TextFont *ami_font_open_disk_font(struct TextAttr *tattr);
+void ami_font_close_disk_font(struct TextFont *tfont);
+
+/* Font engine tables */
+struct ami_font_functions {
+ nserror (*width)(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int *width);
+
+ nserror (*posn)(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x);
+
+ nserror (*split)(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x);
+
+ ULONG (*text)(struct RastPort *rp, const char *string,
+ ULONG length, const plot_font_style_t *fstyle,
+ ULONG x, ULONG y, bool aa);
+};
+
+const struct ami_font_functions *ami_nsfont;
+
+struct gui_layout_table *ami_layout_table;
+
+#endif
+
diff --git a/frontends/amiga/font_bullet.c b/frontends/amiga/font_bullet.c
new file mode 100644
index 000000000..3032b9735
--- /dev/null
+++ b/frontends/amiga/font_bullet.c
@@ -0,0 +1,895 @@
+/*
+ * Copyright 2008 - 2016 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "amiga/os3support.h"
+
+#include <stdlib.h>
+
+#ifndef __amigaos4__
+#include <proto/bullet.h>
+#endif
+#include <proto/diskfont.h>
+#include <proto/exec.h>
+#include <proto/graphics.h>
+#include <proto/utility.h>
+
+#include <diskfont/diskfonttag.h>
+#include <diskfont/oterrors.h>
+
+#include "amiga/font.h"
+#include "amiga/font_bullet.h"
+#include "amiga/font_cache.h"
+#include "amiga/font_scan.h"
+
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "utils/utf8.h"
+#include "utils/utils.h"
+
+#define NSA_UNICODE_FONT PLOT_FONT_FAMILY_COUNT
+
+#define NSA_NORMAL 0
+#define NSA_ITALIC 1
+#define NSA_BOLD 2
+#define NSA_BOLDITALIC 3
+#define NSA_OBLIQUE 4
+#define NSA_BOLDOBLIQUE 6
+
+#define NSA_VALUE_BOLDX (1 << 12)
+#define NSA_VALUE_BOLDY 0
+#define NSA_VALUE_SHEARSIN (1 << 14)
+#define NSA_VALUE_SHEARCOS (1 << 16)
+
+#define NSA_FONT_EMWIDTH(s) (s / FONT_SIZE_SCALE) * (ami_font_dpi_get_xdpi() / 72.0)
+
+const uint16 sc_table[] = {
+ 0x0061, 0x1D00, /* a */
+ 0x0062, 0x0299, /* b */
+ 0x0063, 0x1D04, /* c */
+ 0x0064, 0x1D05, /* d */
+ 0x0065, 0x1D07, /* e */
+ 0x0066, 0xA730, /* f */
+ 0x0067, 0x0262, /* g */
+ 0x0068, 0x029C, /* h */
+ 0x0069, 0x026A, /* i */
+ 0x006A, 0x1D0A, /* j */
+ 0x006B, 0x1D0B, /* k */
+ 0x006C, 0x029F, /* l */
+ 0x006D, 0x1D0D, /* m */
+ 0x006E, 0x0274, /* n */
+ 0x006F, 0x1D0F, /* o */
+ 0x0070, 0x1D18, /* p */
+ 0x0071, 0xA7EE, /* q (proposed) (Adobe codepoint 0xF771) */
+ 0x0072, 0x0280, /* r */
+ 0x0073, 0xA731, /* s */
+ 0x0074, 0x1D1B, /* t */
+ 0x0075, 0x1D1C, /* u */
+ 0x0076, 0x1D20, /* v */
+ 0x0077, 0x1D21, /* w */
+ 0x0078, 0xA7EF, /* x (proposed) (Adobe codepoint 0xF778) */
+ 0x0079, 0x028F, /* y */
+ 0x007A, 0x1D22, /* z */
+
+ 0x00C6, 0x1D01, /* ae */
+ 0x0153, 0x0276, /* oe */
+
+#if 0
+/* TODO: fill in the non-small caps character ids for these */
+ 0x0000, 0x1D03, /* barred b */
+ 0x0000, 0x0281, /* inverted r */
+ 0x0000, 0x1D19, /* reversed r */
+ 0x0000, 0x1D1A, /* turned r */
+ 0x0000, 0x029B, /* g with hook */
+ 0x0000, 0x1D06, /* eth à */
+ 0x0000, 0x1D0C, /* l with stroke */
+ 0x0000, 0xA7FA, /* turned m */
+ 0x0000, 0x1D0E, /* reversed n */
+ 0x0000, 0x1D10, /* open o */
+ 0x0000, 0x1D15, /* ou */
+ 0x0000, 0x1D23, /* ezh */
+ 0x0000, 0x1D26, /* gamma */
+ 0x0000, 0x1D27, /* lamda */
+ 0x0000, 0x1D28, /* pi */
+ 0x0000, 0x1D29, /* rho */
+ 0x0000, 0x1D2A, /* psi */
+ 0x0000, 0x1D2B, /* el */
+ 0x0000, 0xA776, /* rum */
+
+ 0x0000, 0x1DDB, /* combining g */
+ 0x0000, 0x1DDE, /* combining l */
+ 0x0000, 0x1DDF, /* combining m */
+ 0x0000, 0x1DE1, /* combining n */
+ 0x0000, 0x1DE2, /* combining r */
+
+ 0x0000, 0x1DA6, /* modifier i */
+ 0x0000, 0x1DA7, /* modifier i with stroke */
+ 0x0000, 0x1DAB, /* modifier l */
+ 0x0000, 0x1DB0, /* modifier n */
+ 0x0000, 0x1DB8, /* modifier u */
+#endif
+ 0, 0};
+
+lwc_string *glypharray[0xffff + 1];
+
+static struct List ami_diskfontlib_list;
+
+static inline int32 ami_font_plot_glyph(struct OutlineFont *ofont, struct RastPort *rp,
+ uint16 *char1, uint16 *char2, uint32 x, uint32 y, uint32 emwidth, bool aa);
+static inline int32 ami_font_width_glyph(struct OutlineFont *ofont,
+ const uint16 *char1, const uint16 *char2, uint32 emwidth);
+static struct OutlineFont *ami_open_outline_font(const plot_font_style_t *fstyle,
+ const uint16 *codepoint);
+static inline ULONG ami_font_unicode_width(const char *string, ULONG length,
+ const plot_font_style_t *fstyle, ULONG x, ULONG y, bool aa);
+
+static inline int amiga_nsfont_utf16_char_length(const uint16 *char1)
+{
+ if (__builtin_expect(((*char1 < 0xD800) || (0xDBFF < *char1)), 1)) {
+ return 1;
+ } else {
+ return 2;
+ }
+}
+
+static inline uint32 amiga_nsfont_decode_surrogate(const uint16 *char1)
+{
+ if(__builtin_expect((amiga_nsfont_utf16_char_length(char1) == 2), 0)) {
+ return ((uint32)char1[0] << 10) + char1[1] - 0x35FDC00;
+ } else {
+ return (uint32)*char1;
+ }
+}
+
+static nserror amiga_nsfont_width(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int *width)
+{
+ *width = ami_font_unicode_width(string, length, fstyle, 0, 0, false);
+
+ if(*width <= 0) *width == length; // fudge
+
+ return NSERROR_OK;
+}
+
+/**
+ * Find the position in a string where an x coordinate falls.
+ *
+ * \param fstyle style for this text
+ * \param string UTF-8 string to measure
+ * \param length length of string
+ * \param x x coordinate to search for
+ * \param char_offset updated to offset in string of actual_x, [0..length]
+ * \param actual_x updated to x coordinate of character closest to x
+ * \return true on success, false on error and error reported
+ */
+
+static nserror amiga_nsfont_position_in_string(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x)
+{
+ uint16 *utf16 = NULL, *outf16 = NULL;
+ uint16 *utf16next = NULL;
+ struct OutlineFont *ofont, *ufont = NULL;
+ int tx = 0;
+ uint32 utf8_pos = 0;
+ int utf16charlen;
+ ULONG emwidth = (ULONG)NSA_FONT_EMWIDTH(fstyle->size);
+ int32 tempx;
+
+ if(utf8_to_enc(string,"UTF-16",length,(char **)&utf16) != NSERROR_OK) return NSERROR_INVALID;
+ outf16 = utf16;
+ if(!(ofont = ami_open_outline_font(fstyle, 0))) return NSERROR_INVALID;
+
+ *char_offset = 0;
+ *actual_x = 0;
+
+ while (utf8_pos < length) {
+ utf16charlen = amiga_nsfont_utf16_char_length(utf16);
+ utf16next = &utf16[utf16charlen];
+
+ tempx = ami_font_width_glyph(ofont, utf16, utf16next, emwidth);
+
+ if (tempx == 0) {
+ if (ufont == NULL)
+ ufont = ami_open_outline_font(fstyle, utf16);
+
+ if (ufont)
+ tempx = ami_font_width_glyph(ufont, utf16,
+ utf16next, emwidth);
+ }
+
+ tx += tempx;
+ utf16 = utf16next;
+ utf8_pos = utf8_next(string, length, utf8_pos);
+
+ if(tx < x) {
+ *actual_x = tx;
+ *char_offset = utf8_pos;
+ } else {
+ if((x - *actual_x) > (tx - x)) {
+ *actual_x = tx;
+ *char_offset = utf8_pos;
+ }
+ free(outf16);
+ return NSERROR_OK;
+ }
+ }
+
+ *actual_x = tx;
+ *char_offset = length;
+
+ free(outf16);
+ return NSERROR_OK;
+}
+
+
+/**
+ * Find where to split a string to make it fit a width.
+ *
+ * \param fstyle style for this text
+ * \param string UTF-8 string to measure
+ * \param length length of string
+ * \param x width available
+ * \param char_offset updated to offset in string of actual_x, [1..length]
+ * \param actual_x updated to x coordinate of character closest to x
+ * \return true on success, false on error and error reported
+ *
+ * On exit, char_offset indicates first character after split point.
+ *
+ * Note: char_offset of 0 should never be returned.
+ *
+ * Returns:
+ * char_offset giving split point closest to x, where actual_x <= x
+ * else
+ * char_offset giving split point closest to x, where actual_x > x
+ *
+ * Returning char_offset == length means no split possible
+ */
+
+static nserror amiga_nsfont_split(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x)
+{
+ uint16 *utf16_str = NULL;
+ const uint16 *utf16 = NULL;
+ const uint16 *utf16next = NULL;
+ struct OutlineFont *ofont, *ufont = NULL;
+ int tx = 0;
+ uint32 utf8_pos = 0;
+ int32 tempx = 0;
+ ULONG emwidth = (ULONG)NSA_FONT_EMWIDTH(fstyle->size);
+
+ /* Get utf16 conversion of string for glyph measuring routines */
+ if (utf8_to_enc(string, "UTF-16", length, (char **)&utf16_str) !=
+ NSERROR_OK)
+ return NSERROR_INVALID;
+
+ utf16 = utf16_str;
+ if (!(ofont = ami_open_outline_font(fstyle, 0)))
+ return NSERROR_INVALID;
+
+ *char_offset = 0;
+ *actual_x = 0;
+
+ if (*utf16 == 0xFEFF) utf16++;
+
+ while (utf8_pos < length) {
+ if ((*utf16 < 0xD800) || (0xDBFF < *utf16))
+ utf16next = utf16 + 1;
+ else
+ utf16next = utf16 + 2;
+
+ tempx = ami_font_width_glyph(ofont, utf16, utf16next, emwidth);
+
+ if (tempx == 0) {
+ if (ufont == NULL)
+ ufont = ami_open_outline_font(fstyle, utf16);
+
+ if (ufont)
+ tempx = ami_font_width_glyph(ufont, utf16,
+ utf16next, emwidth);
+ }
+
+ /* Check whether we have a space */
+ if (*(string + utf8_pos) == ' ') {
+ /* Got a space */
+ *actual_x = tx;
+ *char_offset = utf8_pos;
+ }
+
+ tx += tempx;
+ if ((x < tx) && (*char_offset != 0)) {
+ /* Reached available width, and a space was found;
+ * split there. */
+ free(utf16_str);
+ return NSERROR_OK;
+ }
+
+ utf16 = utf16next;
+ utf8_pos = utf8_next(string, length, utf8_pos);
+ }
+
+ free(utf16_str);
+
+ /* No spaces to split at, or everything fits */
+ assert(*char_offset == 0 || x >= tx);
+
+ *char_offset = length;
+ *actual_x = tx;
+ return NSERROR_OK;
+}
+
+/**
+ * Search for a font in the list and load from disk if not present
+ */
+static struct ami_font_cache_node *ami_font_open(const char *font, bool critical)
+{
+ struct ami_font_cache_node *nodedata = ami_font_cache_locate(font);
+ if(nodedata) return nodedata;
+
+ nodedata = ami_font_cache_alloc_entry(font);
+
+ if(nodedata == NULL) {
+ amiga_warn_user("NoMemory", "");
+ return NULL;
+ }
+
+ nodedata->font = OpenOutlineFont(font, &ami_diskfontlib_list, OFF_OPEN);
+
+ if(!nodedata->font)
+ {
+ LOG("Requested font not found: %s", font);
+ if(critical == true) amiga_warn_user("CompError", font);
+ FreeVec(nodedata);
+ return NULL;
+ }
+
+ nodedata->bold = (char *)GetTagData(OT_BName, 0, nodedata->font->olf_OTagList);
+ if(nodedata->bold)
+ LOG("Bold font defined for %s is %s", font, nodedata->bold);
+ else
+ LOG("Warning: No designed bold font defined for %s", font);
+
+ nodedata->italic = (char *)GetTagData(OT_IName, 0, nodedata->font->olf_OTagList);
+ if(nodedata->italic)
+ LOG("Italic font defined for %s is %s", font, nodedata->italic);
+ else
+ LOG("Warning: No designed italic font defined for %s", font);
+
+ nodedata->bolditalic = (char *)GetTagData(OT_BIName, 0, nodedata->font->olf_OTagList);
+ if(nodedata->bolditalic)
+ LOG("Bold-italic font defined for %s is %s", font, nodedata->bolditalic);
+ else
+ LOG("Warning: No designed bold-italic font defined for %s", font);
+
+ ami_font_cache_insert(nodedata, font);
+ return nodedata;
+}
+
+/**
+ * Open an outline font in the specified size and style
+ *
+ * \param fstyle font style structure
+ * \param codepoint open a default font instead of the one specified by fstyle
+ * \return outline font or NULL on error
+ */
+static struct OutlineFont *ami_open_outline_font(const plot_font_style_t *fstyle,
+ const uint16 *codepoint)
+{
+ struct ami_font_cache_node *node;
+ struct ami_font_cache_node *designed_node = NULL;
+ struct OutlineFont *ofont;
+ char *fontname;
+ ULONG ysize;
+ int tstyle = 0;
+ plot_font_generic_family_t fontfamily;
+ ULONG emboldenx = 0;
+ ULONG emboldeny = 0;
+ ULONG shearsin = 0;
+ ULONG shearcos = (1 << 16);
+
+ if(codepoint) fontfamily = NSA_UNICODE_FONT;
+ else fontfamily = fstyle->family;
+
+ switch(fontfamily)
+ {
+ case PLOT_FONT_FAMILY_SANS_SERIF:
+ fontname = nsoption_charp(font_sans);
+ break;
+ case PLOT_FONT_FAMILY_SERIF:
+ fontname = nsoption_charp(font_serif);
+ break;
+ case PLOT_FONT_FAMILY_MONOSPACE:
+ fontname = nsoption_charp(font_mono);
+ break;
+ case PLOT_FONT_FAMILY_CURSIVE:
+ fontname = nsoption_charp(font_cursive);
+ break;
+ case PLOT_FONT_FAMILY_FANTASY:
+ fontname = nsoption_charp(font_fantasy);
+ break;
+ case NSA_UNICODE_FONT:
+ default:
+ if(__builtin_expect((amiga_nsfont_utf16_char_length(codepoint) == 2), 0)) {
+ /* Multi-byte character */
+ fontname = nsoption_charp(font_surrogate);
+ } else {
+ fontname = (char *)ami_font_scan_lookup(codepoint, glypharray);
+ }
+ if(fontname == NULL) return NULL;
+ break;
+ }
+
+ node = ami_font_open(fontname, true);
+ if(!node) return NULL;
+
+ if (fstyle->flags & FONTF_OBLIQUE)
+ tstyle = NSA_OBLIQUE;
+
+ if (fstyle->flags & FONTF_ITALIC)
+ tstyle = NSA_ITALIC;
+
+ if (fstyle->weight >= 700)
+ tstyle += NSA_BOLD;
+
+ switch(tstyle)
+ {
+ case NSA_ITALIC:
+ if(node->italic) designed_node = ami_font_open(node->italic, false);
+
+ if(designed_node == NULL) {
+ shearsin = NSA_VALUE_SHEARSIN;
+ shearcos = NSA_VALUE_SHEARCOS;
+ }
+ break;
+
+ case NSA_OBLIQUE:
+ shearsin = NSA_VALUE_SHEARSIN;
+ shearcos = NSA_VALUE_SHEARCOS;
+ break;
+
+ case NSA_BOLD:
+ if(node->bold) designed_node = ami_font_open(node->bold, false);
+
+ if(designed_node == NULL) {
+ emboldenx = NSA_VALUE_BOLDX;
+ emboldeny = NSA_VALUE_BOLDY;
+ }
+ break;
+
+ case NSA_BOLDOBLIQUE:
+ shearsin = NSA_VALUE_SHEARSIN;
+ shearcos = NSA_VALUE_SHEARCOS;
+
+ if(node->bold) designed_node = ami_font_open(node->bold, false);
+
+ if(designed_node == NULL) {
+ emboldenx = NSA_VALUE_BOLDX;
+ emboldeny = NSA_VALUE_BOLDY;
+ }
+ break;
+
+ case NSA_BOLDITALIC:
+ if(node->bolditalic) designed_node = ami_font_open(node->bolditalic, false);
+
+ if(designed_node == NULL) {
+ emboldenx = NSA_VALUE_BOLDX;
+ emboldeny = NSA_VALUE_BOLDY;
+ shearsin = NSA_VALUE_SHEARSIN;
+ shearcos = NSA_VALUE_SHEARCOS;
+ }
+ break;
+ }
+
+ /* Scale to 16.16 fixed point */
+ ysize = fstyle->size * ((1 << 16) / FONT_SIZE_SCALE);
+
+ if(designed_node == NULL) {
+ ofont = node->font;
+ } else {
+ ofont = designed_node->font;
+ }
+
+#ifndef __amigaos4__
+ struct BulletBase *BulletBase = ofont->BulletBase;
+#endif
+
+ if(ESetInfo(AMI_OFONT_ENGINE,
+ OT_DeviceDPI, ami_font_dpi_get_devicedpi(),
+ OT_PointHeight, ysize,
+ OT_EmboldenX, emboldenx,
+ OT_EmboldenY, emboldeny,
+ OT_ShearSin, shearsin,
+ OT_ShearCos, shearcos,
+ TAG_END) == OTERR_Success)
+ return ofont;
+
+ return NULL;
+}
+
+static inline int32 ami_font_plot_glyph(struct OutlineFont *ofont, struct RastPort *rp,
+ uint16 *char1, uint16 *char2, uint32 x, uint32 y, uint32 emwidth, bool aa)
+{
+ struct GlyphMap *glyph;
+ UBYTE *glyphbm;
+ int32 char_advance = 0;
+ FIXED kern = 0;
+ ULONG glyphmaptag;
+ ULONG template_type;
+ uint32 long_char_1 = 0, long_char_2 = 0;
+#ifndef __amigaos4__
+ struct BulletBase *BulletBase = ofont->BulletBase;
+#endif
+
+#ifndef __amigaos4__
+ if (__builtin_expect(((*char1 >= 0xD800) && (*char1 <= 0xDBFF)), 0)) {
+ /* We don't support UTF-16 surrogates yet, so just return. */
+ return 0;
+ }
+
+ if (__builtin_expect(((*char2 >= 0xD800) && (*char2 <= 0xDBFF)), 0)) {
+ /* Don't attempt to kern a UTF-16 surrogate */
+ *char2 = 0;
+ }
+#endif
+
+#ifdef __amigaos4__
+ if(__builtin_expect(aa == true, 1)) {
+ glyphmaptag = OT_GlyphMap8Bit;
+ template_type = BLITT_ALPHATEMPLATE;
+ } else {
+#endif
+ glyphmaptag = OT_GlyphMap;
+#ifdef __amigaos4__
+ template_type = BLITT_TEMPLATE;
+ }
+#endif
+
+ long_char_1 = amiga_nsfont_decode_surrogate(char1);
+ long_char_2 = amiga_nsfont_decode_surrogate(char2);
+ /**\todo use OT_GlyphCode_32 so we get an error for old font engines */
+
+ if(ESetInfo(AMI_OFONT_ENGINE,
+ OT_GlyphCode, long_char_1,
+ OT_GlyphCode2, long_char_2,
+ TAG_END) == OTERR_Success)
+ {
+ if(EObtainInfo(AMI_OFONT_ENGINE,
+ glyphmaptag, &glyph,
+ TAG_END) == 0)
+ {
+ glyphbm = glyph->glm_BitMap;
+ if(!glyphbm) return 0;
+
+ if(rp) {
+#ifdef __amigaos4__
+ BltBitMapTags(BLITA_SrcX, glyph->glm_BlackLeft,
+ BLITA_SrcY, glyph->glm_BlackTop,
+ BLITA_DestX, x - glyph->glm_X0 + glyph->glm_BlackLeft,
+ BLITA_DestY, y - glyph->glm_Y0 + glyph->glm_BlackTop,
+ BLITA_Width, glyph->glm_BlackWidth,
+ BLITA_Height, glyph->glm_BlackHeight,
+ BLITA_Source, glyphbm,
+ BLITA_SrcType, template_type,
+ BLITA_Dest, rp,
+ BLITA_DestType, BLITT_RASTPORT,
+ BLITA_SrcBytesPerRow, glyph->glm_BMModulo,
+ TAG_DONE);
+#else
+ /* On OS3 the glyph needs to be in chip RAM */
+ void *chip_glyph = AllocVec(glyph->glm_BMModulo * glyph->glm_BMRows, MEMF_CHIP);
+ if(chip_glyph != NULL) {
+ CopyMem(glyphbm, chip_glyph, glyph->glm_BMModulo * glyph->glm_BMRows);
+
+ BltTemplate(chip_glyph + (glyph->glm_BMModulo * glyph->glm_BlackTop) +
+ ((glyph->glm_BlackLeft >> 4) << 1),
+ glyph->glm_BlackLeft & 0xF, glyph->glm_BMModulo, rp,
+ x - glyph->glm_X0 + glyph->glm_BlackLeft,
+ y - glyph->glm_Y0 + glyph->glm_BlackTop,
+ glyph->glm_BlackWidth, glyph->glm_BlackHeight);
+
+ FreeVec(chip_glyph);
+ }
+#endif
+ }
+
+ kern = 0;
+
+ if(*char2) EObtainInfo(AMI_OFONT_ENGINE,
+ OT_TextKernPair, &kern,
+ TAG_END);
+
+ char_advance = (ULONG)(((glyph->glm_Width - kern) * emwidth) / 65536);
+
+ EReleaseInfo(AMI_OFONT_ENGINE,
+ glyphmaptag, glyph,
+ TAG_END);
+
+ if(*char2) EReleaseInfo(AMI_OFONT_ENGINE,
+ OT_TextKernPair, kern,
+ TAG_END);
+ }
+ }
+
+ return char_advance;
+}
+
+static inline int32 ami_font_width_glyph(struct OutlineFont *ofont,
+ const uint16 *char1, const uint16 *char2, uint32 emwidth)
+{
+ int32 char_advance = 0;
+ FIXED kern = 0;
+ struct MinList *gwlist = NULL;
+ FIXED char1w = 0;
+ struct GlyphWidthEntry *gwnode;
+ bool skip_c2 = false;
+ uint32 long_char_1 = 0;
+ uint32 long_char_2;
+#ifndef __amigaos4__
+ struct BulletBase *BulletBase = ofont->BulletBase;
+#endif
+
+#ifndef __amigaos4__
+ if (__builtin_expect(((*char1 >= 0xD800) && (*char1 <= 0xDBFF)), 0)) {
+ /* We don't support UTF-16 surrogates yet, so just return. */
+ return 0;
+ }
+
+ if (__builtin_expect(((*char2 >= 0xD800) && (*char2 <= 0xDBFF)), 0)) {
+ /* Don't attempt to kern a UTF-16 surrogate */
+ skip_c2 = true;
+ }
+#endif
+
+ if (*char2 < 0x0020) skip_c2 = true;
+
+ long_char_1 = amiga_nsfont_decode_surrogate(char1);
+ /**\todo use OT_GlyphCode_32 so we get an error for old font engines */
+
+ if(ESetInfo(AMI_OFONT_ENGINE,
+ OT_GlyphCode, long_char_1,
+ OT_GlyphCode2, long_char_1,
+ TAG_END) == OTERR_Success)
+ {
+ if(EObtainInfo(AMI_OFONT_ENGINE,
+ OT_WidthList, &gwlist,
+ TAG_END) == 0)
+ {
+ gwnode = (struct GlyphWidthEntry *)GetHead((struct List *)gwlist);
+ if(gwnode) char1w = gwnode->gwe_Width;
+
+ kern = 0;
+
+ if(!skip_c2) {
+ long_char_2 = amiga_nsfont_decode_surrogate(char2);
+ if(ESetInfo(AMI_OFONT_ENGINE,
+ OT_GlyphCode, long_char_1,
+ OT_GlyphCode2, long_char_2,
+ TAG_END) == OTERR_Success)
+ {
+ EObtainInfo(AMI_OFONT_ENGINE,
+ OT_TextKernPair, &kern,
+ TAG_END);
+ }
+ }
+ char_advance = (ULONG)(((char1w - kern) * emwidth) / 65536);
+
+ if(!skip_c2) EReleaseInfo(AMI_OFONT_ENGINE,
+ OT_TextKernPair, kern,
+ TAG_END);
+
+ EReleaseInfo(AMI_OFONT_ENGINE,
+ OT_WidthList, gwlist,
+ TAG_END);
+ }
+ }
+
+ return char_advance;
+}
+
+static const uint16 *ami_font_translate_smallcaps(uint16 *utf16char)
+{
+ const uint16 *p;
+ p = &sc_table[0];
+
+ while (*p != 0)
+ {
+ if(*p == *utf16char) return &p[1];
+ p++;
+ }
+
+ return utf16char;
+}
+
+static ULONG amiga_nsfont_text(struct RastPort *rp, const char *string, ULONG length,
+ const plot_font_style_t *fstyle, ULONG dx, ULONG dy, bool aa)
+{
+ uint16 *utf16 = NULL, *outf16 = NULL;
+ uint16 *utf16charsc = 0, *utf16nextsc = 0;
+ uint16 *utf16next = 0;
+ int utf16charlen;
+ struct OutlineFont *ofont, *ufont = NULL;
+ uint32 x=0;
+ int32 tempx = 0;
+ ULONG emwidth = (ULONG)NSA_FONT_EMWIDTH(fstyle->size);
+ uint16 utf16_a = 0x41;
+
+ if(!string || string[0]=='\0') return 0;
+ if(!length) return 0;
+ if(rp == NULL) return 0;
+
+ if(utf8_to_enc(string,"UTF-16",length,(char **)&utf16) != NSERROR_OK) return 0;
+ outf16 = utf16;
+ if(!(ofont = ami_open_outline_font(fstyle, 0))) {
+ if(!(ofont = ami_open_outline_font(fstyle, &utf16_a))) return 0;
+ }
+
+ while(*utf16 != 0)
+ {
+ utf16charlen = amiga_nsfont_utf16_char_length(utf16);
+ utf16next = &utf16[utf16charlen];
+
+ if(fstyle->flags & FONTF_SMALLCAPS)
+ {
+ utf16charsc = (uint16 *)ami_font_translate_smallcaps(utf16);
+ utf16nextsc = (uint16 *)ami_font_translate_smallcaps(utf16next);
+
+ tempx = ami_font_plot_glyph(ofont, rp, utf16charsc, utf16nextsc,
+ dx + x, dy, emwidth, aa);
+ }
+ else tempx = 0;
+
+ if(tempx == 0) {
+ tempx = ami_font_plot_glyph(ofont, rp, utf16, utf16next,
+ dx + x, dy, emwidth, aa);
+ }
+
+ if(tempx == 0)
+ {
+ if(ufont == NULL)
+ {
+ ufont = ami_open_outline_font(fstyle, utf16);
+ }
+
+ if(ufont) {
+ tempx = ami_font_plot_glyph(ufont, rp, utf16, utf16next,
+ dx + x, dy, emwidth, aa);
+ }
+ }
+
+ x += tempx;
+
+ utf16 += utf16charlen;
+ }
+
+ free(outf16);
+ return x;
+}
+
+static inline ULONG ami_font_unicode_width(const char *string, ULONG length,
+ const plot_font_style_t *fstyle, ULONG dx, ULONG dy, bool aa)
+{
+ uint16 *utf16 = NULL, *outf16 = NULL;
+ uint16 *utf16charsc = 0, *utf16nextsc = 0;
+ uint16 *utf16next = 0;
+ int utf16charlen;
+ struct OutlineFont *ofont, *ufont = NULL;
+ uint32 x=0;
+ int32 tempx = 0;
+ ULONG emwidth = (ULONG)NSA_FONT_EMWIDTH(fstyle->size);
+ uint16 utf16_a = 0x41;
+
+ if(!string || string[0]=='\0') return 0;
+ if(!length) return 0;
+
+ if(utf8_to_enc(string,"UTF-16",length,(char **)&utf16) != NSERROR_OK) return 0;
+ outf16 = utf16;
+ if(!(ofont = ami_open_outline_font(fstyle, 0))) {
+ if(!(ofont = ami_open_outline_font(fstyle, &utf16_a))) return 0;
+ }
+
+ while(*utf16 != 0)
+ {
+ utf16charlen = amiga_nsfont_utf16_char_length(utf16);
+ utf16next = &utf16[utf16charlen];
+
+ if(fstyle->flags & FONTF_SMALLCAPS)
+ {
+ utf16charsc = (uint16 *)ami_font_translate_smallcaps(utf16);
+ utf16nextsc = (uint16 *)ami_font_translate_smallcaps(utf16next);
+
+ tempx = ami_font_width_glyph(ofont, utf16charsc, utf16nextsc, emwidth);
+ }
+ else tempx = 0;
+
+ if(tempx == 0) {
+ tempx = ami_font_width_glyph(ofont, utf16, utf16next, emwidth);
+ }
+
+ if(tempx == 0)
+ {
+ if(ufont == NULL)
+ {
+ ufont = ami_open_outline_font(fstyle, utf16);
+ }
+
+ if(ufont)
+ {
+ tempx = ami_font_width_glyph(ufont, utf16, utf16next, emwidth);
+ }
+ }
+
+ x += tempx;
+
+ utf16 += utf16charlen;
+ }
+
+ free(outf16);
+ return x;
+}
+
+void ami_font_bullet_close(void *nso)
+{
+ struct ami_font_cache_node *node = (struct ami_font_cache_node *)nso;
+ CloseOutlineFont(node->font, &ami_diskfontlib_list);
+}
+
+const struct ami_font_functions ami_font_bullet_table = {
+ amiga_nsfont_width,
+ amiga_nsfont_position_in_string,
+ amiga_nsfont_split,
+ amiga_nsfont_text
+};
+
+void ami_font_bullet_init(void)
+{
+ /* Initialise Unicode font scanner */
+ ami_font_initscanner(false, true);
+
+ /* Initialise font caching etc lists */
+ ami_font_cache_init();
+
+ /* List for diskfont internal cache */
+ NewList(&ami_diskfontlib_list);
+
+ /* Set up table */
+ ami_nsfont = &ami_font_bullet_table;
+}
+
+void ami_font_bullet_fini(void)
+{
+ ami_font_cache_fini();
+ ami_font_finiscanner();
+}
+
+/* Font scanner */
+void ami_font_initscanner(bool force, bool save)
+{
+ ami_font_scan_init(nsoption_charp(font_unicode_file), force, save, glypharray);
+}
+
+void ami_font_finiscanner(void)
+{
+ ami_font_scan_fini(glypharray);
+}
+
+void ami_font_savescanner(void)
+{
+ ami_font_scan_save(nsoption_charp(font_unicode_file), glypharray);
+}
+
diff --git a/frontends/amiga/font_bullet.h b/frontends/amiga/font_bullet.h
new file mode 100644
index 000000000..1ab9582db
--- /dev/null
+++ b/frontends/amiga/font_bullet.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_FONT_BULLET_H
+#define AMIGA_FONT_BULLET_H
+struct ami_font_cache_node;
+
+void ami_font_bullet_init(void);
+void ami_font_bullet_fini(void);
+void ami_font_bullet_close(void *nso);
+
+/* Alternate entry points into font_scan */
+void ami_font_initscanner(bool force, bool save);
+void ami_font_finiscanner(void);
+void ami_font_savescanner(void);
+#endif
+
diff --git a/frontends/amiga/font_cache.c b/frontends/amiga/font_cache.c
new file mode 100644
index 000000000..ea285730a
--- /dev/null
+++ b/frontends/amiga/font_cache.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2015 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "amiga/os3support.h"
+#include <string.h>
+
+#include <proto/timer.h>
+#include <proto/utility.h>
+
+#include "utils/log.h"
+
+#include "amiga/font.h"
+#include "amiga/font_bullet.h"
+#include "amiga/font_cache.h"
+#include "amiga/schedule.h"
+
+#ifdef __amigaos4__
+#include "amiga/hash/xxhash.h"
+#else
+#include "amiga/object.h"
+#endif
+
+#ifdef __amigaos4__
+static struct SkipList *ami_font_cache_list = NULL;
+static struct Hook ami_font_cache_hook;
+#else
+static struct MinList *ami_font_cache_list = NULL;
+#endif
+
+
+
+#ifdef __amigaos4__
+static LONG ami_font_cache_sort(struct Hook *hook, APTR key1, APTR key2)
+{
+ if(key1 == key2) return 0;
+ if(key1 < key2) return -1;
+ return 1;
+}
+#endif
+
+#ifdef __amigaos4__
+static void ami_font_cache_cleanup(struct SkipList *skiplist)
+{
+ struct ami_font_cache_node *node;
+ struct ami_font_cache_node *nnode;
+ struct TimeVal curtime;
+
+ node = (struct ami_font_cache_node *)GetFirstSkipNode(skiplist);
+ if(node == NULL) return;
+
+ do {
+ nnode = (struct ami_font_cache_node *)GetNextSkipNode(skiplist, (struct SkipNode *)node);
+ GetSysTime(&curtime);
+ SubTime(&curtime, &node->lastused);
+ if(curtime.Seconds > 300)
+ {
+ LOG("Freeing font %p not used for %ld seconds", node->skip_node.sn_Key, curtime.Seconds);
+ ami_font_bullet_close(node);
+ RemoveSkipNode(skiplist, node->skip_node.sn_Key);
+ }
+ } while((node = nnode));
+
+ /* reschedule to run in five minutes */
+ ami_schedule(300000, (void *)ami_font_cache_cleanup, ami_font_cache_list);
+}
+#else
+static void ami_font_cache_cleanup(struct MinList *ami_font_cache_list)
+{
+ struct nsObject *node;
+ struct nsObject *nnode;
+ struct ami_font_cache_node *fnode;
+ struct TimeVal curtime;
+
+ if(IsMinListEmpty(ami_font_cache_list)) return;
+
+ node = (struct nsObject *)GetHead((struct List *)ami_font_cache_list);
+
+ do
+ {
+ nnode=(struct nsObject *)GetSucc((struct Node *)node);
+ fnode = node->objstruct;
+ GetSysTime(&curtime);
+ SubTime(&curtime, &fnode->lastused);
+ if(curtime.Seconds > 300)
+ {
+ LOG("Freeing %s not used for %ld seconds", node->dtz_Node.ln_Name, curtime.Seconds);
+ DelObject(node);
+ }
+ } while((node=nnode));
+
+ /* reschedule to run in five minutes */
+ ami_schedule(300000, (void *)ami_font_cache_cleanup, ami_font_cache_list);
+}
+#endif
+
+#ifdef __amigaos4__
+static void ami_font_cache_del_skiplist(struct SkipList *skiplist)
+{
+ struct SkipNode *node;
+ struct SkipNode *nnode;
+
+ node = GetFirstSkipNode(skiplist);
+ if(node == NULL) return;
+
+ do {
+ nnode = GetNextSkipNode(skiplist, node);
+ ami_font_bullet_close((struct ami_font_cache_node *)node);
+
+ } while((node = nnode));
+
+ DeleteSkipList(skiplist);
+}
+#endif
+
+
+struct ami_font_cache_node *ami_font_cache_locate(const char *font)
+{
+ struct ami_font_cache_node *nodedata;
+ uint32 hash = 0;
+
+#ifdef __amigaos4__
+ hash = XXH32(font, strlen(font), 0);
+ nodedata = (struct ami_font_cache_node *)FindSkipNode(ami_font_cache_list, (APTR)hash);
+#else
+ struct nsObject *node = (struct nsObject *)FindIName((struct List *)ami_font_cache_list, font);
+ if(node) nodedata = node->objstruct;
+#endif
+
+ if(nodedata) {
+ GetSysTime(&nodedata->lastused);
+ return nodedata;
+ }
+
+ LOG("Font cache miss: %s (%lx)", font, hash);
+ return NULL;
+}
+
+struct ami_font_cache_node *ami_font_cache_alloc_entry(const char *font)
+{
+ struct ami_font_cache_node *nodedata;
+
+#ifdef __amigaos4__
+ uint32 hash = XXH32(font, strlen(font), 0);
+ nodedata = (struct ami_font_cache_node *)InsertSkipNode(ami_font_cache_list, (APTR)hash, sizeof(struct ami_font_cache_node));
+#else
+ nodedata = AllocVecTagList(sizeof(struct ami_font_cache_node), NULL);
+#endif
+
+ GetSysTime(&nodedata->lastused);
+
+ return nodedata;
+}
+
+void ami_font_cache_insert(struct ami_font_cache_node *nodedata, const char *font)
+{
+#ifndef __amigaos4__
+ struct nsObject *node = AddObject(ami_font_cache_list, AMINS_FONT);
+ if(node) {
+ ObjectCallback(node, ami_font_bullet_close);
+ node->objstruct = nodedata;
+ node->dtz_Node.ln_Name = strdup(font);
+ }
+#endif
+}
+
+void ami_font_cache_fini(void)
+{
+ LOG("Cleaning up font cache");
+ ami_schedule(-1, (void *)ami_font_cache_cleanup, ami_font_cache_list);
+#ifdef __amigaos4__
+ ami_font_cache_del_skiplist(ami_font_cache_list);
+#else
+ FreeObjList(ami_font_cache_list);
+#endif
+ ami_font_cache_list = NULL;
+}
+
+void ami_font_cache_init(void)
+{
+#ifdef __amigaos4__
+ ami_font_cache_hook.h_Entry = (HOOKFUNC)ami_font_cache_sort;
+ ami_font_cache_hook.h_Data = 0;
+ ami_font_cache_list = CreateSkipList(&ami_font_cache_hook, 8);
+#else
+ ami_font_cache_list = NewObjList();
+#endif
+
+ /* run first cleanup in ten minutes */
+ ami_schedule(600000, (void *)ami_font_cache_cleanup, ami_font_cache_list);
+}
+
diff --git a/frontends/amiga/font_cache.h b/frontends/amiga/font_cache.h
new file mode 100644
index 000000000..7c95ba594
--- /dev/null
+++ b/frontends/amiga/font_cache.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2015 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_FONT_CACHE_H
+#define AMIGA_FONT_CACHE_H
+
+#include <proto/timer.h>
+
+struct ami_font_cache_node
+{
+#ifdef __amigaos4__
+ struct SkipNode skip_node;
+#endif
+ struct OutlineFont *font;
+ char *bold;
+ char *italic;
+ char *bolditalic;
+ struct TimeVal lastused;
+};
+
+
+/* locate an entry in the font cache, NULL if not found */
+struct ami_font_cache_node *ami_font_cache_locate(const char *font);
+
+/* allocate a cache entry */
+struct ami_font_cache_node *ami_font_cache_alloc_entry(const char *font);
+
+/* insert a cache entry into the list (OS3) */
+void ami_font_cache_insert(struct ami_font_cache_node *nodedata, const char *font);
+
+/* initialise the cache */
+void ami_font_cache_init(void);
+
+/* cache clean-up */
+void ami_font_cache_fini(void);
+
+#endif
+
+
diff --git a/frontends/amiga/font_diskfont.c b/frontends/amiga/font_diskfont.c
new file mode 100644
index 000000000..a64674ba2
--- /dev/null
+++ b/frontends/amiga/font_diskfont.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2008 - 2016 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "amiga/os3support.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <proto/diskfont.h>
+#include <proto/exec.h>
+#include <proto/graphics.h>
+#include <proto/utility.h>
+
+#include <graphics/rpattr.h>
+
+#include "utils/log.h"
+#include "utils/utf8.h"
+#include "utils/nsoption.h"
+
+#include "amiga/font.h"
+#include "amiga/font_diskfont.h"
+#include "amiga/gui.h"
+#include "amiga/utf8.h"
+
+#define MAX_FONT_NAME_SIZE 33
+
+static struct TextFont *ami_font_bm_open(struct RastPort *rp, const plot_font_style_t *fstyle)
+{
+ struct TextFont *bmfont = NULL;
+ struct TextAttr tattr;
+ char *fontname;
+ char font[MAX_FONT_NAME_SIZE];
+
+ if(rp == NULL) return NULL;
+
+ tattr.ta_Flags = 0;
+
+ switch(fstyle->family)
+ {
+ case PLOT_FONT_FAMILY_SANS_SERIF:
+ fontname = nsoption_charp(font_sans);
+ break;
+ case PLOT_FONT_FAMILY_SERIF:
+ fontname = nsoption_charp(font_serif);
+ break;
+ case PLOT_FONT_FAMILY_MONOSPACE:
+ fontname = nsoption_charp(font_mono);
+ break;
+ case PLOT_FONT_FAMILY_CURSIVE:
+ fontname = nsoption_charp(font_cursive);
+ break;
+ case PLOT_FONT_FAMILY_FANTASY:
+ fontname = nsoption_charp(font_fantasy);
+ break;
+ default:
+ return NULL;
+ break;
+ }
+
+ tattr.ta_Style = FS_NORMAL;
+
+ if (fstyle->flags & FONTF_OBLIQUE)
+ tattr.ta_Style = FSF_ITALIC;
+
+ if (fstyle->flags & FONTF_ITALIC)
+ tattr.ta_Style = FSF_ITALIC;
+
+ if (fstyle->weight >= 700)
+ tattr.ta_Style |= FSF_BOLD;
+
+ snprintf(font, MAX_FONT_NAME_SIZE, "%s.font", fontname);
+ tattr.ta_Name = font;
+ tattr.ta_YSize = fstyle->size / FONT_SIZE_SCALE;
+ LOG("font: %s/%d", tattr.ta_Name, tattr.ta_YSize);
+ if((bmfont = OpenDiskFont(&tattr))) {
+ SetRPAttrs(rp, RPTAG_Font, bmfont, TAG_DONE);
+ }
+
+ return bmfont;
+}
+
+static void ami_font_bm_close(struct TextFont *bmfont)
+{
+ CloseFont(bmfont);
+}
+
+static size_t ami_font_bm_convert_local_to_utf8_offset(const char *utf8string, size_t length, UWORD offset)
+{
+ size_t chr = 0;
+
+ for(UWORD i = 0; i < offset; i++) {
+ chr = utf8_next(utf8string, length, chr);
+ if(chr > length) return length;
+ }
+
+ return chr;
+}
+
+
+static nserror amiga_bm_nsfont_width(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int *width)
+{
+ char *localtext = NULL;
+
+ if((glob == NULL) || (glob->rp == NULL)) return NSERROR_INVALID;
+ *width = length;
+
+ struct TextFont *bmfont = ami_font_bm_open(glob->rp, fstyle);
+ if(bmfont == NULL) return NSERROR_INVALID;
+
+ if(utf8_to_local_encoding(string, length, &localtext) != NSERROR_OK) {
+ ami_font_bm_close(bmfont);
+ return NSERROR_INVALID;
+ }
+
+ *width = TextLength(glob->rp, localtext, (UWORD)strlen(localtext));
+ free(localtext);
+
+ ami_font_bm_close(bmfont);
+
+ return NSERROR_OK;
+}
+
+/**
+ * Find the position in a string where an x coordinate falls.
+ *
+ * \param fstyle style for this text
+ * \param string UTF-8 string to measure
+ * \param length length of string
+ * \param x x coordinate to search for
+ * \param char_offset updated to offset in string of actual_x, [0..length]
+ * \param actual_x updated to x coordinate of character closest to x
+ * \return true on success, false on error and error reported
+ */
+
+static nserror amiga_bm_nsfont_position_in_string(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x)
+{
+ struct TextExtent extent;
+ struct TextFont *bmfont;
+ char *localtext = NULL;
+ UWORD co = 0;
+
+ if((glob == NULL) || (glob->rp == NULL)) return NSERROR_INVALID;
+
+ bmfont = ami_font_bm_open(glob->rp, fstyle);
+ if(bmfont == NULL) return NSERROR_INVALID;
+
+ if(utf8_to_local_encoding(string, length, &localtext) != NSERROR_OK) {
+ ami_font_bm_close(bmfont);
+ return NSERROR_INVALID;
+ }
+
+ co = TextFit(glob->rp, localtext, (UWORD)strlen(localtext),
+ &extent, NULL, 1, x, 32767);
+ *char_offset = ami_font_bm_convert_local_to_utf8_offset(string, length, co);
+ *actual_x = extent.te_Extent.MaxX;
+
+ free(localtext);
+ ami_font_bm_close(bmfont);
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Find where to split a string to make it fit a width.
+ *
+ * \param fstyle style for this text
+ * \param string UTF-8 string to measure
+ * \param length length of string
+ * \param x width available
+ * \param char_offset updated to offset in string of actual_x, [1..length]
+ * \param actual_x updated to x coordinate of character closest to x
+ * \return true on success, false on error and error reported
+ *
+ * On exit, char_offset indicates first character after split point.
+ *
+ * Note: char_offset of 0 should never be returned.
+ *
+ * Returns:
+ * char_offset giving split point closest to x, where actual_x <= x
+ * else
+ * char_offset giving split point closest to x, where actual_x > x
+ *
+ * Returning char_offset == length means no split possible
+ */
+
+static nserror amiga_bm_nsfont_split(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x)
+{
+ struct TextExtent extent;
+ UWORD co, offset;
+ char *charp;
+ char *localtext;
+
+ if((glob == NULL) || (glob->rp == NULL)) return NSERROR_INVALID;
+
+ struct TextFont *bmfont = ami_font_bm_open(glob->rp, fstyle);
+ if(bmfont == NULL) return NSERROR_INVALID;
+
+ if(utf8_to_local_encoding(string, length, &localtext) != NSERROR_OK) {
+ ami_font_bm_close(bmfont);
+ return NSERROR_INVALID;
+ }
+
+ offset = TextFit(glob->rp, localtext, (UWORD)strlen(localtext),
+ &extent, NULL, 1, (UWORD)x, 32767);
+
+ co = offset;
+ charp = localtext + co;
+
+
+ while((*charp != ' ') && (co > 0)) {
+ charp--;
+ co--;
+ }
+
+ if(co == 0) {
+ co = offset;
+ charp = localtext + co;
+ while((*charp != ' ') && (co < strlen(localtext))) {
+ charp++;
+ co++;
+ }
+ }
+
+ if((co > 0) && (co < strlen(localtext))) {
+ *actual_x = TextLength(glob->rp, localtext, co);
+ *char_offset = ami_font_bm_convert_local_to_utf8_offset(string, length, co);
+ } else {
+ *actual_x = x;
+ *char_offset = length;
+ }
+
+ free(localtext);
+ ami_font_bm_close(bmfont);
+
+ return NSERROR_OK;
+}
+
+static ULONG amiga_bm_nsfont_text(struct RastPort *rp, const char *string, ULONG length,
+ const plot_font_style_t *fstyle, ULONG dx, ULONG dy, bool aa)
+{
+ if(!string || string[0]=='\0') return 0;
+ if(!length) return 0;
+ if(rp == NULL) return 0;
+
+ struct TextFont *bmfont = ami_font_bm_open(rp, fstyle);
+ char *localtext = NULL;
+ if(bmfont == NULL) return 0;
+ if(utf8_to_local_encoding(string, length, &localtext) == NSERROR_OK) {
+ Move(rp, dx, dy);
+ Text(rp, localtext, (UWORD)strlen(localtext));
+ free(localtext);
+ }
+
+ ami_font_bm_close(bmfont);
+
+ return 0;
+}
+
+const struct ami_font_functions ami_font_diskfont_table = {
+ amiga_bm_nsfont_width,
+ amiga_bm_nsfont_position_in_string,
+ amiga_bm_nsfont_split,
+ amiga_bm_nsfont_text
+};
+
+void ami_font_diskfont_init(void)
+{
+ /* Set up table */
+ ami_nsfont = &ami_font_diskfont_table;
+}
+
diff --git a/frontends/amiga/font_diskfont.h b/frontends/amiga/font_diskfont.h
new file mode 100644
index 000000000..de19e940e
--- /dev/null
+++ b/frontends/amiga/font_diskfont.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2016 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_FONT_DISKFONT_H
+#define AMIGA_FONT_DISKFONT_H
+void ami_font_diskfont_init(void);
+#endif
+
diff --git a/frontends/amiga/font_scan.c b/frontends/amiga/font_scan.c
new file mode 100644
index 000000000..b65798d6e
--- /dev/null
+++ b/frontends/amiga/font_scan.c
@@ -0,0 +1,522 @@
+/*
+ * Copyright 2012 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Font glyph scanner for Unicode substitutions.
+*/
+
+#include "amiga/os3support.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef __amigaos4__
+#include <proto/bullet.h>
+#endif
+#include <proto/diskfont.h>
+#include <proto/dos.h>
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <diskfont/diskfonttag.h>
+#include <diskfont/oterrors.h>
+
+#include <proto/window.h>
+#include <proto/layout.h>
+#include <proto/fuelgauge.h>
+#include <classes/window.h>
+#include <gadgets/fuelgauge.h>
+#include <gadgets/layout.h>
+
+#include <reaction/reaction_macros.h>
+
+#include "utils/nsoption.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "desktop/mouse.h"
+#include "desktop/gui_window.h"
+
+#include "amiga/font_scan.h"
+#include "amiga/gui.h"
+#include "amiga/libs.h"
+#include "amiga/object.h"
+#include "amiga/utf8.h"
+
+enum {
+ FS_OID_MAIN = 0,
+ FS_GID_MAIN,
+ FS_GID_FONTS,
+ FS_GID_GLYPHS,
+ FS_GID_LAST
+};
+
+struct ami_font_scan_window {
+ struct Window *win;
+ Object *objects[FS_GID_LAST];
+ char *title;
+ char *glyphtext;
+};
+
+/**
+ * Lookup a font that contains a UTF-16 codepoint
+ *
+ * \param code UTF-16 codepoint to lookup
+ * \param glypharray an array of 0xffff lwc_string pointers
+ * \return font name or NULL
+ */
+const char *ami_font_scan_lookup(const uint16 *code, lwc_string **glypharray)
+{
+ if(*code >= 0xd800 && *code <= 0xdbff) {
+ /* This is a multi-byte character, we don't support fallback for these yet. */
+ return NULL;
+ }
+
+ if(glypharray[*code] == NULL) return NULL;
+ else return lwc_string_data(glypharray[*code]);
+}
+
+/**
+ * Open GUI to show font scanning progress
+ *
+ * \param fonts number of fonts that are being scanned
+ * \return pointer to a struct ami_font_scan_window
+ */
+static struct ami_font_scan_window *ami_font_scan_gui_open(int32 fonts)
+{
+ struct ami_font_scan_window *fsw =
+ AllocVecTagList(sizeof(struct ami_font_scan_window), NULL);
+
+ if(fsw == NULL) return NULL;
+
+ fsw->title = ami_utf8_easy(messages_get("FontScanning"));
+ fsw->glyphtext = ami_utf8_easy(messages_get("FontGlyphs"));
+
+ fsw->objects[FS_OID_MAIN] = WindowObj,
+ WA_ScreenTitle, ami_gui_get_screen_title(),
+ WA_Title, fsw->title,
+ WA_Activate, TRUE,
+ WA_DepthGadget, TRUE,
+ WA_DragBar, TRUE,
+ WA_CloseGadget, FALSE,
+ WA_SizeGadget, TRUE,
+ WA_PubScreen, scrn,
+ WA_BusyPointer, TRUE,
+ WA_Width, 400,
+ WINDOW_UserData, fsw,
+ WINDOW_IconifyGadget, FALSE,
+ WINDOW_Position, WPOS_CENTERSCREEN,
+ WINDOW_LockHeight, TRUE,
+ WINDOW_ParentGroup, fsw->objects[FS_GID_MAIN] = LayoutVObj,
+ LAYOUT_AddChild, fsw->objects[FS_GID_FONTS] = FuelGaugeObj,
+ GA_ID, FS_GID_FONTS,
+ GA_Text, fsw->title,
+ FUELGAUGE_Min, 0,
+ FUELGAUGE_Max, fonts,
+ FUELGAUGE_Level, 0,
+ FUELGAUGE_Ticks, 11,
+ FUELGAUGE_ShortTicks, TRUE,
+ FUELGAUGE_Percent, FALSE,
+ FUELGAUGE_Justification, FGJ_CENTER,
+ FuelGaugeEnd,
+ CHILD_NominalSize, TRUE,
+ CHILD_WeightedHeight, 0,
+ LAYOUT_AddChild, fsw->objects[FS_GID_GLYPHS] = FuelGaugeObj,
+ GA_ID, FS_GID_GLYPHS,
+ //GA_Text, "Glyphs",
+ FUELGAUGE_Min, 0x0000,
+ FUELGAUGE_Max, 0xffff,
+ FUELGAUGE_Level, 0,
+ FUELGAUGE_Ticks,11,
+ FUELGAUGE_ShortTicks, TRUE,
+ FUELGAUGE_Percent, FALSE,
+ FUELGAUGE_Justification, FGJ_CENTER,
+ FuelGaugeEnd,
+ CHILD_NominalSize, TRUE,
+ CHILD_WeightedHeight, 0,
+ EndGroup,
+ EndWindow;
+
+ fsw->win = (struct Window *)RA_OpenWindow(fsw->objects[FS_OID_MAIN]);
+
+ return fsw;
+}
+
+/**
+ * Update GUI showing font scanning progress
+ *
+ * \param fsw pointer to a struct ami_font_scan_window
+ * \param font current font being scanned
+ * \param font_num font number being scanned
+ * \param glyphs number of unique glyphs found
+ */
+static void ami_font_scan_gui_update(struct ami_font_scan_window *fsw, const char *font,
+ ULONG font_num, ULONG glyphs)
+{
+ ULONG va[2];
+
+ if(fsw) {
+ RefreshSetGadgetAttrs((struct Gadget *)fsw->objects[FS_GID_FONTS],
+ fsw->win, NULL,
+ FUELGAUGE_Level, font_num,
+ GA_Text, font,
+ TAG_DONE);
+
+ va[0] = glyphs;
+ va[1] = 0;
+
+ RefreshSetGadgetAttrs((struct Gadget *)fsw->objects[FS_GID_GLYPHS],
+ fsw->win, NULL,
+ GA_Text, fsw->glyphtext,
+ FUELGAUGE_VarArgs, va,
+ FUELGAUGE_Level, glyphs,
+ TAG_DONE);
+ } else {
+ printf("Found %ld glyphs\n", glyphs);
+ printf("Scanning font #%ld (%s)...\n", font_num, font);
+ }
+}
+
+/**
+ * Close GUI showing font scanning progress
+ *
+ * \param fsw pointer to a struct ami_font_scan_window
+ */
+static void ami_font_scan_gui_close(struct ami_font_scan_window *fsw)
+{
+ if(fsw) {
+ DisposeObject(fsw->objects[FS_OID_MAIN]);
+ ami_utf8_free(fsw->title);
+ FreeVec(fsw);
+ }
+}
+
+/**
+ * Scan a font for glyphs not present in glypharray.
+ *
+ * \param fontname font to scan
+ * \param glypharray an array of 0xffff lwc_string pointers
+ * \return number of new glyphs found
+ */
+static ULONG ami_font_scan_font(const char *fontname, lwc_string **glypharray)
+{
+ struct OutlineFont *ofont;
+ struct MinList *widthlist = NULL;
+ struct GlyphWidthEntry *gwnode;
+ ULONG foundglyphs = 0;
+ lwc_error lerror;
+ ULONG unicoderanges = 0;
+
+ ofont = OpenOutlineFont(fontname, NULL, OFF_OPEN);
+
+ if(!ofont) return 0;
+
+#ifndef __amigaos4__
+ struct BulletBase *BulletBase = ofont->BulletBase;
+#endif
+
+ if(ESetInfo(AMI_OFONT_ENGINE,
+ OT_PointHeight, 10 * (1 << 16),
+ OT_GlyphCode, 0x0000,
+ OT_GlyphCode2, 0xffff,
+ TAG_END) == OTERR_Success)
+ {
+ if(EObtainInfo(AMI_OFONT_ENGINE,
+ OT_WidthList, &widthlist,
+ TAG_END) == 0)
+ {
+ gwnode = (struct GlyphWidthEntry *)GetHead((struct List *)widthlist);
+ do {
+ if(gwnode && (glypharray[gwnode->gwe_Code] == NULL)) {
+ lerror = lwc_intern_string(fontname, strlen(fontname), &glypharray[gwnode->gwe_Code]);
+ if(lerror != lwc_error_ok) continue;
+ foundglyphs++;
+ }
+ } while((gwnode = (struct GlyphWidthEntry *)GetSucc((struct Node *)gwnode)));
+ EReleaseInfo(AMI_OFONT_ENGINE,
+ OT_WidthList, widthlist,
+ TAG_END);
+ }
+ }
+#ifdef __amigaos4__
+ if(EObtainInfo(AMI_OFONT_ENGINE, OT_UnicodeRanges, &unicoderanges, TAG_END) == 0) {
+ if(unicoderanges & UCR_SURROGATES) {
+ LOG("%s supports UTF-16 surrogates", fontname);
+ if (nsoption_charp(font_surrogate) == NULL) {
+ nsoption_set_charp(font_surrogate, (char *)strdup(fontname));
+ }
+ }
+ EReleaseInfo(AMI_OFONT_ENGINE,
+ OT_UnicodeRanges, unicoderanges,
+ TAG_END);
+ }
+#endif
+ CloseOutlineFont(ofont, NULL);
+
+ return foundglyphs;
+}
+
+/**
+ * Scan all fonts for glyphs.
+ *
+ * \param list min list
+ * \param win scan window
+ * \param glypharray an array of 0xffff lwc_string pointers
+ * \return number of glyphs found
+ */
+static ULONG ami_font_scan_fonts(struct MinList *list,
+ struct ami_font_scan_window *win, lwc_string **glypharray)
+{
+ ULONG found, total = 0, font_num = 0;
+ struct nsObject *node;
+ struct nsObject *nnode;
+
+ if(IsMinListEmpty(list)) return 0;
+
+ node = (struct nsObject *)GetHead((struct List *)list);
+
+ do {
+ nnode = (struct nsObject *)GetSucc((struct Node *)node);
+ ami_font_scan_gui_update(win, node->dtz_Node.ln_Name, font_num, total);
+ LOG("Scanning %s", node->dtz_Node.ln_Name);
+ found = ami_font_scan_font(node->dtz_Node.ln_Name, glypharray);
+ total += found;
+ LOG("Found %ld new glyphs (total = %ld)", found, total);
+ font_num++;
+ } while((node = nnode));
+
+ return total;
+}
+
+/**
+ * Add OS fonts to a list.
+ *
+ * \param list list to add font names to
+ * \return number of fonts found
+ */
+static ULONG ami_font_scan_list(struct MinList *list)
+{
+ int afShortage, afSize = 100;
+ struct AvailFontsHeader *afh;
+ struct AvailFonts *af;
+ ULONG found = 0;
+ struct nsObject *node;
+
+ do {
+ if((afh = (struct AvailFontsHeader *)AllocVecTagList(afSize, NULL))) {
+ if(((afShortage = AvailFonts((STRPTR)afh, afSize,
+ AFF_DISK | AFF_OTAG | AFF_SCALED)))) {
+ FreeVec(afh);
+ afSize += afShortage;
+ }
+ } else {
+ /* out of memory, bail out */
+ return 0;
+ }
+ } while (afShortage);
+
+ if(afh) {
+ af = (struct AvailFonts *)&(afh[1]);
+
+ for(int i = 0; i < afh->afh_NumEntries; i++) {
+ if(af[i].af_Attr.ta_Style == FS_NORMAL) {
+ if(af[i].af_Attr.ta_Name != NULL) {
+ char *p = 0;
+ if((p = strrchr(af[i].af_Attr.ta_Name, '.'))) *p = '\0';
+ node = (struct nsObject *)FindIName((struct List *)list,
+ af[i].af_Attr.ta_Name);
+ if(node == NULL) {
+ node = AddObject(list, AMINS_UNKNOWN);
+ if(node) {
+ node->dtz_Node.ln_Name = strdup(af[i].af_Attr.ta_Name);
+ found++;
+ LOG("Added %s", af[i].af_Attr.ta_Name);
+ }
+ }
+ }
+ }
+ }
+ FreeVec(afh);
+ } else {
+ return 0;
+ }
+ return found;
+}
+
+/**
+ * Load a font glyph cache
+ *
+ * \param filename name of cache file to load
+ * \param glypharray an array of 0xffff lwc_string pointers
+ * \return number of glyphs loaded
+ */
+static ULONG ami_font_scan_load(const char *filename, lwc_string **glypharray)
+{
+ ULONG found = 0;
+ BPTR fh = 0;
+ lwc_error lerror;
+ char buffer[256];
+ struct RDArgs *rargs = NULL;
+ CONST_STRPTR template = "CODE/A,FONT/A";
+ long rarray[] = {0,0};
+
+ enum {
+ A_CODE = 0,
+ A_FONT
+ };
+
+ rargs = AllocDosObjectTags(DOS_RDARGS, TAG_DONE);
+
+ if((fh = FOpen(filename, MODE_OLDFILE, 0))) {
+ LOG("Loading font glyph cache from %s", filename);
+
+ while(FGets(fh, (STRPTR)&buffer, 256) != 0)
+ {
+ rargs->RDA_Source.CS_Buffer = (char *)&buffer;
+ rargs->RDA_Source.CS_Length = 256;
+ rargs->RDA_Source.CS_CurChr = 0;
+
+ rargs->RDA_DAList = NULL;
+ rargs->RDA_Buffer = NULL;
+ rargs->RDA_BufSiz = 0;
+ rargs->RDA_ExtHelp = NULL;
+ rargs->RDA_Flags = 0;
+
+ if(ReadArgs(template, rarray, rargs))
+ {
+ lerror = lwc_intern_string((const char *)rarray[A_FONT],
+ strlen((const char *)rarray[A_FONT]),
+ &glypharray[strtoul((const char *)rarray[A_CODE], NULL, 0)]);
+ if(lerror != lwc_error_ok) continue;
+ found++;
+ }
+ }
+ FClose(fh);
+ }
+
+ return found;
+}
+
+/**
+ * Save a font glyph cache
+ *
+ * \param filename name of cache file to save
+ * \param glypharray an array of 0xffff lwc_string pointers
+ */
+void ami_font_scan_save(const char *filename, lwc_string **glypharray)
+{
+ ULONG i;
+ BPTR fh = 0;
+
+ if((fh = FOpen(filename, MODE_NEWFILE, 0))) {
+ LOG("Writing font glyph cache to %s", filename);
+ FPrintf(fh, "; This file is auto-generated. To re-create the cache, delete this file.\n");
+ FPrintf(fh, "; This file is parsed using ReadArgs() with the following template:\n");
+ FPrintf(fh, "; CODE/A,FONT/A\n;\n");
+
+ for(i=0x0000; i<=0xffff; i++)
+ {
+ if(glypharray[i]) {
+ FPrintf(fh, "0x%04lx \"%s\"\n", i, lwc_string_data(glypharray[i]));
+ }
+ }
+ FClose(fh);
+ }
+}
+
+/**
+ * Finalise the font glyph cache.
+ *
+ * \param glypharray an array of 0xffff lwc_string pointers to free
+ */
+void ami_font_scan_fini(lwc_string **glypharray)
+{
+ ULONG i;
+
+ for(i=0x0000; i<=0xffff; i++)
+ {
+ if(glypharray[i]) {
+ lwc_string_unref(glypharray[i]);
+ glypharray[i] = NULL;
+ }
+ }
+}
+
+/**
+ * Initialise the font glyph cache.
+ * Reads an existing file or, if not present, generates a new cache.
+ *
+ * \param filename cache file to attempt to read
+ * \param force_scan force re-creation of cache
+ * \param save save the cache
+ * \param glypharray an array of 0xffff lwc_string pointers
+ */
+void ami_font_scan_init(const char *filename, bool force_scan, bool save,
+ lwc_string **glypharray)
+{
+ ULONG i, found = 0, entries = 0;
+ struct MinList *list;
+ struct nsObject *node;
+ char *csv;
+ struct ami_font_scan_window *win = NULL;
+
+ /* Ensure array zeroed */
+ for(i=0x0000; i<=0xffff; i++)
+ glypharray[i] = NULL;
+
+ if(force_scan == false)
+ found = ami_font_scan_load(filename, glypharray);
+
+ if(found == 0) {
+ LOG("Creating new font glyph cache");
+ if((list = NewObjList())) {
+
+ /* add preferred fonts list */
+ if(nsoption_charp(font_unicode) &&
+ (csv = strdup(nsoption_charp(font_unicode))))
+ {
+ char *p;
+
+ while((p = strsep(&csv, ","))) {
+ if(p != NULL) {
+ node = AddObject(list, AMINS_UNKNOWN);
+ if(node) node->dtz_Node.ln_Name = strdup(p);
+ entries++;
+ }
+ }
+ free(csv);
+ }
+
+ if(nsoption_bool(font_unicode_only) == false)
+ entries += ami_font_scan_list(list);
+
+ LOG("Found %ld fonts", entries);
+
+ win = ami_font_scan_gui_open(entries);
+ found = ami_font_scan_fonts(list, win, glypharray);
+ ami_font_scan_gui_close(win);
+
+ FreeObjList(list);
+
+ if(save == true)
+ ami_font_scan_save(filename, glypharray);
+ }
+ }
+
+ LOG("Initialised with %ld glyphs", found);
+}
+
diff --git a/frontends/amiga/font_scan.h b/frontends/amiga/font_scan.h
new file mode 100755
index 000000000..7d61e2daa
--- /dev/null
+++ b/frontends/amiga/font_scan.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2012 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_FONT_SCAN_H
+#define AMIGA_FONT_SCAN_H
+#include "amiga/os3support.h"
+#include <libwapcaplet/libwapcaplet.h>
+
+/* Compatibliity define used by font.c and font_scan.c
+ * It's here because this file is included by both. */
+#ifdef __amigaos4__
+#define AMI_OFONT_ENGINE &ofont->olf_EEngine
+#else
+#define AMI_OFONT_ENGINE ofont->GEngine
+#endif
+
+void ami_font_scan_init(const char *filename, bool force_scan, bool save,
+ lwc_string **glypharray);
+void ami_font_scan_fini(lwc_string **glypharray);
+void ami_font_scan_save(const char *filename, lwc_string **glypharray);
+const char *ami_font_scan_lookup(const uint16 *code, lwc_string **glypharray);
+
+#endif
+
diff --git a/frontends/amiga/gui.c b/frontends/amiga/gui.c
new file mode 100644
index 000000000..c6c963312
--- /dev/null
+++ b/frontends/amiga/gui.c
@@ -0,0 +1,5740 @@
+/*
+ * Copyright 2008-2015 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+
+
+/* Custom StringView class */
+#include "amiga/stringview/stringview.h"
+#include "amiga/stringview/urlhistory.h"
+
+/* AmigaOS libraries */
+#ifdef __amigaos4__
+#include <proto/application.h>
+#endif
+#include <proto/asl.h>
+#include <proto/datatypes.h>
+#include <proto/dos.h>
+#include <proto/exec.h>
+#include <proto/graphics.h>
+#include <proto/icon.h>
+#include <proto/intuition.h>
+#include <proto/keymap.h>
+#include <proto/locale.h>
+#include <proto/utility.h>
+#include <proto/wb.h>
+
+/* Other OS includes */
+#include <datatypes/textclass.h>
+#include <devices/inputevent.h>
+#include <graphics/gfxbase.h>
+#include <graphics/rpattr.h>
+#ifdef __amigaos4__
+#include <graphics/blitattr.h>
+#include <intuition/gui.h>
+#include <libraries/application.h>
+#include <libraries/keymap.h>
+#endif
+#include <intuition/icclass.h>
+#include <intuition/screens.h>
+#include <libraries/gadtools.h>
+#include <workbench/workbench.h>
+
+/* ReAction libraries */
+#include <proto/bevel.h>
+#include <proto/bitmap.h>
+#include <proto/button.h>
+#include <proto/chooser.h>
+#include <proto/clicktab.h>
+#include <proto/label.h>
+#include <proto/layout.h>
+#include <proto/scroller.h>
+#include <proto/space.h>
+#include <proto/speedbar.h>
+#include <proto/string.h>
+#include <proto/window.h>
+
+#include <classes/window.h>
+#include <gadgets/button.h>
+#include <gadgets/chooser.h>
+#include <gadgets/clicktab.h>
+#include <gadgets/layout.h>
+#include <gadgets/scroller.h>
+#include <gadgets/space.h>
+#include <gadgets/speedbar.h>
+#include <gadgets/string.h>
+#include <images/bevel.h>
+#include <images/bitmap.h>
+#include <images/label.h>
+
+#include <reaction/reaction_macros.h>
+
+/* newlib includes */
+#include <math.h>
+#include <string.h>
+
+/* NetSurf core includes */
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/nsoption.h"
+#include "utils/utf8.h"
+#include "utils/utils.h"
+#include "utils/nsurl.h"
+#include "utils/file.h"
+#include "content/hlcache.h"
+#include "content/backing_store.h"
+#include "content/fetchers.h"
+#include "content/fetchers/resource.h"
+#include "content/urldb.h"
+#include "image/ico.h"
+#include "desktop/browser_history.h"
+#include "desktop/browser.h"
+#include "desktop/hotlist.h"
+#include "desktop/mouse.h"
+#include "desktop/netsurf.h"
+#include "desktop/version.h"
+#include "desktop/save_complete.h"
+#include "desktop/scrollbar.h"
+#include "desktop/searchweb.h"
+#include "desktop/textinput.h"
+#include "desktop/tree.h"
+#include "desktop/gui_window.h"
+#include "desktop/gui_fetch.h"
+#include "desktop/gui_misc.h"
+
+/* NetSurf Amiga platform includes */
+#include "amiga/gui.h"
+#include "amiga/arexx.h"
+#include "amiga/bitmap.h"
+#include "amiga/clipboard.h"
+#include "amiga/cookies.h"
+#include "amiga/ctxmenu.h"
+#include "amiga/datatypes.h"
+#include "amiga/download.h"
+#include "amiga/drag.h"
+#include "amiga/file.h"
+#include "amiga/filetype.h"
+#include "amiga/font.h"
+#include "amiga/gui_options.h"
+#include "amiga/help.h"
+#include "amiga/history.h"
+#include "amiga/history_local.h"
+#include "amiga/hotlist.h"
+#include "amiga/icon.h"
+#include "amiga/launch.h"
+#include "amiga/libs.h"
+#include "amiga/login.h"
+#include "amiga/menu.h"
+#include "amiga/misc.h"
+#include "amiga/plotters.h"
+#include "amiga/plugin_hack.h"
+#include "amiga/print.h"
+#include "amiga/schedule.h"
+#include "amiga/search.h"
+#include "amiga/selectmenu.h"
+#include "amiga/theme.h"
+#include "amiga/tree.h"
+#include "amiga/utf8.h"
+#include "amiga/sslcert.h"
+
+#define AMINS_SCROLLERPEN NUMDRIPENS
+#define NSA_KBD_SCROLL_PX 10
+#define NSA_MAX_HOTLIST_BUTTON_LEN 20
+
+/* Extra mouse button defines to match those in intuition/intuition.h */
+#define SIDEDOWN (IECODE_4TH_BUTTON)
+#define SIDEUP (IECODE_4TH_BUTTON | IECODE_UP_PREFIX)
+#define EXTRADOWN (IECODE_5TH_BUTTON)
+#define EXTRAUP (IECODE_5TH_BUTTON | IECODE_UP_PREFIX)
+
+#ifdef __amigaos4__
+#define NSA_STATUS_TEXT GA_Text
+#else
+#define NSA_STATUS_TEXT STRINGA_TextVal
+#endif
+
+#ifdef __amigaos4__
+#define BOOL_MISMATCH(a,b) ((a == FALSE) && (b != FALSE)) || ((a != FALSE) && (b == FALSE))
+#else
+#define BOOL_MISMATCH(a,b) (1)
+#endif
+
+static bool ami_quit = false;
+
+extern struct gui_utf8_table *amiga_utf8_table;
+
+struct ami_gui_tb_userdata {
+ struct List *sblist;
+ struct gui_window_2 *gw;
+ int items;
+};
+
+static struct MsgPort *schedulermsgport = NULL;
+static struct MsgPort *appport;
+static Class *urlStringClass;
+
+static BOOL locked_screen = FALSE;
+static int screen_signal = -1;
+static bool win_destroyed;
+static STRPTR nsscreentitle;
+
+static struct MsgPort *applibport = NULL;
+static ULONG applibsig = 0;
+static uint32 ami_appid = 0;
+static struct Hook newprefs_hook;
+
+static STRPTR temp_homepage_url = NULL;
+static bool cli_force = false;
+
+#define USERS_DIR "PROGDIR:Users"
+static char *users_dir = NULL;
+static char *current_user = NULL;
+static char *current_user_dir;
+static char *current_user_faviconcache;
+
+static const __attribute__((used)) char *stack_cookie = "\0$STACK:196608\0";
+
+const char * const versvn;
+const char * const verdate;
+
+void ami_switch_tab(struct gui_window_2 *gwin,bool redraw);
+void ami_change_tab(struct gui_window_2 *gwin, int direction);
+void ami_get_hscroll_pos(struct gui_window_2 *gwin, ULONG *xs);
+void ami_get_vscroll_pos(struct gui_window_2 *gwin, ULONG *ys);
+void ami_quit_netsurf_delayed(void);
+Object *ami_gui_splash_open(void);
+void ami_gui_splash_close(Object *win_obj);
+HOOKF(uint32, ami_set_favicon_render_hook, APTR, space, struct gpRender *);
+HOOKF(uint32, ami_set_throbber_render_hook, APTR, space, struct gpRender *);
+bool ami_gui_map_filename(char **remapped, const char *path, const char *file,
+ const char *map);
+static void ami_gui_window_update_box_deferred(struct gui_window *g, bool draw);
+static void ami_do_redraw(struct gui_window_2 *g);
+static void ami_schedule_redraw_remove(struct gui_window_2 *gwin);
+
+static bool gui_window_get_scroll(struct gui_window *g, int *sx, int *sy);
+static void gui_window_set_scroll(struct gui_window *g, int sx, int sy);
+static void gui_window_remove_caret(struct gui_window *g);
+static void gui_window_place_caret(struct gui_window *g, int x, int y, int height, const struct rect *clip);
+
+
+
+/* accessors for default options - user option is updated if it is set as per default */
+#define nsoption_default_set_int(OPTION, VALUE) \
+ if (nsoptions_default[NSOPTION_##OPTION].value.i == nsoptions[NSOPTION_##OPTION].value.i) \
+ nsoptions[NSOPTION_##OPTION].value.i = VALUE; \
+ nsoptions_default[NSOPTION_##OPTION].value.i = VALUE
+
+
+
+STRPTR ami_locale_langs(void)
+{
+ struct Locale *locale;
+ STRPTR acceptlangs = NULL;
+ char *remapped;
+
+ if((locale = OpenLocale(NULL)))
+ {
+ for(int i = 0; i < 10; i++)
+ {
+ if(locale->loc_PrefLanguages[i])
+ {
+ if(ami_gui_map_filename(&remapped, "PROGDIR:Resources",
+ locale->loc_PrefLanguages[i], "LangNames"))
+ {
+ if(acceptlangs)
+ {
+ STRPTR acceptlangs2 = acceptlangs;
+ acceptlangs = ASPrintf("%s, %s",acceptlangs2, remapped);
+ FreeVec(acceptlangs2);
+ acceptlangs2 = NULL;
+ }
+ else
+ {
+ acceptlangs = ASPrintf("%s", remapped);
+ }
+ }
+ }
+ else
+ {
+ continue;
+ }
+ }
+ CloseLocale(locale);
+ }
+ return acceptlangs;
+}
+
+bool ami_gui_map_filename(char **remapped, const char *path, const char *file, const char *map)
+{
+ BPTR fh = 0;
+ char *mapfile = NULL;
+ size_t mapfile_size = 0;
+ char buffer[1024];
+ char *realfname;
+ bool found = false;
+
+ netsurf_mkpath(&mapfile, &mapfile_size, 2, path, map);
+
+ if(mapfile == NULL) return false;
+
+ fh = FOpen(mapfile, MODE_OLDFILE, 0);
+ if(fh)
+ {
+ while(FGets(fh, buffer, 1024) != 0)
+ {
+ if((buffer[0] == '#') ||
+ (buffer[0] == '\n') ||
+ (buffer[0] == '\0')) continue;
+
+ realfname = strchr(buffer, ':');
+ if(realfname)
+ {
+ if(strncmp(buffer, file, strlen(file)) == 0)
+ {
+ if(realfname[strlen(realfname)-1] == '\n')
+ realfname[strlen(realfname)-1] = '\0';
+ *remapped = strdup(realfname + 1);
+ found = true;
+ break;
+ }
+ }
+ }
+ FClose(fh);
+ }
+
+ if(found == false) *remapped = strdup(file);
+ else LOG("Remapped %s to %s in path %s using %s", file, *remapped, path, map);
+
+ free(mapfile);
+
+ return found;
+}
+
+static bool ami_gui_check_resource(char *fullpath, const char *file)
+{
+ bool found = false;
+ char *remapped;
+ BPTR lock = 0;
+ size_t fullpath_len = 1024;
+
+ ami_gui_map_filename(&remapped, fullpath, file, "Resource.map");
+ netsurf_mkpath(&fullpath, &fullpath_len, 2, fullpath, remapped);
+
+ lock = Lock(fullpath, ACCESS_READ);
+ if(lock)
+ {
+ UnLock(lock);
+ found = true;
+ }
+
+ if(found) LOG("Found %s", fullpath);
+ free(remapped);
+
+ return found;
+}
+
+bool ami_locate_resource(char *fullpath, const char *file)
+{
+ struct Locale *locale;
+ int i;
+ bool found = false;
+ char *remapped;
+ size_t fullpath_len = 1024;
+
+ /* Check NetSurf user data area first */
+
+ if(current_user_dir != NULL) {
+ strcpy(fullpath, current_user_dir);
+ found = ami_gui_check_resource(fullpath, file);
+ if(found) return true;
+ }
+
+ /* Check current theme directory */
+ if(nsoption_charp(theme)) {
+ strcpy(fullpath, nsoption_charp(theme));
+ found = ami_gui_check_resource(fullpath, file);
+ if(found) return true;
+ }
+
+ /* If not found, start on the user's preferred languages */
+
+ locale = OpenLocale(NULL);
+
+ for(i=0;i<10;i++) {
+ strcpy(fullpath,"PROGDIR:Resources/");
+
+ if(locale->loc_PrefLanguages[i]) {
+ ami_gui_map_filename(&remapped, "PROGDIR:Resources",
+ locale->loc_PrefLanguages[i], "LangNames");
+ netsurf_mkpath(&fullpath, &fullpath_len, 2, fullpath, remapped);
+
+ found = ami_gui_check_resource(fullpath, file);
+ } else {
+ continue;
+ }
+
+ if(found) break;
+ }
+
+ if(!found) {
+ /* If not found yet, check in PROGDIR:Resources/en,
+ * might not be in user's preferred languages */
+
+ strcpy(fullpath, "PROGDIR:Resources/en/");
+ found = ami_gui_check_resource(fullpath, file);
+ }
+
+ CloseLocale(locale);
+
+ if(!found) {
+ /* Lastly check directly in PROGDIR:Resources */
+
+ strcpy(fullpath, "PROGDIR:Resources/");
+ found = ami_gui_check_resource(fullpath, file);
+ }
+
+ return found;
+}
+
+static bool ami_open_resources(void)
+{
+ urlStringClass = MakeStringClass();
+
+ if(!(appport = AllocSysObjectTags(ASOT_PORT,
+ ASO_NoTrack, FALSE,
+ TAG_DONE))) return false;
+
+ if(!(sport = AllocSysObjectTags(ASOT_PORT,
+ ASO_NoTrack, FALSE,
+ TAG_DONE))) return false;
+
+ if(!(schedulermsgport = AllocSysObjectTags(ASOT_PORT,
+ ASO_NoTrack, FALSE,
+ TAG_DONE))) return false;
+
+ return true;
+}
+
+static UWORD ami_system_colour_scrollbar_fgpen(struct DrawInfo *drinfo)
+{
+ LONG scrollerfillpen = FALSE;
+#ifdef __amigaos4__
+ GetGUIAttrs(NULL, drinfo, GUIA_PropKnobColor, &scrollerfillpen, TAG_DONE);
+
+ if(scrollerfillpen) return FILLPEN;
+ else return FOREGROUNDPEN;
+#else
+ return FILLPEN;
+#endif
+
+}
+
+/**
+ * set option from pen
+ */
+static nserror
+colour_option_from_pen(UWORD pen,
+ enum nsoption_e option,
+ struct Screen *screen,
+ colour def_colour)
+{
+ ULONG colr[3];
+ struct DrawInfo *drinfo;
+
+ if((option < NSOPTION_SYS_COLOUR_START) ||
+ (option > NSOPTION_SYS_COLOUR_END) ||
+ (nsoptions[option].type != OPTION_COLOUR)) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ if(screen != NULL) {
+ drinfo = GetScreenDrawInfo(screen);
+ if(drinfo != NULL) {
+
+ if(pen == AMINS_SCROLLERPEN) pen = ami_system_colour_scrollbar_fgpen(drinfo);
+
+ /* Get the colour of the pen being used for "pen" */
+ GetRGB32(screen->ViewPort.ColorMap, drinfo->dri_Pens[pen], 1, (ULONG *)&colr);
+
+ /* convert it to a color */
+ def_colour = ((colr[0] & 0xff000000) >> 24) |
+ ((colr[1] & 0xff000000) >> 16) |
+ ((colr[2] & 0xff000000) >> 8);
+
+ FreeScreenDrawInfo(screen, drinfo);
+ }
+ }
+
+ if (nsoptions_default[option].value.c == nsoptions[option].value.c)
+ nsoptions[option].value.c = def_colour;
+ nsoptions_default[option].value.c = def_colour;
+
+ return NSERROR_OK;
+}
+
+/* exported interface documented in amiga/gui.h */
+STRPTR ami_gui_get_screen_title(void)
+{
+ if(nsscreentitle == NULL) {
+ nsscreentitle = ASPrintf("NetSurf %s", netsurf_version);
+ /* If this fails it will be NULL, which means we'll get the screen's
+ * default titlebar text instead - so no need to check for error. */
+ }
+
+ return nsscreentitle;
+}
+
+static void ami_set_screen_defaults(struct Screen *screen)
+{
+ nsoption_default_set_int(window_x, 0);
+ nsoption_default_set_int(window_y, screen->BarHeight + 1);
+ nsoption_default_set_int(window_width, screen->Width);
+ nsoption_default_set_int(window_height, screen->Height - screen->BarHeight - 1);
+
+#ifdef __amigaos4__
+ nsoption_default_set_int(redraw_tile_size_x, screen->Width);
+ nsoption_default_set_int(redraw_tile_size_y, screen->Height);
+
+ /* set system colours for amiga ui */
+ colour_option_from_pen(FILLPEN, NSOPTION_sys_colour_ActiveBorder, screen, 0x00000000);
+ colour_option_from_pen(FILLPEN, NSOPTION_sys_colour_ActiveCaption, screen, 0x00dddddd);
+ colour_option_from_pen(BACKGROUNDPEN, NSOPTION_sys_colour_AppWorkspace, screen, 0x00eeeeee);
+ colour_option_from_pen(BACKGROUNDPEN, NSOPTION_sys_colour_Background, screen, 0x00aa0000);
+ colour_option_from_pen(FOREGROUNDPEN, NSOPTION_sys_colour_ButtonFace, screen, 0x00aaaaaa);
+ colour_option_from_pen(FORESHINEPEN, NSOPTION_sys_colour_ButtonHighlight, screen, 0x00cccccc);
+ colour_option_from_pen(FORESHADOWPEN, NSOPTION_sys_colour_ButtonShadow, screen, 0x00bbbbbb);
+ colour_option_from_pen(TEXTPEN, NSOPTION_sys_colour_ButtonText, screen, 0x00000000);
+ colour_option_from_pen(FILLTEXTPEN, NSOPTION_sys_colour_CaptionText, screen, 0x00000000);
+ colour_option_from_pen(DISABLEDTEXTPEN, NSOPTION_sys_colour_GrayText, screen, 0x00777777);
+ colour_option_from_pen(SELECTPEN, NSOPTION_sys_colour_Highlight, screen, 0x00ee0000);
+ colour_option_from_pen(SELECTTEXTPEN, NSOPTION_sys_colour_HighlightText, screen, 0x00000000);
+ colour_option_from_pen(INACTIVEFILLPEN, NSOPTION_sys_colour_InactiveBorder, screen, 0x00000000);
+ colour_option_from_pen(INACTIVEFILLPEN, NSOPTION_sys_colour_InactiveCaption, screen, 0x00ffffff);
+ colour_option_from_pen(INACTIVEFILLTEXTPEN, NSOPTION_sys_colour_InactiveCaptionText, screen, 0x00cccccc);
+ colour_option_from_pen(BACKGROUNDPEN, NSOPTION_sys_colour_InfoBackground, screen, 0x00aaaaaa);/* This is wrong, HelpHint backgrounds are pale yellow but doesn't seem to be a DrawInfo pen defined for it. */
+ colour_option_from_pen(TEXTPEN, NSOPTION_sys_colour_InfoText, screen, 0x00000000);
+ colour_option_from_pen(MENUBACKGROUNDPEN, NSOPTION_sys_colour_Menu, screen, 0x00aaaaaa);
+ colour_option_from_pen(MENUTEXTPEN, NSOPTION_sys_colour_MenuText, screen, 0x00000000);
+ colour_option_from_pen(AMINS_SCROLLERPEN, NSOPTION_sys_colour_Scrollbar, screen, 0x00aaaaaa);
+ colour_option_from_pen(FORESHADOWPEN, NSOPTION_sys_colour_ThreeDDarkShadow, screen, 0x00555555);
+ colour_option_from_pen(FOREGROUNDPEN, NSOPTION_sys_colour_ThreeDFace, screen, 0x00dddddd);
+ colour_option_from_pen(FORESHINEPEN, NSOPTION_sys_colour_ThreeDHighlight, screen, 0x00aaaaaa);
+ colour_option_from_pen(HALFSHINEPEN, NSOPTION_sys_colour_ThreeDLightShadow, screen, 0x00999999);
+ colour_option_from_pen(HALFSHADOWPEN, NSOPTION_sys_colour_ThreeDShadow, screen, 0x00777777);
+ colour_option_from_pen(BACKGROUNDPEN, NSOPTION_sys_colour_Window, screen, 0x00aaaaaa);
+ colour_option_from_pen(INACTIVEFILLPEN, NSOPTION_sys_colour_WindowFrame, screen, 0x00000000);
+ colour_option_from_pen(TEXTPEN, NSOPTION_sys_colour_WindowText, screen, 0x00000000);
+#else
+ nsoption_default_set_int(redraw_tile_size_x, 100);
+ nsoption_default_set_int(redraw_tile_size_y, 100);
+#endif
+}
+
+
+/**
+ * Set option defaults for amiga frontend
+ *
+ * @param defaults The option table to update.
+ * @return error status.
+ */
+static nserror ami_set_options(struct nsoption_s *defaults)
+{
+ STRPTR tempacceptlangs;
+ char temp[1024];
+
+ /* The following line disables the popupmenu.class select menu.
+ ** It's not recommended to use it!
+ */
+ nsoption_set_bool(core_select_menu, true);
+
+ /* ClickTab < 53 doesn't work with the auto show/hide tab-bar (for reasons forgotten) */
+ if(ClickTabBase->lib_Version < 53)
+ nsoption_set_bool(tab_always_show, true);
+
+ /* Some AmigaOS3 overrides */
+#ifndef __amigaos4__
+ nsoption_set_bool(download_notify, false);
+ nsoption_set_bool(font_antialiasing, false);
+ nsoption_set_bool(truecolour_mouse_pointers, false);
+ nsoption_set_bool(use_openurl_lib, true);
+ nsoption_set_bool(bitmap_fonts, true);
+#endif
+
+ if((!nsoption_charp(accept_language)) ||
+ (nsoption_charp(accept_language)[0] == '\0') ||
+ (nsoption_bool(accept_lang_locale) == true))
+ {
+ if((tempacceptlangs = ami_locale_langs()))
+ {
+ nsoption_set_charp(accept_language,
+ (char *)strdup(tempacceptlangs));
+ FreeVec(tempacceptlangs);
+ }
+ }
+
+ sprintf(temp, "%s/Cookies", current_user_dir);
+ nsoption_setnull_charp(cookie_file,
+ (char *)strdup(temp));
+
+ sprintf(temp, "%s/Hotlist", current_user_dir);
+ nsoption_setnull_charp(hotlist_file,
+ (char *)strdup(temp));
+
+ sprintf(temp, "%s/URLdb", current_user_dir);
+ nsoption_setnull_charp(url_file,
+ (char *)strdup(temp));
+
+ sprintf(temp, "%s/FontGlyphCache", current_user_dir);
+ nsoption_setnull_charp(font_unicode_file,
+ (char *)strdup(temp));
+
+ nsoption_setnull_charp(ca_bundle,
+ (char *)strdup("PROGDIR:Resources/ca-bundle"));
+
+ /* font defaults */
+#ifdef __amigaos4__
+ nsoption_setnull_charp(font_sans, (char *)strdup("DejaVu Sans"));
+ nsoption_setnull_charp(font_serif, (char *)strdup("DejaVu Serif"));
+ nsoption_setnull_charp(font_mono, (char *)strdup("DejaVu Sans Mono"));
+ nsoption_setnull_charp(font_cursive, (char *)strdup("DejaVu Sans"));
+ nsoption_setnull_charp(font_fantasy, (char *)strdup("DejaVu Serif"));
+#else
+ nsoption_setnull_charp(font_sans, (char *)strdup("helvetica"));
+ nsoption_setnull_charp(font_serif, (char *)strdup("times"));
+ nsoption_setnull_charp(font_mono, (char *)strdup("topaz"));
+ nsoption_setnull_charp(font_cursive, (char *)strdup("garnet"));
+ nsoption_setnull_charp(font_fantasy, (char *)strdup("emerald"));
+/* Default CG fonts for OS3 - these work with use_diskfont both on and off,
+ however they are slow in both cases. The bitmap fonts don't work when
+ use_diskfont is off. The bitmap fonts performance on 68k is far superior,
+ so default to those for now whilst testing.
+ \todo maybe add some buttons to the prefs GUI to toggle?
+ nsoption_setnull_charp(font_sans, (char *)strdup("CGTriumvirate"));
+ nsoption_setnull_charp(font_serif, (char *)strdup("CGTimes"));
+ nsoption_setnull_charp(font_mono, (char *)strdup("LetterGothic"));
+ nsoption_setnull_charp(font_cursive, (char *)strdup("CGTriumvirate"));
+ nsoption_setnull_charp(font_fantasy, (char *)strdup("CGTimes"));
+*/
+#endif
+
+ if (nsoption_charp(font_unicode) == NULL)
+ {
+ BPTR lock = 0;
+ /* Search for some likely candidates */
+
+ if((lock = Lock("FONTS:Code2000.otag", ACCESS_READ)))
+ {
+ UnLock(lock);
+ nsoption_set_charp(font_unicode,
+ (char *)strdup("Code2000"));
+ }
+ else if((lock = Lock("FONTS:Bitstream Cyberbit.otag", ACCESS_READ)))
+ {
+ UnLock(lock);
+ nsoption_set_charp(font_unicode,
+ (char *)strdup("Bitstream Cyberbit"));
+ }
+ }
+
+ if (nsoption_charp(font_surrogate) == NULL) {
+ BPTR lock = 0;
+ /* Search for some likely candidates -
+ * Ideally we should pick a font during the scan process which announces it
+ * contains UCR_SURROGATES, but nothing appears to have the tag.
+ */
+ if((lock = Lock("FONTS:Symbola.otag", ACCESS_READ))) {
+ UnLock(lock);
+ nsoption_set_charp(font_surrogate,
+ (char *)strdup("Symbola"));
+ }
+ }
+
+ return NSERROR_OK;
+}
+
+static void ami_amiupdate(void)
+{
+ /* Create AppPath location for AmiUpdate use */
+
+ BPTR lock = 0;
+
+ if(((lock = Lock("ENVARC:AppPaths",SHARED_LOCK)) == 0))
+ {
+ lock = CreateDir("ENVARC:AppPaths");
+ }
+
+ UnLock(lock);
+
+ if((lock = Lock("PROGDIR:", ACCESS_READ)))
+ {
+ char filename[1024];
+ BPTR amiupdatefh;
+
+ DevNameFromLock(lock, (STRPTR)&filename, 1024L, DN_FULLPATH);
+
+ if((amiupdatefh = FOpen("ENVARC:AppPaths/NetSurf", MODE_NEWFILE, 0))) {
+ FPuts(amiupdatefh, (CONST_STRPTR)&filename);
+ FClose(amiupdatefh);
+ }
+
+ UnLock(lock);
+ }
+}
+
+static nsurl *gui_get_resource_url(const char *path)
+{
+ char buf[1024];
+ char path2[1024];
+ nsurl *url = NULL;
+
+ if(ami_locate_resource(buf, path) == false)
+ {
+ if((strncmp(path + strlen(path) - SLEN(".htm"), ".htm", SLEN(".htm")) == 0) ||
+ (strncmp(path + strlen(path) - SLEN(".html"), ".html", SLEN(".html")) == 0))
+ {
+ /* Try with RISC OS HTML filetype, might work */
+ strcpy(path2, path);
+ strcat(path2, ",faf");
+
+ if(ami_locate_resource(buf, path2) == false)
+ {
+ return NULL;
+ }
+ }
+ else return NULL;
+ }
+
+ netsurf_path_to_nsurl(buf, &url);
+
+ return url;
+}
+
+HOOKF(void, ami_gui_newprefs_hook, APTR, window, APTR)
+{
+ ami_set_screen_defaults(scrn);
+}
+
+static void ami_openscreen(void)
+{
+ ULONG id = 0;
+ ULONG compositing;
+
+ if (nsoption_int(screen_compositing) == -1)
+ compositing = ~0UL;
+ else compositing = nsoption_int(screen_compositing);
+
+ if (nsoption_charp(pubscreen_name) == NULL)
+ {
+ if((nsoption_charp(screen_modeid)) &&
+ (strncmp(nsoption_charp(screen_modeid), "0x", 2) == 0))
+ {
+ id = strtoul(nsoption_charp(screen_modeid), NULL, 0);
+ }
+ else
+ {
+ struct ScreenModeRequester *screenmodereq = NULL;
+
+ if((screenmodereq = AllocAslRequest(ASL_ScreenModeRequest,NULL))) {
+ if(AslRequestTags(screenmodereq,
+ ASLSM_MinDepth, 0,
+ ASLSM_MaxDepth, 32,
+ TAG_DONE))
+ {
+ char *modeid = malloc(20);
+ id = screenmodereq->sm_DisplayID;
+ sprintf(modeid, "0x%lx", id);
+ nsoption_set_charp(screen_modeid, modeid);
+ nsoption_write(current_user_options, NULL, NULL);
+ }
+ FreeAslRequest(screenmodereq);
+ }
+ }
+
+ if(screen_signal == -1) screen_signal = AllocSignal(-1);
+ LOG("Screen signal %d", screen_signal);
+ scrn = OpenScreenTags(NULL,
+ /**\todo specify screen depth */
+ SA_DisplayID, id,
+ SA_Title, ami_gui_get_screen_title(),
+ SA_Type, PUBLICSCREEN,
+ SA_PubName, "NetSurf",
+ SA_PubSig, screen_signal,
+ SA_PubTask, FindTask(0),
+ SA_LikeWorkbench, TRUE,
+ SA_Compositing, compositing,
+ TAG_DONE);
+
+ if(scrn)
+ {
+ PubScreenStatus(scrn,0);
+ }
+ else
+ {
+ FreeSignal(screen_signal);
+ screen_signal = -1;
+
+ if((scrn = LockPubScreen("NetSurf")))
+ {
+ locked_screen = TRUE;
+ }
+ else
+ {
+ nsoption_set_charp(pubscreen_name,
+ strdup("Workbench"));
+ }
+ }
+ }
+
+ if (nsoption_charp(pubscreen_name) != NULL)
+ {
+ scrn = LockPubScreen(nsoption_charp(pubscreen_name));
+
+ if(scrn == NULL)
+ {
+ scrn = LockPubScreen("Workbench");
+ }
+ locked_screen = TRUE;
+ }
+
+ ami_font_setdevicedpi(id);
+ ami_set_screen_defaults(scrn);
+ ami_help_new_screen(scrn);
+}
+
+static void ami_openscreenfirst(void)
+{
+ ami_openscreen();
+ if(!browserglob.bm) ami_init_layers(&browserglob, 0, 0, false);
+ ami_theme_throbber_setup();
+}
+
+static struct RDArgs *ami_gui_commandline(int *argc, char **argv, int *nargc, char **nargv)
+{
+ int new_argc = 1;
+ struct RDArgs *args;
+ CONST_STRPTR template = "-v/S,NSOPTS/M,URL/K,USERSDIR/K,FORCE/S";
+ long rarray[] = {0,0,0,0,0};
+ enum
+ {
+ A_VERBOSE, /* ignored */
+ A_NSOPTS, /* ignored */
+ A_URL,
+ A_USERSDIR,
+ A_FORCE
+ };
+
+ if(*argc == 0) return NULL; // argc==0 is started from wb
+
+ if((args = ReadArgs(template, rarray, NULL))) {
+ if(rarray[A_URL]) {
+ LOG("URL %s specified on command line", rarray[A_URL]);
+ temp_homepage_url = ami_to_utf8_easy((char *)rarray[A_URL]);
+ }
+
+ if(rarray[A_USERSDIR]) {
+ LOG("USERSDIR %s specified on command line", rarray[A_USERSDIR]);
+ users_dir = ASPrintf("%s", rarray[A_USERSDIR]);
+ }
+
+ if(rarray[A_FORCE]) {
+ LOG("FORCE specified on command line");
+ cli_force = true;
+ }
+
+ if(rarray[A_NSOPTS]) {
+ /* The NSOPTS/M parameter specified in the ReadArgs template is
+ * special. The /M means it collects all arguments that can't
+ * be assigned to any other parameter, and stores them in an
+ * array. We collect these and pass them as a fake argc/argv
+ * to nsoption_commandline().
+ * This trickery is necessary because if ReadArgs() is called
+ * first, nsoption_commandline() can no longer parse (fetch?)
+ * the arguments. If nsoption_commandline() is called first,
+ * then ReadArgs cannot fetch the arguments.
+ */
+ char **p = (char **)rarray[A_NSOPTS];
+
+ do {
+ LOG("Arg [%d] assigned to NSOPTS/M by ReadArgs: %s", new_argc, *p);
+ new_argc++;
+ p++;
+ } while(*p != NULL);
+
+ const char *new_argv = malloc(sizeof(char *) * new_argc);
+ const char **new_argvp = &new_argv;
+ p = (char **)rarray[A_NSOPTS];
+
+ do {
+ *new_argvp = *p;
+ new_argvp++;
+ p++;
+ } while(*p != NULL);
+
+ *nargc = new_argc;
+ *nargv = new_argv;
+ }
+ } else {
+ LOG("ReadArgs failed to parse command line");
+ }
+ return args;
+}
+
+static void ami_gui_read_tooltypes(struct WBArg *wbarg)
+{
+ struct DiskObject *dobj;
+ STRPTR *toolarray;
+ char *s;
+
+ if((*wbarg->wa_Name) && (dobj = GetDiskObject(wbarg->wa_Name))) {
+ toolarray = (STRPTR *)dobj->do_ToolTypes;
+
+ if((s = (char *)FindToolType(toolarray,"USERSDIR"))) users_dir = ASPrintf("%s", s);
+ if((s = (char *)FindToolType(toolarray,"USER"))) current_user = ASPrintf("%s", s);
+
+ FreeDiskObject(dobj);
+ }
+}
+
+static void ami_gui_read_all_tooltypes(int argc, char **argv)
+{
+ struct WBStartup *WBenchMsg;
+ struct WBArg *wbarg;
+ char i;
+ LONG olddir = -1;
+
+ if(argc == 0) { /* Started from WB */
+ WBenchMsg = (struct WBStartup *)argv;
+ for(i = 0, wbarg = WBenchMsg->sm_ArgList; i < WBenchMsg->sm_NumArgs; i++,wbarg++) {
+ olddir =-1;
+ if((wbarg->wa_Lock) && (*wbarg->wa_Name))
+ olddir = SetCurrentDir(wbarg->wa_Lock);
+
+ ami_gui_read_tooltypes(wbarg);
+
+ if(olddir !=-1) SetCurrentDir(olddir);
+ }
+ }
+}
+
+static void gui_init2(int argc, char** argv)
+{
+ struct Screen *screen;
+ BOOL notalreadyrunning;
+ nsurl *url;
+ nserror error;
+ struct browser_window *bw = NULL;
+
+ notalreadyrunning = ami_arexx_init();
+
+ /* Treeview init code ends up calling a font function which needs this */
+ glob = &browserglob;
+
+ /* ...and this ensures the treeview at least gets the WB colour palette to work with */
+ if(scrn == NULL) {
+ if((screen = LockPubScreen("Workbench"))) {
+ ami_set_screen_defaults(screen);
+ UnlockPubScreen(NULL, screen);
+ }
+ } else {
+ ami_set_screen_defaults(scrn);
+ }
+ /**/
+
+ ami_hotlist_initialise(nsoption_charp(hotlist_file));
+ ami_cookies_initialise();
+ ami_global_history_initialise();
+ search_web_select_provider(nsoption_int(search_provider));
+
+ if (notalreadyrunning &&
+ (nsoption_bool(startup_no_window) == false))
+ ami_openscreenfirst();
+
+ if(temp_homepage_url && notalreadyrunning) {
+ error = nsurl_create(temp_homepage_url, &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ &bw);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ amiga_warn_user(messages_get_errorcode(error), 0);
+ }
+ free(temp_homepage_url);
+ }
+
+ if(cli_force == true) {
+ notalreadyrunning = TRUE;
+ }
+
+ if(argc == 0) { // WB
+ struct WBStartup *WBenchMsg = (struct WBStartup *)argv;
+ struct WBArg *wbarg;
+ int first=0,i=0;
+ char fullpath[1024];
+
+ for(i=0,wbarg=WBenchMsg->sm_ArgList;i<WBenchMsg->sm_NumArgs;i++,wbarg++)
+ {
+ if(i==0) continue;
+ if((wbarg->wa_Lock)&&(*wbarg->wa_Name))
+ {
+ DevNameFromLock(wbarg->wa_Lock,fullpath,1024,DN_FULLPATH);
+ AddPart(fullpath,wbarg->wa_Name,1024);
+
+ if(!temp_homepage_url) {
+ nsurl *temp_url;
+ if (netsurf_path_to_nsurl(fullpath, &temp_url) == NSERROR_OK) {
+ temp_homepage_url = strdup(nsurl_access(temp_url));
+ nsurl_unref(temp_url);
+ }
+ }
+
+ if(notalreadyrunning)
+ {
+ error = nsurl_create(temp_homepage_url, &url);
+
+ if (error == NSERROR_OK) {
+ if(!first)
+ {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ &bw);
+
+ first=1;
+ }
+ else
+ {
+ error = browser_window_create(BW_CREATE_CLONE | BW_CREATE_HISTORY,
+ url,
+ NULL,
+ bw,
+ &bw);
+
+ }
+ nsurl_unref(url);
+
+ }
+ if (error != NSERROR_OK) {
+ amiga_warn_user(messages_get_errorcode(error), 0);
+ }
+ free(temp_homepage_url);
+ temp_homepage_url = NULL;
+ }
+ }
+ /* this should be where we read tooltypes, but it's too late for that now */
+ }
+ }
+
+ nsoption_setnull_charp(homepage_url, (char *)strdup(NETSURF_HOMEPAGE));
+
+ if(!notalreadyrunning)
+ {
+ STRPTR sendcmd = NULL;
+
+ if(temp_homepage_url)
+ {
+ sendcmd = ASPrintf("OPEN \"%s\" NEW",temp_homepage_url);
+ free(temp_homepage_url);
+ }
+ else
+ {
+ sendcmd = ASPrintf("OPEN \"%s\" NEW",nsoption_charp(homepage_url));
+ }
+ IDoMethod(arexx_obj,AM_EXECUTE,sendcmd,"NETSURF",NULL,NULL,NULL,NULL);
+ FreeVec(sendcmd);
+
+ ami_quit=true;
+ return;
+ }
+#ifdef __amigaos4__
+ if(IApplication)
+ {
+ if(argc == 0)
+ {
+ ULONG noicon = TAG_IGNORE;
+
+ if (nsoption_bool(hide_docky_icon))
+ noicon = REGAPP_NoIcon;
+
+ ami_appid = RegisterApplication(messages_get("NetSurf"),
+ REGAPP_URLIdentifier, "netsurf-browser.org",
+ REGAPP_WBStartup, (struct WBStartup *)argv,
+ noicon, TRUE,
+ REGAPP_HasPrefsWindow, TRUE,
+ REGAPP_CanCreateNewDocs, TRUE,
+ REGAPP_UniqueApplication, TRUE,
+ REGAPP_Description, messages_get("NetSurfDesc"),
+ TAG_DONE);
+ }
+ else
+ {
+/* TODO: Specify icon when run from Shell */
+ ami_appid = RegisterApplication(messages_get("NetSurf"),
+ REGAPP_URLIdentifier, "netsurf-browser.org",
+ REGAPP_FileName, argv[0],
+ REGAPP_NoIcon, TRUE,
+ REGAPP_HasPrefsWindow, TRUE,
+ REGAPP_CanCreateNewDocs, TRUE,
+ REGAPP_UniqueApplication, TRUE,
+ REGAPP_Description, messages_get("NetSurfDesc"),
+ TAG_DONE);
+ }
+
+ GetApplicationAttrs(ami_appid, APPATTR_Port, (ULONG)&applibport, TAG_DONE);
+ if(applibport) applibsig = (1L << applibport->mp_SigBit);
+ }
+#endif
+ if(!bw && (nsoption_bool(startup_no_window) == false)) {
+ error = nsurl_create(nsoption_charp(homepage_url), &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ amiga_warn_user(messages_get_errorcode(error), 0);
+ }
+ }
+}
+
+static void ami_update_buttons(struct gui_window_2 *gwin)
+{
+ long back=FALSE, forward=FALSE, tabclose=FALSE, stop=FALSE, reload=FALSE;
+ long s_back, s_forward, s_tabclose, s_stop, s_reload;
+
+ if(!browser_window_back_available(gwin->gw->bw))
+ back=TRUE;
+
+ if(!browser_window_forward_available(gwin->gw->bw))
+ forward=TRUE;
+
+ if(!browser_window_stop_available(gwin->gw->bw))
+ stop=TRUE;
+
+ if(!browser_window_reload_available(gwin->gw->bw))
+ reload=TRUE;
+
+ if(nsoption_bool(kiosk_mode) == false)
+ {
+ if(gwin->tabs <= 1)
+ {
+ tabclose=TRUE;
+ OffMenu(gwin->win,AMI_MENU_CLOSETAB);
+ }
+ else
+ {
+ OnMenu(gwin->win,AMI_MENU_CLOSETAB);
+ }
+ }
+
+#ifdef __amigaos4__
+ GetAttr(GA_Disabled, gwin->objects[GID_BACK], (uint32 *)&s_back);
+ GetAttr(GA_Disabled, gwin->objects[GID_FORWARD], (uint32 *)&s_forward);
+ GetAttr(GA_Disabled, gwin->objects[GID_RELOAD], (uint32 *)&s_reload);
+ GetAttr(GA_Disabled, gwin->objects[GID_STOP], (uint32 *)&s_stop);
+#endif
+
+ if(BOOL_MISMATCH(s_back, back))
+ SetGadgetAttrs((struct Gadget *)gwin->objects[GID_BACK],
+ gwin->win, NULL, GA_Disabled, back, TAG_DONE);
+
+ if(BOOL_MISMATCH(s_forward, forward))
+ SetGadgetAttrs((struct Gadget *)gwin->objects[GID_FORWARD],
+ gwin->win, NULL, GA_Disabled, forward, TAG_DONE);
+
+ if(BOOL_MISMATCH(s_reload, reload))
+ SetGadgetAttrs((struct Gadget *)gwin->objects[GID_RELOAD],
+ gwin->win, NULL, GA_Disabled, reload, TAG_DONE);
+
+ if(BOOL_MISMATCH(s_stop, stop))
+ SetGadgetAttrs((struct Gadget *)gwin->objects[GID_STOP],
+ gwin->win, NULL, GA_Disabled, stop, TAG_DONE);
+
+ if(ClickTabBase->lib_Version < 53) {
+ if(gwin->tabs <= 1) tabclose = TRUE;
+#ifdef __amigaos4__
+ GetAttr(GA_Disabled, gwin->objects[GID_CLOSETAB], (uint32 *)&s_tabclose);
+#endif
+ if(BOOL_MISMATCH(s_tabclose, tabclose))
+ SetGadgetAttrs((struct Gadget *)gwin->objects[GID_CLOSETAB],
+ gwin->win, NULL, GA_Disabled, tabclose, TAG_DONE);
+ }
+
+ /* Update the back/forward buttons history context menu */
+ ami_ctxmenu_history_create(AMI_CTXMENU_HISTORY_BACK, gwin);
+ ami_ctxmenu_history_create(AMI_CTXMENU_HISTORY_FORWARD, gwin);
+}
+
+void ami_gui_history(struct gui_window_2 *gwin, bool back)
+{
+ if(back == true)
+ {
+ if(browser_window_back_available(gwin->gw->bw))
+ browser_window_history_back(gwin->gw->bw, false);
+ }
+ else
+ {
+ if(browser_window_forward_available(gwin->gw->bw))
+ browser_window_history_forward(gwin->gw->bw, false);
+ }
+
+ ami_update_buttons(gwin);
+}
+
+int ami_key_to_nskey(ULONG keycode, struct InputEvent *ie)
+{
+ int nskey = 0, chars;
+ char buffer[20];
+ char *utf8 = NULL;
+
+ if(keycode >= IECODE_UP_PREFIX) return 0;
+
+ switch(keycode)
+ {
+ case RAWKEY_CRSRUP:
+ if(ie->ie_Qualifier & IEQUALIFIER_RSHIFT)
+ {
+ nskey = NS_KEY_PAGE_UP;
+ }
+ else if(ie->ie_Qualifier & IEQUALIFIER_RALT)
+ {
+ nskey = NS_KEY_TEXT_START;
+ }
+ else nskey = NS_KEY_UP;
+ break;
+ case RAWKEY_CRSRDOWN:
+ if(ie->ie_Qualifier & IEQUALIFIER_RSHIFT)
+ {
+ nskey = NS_KEY_PAGE_DOWN;
+ }
+ else if(ie->ie_Qualifier & IEQUALIFIER_RALT)
+ {
+ nskey = NS_KEY_TEXT_END;
+ }
+ else nskey = NS_KEY_DOWN;
+ break;
+ case RAWKEY_CRSRLEFT:
+ if(ie->ie_Qualifier & IEQUALIFIER_RSHIFT)
+ {
+ nskey = NS_KEY_LINE_START;
+ }
+ else if(ie->ie_Qualifier & IEQUALIFIER_RALT)
+ {
+ nskey = NS_KEY_WORD_LEFT;
+ }
+ else nskey = NS_KEY_LEFT;
+ break;
+ case RAWKEY_CRSRRIGHT:
+ if(ie->ie_Qualifier & IEQUALIFIER_RSHIFT)
+ {
+ nskey = NS_KEY_LINE_END;
+ }
+ else if(ie->ie_Qualifier & IEQUALIFIER_RALT)
+ {
+ nskey = NS_KEY_WORD_RIGHT;
+ }
+ else nskey = NS_KEY_RIGHT;
+ break;
+ case RAWKEY_ESC:
+ nskey = NS_KEY_ESCAPE;
+ break;
+ case RAWKEY_PAGEUP:
+ nskey = NS_KEY_PAGE_UP;
+ break;
+ case RAWKEY_PAGEDOWN:
+ nskey = NS_KEY_PAGE_DOWN;
+ break;
+ case RAWKEY_HOME:
+ nskey = NS_KEY_TEXT_START;
+ break;
+ case RAWKEY_END:
+ nskey = NS_KEY_TEXT_END;
+ break;
+ case RAWKEY_BACKSPACE:
+ if(ie->ie_Qualifier & IEQUALIFIER_RSHIFT)
+ {
+ nskey = NS_KEY_DELETE_LINE_START;
+ }
+ else nskey = NS_KEY_DELETE_LEFT;
+ break;
+ case RAWKEY_DEL:
+ if(ie->ie_Qualifier & IEQUALIFIER_RSHIFT)
+ {
+ nskey = NS_KEY_DELETE_LINE_END;
+ }
+ else nskey = NS_KEY_DELETE_RIGHT;
+ break;
+ case RAWKEY_TAB:
+ if(ie->ie_Qualifier & IEQUALIFIER_RSHIFT)
+ {
+ nskey = NS_KEY_SHIFT_TAB;
+ }
+ else nskey = NS_KEY_TAB;
+ break;
+ case RAWKEY_F5:
+ case RAWKEY_F8:
+ case RAWKEY_F9:
+ case RAWKEY_F10:
+ case RAWKEY_HELP:
+ // don't translate
+ nskey = keycode;
+ break;
+ default:
+ if((chars = MapRawKey(ie,buffer,20,NULL)) > 0) {
+ utf8_from_local_encoding(buffer, chars, &utf8);
+ nskey = utf8_to_ucs4(utf8, utf8_char_byte_length(utf8));
+
+ if(ie->ie_Qualifier & IEQUALIFIER_RCOMMAND) {
+ switch(nskey) {
+ case 'a':
+ nskey = NS_KEY_SELECT_ALL;
+ break;
+ case 'c':
+ nskey = NS_KEY_COPY_SELECTION;
+ break;
+ case 'v':
+ nskey = NS_KEY_PASTE;
+ break;
+ case 'x':
+ nskey = NS_KEY_CUT_SELECTION;
+ break;
+ case 'y':
+ nskey = NS_KEY_REDO;
+ break;
+ case 'z':
+ nskey = NS_KEY_UNDO;
+ break;
+ }
+ }
+ }
+ break;
+ }
+
+ return nskey;
+}
+
+static void ami_update_quals(struct gui_window_2 *gwin)
+{
+ uint32 quals = 0;
+#ifdef __amigaos4__
+ GetAttr(WINDOW_Qualifier,gwin->objects[OID_MAIN],(uint32 *)&quals);
+#else
+#warning qualifier needs fixing for OS3
+#endif
+ gwin->key_state = 0;
+
+ if((quals & IEQUALIFIER_LSHIFT) || (quals & IEQUALIFIER_RSHIFT))
+ {
+ gwin->key_state |= BROWSER_MOUSE_MOD_1;
+ }
+
+ if(quals & IEQUALIFIER_CONTROL)
+ {
+ gwin->key_state |= BROWSER_MOUSE_MOD_2;
+ }
+
+ if((quals & IEQUALIFIER_LALT) || (quals & IEQUALIFIER_RALT))
+ {
+ gwin->key_state |= BROWSER_MOUSE_MOD_3;
+ }
+}
+
+/* exported interface documented in amiga/gui.h */
+nserror ami_gui_get_space_box(Object *obj, struct IBox **bbox)
+{
+#ifdef __amigaos4__
+ if(LIB_IS_AT_LEAST((struct Library *)SpaceBase, 53, 6)) {
+ *bbox = AllocVecTagList(sizeof(struct IBox), NULL);
+ if(*bbox == NULL) return NSERROR_NOMEM;
+ GetAttr(SPACE_RenderBox, obj, (ULONG *)*bbox);
+ } else
+#endif
+ {
+ GetAttr(SPACE_AreaBox, obj, (ULONG *)bbox);
+ }
+
+ return NSERROR_OK;
+}
+
+/* exported interface documented in amiga/gui.h */
+void ami_gui_free_space_box(struct IBox *bbox)
+{
+#ifdef __amigaos4__
+ if(LIB_IS_AT_LEAST((struct Library *)SpaceBase, 53, 6)) {
+ FreeVec(bbox);
+ }
+#endif
+}
+
+static bool ami_spacebox_to_ns_coords(struct gui_window_2 *gwin, int *x, int *y,
+ int space_x, int space_y)
+{
+ int ns_x = space_x;
+ int ns_y = space_y;
+
+ ns_x /= gwin->gw->scale;
+ ns_y /= gwin->gw->scale;
+
+ ns_x += gwin->gw->scrollx;
+ ns_y += gwin->gw->scrolly;
+
+ *x = ns_x;
+ *y = ns_y;
+
+ return true;
+}
+
+bool ami_mouse_to_ns_coords(struct gui_window_2 *gwin, int *x, int *y,
+ int mouse_x, int mouse_y)
+{
+ int ns_x, ns_y;
+ struct IBox *bbox;
+
+ if(mouse_x == -1) mouse_x = gwin->win->MouseX;
+ if(mouse_y == -1) mouse_y = gwin->win->MouseY;
+
+ if(ami_gui_get_space_box((Object *)gwin->objects[GID_BROWSER], &bbox) == NSERROR_OK) {
+ ns_x = (ULONG)(mouse_x - bbox->Left);
+ ns_y = (ULONG)(mouse_y - bbox->Top);
+
+ if((ns_x < 0) || (ns_x > bbox->Width) || (ns_y < 0) || (ns_y > bbox->Height))
+ return false;
+
+ ami_gui_free_space_box(bbox);
+ } else {
+ amiga_warn_user("NoMemory", "");
+ return false;
+ }
+
+ return ami_spacebox_to_ns_coords(gwin, x, y, ns_x, ns_y);
+}
+
+static void ami_gui_scroll_internal(struct gui_window_2 *gwin, int xs, int ys)
+{
+ struct IBox *bbox;
+ int x, y;
+
+ if(ami_mouse_to_ns_coords(gwin, &x, &y, -1, -1) == true)
+ {
+ if(browser_window_scroll_at_point(gwin->gw->bw, x, y,
+ xs, ys) == false)
+ {
+ int width, height;
+
+ gui_window_get_scroll(gwin->gw,
+ &gwin->gw->scrollx,
+ &gwin->gw->scrolly);
+
+ if(ami_gui_get_space_box((Object *)gwin->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return;
+ }
+
+ browser_window_get_extents(gwin->gw->bw, false, &width, &height);
+
+ switch(xs)
+ {
+ case SCROLL_PAGE_UP:
+ xs = gwin->gw->scrollx - bbox->Width;
+ break;
+
+ case SCROLL_PAGE_DOWN:
+ xs = gwin->gw->scrollx + bbox->Width;
+ break;
+
+ case SCROLL_TOP:
+ xs = 0;
+ break;
+
+ case SCROLL_BOTTOM:
+ xs = width;
+ break;
+
+ default:
+ xs += gwin->gw->scrollx;
+ break;
+ }
+
+ switch(ys)
+ {
+ case SCROLL_PAGE_UP:
+ ys = gwin->gw->scrolly - bbox->Height;
+ break;
+
+ case SCROLL_PAGE_DOWN:
+ ys = gwin->gw->scrolly + bbox->Height;
+ break;
+
+ case SCROLL_TOP:
+ ys = 0;
+ break;
+
+ case SCROLL_BOTTOM:
+ ys = height;
+ break;
+
+ default:
+ ys += gwin->gw->scrolly;
+ break;
+ }
+
+ ami_gui_free_space_box(bbox);
+
+ gui_window_set_scroll(gwin->gw, xs, ys);
+ }
+ }
+}
+
+static struct IBox *ami_ns_rect_to_ibox(struct gui_window_2 *gwin, const struct rect *rect)
+{
+ struct IBox *bbox, *ibox;
+
+ ibox = AllocVecTagList(sizeof(struct IBox), NULL);
+ if(ibox == NULL) return NULL;
+
+ if(ami_gui_get_space_box((Object *)gwin->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return NULL;
+ }
+
+ ibox->Left = gwin->win->MouseX + (rect->x0 * gwin->gw->scale);
+ ibox->Top = gwin->win->MouseY + (rect->y0 * gwin->gw->scale);
+
+ ibox->Width = (rect->x1 - rect->x0) * gwin->gw->scale;
+ ibox->Height = (rect->y1 - rect->y0) * gwin->gw->scale;
+
+ if(ibox->Left < bbox->Left) ibox->Left = bbox->Left;
+ if(ibox->Top < bbox->Top) ibox->Top = bbox->Top;
+
+ if((ibox->Left > (bbox->Left + bbox->Width)) ||
+ (ibox->Top > (bbox->Top + bbox->Height)) ||
+ (ibox->Width < 0) || (ibox->Height < 0))
+ {
+ FreeVec(ibox);
+ ami_gui_free_space_box(bbox);
+ return NULL;
+ }
+
+ ami_gui_free_space_box(bbox);
+ return ibox;
+}
+
+static void ami_gui_trap_mouse(struct gui_window_2 *gwin)
+{
+#ifdef __amigaos4__
+ switch(gwin->drag_op)
+ {
+ case GDRAGGING_NONE:
+ case GDRAGGING_SCROLLBAR:
+ case GDRAGGING_OTHER:
+ break;
+
+ default:
+ if(gwin->ptr_lock)
+ {
+ SetWindowAttrs(gwin->win, WA_GrabFocus, 10,
+ WA_MouseLimits, gwin->ptr_lock, TAG_DONE);
+ }
+ break;
+ }
+#endif
+}
+
+static void ami_gui_menu_update_all(void)
+{
+ struct nsObject *node;
+ struct nsObject *nnode;
+ struct gui_window_2 *gwin;
+
+ if(IsMinListEmpty(window_list)) return;
+
+ node = (struct nsObject *)GetHead((struct List *)window_list);
+
+ do {
+ nnode=(struct nsObject *)GetSucc((struct Node *)node);
+ gwin = node->objstruct;
+
+ if(node->Type == AMINS_WINDOW)
+ {
+ ami_menu_update_checked(gwin);
+ }
+ } while((node = nnode));
+}
+
+static void gui_window_get_dimensions(struct gui_window *g, int *width, int *height,
+ bool scaled)
+{
+ struct IBox *bbox;
+ if(!g) return;
+
+ if(ami_gui_get_space_box((Object *)g->shared->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return;
+ }
+
+ *width = bbox->Width;
+ *height = bbox->Height;
+
+ ami_gui_free_space_box(bbox);
+
+ if(scaled)
+ {
+ *width /= g->scale;
+ *height /= g->scale;
+ }
+}
+
+/* Add a horizontal scroller, if not already present
+ * Returns true if changed, false otherwise */
+static bool ami_gui_hscroll_add(struct gui_window_2 *gwin)
+{
+ struct TagItem attrs[2];
+
+ if(gwin->objects[GID_HSCROLL] != NULL) return false;
+
+ attrs[0].ti_Tag = CHILD_MinWidth;
+ attrs[0].ti_Data = 0;
+ attrs[1].ti_Tag = TAG_DONE;
+ attrs[1].ti_Data = 0;
+
+ gwin->objects[GID_HSCROLL] = ScrollerObj,
+ GA_ID, GID_HSCROLL,
+ GA_RelVerify, TRUE,
+ SCROLLER_Orientation, SORIENT_HORIZ,
+ ICA_TARGET, ICTARGET_IDCMP,
+ ScrollerEnd;
+#ifdef __amigaos4__
+ IDoMethod(gwin->objects[GID_HSCROLLLAYOUT], LM_ADDCHILD,
+ gwin->win, gwin->objects[GID_HSCROLL], attrs);
+#else
+ SetAttrs(gwin->objects[GID_HSCROLLLAYOUT],
+ LAYOUT_AddChild, gwin->objects[GID_HSCROLL], TAG_MORE, &attrs);
+#endif
+ return true;
+}
+
+/* Remove the horizontal scroller, if present */
+static bool ami_gui_hscroll_remove(struct gui_window_2 *gwin)
+{
+ if(gwin->objects[GID_HSCROLL] == NULL) return false;
+
+#ifdef __amigaos4__
+ IDoMethod(gwin->objects[GID_HSCROLLLAYOUT], LM_REMOVECHILD,
+ gwin->win, gwin->objects[GID_HSCROLL]);
+#else
+ SetAttrs(gwin->objects[GID_HSCROLLLAYOUT], LAYOUT_RemoveChild, gwin->objects[GID_HSCROLL]);
+#endif
+
+ gwin->objects[GID_HSCROLL] = NULL;
+
+ return true;
+}
+
+/* Add a vertical scroller, if not already present
+ * Returns true if changed, false otherwise */
+static bool ami_gui_vscroll_add(struct gui_window_2 *gwin)
+{
+ struct TagItem attrs[2];
+
+ if(gwin->objects[GID_VSCROLL] != NULL) return false;
+
+ attrs[0].ti_Tag = CHILD_MinWidth;
+ attrs[0].ti_Data = 0;
+ attrs[1].ti_Tag = TAG_DONE;
+ attrs[1].ti_Data = 0;
+
+ gwin->objects[GID_VSCROLL] = ScrollerObj,
+ GA_ID, GID_VSCROLL,
+ GA_RelVerify, TRUE,
+ ICA_TARGET, ICTARGET_IDCMP,
+ ScrollerEnd;
+#ifdef __amigaos4__
+ IDoMethod(gwin->objects[GID_VSCROLLLAYOUT], LM_ADDCHILD,
+ gwin->win, gwin->objects[GID_VSCROLL], attrs);
+#else
+ SetAttrs(gwin->objects[GID_VSCROLLLAYOUT],
+ LAYOUT_AddChild, gwin->objects[GID_VSCROLL], TAG_MORE, &attrs);
+#endif
+ return true;
+}
+
+/* Remove the vertical scroller, if present */
+static bool ami_gui_vscroll_remove(struct gui_window_2 *gwin)
+{
+ if(gwin->objects[GID_VSCROLL] == NULL) return false;
+
+#ifdef __amigaos4__
+ IDoMethod(gwin->objects[GID_VSCROLLLAYOUT], LM_REMOVECHILD,
+ gwin->win, gwin->objects[GID_VSCROLL]);
+#else
+ SetAttrs(gwin->objects[GID_VSCROLLLAYOUT], LAYOUT_RemoveChild, gwin->objects[GID_VSCROLL]);
+#endif
+
+ gwin->objects[GID_VSCROLL] = NULL;
+
+ return true;
+}
+
+/**
+ * Check the scroll bar requirements for a browser window, and add/remove
+ * the vertical scroller as appropriate. This should be the main entry
+ * point used to perform this task.
+ *
+ * \param gwin "Shared" GUI window to check the state of
+ */
+static void ami_gui_scroller_update(struct gui_window_2 *gwin)
+{
+ int h = 1, w = 1, wh = 0, ww = 0;
+ bool rethinkv = false;
+ bool rethinkh = false;
+ browser_scrolling hscroll = BW_SCROLLING_YES;
+ browser_scrolling vscroll = BW_SCROLLING_YES;
+
+ browser_window_get_scrollbar_type(gwin->gw->bw, &hscroll, &vscroll);
+
+ if(browser_window_is_frameset(gwin->gw->bw) == true) {
+ rethinkv = ami_gui_vscroll_remove(gwin);
+ rethinkh = ami_gui_hscroll_remove(gwin);
+ } else {
+ if((browser_window_get_extents(gwin->gw->bw, false, &w, &h) == NSERROR_OK)) {
+ gui_window_get_dimensions(gwin->gw, &ww, &wh, false);
+ }
+
+ if(vscroll == BW_SCROLLING_NO) {
+ rethinkv = ami_gui_vscroll_remove(gwin);
+ } else {
+ if (h > wh) rethinkv = ami_gui_vscroll_add(gwin);
+ else rethinkv = ami_gui_vscroll_remove(gwin);
+ }
+
+ if(hscroll == BW_SCROLLING_NO) {
+ rethinkh = ami_gui_hscroll_remove(gwin);
+ } else {
+ if (w > ww) rethinkh = ami_gui_hscroll_add(gwin);
+ else rethinkh = ami_gui_hscroll_remove(gwin);
+ }
+ }
+
+ if(rethinkv || rethinkh) {
+ FlushLayoutDomainCache((struct Gadget *)gwin->objects[GID_MAIN]);
+ RethinkLayout((struct Gadget *)gwin->objects[GID_MAIN],
+ gwin->win, NULL, TRUE);
+ browser_window_schedule_reformat(gwin->gw->bw);
+ }
+}
+
+/**
+ * function to add retrieved favicon to gui
+ */
+static void gui_window_set_icon(struct gui_window *g, hlcache_handle *icon)
+{
+ struct BitMap *bm = NULL;
+ struct IBox *bbox;
+ struct bitmap *icon_bitmap = NULL;
+
+ if(nsoption_bool(kiosk_mode) == true) return;
+ if(!g) return;
+
+ if ((icon != NULL) && ((icon_bitmap = content_get_bitmap(icon)) != NULL))
+ {
+ bm = ami_bitmap_get_native(icon_bitmap, 16, 16,
+ g->shared->win->RPort->BitMap);
+ }
+
+ if(g == g->shared->gw) {
+ RefreshGList((struct Gadget *)g->shared->objects[GID_ICON],
+ g->shared->win, NULL, 1);
+
+ if(bm)
+ {
+ ULONG tag, tag_data, minterm;
+
+ if(ami_plot_screen_is_palettemapped() == false) {
+ tag = BLITA_UseSrcAlpha;
+ tag_data = !amiga_bitmap_get_opaque(icon_bitmap);
+ minterm = 0xc0;
+ } else {
+ tag = BLITA_MaskPlane;
+ tag_data = (ULONG)ami_bitmap_get_mask(icon_bitmap, 16, 16, bm);
+ minterm = MINTERM_SRCMASK;
+ }
+
+ if(ami_gui_get_space_box((Object *)g->shared->objects[GID_ICON], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return;
+ }
+
+ EraseRect(g->shared->win->RPort, bbox->Left, bbox->Top,
+ bbox->Left + 16, bbox->Top + 16);
+
+#ifdef __amigaos4__
+ BltBitMapTags(BLITA_SrcX, 0,
+ BLITA_SrcY, 0,
+ BLITA_DestX, bbox->Left,
+ BLITA_DestY, bbox->Top,
+ BLITA_Width, 16,
+ BLITA_Height, 16,
+ BLITA_Source, bm,
+ BLITA_Dest, g->shared->win->RPort,
+ BLITA_SrcType, BLITT_BITMAP,
+ BLITA_DestType, BLITT_RASTPORT,
+ BLITA_Minterm, minterm,
+ tag, tag_data,
+ TAG_DONE);
+#else
+ if(tag_data) {
+ BltMaskBitMapRastPort(bm, 0, 0, g->shared->win->RPort,
+ bbox->Left, bbox->Top, 16, 16, minterm, tag_data);
+ } else {
+ BltBitMapRastPort(bm, 0, 0, g->shared->win->RPort,
+ bbox->Left, bbox->Top, 16, 16, 0xc0);
+ }
+#endif
+ ami_gui_free_space_box(bbox);
+ }
+ }
+
+ g->favicon = icon;
+}
+
+static void ami_gui_refresh_favicon(void *p)
+{
+ struct gui_window_2 *gwin = (struct gui_window_2 *)p;
+ gui_window_set_icon(gwin->gw, gwin->gw->favicon);
+}
+
+/* Gets the size that border gadget 1 (status) needs to be.
+ * Returns the width of the size gadget as a convenience.
+ */
+#ifdef __amigaos4__
+static ULONG ami_get_border_gadget_size(struct gui_window_2 *gwin, ULONG *width, ULONG *height)
+{
+ static ULONG sz_gad_width = 0;
+ static ULONG sz_gad_height = 0;
+ ULONG available_width;
+
+ if((sz_gad_width == 0) || (sz_gad_height == 0)) {
+ struct DrawInfo *dri = GetScreenDrawInfo(scrn);
+ GetGUIAttrs(NULL, dri,
+ GUIA_SizeGadgetWidth, &sz_gad_width,
+ GUIA_SizeGadgetHeight, &sz_gad_height,
+ TAG_DONE);
+ FreeScreenDrawInfo(scrn, dri);
+ }
+ available_width = gwin->win->Width - scrn->WBorLeft - sz_gad_width;
+
+ *width = available_width;
+ *height = sz_gad_height;
+
+ return sz_gad_width;
+}
+#endif
+
+static void ami_set_border_gadget_size(struct gui_window_2 *gwin)
+{
+#ifdef __amigaos4__
+ /* Reset gadget widths according to new calculation */
+ ULONG size1, size2;
+
+ ami_get_border_gadget_size(gwin, &size1, &size2);
+
+ RefreshSetGadgetAttrs((struct Gadget *)(APTR)gwin->objects[GID_STATUS],
+ gwin->win, NULL,
+ GA_Width, size1,
+ TAG_DONE);
+
+ RefreshWindowFrame(gwin->win);
+#endif
+}
+
+static void ami_handle_msg(void)
+{
+ ULONG result,storage = 0,x,y,xs,ys,width=800,height=600;
+ uint16 code;
+ struct IBox *bbox;
+ struct nsObject *node;
+ struct nsObject *nnode;
+ struct gui_window_2 *gwin = NULL;
+ struct InputEvent *ie;
+ struct Node *tabnode;
+ int nskey;
+ struct timeval curtime;
+ static int drag_x_move = 0, drag_y_move = 0;
+ char *utf8 = NULL;
+ nsurl *url;
+
+ if(IsMinListEmpty(window_list))
+ {
+ /* no windows in list, so NetSurf should not be running */
+ ami_try_quit();
+ return;
+ }
+
+ node = (struct nsObject *)GetHead((struct List *)window_list);
+
+ do
+ {
+ nnode=(struct nsObject *)GetSucc((struct Node *)node);
+
+ gwin = node->objstruct;
+
+ if(node->Type == AMINS_TVWINDOW) {
+ if(ami_tree_event((struct treeview_window *)gwin)) {
+ ami_try_quit();
+ break;
+ } else {
+ node = nnode;
+ continue;
+ }
+ } else if(node->Type == AMINS_FINDWINDOW) {
+ if(ami_search_event()) {
+ ami_try_quit();
+ break;
+ } else {
+ node = nnode;
+ continue;
+ }
+ } else if(node->Type == AMINS_HISTORYWINDOW) {
+ if(ami_history_event((struct history_window *)gwin)) {
+ ami_try_quit();
+ break;
+ } else {
+ node = nnode;
+ continue;
+ }
+ } else if(node->Type == AMINS_PRINTWINDOW) {
+ if(ami_print_event((struct ami_print_window *)gwin)) {
+ ami_try_quit();
+ break;
+ } else {
+ node = nnode;
+ continue;
+ }
+ } else if(node->Type == AMINS_GUIOPTSWINDOW) {
+ if(ami_gui_opts_event()) {
+ /* last window possibly closed, so exit with conditions ;) */
+ if(scrn) ami_try_quit();
+ break;
+ } else {
+ node = nnode;
+ continue;
+ }
+ } else if(node->Type == AMINS_DLWINDOW) {
+ if(ami_download_window_event((struct gui_download_window *)gwin)) {
+ ami_try_quit();
+ break;
+ } else {
+ node = nnode;
+ continue;
+ }
+ } else if(node->Type == AMINS_LOGINWINDOW) {
+ if(ami_401login_event((struct gui_login_window *)gwin)) {
+ ami_try_quit();
+ break;
+ } else {
+ node = nnode;
+ continue;
+ }
+ }
+
+ if((gwin == NULL) || (gwin->objects[OID_MAIN] == NULL)) continue;
+
+ while((result = RA_HandleInput(gwin->objects[OID_MAIN], &code)) != WMHI_LASTMSG) {
+ switch(result & WMHI_CLASSMASK) // class
+ {
+ case WMHI_MOUSEMOVE:
+ ami_gui_trap_mouse(gwin); /* re-assert mouse area */
+
+ drag_x_move = 0;
+ drag_y_move = 0;
+
+ if(ami_gui_get_space_box((Object *)gwin->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ break;
+ }
+
+ x = (ULONG)((gwin->win->MouseX - bbox->Left) / gwin->gw->scale);
+ y = (ULONG)((gwin->win->MouseY - bbox->Top) / gwin->gw->scale);
+
+ ami_get_hscroll_pos(gwin, (ULONG *)&xs);
+ ami_get_vscroll_pos(gwin, (ULONG *)&ys);
+
+ x += xs;
+ y += ys;
+
+ width=bbox->Width;
+ height=bbox->Height;
+
+ if(gwin->mouse_state & BROWSER_MOUSE_DRAG_ON)
+ {
+ ami_drag_icon_move();
+
+ if(ami_autoscroll == TRUE) {
+ if((gwin->win->MouseX < bbox->Left) &&
+ ((gwin->win->MouseX - bbox->Left) > -AMI_DRAG_THRESHOLD))
+ drag_x_move = gwin->win->MouseX - bbox->Left;
+ if((gwin->win->MouseX > (bbox->Left + bbox->Width)) &&
+ ((gwin->win->MouseX - (bbox->Left + bbox->Width)) < AMI_DRAG_THRESHOLD))
+ drag_x_move = gwin->win->MouseX - (bbox->Left + bbox->Width);
+ if((gwin->win->MouseY < bbox->Top) &&
+ ((gwin->win->MouseY - bbox->Top) > -AMI_DRAG_THRESHOLD))
+ drag_y_move = gwin->win->MouseY - bbox->Top;
+ if((gwin->win->MouseY > (bbox->Top + bbox->Height)) &&
+ ((gwin->win->MouseY - (bbox->Top + bbox->Height)) < AMI_DRAG_THRESHOLD))
+ drag_y_move = gwin->win->MouseY - (bbox->Top + bbox->Height);
+ }
+ }
+
+ ami_gui_free_space_box(bbox);
+
+ if((x>=xs) && (y>=ys) && (x<width+xs) && (y<height+ys))
+ {
+ ami_update_quals(gwin);
+
+ if(gwin->mouse_state & BROWSER_MOUSE_PRESS_1)
+ {
+ browser_window_mouse_track(gwin->gw->bw,BROWSER_MOUSE_DRAG_1 | gwin->key_state,x,y);
+ gwin->mouse_state = BROWSER_MOUSE_HOLDING_1 | BROWSER_MOUSE_DRAG_ON;
+ }
+ else if(gwin->mouse_state & BROWSER_MOUSE_PRESS_2)
+ {
+ browser_window_mouse_track(gwin->gw->bw,BROWSER_MOUSE_DRAG_2 | gwin->key_state,x,y);
+ gwin->mouse_state = BROWSER_MOUSE_HOLDING_2 | BROWSER_MOUSE_DRAG_ON;
+ }
+ else
+ {
+ browser_window_mouse_track(gwin->gw->bw,gwin->mouse_state | gwin->key_state,x,y);
+ }
+ } else {
+ if(!gwin->mouse_state) ami_set_pointer(gwin, GUI_POINTER_DEFAULT, true);
+ }
+ break;
+
+ case WMHI_MOUSEBUTTONS:
+ if(ami_gui_get_space_box((Object *)gwin->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return;
+ }
+
+ x = (ULONG)((gwin->win->MouseX - bbox->Left) / gwin->gw->scale);
+ y = (ULONG)((gwin->win->MouseY - bbox->Top) / gwin->gw->scale);
+
+ ami_get_hscroll_pos(gwin, (ULONG *)&xs);
+ ami_get_vscroll_pos(gwin, (ULONG *)&ys);
+
+ x += xs;
+ y += ys;
+
+ width=bbox->Width;
+ height=bbox->Height;
+
+ ami_gui_free_space_box(bbox);
+
+ ami_update_quals(gwin);
+
+ if((x>=xs) && (y>=ys) && (x<width+xs) && (y<height+ys))
+ {
+ //code = code>>16;
+ switch(code)
+ {
+ case SELECTDOWN:
+ browser_window_mouse_click(gwin->gw->bw,BROWSER_MOUSE_PRESS_1 | gwin->key_state,x,y);
+ gwin->mouse_state=BROWSER_MOUSE_PRESS_1;
+ break;
+ case MIDDLEDOWN:
+ browser_window_mouse_click(gwin->gw->bw,BROWSER_MOUSE_PRESS_2 | gwin->key_state,x,y);
+ gwin->mouse_state=BROWSER_MOUSE_PRESS_2;
+ break;
+ }
+ }
+
+ if(x<xs) x=xs;
+ if(y<ys) y=ys;
+ if(x>=width+xs) x=width+xs-1;
+ if(y>=height+ys) y=height+ys-1;
+
+ switch(code)
+ {
+ case SELECTUP:
+ if(gwin->mouse_state & BROWSER_MOUSE_PRESS_1)
+ {
+ CurrentTime((ULONG *)&curtime.tv_sec, (ULONG *)&curtime.tv_usec);
+
+ gwin->mouse_state = BROWSER_MOUSE_CLICK_1;
+
+ if(gwin->lastclick.tv_sec)
+ {
+ if(DoubleClick(gwin->lastclick.tv_sec,
+ gwin->lastclick.tv_usec,
+ curtime.tv_sec, curtime.tv_usec)) {
+ if(gwin->prev_mouse_state & BROWSER_MOUSE_DOUBLE_CLICK) {
+ gwin->mouse_state |= BROWSER_MOUSE_TRIPLE_CLICK;
+ } else {
+ gwin->mouse_state |= BROWSER_MOUSE_DOUBLE_CLICK;
+ }
+ }
+ }
+
+ browser_window_mouse_click(gwin->gw->bw,
+ gwin->mouse_state | gwin->key_state,x,y);
+
+ if(gwin->mouse_state & BROWSER_MOUSE_TRIPLE_CLICK)
+ {
+ gwin->lastclick.tv_sec = 0;
+ gwin->lastclick.tv_usec = 0;
+ }
+ else
+ {
+ gwin->lastclick.tv_sec = curtime.tv_sec;
+ gwin->lastclick.tv_usec = curtime.tv_usec;
+ }
+ }
+ else
+ {
+ browser_window_mouse_track(gwin->gw->bw, 0, x, y);
+ }
+ gwin->prev_mouse_state = gwin->mouse_state;
+ gwin->mouse_state=0;
+ break;
+
+ case MIDDLEUP:
+ if(gwin->mouse_state & BROWSER_MOUSE_PRESS_2)
+ {
+ CurrentTime((ULONG *)&curtime.tv_sec, (ULONG *)&curtime.tv_usec);
+
+ gwin->mouse_state = BROWSER_MOUSE_CLICK_2;
+
+ if(gwin->lastclick.tv_sec)
+ {
+ if(DoubleClick(gwin->lastclick.tv_sec,
+ gwin->lastclick.tv_usec,
+ curtime.tv_sec, curtime.tv_usec)) {
+ if(gwin->prev_mouse_state & BROWSER_MOUSE_DOUBLE_CLICK) {
+ gwin->mouse_state |= BROWSER_MOUSE_TRIPLE_CLICK;
+ } else {
+ gwin->mouse_state |= BROWSER_MOUSE_DOUBLE_CLICK;
+ }
+ }
+ }
+
+ browser_window_mouse_click(gwin->gw->bw,
+ gwin->mouse_state | gwin->key_state,x,y);
+
+ if(gwin->mouse_state & BROWSER_MOUSE_TRIPLE_CLICK)
+ {
+ gwin->lastclick.tv_sec = 0;
+ gwin->lastclick.tv_usec = 0;
+ }
+ else
+ {
+ gwin->lastclick.tv_sec = curtime.tv_sec;
+ gwin->lastclick.tv_usec = curtime.tv_usec;
+ }
+ }
+ else
+ {
+ browser_window_mouse_track(gwin->gw->bw, 0, x, y);
+ }
+ gwin->prev_mouse_state = gwin->mouse_state;
+ gwin->mouse_state=0;
+ break;
+#ifdef __amigaos4__
+ case SIDEUP:
+ ami_gui_history(gwin, true);
+ break;
+
+ case EXTRAUP:
+ ami_gui_history(gwin, false);
+ break;
+#endif
+ }
+
+ if(drag_save_data && !gwin->mouse_state)
+ ami_drag_save(gwin->win);
+ break;
+
+ case WMHI_GADGETUP:
+ switch(result & WMHI_GADGETMASK)
+ {
+ case GID_TABS:
+ if(gwin->objects[GID_TABS] == NULL) break;
+ if(ClickTabBase->lib_Version >= 53) {
+ GetAttrs(gwin->objects[GID_TABS],
+ CLICKTAB_NodeClosed, &tabnode, TAG_DONE);
+ } else {
+ tabnode = NULL;
+ }
+
+ if(tabnode) {
+ struct gui_window *closedgw;
+
+ GetClickTabNodeAttrs(tabnode,
+ TNA_UserData, &closedgw,
+ TAG_DONE);
+
+ browser_window_destroy(closedgw->bw);
+ } else {
+ ami_switch_tab(gwin, true);
+ }
+ break;
+
+ case GID_CLOSETAB:
+ browser_window_destroy(gwin->gw->bw);
+ break;
+
+ case GID_ADDTAB:
+ ami_gui_new_blank_tab(gwin);
+ break;
+
+ case GID_URL:
+ {
+ nserror ret;
+ nsurl *url;
+ GetAttr(STRINGA_TextVal,
+ (Object *)gwin->objects[GID_URL],
+ (ULONG *)&storage);
+ utf8 = ami_to_utf8_easy((const char *)storage);
+
+ ret = search_web_omni(utf8, SEARCH_WEB_OMNI_NONE, &url);
+ ami_utf8_free(utf8);
+ if (ret == NSERROR_OK) {
+ browser_window_navigate(gwin->gw->bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (ret != NSERROR_OK) {
+ amiga_warn_user(messages_get_errorcode(ret), 0);
+ }
+ }
+ break;
+
+ case GID_TOOLBARLAYOUT:
+ /* Need fixing: never gets here */
+ search_web_select_provider(-1);
+ break;
+
+ case GID_SEARCH_ICON:
+ GetAttr(CHOOSER_Selected, gwin->objects[GID_SEARCH_ICON], (ULONG *)&storage);
+ search_web_select_provider(storage);
+ break;
+
+ case GID_SEARCHSTRING:
+ {
+ nserror ret;
+ nsurl *url;
+
+ GetAttr(STRINGA_TextVal,
+ (Object *)gwin->objects[GID_SEARCHSTRING],
+ (ULONG *)&storage);
+
+ utf8 = ami_to_utf8_easy((const char *)storage);
+
+ ret = search_web_omni(utf8, SEARCH_WEB_OMNI_SEARCHONLY, &url);
+ ami_utf8_free(utf8);
+ if (ret == NSERROR_OK) {
+ browser_window_navigate(gwin->gw->bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (ret != NSERROR_OK) {
+ amiga_warn_user(messages_get_errorcode(ret), 0);
+ }
+
+ }
+ break;
+
+ case GID_HOME:
+ {
+ if (nsurl_create(nsoption_charp(homepage_url), &url) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", 0);
+ } else {
+ browser_window_navigate(gwin->gw->bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ }
+ break;
+
+ case GID_STOP:
+ if(browser_window_stop_available(gwin->gw->bw))
+ browser_window_stop(gwin->gw->bw);
+ break;
+
+ case GID_RELOAD:
+ ami_update_quals(gwin);
+
+ if(browser_window_reload_available(gwin->gw->bw))
+ {
+ if(gwin->key_state & BROWSER_MOUSE_MOD_1)
+ {
+ browser_window_reload(gwin->gw->bw, true);
+ }
+ else
+ {
+ browser_window_reload(gwin->gw->bw, false);
+ }
+ }
+ break;
+
+ case GID_BACK:
+ ami_gui_history(gwin, true);
+ break;
+
+ case GID_FORWARD:
+ ami_gui_history(gwin, false);
+ break;
+
+ case GID_FAVE:
+ GetAttr(STRINGA_TextVal,
+ (Object *)gwin->objects[GID_URL],
+ (ULONG *)&storage);
+ if(nsurl_create((const char *)storage, &url) == NSERROR_OK) {
+ if(hotlist_has_url(url)) {
+ hotlist_remove_url(url);
+ } else {
+ hotlist_add_url(url);
+ }
+ nsurl_unref(url);
+ }
+ ami_gui_update_hotlist_button(gwin);
+ break;
+
+ case GID_HOTLIST:
+ default:
+// printf("GADGET: %ld\n",(result & WMHI_GADGETMASK));
+ break;
+ }
+ break;
+
+ case WMHI_RAWKEY:
+ ami_update_quals(gwin);
+
+ storage = result & WMHI_GADGETMASK;
+ if(storage >= IECODE_UP_PREFIX) break;
+
+ GetAttr(WINDOW_InputEvent,gwin->objects[OID_MAIN],(ULONG *)&ie);
+
+ nskey = ami_key_to_nskey(storage, ie);
+
+ if((ie->ie_Qualifier & IEQUALIFIER_RCOMMAND) &&
+ ((31 < nskey) && (nskey < 127))) {
+ /* NB: Some keypresses are converted to generic keypresses above
+ * rather than being "menu-emulated" here. */
+ switch(nskey)
+ {
+ /* The following aren't available from the menu at the moment */
+
+ case 'r': // reload
+ if(browser_window_reload_available(gwin->gw->bw))
+ browser_window_reload(gwin->gw->bw, false);
+ break;
+
+ case 'u': // open url
+ if((nsoption_bool(kiosk_mode) == false))
+ ActivateLayoutGadget((struct Gadget *)gwin->objects[GID_MAIN],
+ gwin->win, NULL, (uint32)gwin->objects[GID_URL]);
+ break;
+ }
+ }
+ else
+ {
+ if(!browser_window_key_press(gwin->gw->bw, nskey))
+ {
+ switch(nskey)
+ {
+ case NS_KEY_UP:
+ ami_gui_scroll_internal(gwin, 0, -NSA_KBD_SCROLL_PX);
+ break;
+
+ case NS_KEY_DOWN:
+ ami_gui_scroll_internal(gwin, 0, +NSA_KBD_SCROLL_PX);
+ break;
+
+ case NS_KEY_LEFT:
+ ami_gui_scroll_internal(gwin, -NSA_KBD_SCROLL_PX, 0);
+ break;
+
+ case NS_KEY_RIGHT:
+ ami_gui_scroll_internal(gwin, +NSA_KBD_SCROLL_PX, 0);
+ break;
+
+ case NS_KEY_PAGE_UP:
+ ami_gui_scroll_internal(gwin, 0, SCROLL_PAGE_UP);
+ break;
+
+ case NS_KEY_PAGE_DOWN:
+ case ' ':
+ ami_gui_scroll_internal(gwin, 0, SCROLL_PAGE_DOWN);
+ break;
+
+ case NS_KEY_LINE_START: // page left
+ ami_gui_scroll_internal(gwin, SCROLL_PAGE_UP, 0);
+ break;
+
+ case NS_KEY_LINE_END: // page right
+ ami_gui_scroll_internal(gwin, SCROLL_PAGE_DOWN, 0);
+ break;
+
+ case NS_KEY_TEXT_START: // home
+ ami_gui_scroll_internal(gwin, SCROLL_TOP, SCROLL_TOP);
+ break;
+
+ case NS_KEY_TEXT_END: // end
+ ami_gui_scroll_internal(gwin, SCROLL_BOTTOM, SCROLL_BOTTOM);
+ break;
+
+ case NS_KEY_WORD_RIGHT: // alt+right
+ ami_change_tab(gwin, 1);
+ break;
+
+ case NS_KEY_WORD_LEFT: // alt+left
+ ami_change_tab(gwin, -1);
+ break;
+
+ case NS_KEY_DELETE_LEFT: // backspace
+ ami_gui_history(gwin, true);
+ break;
+
+ /* RawKeys. NB: These are passthrus in ami_key_to_nskey() */
+ case RAWKEY_F5: // reload
+ if(browser_window_reload_available(gwin->gw->bw))
+ browser_window_reload(gwin->gw->bw,false);
+ break;
+
+ case RAWKEY_F8: // scale 100%
+ ami_gui_set_scale(gwin->gw, 1.0);
+ break;
+
+ case RAWKEY_F9: // decrease scale
+ ami_gui_set_scale(gwin->gw, gwin->gw->scale - 0.1);
+ break;
+
+ case RAWKEY_F10: // increase scale
+ ami_gui_set_scale(gwin->gw, gwin->gw->scale + 0.1);
+ break;
+
+ case RAWKEY_HELP: // help
+ ami_help_open(AMI_HELP_GUI, scrn);
+ break;
+ }
+ } else if(nskey == NS_KEY_COPY_SELECTION) {
+ /* if we've copied a selection we need to clear it - style guide rules */
+ browser_window_key_press(gwin->gw->bw, NS_KEY_CLEAR_SELECTION);
+ }
+ }
+ break;
+
+ case WMHI_NEWSIZE:
+ ami_set_border_gadget_size(gwin);
+ ami_throbber_redraw_schedule(0, gwin->gw);
+ ami_schedule(0, ami_gui_refresh_favicon, gwin);
+ browser_window_schedule_reformat(gwin->gw->bw);
+ break;
+
+ case WMHI_CLOSEWINDOW:
+ ami_gui_close_window(gwin);
+ break;
+#ifdef __amigaos4__
+ case WMHI_ICONIFY:
+ {
+ struct bitmap *bm;
+
+ bm = urldb_get_thumbnail(browser_window_get_url(gwin->gw->bw));
+ if(!bm) bm = content_get_bitmap(browser_window_get_content(gwin->gw->bw));
+ gwin->dobj = amiga_icon_from_bitmap(bm);
+ amiga_icon_superimpose_favicon_internal(gwin->gw->favicon,
+ gwin->dobj);
+ HideWindow(gwin->win);
+ gwin->appicon = AddAppIcon((ULONG)gwin->objects[OID_MAIN],
+ (ULONG)gwin, gwin->win->Title, appport,
+ 0, gwin->dobj, NULL);
+
+ cur_gw = NULL;
+ }
+ break;
+#endif
+ case WMHI_INACTIVE:
+ gwin->gw->c_h_temp = gwin->gw->c_h;
+ gui_window_remove_caret(gwin->gw);
+ break;
+
+ case WMHI_ACTIVE:
+ if(gwin->gw->bw) cur_gw = gwin->gw;
+ if(gwin->gw->c_h_temp)
+ gwin->gw->c_h = gwin->gw->c_h_temp;
+ break;
+
+ case WMHI_INTUITICK:
+ break;
+
+ default:
+ //printf("class: %ld\n",(result & WMHI_CLASSMASK));
+ break;
+ }
+
+ if(win_destroyed)
+ {
+ /* we can't be sure what state our window_list is in, so let's
+ jump out of the function and start again */
+
+ win_destroyed = false;
+ return;
+ }
+
+ if(drag_x_move || drag_y_move)
+ {
+ gui_window_get_scroll(gwin->gw,
+ &gwin->gw->scrollx, &gwin->gw->scrolly);
+
+ gui_window_set_scroll(gwin->gw,
+ gwin->gw->scrollx + drag_x_move,
+ gwin->gw->scrolly + drag_y_move);
+ }
+
+// ReplyMsg((struct Message *)message);
+ }
+
+ } while((node = nnode));
+
+ if(ami_menu_window_close)
+ {
+ if(ami_menu_window_close == (void *)AMI_MENU_WINDOW_CLOSE_ALL)
+ ami_quit_netsurf();
+ else
+ ami_gui_close_window(ami_menu_window_close);
+
+ ami_menu_window_close = NULL;
+ }
+
+ if(ami_menu_check_toggled) {
+ ami_gui_menu_update_all();
+ ami_menu_check_toggled = false;
+ }
+}
+
+static void ami_gui_appicon_remove(struct gui_window_2 *gwin)
+{
+ if(gwin->appicon)
+ {
+ RemoveAppIcon(gwin->appicon);
+ amiga_icon_free(gwin->dobj);
+ gwin->appicon = NULL;
+ }
+}
+
+static void ami_handle_appmsg(void)
+{
+ struct AppMessage *appmsg;
+ struct gui_window_2 *gwin;
+ int x, y;
+ struct WBArg *appwinargs;
+ STRPTR filename;
+ int i = 0;
+
+ while((appmsg = (struct AppMessage *)GetMsg(appport)))
+ {
+ gwin = (struct gui_window_2 *)appmsg->am_UserData;
+
+ if(appmsg->am_Type == AMTYPE_APPICON)
+ {
+ ami_gui_appicon_remove(gwin);
+ ShowWindow(gwin->win, WINDOW_FRONTMOST);
+ ActivateWindow(gwin->win);
+ }
+ else if(appmsg->am_Type == AMTYPE_APPWINDOW)
+ {
+ for(i = 0; i < appmsg->am_NumArgs; ++i)
+ {
+ if((appwinargs = &appmsg->am_ArgList[i]))
+ {
+ if((filename = AllocVecTagList(1024, NULL)))
+ {
+ if(appwinargs->wa_Lock)
+ {
+ NameFromLock(appwinargs->wa_Lock, filename, 1024);
+ }
+
+ AddPart(filename, appwinargs->wa_Name, 1024);
+
+ if(ami_mouse_to_ns_coords(gwin, &x, &y,
+ appmsg->am_MouseX, appmsg->am_MouseY) == false)
+ {
+ nsurl *url;
+
+ if (netsurf_path_to_nsurl(filename, &url) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", 0);
+ }
+ else
+ {
+ if(i == 0)
+ {
+ browser_window_navigate(gwin->gw->bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+
+ ActivateWindow(gwin->win);
+ }
+ else
+ {
+ browser_window_create(BW_CREATE_CLONE | BW_CREATE_HISTORY |
+ BW_CREATE_TAB,
+ url,
+ NULL,
+ gwin->gw->bw,
+ NULL);
+ }
+ nsurl_unref(url);
+ }
+ }
+ else
+ {
+ if(browser_window_drop_file_at_point(gwin->gw->bw, x, y, filename) == false)
+ {
+ nsurl *url;
+
+ if (netsurf_path_to_nsurl(filename, &url) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", 0);
+ }
+ else
+ {
+
+ if(i == 0)
+ {
+ browser_window_navigate(gwin->gw->bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+
+ ActivateWindow(gwin->win);
+ }
+ else
+ {
+ browser_window_create(BW_CREATE_CLONE | BW_CREATE_HISTORY |
+ BW_CREATE_TAB,
+ url,
+ NULL,
+ gwin->gw->bw,
+ NULL);
+
+ }
+ nsurl_unref(url);
+ }
+ }
+ }
+ FreeVec(filename);
+ }
+ }
+ }
+ }
+ ReplyMsg((struct Message *)appmsg);
+ }
+}
+
+static void ami_handle_applib(void)
+{
+#ifdef __amigaos4__
+ struct ApplicationMsg *applibmsg;
+ struct browser_window *bw;
+ nsurl *url;
+ nserror error;
+
+ if(!applibport) return;
+
+ while((applibmsg=(struct ApplicationMsg *)GetMsg(applibport)))
+ {
+ switch (applibmsg->type)
+ {
+ case APPLIBMT_NewBlankDoc:
+ {
+
+ error = nsurl_create(nsoption_charp(homepage_url), &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ &bw);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ amiga_warn_user(messages_get_errorcode(error), 0);
+ }
+ }
+ break;
+
+ case APPLIBMT_OpenDoc:
+ {
+ struct ApplicationOpenPrintDocMsg *applibopdmsg =
+ (struct ApplicationOpenPrintDocMsg *)applibmsg;
+
+ error = netsurf_path_to_nsurl(applibopdmsg->fileName, &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ &bw);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ amiga_warn_user(messages_get_errorcode(error), 0);
+ }
+ }
+ break;
+
+ case APPLIBMT_ToFront:
+ if(cur_gw)
+ {
+ ScreenToFront(scrn);
+ WindowToFront(cur_gw->shared->win);
+ ActivateWindow(cur_gw->shared->win);
+ }
+ break;
+
+ case APPLIBMT_OpenPrefs:
+ ScreenToFront(scrn);
+ ami_gui_opts_open();
+ break;
+
+ case APPLIBMT_Quit:
+ case APPLIBMT_ForceQuit:
+ ami_quit_netsurf();
+ break;
+
+ case APPLIBMT_CustomMsg:
+ {
+ struct ApplicationCustomMsg *applibcustmsg =
+ (struct ApplicationCustomMsg *)applibmsg;
+ LOG("Ringhio BackMsg received: %s", applibcustmsg->customMsg);
+ OpenWorkbenchObjectA(applibcustmsg->customMsg, NULL);
+ }
+ break;
+ }
+ ReplyMsg((struct Message *)applibmsg);
+ }
+#endif
+}
+
+void ami_get_msg(void)
+{
+ ULONG winsignal = 1L << sport->mp_SigBit;
+ ULONG appsig = 1L << appport->mp_SigBit;
+ ULONG schedulesig = 1L << schedulermsgport->mp_SigBit;
+ ULONG ctrlcsig = SIGBREAKF_CTRL_C;
+ uint32 signal = 0;
+ fd_set read_fd_set, write_fd_set, except_fd_set;
+ int max_fd = -1;
+ struct MsgPort *printmsgport = ami_print_get_msgport();
+ ULONG printsig = 0;
+ ULONG helpsignal = ami_help_signal();
+ if(printmsgport) printsig = 1L << printmsgport->mp_SigBit;
+ uint32 signalmask = winsignal | appsig | schedulesig | rxsig |
+ printsig | applibsig | helpsignal;
+
+ if ((fetcher_fdset(&read_fd_set, &write_fd_set, &except_fd_set, &max_fd) == NSERROR_OK) &&
+ (max_fd != -1)) {
+ /* max_fd is the highest fd in use, but waitselect() needs to know how many
+ * are in use, so we add 1. */
+
+ if (waitselect(max_fd + 1, &read_fd_set, &write_fd_set, &except_fd_set,
+ NULL, (unsigned int *)&signalmask) != -1) {
+ signal = signalmask;
+ } else {
+ LOG("waitselect() returned error");
+ /* \todo Fix Ctrl-C handling.
+ * WaitSelect() from bsdsocket.library returns -1 if the task was
+ * signalled with a Ctrl-C. waitselect() from newlib.library does not.
+ * Adding the Ctrl-C signal to our user signal mask causes a Ctrl-C to
+ * occur sporadically. Otherwise we never get a -1 except on error.
+ * NetSurf still terminates at the Wait() when network activity is over.
+ */
+ }
+ } else {
+ /* If fetcher_fdset fails or no network activity, do it the old fashioned way. */
+ signalmask |= ctrlcsig;
+ signal = Wait(signalmask);
+ }
+
+ if(signal & winsignal)
+ ami_handle_msg();
+
+ if(signal & appsig)
+ ami_handle_appmsg();
+
+ if(signal & rxsig)
+ ami_arexx_handle();
+
+ if(signal & applibsig)
+ ami_handle_applib();
+
+ if(signal & printsig) {
+ while(GetMsg(printmsgport)); //ReplyMsg
+ ami_print_cont();
+ }
+
+ if(signal & schedulesig) {
+ ami_schedule_handle(schedulermsgport);
+ }
+
+ if(signal & helpsignal)
+ ami_help_process();
+
+ if(signal & ctrlcsig)
+ ami_quit_netsurf_delayed();
+}
+
+void ami_change_tab(struct gui_window_2 *gwin, int direction)
+{
+ struct Node *tab_node = gwin->gw->tab_node;
+ struct Node *ptab = NULL;
+
+ if(gwin->tabs <= 1) return;
+
+ if(direction > 0) {
+ ptab = GetSucc(tab_node);
+ } else {
+ ptab = GetPred(tab_node);
+ }
+
+ if(!ptab) return;
+
+ RefreshSetGadgetAttrs((struct Gadget *)gwin->objects[GID_TABS], gwin->win, NULL,
+ CLICKTAB_CurrentNode, ptab,
+ TAG_DONE);
+
+ ami_switch_tab(gwin, true);
+}
+
+void ami_switch_tab(struct gui_window_2 *gwin, bool redraw)
+{
+ struct Node *tabnode;
+ struct IBox *bbox;
+
+ /* Clear the last new tab list */
+ gwin->gw->last_new_tab = NULL;
+
+ if(gwin->tabs == 0) return;
+
+ gui_window_get_scroll(gwin->gw,
+ &gwin->gw->scrollx, &gwin->gw->scrolly);
+
+ GetAttr(CLICKTAB_CurrentNode, (Object *)gwin->objects[GID_TABS],
+ (ULONG *)&tabnode);
+ GetClickTabNodeAttrs(tabnode,
+ TNA_UserData, &gwin->gw,
+ TAG_DONE);
+ cur_gw = gwin->gw;
+
+ if(ami_gui_get_space_box((Object *)gwin->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return;
+ }
+
+ if((gwin->gw->bw == NULL) || (browser_window_has_content(gwin->gw->bw)) == false) {
+ RefreshSetGadgetAttrs((struct Gadget *)gwin->objects[GID_URL],
+ gwin->win, NULL, STRINGA_TextVal, "", TAG_DONE);
+
+ ami_plot_clear_bbox(gwin->win->RPort, bbox);
+ ami_gui_free_space_box(bbox);
+ return;
+ }
+
+ ami_plot_release_pens(gwin->shared_pens);
+ ami_update_buttons(gwin);
+ ami_menu_update_disabled(gwin->gw, browser_window_get_content(gwin->gw->bw));
+
+ if(redraw)
+ {
+ ami_plot_clear_bbox(gwin->win->RPort, bbox);
+ browser_window_update(gwin->gw->bw, false);
+
+ gui_window_set_scroll(gwin->gw,
+ gwin->gw->scrollx, gwin->gw->scrolly);
+ gwin->redraw_scroll = false;
+
+ browser_window_refresh_url_bar(gwin->gw->bw);
+ ami_gui_update_hotlist_button(gwin);
+ ami_gui_scroller_update(gwin);
+ ami_throbber_redraw_schedule(0, gwin->gw);
+
+ gui_window_set_icon(gwin->gw, gwin->gw->favicon);
+ }
+
+ ami_gui_free_space_box(bbox);
+}
+
+void ami_quit_netsurf(void)
+{
+ struct nsObject *node;
+ struct nsObject *nnode;
+ struct gui_window_2 *gwin;
+
+ if(!IsMinListEmpty(window_list)) {
+ node = (struct nsObject *)GetHead((struct List *)window_list);
+
+ do {
+ nnode=(struct nsObject *)GetSucc((struct Node *)node);
+ gwin = node->objstruct;
+
+ switch(node->Type) {
+ case AMINS_TVWINDOW:
+ ami_tree_close((struct treeview_window *)gwin);
+ break;
+
+ case AMINS_WINDOW:
+ /* This also closes windows that are attached to the
+ * gui_window, such as local history and find. */
+ ShowWindow(gwin->win, WINDOW_BACKMOST);
+ ami_gui_close_window(gwin);
+ break;
+
+ case AMINS_GUIOPTSWINDOW:
+ ami_gui_opts_close();
+ break;
+
+ case AMINS_DLWINDOW:
+ ami_download_window_abort((struct gui_download_window *)gwin);
+ break;
+ }
+ } while((node = nnode));
+
+ win_destroyed = true;
+ }
+
+ if(IsMinListEmpty(window_list)) {
+ /* last window closed, so exit */
+ ami_quit = true;
+ }
+}
+
+void ami_quit_netsurf_delayed(void)
+{
+ int res = -1;
+#ifdef __amigaos4__
+ char *utf8text = ami_utf8_easy(messages_get("TCPIPShutdown"));
+ char *utf8gadgets = ami_utf8_easy(messages_get("AbortShutdown"));
+
+ DisplayBeep(NULL);
+
+ res = TimedDosRequesterTags(TDR_ImageType, TDRIMAGE_INFO,
+ TDR_TitleString, messages_get("NetSurf"),
+ TDR_FormatString, utf8text,
+ TDR_GadgetString, utf8gadgets,
+ TDR_Timeout, 5,
+ TDR_Inactive, TRUE,
+ TAG_DONE);
+
+ free(utf8text);
+ free(utf8gadgets);
+#endif
+ if(res == -1) { /* Requester timed out */
+ nsoption_set_bool(tab_close_warn, false);
+ ami_quit_netsurf();
+ }
+}
+
+static void ami_gui_close_screen(struct Screen *scrn, BOOL locked_screen, BOOL donotwait)
+{
+ if(scrn == NULL) return;
+
+ if(locked_screen) {
+ UnlockPubScreen(NULL,scrn);
+ locked_screen = FALSE;
+ }
+
+ if(CloseScreen(scrn) == TRUE) {
+ if(screen_signal != -1) {
+ FreeSignal(screen_signal);
+ screen_signal = -1;
+ scrn = NULL;
+ }
+ return;
+ }
+ if(donotwait == TRUE) return;
+
+ /* If this is our own screen, wait for visitor windows to close */
+ if(screen_signal == -1) return;
+
+ ULONG scrnsig = 1 << screen_signal;
+ LOG("Waiting for visitor windows to close... (signal)");
+ Wait(scrnsig);
+
+ while (CloseScreen(scrn) == FALSE) {
+ LOG("Waiting for visitor windows to close... (polling)");
+ Delay(50);
+ }
+
+ FreeSignal(screen_signal);
+ screen_signal = -1;
+ scrn = NULL;
+}
+
+void ami_try_quit(void)
+{
+ if(!IsMinListEmpty(window_list)) return;
+
+ if(nsoption_bool(close_no_quit) == false)
+ {
+ ami_quit = true;
+ return;
+ }
+ else
+ {
+ ami_gui_close_screen(scrn, locked_screen, TRUE);
+ }
+}
+
+static void gui_quit(void)
+{
+ ami_theme_throbber_free();
+
+ urldb_save(nsoption_charp(url_file));
+ urldb_save_cookies(nsoption_charp(cookie_file));
+ ami_hotlist_free(nsoption_charp(hotlist_file));
+ ami_cookies_free();
+ ami_global_history_free();
+#ifdef __amigaos4__
+ if(IApplication && ami_appid)
+ UnregisterApplication(ami_appid, NULL);
+#endif
+ ami_arexx_cleanup();
+
+ ami_free_layers(&browserglob);
+
+ ami_font_fini();
+ ami_help_free();
+
+ LOG("Freeing menu items");
+ ami_ctxmenu_free();
+ ami_menu_free_glyphs();
+
+ LOG("Freeing mouse pointers");
+ ami_mouse_pointers_free();
+
+ ami_file_req_free();
+ ami_openurl_close();
+ FreeStringClass(urlStringClass);
+
+ FreeObjList(window_list);
+}
+
+char *ami_gui_get_cache_favicon_name(nsurl *url, bool only_if_avail)
+{
+ STRPTR filename = NULL;
+ BPTR lock = 0;
+
+ if ((filename = ASPrintf("%s/%x", current_user_faviconcache, nsurl_hash(url)))) {
+ LOG("favicon cache location: %s", filename);
+
+ if (only_if_avail == true) {
+ if((lock = Lock(filename, ACCESS_READ))) {
+ UnLock(lock);
+ return filename;
+ }
+ } else {
+ return filename;
+ }
+ }
+ return NULL;
+}
+
+static void ami_gui_cache_favicon(nsurl *url, struct bitmap *favicon)
+{
+ STRPTR filename = NULL;
+
+ if ((filename = ami_gui_get_cache_favicon_name(url, false))) {
+ if(favicon) amiga_bitmap_save(favicon, filename, AMI_BITMAP_SCALE_ICON);
+ FreeVec(filename);
+ }
+}
+
+void ami_gui_update_hotlist_button(struct gui_window_2 *gwin)
+{
+ char *url;
+ nsurl *nsurl;
+
+ GetAttr(STRINGA_TextVal,
+ (Object *)gwin->objects[GID_URL],
+ (ULONG *)&url);
+
+ if(nsurl_create(url, &nsurl) == NSERROR_OK) {
+ if(hotlist_has_url(nsurl)) {
+ RefreshSetGadgetAttrs((struct Gadget *)gwin->objects[GID_FAVE], gwin->win, NULL,
+ BUTTON_RenderImage, gwin->objects[GID_FAVE_RMV], TAG_DONE);
+
+ if (gwin->gw->favicon)
+ ami_gui_cache_favicon(nsurl, content_get_bitmap(gwin->gw->favicon));
+ } else {
+ RefreshSetGadgetAttrs((struct Gadget *)gwin->objects[GID_FAVE], gwin->win, NULL,
+ BUTTON_RenderImage, gwin->objects[GID_FAVE_ADD], TAG_DONE);
+ }
+
+ nsurl_unref(nsurl);
+ }
+}
+
+static bool ami_gui_hotlist_add(void *userdata, int level, int item, const char *title, nsurl *url, bool is_folder)
+{
+ struct ami_gui_tb_userdata *tb_userdata = (struct ami_gui_tb_userdata *)userdata;
+ struct Node *speed_button_node;
+ char menu_icon[1024];
+ char *utf8title = NULL;
+
+ if(level != 1) return false;
+ if(item > AMI_GUI_TOOLBAR_MAX) return false;
+ if(is_folder == true) return false;
+
+ if(utf8_from_local_encoding(title,
+ (strlen(title) < NSA_MAX_HOTLIST_BUTTON_LEN) ? strlen(title) : NSA_MAX_HOTLIST_BUTTON_LEN,
+ &utf8title) != NSERROR_OK)
+ return false;
+
+ char *iconname = ami_gui_get_cache_favicon_name(url, true);
+ if (iconname == NULL) iconname = ASPrintf("icons/content.png");
+ ami_locate_resource(menu_icon, iconname);
+
+ tb_userdata->gw->hotlist_toolbar_lab[item] = BitMapObj,
+ IA_Scalable, TRUE,
+ BITMAP_Screen, scrn,
+ BITMAP_SourceFile, menu_icon,
+ BITMAP_Masking, TRUE,
+ BitMapEnd;
+
+ /* \todo make this scale the bitmap to these dimensions */
+ SetAttrs(tb_userdata->gw->hotlist_toolbar_lab[item],
+ BITMAP_Width, 16,
+ BITMAP_Height, 16,
+ TAG_DONE);
+
+ Object *lab_item = LabelObj,
+ // LABEL_DrawInfo, dri,
+ LABEL_DisposeImage, TRUE,
+ LABEL_Image, tb_userdata->gw->hotlist_toolbar_lab[item],
+ LABEL_Text, " ",
+ LABEL_Text, utf8title,
+ LabelEnd;
+
+ free(utf8title);
+
+ speed_button_node = AllocSpeedButtonNode(item,
+ SBNA_Image, lab_item,
+ SBNA_HintInfo, nsurl_access(url),
+ SBNA_UserData, (void *)url,
+ TAG_DONE);
+
+ AddTail(tb_userdata->sblist, speed_button_node);
+
+ tb_userdata->items++;
+ return true;
+}
+
+static int ami_gui_hotlist_scan(struct tree *tree, struct List *speed_button_list, struct gui_window_2 *gwin)
+{
+ struct ami_gui_tb_userdata userdata;
+ userdata.gw = gwin;
+ userdata.sblist = speed_button_list;
+ userdata.items = 0;
+
+ ami_hotlist_scan((void *)&userdata, 0, messages_get("HotlistToolbar"), ami_gui_hotlist_add);
+ return userdata.items;
+}
+
+static void ami_gui_hotlist_toolbar_add(struct gui_window_2 *gwin)
+{
+ struct TagItem attrs[2];
+
+ attrs[0].ti_Tag = CHILD_MinWidth;
+ attrs[0].ti_Data = 0;
+ attrs[1].ti_Tag = TAG_DONE;
+ attrs[1].ti_Data = 0;
+
+ NewList(&gwin->hotlist_toolbar_list);
+
+ if(ami_gui_hotlist_scan(ami_tree_get_tree(hotlist_window), &gwin->hotlist_toolbar_list, gwin) > 0) {
+ gwin->objects[GID_HOTLIST] =
+ SpeedBarObj,
+ GA_ID, GID_HOTLIST,
+ GA_RelVerify, TRUE,
+ ICA_TARGET, ICTARGET_IDCMP,
+ SPEEDBAR_BevelStyle, BVS_NONE,
+ SPEEDBAR_Buttons, &gwin->hotlist_toolbar_list,
+ SpeedBarEnd;
+
+ gwin->objects[GID_HOTLISTSEPBAR] =
+ BevelObj,
+ BEVEL_Style, BVS_SBAR_VERT,
+ BevelEnd;
+#ifdef __amigaos4__
+ IDoMethod(gwin->objects[GID_HOTLISTLAYOUT], LM_ADDCHILD,
+ gwin->win, gwin->objects[GID_HOTLIST], attrs);
+
+ IDoMethod(gwin->objects[GID_HOTLISTLAYOUT], LM_ADDIMAGE,
+ gwin->win, gwin->objects[GID_HOTLISTSEPBAR], NULL);
+
+#else
+ SetAttrs(gwin->objects[GID_HOTLISTLAYOUT],
+ LAYOUT_AddChild, gwin->objects[GID_HOTLIST], TAG_MORE, &attrs);
+ SetAttrs(gwin->objects[GID_HOTLISTLAYOUT],
+ LAYOUT_AddChild, gwin->objects[GID_HOTLISTSEPBAR], TAG_DONE);
+#endif
+
+ FlushLayoutDomainCache((struct Gadget *)gwin->objects[GID_MAIN]);
+
+ RethinkLayout((struct Gadget *)gwin->objects[GID_MAIN],
+ gwin->win, NULL, TRUE);
+
+ ami_schedule_redraw(gwin, true);
+ }
+}
+
+static void ami_gui_hotlist_toolbar_free(struct gui_window_2 *gwin, struct List *speed_button_list)
+{
+ int i;
+ struct Node *node;
+ struct Node *nnode;
+
+ if(nsoption_bool(kiosk_mode) == true) return;
+
+ if(IsListEmpty(speed_button_list)) return;
+ node = GetHead(speed_button_list);
+
+ do {
+ nnode = GetSucc(node);
+ Remove(node);
+ FreeSpeedButtonNode(node);
+ } while((node = nnode));
+
+ for(i = 0; i < AMI_GUI_TOOLBAR_MAX; i++) {
+ if(gwin->hotlist_toolbar_lab[i]) {
+ DisposeObject(gwin->hotlist_toolbar_lab[i]);
+ gwin->hotlist_toolbar_lab[i] = NULL;
+ }
+ }
+}
+
+static void ami_gui_hotlist_toolbar_remove(struct gui_window_2 *gwin)
+{
+#ifdef __amigaos4__
+ IDoMethod(gwin->objects[GID_HOTLISTLAYOUT], LM_REMOVECHILD,
+ gwin->win, gwin->objects[GID_HOTLIST]);
+
+ IDoMethod(gwin->objects[GID_HOTLISTLAYOUT], LM_REMOVECHILD,
+ gwin->win, gwin->objects[GID_HOTLISTSEPBAR]);
+#else
+ SetAttrs(gwin->objects[GID_HOTLISTLAYOUT], LAYOUT_RemoveChild, gwin->objects[GID_HOTLIST]);
+ SetAttrs(gwin->objects[GID_HOTLISTLAYOUT], LAYOUT_RemoveChild, gwin->objects[GID_HOTLISTSEPBAR]);
+#endif
+ FlushLayoutDomainCache((struct Gadget *)gwin->objects[GID_MAIN]);
+
+ RethinkLayout((struct Gadget *)gwin->objects[GID_MAIN],
+ gwin->win, NULL, TRUE);
+
+ ami_schedule_redraw(gwin, true);
+}
+
+static void ami_gui_hotlist_toolbar_update(struct gui_window_2 *gwin)
+{
+ if(IsListEmpty(&gwin->hotlist_toolbar_list)) {
+ ami_gui_hotlist_toolbar_add(gwin);
+ return;
+ }
+
+ /* Below should be SetAttr according to Autodocs */
+ SetGadgetAttrs((struct Gadget *)gwin->objects[GID_HOTLIST],
+ gwin->win, NULL,
+ SPEEDBAR_Buttons, ~0,
+ TAG_DONE);
+
+ ami_gui_hotlist_toolbar_free(gwin, &gwin->hotlist_toolbar_list);
+
+ if(ami_gui_hotlist_scan(ami_tree_get_tree(hotlist_window), &gwin->hotlist_toolbar_list, gwin) > 0) {
+ SetGadgetAttrs((struct Gadget *)gwin->objects[GID_HOTLIST],
+ gwin->win, NULL,
+ SPEEDBAR_Buttons, &gwin->hotlist_toolbar_list,
+ TAG_DONE);
+ } else {
+ ami_gui_hotlist_toolbar_remove(gwin);
+ }
+}
+
+/**
+ * Update hotlist toolbar and recreate the menu for all windows
+ */
+void ami_gui_hotlist_update_all(void)
+{
+ struct nsObject *node;
+ struct nsObject *nnode;
+ struct gui_window_2 *gwin;
+
+ if(IsMinListEmpty(window_list)) return;
+
+ node = (struct nsObject *)GetHead((struct List *)window_list);
+
+ do {
+ nnode=(struct nsObject *)GetSucc((struct Node *)node);
+ gwin = node->objstruct;
+
+ if(node->Type == AMINS_WINDOW)
+ {
+ ami_gui_hotlist_toolbar_update(gwin);
+ ami_menu_refresh(gwin);
+ }
+ } while((node = nnode));
+}
+
+static void ami_toggletabbar(struct gui_window_2 *gwin, bool show)
+{
+ if(ClickTabBase->lib_Version < 53) return;
+
+ if(show) {
+ struct TagItem attrs[3];
+
+ attrs[0].ti_Tag = CHILD_WeightedWidth;
+ attrs[0].ti_Data = 0;
+ attrs[1].ti_Tag = CHILD_WeightedHeight;
+ attrs[1].ti_Data = 0;
+ attrs[2].ti_Tag = TAG_DONE;
+ attrs[2].ti_Data = 0;
+
+ gwin->objects[GID_TABS] = ClickTabObj,
+ GA_ID, GID_TABS,
+ GA_RelVerify, TRUE,
+ GA_Underscore, 13, // disable kb shortcuts
+ GA_ContextMenu, ami_ctxmenu_clicktab_create(gwin),
+ CLICKTAB_Labels, &gwin->tab_list,
+ CLICKTAB_LabelTruncate, TRUE,
+ CLICKTAB_CloseImage, gwin->objects[GID_CLOSETAB_BM],
+ CLICKTAB_FlagImage, gwin->objects[GID_TABS_FLAG],
+ ClickTabEnd;
+
+ gwin->objects[GID_ADDTAB] = ButtonObj,
+ GA_ID, GID_ADDTAB,
+ GA_RelVerify, TRUE,
+ GA_HintInfo, gwin->helphints[GID_ADDTAB],
+ GA_Text, "+",
+ BUTTON_RenderImage, gwin->objects[GID_ADDTAB_BM],
+ ButtonEnd;
+#ifdef __amigaos4__
+ IDoMethod(gwin->objects[GID_TABLAYOUT], LM_ADDCHILD,
+ gwin->win, gwin->objects[GID_TABS], NULL);
+
+ IDoMethod(gwin->objects[GID_TABLAYOUT], LM_ADDCHILD,
+ gwin->win, gwin->objects[GID_ADDTAB], attrs);
+#else
+ SetAttrs(gwin->objects[GID_TABLAYOUT],
+ LAYOUT_AddChild, gwin->objects[GID_TABS], TAG_DONE);
+ SetAttrs(gwin->objects[GID_TABLAYOUT],
+ LAYOUT_AddChild, gwin->objects[GID_ADDTAB], TAG_MORE, &attrs);
+#endif
+ } else {
+#ifdef __amigaos4__
+ IDoMethod(gwin->objects[GID_TABLAYOUT], LM_REMOVECHILD,
+ gwin->win, gwin->objects[GID_TABS]);
+
+ IDoMethod(gwin->objects[GID_TABLAYOUT], LM_REMOVECHILD,
+ gwin->win, gwin->objects[GID_ADDTAB]);
+#else
+ SetAttrs(gwin->objects[GID_TABLAYOUT],
+ LAYOUT_RemoveChild, gwin->objects[GID_TABS], TAG_DONE);
+ SetAttrs(gwin->objects[GID_TABLAYOUT],
+ LAYOUT_RemoveChild, gwin->objects[GID_ADDTAB], TAG_DONE);
+#endif
+
+ gwin->objects[GID_TABS] = NULL;
+ gwin->objects[GID_ADDTAB] = NULL;
+ }
+
+ FlushLayoutDomainCache((struct Gadget *)gwin->objects[GID_MAIN]);
+
+ RethinkLayout((struct Gadget *)gwin->objects[GID_MAIN],
+ gwin->win, NULL, TRUE);
+
+ if(gwin->gw && gwin->gw->bw) browser_window_update(gwin->gw->bw, false);
+}
+
+void ami_gui_tabs_toggle_all(void)
+{
+ struct nsObject *node;
+ struct nsObject *nnode;
+ struct gui_window_2 *gwin;
+
+ if(IsMinListEmpty(window_list)) return;
+
+ node = (struct nsObject *)GetHead((struct List *)window_list);
+
+ do {
+ nnode=(struct nsObject *)GetSucc((struct Node *)node);
+ gwin = node->objstruct;
+
+ if(node->Type == AMINS_WINDOW)
+ {
+ if(gwin->tabs == 1) {
+ if(nsoption_bool(tab_always_show) == true) {
+ ami_toggletabbar(gwin, true);
+ } else {
+ ami_toggletabbar(gwin, false);
+ }
+ }
+ }
+ } while((node = nnode));
+}
+
+static void ami_gui_search_ico_refresh(void *p)
+{
+ search_web_select_provider(-1);
+}
+
+/**
+ * Count windows, and optionally tabs.
+ *
+ * \param window window to count tabs of
+ * \param tabs if window > 0, will be updated to contain the number of tabs
+ * in that window, unchanged otherwise
+ * \return number of windows currently open
+ */
+int ami_gui_count_windows(int window, int *tabs)
+{
+ int windows = 0;
+ struct nsObject *node, *nnode;
+ struct gui_window_2 *gwin;
+
+ if(!IsMinListEmpty(window_list)) {
+ node = (struct nsObject *)GetHead((struct List *)window_list);
+ do {
+ nnode=(struct nsObject *)GetSucc((struct Node *)node);
+
+ gwin = node->objstruct;
+
+ if(node->Type == AMINS_WINDOW) {
+ windows++;
+ if(window == windows) *tabs = gwin->tabs;
+ }
+ } while((node = nnode));
+ }
+ return windows;
+}
+
+/**
+ * Set the scale of a gui window
+ *
+ * \param gw gui_window to set scale for
+ * \param scale scale to set
+ */
+void ami_gui_set_scale(struct gui_window *gw, float scale)
+{
+ if(scale <= 0.0) return;
+ gw->scale = scale;
+ browser_window_set_scale(gw->bw, scale, true);
+}
+
+nserror ami_gui_new_blank_tab(struct gui_window_2 *gwin)
+{
+ nsurl *url;
+ nserror error;
+ struct browser_window *bw = NULL;
+
+ error = nsurl_create(nsoption_charp(homepage_url), &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY |
+ BW_CREATE_TAB,
+ url,
+ NULL,
+ gwin->gw->bw,
+ &bw);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ amiga_warn_user(messages_get_errorcode(error), 0);
+ return error;
+ }
+
+ return NSERROR_OK;
+}
+
+static void ami_do_redraw_tiled(struct gui_window_2 *gwin, bool busy,
+ int left, int top, int width, int height,
+ int sx, int sy, struct IBox *bbox, struct redraw_context *ctx)
+{
+ int x, y;
+ struct rect clip;
+ int tile_size_x = glob->width;
+ int tile_size_y = glob->height;
+
+ int tile_x_scale = (int)(tile_size_x / gwin->gw->scale);
+ int tile_y_scale = (int)(tile_size_y / gwin->gw->scale);
+
+ browserglob.shared_pens = gwin->shared_pens; /* do we need this?? */
+
+ if(top < 0) {
+ height += top;
+ top = 0;
+ }
+
+ if(left < 0) {
+ width += left;
+ left = 0;
+ }
+
+ if(top < sy) {
+ height += (top - sy);
+ top = sy;
+ }
+ if(left < sx) {
+ width += (left - sx);
+ left = sx;
+ }
+
+ if(((top - sy) + height) > bbox->Height)
+ height = bbox->Height - (top - sy);
+
+ if(((left - sx) + width) > bbox->Width)
+ width = bbox->Width - (left - sx);
+
+ if(width <= 0) return;
+ if(height <= 0) return;
+
+ if(busy) ami_set_pointer(gwin, GUI_POINTER_WAIT, false);
+
+ for(y = top; y < (top + height); y += tile_y_scale) {
+ clip.y0 = 0;
+ clip.y1 = tile_size_y;
+ if(clip.y1 > height) clip.y1 = height;
+ if((((y - sy) * gwin->gw->scale) + clip.y1) > bbox->Height)
+ clip.y1 = bbox->Height - ((y - sy) * gwin->gw->scale);
+
+ for(x = left; x < (left + width); x += tile_x_scale) {
+ clip.x0 = 0;
+ clip.x1 = tile_size_x;
+ if(clip.x1 > width) clip.x1 = width;
+ if((((x - sx) * gwin->gw->scale) + clip.x1) > bbox->Width)
+ clip.x1 = bbox->Width - ((x - sx) * gwin->gw->scale);
+
+ if(browser_window_redraw(gwin->gw->bw,
+ clip.x0 - (int)x,
+ clip.y0 - (int)y,
+ &clip, ctx))
+ {
+ ami_clearclipreg(&browserglob);
+#ifdef __amigaos4__
+ BltBitMapTags(BLITA_SrcType, BLITT_BITMAP,
+ BLITA_Source, browserglob.bm,
+ BLITA_SrcX, 0,
+ BLITA_SrcY, 0,
+ BLITA_DestType, BLITT_RASTPORT,
+ BLITA_Dest, gwin->win->RPort,
+ BLITA_DestX, bbox->Left + (int)((x - sx) * gwin->gw->scale),
+ BLITA_DestY, bbox->Top + (int)((y - sy) * gwin->gw->scale),
+ BLITA_Width, (int)(clip.x1),
+ BLITA_Height, (int)(clip.y1),
+ TAG_DONE);
+#else
+ BltBitMapRastPort(browserglob.bm, 0, 0, gwin->win->RPort,
+ bbox->Left + (int)((x - sx) * gwin->gw->scale),
+ bbox->Top + (int)((y - sy) * gwin->gw->scale),
+ (int)(clip.x1), (int)(clip.y1), 0xC0);
+#endif
+ }
+ }
+ }
+
+ if(busy) ami_reset_pointer(gwin);
+}
+
+
+/**
+ * Redraw an area of the browser window - Amiga-specific function
+ *
+ * \param g a struct gui_window
+ * \param bw a struct browser_window
+ * \param busy busy flag passed to tiled redraw.
+ * \param x0 top-left co-ordinate (in document co-ordinates)
+ * \param y0 top-left co-ordinate (in document co-ordinates)
+ * \param x1 bottom-right co-ordinate (in document co-ordinates)
+ * \param y1 bottom-right co-ordinate (in document co-ordinates)
+ */
+
+static void ami_do_redraw_limits(struct gui_window *g, struct browser_window *bw, bool busy,
+ int x0, int y0, int x1, int y1)
+{
+ struct IBox *bbox;
+ ULONG sx, sy;
+
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &amiplot
+ };
+
+ if(!g) return;
+ if(browser_window_redraw_ready(bw) == false) return;
+
+ sx = g->scrollx;
+ sy = g->scrolly;
+
+ if(g != g->shared->gw) return;
+
+ if(ami_gui_get_space_box((Object *)g->shared->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return;
+ }
+
+ ami_do_redraw_tiled(g->shared, busy, x0, y0,
+ (x1 - x0) * g->scale, (y1 - y0) * g->scale, sx, sy, bbox, &ctx);
+
+ ami_gui_free_space_box(bbox);
+
+ return;
+}
+
+static void ami_refresh_window(struct gui_window_2 *gwin)
+{
+ /* simplerefresh only */
+
+ struct IBox *bbox;
+ int x0, x1, y0, y1, sx, sy;
+ struct RegionRectangle *regrect;
+
+ sx = gwin->gw->scrollx;
+ sy = gwin->gw->scrolly;
+
+ ami_set_pointer(gwin, GUI_POINTER_WAIT, false);
+
+ if(ami_gui_get_space_box((Object *)gwin->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return;
+ }
+
+ BeginRefresh(gwin->win);
+
+ x0 = ((gwin->win->RPort->Layer->DamageList->bounds.MinX - bbox->Left) /
+ browser_window_get_scale(gwin->gw->bw)) + sx - 1;
+ x1 = ((gwin->win->RPort->Layer->DamageList->bounds.MaxX - bbox->Left) /
+ browser_window_get_scale(gwin->gw->bw)) + sx + 2;
+ y0 = ((gwin->win->RPort->Layer->DamageList->bounds.MinY - bbox->Top) /
+ browser_window_get_scale(gwin->gw->bw)) + sy - 1;
+ y1 = ((gwin->win->RPort->Layer->DamageList->bounds.MaxY - bbox->Top) /
+ browser_window_get_scale(gwin->gw->bw)) + sy + 2;
+
+ regrect = gwin->win->RPort->Layer->DamageList->RegionRectangle;
+
+ ami_do_redraw_limits(gwin->gw, gwin->gw->bw, false, x0, y0, x1, y1);
+
+ while(regrect)
+ {
+ x0 = ((regrect->bounds.MinX - bbox->Left) /
+ browser_window_get_scale(gwin->gw->bw)) + sx - 1;
+ x1 = ((regrect->bounds.MaxX - bbox->Left) /
+ browser_window_get_scale(gwin->gw->bw)) + sx + 2;
+ y0 = ((regrect->bounds.MinY - bbox->Top) /
+ browser_window_get_scale(gwin->gw->bw)) + sy - 1;
+ y1 = ((regrect->bounds.MaxY - bbox->Top) /
+ browser_window_get_scale(gwin->gw->bw)) + sy + 2;
+
+ regrect = regrect->Next;
+
+ ami_do_redraw_limits(gwin->gw, gwin->gw->bw, false, x0, y0, x1, y1);
+ }
+
+ EndRefresh(gwin->win, TRUE);
+
+ ami_gui_free_space_box(bbox);
+ ami_reset_pointer(gwin);
+}
+
+HOOKF(void, ami_scroller_hook, Object *, object, struct IntuiMessage *)
+{
+ ULONG gid;
+ struct gui_window_2 *gwin = hook->h_Data;
+ struct IntuiWheelData *wheel;
+ struct Node *node = NULL;
+ nsurl *url;
+
+ switch(msg->Class)
+ {
+ case IDCMP_IDCMPUPDATE:
+ gid = GetTagData( GA_ID, 0, msg->IAddress );
+
+ switch( gid )
+ {
+ case GID_HSCROLL:
+ case GID_VSCROLL:
+ if(nsoption_bool(faster_scroll) == true) gwin->redraw_scroll = true;
+ else gwin->redraw_scroll = false;
+
+ ami_schedule_redraw(gwin, true);
+ break;
+
+ case GID_HOTLIST:
+ if((node = (struct Node *)GetTagData(SPEEDBAR_SelectedNode, 0, msg->IAddress))) {
+ GetSpeedButtonNodeAttrs(node, SBNA_UserData, (ULONG *)&url, TAG_DONE);
+
+ if(gwin->key_state & BROWSER_MOUSE_MOD_2) {
+ browser_window_create(BW_CREATE_TAB,
+ url,
+ NULL,
+ gwin->gw->bw,
+ NULL);
+ } else {
+ browser_window_navigate(gwin->gw->bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+
+ }
+ }
+ break;
+ }
+ break;
+#ifdef __amigaos4__
+ case IDCMP_EXTENDEDMOUSE:
+ if(msg->Code == IMSGCODE_INTUIWHEELDATA)
+ {
+ wheel = (struct IntuiWheelData *)msg->IAddress;
+
+ ami_gui_scroll_internal(gwin, wheel->WheelX * 50, wheel->WheelY * 50);
+ }
+ break;
+#endif
+ case IDCMP_SIZEVERIFY:
+ break;
+
+ case IDCMP_REFRESHWINDOW:
+ ami_refresh_window(gwin);
+ break;
+
+ default:
+ LOG("IDCMP hook unhandled event: %ld\n", msg->Class);
+ break;
+ }
+// ReplyMsg((struct Message *)msg);
+}
+
+static struct gui_window *
+gui_window_create(struct browser_window *bw,
+ struct gui_window *existing,
+ gui_window_create_flags flags)
+{
+ struct gui_window *g = NULL;
+ ULONG offset = 0;
+ ULONG curx = nsoption_int(window_x), cury = nsoption_int(window_y);
+ ULONG curw = nsoption_int(window_width), curh = nsoption_int(window_height);
+ char nav_west[100],nav_west_s[100],nav_west_g[100];
+ char nav_east[100],nav_east_s[100],nav_east_g[100];
+ char stop[100],stop_s[100],stop_g[100];
+ char reload[100],reload_s[100],reload_g[100];
+ char home[100],home_s[100],home_g[100];
+ char closetab[100],closetab_s[100],closetab_g[100];
+ char addtab[100],addtab_s[100],addtab_g[100];
+ char fave[100], unfave[100];
+ char tabthrobber[100];
+ ULONG refresh_mode = WA_SmartRefresh;
+ ULONG defer_layout = TRUE;
+ ULONG idcmp_sizeverify = IDCMP_SIZEVERIFY;
+
+ LOG("Creating window");
+
+ if (!scrn) ami_openscreenfirst();
+
+ if (nsoption_bool(kiosk_mode)) flags &= ~GW_CREATE_TAB;
+ if (nsoption_bool(resize_with_contents)) idcmp_sizeverify = 0;
+
+ /* Offset the new window by titlebar + 1 as per AmigaOS style guide.
+ * If we don't have a clone window we offset by all windows open. */
+ offset = scrn->WBorTop + scrn->Font->ta_YSize + 1;
+
+ if(existing) {
+ curx = existing->shared->win->LeftEdge;
+ cury = existing->shared->win->TopEdge + offset;
+ curw = existing->shared->win->Width;
+ curh = existing->shared->win->Height;
+ } else {
+ if(nsoption_bool(kiosk_mode) == false) {
+ cury += offset * ami_gui_count_windows(0, NULL);
+ }
+ }
+
+ if(curh > (scrn->Height - cury)) curh = scrn->Height - cury;
+
+ g = ami_misc_allocvec_clear(sizeof(struct gui_window), 0);
+
+ if(!g)
+ {
+ amiga_warn_user("NoMemory","");
+ return NULL;
+ }
+
+ NewList(&g->dllist);
+ g->deferred_rects = NewObjList();
+ g->deferred_rects_pool = ami_misc_itempool_create(sizeof(struct rect));
+ g->bw = bw;
+ g->scale = browser_window_get_scale(bw);
+
+ if((flags & GW_CREATE_TAB) && existing)
+ {
+ g->shared = existing->shared;
+ g->tab = g->shared->next_tab;
+ g->shared->tabs++; /* do this early so functions know to update the tabs */
+
+ if((g->shared->tabs == 2) && (nsoption_bool(tab_always_show) == false)) {
+ ami_toggletabbar(g->shared, true);
+ }
+
+ SetGadgetAttrs((struct Gadget *)g->shared->objects[GID_TABS],
+ g->shared->win, NULL,
+ CLICKTAB_Labels, ~0,
+ TAG_DONE);
+
+ g->tab_node = AllocClickTabNode(TNA_Text, messages_get("NetSurf"),
+ TNA_Number, g->tab,
+ TNA_UserData, g,
+ TNA_CloseGadget, TRUE,
+ TAG_DONE);
+
+ if(nsoption_bool(new_tab_last)) {
+ AddTail(&g->shared->tab_list, g->tab_node);
+ } else {
+ struct Node *insert_after = existing->tab_node;
+
+ if(existing->last_new_tab)
+ insert_after = existing->last_new_tab;
+ Insert(&g->shared->tab_list, g->tab_node, insert_after);
+ existing->last_new_tab = g->tab_node;
+ }
+
+ RefreshSetGadgetAttrs((struct Gadget *)g->shared->objects[GID_TABS],
+ g->shared->win, NULL,
+ CLICKTAB_Labels, &g->shared->tab_list,
+ TAG_DONE);
+
+ if(nsoption_bool(new_tab_is_active)) {
+ RefreshSetGadgetAttrs((struct Gadget *)g->shared->objects[GID_TABS],
+ g->shared->win, NULL,
+ CLICKTAB_Current, g->tab,
+ TAG_DONE);
+ }
+
+ if(ClickTabBase->lib_Version < 53) {
+ RethinkLayout((struct Gadget *)g->shared->objects[GID_TABLAYOUT],
+ g->shared->win, NULL, TRUE);
+ }
+
+ g->shared->next_tab++;
+
+ if(nsoption_bool(new_tab_is_active)) ami_switch_tab(g->shared,false);
+
+ ami_update_buttons(g->shared);
+ ami_schedule(0, ami_gui_refresh_favicon, g->shared);
+
+ return g;
+ }
+
+ g->shared = ami_misc_allocvec_clear(sizeof(struct gui_window_2), 0);
+
+ if(!g->shared)
+ {
+ amiga_warn_user("NoMemory","");
+ return NULL;
+ }
+
+ g->shared->shared_pens = ami_AllocMinList();
+
+ g->shared->scrollerhook.h_Entry = (void *)ami_scroller_hook;
+ g->shared->scrollerhook.h_Data = g->shared;
+
+ g->shared->favicon_hook.h_Entry = (void *)ami_set_favicon_render_hook;
+ g->shared->favicon_hook.h_Data = g->shared;
+
+ g->shared->throbber_hook.h_Entry = (void *)ami_set_throbber_render_hook;
+ g->shared->throbber_hook.h_Data = g->shared;
+
+ newprefs_hook.h_Entry = (void *)ami_gui_newprefs_hook;
+ newprefs_hook.h_Data = 0;
+
+ g->shared->ctxmenu_hook = ami_ctxmenu_get_hook(g->shared);
+ g->shared->history_ctxmenu[AMI_CTXMENU_HISTORY_BACK] = NULL;
+ g->shared->history_ctxmenu[AMI_CTXMENU_HISTORY_FORWARD] = NULL;
+ g->shared->clicktab_ctxmenu = NULL;
+
+ if(nsoption_bool(window_simple_refresh) == true) {
+ refresh_mode = WA_SimpleRefresh;
+ defer_layout = FALSE; /* testing reveals this does work with SimpleRefresh,
+ but the docs say it doesn't so err on the side of caution. */
+ } else {
+ refresh_mode = WA_SmartRefresh;
+ defer_layout = TRUE;
+ }
+
+ if(!nsoption_bool(kiosk_mode))
+ {
+ ULONG addtabclosegadget = TAG_IGNORE;
+ ULONG iconifygadget = FALSE;
+
+ if (nsoption_charp(pubscreen_name) &&
+ (locked_screen == TRUE) &&
+ (strcmp(nsoption_charp(pubscreen_name), "Workbench") == 0))
+ iconifygadget = TRUE;
+
+ LOG("Creating menu");
+ struct Menu *menu = ami_menu_create(g->shared);
+
+ NewList(&g->shared->tab_list);
+ g->tab_node = AllocClickTabNode(TNA_Text,messages_get("NetSurf"),
+ TNA_Number, 0,
+ TNA_UserData, g,
+ TNA_CloseGadget, TRUE,
+ TAG_DONE);
+ AddTail(&g->shared->tab_list,g->tab_node);
+
+ g->shared->web_search_list = ami_gui_opts_websearch();
+ g->shared->search_bm = NULL;
+
+ g->shared->tabs=1;
+ g->shared->next_tab=1;
+
+ g->shared->svbuffer = ami_misc_allocvec_clear(2000, 0);
+
+ g->shared->helphints[GID_BACK] =
+ translate_escape_chars(messages_get("HelpToolbarBack"));
+ g->shared->helphints[GID_FORWARD] =
+ translate_escape_chars(messages_get("HelpToolbarForward"));
+ g->shared->helphints[GID_STOP] =
+ translate_escape_chars(messages_get("HelpToolbarStop"));
+ g->shared->helphints[GID_RELOAD] =
+ translate_escape_chars(messages_get("HelpToolbarReload"));
+ g->shared->helphints[GID_HOME] =
+ translate_escape_chars(messages_get("HelpToolbarHome"));
+ g->shared->helphints[GID_URL] =
+ translate_escape_chars(messages_get("HelpToolbarURL"));
+ g->shared->helphints[GID_SEARCHSTRING] =
+ translate_escape_chars(messages_get("HelpToolbarWebSearch"));
+ g->shared->helphints[GID_ADDTAB] =
+ translate_escape_chars(messages_get("HelpToolbarAddTab"));
+
+ ami_get_theme_filename(nav_west, "theme_nav_west", false);
+ ami_get_theme_filename(nav_west_s, "theme_nav_west_s", false);
+ ami_get_theme_filename(nav_west_g, "theme_nav_west_g", false);
+ ami_get_theme_filename(nav_east, "theme_nav_east", false);
+ ami_get_theme_filename(nav_east_s, "theme_nav_east_s", false);
+ ami_get_theme_filename(nav_east_g, "theme_nav_east_g", false);
+ ami_get_theme_filename(stop, "theme_stop", false);
+ ami_get_theme_filename(stop_s, "theme_stop_s", false);
+ ami_get_theme_filename(stop_g, "theme_stop_g", false);
+ ami_get_theme_filename(reload, "theme_reload", false);
+ ami_get_theme_filename(reload_s, "theme_reload_s", false);
+ ami_get_theme_filename(reload_g, "theme_reload_g", false);
+ ami_get_theme_filename(home, "theme_home", false);
+ ami_get_theme_filename(home_s, "theme_home_s", false);
+ ami_get_theme_filename(home_g, "theme_home_g", false);
+ ami_get_theme_filename(closetab, "theme_closetab", false);
+ ami_get_theme_filename(closetab_s, "theme_closetab_s", false);
+ ami_get_theme_filename(closetab_g, "theme_closetab_g", false);
+ ami_get_theme_filename(addtab, "theme_addtab", false);
+ ami_get_theme_filename(addtab_s, "theme_addtab_s", false);
+ ami_get_theme_filename(addtab_g, "theme_addtab_g", false);
+ ami_get_theme_filename(tabthrobber, "theme_tab_loading", false);
+ ami_get_theme_filename(fave, "theme_fave", false);
+ ami_get_theme_filename(unfave, "theme_unfave", false);
+
+ g->shared->objects[GID_FAVE_ADD] = BitMapObj,
+ BITMAP_SourceFile, fave,
+ BITMAP_Screen, scrn,
+ BITMAP_Masking, TRUE,
+ BitMapEnd;
+
+ g->shared->objects[GID_FAVE_RMV] = BitMapObj,
+ BITMAP_SourceFile, unfave,
+ BITMAP_Screen, scrn,
+ BITMAP_Masking, TRUE,
+ BitMapEnd;
+
+ g->shared->objects[GID_ADDTAB_BM] = BitMapObj,
+ BITMAP_SourceFile, addtab,
+ BITMAP_SelectSourceFile, addtab_s,
+ BITMAP_DisabledSourceFile, addtab_g,
+ BITMAP_Screen, scrn,
+ BITMAP_Masking, TRUE,
+ BitMapEnd;
+
+ g->shared->objects[GID_CLOSETAB_BM] = BitMapObj,
+ BITMAP_SourceFile, closetab,
+ BITMAP_SelectSourceFile, closetab_s,
+ BITMAP_DisabledSourceFile, closetab_g,
+ BITMAP_Screen, scrn,
+ BITMAP_Masking, TRUE,
+ BitMapEnd;
+
+ if(ClickTabBase->lib_Version < 53)
+ {
+ addtabclosegadget = LAYOUT_AddChild;
+ g->shared->objects[GID_CLOSETAB] = ButtonObj,
+ GA_ID, GID_CLOSETAB,
+ GA_RelVerify, TRUE,
+ BUTTON_RenderImage, g->shared->objects[GID_CLOSETAB_BM],
+ ButtonEnd;
+
+ g->shared->objects[GID_TABS] = ClickTabObj,
+ GA_ID,GID_TABS,
+ GA_RelVerify,TRUE,
+ GA_Underscore,13, // disable kb shortcuts
+ CLICKTAB_Labels,&g->shared->tab_list,
+ CLICKTAB_LabelTruncate,TRUE,
+ ClickTabEnd;
+
+ g->shared->objects[GID_ADDTAB] = ButtonObj,
+ GA_ID, GID_ADDTAB,
+ GA_RelVerify, TRUE,
+ GA_Text, "+",
+ BUTTON_RenderImage, g->shared->objects[GID_ADDTAB_BM],
+ ButtonEnd;
+ }
+ else
+ {
+ g->shared->objects[GID_TABS_FLAG] = BitMapObj,
+ BITMAP_SourceFile, tabthrobber,
+ BITMAP_Screen,scrn,
+ BITMAP_Masking,TRUE,
+ BitMapEnd;
+ }
+
+ LOG("Creating window object");
+
+ g->shared->objects[OID_MAIN] = WindowObj,
+ WA_ScreenTitle, ami_gui_get_screen_title(),
+ WA_Activate, TRUE,
+ WA_DepthGadget, TRUE,
+ WA_DragBar, TRUE,
+ WA_CloseGadget, TRUE,
+ WA_SizeGadget, TRUE,
+ WA_Top,cury,
+ WA_Left,curx,
+ WA_Width,curw,
+ WA_Height,curh,
+ WA_PubScreen,scrn,
+ WA_ReportMouse,TRUE,
+ refresh_mode, TRUE,
+ WA_SizeBBottom, TRUE,
+ WA_ContextMenuHook, g->shared->ctxmenu_hook,
+ WA_IDCMP, IDCMP_MENUPICK | IDCMP_MOUSEMOVE |
+ IDCMP_MOUSEBUTTONS | IDCMP_NEWSIZE |
+ IDCMP_RAWKEY | idcmp_sizeverify |
+ IDCMP_GADGETUP | IDCMP_IDCMPUPDATE |
+ IDCMP_REFRESHWINDOW |
+ IDCMP_ACTIVEWINDOW | IDCMP_EXTENDEDMOUSE,
+ WINDOW_IconifyGadget, iconifygadget,
+ WINDOW_MenuStrip, menu,
+ WINDOW_MenuUserData, WGUD_HOOK,
+ WINDOW_NewPrefsHook, &newprefs_hook,
+ WINDOW_IDCMPHook, &g->shared->scrollerhook,
+ WINDOW_IDCMPHookBits, IDCMP_IDCMPUPDATE | IDCMP_REFRESHWINDOW |
+ IDCMP_EXTENDEDMOUSE | IDCMP_SIZEVERIFY,
+ WINDOW_SharedPort, sport,
+ WINDOW_BuiltInScroll, TRUE,
+ WINDOW_GadgetHelp, TRUE,
+ WINDOW_UserData, g->shared,
+ WINDOW_ParentGroup, g->shared->objects[GID_MAIN] = LayoutVObj,
+ LAYOUT_DeferLayout, defer_layout,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_AddChild, g->shared->objects[GID_TOOLBARLAYOUT] = LayoutHObj,
+ LAYOUT_VertAlignment, LALIGN_CENTER,
+ LAYOUT_AddChild, g->shared->objects[GID_BACK] = ButtonObj,
+ GA_ID, GID_BACK,
+ GA_RelVerify, TRUE,
+ GA_Disabled, TRUE,
+ GA_ContextMenu, ami_ctxmenu_history_create(AMI_CTXMENU_HISTORY_BACK, g->shared),
+ GA_HintInfo, g->shared->helphints[GID_BACK],
+ BUTTON_RenderImage,BitMapObj,
+ BITMAP_SourceFile,nav_west,
+ BITMAP_SelectSourceFile,nav_west_s,
+ BITMAP_DisabledSourceFile,nav_west_g,
+ BITMAP_Screen,scrn,
+ BITMAP_Masking,TRUE,
+ BitMapEnd,
+ ButtonEnd,
+ CHILD_WeightedWidth,0,
+ CHILD_WeightedHeight,0,
+ LAYOUT_AddChild, g->shared->objects[GID_FORWARD] = ButtonObj,
+ GA_ID, GID_FORWARD,
+ GA_RelVerify, TRUE,
+ GA_Disabled, TRUE,
+ GA_ContextMenu, ami_ctxmenu_history_create(AMI_CTXMENU_HISTORY_FORWARD, g->shared),
+ GA_HintInfo, g->shared->helphints[GID_FORWARD],
+ BUTTON_RenderImage,BitMapObj,
+ BITMAP_SourceFile,nav_east,
+ BITMAP_SelectSourceFile,nav_east_s,
+ BITMAP_DisabledSourceFile,nav_east_g,
+ BITMAP_Screen,scrn,
+ BITMAP_Masking,TRUE,
+ BitMapEnd,
+ ButtonEnd,
+ CHILD_WeightedWidth,0,
+ CHILD_WeightedHeight,0,
+ LAYOUT_AddChild, g->shared->objects[GID_STOP] = ButtonObj,
+ GA_ID,GID_STOP,
+ GA_RelVerify,TRUE,
+ GA_HintInfo, g->shared->helphints[GID_STOP],
+ BUTTON_RenderImage,BitMapObj,
+ BITMAP_SourceFile,stop,
+ BITMAP_SelectSourceFile,stop_s,
+ BITMAP_DisabledSourceFile,stop_g,
+ BITMAP_Screen,scrn,
+ BITMAP_Masking,TRUE,
+ BitMapEnd,
+ ButtonEnd,
+ CHILD_WeightedWidth,0,
+ CHILD_WeightedHeight,0,
+ LAYOUT_AddChild, g->shared->objects[GID_RELOAD] = ButtonObj,
+ GA_ID,GID_RELOAD,
+ GA_RelVerify,TRUE,
+ GA_HintInfo, g->shared->helphints[GID_RELOAD],
+ BUTTON_RenderImage,BitMapObj,
+ BITMAP_SourceFile,reload,
+ BITMAP_SelectSourceFile,reload_s,
+ BITMAP_DisabledSourceFile,reload_g,
+ BITMAP_Screen,scrn,
+ BITMAP_Masking,TRUE,
+ BitMapEnd,
+ ButtonEnd,
+ CHILD_WeightedWidth,0,
+ CHILD_WeightedHeight,0,
+ LAYOUT_AddChild, g->shared->objects[GID_HOME] = ButtonObj,
+ GA_ID,GID_HOME,
+ GA_RelVerify,TRUE,
+ GA_HintInfo, g->shared->helphints[GID_HOME],
+ BUTTON_RenderImage,BitMapObj,
+ BITMAP_SourceFile,home,
+ BITMAP_SelectSourceFile,home_s,
+ BITMAP_DisabledSourceFile,home_g,
+ BITMAP_Screen,scrn,
+ BITMAP_Masking,TRUE,
+ BitMapEnd,
+ ButtonEnd,
+ CHILD_WeightedWidth,0,
+ CHILD_WeightedHeight,0,
+ LAYOUT_AddChild, LayoutHObj, // FavIcon, URL bar and hotlist star
+ LAYOUT_VertAlignment, LALIGN_CENTER,
+ LAYOUT_AddChild, g->shared->objects[GID_ICON] = SpaceObj,
+ GA_ID, GID_ICON,
+ SPACE_MinWidth, 16,
+ SPACE_MinHeight, 16,
+ SPACE_Transparent, TRUE,
+ // SPACE_RenderHook, &g->shared->favicon_hook,
+ SpaceEnd,
+ CHILD_WeightedWidth, 0,
+ CHILD_WeightedHeight, 0,
+ LAYOUT_AddChild, g->shared->objects[GID_URL] =
+#ifdef __amigaos4__
+ NewObject(urlStringClass, NULL,
+#else
+ StringObj,
+#endif
+ STRINGA_MaxChars, 2000,
+ GA_ID, GID_URL,
+ GA_RelVerify, TRUE,
+ GA_HintInfo, g->shared->helphints[GID_URL],
+ GA_TabCycle, TRUE,
+ STRINGA_Buffer, g->shared->svbuffer,
+#ifdef __amigaos4__
+ STRINGVIEW_Header, URLHistory_GetList(),
+#endif
+ TAG_DONE),
+ LAYOUT_AddChild, g->shared->objects[GID_FAVE] = ButtonObj,
+ GA_ID, GID_FAVE,
+ GA_RelVerify, TRUE,
+ // GA_HintInfo, g->shared->helphints[GID_FAVE],
+ BUTTON_RenderImage, g->shared->objects[GID_FAVE_ADD],
+ ButtonEnd,
+ CHILD_WeightedWidth, 0,
+ CHILD_WeightedHeight, 0,
+ LayoutEnd,
+ // GA_ID, GID_TOOLBARLAYOUT,
+ // GA_RelVerify, TRUE,
+ // LAYOUT_RelVerify, TRUE,
+ LAYOUT_WeightBar, TRUE,
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_VertAlignment, LALIGN_CENTER,
+ LAYOUT_AddChild, g->shared->objects[GID_SEARCH_ICON] = ChooserObj,
+ GA_ID, GID_SEARCH_ICON,
+ GA_RelVerify, TRUE,
+ CHOOSER_DropDown, TRUE,
+ CHOOSER_Labels, g->shared->web_search_list,
+ CHOOSER_MaxLabels, 40, /* Same as options GUI */
+ ChooserEnd,
+ CHILD_WeightedWidth,0,
+ CHILD_WeightedHeight,0,
+ LAYOUT_AddChild, g->shared->objects[GID_SEARCHSTRING] = StringObj,
+ GA_ID,GID_SEARCHSTRING,
+ STRINGA_TextVal, NULL,
+ GA_RelVerify,TRUE,
+ GA_HintInfo, g->shared->helphints[GID_SEARCHSTRING],
+ StringEnd,
+ LayoutEnd,
+ CHILD_WeightedWidth, nsoption_int(web_search_width),
+ LAYOUT_AddChild, g->shared->objects[GID_THROBBER] = SpaceObj,
+ GA_ID,GID_THROBBER,
+ SPACE_MinWidth,throbber_width,
+ SPACE_MinHeight,throbber_height,
+ SPACE_Transparent,TRUE,
+ // SPACE_RenderHook, &g->shared->throbber_hook,
+ SpaceEnd,
+ CHILD_WeightedWidth,0,
+ CHILD_WeightedHeight,0,
+ LayoutEnd,
+ CHILD_WeightedHeight,0,
+ LAYOUT_AddImage, BevelObj,
+ BEVEL_Style, BVS_SBAR_VERT,
+ BevelEnd,
+ CHILD_WeightedHeight, 0,
+ LAYOUT_AddChild, g->shared->objects[GID_HOTLISTLAYOUT] = LayoutVObj,
+ LAYOUT_SpaceInner, FALSE,
+ LayoutEnd,
+ CHILD_WeightedHeight,0,
+ LAYOUT_AddChild, g->shared->objects[GID_TABLAYOUT] = LayoutHObj,
+ LAYOUT_SpaceInner,FALSE,
+ addtabclosegadget, g->shared->objects[GID_CLOSETAB],
+ CHILD_WeightedWidth,0,
+ CHILD_WeightedHeight,0,
+
+ addtabclosegadget, g->shared->objects[GID_TABS],
+ CHILD_CacheDomain,FALSE,
+
+ addtabclosegadget, g->shared->objects[GID_ADDTAB],
+ CHILD_WeightedWidth,0,
+ CHILD_WeightedHeight,0,
+ LayoutEnd,
+ CHILD_WeightedHeight,0,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_AddChild, g->shared->objects[GID_VSCROLLLAYOUT] = LayoutHObj,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_AddChild, g->shared->objects[GID_HSCROLLLAYOUT] = LayoutVObj,
+ LAYOUT_AddChild, g->shared->objects[GID_BROWSER] = SpaceObj,
+ GA_ID,GID_BROWSER,
+ SPACE_Transparent,TRUE,
+ SpaceEnd,
+ EndGroup,
+ EndGroup,
+ EndGroup,
+#ifndef __amigaos4__
+ LAYOUT_AddChild, g->shared->objects[GID_STATUS] = StringObj,
+ GA_ID, GID_STATUS,
+ GA_ReadOnly, TRUE,
+ STRINGA_TextVal, NULL,
+ GA_RelVerify, TRUE,
+ StringEnd,
+#endif
+ EndGroup,
+ EndGroup,
+ EndWindow;
+ }
+ else
+ {
+ /* borderless kiosk mode window */
+ g->tab = 0;
+ g->shared->tabs = 0;
+ g->tab_node = NULL;
+
+ g->shared->objects[OID_MAIN] = WindowObj,
+ WA_ScreenTitle, ami_gui_get_screen_title(),
+ WA_Activate, TRUE,
+ WA_DepthGadget, FALSE,
+ WA_DragBar, FALSE,
+ WA_CloseGadget, FALSE,
+ WA_Borderless,TRUE,
+ WA_RMBTrap,TRUE,
+ WA_Top,0,
+ WA_Left,0,
+ WA_Width, scrn->Width,
+ WA_Height, scrn->Height,
+ WA_SizeGadget, FALSE,
+ WA_PubScreen, scrn,
+ WA_ReportMouse, TRUE,
+ refresh_mode, TRUE,
+ WA_IDCMP, IDCMP_MENUPICK | IDCMP_MOUSEMOVE |
+ IDCMP_MOUSEBUTTONS | IDCMP_NEWSIZE |
+ IDCMP_RAWKEY | IDCMP_REFRESHWINDOW |
+ IDCMP_GADGETUP | IDCMP_IDCMPUPDATE |
+ IDCMP_EXTENDEDMOUSE,
+ WINDOW_IDCMPHook,&g->shared->scrollerhook,
+ WINDOW_IDCMPHookBits, IDCMP_IDCMPUPDATE |
+ IDCMP_EXTENDEDMOUSE | IDCMP_REFRESHWINDOW,
+ WINDOW_SharedPort,sport,
+ WINDOW_UserData,g->shared,
+ WINDOW_BuiltInScroll,TRUE,
+ WINDOW_ParentGroup, g->shared->objects[GID_MAIN] = LayoutHObj,
+ LAYOUT_DeferLayout, defer_layout,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_AddChild, g->shared->objects[GID_VSCROLLLAYOUT] = LayoutHObj,
+ LAYOUT_AddChild, g->shared->objects[GID_HSCROLLLAYOUT] = LayoutVObj,
+ LAYOUT_AddChild, g->shared->objects[GID_BROWSER] = SpaceObj,
+ GA_ID,GID_BROWSER,
+ SPACE_Transparent,TRUE,
+ SpaceEnd,
+ EndGroup,
+ EndGroup,
+ EndGroup,
+ EndWindow;
+ }
+
+ LOG("Opening window");
+
+ g->shared->win = (struct Window *)RA_OpenWindow(g->shared->objects[OID_MAIN]);
+
+ LOG("Window opened, adding border gadgets");
+
+ if(!g->shared->win)
+ {
+ amiga_warn_user("NoMemory","");
+ FreeVec(g->shared);
+ FreeVec(g);
+ return NULL;
+ }
+
+ if(nsoption_bool(kiosk_mode) == false)
+ {
+#ifdef __amigaos4__
+ ULONG width, height;
+ struct DrawInfo *dri = GetScreenDrawInfo(scrn);
+
+ ami_get_border_gadget_size(g->shared,
+ (ULONG *)&width, (ULONG *)&height);
+
+ g->shared->objects[GID_STATUS] = NewObject(
+ NULL,
+ "frbuttonclass",
+ GA_ID, GID_STATUS,
+ GA_Left, scrn->WBorLeft + 2,
+ GA_RelBottom, scrn->WBorBottom - (height/2),
+ GA_BottomBorder, TRUE,
+ GA_Width, width,
+ GA_Height, 1 + height - scrn->WBorBottom,
+ GA_DrawInfo, dri,
+ GA_ReadOnly, TRUE,
+ GA_Disabled, TRUE,
+ GA_Image, (struct Image *)NewObject(
+ NULL,
+ "gaugeiclass",
+ GAUGEIA_Level, 0,
+ IA_Top, (int)(- ceil((scrn->WBorBottom + height) / 2)),
+ IA_Left, -4,
+ IA_Height, 2 + height - scrn->WBorBottom,
+ IA_Label, NULL,
+ IA_InBorder, TRUE,
+ IA_Screen, scrn,
+ TAG_DONE),
+ TAG_DONE);
+
+ AddGList(g->shared->win, (struct Gadget *)g->shared->objects[GID_STATUS],
+ (UWORD)~0, -1, NULL);
+
+ /* Apparently you can't set GA_Width on creation time for frbuttonclass */
+
+ SetGadgetAttrs((struct Gadget *)g->shared->objects[GID_STATUS],
+ g->shared->win, NULL,
+ GA_Width, width,
+ TAG_DONE);
+
+ RefreshGadgets((APTR)g->shared->objects[GID_STATUS],
+ g->shared->win, NULL);
+
+ FreeScreenDrawInfo(scrn, dri);
+#endif //__amigaos4__
+ ami_gui_hotlist_toolbar_add(g->shared); /* is this the right place for this? */
+ if(nsoption_bool(tab_always_show)) ami_toggletabbar(g->shared, true);
+ }
+
+ g->shared->gw = g;
+ cur_gw = g;
+
+ g->shared->appwin = AddAppWindowA((ULONG)g->shared->objects[OID_MAIN],
+ (ULONG)g->shared, g->shared->win, appport, NULL);
+
+ g->shared->node = AddObject(window_list,AMINS_WINDOW);
+ g->shared->node->objstruct = g->shared;
+
+ glob = &browserglob;
+
+ if(locked_screen) {
+ UnlockPubScreen(NULL,scrn);
+ locked_screen = FALSE;
+ }
+
+ ami_schedule(0, ami_gui_search_ico_refresh, NULL);
+
+ ScreenToFront(scrn);
+
+ return g;
+}
+
+static void ami_gui_close_tabs(struct gui_window_2 *gwin, bool other_tabs)
+{
+ struct Node *tab;
+ struct Node *ntab;
+ struct gui_window *gw;
+
+ if((gwin->tabs > 1) && (nsoption_bool(tab_close_warn) == true)) {
+ char *req_body = ami_utf8_easy(messages_get("MultiTabClose"));
+ int32 res = amiga_warn_user_multi(req_body, "Yes", "No", gwin->win);
+ free(req_body);
+
+ if(res == 0) return;
+ }
+
+ if(gwin->tabs) {
+ tab = GetHead(&gwin->tab_list);
+
+ do {
+ ntab=GetSucc(tab);
+ GetClickTabNodeAttrs(tab,
+ TNA_UserData,&gw,
+ TAG_DONE);
+
+ if((other_tabs == false) || (gwin->gw != gw)) {
+ browser_window_destroy(gw->bw);
+ }
+ } while((tab=ntab));
+ } else {
+ if(other_tabs == false) browser_window_destroy(gwin->gw->bw);
+ }
+}
+
+void ami_gui_close_window(struct gui_window_2 *gwin)
+{
+ ami_gui_close_tabs(gwin, false);
+}
+
+void ami_gui_close_inactive_tabs(struct gui_window_2 *gwin)
+{
+ ami_gui_close_tabs(gwin, true);
+}
+
+static void gui_window_destroy(struct gui_window *g)
+{
+ struct Node *ptab = NULL;
+ int gid;
+
+ if(!g) return;
+
+ if(g->shared->searchwin && (g->shared->searchwin->gwin == g))
+ {
+ ami_search_close();
+ win_destroyed = true;
+ }
+
+ if(g->hw)
+ {
+ ami_history_close(g->hw);
+ win_destroyed = true;
+ }
+
+ ami_free_download_list(&g->dllist);
+ FreeObjList(g->deferred_rects);
+ ami_misc_itempool_delete(g->deferred_rects_pool);
+ gui_window_stop_throbber(g);
+
+ cur_gw = NULL;
+
+ if(g->shared->tabs > 1) {
+ SetGadgetAttrs((struct Gadget *)g->shared->objects[GID_TABS],g->shared->win,NULL,
+ CLICKTAB_Labels,~0,
+ TAG_DONE);
+
+ GetAttr(CLICKTAB_CurrentNode, g->shared->objects[GID_TABS], (ULONG *)&ptab);
+
+ if(ptab == g->tab_node) {
+ ptab = GetSucc(g->tab_node);
+ if(!ptab) ptab = GetPred(g->tab_node);
+ }
+
+ Remove(g->tab_node);
+ FreeClickTabNode(g->tab_node);
+ RefreshSetGadgetAttrs((struct Gadget *)g->shared->objects[GID_TABS], g->shared->win, NULL,
+ CLICKTAB_Labels, &g->shared->tab_list,
+ CLICKTAB_CurrentNode, ptab,
+ TAG_DONE);
+
+ if(ClickTabBase->lib_Version < 53)
+ RethinkLayout((struct Gadget *)g->shared->objects[GID_TABLAYOUT],
+ g->shared->win, NULL, TRUE);
+
+ g->shared->tabs--;
+ ami_switch_tab(g->shared,true);
+ ami_schedule(0, ami_gui_refresh_favicon, g->shared);
+
+ if((g->shared->tabs == 1) && (nsoption_bool(tab_always_show) == false))
+ ami_toggletabbar(g->shared, false);
+
+ ami_utf8_free(g->tabtitle);
+
+ FreeVec(g);
+ return;
+ }
+
+ ami_plot_release_pens(g->shared->shared_pens);
+ FreeVec(g->shared->shared_pens);
+ ami_schedule_redraw_remove(g->shared);
+ ami_schedule(-1, ami_gui_refresh_favicon, g->shared);
+
+ DisposeObject(g->shared->objects[OID_MAIN]);
+ ami_gui_appicon_remove(g->shared);
+ if(g->shared->appwin) RemoveAppWindow(g->shared->appwin);
+ ami_gui_hotlist_toolbar_free(g->shared, &g->shared->hotlist_toolbar_list);
+
+ /* These aren't freed by the above.
+ * TODO: nav_west etc need freeing too? */
+ DisposeObject(g->shared->objects[GID_ADDTAB_BM]);
+ DisposeObject(g->shared->objects[GID_CLOSETAB_BM]);
+ DisposeObject(g->shared->objects[GID_TABS_FLAG]);
+ DisposeObject(g->shared->objects[GID_FAVE_ADD]);
+ DisposeObject(g->shared->objects[GID_FAVE_RMV]);
+
+ ami_gui_opts_websearch_free(g->shared->web_search_list);
+ if(g->shared->search_bm) DisposeObject(g->shared->search_bm);
+
+ /* This appears to be disposed along with the ClickTab object
+ if(g->shared->clicktab_ctxmenu) DisposeObject((Object *)g->shared->clicktab_ctxmenu); */
+ DisposeObject((Object *)g->shared->history_ctxmenu[AMI_CTXMENU_HISTORY_BACK]);
+ DisposeObject((Object *)g->shared->history_ctxmenu[AMI_CTXMENU_HISTORY_FORWARD]);
+ ami_ctxmenu_release_hook(g->shared->ctxmenu_hook);
+ ami_free_menulabs(g->shared);
+ ami_menu_free(g->shared);
+
+ free(g->shared->wintitle);
+ ami_utf8_free(g->shared->status);
+ FreeVec(g->shared->svbuffer);
+
+ for(gid = 0; gid < GID_LAST; gid++)
+ free(g->shared->helphints[gid]);
+
+ DelObject(g->shared->node);
+ if(g->tab_node) {
+ Remove(g->tab_node);
+ FreeClickTabNode(g->tab_node);
+ }
+ FreeVec(g); // g->shared should be freed by DelObject()
+
+ if(IsMinListEmpty(window_list))
+ {
+ /* last window closed, so exit */
+ ami_try_quit();
+ }
+
+ win_destroyed = true;
+}
+
+static void gui_window_set_title(struct gui_window *g, const char *title)
+{
+ struct Node *node;
+ char *utf8title;
+
+ if(!g) return;
+ if(!title) return;
+
+ utf8title = ami_utf8_easy((char *)title);
+
+ if(g->tab_node) // && (g->shared->tabs > 1))
+ {
+ node = g->tab_node;
+
+ if((g->tabtitle == NULL) || (strcmp(utf8title, g->tabtitle)))
+ {
+ SetGadgetAttrs((struct Gadget *)g->shared->objects[GID_TABS],
+ g->shared->win, NULL,
+ CLICKTAB_Labels, ~0,
+ TAG_DONE);
+
+ if(g->tabtitle) ami_utf8_free(g->tabtitle);
+ g->tabtitle = strdup(utf8title);
+
+ SetClickTabNodeAttrs(node, TNA_Text, g->tabtitle,
+ TNA_HintInfo, g->tabtitle,
+ TAG_DONE);
+
+ RefreshSetGadgetAttrs((struct Gadget *)g->shared->objects[GID_TABS],
+ g->shared->win, NULL,
+ CLICKTAB_Labels, &g->shared->tab_list,
+ TAG_DONE);
+
+ if(ClickTabBase->lib_Version < 53)
+ RethinkLayout((struct Gadget *)g->shared->objects[GID_TABLAYOUT],
+ g->shared->win, NULL, TRUE);
+ }
+ }
+
+ if(g == g->shared->gw) {
+ if((g->shared->wintitle == NULL) || (strcmp(utf8title, g->shared->wintitle)))
+ {
+ if(g->shared->wintitle) free(g->shared->wintitle);
+ g->shared->wintitle = strdup(utf8title);
+ SetWindowTitles(g->shared->win, g->shared->wintitle, ami_gui_get_screen_title());
+ }
+ }
+
+ ami_utf8_free(utf8title);
+}
+
+static void ami_redraw_callback(void *p)
+{
+ struct gui_window_2 *gwin = (struct gui_window_2 *)p;
+
+ if(gwin->redraw_required) {
+ ami_do_redraw(gwin);
+ }
+
+ ami_gui_window_update_box_deferred(gwin->gw, true);
+
+ if(gwin->gw->c_h)
+ {
+ gui_window_place_caret(gwin->gw, gwin->gw->c_x,
+ gwin->gw->c_y, gwin->gw->c_h, NULL);
+ }
+}
+
+/**
+ * Schedule a redraw of the browser window - Amiga-specific function
+ *
+ * \param gwin a struct gui_window_2
+ * \param full_redraw set to true to schedule a full redraw,
+ should only be set to false when called from gui_window_update_box()
+ */
+void ami_schedule_redraw(struct gui_window_2 *gwin, bool full_redraw)
+{
+ int ms = 1;
+
+ if(full_redraw) gwin->redraw_required = true;
+ ami_schedule(ms, ami_redraw_callback, gwin);
+}
+
+static void ami_schedule_redraw_remove(struct gui_window_2 *gwin)
+{
+ ami_schedule(-1, ami_redraw_callback, gwin);
+}
+
+static void gui_window_redraw_window(struct gui_window *g)
+{
+ if(!g) return;
+
+ if(g == g->shared->gw)
+ ami_schedule_redraw(g->shared, true);
+}
+
+static void ami_gui_window_update_box_deferred(struct gui_window *g, bool draw)
+{
+ struct nsObject *node;
+ struct nsObject *nnode;
+ struct rect *rect;
+
+ if(!g) return;
+ if(IsMinListEmpty(g->deferred_rects)) return;
+
+ if(draw == true) {
+ ami_set_pointer(g->shared, GUI_POINTER_WAIT, false);
+ } else {
+ LOG("Ignoring deferred box redraw queue");
+ }
+
+ node = (struct nsObject *)GetHead((struct List *)g->deferred_rects);
+
+ do {
+ if(draw == true) {
+ rect = (struct rect *)node->objstruct;
+ ami_do_redraw_limits(g, g->bw, false,
+ rect->x0, rect->y0, rect->x1, rect->y1);
+ }
+ nnode=(struct nsObject *)GetSucc((struct Node *)node);
+ ami_misc_itempool_free(g->deferred_rects_pool, node->objstruct, sizeof(struct rect));
+ DelObjectNoFree(node);
+ } while((node = nnode));
+
+ if(draw == true) ami_reset_pointer(g->shared);
+}
+
+static bool ami_gui_window_update_box_deferred_check(struct MinList *deferred_rects,
+ const struct rect *new_rect, APTR mempool)
+{
+ struct nsObject *node;
+ struct nsObject *nnode;
+ struct rect *rect;
+
+ if(IsMinListEmpty(deferred_rects)) return true;
+
+ node = (struct nsObject *)GetHead((struct List *)deferred_rects);
+
+ do {
+ nnode=(struct nsObject *)GetSucc((struct Node *)node);
+ rect = (struct rect *)node->objstruct;
+
+ if((rect->x0 <= new_rect->x0) &&
+ (rect->y0 <= new_rect->y0) &&
+ (rect->x1 >= new_rect->x1) &&
+ (rect->y1 >= new_rect->y1)) {
+ return false;
+ }
+
+ if ((new_rect->x0 <= rect->x0) &&
+ (new_rect->y0 <= rect->y0) &&
+ (new_rect->x1 >= rect->x1) &&
+ (new_rect->y1 >= rect->y1)) {
+ LOG("Removing queued redraw that is a subset of new box redraw");
+ ami_misc_itempool_free(mempool, node->objstruct, sizeof(struct rect));
+ DelObjectNoFree(node);
+ /* Don't return - we might find more */
+ }
+ } while((node = nnode));
+
+ return true;
+}
+
+static void gui_window_update_box(struct gui_window *g, const struct rect *rect)
+{
+ struct nsObject *nsobj;
+ struct rect *deferred_rect;
+ if(!g) return;
+
+ if(ami_gui_window_update_box_deferred_check(g->deferred_rects, rect,
+ g->deferred_rects_pool)) {
+ deferred_rect = ami_misc_itempool_alloc(g->deferred_rects_pool, sizeof(struct rect));
+ CopyMem(rect, deferred_rect, sizeof(struct rect));
+ nsobj = AddObject(g->deferred_rects, AMINS_RECT);
+ nsobj->objstruct = deferred_rect;
+ } else {
+ LOG("Ignoring duplicate or subset of queued box redraw");
+ }
+ ami_schedule_redraw(g->shared, false);
+}
+
+/**
+ * callback from core to reformat a window.
+ */
+static void amiga_window_reformat(struct gui_window *gw)
+{
+ struct IBox *bbox;
+
+ LOG("reformat window %p", gw);
+
+ if (gw != NULL) {
+ if(ami_gui_get_space_box((Object *)gw->shared->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return;
+ }
+ browser_window_reformat(gw->bw, false, bbox->Width, bbox->Height);
+ gw->shared->redraw_scroll = false;
+ ami_gui_free_space_box(bbox);
+ }
+}
+
+static void ami_do_redraw(struct gui_window_2 *gwin)
+{
+ ULONG hcurrent,vcurrent,xoffset,yoffset,width=800,height=600;
+ struct IBox *bbox;
+ ULONG oldh = gwin->oldh, oldv=gwin->oldv;
+ struct RastPort *temprp;
+
+ if(browser_window_redraw_ready(gwin->gw->bw) == false) return;
+
+ ami_get_hscroll_pos(gwin, (ULONG *)&hcurrent);
+ ami_get_vscroll_pos(gwin, (ULONG *)&vcurrent);
+
+ gwin->gw->scrollx = hcurrent;
+ gwin->gw->scrolly = vcurrent;
+
+ if(ami_gui_get_space_box((Object *)gwin->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return;
+ }
+
+ width=bbox->Width;
+ height=bbox->Height;
+ xoffset=bbox->Left;
+ yoffset=bbox->Top;
+
+ if(gwin->redraw_scroll)
+ {
+ if((abs(vcurrent-oldv) > height) || (abs(hcurrent-oldh) > width))
+ gwin->redraw_scroll = false;
+
+ if(gwin->new_content) gwin->redraw_scroll = false;
+// if(gwin->gw->scale != 1.0) gwin->redraw_scroll = false;
+ }
+
+ if(gwin->redraw_scroll)
+ {
+ struct rect rect;
+
+ gwin->gw->c_h_temp = gwin->gw->c_h;
+ gui_window_remove_caret(gwin->gw);
+
+ ScrollWindowRaster(gwin->win, hcurrent - oldh, vcurrent - oldv,
+ xoffset, yoffset, xoffset + width - 1, yoffset + height - 1);
+
+ gwin->gw->c_h = gwin->gw->c_h_temp;
+
+ if(vcurrent>oldv) /* Going down */
+ {
+ ami_spacebox_to_ns_coords(gwin, &rect.x0, &rect.y0, 0, height - (vcurrent - oldv) - 1);
+ ami_spacebox_to_ns_coords(gwin, &rect.x1, &rect.y1, width + 1, height + 1);
+ gui_window_update_box(gwin->gw, &rect);
+ }
+ else if(vcurrent<oldv) /* Going up */
+ {
+ ami_spacebox_to_ns_coords(gwin, &rect.x0, &rect.y0, 0, 0);
+ ami_spacebox_to_ns_coords(gwin, &rect.x1, &rect.y1, width + 1, oldv - vcurrent + 1);
+ gui_window_update_box(gwin->gw, &rect);
+ }
+
+ if(hcurrent>oldh) /* Going right */
+ {
+ ami_spacebox_to_ns_coords(gwin, &rect.x0, &rect.y0, width - (hcurrent - oldh), 0);
+ ami_spacebox_to_ns_coords(gwin, &rect.x1, &rect.y1, width + 1, height + 1);
+ gui_window_update_box(gwin->gw, &rect);
+ }
+ else if(hcurrent<oldh) /* Going left */
+ {
+ ami_spacebox_to_ns_coords(gwin, &rect.x0, &rect.y0, 0, 0);
+ ami_spacebox_to_ns_coords(gwin, &rect.x1, &rect.y1, oldh - hcurrent + 1, height + 1);
+ gui_window_update_box(gwin->gw, &rect);
+ }
+ }
+ else
+ {
+ struct rect clip;
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &amiplot
+ };
+
+ glob = &browserglob;
+
+ if(nsoption_bool(direct_render) == false)
+ {
+ ami_do_redraw_tiled(gwin, true, hcurrent, vcurrent, width, height, hcurrent, vcurrent, bbox, &ctx);
+ }
+ else
+ {
+ browserglob.shared_pens = gwin->shared_pens;
+ temprp = browserglob.rp;
+ browserglob.rp = gwin->win->RPort;
+ clip.x0 = bbox->Left;
+ clip.y0 = bbox->Top;
+ clip.x1 = bbox->Left + bbox->Width;
+ clip.y1 = bbox->Top + bbox->Height;
+
+ ami_set_pointer(gwin, GUI_POINTER_WAIT, false);
+
+ if(browser_window_redraw(gwin->gw->bw, clip.x0 - hcurrent, clip.y0 - vcurrent, &clip, &ctx))
+ {
+ ami_clearclipreg(&browserglob);
+ browserglob.rp = temprp;
+ }
+
+ ami_reset_pointer(gwin);
+ }
+ /* Tell NetSurf not to bother with the next queued box redraw, as we've redrawn everything. */
+ ami_gui_window_update_box_deferred(gwin->gw, false);
+ }
+
+ ami_update_buttons(gwin);
+
+ gwin->oldh = hcurrent;
+ gwin->oldv = vcurrent;
+
+ gwin->redraw_scroll = false;
+ gwin->redraw_required = false;
+ gwin->new_content = false;
+
+ ami_gui_free_space_box(bbox);
+}
+
+void ami_get_hscroll_pos(struct gui_window_2 *gwin, ULONG *xs)
+{
+ if(gwin->objects[GID_HSCROLL])
+ {
+ GetAttr(SCROLLER_Top, (Object *)gwin->objects[GID_HSCROLL], xs);
+ } else {
+ *xs = 0;
+ }
+
+ *xs /= gwin->gw->scale;
+}
+
+void ami_get_vscroll_pos(struct gui_window_2 *gwin, ULONG *ys)
+{
+ if(gwin->objects[GID_VSCROLL]) {
+ GetAttr(SCROLLER_Top, gwin->objects[GID_VSCROLL], ys);
+ } else {
+ *ys = 0;
+ }
+
+ *ys /= gwin->gw->scale;
+}
+
+static bool gui_window_get_scroll(struct gui_window *g, int *sx, int *sy)
+{
+ ami_get_hscroll_pos(g->shared, (ULONG *)sx);
+ ami_get_vscroll_pos(g->shared, (ULONG *)sy);
+
+ return true;
+}
+
+static void gui_window_set_scroll(struct gui_window *g, int sx, int sy)
+{
+ struct IBox *bbox;
+ int width, height;
+
+ if(!g) return;
+ if(!g->bw || browser_window_has_content(g->bw) == false) return;
+
+ if(ami_gui_get_space_box((Object *)g->shared->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return;
+ }
+
+ if(sx < 0) sx=0;
+ if(sy < 0) sy=0;
+
+ browser_window_get_extents(g->bw, false, &width, &height);
+
+ if(sx >= width - bbox->Width)
+ sx = width - bbox->Width;
+ if(sy >= height - bbox->Height)
+ sy = height - bbox->Height;
+
+ if(width <= bbox->Width) sx = 0;
+ if(height <= bbox->Height) sy = 0;
+
+ ami_gui_free_space_box(bbox);
+
+ if(g == g->shared->gw) {
+ if(g->shared->objects[GID_VSCROLL]) {
+ RefreshSetGadgetAttrs((struct Gadget *)(APTR)g->shared->objects[GID_VSCROLL],
+ g->shared->win, NULL,
+ SCROLLER_Top, (ULONG)(sy * g->scale),
+ TAG_DONE);
+ }
+
+ if(g->shared->objects[GID_HSCROLL])
+ {
+ RefreshSetGadgetAttrs((struct Gadget *)(APTR)g->shared->objects[GID_HSCROLL],
+ g->shared->win, NULL,
+ SCROLLER_Top, (ULONG)(sx * g->scale),
+ TAG_DONE);
+ }
+
+ ami_schedule_redraw(g->shared, true);
+
+ if(nsoption_bool(faster_scroll) == true) g->shared->redraw_scroll = true;
+ else g->shared->redraw_scroll = false;
+
+ g->scrollx = sx;
+ g->scrolly = sy;
+ }
+}
+
+static void gui_window_update_extent(struct gui_window *g)
+{
+ struct IBox *bbox;
+
+ if(!g || !g->bw) return;
+ if(browser_window_has_content(g->bw) == false) return;
+
+ if(g == g->shared->gw) {
+ int width, height;
+ if(ami_gui_get_space_box((Object *)g->shared->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return;
+ }
+
+ if(g->shared->objects[GID_VSCROLL]) {
+ browser_window_get_extents(g->bw, true, &width, &height);
+ RefreshSetGadgetAttrs((struct Gadget *)(APTR)g->shared->objects[GID_VSCROLL],g->shared->win,NULL,
+ SCROLLER_Total, (ULONG)(height),
+ SCROLLER_Visible, bbox->Height,
+ TAG_DONE);
+ }
+
+ if(g->shared->objects[GID_HSCROLL])
+ {
+ browser_window_get_extents(g->bw, true, &width, &height);
+ RefreshSetGadgetAttrs((struct Gadget *)(APTR)g->shared->objects[GID_HSCROLL],
+ g->shared->win, NULL,
+ SCROLLER_Total, (ULONG)(width),
+ SCROLLER_Visible, bbox->Width,
+ TAG_DONE);
+ }
+
+ ami_gui_free_space_box(bbox);
+ }
+
+ ami_gui_scroller_update(g->shared);
+ g->shared->new_content = true;
+}
+
+static void gui_window_set_status(struct gui_window *g, const char *text)
+{
+ char *utf8text;
+ ULONG size;
+ UWORD chars;
+ struct TextExtent textex;
+
+ if(!g) return;
+ if(!text) return;
+ if(!g->shared->objects[GID_STATUS]) return;
+
+ if(g == g->shared->gw) {
+ utf8text = ami_utf8_easy((char *)text);
+ if(utf8text == NULL) return;
+
+ GetAttr(GA_Width, g->shared->objects[GID_STATUS], (ULONG *)&size);
+ chars = TextFit(&scrn->RastPort, utf8text, (UWORD)strlen(utf8text),
+ &textex, NULL, 1, size - 4, scrn->RastPort.TxHeight);
+
+ utf8text[chars] = 0;
+
+ SetGadgetAttrs((struct Gadget *)g->shared->objects[GID_STATUS],
+ g->shared->win, NULL,
+ NSA_STATUS_TEXT, utf8text,
+ TAG_DONE);
+
+ RefreshGList((struct Gadget *)g->shared->objects[GID_STATUS],
+ g->shared->win, NULL, 1);
+
+ if(g->shared->status) ami_utf8_free(g->shared->status);
+ g->shared->status = utf8text;
+ }
+}
+
+static nserror gui_window_set_url(struct gui_window *g, nsurl *url)
+{
+ size_t idn_url_l;
+ char *idn_url_s = NULL;
+ char *url_lc = NULL;
+
+ if(!g) return NSERROR_OK;
+
+ if(g == g->shared->gw) {
+ if(nsoption_bool(display_decoded_idn) == true) {
+ if (nsurl_get_utf8(url, &idn_url_s, &idn_url_l) == NSERROR_OK) {
+ url_lc = ami_utf8_easy(idn_url_s);
+ }
+ }
+
+ RefreshSetGadgetAttrs((struct Gadget *)g->shared->objects[GID_URL],
+ g->shared->win, NULL,
+ STRINGA_TextVal, url_lc ? url_lc : nsurl_access(url),
+ TAG_DONE);
+
+ if(url_lc) {
+ ami_utf8_free(url_lc);
+ if(idn_url_s) free(idn_url_s);
+ }
+ }
+
+ ami_update_buttons(g->shared);
+
+ return NSERROR_OK;
+}
+
+HOOKF(uint32, ami_set_favicon_render_hook, APTR, space, struct gpRender *)
+{
+ ami_schedule(0, ami_gui_refresh_favicon, hook->h_Data);
+ return 0;
+}
+
+/**
+ * Gui callback when search provider details are updated.
+ *
+ * \param provider_name The providers name.
+ * \param ico_bitmap The icon bitmap representing the provider.
+ * \return NSERROR_OK on success else error code.
+ */
+static nserror gui_search_web_provider_update(const char *provider_name,
+ struct bitmap *ico_bitmap)
+{
+ struct BitMap *bm = NULL;
+ struct nsObject *node;
+ struct nsObject *nnode;
+ struct gui_window_2 *gwin;
+
+ if(IsMinListEmpty(window_list)) return NSERROR_BAD_PARAMETER;
+ if(nsoption_bool(kiosk_mode) == true) return NSERROR_BAD_PARAMETER;
+
+ if (ico_bitmap != NULL) {
+ bm = ami_bitmap_get_native(ico_bitmap, 16, 16, NULL);
+ }
+
+ if(bm == NULL) return NSERROR_BAD_PARAMETER;
+
+ node = (struct nsObject *)GetHead((struct List *)window_list);
+
+ do {
+ nnode=(struct nsObject *)GetSucc((struct Node *)node);
+ gwin = node->objstruct;
+
+ if(node->Type == AMINS_WINDOW)
+ {
+ if(gwin->search_bm != NULL)
+ DisposeObject(gwin->search_bm);
+
+ ULONG bm_masking_tag = TAG_IGNORE;
+
+ if(LIB_IS_AT_LEAST((struct Library *)ChooserBase, 53, 21)) {
+ /* Broken in earlier versions */
+ bm_masking_tag = BITMAP_Masking;
+ }
+
+ gwin->search_bm = BitMapObj,
+ BITMAP_Screen, scrn,
+ BITMAP_Width, 16,
+ BITMAP_Height, 16,
+ BITMAP_BitMap, bm,
+ BITMAP_HasAlpha, TRUE,
+ bm_masking_tag, TRUE,
+ BitMapEnd;
+
+ RefreshSetGadgetAttrs((struct Gadget *)gwin->objects[GID_SEARCH_ICON],
+ gwin->win, NULL,
+ GA_HintInfo, provider_name,
+ GA_Image, gwin->search_bm,
+ TAG_DONE);
+ }
+ } while((node = nnode));
+
+ return NSERROR_OK;
+}
+
+HOOKF(uint32, ami_set_throbber_render_hook, APTR, space, struct gpRender *)
+{
+ struct gui_window_2 *gwin = hook->h_Data;
+ ami_throbber_redraw_schedule(0, gwin->gw);
+ return 0;
+}
+
+static void gui_window_place_caret(struct gui_window *g, int x, int y, int height,
+ const struct rect *clip)
+{
+ struct IBox *bbox;
+ int xs,ys;
+
+ if(!g) return;
+
+ gui_window_remove_caret(g);
+
+ xs = g->scrollx;
+ ys = g->scrolly;
+
+ SetAPen(g->shared->win->RPort,3);
+
+ if(ami_gui_get_space_box((Object *)g->shared->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return;
+ }
+
+ if((y-ys+height) > (bbox->Height)) height = bbox->Height-y+ys;
+
+ if(((x-xs) <= 0) || ((x-xs+2) >= (bbox->Width)) || ((y-ys) <= 0) || ((y-ys) >= (bbox->Height))) {
+ ami_gui_free_space_box(bbox);
+ return;
+ }
+
+ g->c_w = 2;
+
+ SetDrMd(g->shared->win->RPort,COMPLEMENT);
+ RectFill(g->shared->win->RPort, x + bbox->Left - xs, y + bbox->Top - ys,
+ x + bbox->Left + g->c_w - xs, y+bbox->Top + height - ys);
+ SetDrMd(g->shared->win->RPort,JAM1);
+
+ ami_gui_free_space_box(bbox);
+
+ g->c_x = x;
+ g->c_y = y;
+ g->c_h = height;
+
+ if((nsoption_bool(kiosk_mode) == false))
+ OnMenu(g->shared->win, AMI_MENU_PASTE);
+}
+
+static void gui_window_remove_caret(struct gui_window *g)
+{
+ if(!g) return;
+ if(g->c_h == 0) return;
+
+ if((nsoption_bool(kiosk_mode) == false))
+ OffMenu(g->shared->win, AMI_MENU_PASTE);
+
+ ami_do_redraw_limits(g, g->bw, false, g->c_x, g->c_y,
+ g->c_x + g->c_w + 1, g->c_y + g->c_h + 1);
+
+ g->c_h = 0;
+}
+
+static void gui_window_new_content(struct gui_window *g)
+{
+ hlcache_handle *c;
+
+ if(g && g->shared && g->bw && browser_window_has_content(g->bw))
+ c = browser_window_get_content(g->bw);
+ else return;
+
+ ami_clearclipreg(&browserglob);
+ g->shared->new_content = true;
+ g->scrollx = 0;
+ g->scrolly = 0;
+ g->shared->oldh = 0;
+ g->shared->oldv = 0;
+ g->favicon = NULL;
+ ami_plot_release_pens(g->shared->shared_pens);
+ ami_menu_update_disabled(g, c);
+ ami_gui_update_hotlist_button(g->shared);
+ ami_gui_scroller_update(g->shared);
+}
+
+static bool gui_window_drag_start(struct gui_window *g, gui_drag_type type,
+ const struct rect *rect)
+{
+#ifdef __amigaos4__
+ g->shared->drag_op = type;
+ if(rect) g->shared->ptr_lock = ami_ns_rect_to_ibox(g->shared, rect);
+
+ if(type == GDRAGGING_NONE)
+ {
+ SetWindowAttrs(g->shared->win, WA_GrabFocus, 0,
+ WA_MouseLimits, NULL, TAG_DONE);
+
+ if(g->shared->ptr_lock)
+ {
+ FreeVec(g->shared->ptr_lock);
+ g->shared->ptr_lock = NULL;
+ }
+ }
+#endif
+ return true;
+}
+
+/* return the text box at posn x,y in window coordinates
+ x,y are updated to be document co-ordinates */
+
+bool ami_text_box_at_point(struct gui_window_2 *gwin, ULONG *x, ULONG *y)
+{
+ struct IBox *bbox;
+ ULONG xs, ys;
+ struct browser_window_features data;
+
+ if(ami_gui_get_space_box((Object *)gwin->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return false;
+ }
+
+ ami_get_hscroll_pos(gwin, (ULONG *)&xs);
+ *x = *x - (bbox->Left) +xs;
+
+ ami_get_vscroll_pos(gwin, (ULONG *)&ys);
+ *y = *y - (bbox->Top) + ys;
+
+ ami_gui_free_space_box(bbox);
+
+ browser_window_get_features(gwin->gw->bw, *x, *y, &data);
+
+ if (data.form_features == CTX_FORM_TEXT)
+ return true;
+
+ return false;
+}
+
+BOOL ami_gadget_hit(Object *obj, int x, int y)
+{
+ int top, left, width, height;
+
+ GetAttrs(obj,
+ GA_Left, &left,
+ GA_Top, &top,
+ GA_Width, &width,
+ GA_Height, &height,
+ TAG_DONE);
+
+ if((x >= left) && (x <= (left + width)) && (y >= top) && (y <= (top + height)))
+ return TRUE;
+ else return FALSE;
+}
+
+Object *ami_gui_splash_open(void)
+{
+ Object *win_obj, *bm_obj;
+ struct Window *win;
+ struct Screen *wbscreen = LockPubScreen("Workbench");
+ uint32 top = 0, left = 0;
+ STRPTR ver_string;
+ struct TextAttr tattr;
+ struct TextFont *tfont;
+
+ win_obj = WindowObj,
+#ifdef __amigaos4__
+ WA_ToolBox, TRUE,
+#endif
+ WA_Borderless, TRUE,
+ WA_BusyPointer, TRUE,
+ WINDOW_Position, WPOS_CENTERSCREEN,
+ WINDOW_LockWidth, TRUE,
+ WINDOW_LockHeight, TRUE,
+ WINDOW_ParentGroup, LayoutVObj,
+ LAYOUT_AddImage, bm_obj = BitMapObj,
+ BITMAP_SourceFile, "PROGDIR:Resources/splash.png",
+ BITMAP_Screen, wbscreen,
+ BITMAP_Precision, PRECISION_IMAGE,
+ BitMapEnd,
+ LayoutEnd,
+ EndWindow;
+
+ LOG("Attempting to open splash window...");
+ win = RA_OpenWindow(win_obj);
+
+ GetAttrs(bm_obj, IA_Top, &top,
+ IA_Left, &left,
+ TAG_DONE);
+
+ SetDrMd(win->RPort, JAM1);
+#ifdef __amigaos4__
+ SetRPAttrs(win->RPort, RPTAG_APenColor, 0xFF3F6DFE, TAG_DONE);
+ tattr.ta_Name = "DejaVu Serif Italic.font";
+#else
+ SetAPen(win->RPort, 3); /* Pen 3 is usually blue */
+ tattr.ta_Name = "ruby.font";
+#endif
+ tattr.ta_YSize = 24;
+ tattr.ta_Style = 0;
+ tattr.ta_Flags = 0;
+
+ if((tfont = ami_font_open_disk_font(&tattr)))
+ {
+ SetFont(win->RPort, tfont);
+ }
+ else
+ {
+ tattr.ta_Name = "DejaVu Serif Oblique.font";
+ if((tfont = ami_font_open_disk_font(&tattr)))
+ SetFont(win->RPort, tfont);
+ }
+
+ Move(win->RPort, left + 5, top + 25);
+ Text(win->RPort, "Initialising...", strlen("Initialising..."));
+
+ if(tfont) ami_font_close_disk_font(tfont);
+
+#ifdef __amigaos4__
+ tattr.ta_Name = "DejaVu Sans.font";
+#else
+ tattr.ta_Name = "helvetica.font";
+#endif
+ tattr.ta_YSize = 16;
+ tattr.ta_Style = 0;
+ tattr.ta_Flags = 0;
+
+ if((tfont = ami_font_open_disk_font(&tattr)))
+ SetFont(win->RPort, tfont);
+
+ ver_string = ASPrintf("%s", netsurf_version);
+
+ Move(win->RPort, left + 185, top + 220);
+ Text(win->RPort, ver_string, strlen(ver_string));
+
+ if(ver_string) FreeVec(ver_string);
+ if(tfont) ami_font_close_disk_font(tfont);
+
+ UnlockPubScreen(NULL, wbscreen);
+
+ return win_obj;
+}
+
+void ami_gui_splash_close(Object *win_obj)
+{
+ if(win_obj == NULL) return;
+
+ LOG("Closing splash window");
+ DisposeObject(win_obj);
+}
+
+static void gui_file_gadget_open(struct gui_window *g, hlcache_handle *hl,
+ struct form_control *gadget)
+{
+ LOG("File open dialog request for %p/%p", g, gadget);
+
+ if(AslRequestTags(filereq,
+ ASLFR_Window, g->shared->win,
+ ASLFR_SleepWindow, TRUE,
+ ASLFR_TitleText, messages_get("NetSurf"),
+ ASLFR_Screen, scrn,
+ ASLFR_DoSaveMode, FALSE,
+ TAG_DONE)) {
+ char fname[1024];
+ strlcpy(fname, filereq->fr_Drawer, 1024);
+ AddPart(fname, filereq->fr_File, 1024);
+ browser_window_set_gadget_filename(g->bw, gadget, fname);
+ }
+}
+
+/* exported function documented in amiga/gui.h */
+uint32 ami_gui_get_app_id(void)
+{
+ return ami_appid;
+}
+
+static struct gui_window_table amiga_window_table = {
+ .create = gui_window_create,
+ .destroy = gui_window_destroy,
+ .redraw = gui_window_redraw_window,
+ .update = gui_window_update_box,
+ .get_scroll = gui_window_get_scroll,
+ .set_scroll = gui_window_set_scroll,
+ .get_dimensions = gui_window_get_dimensions,
+ .update_extent = gui_window_update_extent,
+ .reformat = amiga_window_reformat,
+
+ .set_icon = gui_window_set_icon,
+ .set_title = gui_window_set_title,
+ .set_url = gui_window_set_url,
+ .set_status = gui_window_set_status,
+ .place_caret = gui_window_place_caret,
+ .remove_caret = gui_window_remove_caret,
+ .drag_start = gui_window_drag_start,
+ .new_content = gui_window_new_content,
+ .create_form_select_menu = gui_create_form_select_menu,
+ .file_gadget_open = gui_file_gadget_open,
+ .drag_save_object = gui_drag_save_object,
+ .drag_save_selection =gui_drag_save_selection,
+ .start_selection = gui_start_selection,
+
+ /* from theme */
+ .set_pointer = gui_window_set_pointer,
+ .start_throbber = gui_window_start_throbber,
+ .stop_throbber = gui_window_stop_throbber,
+
+ /* from download */
+ .save_link = gui_window_save_link,
+};
+
+
+static struct gui_fetch_table amiga_fetch_table = {
+ .filetype = fetch_filetype,
+
+ .get_resource_url = gui_get_resource_url,
+};
+
+static struct gui_search_web_table amiga_search_web_table = {
+ .provider_update = gui_search_web_provider_update,
+};
+
+static struct gui_misc_table amiga_misc_table = {
+ .schedule = ami_schedule,
+ .warning = amiga_warn_user,
+
+ .quit = gui_quit,
+ .launch_url = gui_launch_url,
+ .cert_verify = gui_cert_verify,
+ .login = gui_401login_open,
+};
+
+/** Normal entry point from OS */
+int main(int argc, char** argv)
+{
+ setbuf(stderr, NULL);
+ char messages[100];
+ char script[1024];
+ char temp[1024];
+ STRPTR current_user_cache = NULL;
+ BPTR lock = 0;
+ int32 user = 0;
+ nserror ret;
+ int nargc = 0;
+ char *nargv = NULL;
+
+ struct netsurf_table amiga_table = {
+ .misc = &amiga_misc_table,
+ .window = &amiga_window_table,
+ .clipboard = amiga_clipboard_table,
+ .download = amiga_download_table,
+ .fetch = &amiga_fetch_table,
+ .file = amiga_file_table,
+ .utf8 = amiga_utf8_table,
+ .search = amiga_search_table,
+ .search_web = &amiga_search_web_table,
+ .llcache = filesystem_llcache_table,
+ .bitmap = amiga_bitmap_table,
+ .layout = ami_layout_table,
+ };
+
+#ifdef __amigaos4__
+ signal(SIGINT, SIG_IGN);
+#endif
+ ret = netsurf_register(&amiga_table);
+ if (ret != NSERROR_OK) {
+ ami_misc_fatal_error("NetSurf operation table failed registration");
+ return RETURN_FAIL;
+ }
+
+ /* initialise logging. Not fatal if it fails but not much we
+ * can do about it either.
+ */
+ nslog_init(NULL, &argc, argv);
+
+ /* Need to do this before opening any splash windows etc... */
+ if ((ami_libs_open() == false)) {
+ return RETURN_FAIL;
+ }
+
+ /* Open splash window */
+ Object *splash_window = ami_gui_splash_open();
+
+ ami_object_init();
+
+ if (ami_open_resources() == false) { /* alloc message ports */
+ ami_misc_fatal_error("Unable to allocate resources");
+ ami_gui_splash_close(splash_window);
+ ami_libs_close();
+ return RETURN_FAIL;
+ }
+
+ if(ami_schedule_create(schedulermsgport) != NSERROR_OK) {
+ ami_misc_fatal_error("Failed to initialise scheduler");
+ ami_gui_splash_close(splash_window);
+ ami_libs_close();
+ return RETURN_FAIL;
+ }
+
+ ami_gui_read_all_tooltypes(argc, argv);
+ struct RDArgs *args = ami_gui_commandline(&argc, argv, &nargc, &nargv);
+
+ if(current_user == NULL) {
+ user = GetVar("user", temp, 1024, GVF_GLOBAL_ONLY);
+ current_user = ASPrintf("%s", (user == -1) ? "Default" : temp);
+ }
+ LOG("User: %s", current_user);
+
+ if(users_dir == NULL) {
+ users_dir = ASPrintf("%s", USERS_DIR);
+ if(users_dir == NULL) {
+ ami_misc_fatal_error("Failed to allocate memory");
+ ami_schedule_free();
+ ami_gui_splash_close(splash_window);
+ ami_libs_close();
+ return RETURN_FAIL;
+ }
+ }
+
+ if(LIB_IS_AT_LEAST((struct Library *)DOSBase, 51, 96)) {
+#ifdef __amigaos4__
+ struct InfoData *infodata = AllocDosObject(DOS_INFODATA, 0);
+ if(infodata == NULL) {
+ ami_misc_fatal_error("Failed to allocate memory");
+ ami_schedule_free();
+ ami_gui_splash_close(splash_window);
+ ami_libs_close();
+ return RETURN_FAIL;
+ }
+ GetDiskInfoTags(GDI_StringNameInput, users_dir,
+ GDI_InfoData, infodata,
+ TAG_DONE);
+ if(infodata->id_DiskState == ID_DISKSTATE_WRITE_PROTECTED) {
+ FreeDosObject(DOS_INFODATA, infodata);
+ ami_misc_fatal_error("User directory MUST be on a writeable volume");
+ ami_schedule_free();
+ ami_gui_splash_close(splash_window);
+ ami_libs_close();
+ return RETURN_FAIL;
+ }
+ FreeDosObject(DOS_INFODATA, infodata);
+#else
+#warning FIXME for OS3 and older OS4
+#endif
+ } else {
+//TODO: check volume write status using old API
+ }
+
+ int len = strlen(current_user);
+ len += strlen(users_dir);
+ len += 2; /* for poss path sep and NULL term */
+
+ current_user_dir = AllocVecTagList(len, NULL);
+ if(current_user_dir == NULL) {
+ ami_misc_fatal_error("Failed to allocate memory");
+ ami_schedule_free();
+ ami_gui_splash_close(splash_window);
+ ami_libs_close();
+ return RETURN_FAIL;
+ }
+
+ strlcpy(current_user_dir, users_dir, len);
+ AddPart(current_user_dir, current_user, len);
+ FreeVec(users_dir);
+ LOG("User dir: %s", current_user_dir);
+
+ if((lock = CreateDirTree(current_user_dir)))
+ UnLock(lock);
+
+ current_user_options = ASPrintf("%s/Choices", current_user_dir);
+ current_user_cache = ASPrintf("%s/Cache", current_user_dir);
+ current_user_faviconcache = ASPrintf("%s/IconCache", current_user_dir);
+
+ if((lock = CreateDirTree(current_user_cache))) UnLock(lock);
+ if((lock = CreateDirTree(current_user_faviconcache))) UnLock(lock);
+
+ ami_mime_init("PROGDIR:Resources/mimetypes");
+ sprintf(temp, "%s/mimetypes.user", current_user_dir);
+ ami_mime_init(temp);
+
+#ifdef __amigaos4__
+ amiga_plugin_hack_init();
+
+ /* DataTypes loader needs datatypes.library v45,
+ * but for some reason that's not in OS3.9.
+ * Skip it to ensure it isn't causing other problems. */
+ ret = amiga_datatypes_init();
+#endif
+
+ /* user options setup */
+ ret = nsoption_init(ami_set_options, &nsoptions, &nsoptions_default);
+ if (ret != NSERROR_OK) {
+ ami_misc_fatal_error("Options failed to initialise");
+ ami_schedule_free();
+ ami_gui_splash_close(splash_window);
+ ami_libs_close();
+ return RETURN_FAIL;
+ }
+ nsoption_read(current_user_options, NULL);
+ if(args != NULL) {
+ nsoption_commandline(&nargc, &nargv, NULL);
+ FreeArgs(args);
+ }
+
+ if (ami_locate_resource(messages, "Messages") == false) {
+ ami_misc_fatal_error("Cannot open Messages file");
+ ami_schedule_free();
+ ami_gui_splash_close(splash_window);
+ ami_libs_close();
+ return RETURN_FAIL;
+ }
+
+ ret = messages_add_from_file(messages);
+
+ ret = netsurf_init(current_user_cache);
+ if (ret != NSERROR_OK) {
+ ami_misc_fatal_error("NetSurf failed to initialise");
+ ami_schedule_free();
+ ami_gui_splash_close(splash_window);
+ ami_libs_close();
+ return RETURN_FAIL;
+ }
+
+ if(current_user_cache != NULL) FreeVec(current_user_cache);
+ ret = amiga_icon_init();
+
+ search_web_init(nsoption_charp(search_engines_file));
+ ami_clipboard_init();
+ ami_openurl_open();
+ ami_amiupdate(); /* set env-vars for AmiUpdate */
+ ami_font_init();
+ save_complete_init();
+ ami_theme_init();
+ ami_init_mouse_pointers();
+ ami_file_req_init();
+
+ win_destroyed = false;
+ ami_font_setdevicedpi(0); /* for early font requests, eg treeview init */
+
+ window_list = NewObjList();
+
+ urldb_load(nsoption_charp(url_file));
+ urldb_load_cookies(nsoption_charp(cookie_file));
+
+ gui_init2(argc, argv);
+
+ ami_ctxmenu_init(); /* Requires screen pointer */
+
+ ami_gui_splash_close(splash_window);
+
+ strlcpy(script, nsoption_charp(arexx_dir), 1024);
+ AddPart(script, nsoption_charp(arexx_startup), 1024);
+ ami_arexx_execute(script);
+
+ LOG("Entering main loop");
+
+ while (!ami_quit) {
+ ami_get_msg();
+ }
+
+ strlcpy(script, nsoption_charp(arexx_dir), 1024);
+ AddPart(script, nsoption_charp(arexx_shutdown), 1024);
+ ami_arexx_execute(script);
+
+ ami_mime_free();
+
+ netsurf_exit();
+
+ FreeVec(current_user_options);
+ FreeVec(current_user_dir);
+ FreeVec(current_user_faviconcache);
+ FreeVec(current_user);
+
+ ami_clipboard_free();
+ ami_schedule_free();
+
+ FreeSysObject(ASOT_PORT, appport);
+ FreeSysObject(ASOT_PORT, sport);
+ FreeSysObject(ASOT_PORT, schedulermsgport);
+
+ ami_object_fini();
+ ami_bitmap_fini();
+
+ LOG("Closing screen");
+ ami_gui_close_screen(scrn, locked_screen, FALSE);
+ if(nsscreentitle) FreeVec(nsscreentitle);
+
+ ami_libs_close();
+
+ return RETURN_OK;
+}
+
diff --git a/frontends/amiga/gui.h b/frontends/amiga/gui.h
new file mode 100644
index 000000000..d301ac574
--- /dev/null
+++ b/frontends/amiga/gui.h
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2008-2015 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_GUI_H
+#define AMIGA_GUI_H
+
+#include <stdbool.h>
+#include <graphics/rastport.h>
+#include <intuition/classusr.h>
+#include <dos/dos.h>
+#include <devices/inputevent.h>
+
+#include "desktop/gui_window.h"
+#include "desktop/mouse.h"
+
+#include "amiga/menu.h"
+#include "amiga/object.h"
+#include "amiga/os3support.h"
+#include "amiga/plotters.h"
+
+#ifdef __amigaos4__
+#define HOOKF(ret,func,type,ptr,msgtype) static ret func(struct Hook *hook, type ptr, msgtype msg)
+#else
+#define HOOKF(ret,func,type,ptr,msgtype) static ASM ret func(REG(a0, struct Hook *hook),REG(a2, type ptr), REG(a1, msgtype msg))
+#endif
+
+enum
+{
+ OID_MAIN = 0,
+ OID_VSCROLL,
+ OID_HSCROLL,
+ OID_LAST, /* for compatibility */
+ GID_MAIN,
+ GID_TABLAYOUT,
+ GID_BROWSER,
+ GID_STATUS,
+ GID_URL,
+ GID_ICON,
+ GID_STOP,
+ GID_RELOAD,
+ GID_HOME,
+ GID_BACK,
+ GID_FORWARD,
+ GID_THROBBER,
+ GID_SEARCH_ICON,
+ GID_FAVE,
+ GID_FAVE_ADD,
+ GID_FAVE_RMV,
+ GID_CLOSETAB,
+ GID_CLOSETAB_BM,
+ GID_ADDTAB,
+ GID_ADDTAB_BM,
+ GID_TABS,
+ GID_TABS_FLAG,
+ GID_USER,
+ GID_PASS,
+ GID_LOGIN,
+ GID_CANCEL,
+ GID_NEXT,
+ GID_PREV,
+ GID_SEARCHSTRING,
+ GID_SHOWALL,
+ GID_CASE,
+ GID_TOOLBARLAYOUT,
+ GID_HOTLIST,
+ GID_HOTLISTLAYOUT,
+ GID_HOTLISTSEPBAR,
+ GID_HSCROLL,
+ GID_HSCROLLLAYOUT,
+ GID_VSCROLL,
+ GID_VSCROLLLAYOUT,
+ GID_LAST
+};
+
+struct find_window;
+struct history_window;
+
+#define AMI_GUI_TOOLBAR_MAX 20
+
+struct gui_window_2 {
+ struct nsObject *node;
+ struct Window *win;
+ Object *objects[GID_LAST];
+ struct gui_window *gw; /* currently-displayed gui_window */
+ bool redraw_required;
+ int throbber_frame;
+ struct List tab_list;
+ ULONG tabs;
+ ULONG next_tab;
+ struct Hook scrollerhook;
+ struct form_control *control;
+ browser_mouse_state mouse_state;
+ browser_mouse_state key_state;
+ ULONG throbber_update_count;
+ struct find_window *searchwin;
+ ULONG oldh;
+ ULONG oldv;
+ int temp;
+ bool redraw_scroll;
+ bool new_content;
+ char *menulab[AMI_MENU_AREXX_MAX + 1];
+ Object *menuobj[AMI_MENU_AREXX_MAX + 1];
+ char menukey[AMI_MENU_AREXX_MAX + 1];
+ char *menuicon[AMI_MENU_AREXX_MAX + 1];
+ struct Hook menu_hook[AMI_MENU_AREXX_MAX + 1];
+ UBYTE *menutype;
+ struct NewMenu *menu;
+ ULONG hotlist_items;
+ Object *hotlist_toolbar_lab[AMI_GUI_TOOLBAR_MAX];
+ struct List hotlist_toolbar_list;
+ struct List *web_search_list;
+ Object *search_bm;
+ char *svbuffer;
+ char *status;
+ char *wintitle;
+ char *helphints[GID_LAST];
+ browser_mouse_state prev_mouse_state;
+ struct timeval lastclick;
+ struct AppIcon *appicon; /* iconify appicon */
+ struct DiskObject *dobj; /* iconify appicon */
+ struct Hook favicon_hook;
+ struct Hook throbber_hook;
+ struct Hook *ctxmenu_hook;
+ Object *history_ctxmenu[2];
+ Object *clicktab_ctxmenu;
+ gui_drag_type drag_op;
+ struct IBox *ptr_lock;
+ struct AppWindow *appwin;
+ struct MinList *shared_pens;
+ gui_pointer_shape mouse_pointer;
+ struct Menu *imenu; /* Intuition menu */
+ struct VisualInfo *vi; /* For GadTools menu */
+};
+
+struct gui_window
+{
+ struct gui_window_2 *shared;
+ int tab;
+ struct Node *tab_node;
+ struct Node *last_new_tab;
+ int c_x; /* Caret X posn */
+ int c_y; /* Caret Y posn */
+ int c_w; /* Caret width */
+ int c_h; /* Caret height */
+ int c_h_temp;
+ int scrollx;
+ int scrolly;
+ struct history_window *hw;
+ struct List dllist;
+ struct hlcache_handle *favicon;
+ bool throbbing;
+ char *tabtitle;
+ APTR deferred_rects_pool;
+ struct MinList *deferred_rects;
+ struct browser_window *bw;
+ float scale;
+};
+
+void ami_get_msg(void);
+void ami_try_quit(void);
+void ami_quit_netsurf(void);
+void ami_schedule_redraw(struct gui_window_2 *gwin, bool full_redraw);
+STRPTR ami_locale_langs(void);
+int ami_key_to_nskey(ULONG keycode, struct InputEvent *ie);
+bool ami_text_box_at_point(struct gui_window_2 *gwin, ULONG *x, ULONG *y);
+bool ami_mouse_to_ns_coords(struct gui_window_2 *gwin, int *x, int *y,
+ int mouse_x, int mouse_y);
+BOOL ami_gadget_hit(Object *obj, int x, int y);
+void ami_gui_history(struct gui_window_2 *gwin, bool back);
+void ami_gui_hotlist_update_all(void);
+void ami_gui_tabs_toggle_all(void);
+bool ami_locate_resource(char *fullpath, const char *file);
+void ami_gui_update_hotlist_button(struct gui_window_2 *gwin);
+nserror ami_gui_new_blank_tab(struct gui_window_2 *gwin);
+char *ami_gui_get_cache_favicon_name(struct nsurl *url, bool only_if_avail);
+int ami_gui_count_windows(int window, int *tabs);
+void ami_gui_set_scale(struct gui_window *gw, float scale);
+
+
+/**
+ * Close a window and all tabs attached to it.
+ *
+ * @param gwin gui_window_2 to act upon.
+ */
+void ami_gui_close_window(struct gui_window_2 *gwin);
+
+/**
+ * Close all tabs in a window except the active one.
+ *
+ * @param gwin gui_window_2 to act upon.
+ */
+void ami_gui_close_inactive_tabs(struct gui_window_2 *gwin);
+
+/**
+ * Compatibility function to get space.gadget render area.
+ *
+ * @param obj A space.gadget object.
+ * @param bbox A pointer to a struct IBox *.
+ * @return error status.
+ */
+nserror ami_gui_get_space_box(Object *obj, struct IBox **bbox);
+
+/**
+ * Free any data obtained via ami_gui_get_space_box().
+ *
+ * @param bbox A pointer to a struct IBox.
+ */
+void ami_gui_free_space_box(struct IBox *bbox);
+
+/**
+ * Get the application.library ID NetSurf is registered as.
+ *
+ * @return App ID.
+ */
+uint32 ami_gui_get_app_id(void);
+
+/**
+ * Get the string for NetSurf's screen titlebar.
+ *
+ * @return String to use as the screen's titlebar text.
+ */
+STRPTR ami_gui_get_screen_title(void);
+
+struct MinList *window_list;
+struct Screen *scrn;
+struct MsgPort *sport;
+struct gui_window *cur_gw;
+struct gui_globals browserglob;
+BOOL ami_autoscroll;
+#endif
+
diff --git a/frontends/amiga/gui_options.c b/frontends/amiga/gui_options.c
new file mode 100755
index 000000000..221b0da95
--- /dev/null
+++ b/frontends/amiga/gui_options.c
@@ -0,0 +1,2316 @@
+/*
+ * Copyright 2009 - 2012 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <proto/utility.h>
+#ifdef __amigaos4__
+#include <proto/application.h>
+#endif
+#include <libraries/gadtools.h>
+#include <exec/types.h>
+#include <intuition/classusr.h>
+#include <graphics/gfxbase.h>
+
+#include <proto/window.h>
+#include <proto/layout.h>
+#include <proto/button.h>
+#include <proto/clicktab.h>
+#include <proto/label.h>
+#include <proto/string.h>
+#include <proto/checkbox.h>
+#include <proto/radiobutton.h>
+#include <proto/getscreenmode.h>
+#include <proto/getfile.h>
+#include <proto/chooser.h>
+#include <proto/integer.h>
+#include <proto/getfont.h>
+#include <classes/window.h>
+#include <images/label.h>
+#include <gadgets/button.h>
+#include <gadgets/clicktab.h>
+#include <gadgets/string.h>
+#include <gadgets/checkbox.h>
+#include <gadgets/radiobutton.h>
+#include <gadgets/getscreenmode.h>
+#include <gadgets/getfile.h>
+#include <gadgets/chooser.h>
+#include <gadgets/integer.h>
+#include <gadgets/getfont.h>
+#include <reaction/reaction.h>
+#include <reaction/reaction_macros.h>
+
+#include "utils/nsurl.h"
+#include "utils/messages.h"
+#include "utils/nsoption.h"
+#include "desktop/browser.h"
+#include "desktop/searchweb.h"
+#include "desktop/gui_window.h"
+
+#include "amiga/file.h"
+#include "amiga/font.h"
+#include "amiga/font_bullet.h"
+#include "amiga/gui.h"
+#include "amiga/gui_options.h"
+#include "amiga/help.h"
+#include "amiga/libs.h"
+#include "amiga/misc.h"
+#include "amiga/object.h"
+#include "amiga/selectmenu.h"
+#include "amiga/theme.h"
+#include "amiga/utf8.h"
+
+enum
+{
+ GID_OPTS_MAIN = GID_MAIN,
+ GID_OPTS_HOMEPAGE,
+ GID_OPTS_HOMEPAGE_DEFAULT,
+ GID_OPTS_HOMEPAGE_CURRENT,
+ GID_OPTS_HOMEPAGE_BLANK,
+ GID_OPTS_HIDEADS,
+ GID_OPTS_CONTENTLANG,
+ GID_OPTS_FROMLOCALE,
+ GID_OPTS_HISTORY,
+ GID_OPTS_JAVASCRIPT,
+ GID_OPTS_REFERRAL,
+ GID_OPTS_DONOTTRACK,
+ GID_OPTS_FASTSCROLL,
+ GID_OPTS_SCREEN,
+ GID_OPTS_SCREENMODE,
+ GID_OPTS_SCREENNAME,
+ GID_OPTS_WIN_SIMPLE,
+ GID_OPTS_THEME,
+ GID_OPTS_PTRTRUE,
+ GID_OPTS_PTROS,
+ GID_OPTS_PROXY,
+ GID_OPTS_PROXY_HOST,
+ GID_OPTS_PROXY_PORT,
+ GID_OPTS_PROXY_USER,
+ GID_OPTS_PROXY_PASS,
+ GID_OPTS_PROXY_BYPASS,
+ GID_OPTS_FETCHMAX,
+ GID_OPTS_FETCHHOST,
+ GID_OPTS_FETCHCACHE,
+ GID_OPTS_NATIVEBM,
+ GID_OPTS_SCALEQ,
+ GID_OPTS_DITHERQ,
+ GID_OPTS_ANIMSPEED,
+ GID_OPTS_ANIMDISABLE,
+ GID_OPTS_DPI_Y,
+ GID_OPTS_FONT_SANS,
+ GID_OPTS_FONT_SERIF,
+ GID_OPTS_FONT_MONO,
+ GID_OPTS_FONT_CURSIVE,
+ GID_OPTS_FONT_FANTASY,
+ GID_OPTS_FONT_DEFAULT,
+ GID_OPTS_FONT_SIZE,
+ GID_OPTS_FONT_MINSIZE,
+ GID_OPTS_FONT_ANTIALIASING,
+ GID_OPTS_FONT_BITMAP,
+ GID_OPTS_CACHE_MEM,
+ GID_OPTS_CACHE_DISC,
+ GID_OPTS_OVERWRITE,
+ GID_OPTS_NOTIFY,
+ GID_OPTS_DLDIR,
+ GID_OPTS_TAB_ACTIVE,
+ GID_OPTS_TAB_2,
+ GID_OPTS_TAB_LAST,
+ GID_OPTS_TAB_ALWAYS,
+ GID_OPTS_TAB_CLOSE,
+ GID_OPTS_SEARCH_PROV,
+ GID_OPTS_CLIPBOARD,
+ GID_OPTS_SELECTMENU,
+ GID_OPTS_STARTUP_NO_WIN,
+ GID_OPTS_CLOSE_NO_QUIT,
+ GID_OPTS_DOCKY,
+ GID_OPTS_MARGIN_TOP,
+ GID_OPTS_MARGIN_LEFT,
+ GID_OPTS_MARGIN_BOTTOM,
+ GID_OPTS_MARGIN_RIGHT,
+ GID_OPTS_EXPORT_SCALE,
+ GID_OPTS_EXPORT_NOIMAGES,
+ GID_OPTS_EXPORT_NOBKG,
+ GID_OPTS_EXPORT_LOOSEN,
+ GID_OPTS_EXPORT_COMPRESS,
+ GID_OPTS_EXPORT_PASSWORD,
+ GID_OPTS_SAVE,
+ GID_OPTS_USE,
+ GID_OPTS_CANCEL,
+ GID_OPTS_LAST
+};
+
+enum
+{
+ GRP_OPTS_HOMEPAGE = GID_OPTS_LAST,
+ GRP_OPTS_CONTENTBLOCKING,
+ GRP_OPTS_CONTENTLANGUAGE,
+ GRP_OPTS_HISTORY,
+ GRP_OPTS_SCRIPTING,
+ GRP_OPTS_PRIVACY,
+ GRP_OPTS_MISC,
+ GRP_OPTS_SCREEN,
+ GRP_OPTS_WINDOW,
+ GRP_OPTS_THEME,
+ GRP_OPTS_MOUSE,
+ GRP_OPTS_PROXY,
+ GRP_OPTS_FETCHING,
+ GRP_OPTS_IMAGES,
+ GRP_OPTS_ANIMS,
+ GRP_OPTS_DPI,
+ GRP_OPTS_FONTFACES,
+ GRP_OPTS_FONTSIZE,
+ GRP_OPTS_MEMCACHE,
+ GRP_OPTS_DISCCACHE,
+ GRP_OPTS_DOWNLOADS,
+ GRP_OPTS_TABS,
+ GRP_OPTS_SEARCH,
+ GRP_OPTS_CLIPBOARD,
+ GRP_OPTS_BEHAVIOUR,
+ GRP_OPTS_MARGINS,
+ GRP_OPTS_SCALING,
+ GRP_OPTS_APPEARANCE,
+ GRP_OPTS_ADVANCED,
+ GRP_OPTS_LAST
+};
+
+enum
+{
+ LAB_OPTS_WINTITLE = GRP_OPTS_LAST,
+ LAB_OPTS_RESTART,
+ LAB_OPTS_DAYS,
+ LAB_OPTS_SECS,
+ LAB_OPTS_PT,
+ LAB_OPTS_MB,
+ LAB_OPTS_MM,
+ LAB_OPTS_DPI,
+ LAB_OPTS_LAST
+};
+
+#define OPTS_LAST LAB_OPTS_LAST
+#define OPTS_MAX_TABS 10
+#define OPTS_MAX_SCREEN 4
+#define OPTS_MAX_PROXY 5
+#define OPTS_MAX_NATIVEBM 4
+#define OPTS_MAX_DITHER 4
+
+enum {
+ NSA_LIST_CLICKTAB = 0,
+ NSA_LIST_CHOOSER,
+ NSA_LIST_RADIO,
+};
+
+struct ami_gui_opts_window {
+ struct nsObject *node;
+ struct Window *win;
+ Object *objects[GID_OPTS_LAST];
+#ifndef __amigaos4__
+ struct List clicktablist;
+ struct List screenoptslist;
+ struct List proxyoptslist;
+ struct List nativebmoptslist;
+ struct List ditheroptslist;
+ struct List fontoptslist;
+#endif
+};
+
+static struct ami_gui_opts_window *gow = NULL;
+
+CONST_STRPTR tabs[OPTS_MAX_TABS];
+static STRPTR screenopts[OPTS_MAX_SCREEN];
+CONST_STRPTR proxyopts[OPTS_MAX_PROXY];
+CONST_STRPTR nativebmopts[OPTS_MAX_NATIVEBM];
+CONST_STRPTR ditheropts[OPTS_MAX_DITHER];
+CONST_STRPTR fontopts[6];
+CONST_STRPTR gadlab[OPTS_LAST];
+struct List *websearch_list;
+
+#ifndef __amigaos4__
+static void ami_gui_opts_array_to_list(struct List *list, const char *array[], int type)
+{
+ int i = 0;
+ struct Node *node;
+
+ NewList(list);
+
+ do {
+ switch(type) {
+ case NSA_LIST_CLICKTAB:
+ node = AllocClickTabNode(TNA_Text, array[i], TNA_Number, i, TAG_DONE);
+ break;
+ case NSA_LIST_CHOOSER:
+ node = AllocChooserNode(CNA_Text, array[i], TAG_DONE);
+ break;
+ case NSA_LIST_RADIO:
+ /* Note: RBNA_Labels is RBNA_Label in OS4
+ * Also note: These labels don't work (FIXME) */
+ node = AllocRadioButtonNode(RBNA_Labels, array[i], TAG_DONE);
+ break;
+ default:
+ break;
+ }
+ AddTail(list, node);
+ i++;
+ } while (array[i] != 0);
+}
+
+static void ami_gui_opts_free_list(struct List *list, int type)
+{
+ struct Node *node;
+ struct Node *nnode;
+
+ if(IsListEmpty((struct List *)list)) return;
+ node = GetHead((struct List *)list);
+
+ do {
+ nnode = GetSucc(node);
+ Remove(node);
+ if(node) {
+ switch(type) {
+ case NSA_LIST_CLICKTAB:
+ FreeClickTabNode(node);
+ break;
+ case NSA_LIST_CHOOSER:
+ FreeChooserNode(node);
+ break;
+ case NSA_LIST_RADIO:
+ FreeRadioButtonNode(node);
+ break;
+ default:
+ break;
+ }
+ }
+ } while((node = nnode));
+}
+#endif
+
+static void ami_gui_opts_setup(struct ami_gui_opts_window *gow)
+{
+ tabs[0] = (char *)ami_utf8_easy((char *)messages_get("con_general"));
+ tabs[1] = (char *)ami_utf8_easy((char *)messages_get("Display"));
+ tabs[2] = (char *)ami_utf8_easy((char *)messages_get("con_connect"));
+ tabs[3] = (char *)ami_utf8_easy((char *)messages_get("con_rendering"));
+ tabs[4] = (char *)ami_utf8_easy((char *)messages_get("con_fonts"));
+ tabs[5] = (char *)ami_utf8_easy((char *)messages_get("con_cache"));
+ tabs[6] = (char *)ami_utf8_easy((char *)messages_get("Tabs"));
+ tabs[7] = (char *)ami_utf8_easy((char *)messages_get("con_advanced"));
+#ifdef WITH_PDF_EXPORT
+ tabs[8] = (char *)ami_utf8_easy((char *)messages_get("Export"));
+ tabs[9] = NULL;
+#else
+ tabs[8] = NULL;
+#endif
+
+ screenopts[0] = (char *)ami_utf8_easy((char *)messages_get("ScreenOwn"));
+ screenopts[1] = (char *)ami_utf8_easy((char *)messages_get("ScreenWB"));
+ screenopts[2] = (char *)ami_utf8_easy((char *)messages_get("ScreenPublic"));
+ screenopts[3] = NULL;
+
+ proxyopts[0] = (char *)ami_utf8_easy((char *)messages_get("ProxyNone"));
+ proxyopts[1] = (char *)ami_utf8_easy((char *)messages_get("ProxyNoAuth"));
+ proxyopts[2] = (char *)ami_utf8_easy((char *)messages_get("ProxyBasic"));
+ proxyopts[3] = (char *)ami_utf8_easy((char *)messages_get("ProxyNTLM"));
+ proxyopts[4] = NULL;
+
+ nativebmopts[0] = (char *)ami_utf8_easy((char *)messages_get("None"));
+ nativebmopts[1] = (char *)ami_utf8_easy((char *)messages_get("Scaled"));
+ nativebmopts[2] = (char *)ami_utf8_easy((char *)messages_get("All"));
+ nativebmopts[3] = NULL;
+
+ ditheropts[0] = (char *)ami_utf8_easy((char *)messages_get("Low"));
+ ditheropts[1] = (char *)ami_utf8_easy((char *)messages_get("Medium"));
+ ditheropts[2] = (char *)ami_utf8_easy((char *)messages_get("High"));
+ ditheropts[3] = NULL;
+
+ websearch_list = ami_gui_opts_websearch();
+
+ gadlab[GID_OPTS_HOMEPAGE] = (char *)ami_utf8_easy((char *)messages_get("HomePageURL"));
+ gadlab[GID_OPTS_HOMEPAGE_DEFAULT] = (char *)ami_utf8_easy((char *)messages_get("HomePageDefault"));
+ gadlab[GID_OPTS_HOMEPAGE_CURRENT] = (char *)ami_utf8_easy((char *)messages_get("HomePageCurrent"));
+ gadlab[GID_OPTS_HOMEPAGE_BLANK] = (char *)ami_utf8_easy((char *)messages_get("HomePageBlank"));
+ gadlab[GID_OPTS_HIDEADS] = (char *)ami_utf8_easy((char *)messages_get("BlockAds"));
+ gadlab[GID_OPTS_FROMLOCALE] = (char *)ami_utf8_easy((char *)messages_get("LocaleLang"));
+ gadlab[GID_OPTS_HISTORY] = (char *)ami_utf8_easy((char *)messages_get("HistoryAge"));
+ gadlab[GID_OPTS_JAVASCRIPT] = (char *)ami_utf8_easy((char *)messages_get("EnableJS"));
+ gadlab[GID_OPTS_REFERRAL] = (char *)ami_utf8_easy((char *)messages_get("SendReferer"));
+ gadlab[GID_OPTS_DONOTTRACK] = (char *)ami_utf8_easy((char *)messages_get("DoNotTrack"));
+ gadlab[GID_OPTS_FASTSCROLL] = (char *)ami_utf8_easy((char *)messages_get("FastScrolling"));
+ gadlab[GID_OPTS_WIN_SIMPLE] = (char *)ami_utf8_easy((char *)messages_get("SimpleRefresh"));
+ gadlab[GID_OPTS_PTRTRUE] = (char *)ami_utf8_easy((char *)messages_get("TrueColour"));
+ gadlab[GID_OPTS_PTROS] = (char *)ami_utf8_easy((char *)messages_get("OSPointers"));
+ gadlab[GID_OPTS_PROXY] = (char *)ami_utf8_easy((char *)messages_get("ProxyType"));
+ gadlab[GID_OPTS_PROXY_HOST] = (char *)ami_utf8_easy((char *)messages_get("Host"));
+ gadlab[GID_OPTS_PROXY_USER] = (char *)ami_utf8_easy((char *)messages_get("Username"));
+ gadlab[GID_OPTS_PROXY_PASS] = (char *)ami_utf8_easy((char *)messages_get("Password"));
+ gadlab[GID_OPTS_PROXY_BYPASS] = (char *)ami_utf8_easy((char *)messages_get("ProxyBypass"));
+ gadlab[GID_OPTS_FETCHMAX] = (char *)ami_utf8_easy((char *)messages_get("FetchesMax"));
+ gadlab[GID_OPTS_FETCHHOST] = (char *)ami_utf8_easy((char *)messages_get("FetchesHost"));
+ gadlab[GID_OPTS_FETCHCACHE] = (char *)ami_utf8_easy((char *)messages_get("FetchesCached"));
+ gadlab[GID_OPTS_NATIVEBM] = (char *)ami_utf8_easy((char *)messages_get("CacheNative"));
+ gadlab[GID_OPTS_SCALEQ] = (char *)ami_utf8_easy((char *)messages_get("ScaleQuality"));
+ gadlab[GID_OPTS_DITHERQ] = (char *)ami_utf8_easy((char *)messages_get("DitherQuality"));
+ gadlab[GID_OPTS_ANIMSPEED] = (char *)ami_utf8_easy((char *)messages_get("AnimSpeedLimit"));
+ gadlab[GID_OPTS_DPI_Y] = (char *)ami_utf8_easy((char *)messages_get("ResolutionY"));
+ gadlab[GID_OPTS_ANIMDISABLE] = (char *)ami_utf8_easy((char *)messages_get("AnimDisable"));
+ gadlab[GID_OPTS_FONT_SANS] = (char *)ami_utf8_easy((char *)messages_get("FontSans"));
+ gadlab[GID_OPTS_FONT_SERIF] = (char *)ami_utf8_easy((char *)messages_get("FontSerif"));
+ gadlab[GID_OPTS_FONT_MONO] = (char *)ami_utf8_easy((char *)messages_get("FontMono"));
+ gadlab[GID_OPTS_FONT_CURSIVE] = (char *)ami_utf8_easy((char *)messages_get("FontCursive"));
+ gadlab[GID_OPTS_FONT_FANTASY] = (char *)ami_utf8_easy((char *)messages_get("FontFantasy"));
+ gadlab[GID_OPTS_FONT_DEFAULT] = (char *)ami_utf8_easy((char *)messages_get("Default"));
+ gadlab[GID_OPTS_FONT_SIZE] = (char *)ami_utf8_easy((char *)messages_get("Default"));
+ gadlab[GID_OPTS_FONT_MINSIZE] = (char *)ami_utf8_easy((char *)messages_get("Minimum"));
+ gadlab[GID_OPTS_FONT_ANTIALIASING] = (char *)ami_utf8_easy((char *)messages_get("FontAntialiasing"));
+ gadlab[GID_OPTS_FONT_BITMAP] = (char *)ami_utf8_easy((char *)messages_get("FontBitmap"));
+ gadlab[GID_OPTS_CACHE_MEM] = (char *)ami_utf8_easy((char *)messages_get("Size"));
+ gadlab[GID_OPTS_CACHE_DISC] = (char *)ami_utf8_easy((char *)messages_get("Size"));
+ gadlab[GID_OPTS_OVERWRITE] = (char *)ami_utf8_easy((char *)messages_get("ConfirmOverwrite"));
+ gadlab[GID_OPTS_NOTIFY] = (char *)ami_utf8_easy((char *)messages_get("DownloadNotify"));
+ gadlab[GID_OPTS_DLDIR] = (char *)ami_utf8_easy((char *)messages_get("DownloadDir"));
+ gadlab[GID_OPTS_TAB_ACTIVE] = (char *)ami_utf8_easy((char *)messages_get("TabActive"));
+ gadlab[GID_OPTS_TAB_2] = (char *)ami_utf8_easy((char *)messages_get("TabMiddle"));
+ gadlab[GID_OPTS_TAB_LAST] = (char *)ami_utf8_easy((char *)messages_get("TabLast"));
+ gadlab[GID_OPTS_TAB_ALWAYS] = (char *)ami_utf8_easy((char *)messages_get("TabAlways"));
+ gadlab[GID_OPTS_TAB_CLOSE] = (char *)ami_utf8_easy((char *)messages_get("TabClose"));
+ gadlab[GID_OPTS_SEARCH_PROV] = (char *)ami_utf8_easy((char *)messages_get("SearchProvider"));
+ gadlab[GID_OPTS_CLIPBOARD] = (char *)ami_utf8_easy((char *)messages_get("ClipboardUTF8"));
+ gadlab[GID_OPTS_SELECTMENU] = (char *)ami_utf8_easy((char *)messages_get("PopupMenu"));
+ gadlab[GID_OPTS_STARTUP_NO_WIN] = (char *)ami_utf8_easy((char *)messages_get("OptionNoWindow"));
+ gadlab[GID_OPTS_CLOSE_NO_QUIT] = (char *)ami_utf8_easy((char *)messages_get("OptionNoQuit"));
+ gadlab[GID_OPTS_DOCKY] = (char *)ami_utf8_easy((char *)messages_get("OptionDocky"));
+ gadlab[GID_OPTS_MARGIN_TOP] = (char *)ami_utf8_easy((char *)messages_get("Top"));
+ gadlab[GID_OPTS_MARGIN_LEFT] = (char *)ami_utf8_easy((char *)messages_get("Left"));
+ gadlab[GID_OPTS_MARGIN_RIGHT] = (char *)ami_utf8_easy((char *)messages_get("Right"));
+ gadlab[GID_OPTS_MARGIN_BOTTOM] = (char *)ami_utf8_easy((char *)messages_get("Bottom"));
+ gadlab[GID_OPTS_EXPORT_SCALE] = (char *)ami_utf8_easy((char *)messages_get("Scale"));
+ gadlab[GID_OPTS_EXPORT_NOIMAGES] = (char *)ami_utf8_easy((char *)messages_get("SuppressImages"));
+ gadlab[GID_OPTS_EXPORT_NOBKG] = (char *)ami_utf8_easy((char *)messages_get("RemoveBackground"));
+ gadlab[GID_OPTS_EXPORT_LOOSEN] = (char *)ami_utf8_easy((char *)messages_get("FitPage"));
+ gadlab[GID_OPTS_EXPORT_COMPRESS] = (char *)ami_utf8_easy((char *)messages_get("CompressPDF"));
+ gadlab[GID_OPTS_EXPORT_PASSWORD] = (char *)ami_utf8_easy((char *)messages_get("SetPassword"));
+ gadlab[GID_OPTS_SAVE] = (char *)ami_utf8_easy((char *)messages_get("SelSave"));
+ gadlab[GID_OPTS_USE] = (char *)ami_utf8_easy((char *)messages_get("Use"));
+ gadlab[GID_OPTS_CANCEL] = (char *)ami_utf8_easy((char *)messages_get("Cancel"));
+
+ gadlab[LAB_OPTS_WINTITLE] = (char *)ami_utf8_easy((char *)messages_get("Preferences"));
+ gadlab[LAB_OPTS_RESTART] = (char *)ami_utf8_easy((char *)messages_get("NeedRestart"));
+ gadlab[LAB_OPTS_DAYS] = (char *)ami_utf8_easy((char *)messages_get("Days"));
+ gadlab[LAB_OPTS_SECS] = (char *)ami_utf8_easy((char *)messages_get("AnimSpeedFrames"));
+ gadlab[LAB_OPTS_PT] = (char *)ami_utf8_easy((char *)messages_get("Pt"));
+ gadlab[LAB_OPTS_MM] = (char *)ami_utf8_easy((char *)messages_get("MM"));
+ gadlab[LAB_OPTS_MB] = (char *)ami_utf8_easy((char *)messages_get("MBytes"));
+ gadlab[LAB_OPTS_DPI] = (char *)ami_utf8_easy((char *)messages_get("DPI"));
+
+ gadlab[GRP_OPTS_HOMEPAGE] = (char *)ami_utf8_easy((char *)messages_get("Home"));
+ gadlab[GRP_OPTS_CONTENTBLOCKING] = (char *)ami_utf8_easy((char *)messages_get("ContentBlocking"));
+ gadlab[GRP_OPTS_CONTENTLANGUAGE] = (char *)ami_utf8_easy((char *)messages_get("ContentLanguage"));
+ gadlab[GRP_OPTS_HISTORY] = (char *)ami_utf8_easy((char *)messages_get("History"));
+ gadlab[GRP_OPTS_SCRIPTING] = (char *)ami_utf8_easy((char *)messages_get("Scripting"));
+ gadlab[GRP_OPTS_MISC] = (char *)ami_utf8_easy((char *)messages_get("Miscellaneous"));
+ gadlab[GRP_OPTS_SCREEN] = (char *)ami_utf8_easy((char *)messages_get("Screen"));
+ gadlab[GRP_OPTS_WINDOW] = (char *)ami_utf8_easy((char *)messages_get("Window"));
+ gadlab[GRP_OPTS_THEME] = (char *)ami_utf8_easy((char *)messages_get("Theme"));
+ gadlab[GRP_OPTS_MOUSE] = (char *)ami_utf8_easy((char *)messages_get("MousePointers"));
+ gadlab[GRP_OPTS_PROXY] = (char *)ami_utf8_easy((char *)messages_get("Proxy"));
+ gadlab[GRP_OPTS_FETCHING] = (char *)ami_utf8_easy((char *)messages_get("Fetching"));
+ gadlab[GRP_OPTS_IMAGES] = (char *)ami_utf8_easy((char *)messages_get("Images"));
+ gadlab[GRP_OPTS_ANIMS] = (char *)ami_utf8_easy((char *)messages_get("Animations"));
+ gadlab[GRP_OPTS_DPI] = (char *)ami_utf8_easy((char *)messages_get("Resolution"));
+ gadlab[GRP_OPTS_FONTFACES] = (char *)ami_utf8_easy((char *)messages_get("FontFamilies"));
+ gadlab[GRP_OPTS_FONTSIZE] = (char *)ami_utf8_easy((char *)messages_get("FontSize"));
+ gadlab[GRP_OPTS_MEMCACHE] = (char *)ami_utf8_easy((char *)messages_get("CacheMemory"));
+ gadlab[GRP_OPTS_DISCCACHE] = (char *)ami_utf8_easy((char *)messages_get("CacheDisc"));
+ gadlab[GRP_OPTS_DOWNLOADS] = (char *)ami_utf8_easy((char *)messages_get("Downloads"));
+ gadlab[GRP_OPTS_TABS] = (char *)ami_utf8_easy((char *)messages_get("TabbedBrowsing"));
+ gadlab[GRP_OPTS_SEARCH] = (char *)ami_utf8_easy((char *)messages_get("SearchWeb"));
+ gadlab[GRP_OPTS_CLIPBOARD] = (char *)ami_utf8_easy((char *)messages_get("Clipboard"));
+ gadlab[GRP_OPTS_PRIVACY] = (char *)ami_utf8_easy((char *)messages_get("Privacy"));
+ gadlab[GRP_OPTS_BEHAVIOUR] = (char *)ami_utf8_easy((char *)messages_get("Behaviour"));
+ gadlab[GRP_OPTS_MARGINS] = (char *)ami_utf8_easy((char *)messages_get("Margins"));
+ gadlab[GRP_OPTS_SCALING] = (char *)ami_utf8_easy((char *)messages_get("Scaling"));
+ gadlab[GRP_OPTS_APPEARANCE] = (char *)ami_utf8_easy((char *)messages_get("Appearance"));
+ gadlab[GRP_OPTS_ADVANCED] = (char *)ami_utf8_easy((char *)messages_get("con_advanced"));
+
+ fontopts[0] = gadlab[GID_OPTS_FONT_SANS];
+ fontopts[1] = gadlab[GID_OPTS_FONT_SERIF];
+ fontopts[2] = gadlab[GID_OPTS_FONT_MONO];
+ fontopts[3] = gadlab[GID_OPTS_FONT_CURSIVE];
+ fontopts[4] = gadlab[GID_OPTS_FONT_FANTASY];
+ fontopts[5] = NULL;
+
+#ifndef __amigaos4__
+ ami_gui_opts_array_to_list(&gow->clicktablist, tabs, NSA_LIST_CLICKTAB);
+ ami_gui_opts_array_to_list(&gow->screenoptslist, screenopts, NSA_LIST_RADIO);
+ ami_gui_opts_array_to_list(&gow->proxyoptslist, proxyopts, NSA_LIST_CHOOSER);
+ ami_gui_opts_array_to_list(&gow->nativebmoptslist, nativebmopts, NSA_LIST_CHOOSER);
+ ami_gui_opts_array_to_list(&gow->ditheroptslist, ditheropts, NSA_LIST_CHOOSER);
+ ami_gui_opts_array_to_list(&gow->fontoptslist, fontopts, NSA_LIST_CHOOSER);
+#endif
+}
+
+static void ami_gui_opts_free(struct ami_gui_opts_window *gow)
+{
+ int i;
+
+ for(i = 0; i < OPTS_LAST; i++)
+ if(gadlab[i]) free((APTR)gadlab[i]);
+
+ for(i = 0; i < OPTS_MAX_TABS; i++)
+ if(tabs[i]) free((APTR)tabs[i]);
+
+ for(i = 0; i < OPTS_MAX_SCREEN; i++)
+ if(screenopts[i]) free((APTR)screenopts[i]);
+
+ for(i = 0; i < OPTS_MAX_PROXY; i++)
+ if(proxyopts[i]) free((APTR)proxyopts[i]);
+
+ for(i = 0; i < OPTS_MAX_NATIVEBM; i++)
+ if(nativebmopts[i]) free((APTR)nativebmopts[i]);
+
+ ami_gui_opts_websearch_free(websearch_list);
+
+#ifndef __amigaos4__
+ ami_gui_opts_free_list(&gow->clicktablist, NSA_LIST_CLICKTAB);
+ ami_gui_opts_free_list(&gow->screenoptslist, NSA_LIST_RADIO);
+ ami_gui_opts_free_list(&gow->proxyoptslist, NSA_LIST_CHOOSER);
+ ami_gui_opts_free_list(&gow->nativebmoptslist, NSA_LIST_CHOOSER);
+ ami_gui_opts_free_list(&gow->ditheroptslist, NSA_LIST_CHOOSER);
+ ami_gui_opts_free_list(&gow->fontoptslist, NSA_LIST_CHOOSER);
+#endif
+}
+
+void ami_gui_opts_open(void)
+{
+ uint16 screenoptsselected;
+ ULONG screenmodeid = 0;
+ ULONG proxytype = 0;
+ BOOL screenmodedisabled = FALSE, screennamedisabled = FALSE;
+ BOOL proxyhostdisabled = TRUE, proxyauthdisabled = TRUE, proxybypassdisabled = FALSE;
+ BOOL disableanims, animspeeddisabled = FALSE, acceptlangdisabled = FALSE;
+ BOOL scaleselected = nsoption_bool(scale_quality), scaledisabled = FALSE;
+ BOOL ditherdisable = TRUE;
+ BOOL download_notify_disabled = FALSE, tab_always_show_disabled = FALSE;
+ BOOL ptr_disable = FALSE;
+ char animspeed[10];
+ char *homepage_url_lc = ami_utf8_easy(nsoption_charp(homepage_url));
+
+ struct TextAttr fontsans, fontserif, fontmono, fontcursive, fontfantasy;
+
+ if(gow && gow->win)
+ {
+ WindowToFront(gow->win);
+ ActivateWindow(gow->win);
+ return;
+ }
+
+#ifdef __amigaos4__
+ if(LIB_IS_AT_LEAST((struct Library *)IntuitionBase, 53, 42)) ptr_disable = TRUE;
+#else
+ ptr_disable = TRUE;
+#endif
+
+ if(nsoption_charp(pubscreen_name))
+ {
+ if(strcmp(nsoption_charp(pubscreen_name),"Workbench") == 0)
+ {
+ screenoptsselected = 1;
+ screennamedisabled = TRUE;
+ screenmodedisabled = TRUE;
+ }
+ else
+ {
+ screenoptsselected = 2;
+ screenmodedisabled = TRUE;
+ }
+ }
+ else
+ {
+ screenoptsselected = 0;
+ screennamedisabled = TRUE;
+ }
+
+ if((nsoption_charp(screen_modeid)) &&
+ (strncmp(nsoption_charp(screen_modeid),"0x",2) == 0))
+ {
+ screenmodeid = strtoul(nsoption_charp(screen_modeid),NULL,0);
+ }
+
+ if(ami_plot_screen_is_palettemapped() == true) {
+ ditherdisable = FALSE;
+ }
+
+ if(nsoption_bool(http_proxy) == true)
+ {
+ proxytype = nsoption_int(http_proxy_auth) + 1;
+ switch(nsoption_int(http_proxy_auth))
+ {
+ case OPTION_HTTP_PROXY_AUTH_BASIC:
+ case OPTION_HTTP_PROXY_AUTH_NTLM:
+ proxyauthdisabled = FALSE;
+ case OPTION_HTTP_PROXY_AUTH_NONE:
+ proxyhostdisabled = FALSE;
+ break;
+ }
+ } else {
+ proxybypassdisabled = TRUE;
+ }
+
+ sprintf(animspeed,"%.2f",(float)(nsoption_int(minimum_gif_delay)/100.0));
+
+ if(nsoption_bool(animate_images))
+ {
+ disableanims = FALSE;
+ animspeeddisabled = FALSE;
+ }
+ else
+ {
+ disableanims = TRUE;
+ animspeeddisabled = TRUE;
+ }
+
+ if(nsoption_bool(accept_lang_locale))
+ acceptlangdisabled = TRUE;
+ else
+ acceptlangdisabled = FALSE;
+
+ if(GfxBase->LibNode.lib_Version < 53)
+ {
+ scaledisabled = TRUE;
+ scaleselected = FALSE;
+ }
+#ifdef __amigaos4__
+ if(ApplicationBase->lib_Version < 53)
+#endif
+ {
+ download_notify_disabled = TRUE;
+ nsoption_set_bool(download_notify, FALSE);
+ }
+
+ if(ClickTabBase->lib_Version < 53) {
+ tab_always_show_disabled = TRUE;
+ }
+
+ fontsans.ta_Name = ASPrintf("%s.font", nsoption_charp(font_sans));
+ fontserif.ta_Name = ASPrintf("%s.font", nsoption_charp(font_serif));
+ fontmono.ta_Name = ASPrintf("%s.font", nsoption_charp(font_mono));
+ fontcursive.ta_Name = ASPrintf("%s.font", nsoption_charp(font_cursive));
+ fontfantasy.ta_Name = ASPrintf("%s.font", nsoption_charp(font_fantasy));
+
+ fontsans.ta_Style = 0;
+ fontserif.ta_Style = 0;
+ fontmono.ta_Style = 0;
+ fontcursive.ta_Style = 0;
+ fontfantasy.ta_Style = 0;
+
+ fontsans.ta_YSize = 0;
+ fontserif.ta_YSize = 0;
+ fontmono.ta_YSize = 0;
+ fontcursive.ta_YSize = 0;
+ fontfantasy.ta_YSize = 0;
+
+ fontsans.ta_Flags = 0;
+ fontserif.ta_Flags = 0;
+ fontmono.ta_Flags = 0;
+ fontcursive.ta_Flags = 0;
+ fontfantasy.ta_Flags = 0;
+
+ if(!gow)
+ {
+ gow = ami_misc_allocvec_clear(sizeof(struct ami_gui_opts_window), 0);
+ if(gow == NULL) return;
+
+ ami_gui_opts_setup(gow);
+
+ gow->objects[OID_MAIN] = WindowObj,
+ WA_ScreenTitle, ami_gui_get_screen_title(),
+ WA_Title, gadlab[LAB_OPTS_WINTITLE],
+ WA_Activate, TRUE,
+ WA_DepthGadget, TRUE,
+ WA_DragBar, TRUE,
+ WA_CloseGadget, TRUE,
+ WA_SizeGadget, FALSE,
+ WA_PubScreen,scrn,
+ WINDOW_SharedPort,sport,
+ WINDOW_UserData,gow,
+ WINDOW_IconifyGadget, FALSE,
+ WINDOW_Position, WPOS_CENTERSCREEN,
+ WA_IDCMP, IDCMP_GADGETUP | IDCMP_CLOSEWINDOW,
+ WINDOW_ParentGroup, gow->objects[GID_OPTS_MAIN] = LayoutVObj,
+ LAYOUT_AddChild, ClickTabObj,
+ GA_RelVerify, TRUE,
+#ifdef __amigaos4__
+ GA_Text, tabs,
+#else
+ CLICKTAB_Labels, &gow->clicktablist,
+#endif
+ CLICKTAB_PageGroup, PageObj,
+ /*
+ ** General
+ */
+ PAGE_Add, LayoutVObj,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_HOMEPAGE],
+ LAYOUT_AddChild, gow->objects[GID_OPTS_HOMEPAGE] = StringObj,
+ GA_ID, GID_OPTS_HOMEPAGE,
+ GA_RelVerify, TRUE,
+ STRINGA_TextVal, homepage_url_lc,
+ STRINGA_BufferPos,0,
+ StringEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_HOMEPAGE],
+ LabelEnd,
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_HOMEPAGE_DEFAULT] = ButtonObj,
+ GA_ID,GID_OPTS_HOMEPAGE_DEFAULT,
+ GA_Text,gadlab[GID_OPTS_HOMEPAGE_DEFAULT],
+ GA_RelVerify,TRUE,
+ ButtonEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_HOMEPAGE_CURRENT] = ButtonObj,
+ GA_ID,GID_OPTS_HOMEPAGE_CURRENT,
+ GA_Text,gadlab[GID_OPTS_HOMEPAGE_CURRENT],
+ GA_RelVerify,TRUE,
+ ButtonEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_HOMEPAGE_BLANK] = ButtonObj,
+ GA_ID,GID_OPTS_HOMEPAGE_BLANK,
+ GA_Text,gadlab[GID_OPTS_HOMEPAGE_BLANK],
+ GA_RelVerify,TRUE,
+ ButtonEnd,
+ LayoutEnd,
+ LayoutEnd, //homepage
+ CHILD_WeightedHeight, 0,
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_CONTENTBLOCKING],
+ LAYOUT_AddChild, gow->objects[GID_OPTS_HIDEADS] = CheckBoxObj,
+ GA_ID, GID_OPTS_HIDEADS,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_HIDEADS],
+ GA_Selected, nsoption_bool(block_advertisements),
+ CheckBoxEnd,
+ LayoutEnd, // content blocking
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_CONTENTLANGUAGE],
+ LAYOUT_AddChild, gow->objects[GID_OPTS_CONTENTLANG] = StringObj,
+ GA_ID, GID_OPTS_CONTENTLANG,
+ GA_RelVerify, TRUE,
+ GA_Disabled, acceptlangdisabled,
+ STRINGA_TextVal, nsoption_charp(accept_language),
+ STRINGA_BufferPos,0,
+ StringEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_FROMLOCALE] = CheckBoxObj,
+ GA_ID, GID_OPTS_FROMLOCALE,
+ GA_Text, gadlab[GID_OPTS_FROMLOCALE],
+ GA_RelVerify, TRUE,
+ GA_Selected, nsoption_bool(accept_lang_locale),
+ ButtonEnd,
+ // CHILD_WeightedWidth, 0,
+ LayoutEnd, // content language
+ LayoutEnd, // content
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_HISTORY],
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_LabelColumn, PLACETEXT_RIGHT,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_HISTORY] = IntegerObj,
+ GA_ID, GID_OPTS_HISTORY,
+ GA_RelVerify, TRUE,
+ INTEGER_Number, nsoption_int(expire_url),
+ INTEGER_Minimum, 0,
+ INTEGER_Maximum, 366,
+ INTEGER_Arrows, TRUE,
+ IntegerEnd,
+ CHILD_WeightedWidth, 0,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[LAB_OPTS_DAYS],
+ LabelEnd,
+ LayoutEnd,
+ CHILD_WeightedWidth, 0,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_HISTORY],
+ LabelEnd,
+ LayoutEnd, // history
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_SCRIPTING],
+ LAYOUT_AddChild, gow->objects[GID_OPTS_JAVASCRIPT] = CheckBoxObj,
+ GA_ID, GID_OPTS_JAVASCRIPT,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_JAVASCRIPT],
+ GA_Selected, nsoption_bool(enable_javascript),
+ CheckBoxEnd,
+ LayoutEnd, // scripting
+ LayoutEnd,
+ CHILD_WeightedHeight, 0,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_PRIVACY],
+ LAYOUT_AddChild, gow->objects[GID_OPTS_REFERRAL] = CheckBoxObj,
+ GA_ID, GID_OPTS_REFERRAL,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_REFERRAL],
+ GA_Selected, nsoption_bool(send_referer),
+ CheckBoxEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_DONOTTRACK] = CheckBoxObj,
+ GA_ID, GID_OPTS_DONOTTRACK,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_DONOTTRACK],
+ GA_Selected, nsoption_bool(do_not_track),
+ CheckBoxEnd,
+ LayoutEnd, // misc
+ CHILD_WeightedHeight, 0,
+ LayoutEnd, // page vgroup
+ CHILD_WeightedHeight, 0,
+ PageEnd, // pageadd
+ /*
+ ** Display
+ */
+ PAGE_Add, LayoutVObj,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_SCREEN],
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_SCREEN] = RadioButtonObj,
+ GA_ID, GID_OPTS_SCREEN,
+ GA_RelVerify, TRUE,
+#ifdef __amigaos4__
+ GA_Text, screenopts,
+#else
+ RADIOBUTTON_Labels, &gow->screenoptslist,
+#endif
+ RADIOBUTTON_Selected, screenoptsselected,
+ RadioButtonEnd,
+ CHILD_WeightedWidth,0,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_SCREENMODE] = GetScreenModeObj,
+ GA_ID, GID_OPTS_SCREENMODE,
+ GA_RelVerify, TRUE,
+ GA_Disabled,screenmodedisabled,
+ GETSCREENMODE_DisplayID,screenmodeid,
+ GETSCREENMODE_MinDepth, 0,
+ GETSCREENMODE_MaxDepth, 32,
+ GetScreenModeEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_SCREENNAME] = StringObj,
+ GA_ID, GID_OPTS_SCREENNAME,
+ GA_RelVerify, TRUE,
+ GA_Disabled,screennamedisabled,
+ STRINGA_TextVal, nsoption_charp(pubscreen_name),
+ STRINGA_BufferPos,0,
+ StringEnd,
+ LayoutEnd,
+ CHILD_WeightedHeight,0,
+ LayoutEnd,
+ LayoutEnd, // screen
+ CHILD_WeightedHeight,0,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_WINDOW],
+ LAYOUT_AddChild, gow->objects[GID_OPTS_WIN_SIMPLE] = CheckBoxObj,
+ GA_ID, GID_OPTS_WIN_SIMPLE,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_WIN_SIMPLE],
+ GA_Selected, nsoption_bool(window_simple_refresh),
+ CheckBoxEnd,
+ LayoutEnd, // window
+ CHILD_WeightedHeight,0,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_THEME],
+ LAYOUT_AddChild, gow->objects[GID_OPTS_THEME] = GetFileObj,
+ GA_ID, GID_OPTS_THEME,
+ GA_RelVerify, TRUE,
+ GETFILE_Drawer, nsoption_charp(theme),
+ GETFILE_DrawersOnly, TRUE,
+ GETFILE_ReadOnly, TRUE,
+ GETFILE_FullFileExpand, FALSE,
+ GetFileEnd,
+ LayoutEnd, // theme
+ CHILD_WeightedHeight, 0,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_MOUSE],
+ LAYOUT_AddChild, gow->objects[GID_OPTS_PTRTRUE] = CheckBoxObj,
+ GA_ID, GID_OPTS_PTRTRUE,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_PTRTRUE],
+ GA_Selected, nsoption_bool(truecolour_mouse_pointers),
+ GA_Disabled, ptr_disable,
+ CheckBoxEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_PTROS] = CheckBoxObj,
+ GA_ID, GID_OPTS_PTROS,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_PTROS],
+ GA_Selected, nsoption_bool(os_mouse_pointers),
+ GA_Disabled, ptr_disable,
+ CheckBoxEnd,
+ LayoutEnd, // mouse
+ CHILD_WeightedHeight,0,
+ LAYOUT_AddImage, LabelObj,
+ LABEL_Text, gadlab[LAB_OPTS_RESTART],
+ LabelEnd,
+ LayoutEnd, // page vgroup
+ CHILD_WeightedHeight, 0,
+ PageEnd, // pageadd
+ /*
+ ** Network
+ */
+ PAGE_Add, LayoutVObj,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_PROXY],
+ LAYOUT_AddChild, gow->objects[GID_OPTS_PROXY] = ChooserObj,
+ GA_ID, GID_OPTS_PROXY,
+ GA_RelVerify, TRUE,
+ CHOOSER_PopUp, TRUE,
+#ifdef __amigaos4__
+ CHOOSER_LabelArray, proxyopts,
+#else
+ CHOOSER_Labels, &gow->proxyoptslist,
+#endif
+ CHOOSER_Selected, proxytype,
+ ChooserEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_PROXY],
+ LabelEnd,
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_PROXY_HOST] = StringObj,
+ GA_ID, GID_OPTS_PROXY_HOST,
+ GA_RelVerify, TRUE,
+ GA_Disabled, proxyhostdisabled,
+ STRINGA_TextVal, nsoption_charp(http_proxy_host),
+ STRINGA_BufferPos,0,
+ StringEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_PROXY_PORT] = IntegerObj,
+ GA_ID, GID_OPTS_PROXY_PORT,
+ GA_RelVerify, TRUE,
+ GA_Disabled, proxyhostdisabled,
+ INTEGER_Number, nsoption_charp(http_proxy_port),
+ INTEGER_Minimum, 1,
+ INTEGER_Maximum, 65535,
+ INTEGER_Arrows, FALSE,
+ IntegerEnd,
+ CHILD_WeightedWidth, 0,
+ CHILD_Label, LabelObj,
+ LABEL_Text, ":",
+ LabelEnd,
+ LayoutEnd, //host:port group
+ CHILD_WeightedHeight, 0,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_PROXY_HOST],
+ LabelEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_PROXY_USER] = StringObj,
+ GA_ID, GID_OPTS_PROXY_USER,
+ GA_RelVerify, TRUE,
+ GA_Disabled, proxyauthdisabled,
+ STRINGA_TextVal, nsoption_charp(http_proxy_auth_user),
+ STRINGA_BufferPos,0,
+ StringEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_PROXY_USER],
+ LabelEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_PROXY_PASS] = StringObj,
+ GA_ID, GID_OPTS_PROXY_PASS,
+ GA_RelVerify, TRUE,
+ GA_Disabled, proxyauthdisabled,
+ STRINGA_TextVal, nsoption_charp(http_proxy_auth_pass),
+ STRINGA_BufferPos,0,
+ StringEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_PROXY_PASS],
+ LabelEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_PROXY_BYPASS] = StringObj,
+ GA_ID, GID_OPTS_PROXY_BYPASS,
+ GA_RelVerify, TRUE,
+ GA_Disabled, proxybypassdisabled,
+ STRINGA_TextVal, nsoption_charp(http_proxy_noproxy),
+ STRINGA_BufferPos, 0,
+ StringEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_PROXY_BYPASS],
+ LabelEnd,
+ LayoutEnd, // proxy
+ CHILD_WeightedHeight, 0,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_FETCHING],
+ LAYOUT_AddChild, gow->objects[GID_OPTS_FETCHMAX] = IntegerObj,
+ GA_ID, GID_OPTS_FETCHMAX,
+ GA_RelVerify, TRUE,
+ INTEGER_Number, nsoption_int(max_fetchers),
+ INTEGER_Minimum, 1,
+ INTEGER_Maximum, 99,
+ INTEGER_Arrows, TRUE,
+ IntegerEnd,
+ CHILD_WeightedWidth, 0,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_FETCHMAX],
+ LabelEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_FETCHHOST] = IntegerObj,
+ GA_ID, GID_OPTS_FETCHHOST,
+ GA_RelVerify, TRUE,
+ INTEGER_Number, nsoption_int(max_fetchers_per_host),
+ INTEGER_Minimum, 1,
+ INTEGER_Maximum, 99,
+ INTEGER_Arrows, TRUE,
+ IntegerEnd,
+ CHILD_WeightedWidth, 0,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_FETCHHOST],
+ LabelEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_FETCHCACHE] = IntegerObj,
+ GA_ID, GID_OPTS_FETCHCACHE,
+ GA_RelVerify, TRUE,
+ INTEGER_Number, nsoption_int(max_cached_fetch_handles),
+ INTEGER_Minimum, 1,
+ INTEGER_Maximum, 99,
+ INTEGER_Arrows, TRUE,
+ IntegerEnd,
+ CHILD_WeightedWidth, 0,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_FETCHCACHE],
+ LabelEnd,
+ LayoutEnd,
+ CHILD_WeightedHeight, 0,
+ LayoutEnd, // page vgroup
+ CHILD_WeightedHeight, 0,
+ PageEnd, // page object
+ /*
+ ** Rendering
+ */
+ PAGE_Add, LayoutVObj,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_IMAGES],
+ LAYOUT_AddChild, gow->objects[GID_OPTS_NATIVEBM] = ChooserObj,
+ GA_ID, GID_OPTS_NATIVEBM,
+ GA_RelVerify, TRUE,
+ CHOOSER_PopUp, TRUE,
+#ifdef __amigaos4__
+ CHOOSER_LabelArray, nativebmopts,
+#else
+ CHOOSER_Labels, &gow->nativebmoptslist,
+#endif
+ CHOOSER_Selected, nsoption_int(cache_bitmaps),
+ ChooserEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_NATIVEBM],
+ LabelEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_DITHERQ] = ChooserObj,
+ GA_ID, GID_OPTS_DITHERQ,
+ GA_RelVerify, TRUE,
+ GA_Disabled, ditherdisable,
+ CHOOSER_PopUp, TRUE,
+#ifdef __amigaos4__
+ CHOOSER_LabelArray, ditheropts,
+#else
+ CHOOSER_Labels, &gow->ditheroptslist,
+#endif
+ CHOOSER_Selected, nsoption_int(dither_quality),
+ ChooserEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_DITHERQ],
+ LabelEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_SCALEQ] = CheckBoxObj,
+ GA_ID, GID_OPTS_SCALEQ,
+ GA_Disabled, scaledisabled,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_SCALEQ],
+ GA_Selected, scaleselected,
+ CheckBoxEnd,
+ LayoutEnd, // images
+ CHILD_WeightedHeight, 0,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_ANIMS],
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_LabelColumn, PLACETEXT_RIGHT,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_ANIMSPEED] = StringObj,
+ GA_ID, GID_OPTS_ANIMSPEED,
+ GA_RelVerify, TRUE,
+ GA_Disabled, animspeeddisabled,
+ STRINGA_HookType, SHK_FLOAT,
+ STRINGA_TextVal, animspeed,
+ STRINGA_BufferPos,0,
+ StringEnd,
+ CHILD_WeightedWidth, 0,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[LAB_OPTS_SECS],
+ LabelEnd,
+ LayoutEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_ANIMSPEED],
+ LabelEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_ANIMDISABLE] = CheckBoxObj,
+ GA_ID, GID_OPTS_ANIMDISABLE,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_ANIMDISABLE],
+ GA_Selected, disableanims,
+ CheckBoxEnd,
+ LayoutEnd, //animations
+ CHILD_WeightedHeight, 0,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_DPI],
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_LabelColumn, PLACETEXT_RIGHT,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_DPI_Y] = IntegerObj,
+ GA_ID, GID_OPTS_DPI_Y,
+ GA_RelVerify, TRUE,
+ INTEGER_Number, nsoption_int(screen_ydpi),
+ INTEGER_Minimum, 60,
+ INTEGER_Maximum, 150,
+ INTEGER_Arrows, TRUE,
+ GA_Disabled, nsoption_bool(bitmap_fonts),
+ IntegerEnd,
+ CHILD_WeightedWidth, 0,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[LAB_OPTS_DPI],
+ LabelEnd,
+ LayoutEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_DPI_Y],
+ LabelEnd,
+ LayoutEnd, //animations
+ CHILD_WeightedHeight, 0,
+ LayoutEnd, // page vgroup
+ CHILD_WeightedHeight, 0,
+ PageEnd, // page object
+ /*
+ ** Fonts
+ */
+ PAGE_Add, LayoutVObj,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_FONTFACES],
+ LAYOUT_AddChild, gow->objects[GID_OPTS_FONT_SANS] = GetFontObj,
+ GA_ID, GID_OPTS_FONT_SANS,
+ GA_RelVerify, TRUE,
+ GETFONT_TextAttr, &fontsans,
+ GETFONT_OTagOnly, TRUE,
+ GETFONT_ScalableOnly, TRUE,
+ GetFontEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_FONT_SANS],
+ LabelEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_FONT_SERIF] = GetFontObj,
+ GA_ID, GID_OPTS_FONT_SERIF,
+ GA_RelVerify, TRUE,
+ GETFONT_TextAttr, &fontserif,
+ GETFONT_OTagOnly, TRUE,
+ GETFONT_ScalableOnly, TRUE,
+ GetFontEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_FONT_SERIF],
+ LabelEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_FONT_MONO] = GetFontObj,
+ GA_ID, GID_OPTS_FONT_MONO,
+ GA_RelVerify, TRUE,
+ GETFONT_TextAttr, &fontmono,
+ GETFONT_OTagOnly, TRUE,
+ GETFONT_ScalableOnly, TRUE,
+ GETFONT_FixedWidthOnly, TRUE,
+ GetFontEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_FONT_MONO],
+ LabelEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_FONT_CURSIVE] = GetFontObj,
+ GA_ID, GID_OPTS_FONT_CURSIVE,
+ GA_RelVerify, TRUE,
+ GETFONT_TextAttr, &fontcursive,
+ GETFONT_OTagOnly, TRUE,
+ GETFONT_ScalableOnly, TRUE,
+ GetFontEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_FONT_CURSIVE],
+ LabelEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_FONT_FANTASY] = GetFontObj,
+ GA_ID, GID_OPTS_FONT_FANTASY,
+ GA_RelVerify, TRUE,
+ GETFONT_TextAttr, &fontfantasy,
+ GETFONT_OTagOnly, TRUE,
+ GETFONT_ScalableOnly, TRUE,
+ GetFontEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_FONT_FANTASY],
+ LabelEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_FONT_DEFAULT] = ChooserObj,
+ GA_ID, GID_OPTS_FONT_DEFAULT,
+ GA_RelVerify, TRUE,
+ CHOOSER_PopUp, TRUE,
+#ifdef __amigaos4__
+ CHOOSER_LabelArray, fontopts,
+#else
+ CHOOSER_Labels, &gow->fontoptslist,
+#endif
+ CHOOSER_Selected, nsoption_int(font_default) - PLOT_FONT_FAMILY_SANS_SERIF,
+ ChooserEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_FONT_DEFAULT],
+ LabelEnd,
+ LayoutEnd, // font faces
+ CHILD_WeightedHeight, 0,
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_FONTSIZE],
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_LabelColumn, PLACETEXT_RIGHT,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_FONT_SIZE] = IntegerObj,
+ GA_ID, GID_OPTS_FONT_SIZE,
+ GA_RelVerify, TRUE,
+ INTEGER_Number, nsoption_int(font_size) / 10,
+ INTEGER_Minimum, 1,
+ INTEGER_Maximum, 99,
+ INTEGER_Arrows, TRUE,
+ IntegerEnd,
+ CHILD_WeightedWidth, 0,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[LAB_OPTS_PT],
+ LabelEnd,
+ LayoutEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_FONT_SIZE],
+ LabelEnd,
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_LabelColumn, PLACETEXT_RIGHT,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_FONT_MINSIZE] = IntegerObj,
+ GA_ID, GID_OPTS_FONT_MINSIZE,
+ GA_RelVerify, TRUE,
+ INTEGER_Number, nsoption_int(font_min_size) / 10,
+ INTEGER_Minimum, 1,
+ INTEGER_Maximum, 99,
+ INTEGER_Arrows, TRUE,
+ IntegerEnd,
+ CHILD_WeightedWidth, 0,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[LAB_OPTS_PT],
+ LabelEnd,
+ LayoutEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_FONT_MINSIZE],
+ LabelEnd,
+ LayoutEnd,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_MISC],
+ LAYOUT_AddChild, gow->objects[GID_OPTS_FONT_ANTIALIASING] = CheckBoxObj,
+ GA_ID, GID_OPTS_FONT_ANTIALIASING,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_FONT_ANTIALIASING],
+ GA_Selected, nsoption_bool(font_antialiasing),
+#ifndef __amigaos4__
+ GA_Disabled, TRUE,
+#endif
+ CheckBoxEnd,
+#ifndef __amigaos4__
+ LAYOUT_AddChild, gow->objects[GID_OPTS_FONT_BITMAP] = CheckBoxObj,
+ GA_ID, GID_OPTS_FONT_BITMAP,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_FONT_BITMAP],
+ GA_Selected, nsoption_bool(bitmap_fonts),
+ CheckBoxEnd,
+#endif
+ LayoutEnd,
+ LayoutEnd,
+ CHILD_WeightedHeight, 0,
+ LayoutEnd, // page vgroup
+ CHILD_WeightedHeight, 0,
+ PageEnd, // page object
+ /*
+ ** Cache
+ */
+ PAGE_Add, LayoutVObj,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_MEMCACHE],
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_LabelColumn, PLACETEXT_RIGHT,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_CACHE_MEM] = IntegerObj,
+ GA_ID, GID_OPTS_CACHE_MEM,
+ GA_RelVerify, TRUE,
+ INTEGER_Number, nsoption_int(memory_cache_size) / 1048576,
+ INTEGER_Minimum, 0,
+ INTEGER_Maximum, 2048,
+ INTEGER_Arrows, TRUE,
+ IntegerEnd,
+ CHILD_WeightedWidth, 0,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[LAB_OPTS_MB],
+ LabelEnd,
+ LayoutEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_CACHE_MEM],
+ LabelEnd,
+ LayoutEnd, // memory cache
+ CHILD_WeightedHeight, 0,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_DISCCACHE],
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_LabelColumn, PLACETEXT_RIGHT,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_CACHE_DISC] = IntegerObj,
+ GA_ID, GID_OPTS_CACHE_DISC,
+ GA_RelVerify, TRUE,
+ INTEGER_Number, nsoption_uint(disc_cache_size) / 1048576,
+ INTEGER_Minimum, 0,
+ INTEGER_Maximum, 4096,
+ INTEGER_Arrows, TRUE,
+ IntegerEnd,
+ CHILD_WeightedWidth, 0,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[LAB_OPTS_MB],
+ LabelEnd,
+ LayoutEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_CACHE_DISC],
+ LabelEnd,
+ LayoutEnd, // disc cache
+ CHILD_WeightedHeight, 0,
+ LayoutEnd, // page vgroup
+ CHILD_WeightedHeight, 0,
+ PageEnd, // page object
+ /*
+ ** Tabs
+ */
+ PAGE_Add, LayoutVObj,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_TABS],
+ LAYOUT_AddChild, gow->objects[GID_OPTS_TAB_ACTIVE] = CheckBoxObj,
+ GA_ID, GID_OPTS_TAB_ACTIVE,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_TAB_ACTIVE],
+ GA_Selected, !nsoption_bool(new_tab_is_active),
+ CheckBoxEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_TAB_LAST] = CheckBoxObj,
+ GA_ID, GID_OPTS_TAB_LAST,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_TAB_LAST],
+ GA_Selected, nsoption_bool(new_tab_last),
+ CheckBoxEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_TAB_2] = CheckBoxObj,
+ GA_ID, GID_OPTS_TAB_2,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_TAB_2],
+ GA_Selected, nsoption_bool(button_2_tab),
+ CheckBoxEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_TAB_ALWAYS] = CheckBoxObj,
+ GA_ID, GID_OPTS_TAB_ALWAYS,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_TAB_ALWAYS],
+ GA_Selected, nsoption_bool(tab_always_show),
+ GA_Disabled, tab_always_show_disabled,
+ CheckBoxEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_TAB_CLOSE] = CheckBoxObj,
+ GA_ID, GID_OPTS_TAB_CLOSE,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_TAB_CLOSE],
+ GA_Selected, nsoption_bool(tab_close_warn),
+ CheckBoxEnd,
+ LayoutEnd, // tabbed browsing
+ LayoutEnd,
+ LayoutEnd, // page vgroup
+ CHILD_WeightedHeight, 0,
+ PageEnd, // page object
+ /*
+ ** Advanced
+ */
+ PAGE_Add, LayoutVObj,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_DOWNLOADS],
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_OVERWRITE] = CheckBoxObj,
+ GA_ID, GID_OPTS_OVERWRITE,
+ GA_RelVerify, TRUE,
+ GA_Disabled, FALSE,
+ GA_Text, gadlab[GID_OPTS_OVERWRITE],
+ GA_Selected, nsoption_bool(ask_overwrite),
+ CheckBoxEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_NOTIFY] = CheckBoxObj,
+ GA_ID, GID_OPTS_NOTIFY,
+ GA_RelVerify, TRUE,
+ GA_Disabled, download_notify_disabled,
+ GA_Text, gadlab[GID_OPTS_NOTIFY],
+ GA_Selected, nsoption_bool(download_notify),
+#ifndef __amigaos4__
+ GA_Disabled, TRUE,
+#endif
+ CheckBoxEnd,
+ LayoutEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_DLDIR] = GetFileObj,
+ GA_ID, GID_OPTS_DLDIR,
+ GA_RelVerify, TRUE,
+ GETFILE_Drawer, nsoption_charp(download_dir),
+ GETFILE_DrawersOnly, TRUE,
+ GETFILE_ReadOnly, TRUE,
+ GETFILE_FullFileExpand, FALSE,
+ GetFileEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_DLDIR],
+ LabelEnd,
+ LayoutEnd, // downloads
+ CHILD_WeightedHeight, 0,
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_BEHAVIOUR],
+ LAYOUT_AddChild, gow->objects[GID_OPTS_STARTUP_NO_WIN] = CheckBoxObj,
+ GA_ID, GID_OPTS_STARTUP_NO_WIN,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_STARTUP_NO_WIN],
+ GA_Selected, nsoption_bool(startup_no_window),
+ CheckBoxEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_CLOSE_NO_QUIT] = CheckBoxObj,
+ GA_ID, GID_OPTS_CLOSE_NO_QUIT,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_CLOSE_NO_QUIT],
+ GA_Selected, nsoption_bool(close_no_quit),
+ CheckBoxEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_DOCKY] = CheckBoxObj,
+ GA_ID, GID_OPTS_DOCKY,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_DOCKY],
+ GA_Selected, !nsoption_bool(hide_docky_icon),
+#ifndef __amigaos4__
+ GA_Disabled, TRUE,
+#endif
+ CheckBoxEnd,
+ LayoutEnd, // behaviour
+ CHILD_WeightedHeight, 0,
+ LayoutEnd, // hgroup
+ CHILD_WeightedHeight, 0,
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_CLIPBOARD],
+ LAYOUT_AddChild, gow->objects[GID_OPTS_CLIPBOARD] = CheckBoxObj,
+ GA_ID, GID_OPTS_CLIPBOARD,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_CLIPBOARD],
+ GA_Selected, nsoption_bool(clipboard_write_utf8),
+ CheckBoxEnd,
+ LayoutEnd, // clipboard
+ CHILD_WeightedHeight, 0,
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_SEARCH],
+ LAYOUT_AddChild, gow->objects[GID_OPTS_SEARCH_PROV] = ChooserObj,
+ GA_ID, GID_OPTS_SEARCH_PROV,
+ GA_RelVerify, TRUE,
+ CHOOSER_PopUp, TRUE,
+ CHOOSER_Labels, websearch_list,
+ CHOOSER_Selected, nsoption_int(search_provider),
+ CHOOSER_MaxLabels, 40,
+ ChooserEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_SEARCH_PROV],
+ LabelEnd,
+ LayoutEnd, // search
+ CHILD_WeightedHeight, 0,
+ LayoutEnd, // hgroup
+ CHILD_WeightedHeight, 0,
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_MISC],
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_FASTSCROLL] = CheckBoxObj,
+ GA_ID, GID_OPTS_FASTSCROLL,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_FASTSCROLL],
+ GA_Selected, nsoption_bool(faster_scroll),
+ CheckBoxEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_SELECTMENU] = CheckBoxObj,
+ GA_ID, GID_OPTS_SELECTMENU,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_SELECTMENU],
+ GA_Selected, !nsoption_bool(core_select_menu),
+ GA_Disabled, !ami_selectmenu_is_safe(),
+ CheckBoxEnd,
+ LayoutEnd, // misc
+ CHILD_WeightedHeight, 0,
+
+ LayoutEnd, // page vgroup
+ CHILD_WeightedHeight, 0,
+ PageEnd, // page object
+ /*
+ ** Export
+ */
+#ifdef WITH_PDF_EXPORT
+ PAGE_Add, LayoutVObj,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_MARGINS],
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_LabelColumn, PLACETEXT_RIGHT,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_MARGIN_TOP] = IntegerObj,
+ GA_ID, GID_OPTS_MARGIN_TOP,
+ GA_RelVerify, TRUE,
+ INTEGER_Number, nsoption_int(margin_top),
+ INTEGER_Minimum, 0,
+ INTEGER_Maximum, 99,
+ INTEGER_Arrows, TRUE,
+ IntegerEnd,
+ CHILD_WeightedWidth, 0,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[LAB_OPTS_MM],
+ LabelEnd,
+ LayoutEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_MARGIN_TOP],
+ LabelEnd,
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_LabelColumn, PLACETEXT_RIGHT,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_MARGIN_LEFT] = IntegerObj,
+ GA_ID, GID_OPTS_MARGIN_LEFT,
+ GA_RelVerify, TRUE,
+ INTEGER_Number, nsoption_int(margin_left),
+ INTEGER_Minimum, 0,
+ INTEGER_Maximum, 99,
+ INTEGER_Arrows, TRUE,
+ IntegerEnd,
+ CHILD_WeightedWidth, 0,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[LAB_OPTS_MM],
+ LabelEnd,
+ LayoutEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_MARGIN_LEFT],
+ LabelEnd,
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_LabelColumn, PLACETEXT_RIGHT,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_MARGIN_BOTTOM] = IntegerObj,
+ GA_ID, GID_OPTS_MARGIN_BOTTOM,
+ GA_RelVerify, TRUE,
+ INTEGER_Number, nsoption_int(margin_bottom),
+ INTEGER_Minimum, 0,
+ INTEGER_Maximum, 99,
+ INTEGER_Arrows, TRUE,
+ IntegerEnd,
+ CHILD_WeightedWidth, 0,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[LAB_OPTS_MM],
+ LabelEnd,
+ LayoutEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_MARGIN_BOTTOM],
+ LabelEnd,
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_LabelColumn, PLACETEXT_RIGHT,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_MARGIN_RIGHT] = IntegerObj,
+ GA_ID, GID_OPTS_MARGIN_RIGHT,
+ GA_RelVerify, TRUE,
+ INTEGER_Number, nsoption_int(margin_right),
+ INTEGER_Minimum, 0,
+ INTEGER_Maximum, 99,
+ INTEGER_Arrows, TRUE,
+ IntegerEnd,
+ CHILD_WeightedWidth, 0,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[LAB_OPTS_MM],
+ LabelEnd,
+ LayoutEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_MARGIN_RIGHT],
+ LabelEnd,
+ LayoutEnd, // margins
+ CHILD_WeightedHeight, 0,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_SCALING],
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_LabelColumn, PLACETEXT_RIGHT,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_EXPORT_SCALE] = IntegerObj,
+ GA_ID, GID_OPTS_EXPORT_SCALE,
+ GA_RelVerify, TRUE,
+ INTEGER_Number, nsoption_int(export_scale),
+ INTEGER_Minimum, 0,
+ INTEGER_Maximum, 100,
+ INTEGER_Arrows, TRUE,
+ IntegerEnd,
+ CHILD_WeightedWidth, 0,
+ CHILD_Label, LabelObj,
+ LABEL_Text, "%",
+ LabelEnd,
+ LayoutEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text, gadlab[GID_OPTS_EXPORT_SCALE],
+ LabelEnd,
+ LayoutEnd, // scaling
+ CHILD_WeightedHeight, 0,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_APPEARANCE],
+ LAYOUT_AddChild, gow->objects[GID_OPTS_EXPORT_NOIMAGES] = CheckBoxObj,
+ GA_ID, GID_OPTS_EXPORT_NOIMAGES,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_EXPORT_NOIMAGES],
+ GA_Selected, nsoption_bool(suppress_images),
+ CheckBoxEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_EXPORT_NOBKG] = CheckBoxObj,
+ GA_ID, GID_OPTS_EXPORT_NOBKG,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_EXPORT_NOBKG],
+ GA_Selected, nsoption_bool(remove_backgrounds),
+ CheckBoxEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_EXPORT_LOOSEN] = CheckBoxObj,
+ GA_ID, GID_OPTS_EXPORT_LOOSEN,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_EXPORT_LOOSEN],
+ GA_Selected, nsoption_bool(enable_loosening),
+ CheckBoxEnd,
+ LayoutEnd, // appearance
+ CHILD_WeightedHeight, 0,
+ LAYOUT_AddChild, LayoutVObj,
+ LAYOUT_SpaceOuter, TRUE,
+ LAYOUT_BevelStyle, BVS_GROUP,
+ LAYOUT_Label, gadlab[GRP_OPTS_ADVANCED],
+ LAYOUT_AddChild, gow->objects[GID_OPTS_EXPORT_COMPRESS] = CheckBoxObj,
+ GA_ID, GID_OPTS_EXPORT_COMPRESS,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[GID_OPTS_EXPORT_COMPRESS],
+ GA_Selected, nsoption_bool(enable_PDF_compression),
+ CheckBoxEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_EXPORT_PASSWORD] = CheckBoxObj,
+ GA_ID, GID_OPTS_EXPORT_PASSWORD,
+ GA_RelVerify, TRUE,
+ GA_Disabled, TRUE,
+ GA_Text, gadlab[GID_OPTS_EXPORT_PASSWORD],
+ GA_Selected, nsoption_bool(enable_PDF_password),
+ CheckBoxEnd,
+ LayoutEnd, // export
+ CHILD_WeightedHeight, 0,
+ LayoutEnd, // page vgroup
+ CHILD_WeightedHeight, 0,
+ PageEnd, // page object
+#endif
+ End, // pagegroup
+ ClickTabEnd,
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_SAVE] = ButtonObj,
+ GA_ID,GID_OPTS_SAVE,
+ GA_Text,gadlab[GID_OPTS_SAVE],
+ GA_RelVerify,TRUE,
+ ButtonEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_USE] = ButtonObj,
+ GA_ID,GID_OPTS_USE,
+ GA_Text,gadlab[GID_OPTS_USE],
+ GA_RelVerify,TRUE,
+ ButtonEnd,
+ LAYOUT_AddChild, gow->objects[GID_OPTS_CANCEL] = ButtonObj,
+ GA_ID,GID_OPTS_CANCEL,
+ GA_Text,gadlab[GID_OPTS_CANCEL],
+ GA_RelVerify,TRUE,
+ ButtonEnd,
+ EndGroup, // save/use/cancel
+ EndGroup, // main
+ EndWindow;
+
+ gow->win = (struct Window *)RA_OpenWindow(gow->objects[OID_MAIN]);
+ gow->node = AddObject(window_list,AMINS_GUIOPTSWINDOW);
+ gow->node->objstruct = gow;
+ }
+ ami_utf8_free(homepage_url_lc);
+}
+
+static void ami_gui_opts_use(bool save)
+{
+ ULONG data, id = 0;
+ float animspeed;
+ struct TextAttr *tattr;
+ char *dot;
+ bool rescan_fonts = false;
+ bool old_tab_always_show;
+
+ ami_update_pointer(gow->win, GUI_POINTER_WAIT);
+
+ GetAttr(STRINGA_TextVal,gow->objects[GID_OPTS_HOMEPAGE],(ULONG *)&data);
+ nsoption_set_charp(homepage_url, (char *)ami_to_utf8_easy((char *)data));
+
+ GetAttr(STRINGA_TextVal,gow->objects[GID_OPTS_CONTENTLANG],(ULONG *)&data);
+ nsoption_set_charp(accept_language, (char *)strdup((char *)data));
+
+ GetAttr(GA_Selected, gow->objects[GID_OPTS_FROMLOCALE],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(accept_lang_locale, true);
+ } else {
+ nsoption_set_bool(accept_lang_locale, false);
+ }
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_HIDEADS],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(block_advertisements, true);
+ } else {
+ nsoption_set_bool(block_advertisements, false);
+ }
+
+ GetAttr(INTEGER_Number,gow->objects[GID_OPTS_HISTORY],(ULONG *)&nsoption_int(expire_url));
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_REFERRAL],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(send_referer, true);
+ } else {
+ nsoption_set_bool(send_referer, false);
+ }
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_JAVASCRIPT],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(enable_javascript, true);
+ } else {
+ nsoption_set_bool(enable_javascript, false);
+ }
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_DONOTTRACK],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(do_not_track, true);
+ } else {
+ nsoption_set_bool(do_not_track, false);
+ }
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_FASTSCROLL],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(faster_scroll, true);
+ } else {
+ nsoption_set_bool(faster_scroll, false);
+ }
+
+ GetAttr(RADIOBUTTON_Selected,gow->objects[GID_OPTS_SCREEN],(ULONG *)&data);
+ switch(data)
+ {
+ case 0:
+ nsoption_set_charp(pubscreen_name, strdup("\0"));
+ break;
+
+ case 1:
+ nsoption_set_charp(pubscreen_name, (char *)strdup("Workbench"));
+ break;
+
+ case 2:
+ GetAttr(STRINGA_TextVal,gow->objects[GID_OPTS_SCREENNAME],(ULONG *)&data);
+ nsoption_set_charp(pubscreen_name, (char *)strdup((char *)data));
+ break;
+ }
+
+ GetAttr(GETSCREENMODE_DisplayID, gow->objects[GID_OPTS_SCREENMODE], (ULONG *)&id);
+ if(id)
+ {
+ char *modeid = malloc(20);
+ sprintf(modeid,"0x%lx", id);
+ nsoption_set_charp(screen_modeid, modeid);
+ }
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_WIN_SIMPLE],(ULONG *)&data);
+ if ((data == TRUE) && (nsoption_bool(window_simple_refresh) == false)) {
+ nsoption_set_bool(window_simple_refresh, true);
+ nsoption_set_int(screen_compositing, 0);
+ } else if ((data == FALSE) && (nsoption_bool(window_simple_refresh) == true)) {
+ nsoption_set_bool(window_simple_refresh, false);
+ nsoption_set_int(screen_compositing, -1);
+ }
+
+ GetAttr(GETFILE_Drawer,gow->objects[GID_OPTS_THEME],(ULONG *)&data);
+ nsoption_set_charp(theme, (char *)strdup((char *)data));
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_PTRTRUE],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(truecolour_mouse_pointers, true);
+ } else {
+ nsoption_set_bool(truecolour_mouse_pointers, false);
+ }
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_PTROS],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(os_mouse_pointers, true);
+ } else {
+ nsoption_set_bool(os_mouse_pointers, false);
+ }
+
+ GetAttr(CHOOSER_Selected,gow->objects[GID_OPTS_PROXY],(ULONG *)&data);
+ if(data)
+ {
+ nsoption_set_bool(http_proxy, true);
+ nsoption_set_int(http_proxy_auth, data - 1);
+ }
+ else
+ {
+ nsoption_set_bool(http_proxy, false);
+ }
+
+ GetAttr(STRINGA_TextVal,gow->objects[GID_OPTS_PROXY_HOST],(ULONG *)&data);
+ nsoption_set_charp(http_proxy_host, (char *)strdup((char *)data));
+
+ GetAttr(INTEGER_Number,gow->objects[GID_OPTS_PROXY_PORT],(ULONG *)&nsoption_int(http_proxy_port));
+
+ GetAttr(STRINGA_TextVal,gow->objects[GID_OPTS_PROXY_USER],(ULONG *)&data);
+ nsoption_set_charp(http_proxy_auth_user, (char *)strdup((char *)data));
+
+ GetAttr(STRINGA_TextVal,gow->objects[GID_OPTS_PROXY_PASS],(ULONG *)&data);
+ nsoption_set_charp(http_proxy_auth_pass, (char *)strdup((char *)data));
+
+ GetAttr(STRINGA_TextVal,gow->objects[GID_OPTS_PROXY_BYPASS],(ULONG *)&data);
+ nsoption_set_charp(http_proxy_noproxy, (char *)strdup((char *)data));
+
+ GetAttr(INTEGER_Number,gow->objects[GID_OPTS_FETCHMAX],(ULONG *)&nsoption_int(max_fetchers));
+ GetAttr(INTEGER_Number,gow->objects[GID_OPTS_FETCHHOST],(ULONG *)&nsoption_int(max_fetchers_per_host));
+ GetAttr(INTEGER_Number,gow->objects[GID_OPTS_FETCHCACHE],(ULONG *)&nsoption_int(max_cached_fetch_handles));
+
+ GetAttr(CHOOSER_Selected,gow->objects[GID_OPTS_NATIVEBM],(ULONG *)&nsoption_int(cache_bitmaps));
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_SCALEQ],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(scale_quality, true);
+ } else {
+ nsoption_set_bool(scale_quality, false);
+ }
+
+ GetAttr(CHOOSER_Selected,gow->objects[GID_OPTS_DITHERQ],(ULONG *)&nsoption_int(dither_quality));
+
+ GetAttr(STRINGA_TextVal,gow->objects[GID_OPTS_ANIMSPEED],(ULONG *)&data);
+ animspeed = strtof((char *)data, NULL);
+ nsoption_set_int(minimum_gif_delay, (int)(animspeed * 100));
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_ANIMDISABLE],(ULONG *)&data);
+ if(data) {
+ nsoption_set_bool(animate_images, false);
+ } else {
+ nsoption_set_bool(animate_images, true);
+ }
+
+ GetAttr(INTEGER_Number,gow->objects[GID_OPTS_DPI_Y],(ULONG *)&nsoption_int(screen_ydpi));
+ ami_font_setdevicedpi(id); // id set above
+
+ GetAttr(GETFONT_TextAttr,gow->objects[GID_OPTS_FONT_SANS],(ULONG *)&data);
+ tattr = (struct TextAttr *)data;
+
+ if((dot = strrchr(tattr->ta_Name,'.'))) *dot = '\0';
+ nsoption_set_charp(font_sans, (char *)strdup((char *)tattr->ta_Name));
+
+ GetAttr(GETFONT_TextAttr,gow->objects[GID_OPTS_FONT_SERIF],(ULONG *)&data);
+ tattr = (struct TextAttr *)data;
+
+ if((dot = strrchr(tattr->ta_Name,'.'))) *dot = '\0';
+ nsoption_set_charp(font_serif, (char *)strdup((char *)tattr->ta_Name));
+
+ GetAttr(GETFONT_TextAttr,gow->objects[GID_OPTS_FONT_MONO],(ULONG *)&data);
+ tattr = (struct TextAttr *)data;
+
+ if((dot = strrchr(tattr->ta_Name,'.'))) *dot = '\0';
+ nsoption_set_charp(font_mono, (char *)strdup((char *)tattr->ta_Name));
+
+ GetAttr(GETFONT_TextAttr,gow->objects[GID_OPTS_FONT_CURSIVE],(ULONG *)&data);
+ tattr = (struct TextAttr *)data;
+
+ if((dot = strrchr(tattr->ta_Name,'.'))) *dot = '\0';
+ nsoption_set_charp(font_cursive, (char *)strdup((char *)tattr->ta_Name));
+
+ GetAttr(GETFONT_TextAttr,gow->objects[GID_OPTS_FONT_FANTASY],(ULONG *)&data);
+ tattr = (struct TextAttr *)data;
+
+ if((dot = strrchr(tattr->ta_Name,'.'))) *dot = '\0';
+ nsoption_set_charp(font_fantasy, (char *)strdup((char *)tattr->ta_Name));
+
+ GetAttr(CHOOSER_Selected,gow->objects[GID_OPTS_FONT_DEFAULT],(ULONG *)&nsoption_int(font_default));
+ nsoption_set_int(font_default, nsoption_int(font_default) + PLOT_FONT_FAMILY_SANS_SERIF);
+
+ GetAttr(INTEGER_Number,gow->objects[GID_OPTS_FONT_SIZE],(ULONG *)&nsoption_int(font_size));
+ nsoption_set_int(font_size, nsoption_int(font_size) * 10);
+
+ GetAttr(INTEGER_Number,gow->objects[GID_OPTS_FONT_MINSIZE],(ULONG *)&nsoption_int(font_min_size));
+ nsoption_set_int(font_min_size, nsoption_int(font_min_size) * 10);
+
+ GetAttr(GA_Selected, gow->objects[GID_OPTS_FONT_ANTIALIASING], (ULONG *)&data);
+ if(data) {
+ nsoption_set_bool(font_antialiasing, true);
+ } else {
+ nsoption_set_bool(font_antialiasing, false);
+ }
+
+#ifndef __amigaos4__
+ GetAttr(GA_Selected, gow->objects[GID_OPTS_FONT_BITMAP], (ULONG *)&data);
+ ami_font_fini();
+ if(data) {
+ nsoption_set_bool(bitmap_fonts, true);
+ } else {
+ nsoption_set_bool(bitmap_fonts, false);
+ }
+ ami_font_init();
+#endif
+
+ GetAttr(INTEGER_Number,gow->objects[GID_OPTS_CACHE_MEM],(ULONG *)&nsoption_int(memory_cache_size));
+ nsoption_set_int(memory_cache_size, nsoption_int(memory_cache_size) * 1048576);
+
+ GetAttr(INTEGER_Number,gow->objects[GID_OPTS_CACHE_DISC],(ULONG *)&nsoption_uint(disc_cache_size));
+ nsoption_set_uint(disc_cache_size, nsoption_uint(disc_cache_size) * 1048576);
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_OVERWRITE],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(ask_overwrite, true);
+ } else {
+ nsoption_set_bool(ask_overwrite, false);
+ }
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_NOTIFY],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(download_notify, true);
+ } else {
+ nsoption_set_bool(download_notify, false);
+ }
+
+ GetAttr(GETFILE_Drawer,gow->objects[GID_OPTS_DLDIR],(ULONG *)&data);
+ if((nsoption_charp(download_dir) == NULL) ||
+ (strcmp((char *)data, nsoption_charp(download_dir)) != 0)) {
+ nsoption_set_charp(download_dir, (char *)strdup((char *)data));
+ ami_file_req_free();
+ ami_file_req_init();
+ }
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_TAB_ACTIVE],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(new_tab_is_active, false);
+ } else {
+ nsoption_set_bool(new_tab_is_active, true);
+ }
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_TAB_LAST],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(new_tab_last, true);
+ } else {
+ nsoption_set_bool(new_tab_last, false);
+ }
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_TAB_2],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(button_2_tab, true);
+ } else {
+ nsoption_set_bool(button_2_tab, false);
+ }
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_TAB_CLOSE],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(tab_close_warn, true);
+ } else {
+ nsoption_set_bool(tab_close_warn, false);
+ }
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_TAB_ALWAYS],(ULONG *)&data);
+ old_tab_always_show = nsoption_bool(tab_always_show);
+
+ if (data) {
+ nsoption_set_bool(tab_always_show, true);
+ } else {
+ nsoption_set_bool(tab_always_show, false);
+ }
+
+ if(old_tab_always_show != nsoption_bool(tab_always_show))
+ ami_gui_tabs_toggle_all();
+
+ GetAttr(CHOOSER_Selected,gow->objects[GID_OPTS_SEARCH_PROV],(ULONG *)&nsoption_int(search_provider));
+ search_web_select_provider(nsoption_int(search_provider));
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_CLIPBOARD],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(clipboard_write_utf8, true);
+ } else {
+ nsoption_set_bool(clipboard_write_utf8, false);
+ }
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_SELECTMENU],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(core_select_menu, false);
+ } else {
+ nsoption_set_bool(core_select_menu, true);
+ }
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_STARTUP_NO_WIN],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(startup_no_window, true);
+ } else {
+ nsoption_set_bool(startup_no_window, false);
+ }
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_CLOSE_NO_QUIT],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(close_no_quit, true);
+ } else {
+ nsoption_set_bool(close_no_quit, false);
+ }
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_DOCKY],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(hide_docky_icon, false);
+ } else {
+ nsoption_set_bool(hide_docky_icon, true);
+ }
+
+#ifdef WITH_PDF_EXPORT
+ GetAttr(INTEGER_Number,gow->objects[GID_OPTS_MARGIN_TOP],(ULONG *)&nsoption_int(margin_top));
+
+ GetAttr(INTEGER_Number,gow->objects[GID_OPTS_MARGIN_LEFT],(ULONG *)&nsoption_int(margin_left));
+
+ GetAttr(INTEGER_Number,gow->objects[GID_OPTS_MARGIN_BOTTOM],(ULONG *)&nsoption_int(margin_bottom));
+
+ GetAttr(INTEGER_Number,gow->objects[GID_OPTS_MARGIN_RIGHT],(ULONG *)&nsoption_int(margin_right));
+
+ GetAttr(INTEGER_Number,gow->objects[GID_OPTS_EXPORT_SCALE],(ULONG *)&nsoption_int(export_scale));
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_EXPORT_NOIMAGES],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(suppress_images, true);
+ } else {
+ nsoption_set_bool(suppress_images, false);
+ }
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_EXPORT_NOBKG],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(remove_backgrounds, true);
+ } else {
+ nsoption_set_bool(remove_backgrounds, false);
+ }
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_EXPORT_LOOSEN],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(enable_loosening, true);
+ } else {
+ nsoption_set_bool(enable_loosening, false);
+ }
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_EXPORT_COMPRESS],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(enable_PDF_compression, true);
+ } else {
+ nsoption_set_bool(enable_PDF_compression, false);
+ }
+
+ GetAttr(GA_Selected,gow->objects[GID_OPTS_EXPORT_PASSWORD],(ULONG *)&data);
+ if (data) {
+ nsoption_set_bool(enable_PDF_password, true);
+ } else {
+ nsoption_set_bool(enable_PDF_password, false);
+ }
+#endif
+
+ if(rescan_fonts == true) {
+ ami_font_finiscanner();
+ ami_font_initscanner(true, false);
+ }
+
+ if(save == true) {
+ nsoption_write(current_user_options, NULL, NULL);
+ ami_font_savescanner(); /* just in case it has changed and been used only */
+ }
+
+ ami_menu_check_toggled = true;
+
+ ami_update_pointer(gow->win, GUI_POINTER_DEFAULT);
+}
+
+void ami_gui_opts_close(void)
+{
+ DisposeObject(gow->objects[OID_MAIN]);
+ ami_gui_opts_free(gow);
+ DelObject(gow->node);
+ gow = NULL;
+}
+
+BOOL ami_gui_opts_event(void)
+{
+ /* return TRUE if window destroyed */
+ ULONG result,data = 0;
+ uint16 code;
+ STRPTR text;
+
+ while((result = RA_HandleInput(gow->objects[OID_MAIN],&code)) != WMHI_LASTMSG)
+ {
+ switch(result & WMHI_CLASSMASK) // class
+ {
+ case WMHI_CLOSEWINDOW:
+ ami_gui_opts_close();
+ return TRUE;
+ break;
+
+ case WMHI_GADGETHELP:
+ if((result & WMHI_GADGETMASK) == 0) {
+ /* Pointer not over our window */
+ ami_help_open(AMI_HELP_MAIN, scrn);
+ } else {
+ /* TODO: Make this sensitive to the tab the user is currently on */
+ ami_help_open(AMI_HELP_PREFS, scrn);
+ }
+ break;
+
+ case WMHI_GADGETUP:
+ switch(result & WMHI_GADGETMASK)
+ {
+ case GID_OPTS_SAVE:
+ ami_gui_opts_use(true);
+ ami_gui_opts_close();
+ return TRUE;
+ break;
+
+ case GID_OPTS_USE:
+ ami_gui_opts_use(false);
+ // fall through
+
+ case GID_OPTS_CANCEL:
+ ami_gui_opts_close();
+ return TRUE;
+ break;
+
+ case GID_OPTS_HOMEPAGE_DEFAULT:
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_HOMEPAGE],
+ gow->win,NULL,STRINGA_TextVal,NETSURF_HOMEPAGE,
+ TAG_DONE);
+ break;
+
+ case GID_OPTS_HOMEPAGE_CURRENT:
+ if(cur_gw) RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_HOMEPAGE],
+ gow->win, NULL, STRINGA_TextVal,
+ nsurl_access(browser_window_get_url(cur_gw->bw)), TAG_DONE);
+ break;
+
+ case GID_OPTS_HOMEPAGE_BLANK:
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_HOMEPAGE],
+ gow->win, NULL, STRINGA_TextVal,
+ "about:blank", TAG_DONE);
+ break;
+
+ case GID_OPTS_FROMLOCALE:
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_CONTENTLANG],
+ gow->win, NULL, GA_Disabled, code, TAG_DONE);
+
+ if(code && (text = ami_locale_langs()))
+ {
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_CONTENTLANG],
+ gow->win, NULL, STRINGA_TextVal, text, TAG_DONE);
+ FreeVec(text);
+ }
+ break;
+
+ case GID_OPTS_SCREEN:
+ GetAttr(RADIOBUTTON_Selected,gow->objects[GID_OPTS_SCREEN],(ULONG *)&data);
+ switch(data)
+ {
+ case 0:
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_SCREENMODE],
+ gow->win,NULL, GA_Disabled, FALSE, TAG_DONE);
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_SCREENNAME],
+ gow->win,NULL, GA_Disabled, TRUE, TAG_DONE);
+ break;
+
+ case 1:
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_SCREENMODE],
+ gow->win,NULL, GA_Disabled, TRUE, TAG_DONE);
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_SCREENNAME],
+ gow->win,NULL, GA_Disabled, TRUE, TAG_DONE);
+ break;
+
+ case 2:
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_SCREENMODE],
+ gow->win,NULL, GA_Disabled, TRUE, TAG_DONE);
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_SCREENNAME],
+ gow->win,NULL, GA_Disabled, FALSE, TAG_DONE);
+ break;
+ }
+ break;
+
+ case GID_OPTS_SCREENMODE:
+ IDoMethod(gow->objects[GID_OPTS_SCREENMODE],
+ GSM_REQUEST,gow->win);
+ break;
+
+ case GID_OPTS_THEME:
+ IDoMethod(gow->objects[GID_OPTS_THEME],
+ GFILE_REQUEST,gow->win);
+ break;
+
+ case GID_OPTS_PROXY:
+ GetAttr(CHOOSER_Selected,gow->objects[GID_OPTS_PROXY],(ULONG *)&data);
+ switch(data)
+ {
+ case 0:
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_PROXY_HOST],
+ gow->win,NULL, GA_Disabled, TRUE, TAG_DONE);
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_PROXY_PORT],
+ gow->win,NULL, GA_Disabled, TRUE, TAG_DONE);
+
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_PROXY_USER],
+ gow->win,NULL, GA_Disabled, TRUE, TAG_DONE);
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_PROXY_PASS],
+ gow->win,NULL, GA_Disabled, TRUE, TAG_DONE);
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_PROXY_BYPASS],
+ gow->win,NULL, GA_Disabled, TRUE, TAG_DONE);
+ break;
+ case 1:
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_PROXY_HOST],
+ gow->win,NULL, GA_Disabled, FALSE, TAG_DONE);
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_PROXY_PORT],
+ gow->win,NULL, GA_Disabled, FALSE, TAG_DONE);
+
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_PROXY_USER],
+ gow->win,NULL, GA_Disabled, TRUE, TAG_DONE);
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_PROXY_PASS],
+ gow->win,NULL, GA_Disabled, TRUE, TAG_DONE);
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_PROXY_BYPASS],
+ gow->win,NULL, GA_Disabled, FALSE, TAG_DONE);
+ break;
+
+ case 2:
+ case 3:
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_PROXY_HOST],
+ gow->win,NULL, GA_Disabled, FALSE, TAG_DONE);
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_PROXY_PORT],
+ gow->win,NULL, GA_Disabled, FALSE, TAG_DONE);
+
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_PROXY_USER],
+ gow->win,NULL, GA_Disabled, FALSE, TAG_DONE);
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_PROXY_PASS],
+ gow->win,NULL, GA_Disabled, FALSE, TAG_DONE);
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_PROXY_BYPASS],
+ gow->win,NULL, GA_Disabled, FALSE, TAG_DONE);
+ break;
+ }
+ break;
+
+ case GID_OPTS_ANIMDISABLE:
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_ANIMSPEED],
+ gow->win,NULL, GA_Disabled, code, TAG_DONE);
+ break;
+
+ case GID_OPTS_FONT_SANS:
+ IDoMethod(gow->objects[GID_OPTS_FONT_SANS],
+ GFONT_REQUEST,gow->win);
+ break;
+
+ case GID_OPTS_FONT_SERIF:
+ IDoMethod(gow->objects[GID_OPTS_FONT_SERIF],
+ GFONT_REQUEST,gow->win);
+ break;
+
+ case GID_OPTS_FONT_MONO:
+ IDoMethod(gow->objects[GID_OPTS_FONT_MONO],
+ GFONT_REQUEST,gow->win);
+ break;
+
+ case GID_OPTS_FONT_CURSIVE:
+ IDoMethod(gow->objects[GID_OPTS_FONT_CURSIVE],
+ GFONT_REQUEST,gow->win);
+ break;
+
+ case GID_OPTS_FONT_FANTASY:
+ IDoMethod(gow->objects[GID_OPTS_FONT_FANTASY],
+ GFONT_REQUEST,gow->win);
+ break;
+#ifndef __amigaos4__
+ case GID_OPTS_FONT_BITMAP:
+ RefreshSetGadgetAttrs((struct Gadget *)gow->objects[GID_OPTS_DPI_Y],
+ gow->win, NULL, GA_Disabled, code, TAG_DONE);
+ break;
+#endif
+ case GID_OPTS_DLDIR:
+ IDoMethod(gow->objects[GID_OPTS_DLDIR],
+ GFILE_REQUEST,gow->win);
+ break;
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
+
+struct List *ami_gui_opts_websearch(void)
+{
+ struct List *list;
+ struct Node *node;
+ const char *name;
+ int iter;
+
+ list = AllocVecTagList(sizeof(struct List), NULL);
+ NewList(list);
+
+ if (nsoption_charp(search_engines_file) == NULL) return list;
+
+ for (iter = search_web_iterate_providers(0, &name);
+ iter != -1;
+ iter = search_web_iterate_providers(iter, &name)) {
+ node = AllocChooserNode(CNA_Text, name, TAG_DONE);
+ AddTail(list, node);
+ }
+
+ return list;
+}
+
+void ami_gui_opts_websearch_free(struct List *websearchlist)
+{
+ struct Node *node;
+ struct Node *nnode;
+
+ if(IsListEmpty(websearchlist)) return;
+ node = GetHead(websearchlist);
+
+ do {
+ nnode = GetSucc(node);
+ Remove(node);
+ FreeChooserNode(node);
+ } while((node = nnode));
+
+ FreeVec(websearchlist);
+}
+
diff --git a/frontends/amiga/gui_options.h b/frontends/amiga/gui_options.h
new file mode 100755
index 000000000..04c1be51e
--- /dev/null
+++ b/frontends/amiga/gui_options.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2009 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_GUI_OPTIONS_H
+#define AMIGA_GUI_OPTIONS_H
+/* Prefs GUI control */
+void ami_gui_opts_open(void);
+BOOL ami_gui_opts_event(void);
+void ami_gui_opts_close(void);
+
+/* Web search list */
+struct List *ami_gui_opts_websearch(void);
+void ami_gui_opts_websearch_free(struct List *websearchlist);
+
+char *current_user_options;
+#endif
+
diff --git a/frontends/amiga/hash/xxhash.c b/frontends/amiga/hash/xxhash.c
new file mode 100644
index 000000000..d55a36154
--- /dev/null
+++ b/frontends/amiga/hash/xxhash.c
@@ -0,0 +1,962 @@
+/*
+xxHash - Fast Hash algorithm
+Copyright (C) 2012-2015, Yann Collet
+
+BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+You can contact the author at :
+- xxHash source repository : https://github.com/Cyan4973/xxHash
+*/
+
+
+/**************************************
+* Tuning parameters
+**************************************/
+/* XXH_FORCE_MEMORY_ACCESS
+ * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.
+ * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.
+ * The below switch allow to select different access method for improved performance.
+ * Method 0 (default) : use `memcpy()`. Safe and portable.
+ * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).
+ * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.
+ * Method 2 : direct access. This method is portable but violate C standard.
+ * It can generate buggy code on targets which generate assembly depending on alignment.
+ * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)
+ * See http://stackoverflow.com/a/32095106/646947 for details.
+ * Prefer these methods in priority order (0 > 1 > 2)
+ */
+#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */
+# if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )
+# define XXH_FORCE_MEMORY_ACCESS 2
+# elif defined(__INTEL_COMPILER) || \
+ (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) ))
+# define XXH_FORCE_MEMORY_ACCESS 1
+# endif
+#endif
+
+/* XXH_ACCEPT_NULL_INPUT_POINTER :
+ * If the input pointer is a null pointer, xxHash default behavior is to trigger a memory access error, since it is a bad pointer.
+ * When this option is enabled, xxHash output for null input pointers will be the same as a null-length input.
+ * By default, this option is disabled. To enable it, uncomment below define :
+ */
+/* #define XXH_ACCEPT_NULL_INPUT_POINTER 1 */
+
+/* XXH_FORCE_NATIVE_FORMAT :
+ * By default, xxHash library provides endian-independant Hash values, based on little-endian convention.
+ * Results are therefore identical for little-endian and big-endian CPU.
+ * This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format.
+ * Should endian-independance be of no importance for your application, you may set the #define below to 1,
+ * to improve speed for Big-endian CPU.
+ * This option has no impact on Little_Endian CPU.
+ */
+#define XXH_FORCE_NATIVE_FORMAT 1
+
+/* XXH_USELESS_ALIGN_BRANCH :
+ * This is a minor performance trick, only useful with lots of very small keys.
+ * It means : don't make a test between aligned/unaligned, because performance will be the same.
+ * It saves one initial branch per hash.
+ */
+#if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
+# define XXH_USELESS_ALIGN_BRANCH 1
+#endif
+
+
+/**************************************
+* Compiler Specific Options
+***************************************/
+#ifdef _MSC_VER /* Visual Studio */
+# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
+# define FORCE_INLINE static __forceinline
+#else
+# if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
+# ifdef __GNUC__
+# define FORCE_INLINE static inline __attribute__((always_inline))
+# else
+# define FORCE_INLINE static inline
+# endif
+# else
+# define FORCE_INLINE static
+# endif /* __STDC_VERSION__ */
+#endif
+
+
+/**************************************
+* Includes & Memory related functions
+***************************************/
+#include "xxhash.h"
+/* Modify the local functions below should you wish to use some other memory routines */
+/* for malloc(), free() */
+#include <stdlib.h>
+static void* XXH_malloc(size_t s) { return malloc(s); }
+static void XXH_free (void* p) { free(p); }
+/* for memcpy() */
+#include <string.h>
+static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); }
+
+
+/**************************************
+* Basic Types
+***************************************/
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
+# include <stdint.h>
+ typedef uint8_t BYTE;
+ typedef uint16_t U16;
+ typedef uint32_t U32;
+ typedef int32_t S32;
+ typedef uint64_t U64;
+#else
+ typedef unsigned char BYTE;
+ typedef unsigned short U16;
+ typedef unsigned int U32;
+ typedef signed int S32;
+ typedef unsigned long long U64;
+#endif
+
+
+#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))
+
+/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */
+static U32 XXH_read32(const void* memPtr) { return *(const U32*) memPtr; }
+static U64 XXH_read64(const void* memPtr) { return *(const U64*) memPtr; }
+
+#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))
+
+/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */
+/* currently only defined for gcc and icc */
+typedef union { U32 u32; U64 u64; } __attribute__((packed)) unalign;
+
+static U32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; }
+static U64 XXH_read64(const void* ptr) { return ((const unalign*)ptr)->u64; }
+
+#else
+
+/* portable and safe solution. Generally efficient.
+ * see : http://stackoverflow.com/a/32095106/646947
+ */
+
+static U32 XXH_read32(const void* memPtr)
+{
+ U32 val;
+ memcpy(&val, memPtr, sizeof(val));
+ return val;
+}
+
+static U64 XXH_read64(const void* memPtr)
+{
+ U64 val;
+ memcpy(&val, memPtr, sizeof(val));
+ return val;
+}
+
+#endif // XXH_FORCE_DIRECT_MEMORY_ACCESS
+
+
+/******************************************
+* Compiler-specific Functions and Macros
+******************************************/
+#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+
+/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */
+#if defined(_MSC_VER)
+# define XXH_rotl32(x,r) _rotl(x,r)
+# define XXH_rotl64(x,r) _rotl64(x,r)
+#else
+# define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r)))
+# define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r)))
+#endif
+
+#if defined(_MSC_VER) /* Visual Studio */
+# define XXH_swap32 _byteswap_ulong
+# define XXH_swap64 _byteswap_uint64
+#elif GCC_VERSION >= 403
+# define XXH_swap32 __builtin_bswap32
+# define XXH_swap64 __builtin_bswap64
+#else
+static U32 XXH_swap32 (U32 x)
+{
+ return ((x << 24) & 0xff000000 ) |
+ ((x << 8) & 0x00ff0000 ) |
+ ((x >> 8) & 0x0000ff00 ) |
+ ((x >> 24) & 0x000000ff );
+}
+static U64 XXH_swap64 (U64 x)
+{
+ return ((x << 56) & 0xff00000000000000ULL) |
+ ((x << 40) & 0x00ff000000000000ULL) |
+ ((x << 24) & 0x0000ff0000000000ULL) |
+ ((x << 8) & 0x000000ff00000000ULL) |
+ ((x >> 8) & 0x00000000ff000000ULL) |
+ ((x >> 24) & 0x0000000000ff0000ULL) |
+ ((x >> 40) & 0x000000000000ff00ULL) |
+ ((x >> 56) & 0x00000000000000ffULL);
+}
+#endif
+
+
+/***************************************
+* Architecture Macros
+***************************************/
+typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess;
+
+/* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example one the compiler command line */
+#ifndef XXH_CPU_LITTLE_ENDIAN
+ static const int one = 1;
+# define XXH_CPU_LITTLE_ENDIAN (*(const char*)(&one))
+#endif
+
+
+/*****************************
+* Memory reads
+*****************************/
+typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment;
+
+FORCE_INLINE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align)
+{
+ if (align==XXH_unaligned)
+ return endian==XXH_littleEndian ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr));
+ else
+ return endian==XXH_littleEndian ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr);
+}
+
+FORCE_INLINE U32 XXH_readLE32(const void* ptr, XXH_endianess endian)
+{
+ return XXH_readLE32_align(ptr, endian, XXH_unaligned);
+}
+
+FORCE_INLINE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align)
+{
+ if (align==XXH_unaligned)
+ return endian==XXH_littleEndian ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr));
+ else
+ return endian==XXH_littleEndian ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr);
+}
+
+FORCE_INLINE U64 XXH_readLE64(const void* ptr, XXH_endianess endian)
+{
+ return XXH_readLE64_align(ptr, endian, XXH_unaligned);
+}
+
+
+/***************************************
+* Macros
+***************************************/
+#define XXH_STATIC_ASSERT(c) { enum { XXH_static_assert = 1/(!!(c)) }; } /* use only *after* variable declarations */
+
+
+/***************************************
+* Constants
+***************************************/
+#define PRIME32_1 2654435761U
+#define PRIME32_2 2246822519U
+#define PRIME32_3 3266489917U
+#define PRIME32_4 668265263U
+#define PRIME32_5 374761393U
+
+#define PRIME64_1 11400714785074694791ULL
+#define PRIME64_2 14029467366897019727ULL
+#define PRIME64_3 1609587929392839161ULL
+#define PRIME64_4 9650029242287828579ULL
+#define PRIME64_5 2870177450012600261ULL
+
+
+/*****************************
+* Simple Hash Functions
+*****************************/
+FORCE_INLINE U32 XXH32_endian_align(const void* input, size_t len, U32 seed, XXH_endianess endian, XXH_alignment align)
+{
+ const BYTE* p = (const BYTE*)input;
+ const BYTE* bEnd = p + len;
+ U32 h32;
+#define XXH_get32bits(p) XXH_readLE32_align(p, endian, align)
+
+#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
+ if (p==NULL)
+ {
+ len=0;
+ bEnd=p=(const BYTE*)(size_t)16;
+ }
+#endif
+
+ if (len>=16)
+ {
+ const BYTE* const limit = bEnd - 16;
+ U32 v1 = seed + PRIME32_1 + PRIME32_2;
+ U32 v2 = seed + PRIME32_2;
+ U32 v3 = seed + 0;
+ U32 v4 = seed - PRIME32_1;
+
+ do
+ {
+ v1 += XXH_get32bits(p) * PRIME32_2;
+ v1 = XXH_rotl32(v1, 13);
+ v1 *= PRIME32_1;
+ p+=4;
+ v2 += XXH_get32bits(p) * PRIME32_2;
+ v2 = XXH_rotl32(v2, 13);
+ v2 *= PRIME32_1;
+ p+=4;
+ v3 += XXH_get32bits(p) * PRIME32_2;
+ v3 = XXH_rotl32(v3, 13);
+ v3 *= PRIME32_1;
+ p+=4;
+ v4 += XXH_get32bits(p) * PRIME32_2;
+ v4 = XXH_rotl32(v4, 13);
+ v4 *= PRIME32_1;
+ p+=4;
+ }
+ while (p<=limit);
+
+ h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);
+ }
+ else
+ {
+ h32 = seed + PRIME32_5;
+ }
+
+ h32 += (U32) len;
+
+ while (p+4<=bEnd)
+ {
+ h32 += XXH_get32bits(p) * PRIME32_3;
+ h32 = XXH_rotl32(h32, 17) * PRIME32_4 ;
+ p+=4;
+ }
+
+ while (p<bEnd)
+ {
+ h32 += (*p) * PRIME32_5;
+ h32 = XXH_rotl32(h32, 11) * PRIME32_1 ;
+ p++;
+ }
+
+ h32 ^= h32 >> 15;
+ h32 *= PRIME32_2;
+ h32 ^= h32 >> 13;
+ h32 *= PRIME32_3;
+ h32 ^= h32 >> 16;
+
+ return h32;
+}
+
+
+unsigned int XXH32 (const void* input, size_t len, unsigned int seed)
+{
+#if 0
+ /* Simple version, good for code maintenance, but unfortunately slow for small inputs */
+ XXH32_state_t state;
+ XXH32_reset(&state, seed);
+ XXH32_update(&state, input, len);
+ return XXH32_digest(&state);
+#else
+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+# if !defined(XXH_USELESS_ALIGN_BRANCH)
+ if ((((size_t)input) & 3) == 0) /* Input is 4-bytes aligned, leverage the speed benefit */
+ {
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);
+ else
+ return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);
+ }
+# endif
+
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);
+ else
+ return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);
+#endif
+}
+
+FORCE_INLINE U64 XXH64_endian_align(const void* input, size_t len, U64 seed, XXH_endianess endian, XXH_alignment align)
+{
+ const BYTE* p = (const BYTE*)input;
+ const BYTE* bEnd = p + len;
+ U64 h64;
+#define XXH_get64bits(p) XXH_readLE64_align(p, endian, align)
+
+#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
+ if (p==NULL)
+ {
+ len=0;
+ bEnd=p=(const BYTE*)(size_t)32;
+ }
+#endif
+
+ if (len>=32)
+ {
+ const BYTE* const limit = bEnd - 32;
+ U64 v1 = seed + PRIME64_1 + PRIME64_2;
+ U64 v2 = seed + PRIME64_2;
+ U64 v3 = seed + 0;
+ U64 v4 = seed - PRIME64_1;
+
+ do
+ {
+ v1 += XXH_get64bits(p) * PRIME64_2;
+ p+=8;
+ v1 = XXH_rotl64(v1, 31);
+ v1 *= PRIME64_1;
+ v2 += XXH_get64bits(p) * PRIME64_2;
+ p+=8;
+ v2 = XXH_rotl64(v2, 31);
+ v2 *= PRIME64_1;
+ v3 += XXH_get64bits(p) * PRIME64_2;
+ p+=8;
+ v3 = XXH_rotl64(v3, 31);
+ v3 *= PRIME64_1;
+ v4 += XXH_get64bits(p) * PRIME64_2;
+ p+=8;
+ v4 = XXH_rotl64(v4, 31);
+ v4 *= PRIME64_1;
+ }
+ while (p<=limit);
+
+ h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);
+
+ v1 *= PRIME64_2;
+ v1 = XXH_rotl64(v1, 31);
+ v1 *= PRIME64_1;
+ h64 ^= v1;
+ h64 = h64 * PRIME64_1 + PRIME64_4;
+
+ v2 *= PRIME64_2;
+ v2 = XXH_rotl64(v2, 31);
+ v2 *= PRIME64_1;
+ h64 ^= v2;
+ h64 = h64 * PRIME64_1 + PRIME64_4;
+
+ v3 *= PRIME64_2;
+ v3 = XXH_rotl64(v3, 31);
+ v3 *= PRIME64_1;
+ h64 ^= v3;
+ h64 = h64 * PRIME64_1 + PRIME64_4;
+
+ v4 *= PRIME64_2;
+ v4 = XXH_rotl64(v4, 31);
+ v4 *= PRIME64_1;
+ h64 ^= v4;
+ h64 = h64 * PRIME64_1 + PRIME64_4;
+ }
+ else
+ {
+ h64 = seed + PRIME64_5;
+ }
+
+ h64 += (U64) len;
+
+ while (p+8<=bEnd)
+ {
+ U64 k1 = XXH_get64bits(p);
+ k1 *= PRIME64_2;
+ k1 = XXH_rotl64(k1,31);
+ k1 *= PRIME64_1;
+ h64 ^= k1;
+ h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4;
+ p+=8;
+ }
+
+ if (p+4<=bEnd)
+ {
+ h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1;
+ h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3;
+ p+=4;
+ }
+
+ while (p<bEnd)
+ {
+ h64 ^= (*p) * PRIME64_5;
+ h64 = XXH_rotl64(h64, 11) * PRIME64_1;
+ p++;
+ }
+
+ h64 ^= h64 >> 33;
+ h64 *= PRIME64_2;
+ h64 ^= h64 >> 29;
+ h64 *= PRIME64_3;
+ h64 ^= h64 >> 32;
+
+ return h64;
+}
+
+
+unsigned long long XXH64 (const void* input, size_t len, unsigned long long seed)
+{
+#if 0
+ /* Simple version, good for code maintenance, but unfortunately slow for small inputs */
+ XXH64_state_t state;
+ XXH64_reset(&state, seed);
+ XXH64_update(&state, input, len);
+ return XXH64_digest(&state);
+#else
+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+# if !defined(XXH_USELESS_ALIGN_BRANCH)
+ if ((((size_t)input) & 7)==0) /* Input is aligned, let's leverage the speed advantage */
+ {
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);
+ else
+ return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);
+ }
+# endif
+
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);
+ else
+ return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);
+#endif
+}
+
+/****************************************************
+* Advanced Hash Functions
+****************************************************/
+
+/*** Allocation ***/
+typedef struct
+{
+ U64 total_len;
+ U32 seed;
+ U32 v1;
+ U32 v2;
+ U32 v3;
+ U32 v4;
+ U32 mem32[4]; /* defined as U32 for alignment */
+ U32 memsize;
+} XXH_istate32_t;
+
+typedef struct
+{
+ U64 total_len;
+ U64 seed;
+ U64 v1;
+ U64 v2;
+ U64 v3;
+ U64 v4;
+ U64 mem64[4]; /* defined as U64 for alignment */
+ U32 memsize;
+} XXH_istate64_t;
+
+
+XXH32_state_t* XXH32_createState(void)
+{
+ XXH_STATIC_ASSERT(sizeof(XXH32_state_t) >= sizeof(XXH_istate32_t)); /* A compilation error here means XXH32_state_t is not large enough */
+ return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t));
+}
+XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr)
+{
+ XXH_free(statePtr);
+ return XXH_OK;
+}
+
+XXH64_state_t* XXH64_createState(void)
+{
+ XXH_STATIC_ASSERT(sizeof(XXH64_state_t) >= sizeof(XXH_istate64_t)); /* A compilation error here means XXH64_state_t is not large enough */
+ return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t));
+}
+XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr)
+{
+ XXH_free(statePtr);
+ return XXH_OK;
+}
+
+
+/*** Hash feed ***/
+
+XXH_errorcode XXH32_reset(XXH32_state_t* state_in, unsigned int seed)
+{
+ XXH_istate32_t* state = (XXH_istate32_t*) state_in;
+ state->seed = seed;
+ state->v1 = seed + PRIME32_1 + PRIME32_2;
+ state->v2 = seed + PRIME32_2;
+ state->v3 = seed + 0;
+ state->v4 = seed - PRIME32_1;
+ state->total_len = 0;
+ state->memsize = 0;
+ return XXH_OK;
+}
+
+XXH_errorcode XXH64_reset(XXH64_state_t* state_in, unsigned long long seed)
+{
+ XXH_istate64_t* state = (XXH_istate64_t*) state_in;
+ state->seed = seed;
+ state->v1 = seed + PRIME64_1 + PRIME64_2;
+ state->v2 = seed + PRIME64_2;
+ state->v3 = seed + 0;
+ state->v4 = seed - PRIME64_1;
+ state->total_len = 0;
+ state->memsize = 0;
+ return XXH_OK;
+}
+
+
+FORCE_INLINE XXH_errorcode XXH32_update_endian (XXH32_state_t* state_in, const void* input, size_t len, XXH_endianess endian)
+{
+ XXH_istate32_t* state = (XXH_istate32_t *) state_in;
+ const BYTE* p = (const BYTE*)input;
+ const BYTE* const bEnd = p + len;
+
+#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
+ if (input==NULL) return XXH_ERROR;
+#endif
+
+ state->total_len += len;
+
+ if (state->memsize + len < 16) /* fill in tmp buffer */
+ {
+ XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len);
+ state->memsize += (U32)len;
+ return XXH_OK;
+ }
+
+ if (state->memsize) /* some data left from previous update */
+ {
+ XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize);
+ {
+ const U32* p32 = state->mem32;
+ state->v1 += XXH_readLE32(p32, endian) * PRIME32_2;
+ state->v1 = XXH_rotl32(state->v1, 13);
+ state->v1 *= PRIME32_1;
+ p32++;
+ state->v2 += XXH_readLE32(p32, endian) * PRIME32_2;
+ state->v2 = XXH_rotl32(state->v2, 13);
+ state->v2 *= PRIME32_1;
+ p32++;
+ state->v3 += XXH_readLE32(p32, endian) * PRIME32_2;
+ state->v3 = XXH_rotl32(state->v3, 13);
+ state->v3 *= PRIME32_1;
+ p32++;
+ state->v4 += XXH_readLE32(p32, endian) * PRIME32_2;
+ state->v4 = XXH_rotl32(state->v4, 13);
+ state->v4 *= PRIME32_1;
+ p32++;
+ }
+ p += 16-state->memsize;
+ state->memsize = 0;
+ }
+
+ if (p <= bEnd-16)
+ {
+ const BYTE* const limit = bEnd - 16;
+ U32 v1 = state->v1;
+ U32 v2 = state->v2;
+ U32 v3 = state->v3;
+ U32 v4 = state->v4;
+
+ do
+ {
+ v1 += XXH_readLE32(p, endian) * PRIME32_2;
+ v1 = XXH_rotl32(v1, 13);
+ v1 *= PRIME32_1;
+ p+=4;
+ v2 += XXH_readLE32(p, endian) * PRIME32_2;
+ v2 = XXH_rotl32(v2, 13);
+ v2 *= PRIME32_1;
+ p+=4;
+ v3 += XXH_readLE32(p, endian) * PRIME32_2;
+ v3 = XXH_rotl32(v3, 13);
+ v3 *= PRIME32_1;
+ p+=4;
+ v4 += XXH_readLE32(p, endian) * PRIME32_2;
+ v4 = XXH_rotl32(v4, 13);
+ v4 *= PRIME32_1;
+ p+=4;
+ }
+ while (p<=limit);
+
+ state->v1 = v1;
+ state->v2 = v2;
+ state->v3 = v3;
+ state->v4 = v4;
+ }
+
+ if (p < bEnd)
+ {
+ XXH_memcpy(state->mem32, p, bEnd-p);
+ state->memsize = (int)(bEnd-p);
+ }
+
+ return XXH_OK;
+}
+
+XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void* input, size_t len)
+{
+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH32_update_endian(state_in, input, len, XXH_littleEndian);
+ else
+ return XXH32_update_endian(state_in, input, len, XXH_bigEndian);
+}
+
+
+
+FORCE_INLINE U32 XXH32_digest_endian (const XXH32_state_t* state_in, XXH_endianess endian)
+{
+ const XXH_istate32_t* state = (const XXH_istate32_t*) state_in;
+ const BYTE * p = (const BYTE*)state->mem32;
+ const BYTE* bEnd = (const BYTE*)(state->mem32) + state->memsize;
+ U32 h32;
+
+ if (state->total_len >= 16)
+ {
+ h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18);
+ }
+ else
+ {
+ h32 = state->seed + PRIME32_5;
+ }
+
+ h32 += (U32) state->total_len;
+
+ while (p+4<=bEnd)
+ {
+ h32 += XXH_readLE32(p, endian) * PRIME32_3;
+ h32 = XXH_rotl32(h32, 17) * PRIME32_4;
+ p+=4;
+ }
+
+ while (p<bEnd)
+ {
+ h32 += (*p) * PRIME32_5;
+ h32 = XXH_rotl32(h32, 11) * PRIME32_1;
+ p++;
+ }
+
+ h32 ^= h32 >> 15;
+ h32 *= PRIME32_2;
+ h32 ^= h32 >> 13;
+ h32 *= PRIME32_3;
+ h32 ^= h32 >> 16;
+
+ return h32;
+}
+
+
+unsigned int XXH32_digest (const XXH32_state_t* state_in)
+{
+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH32_digest_endian(state_in, XXH_littleEndian);
+ else
+ return XXH32_digest_endian(state_in, XXH_bigEndian);
+}
+
+
+FORCE_INLINE XXH_errorcode XXH64_update_endian (XXH64_state_t* state_in, const void* input, size_t len, XXH_endianess endian)
+{
+ XXH_istate64_t * state = (XXH_istate64_t *) state_in;
+ const BYTE* p = (const BYTE*)input;
+ const BYTE* const bEnd = p + len;
+
+#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
+ if (input==NULL) return XXH_ERROR;
+#endif
+
+ state->total_len += len;
+
+ if (state->memsize + len < 32) /* fill in tmp buffer */
+ {
+ XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len);
+ state->memsize += (U32)len;
+ return XXH_OK;
+ }
+
+ if (state->memsize) /* some data left from previous update */
+ {
+ XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize);
+ {
+ const U64* p64 = state->mem64;
+ state->v1 += XXH_readLE64(p64, endian) * PRIME64_2;
+ state->v1 = XXH_rotl64(state->v1, 31);
+ state->v1 *= PRIME64_1;
+ p64++;
+ state->v2 += XXH_readLE64(p64, endian) * PRIME64_2;
+ state->v2 = XXH_rotl64(state->v2, 31);
+ state->v2 *= PRIME64_1;
+ p64++;
+ state->v3 += XXH_readLE64(p64, endian) * PRIME64_2;
+ state->v3 = XXH_rotl64(state->v3, 31);
+ state->v3 *= PRIME64_1;
+ p64++;
+ state->v4 += XXH_readLE64(p64, endian) * PRIME64_2;
+ state->v4 = XXH_rotl64(state->v4, 31);
+ state->v4 *= PRIME64_1;
+ p64++;
+ }
+ p += 32-state->memsize;
+ state->memsize = 0;
+ }
+
+ if (p+32 <= bEnd)
+ {
+ const BYTE* const limit = bEnd - 32;
+ U64 v1 = state->v1;
+ U64 v2 = state->v2;
+ U64 v3 = state->v3;
+ U64 v4 = state->v4;
+
+ do
+ {
+ v1 += XXH_readLE64(p, endian) * PRIME64_2;
+ v1 = XXH_rotl64(v1, 31);
+ v1 *= PRIME64_1;
+ p+=8;
+ v2 += XXH_readLE64(p, endian) * PRIME64_2;
+ v2 = XXH_rotl64(v2, 31);
+ v2 *= PRIME64_1;
+ p+=8;
+ v3 += XXH_readLE64(p, endian) * PRIME64_2;
+ v3 = XXH_rotl64(v3, 31);
+ v3 *= PRIME64_1;
+ p+=8;
+ v4 += XXH_readLE64(p, endian) * PRIME64_2;
+ v4 = XXH_rotl64(v4, 31);
+ v4 *= PRIME64_1;
+ p+=8;
+ }
+ while (p<=limit);
+
+ state->v1 = v1;
+ state->v2 = v2;
+ state->v3 = v3;
+ state->v4 = v4;
+ }
+
+ if (p < bEnd)
+ {
+ XXH_memcpy(state->mem64, p, bEnd-p);
+ state->memsize = (int)(bEnd-p);
+ }
+
+ return XXH_OK;
+}
+
+XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void* input, size_t len)
+{
+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH64_update_endian(state_in, input, len, XXH_littleEndian);
+ else
+ return XXH64_update_endian(state_in, input, len, XXH_bigEndian);
+}
+
+
+
+FORCE_INLINE U64 XXH64_digest_endian (const XXH64_state_t* state_in, XXH_endianess endian)
+{
+ const XXH_istate64_t * state = (const XXH_istate64_t *) state_in;
+ const BYTE * p = (const BYTE*)state->mem64;
+ const BYTE* bEnd = (const BYTE*)state->mem64 + state->memsize;
+ U64 h64;
+
+ if (state->total_len >= 32)
+ {
+ U64 v1 = state->v1;
+ U64 v2 = state->v2;
+ U64 v3 = state->v3;
+ U64 v4 = state->v4;
+
+ h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);
+
+ v1 *= PRIME64_2;
+ v1 = XXH_rotl64(v1, 31);
+ v1 *= PRIME64_1;
+ h64 ^= v1;
+ h64 = h64*PRIME64_1 + PRIME64_4;
+
+ v2 *= PRIME64_2;
+ v2 = XXH_rotl64(v2, 31);
+ v2 *= PRIME64_1;
+ h64 ^= v2;
+ h64 = h64*PRIME64_1 + PRIME64_4;
+
+ v3 *= PRIME64_2;
+ v3 = XXH_rotl64(v3, 31);
+ v3 *= PRIME64_1;
+ h64 ^= v3;
+ h64 = h64*PRIME64_1 + PRIME64_4;
+
+ v4 *= PRIME64_2;
+ v4 = XXH_rotl64(v4, 31);
+ v4 *= PRIME64_1;
+ h64 ^= v4;
+ h64 = h64*PRIME64_1 + PRIME64_4;
+ }
+ else
+ {
+ h64 = state->seed + PRIME64_5;
+ }
+
+ h64 += (U64) state->total_len;
+
+ while (p+8<=bEnd)
+ {
+ U64 k1 = XXH_readLE64(p, endian);
+ k1 *= PRIME64_2;
+ k1 = XXH_rotl64(k1,31);
+ k1 *= PRIME64_1;
+ h64 ^= k1;
+ h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4;
+ p+=8;
+ }
+
+ if (p+4<=bEnd)
+ {
+ h64 ^= (U64)(XXH_readLE32(p, endian)) * PRIME64_1;
+ h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3;
+ p+=4;
+ }
+
+ while (p<bEnd)
+ {
+ h64 ^= (*p) * PRIME64_5;
+ h64 = XXH_rotl64(h64, 11) * PRIME64_1;
+ p++;
+ }
+
+ h64 ^= h64 >> 33;
+ h64 *= PRIME64_2;
+ h64 ^= h64 >> 29;
+ h64 *= PRIME64_3;
+ h64 ^= h64 >> 32;
+
+ return h64;
+}
+
+
+unsigned long long XXH64_digest (const XXH64_state_t* state_in)
+{
+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH64_digest_endian(state_in, XXH_littleEndian);
+ else
+ return XXH64_digest_endian(state_in, XXH_bigEndian);
+}
+
+
diff --git a/frontends/amiga/hash/xxhash.h b/frontends/amiga/hash/xxhash.h
new file mode 100644
index 000000000..c60aa6157
--- /dev/null
+++ b/frontends/amiga/hash/xxhash.h
@@ -0,0 +1,192 @@
+/*
+ xxHash - Extremely Fast Hash algorithm
+ Header File
+ Copyright (C) 2012-2015, Yann Collet.
+
+ BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following disclaimer
+ in the documentation and/or other materials provided with the
+ distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ You can contact the author at :
+ - xxHash source repository : https://github.com/Cyan4973/xxHash
+*/
+
+/* Notice extracted from xxHash homepage :
+
+xxHash is an extremely fast Hash algorithm, running at RAM speed limits.
+It also successfully passes all tests from the SMHasher suite.
+
+Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz)
+
+Name Speed Q.Score Author
+xxHash 5.4 GB/s 10
+CrapWow 3.2 GB/s 2 Andrew
+MumurHash 3a 2.7 GB/s 10 Austin Appleby
+SpookyHash 2.0 GB/s 10 Bob Jenkins
+SBox 1.4 GB/s 9 Bret Mulvey
+Lookup3 1.2 GB/s 9 Bob Jenkins
+SuperFastHash 1.2 GB/s 1 Paul Hsieh
+CityHash64 1.05 GB/s 10 Pike & Alakuijala
+FNV 0.55 GB/s 5 Fowler, Noll, Vo
+CRC32 0.43 GB/s 9
+MD5-32 0.33 GB/s 10 Ronald L. Rivest
+SHA1-32 0.28 GB/s 10
+
+Q.Score is a measure of quality of the hash function.
+It depends on successfully passing SMHasher test set.
+10 is a perfect score.
+
+A 64-bits version, named XXH64, is available since r35.
+It offers much better speed, but for 64-bits applications only.
+Name Speed on 64 bits Speed on 32 bits
+XXH64 13.8 GB/s 1.9 GB/s
+XXH32 6.8 GB/s 6.0 GB/s
+*/
+
+#pragma once
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+
+/*****************************
+* Definitions
+*****************************/
+#include <stddef.h> /* size_t */
+typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode;
+
+
+/*****************************
+* Namespace Emulation
+*****************************/
+/* Motivations :
+
+If you need to include xxHash into your library,
+but wish to avoid xxHash symbols to be present on your library interface
+in an effort to avoid potential name collision if another library also includes xxHash,
+
+you can use XXH_NAMESPACE, which will automatically prefix any symbol from xxHash
+with the value of XXH_NAMESPACE (so avoid to keep it NULL, and avoid numeric values).
+
+Note that no change is required within the calling program :
+it can still call xxHash functions using their regular name.
+They will be automatically translated by this header.
+*/
+#ifdef XXH_NAMESPACE
+# define XXH_CAT(A,B) A##B
+# define XXH_NAME2(A,B) XXH_CAT(A,B)
+# define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32)
+# define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64)
+# define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState)
+# define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState)
+# define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState)
+# define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState)
+# define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset)
+# define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset)
+# define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update)
+# define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update)
+# define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest)
+# define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest)
+#endif
+
+
+/*****************************
+* Simple Hash Functions
+*****************************/
+
+unsigned int XXH32 (const void* input, size_t length, unsigned seed);
+unsigned long long XXH64 (const void* input, size_t length, unsigned long long seed);
+
+/*
+XXH32() :
+ Calculate the 32-bits hash of sequence "length" bytes stored at memory address "input".
+ The memory between input & input+length must be valid (allocated and read-accessible).
+ "seed" can be used to alter the result predictably.
+ This function successfully passes all SMHasher tests.
+ Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s
+XXH64() :
+ Calculate the 64-bits hash of sequence of length "len" stored at memory address "input".
+ Faster on 64-bits systems. Slower on 32-bits systems.
+*/
+
+
+
+/*****************************
+* Advanced Hash Functions
+*****************************/
+typedef struct { long long ll[ 6]; } XXH32_state_t;
+typedef struct { long long ll[11]; } XXH64_state_t;
+
+/*
+These structures allow static allocation of XXH states.
+States must then be initialized using XXHnn_reset() before first use.
+
+If you prefer dynamic allocation, please refer to functions below.
+*/
+
+XXH32_state_t* XXH32_createState(void);
+XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr);
+
+XXH64_state_t* XXH64_createState(void);
+XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr);
+
+/*
+These functions create and release memory for XXH state.
+States must then be initialized using XXHnn_reset() before first use.
+*/
+
+
+XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, unsigned seed);
+XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length);
+unsigned int XXH32_digest (const XXH32_state_t* statePtr);
+
+XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, unsigned long long seed);
+XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length);
+unsigned long long XXH64_digest (const XXH64_state_t* statePtr);
+
+/*
+These functions calculate the xxHash of an input provided in multiple smaller packets,
+as opposed to an input provided as a single block.
+
+XXH state space must first be allocated, using either static or dynamic method provided above.
+
+Start a new hash by initializing state with a seed, using XXHnn_reset().
+
+Then, feed the hash state by calling XXHnn_update() as many times as necessary.
+Obviously, input must be valid, meaning allocated and read accessible.
+The function returns an error code, with 0 meaning OK, and any other value meaning there is an error.
+
+Finally, you can produce a hash anytime, by using XXHnn_digest().
+This function returns the final nn-bits hash.
+You can nonetheless continue feeding the hash state with more input,
+and therefore get some new hashes, by calling again XXHnn_digest().
+
+When you are done, don't forget to free XXH state space, using typically XXHnn_freeState().
+*/
+
+
+#if defined (__cplusplus)
+}
+#endif
diff --git a/frontends/amiga/help.c b/frontends/amiga/help.c
new file mode 100755
index 000000000..64b338426
--- /dev/null
+++ b/frontends/amiga/help.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2013-4 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "amiga/os3support.h"
+
+#include "amiga/help.h"
+
+/* AmigaGuide class */
+#include "amiga/agclass/amigaguide_class.h"
+
+Class *AmigaGuideClass = NULL;
+Object *AmigaGuideObject = NULL;
+
+/* This array needs to match the enum in help.h */
+CONST_STRPTR context_nodes[] = {
+ "Main",
+ "GUI",
+ "Prefs",
+ NULL
+};
+
+static void ami_help_init(struct Screen *screen)
+{
+ AmigaGuideClass = initAGClass();
+
+ AmigaGuideObject = NewObject(AmigaGuideClass, NULL,
+ AMIGAGUIDE_Name, "PROGDIR:NetSurf.guide",
+ AMIGAGUIDE_BaseName, "NetSurf",
+ AMIGAGUIDE_Screen, screen,
+ AMIGAGUIDE_ContextArray, context_nodes,
+ AMIGAGUIDE_ContextID, AMI_HELP_MAIN,
+ TAG_DONE);
+}
+
+void ami_help_open(ULONG node, struct Screen *screen)
+{
+ if(AmigaGuideObject == NULL) ami_help_init(screen);
+ SetAttrs(AmigaGuideObject, AMIGAGUIDE_ContextID, node, TAG_DONE);
+ IDoMethod(AmigaGuideObject, AGM_OPEN, NULL);
+}
+
+void ami_help_free(void)
+{
+ if (AmigaGuideObject) DisposeObject(AmigaGuideObject);
+ if (AmigaGuideClass) freeAGClass(AmigaGuideClass);
+
+ AmigaGuideObject = NULL;
+ AmigaGuideClass = NULL;
+}
+
+void ami_help_new_screen(struct Screen *screen)
+{
+ if(AmigaGuideObject == NULL) return;
+ SetAttrs(AmigaGuideObject, AMIGAGUIDE_Screen, screen, TAG_DONE);
+}
+
+ULONG ami_help_signal(void)
+{
+ ULONG ag_sig = 0;
+ if(AmigaGuideObject)
+ GetAttr(AMIGAGUIDE_Signal, AmigaGuideObject, &ag_sig);
+ return ag_sig;
+}
+
+void ami_help_process(void)
+{
+ ULONG ret = IDoMethod(AmigaGuideObject, AGM_PROCESS, NULL);
+ if(ret) ami_help_free();
+}
+
diff --git a/frontends/amiga/help.h b/frontends/amiga/help.h
new file mode 100755
index 000000000..e405edd0c
--- /dev/null
+++ b/frontends/amiga/help.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2013 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_HELP_H
+#define AMIGA_HELP_H
+#include <exec/types.h>
+
+/* This enum needs to match context_array in help.c */
+enum {
+ AMI_HELP_MAIN,
+ AMI_HELP_GUI,
+ AMI_HELP_PREFS,
+};
+
+struct Screen;
+
+void ami_help_open(ULONG node, struct Screen *screen);
+void ami_help_free(void);
+void ami_help_new_screen(struct Screen *screen);
+ULONG ami_help_signal(void);
+void ami_help_process(void);
+#endif
+
diff --git a/frontends/amiga/history.c b/frontends/amiga/history.c
new file mode 100755
index 000000000..72b69d435
--- /dev/null
+++ b/frontends/amiga/history.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2008, 2009 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include <proto/exec.h>
+
+#include "utils/errors.h"
+#include "desktop/mouse.h"
+#include "desktop/gui_window.h"
+
+#include "amiga/history.h"
+#include "amiga/tree.h"
+#include "amiga/tree.h"
+
+void ami_global_history_initialise(void)
+{
+ global_history_window = ami_tree_create(TREE_HISTORY, NULL);
+
+ if(!global_history_window) return;
+}
+
+void ami_global_history_free()
+{
+ ami_tree_destroy(global_history_window);
+ global_history_window = NULL;
+}
diff --git a/frontends/amiga/history.h b/frontends/amiga/history.h
new file mode 100755
index 000000000..1f064746a
--- /dev/null
+++ b/frontends/amiga/history.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2008,2009 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_HISTORY_H
+#define AMIGA_HISTORY_H
+#include "desktop/tree.h"
+
+#define GLOBAL_HISTORY_RECENT_URLS 16
+
+void ami_global_history_initialise(void);
+void ami_global_history_free(void);
+
+struct treeview_window *global_history_window;
+#endif
diff --git a/frontends/amiga/history_local.c b/frontends/amiga/history_local.c
new file mode 100755
index 000000000..3016cbf16
--- /dev/null
+++ b/frontends/amiga/history_local.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2009, 2010 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Browser history window (AmigaOS implementation).
+ *
+ * There is only one history window, not one per browser window.
+ */
+
+#include "amiga/os3support.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <proto/intuition.h>
+#include <proto/exec.h>
+#include <proto/graphics.h>
+#include <intuition/icclass.h>
+#include <proto/utility.h>
+#include <proto/window.h>
+#include <proto/space.h>
+#include <proto/layout.h>
+#include <classes/window.h>
+#include <gadgets/space.h>
+#include <gadgets/scroller.h>
+#include <reaction/reaction.h>
+#include <reaction/reaction_macros.h>
+
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "utils/messages.h"
+#include "desktop/browser_history.h"
+#include "desktop/browser.h"
+#include "desktop/plotters.h"
+#include "desktop/gui_window.h"
+#include "graphics/rpattr.h"
+
+#include "amiga/libs.h"
+#include "amiga/misc.h"
+#include "amiga/object.h"
+#include "amiga/gui.h"
+#include "amiga/history_local.h"
+
+void ami_history_update_extent(struct history_window *hw);
+HOOKF(void, ami_history_scroller_hook, Object *, object, struct IntuiMessage *);
+
+/**
+ * Redraw history window.
+ */
+
+static void ami_history_redraw(struct history_window *hw)
+{
+ struct IBox *bbox;
+ ULONG xs,ys;
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &amiplot
+ };
+
+ GetAttr(SCROLLER_Top,hw->objects[OID_HSCROLL],(ULONG *)&xs);
+ GetAttr(SCROLLER_Top,hw->objects[OID_VSCROLL],(ULONG *)&ys);
+ if(ami_gui_get_space_box(hw->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return;
+ }
+
+ glob = &hw->gg;
+
+ SetRPAttrs(glob->rp, RPTAG_APenColor, 0xffffffff, TAG_DONE);
+ RectFill(glob->rp, 0, 0, bbox->Width - 1, bbox->Height - 1);
+
+ browser_window_history_redraw_rectangle(hw->gw->bw, xs, ys,
+ bbox->Width + xs, bbox->Height + ys, 0, 0, &ctx);
+
+ glob = &browserglob;
+
+ ami_clearclipreg(&hw->gg);
+ ami_history_update_extent(hw);
+
+ BltBitMapRastPort(hw->gg.bm, 0, 0, hw->win->RPort,
+ bbox->Left, bbox->Top, bbox->Width, bbox->Height, 0x0C0);
+
+ ami_gui_free_space_box(bbox);
+}
+
+
+/* exported interface documented in amiga/history_local.h */
+void ami_history_open(struct gui_window *gw)
+{
+ struct history *history;
+ int width, height;
+
+ if (gw->bw == NULL)
+ return;
+
+ history = browser_window_get_history(gw->bw);
+ if (history == NULL)
+ return;
+
+ if(!gw->hw)
+ {
+ gw->hw = ami_misc_allocvec_clear(sizeof(struct history_window), 0);
+
+ ami_init_layers(&gw->hw->gg, scrn->Width, scrn->Height, false);
+
+ gw->hw->gw = gw;
+ browser_window_history_size(gw->bw, &width, &height);
+
+ gw->hw->scrollerhook.h_Entry = (void *)ami_history_scroller_hook;
+ gw->hw->scrollerhook.h_Data = gw->hw;
+
+ gw->hw->objects[OID_MAIN] = WindowObj,
+ WA_ScreenTitle, ami_gui_get_screen_title(),
+ WA_Title, messages_get("History"),
+ WA_Activate, TRUE,
+ WA_DepthGadget, TRUE,
+ WA_DragBar, TRUE,
+ WA_CloseGadget, TRUE,
+ WA_SizeGadget, TRUE,
+ WA_PubScreen,scrn,
+ WA_InnerWidth,width,
+ WA_InnerHeight,height + 10,
+ WINDOW_SharedPort,sport,
+ WINDOW_UserData,gw->hw,
+ WINDOW_IconifyGadget, FALSE,
+ WINDOW_GadgetHelp, TRUE,
+ WINDOW_Position, WPOS_CENTERSCREEN,
+ WINDOW_HorizProp,1,
+ WINDOW_VertProp,1,
+ WINDOW_IDCMPHook,&gw->hw->scrollerhook,
+ WINDOW_IDCMPHookBits,IDCMP_IDCMPUPDATE,
+// WA_ReportMouse,TRUE,
+ WA_IDCMP,IDCMP_MOUSEBUTTONS | IDCMP_NEWSIZE, // | IDCMP_MOUSEMOVE,
+ WINDOW_ParentGroup, gw->hw->objects[GID_MAIN] = LayoutVObj,
+ LAYOUT_AddChild, gw->hw->objects[GID_BROWSER] = SpaceObj,
+ GA_ID,GID_BROWSER,
+// SPACE_MinWidth,width,
+// SPACE_MinHeight,height,
+ SpaceEnd,
+ EndGroup,
+ EndWindow;
+
+ gw->hw->win = (struct Window *)RA_OpenWindow(gw->hw->objects[OID_MAIN]);
+ gw->hw->node = AddObject(window_list,AMINS_HISTORYWINDOW);
+ gw->hw->node->objstruct = gw->hw;
+
+ GetAttr(WINDOW_HorizObject,gw->hw->objects[OID_MAIN],(ULONG *)&gw->hw->objects[OID_HSCROLL]);
+ GetAttr(WINDOW_VertObject,gw->hw->objects[OID_MAIN],(ULONG *)&gw->hw->objects[OID_VSCROLL]);
+
+ RefreshSetGadgetAttrs((APTR)gw->hw->objects[OID_VSCROLL],gw->hw->win,NULL,
+ GA_ID,OID_VSCROLL,
+ SCROLLER_Top,0,
+ ICA_TARGET,ICTARGET_IDCMP,
+ TAG_DONE);
+
+ RefreshSetGadgetAttrs((APTR)gw->hw->objects[OID_HSCROLL],gw->hw->win,NULL,
+ GA_ID,OID_HSCROLL,
+ SCROLLER_Top,0,
+ ICA_TARGET,ICTARGET_IDCMP,
+ TAG_DONE);
+ }
+
+ ami_history_redraw(gw->hw);
+}
+
+
+/**
+ * Handle mouse clicks in the history window.
+ *
+ * \return true if the event was handled, false to pass it on
+ */
+
+static bool ami_history_click(struct history_window *hw, uint16 code)
+{
+ int x, y;
+ struct IBox *bbox;
+ ULONG xs, ys;
+
+ if(ami_gui_get_space_box(hw->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return false;
+ }
+
+ GetAttr(SCROLLER_Top,hw->objects[OID_HSCROLL],(ULONG *)&xs);
+ x = hw->win->MouseX - bbox->Left +xs;
+ GetAttr(SCROLLER_Top,hw->objects[OID_VSCROLL],(ULONG *)&ys);
+ y = hw->win->MouseY - bbox->Top + ys;
+
+ ami_gui_free_space_box(bbox);
+
+ switch(code)
+ {
+ case SELECTUP:
+ browser_window_history_click(hw->gw->bw, x, y, false);
+ ami_history_redraw(hw);
+ ami_schedule_redraw(hw->gw->shared, true);
+ break;
+
+ case MIDDLEUP:
+ browser_window_history_click(hw->gw->bw, x, y, true);
+ ami_history_redraw(hw);
+ break;
+ }
+
+ return true;
+}
+
+void ami_history_close(struct history_window *hw)
+{
+ ami_free_layers(&hw->gg);
+ hw->gw->hw = NULL;
+ DisposeObject(hw->objects[OID_MAIN]);
+ DelObject(hw->node);
+}
+
+BOOL ami_history_event(struct history_window *hw)
+{
+ /* return TRUE if window destroyed */
+ ULONG result = 0;
+ uint16 code;
+ const char *url;
+ struct IBox *bbox;
+ ULONG xs, ys;
+
+ while((result = RA_HandleInput(hw->objects[OID_MAIN],&code)) != WMHI_LASTMSG)
+ {
+ switch(result & WMHI_CLASSMASK) // class
+ {
+/* no menus yet, copied in as will probably need it later
+ case WMHI_MENUPICK:
+ item = ItemAddress(gwin->win->MenuStrip,code);
+ while (code != MENUNULL)
+ {
+ ami_menupick(code,gwin);
+ if(win_destroyed) break;
+ code = item->NextSelect;
+ }
+ break;
+*/
+
+ case WMHI_MOUSEMOVE:
+ GetAttr(SCROLLER_Top, hw->objects[OID_HSCROLL], (ULONG *)&xs);
+ GetAttr(SCROLLER_Top, hw->objects[OID_VSCROLL], (ULONG *)&ys);
+
+ if(ami_gui_get_space_box(hw->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ break;
+ }
+
+ url = browser_window_history_position_url(hw->gw->bw,
+ hw->win->MouseX - bbox->Left + xs,
+ hw->win->MouseY - bbox->Top + ys);
+
+ ami_gui_free_space_box(bbox);
+
+ RefreshSetGadgetAttrs((APTR)hw->objects[GID_BROWSER],
+ hw->win, NULL,
+ GA_HintInfo, url,
+ TAG_DONE);
+ break;
+
+ case WMHI_NEWSIZE:
+ ami_history_redraw(hw);
+ break;
+
+ case WMHI_MOUSEBUTTONS:
+ ami_history_click(hw,code);
+ break;
+
+ case WMHI_CLOSEWINDOW:
+ ami_history_close(hw);
+ return TRUE;
+ break;
+ }
+ }
+ return FALSE;
+}
+
+void ami_history_update_extent(struct history_window *hw)
+{
+ struct IBox *bbox;
+ int width, height;
+
+ browser_window_history_size(hw->gw->bw, &width, &height);
+ if(ami_gui_get_space_box(hw->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return;
+ }
+
+ RefreshSetGadgetAttrs((APTR)hw->objects[OID_VSCROLL], hw->win, NULL,
+ GA_ID, OID_VSCROLL,
+ SCROLLER_Total, height,
+ SCROLLER_Visible, bbox->Height,
+ ICA_TARGET, ICTARGET_IDCMP,
+ TAG_DONE);
+
+ RefreshSetGadgetAttrs((APTR)hw->objects[OID_HSCROLL], hw->win, NULL,
+ GA_ID, OID_HSCROLL,
+ SCROLLER_Total, width,
+ SCROLLER_Visible, bbox->Width,
+ ICA_TARGET, ICTARGET_IDCMP,
+ TAG_DONE);
+
+ ami_gui_free_space_box(bbox);
+}
+
+HOOKF(void, ami_history_scroller_hook, Object *, object, struct IntuiMessage *)
+{
+ ULONG gid;
+ struct history_window *hw = hook->h_Data;
+
+ if (msg->Class == IDCMP_IDCMPUPDATE)
+ {
+ gid = GetTagData( GA_ID, 0, msg->IAddress );
+
+ switch( gid )
+ {
+ case OID_HSCROLL:
+ case OID_VSCROLL:
+ ami_history_redraw(hw);
+ break;
+ }
+ }
+// ReplyMsg((struct Message *)msg);
+}
diff --git a/frontends/amiga/history_local.h b/frontends/amiga/history_local.h
new file mode 100755
index 000000000..452fe1512
--- /dev/null
+++ b/frontends/amiga/history_local.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2009 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_HISTORY_LOCAL_H
+#define AMIGA_HISTORY_LOCAL_H
+
+#include <exec/types.h>
+#include <intuition/classusr.h>
+#include "amiga/gui.h"
+
+struct history_window {
+ struct nsObject *node;
+ struct Window *win;
+ Object *objects[GID_LAST];
+ struct gui_window *gw;
+ struct Hook scrollerhook;
+ struct gui_globals gg;
+};
+
+/**
+ * Open history window.
+ *
+ * \param gw gui_window to open history for
+ */
+void ami_history_open(struct gui_window *gw);
+
+void ami_history_close(struct history_window *hw);
+BOOL ami_history_event(struct history_window *hw);
+#endif
+
diff --git a/frontends/amiga/hotlist.c b/frontends/amiga/hotlist.c
new file mode 100755
index 000000000..79e679f77
--- /dev/null
+++ b/frontends/amiga/hotlist.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2008, 2009 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <string.h>
+#include <proto/exec.h>
+
+#include "utils/nsurl.h"
+#include "desktop/hotlist.h"
+#include "desktop/mouse.h"
+#include "desktop/gui_window.h"
+
+#include "amiga/hotlist.h"
+#include "amiga/tree.h"
+
+struct ami_hotlist_ctx {
+ void *userdata;
+ int level;
+ int item;
+ const char *folder; /* folder we're interested in */
+ bool in_menu; /* set if we are in that folder */
+ bool found; /* set if the folder is found */
+ bool (*cb)(void *userdata, int level, int item, const char *title, nsurl *url, bool folder);
+};
+
+
+void ami_hotlist_initialise(const char *hotlist_file)
+{
+ tree_hotlist_path = hotlist_file;
+ hotlist_window = ami_tree_create(TREE_HOTLIST, NULL);
+ if(!hotlist_window) return;
+}
+
+void ami_hotlist_free(const char *hotlist_file)
+{
+ ami_tree_destroy(hotlist_window);
+ hotlist_window = NULL;
+}
+
+
+static nserror ami_hotlist_folder_enter_cb(void *ctx, const char *title)
+{
+ struct ami_hotlist_ctx *menu_ctx = (struct ami_hotlist_ctx *)ctx;
+
+ if(menu_ctx->in_menu == true) {
+ if(menu_ctx->cb(menu_ctx->userdata, menu_ctx->level, menu_ctx->item, title, NULL, true) == true)
+ menu_ctx->item++;
+ } else {
+ if((menu_ctx->level == 0) && (strcmp(title, menu_ctx->folder) == 0)) {
+ menu_ctx->in_menu = true;
+ menu_ctx->found = true;
+ }
+ }
+ menu_ctx->level++;
+ return NSERROR_OK;
+}
+
+static nserror ami_hotlist_address_cb(void *ctx, nsurl *url, const char *title)
+{
+ struct ami_hotlist_ctx *menu_ctx = (struct ami_hotlist_ctx *)ctx;
+
+ if(menu_ctx->in_menu == true) {
+ if(menu_ctx->cb(menu_ctx->userdata, menu_ctx->level, menu_ctx->item, title, url, false) == true)
+ menu_ctx->item++;
+ }
+
+ return NSERROR_OK;
+}
+
+static nserror ami_hotlist_folder_leave_cb(void *ctx)
+{
+ struct ami_hotlist_ctx *menu_ctx = (struct ami_hotlist_ctx *)ctx;
+
+ menu_ctx->level--;
+
+ if((menu_ctx->in_menu == true) && (menu_ctx->level == 0))
+ menu_ctx->in_menu = false;
+
+ return NSERROR_OK;
+}
+
+nserror ami_hotlist_scan(void *userdata, int first_item, const char *folder,
+ bool (*cb_add_item)(void *userdata, int level, int item, const char *title, nsurl *url, bool folder))
+{
+ nserror error;
+ struct ami_hotlist_ctx ctx;
+
+ ctx.level = 0;
+ ctx.item = first_item;
+ ctx.folder = folder;
+ ctx.in_menu = false;
+ ctx.userdata = userdata;
+ ctx.cb = cb_add_item;
+ ctx.found = false;
+
+ error = hotlist_iterate(&ctx,
+ ami_hotlist_folder_enter_cb,
+ ami_hotlist_address_cb,
+ ami_hotlist_folder_leave_cb);
+
+ if((error == NSERROR_OK) && (ctx.found == false))
+ hotlist_add_folder(folder, false, 0);
+
+ return error;
+}
diff --git a/frontends/amiga/hotlist.h b/frontends/amiga/hotlist.h
new file mode 100755
index 000000000..c50ceecfb
--- /dev/null
+++ b/frontends/amiga/hotlist.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2008, 2009 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_HOTLIST_H
+#define AMIGA_HOTLIST_H
+
+#include "desktop/tree.h"
+#include "amiga/tree.h"
+
+struct nsurl;
+
+void ami_hotlist_initialise(const char *hotlist_file);
+void ami_hotlist_free(const char *hotlist_file);
+nserror ami_hotlist_scan(void *userdata, int first_item, const char *folder,
+ bool (*cb_add_item)(void *userdata, int level, int item, const char *title, struct nsurl *url, bool folder));
+
+struct treeview_window *hotlist_window;
+
+#endif
diff --git a/frontends/amiga/icon.c b/frontends/amiga/icon.c
new file mode 100644
index 000000000..e75bf82b0
--- /dev/null
+++ b/frontends/amiga/icon.c
@@ -0,0 +1,545 @@
+/*
+ * Copyright 2010 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Content for image/x-amiga-icon (icon.library implementation).
+ *
+ */
+
+#include "utils/config.h"
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include <proto/exec.h>
+#include <proto/icon.h>
+
+#include <datatypes/pictureclass.h>
+#ifdef __amigaos4__
+#include <graphics/blitattr.h>
+#endif
+#include <workbench/icon.h>
+
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "utils/file.h"
+#include "desktop/plotters.h"
+#include "image/bitmap.h"
+#include "content/content_protected.h"
+
+#include "amiga/os3support.h"
+#include "amiga/bitmap.h"
+#include "amiga/icon.h"
+#include "amiga/misc.h"
+
+#define THUMBNAIL_WIDTH 100 /* Icon sizes for thumbnails, usually the same as */
+#define THUMBNAIL_HEIGHT 86 /* WIDTH/HEIGHT in desktop/thumbnail.c */
+
+ULONG *amiga_icon_convertcolouricon32(UBYTE *icondata, ULONG width, ULONG height,
+ ULONG trans, ULONG pals1, struct ColorRegister *pal1, int alpha);
+
+#ifdef WITH_AMIGA_ICON
+
+typedef struct amiga_icon_content {
+ struct content base;
+
+ struct bitmap *bitmap; /**< Created NetSurf bitmap */
+} amiga_icon_content;
+
+static nserror amiga_icon_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c);
+static bool amiga_icon_convert(struct content *c);
+static void amiga_icon_destroy(struct content *c);
+static bool amiga_icon_redraw(struct content *c,
+ struct content_redraw_data *data, const struct rect *clip,
+ const struct redraw_context *ctx);
+static nserror amiga_icon_clone(const struct content *old,
+ struct content **newc);
+static content_type amiga_icon_content_type(void);
+
+static void *amiga_icon_get_internal(const struct content *c, void *context)
+{
+ amiga_icon_content *icon_c = (amiga_icon_content *)c;
+
+ return icon_c->bitmap;
+}
+
+static const content_handler amiga_icon_content_handler = {
+ .create = amiga_icon_create,
+ .data_complete = amiga_icon_convert,
+ .destroy = amiga_icon_destroy,
+ .redraw = amiga_icon_redraw,
+ .clone = amiga_icon_clone,
+ .get_internal = amiga_icon_get_internal,
+ .type = amiga_icon_content_type,
+ .no_share = false,
+};
+
+static const char *amiga_icon_types[] = {
+ "image/x-amiga-icon"
+};
+
+CONTENT_FACTORY_REGISTER_TYPES(amiga_icon, amiga_icon_types,
+ amiga_icon_content_handler)
+
+nserror amiga_icon_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c)
+{
+ amiga_icon_content *ai_content;
+ nserror error;
+
+ ai_content = calloc(1, sizeof(amiga_icon_content));
+ if (ai_content == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__init(&ai_content->base, handler, imime_type, params,
+ llcache, fallback_charset, quirks);
+ if (error != NSERROR_OK) {
+ free(ai_content);
+ return error;
+ }
+
+ *c = (struct content *)ai_content;
+
+ return NSERROR_OK;
+}
+
+/**
+ * Convert a CONTENT_AMIGA_ICON for display.
+ *
+ * No conversion is necessary. We merely read the icon dimensions.
+ */
+
+bool amiga_icon_convert(struct content *c)
+{
+ amiga_icon_content *icon_c = (amiga_icon_content *)c;
+ union content_msg_data msg_data;
+ struct DiskObject *dobj;
+ ULONG *imagebuf;
+ unsigned char *imagebufptr = NULL;
+ ULONG size;
+ int width = 0, height = 0;
+ long format = 0;
+ uint8 r, g, b, a;
+ ULONG offset;
+ char *filename = NULL;
+ char *p;
+ ULONG trans, pals1;
+ struct ColorRegister *pal1;
+
+ netsurf_nsurl_to_path(content_get_url(c), &filename);
+ /* This loader will only work on local files, so fail if not a local path */
+ if(filename == NULL)
+ {
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ p = strstr(filename, ".info");
+ *p = '\0';
+
+ dobj = GetIconTagList(filename, NULL);
+
+ if(dobj == NULL)
+ {
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ IconControl(dobj,
+ ICONCTRLA_GetImageDataFormat,&format,
+ ICONCTRLA_GetWidth,&width,
+ ICONCTRLA_GetHeight,&height,
+ TAG_DONE);
+
+ /* Check icon is direct mapped (truecolour) or palette-mapped colour.
+ We need additional code to handle planar icons */
+ if((format != IDFMT_DIRECTMAPPED) && (format==IDFMT_PALETTEMAPPED)) {
+ if(dobj) FreeDiskObject(dobj);
+ return false;
+ }
+
+ icon_c->bitmap = amiga_bitmap_create(width, height, BITMAP_NEW);
+ if (!icon_c->bitmap) {
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ if(dobj) FreeDiskObject(dobj);
+ return false;
+ }
+ imagebuf = (ULONG *) amiga_bitmap_get_buffer(icon_c->bitmap);
+ if (!imagebuf) {
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ if(dobj) FreeDiskObject(dobj);
+ return false;
+ }
+
+ IconControl(dobj,
+ ICONCTRLA_GetImageData1, &imagebufptr,
+ TAG_DONE);
+
+ if(format==IDFMT_PALETTEMAPPED)
+ {
+ IconControl(dobj, ICONCTRLA_GetTransparentColor1, &trans,
+ ICONCTRLA_GetPalette1, &pal1,
+ ICONCTRLA_GetPaletteSize1, &pals1,
+ TAG_DONE);
+
+ imagebufptr = (unsigned char *) amiga_icon_convertcolouricon32((UBYTE *)imagebufptr,
+ width, height, trans, pals1, pal1, 0xff);
+ }
+
+ /* Decoded data is ARGB, so ensure correct byte order */
+
+ size = width * height * 4;
+
+ for (offset = 0; offset < size; offset += 4) {
+ b = imagebufptr[offset+3];
+ g = imagebufptr[offset+2];
+ r = imagebufptr[offset+1];
+ a = imagebufptr[offset];
+
+ *imagebuf = r << 24 | g << 16 | b << 8 | a;
+ imagebuf++;
+ }
+
+ c->width = width;
+ c->height = height;
+
+ amiga_bitmap_modified(icon_c->bitmap);
+ content_set_ready(c);
+ content_set_done(c);
+ content_set_status(c, "");
+
+ if(dobj) FreeDiskObject(dobj);
+
+ if(format==IDFMT_PALETTEMAPPED)
+ FreeVec(imagebufptr);
+
+ return true;
+}
+
+
+/**
+ * Destroy a CONTENT_AMIGA_ICON and free all resources it owns.
+ */
+
+void amiga_icon_destroy(struct content *c)
+{
+ amiga_icon_content *icon_c = (amiga_icon_content *)c;
+
+ if (icon_c->bitmap != NULL)
+ amiga_bitmap_destroy(icon_c->bitmap);
+}
+
+
+/**
+ * Redraw a CONTENT_AMIGA_ICON.
+ */
+
+bool amiga_icon_redraw(struct content *c,
+ struct content_redraw_data *data, const struct rect *clip,
+ const struct redraw_context *ctx)
+{
+ amiga_icon_content *icon_c = (amiga_icon_content *)c;
+ bitmap_flags_t flags = BITMAPF_NONE;
+
+ if (data->repeat_x)
+ flags |= BITMAPF_REPEAT_X;
+ if (data->repeat_y)
+ flags |= BITMAPF_REPEAT_Y;
+
+ return ctx->plot->bitmap(data->x, data->y, data->width, data->height,
+ icon_c->bitmap, data->background_colour, flags);
+}
+
+
+nserror amiga_icon_clone(const struct content *old, struct content **newc)
+{
+ amiga_icon_content *ai;
+ nserror error;
+
+ ai = calloc(1, sizeof(amiga_icon_content));
+ if (ai == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__clone(old, &ai->base);
+ if (error != NSERROR_OK) {
+ content_destroy(&ai->base);
+ return error;
+ }
+
+ /* Simply replay convert */
+ if (old->status == CONTENT_STATUS_READY ||
+ old->status == CONTENT_STATUS_DONE) {
+ if (amiga_icon_convert(&ai->base) == false) {
+ content_destroy(&ai->base);
+ return NSERROR_CLONE_FAILED;
+ }
+ }
+
+ *newc = (struct content *) ai;
+
+ return NSERROR_OK;
+}
+
+content_type amiga_icon_content_type(void)
+{
+ return CONTENT_IMAGE;
+}
+
+#endif /* WITH_AMIGA_ICON */
+
+ULONG *amiga_icon_convertcolouricon32(UBYTE *icondata, ULONG width, ULONG height,
+ ULONG trans, ULONG pals1, struct ColorRegister *pal1, int alpha)
+{
+ ULONG *argbicon;
+ struct ColorRegister *colour;
+ struct ColorMap *cmap;
+ ULONG i;
+ ULONG a,r,g,b;
+
+ if (alpha==0) alpha=0xff;
+
+ argbicon = (ULONG *)AllocVecTagList(width*height*4, NULL);
+ if (!argbicon) return(NULL);
+
+ cmap=GetColorMap(pals1);
+ if(!cmap) return(NULL);
+
+ for(i=0;i<(width*height);i++)
+ {
+ colour = &pal1[icondata[i]];
+
+ if(icondata[i] == trans)
+ {
+ a=0x00;
+ }
+ else
+ {
+ a=alpha;
+ }
+
+ r = colour->red;
+ g = colour->green;
+ b = colour->blue;
+
+ argbicon[i] = (a << 24) +
+ (r << 16) +
+ (g << 8) +
+ (b);
+ }
+
+ return(argbicon);
+
+}
+
+void amiga_icon_superimpose_favicon_internal(struct hlcache_handle *icon, struct DiskObject *dobj)
+{
+ struct BitMap *bm = NULL;
+ ULONG *icondata1, *icondata2;
+ ULONG width, height;
+ long format = 0;
+
+ if(dobj == NULL) return;
+
+ IconControl(dobj,
+ ICONCTRLA_GetImageDataFormat,&format,
+ ICONCTRLA_GetImageData1,&icondata1,
+ ICONCTRLA_GetImageData2,&icondata2,
+ ICONCTRLA_GetWidth,&width,
+ ICONCTRLA_GetHeight,&height,
+ TAG_DONE);
+
+ if(format != IDFMT_DIRECTMAPPED) return;
+#ifdef __amigaos4__
+ if ((icon != NULL) && (content_get_bitmap(icon) != NULL)) {
+ bm = ami_bitmap_get_native(content_get_bitmap(icon), 16, 16, NULL);
+ }
+
+ if(bm) {
+ BltBitMapTags(BLITA_SrcX, 0,
+ BLITA_SrcY, 0,
+ BLITA_DestX, width - 16,
+ BLITA_DestY, height - 16,
+ BLITA_Width, 16,
+ BLITA_Height, 16,
+ BLITA_Source, bm,
+ BLITA_Dest, icondata1,
+ BLITA_SrcType, BLITT_BITMAP,
+ BLITA_DestType, BLITT_ARGB32,
+ BLITA_DestBytesPerRow, width * 4,
+ BLITA_UseSrcAlpha, TRUE,
+ TAG_DONE);
+
+ BltBitMapTags(BLITA_SrcX, 0,
+ BLITA_SrcY, 0,
+ BLITA_DestX, width - 16,
+ BLITA_DestY, height - 16,
+ BLITA_Width, 16,
+ BLITA_Height, 16,
+ BLITA_Source, bm,
+ BLITA_Dest, icondata2,
+ BLITA_SrcType, BLITT_BITMAP,
+ BLITA_DestType, BLITT_ARGB32,
+ BLITA_DestBytesPerRow, width * 4,
+ BLITA_UseSrcAlpha, TRUE,
+ TAG_DONE);
+ }
+#endif
+}
+
+void amiga_icon_superimpose_favicon(char *path, struct hlcache_handle *icon, char *type)
+{
+ struct DiskObject *dobj = NULL;
+ ULONG *icondata1, *icondata2;
+ ULONG width, height;
+ long format = 0;
+ ULONG trans1, pals1;
+ ULONG trans2, pals2;
+ struct ColorRegister *pal1;
+ struct ColorRegister *pal2;
+
+ if(icon == NULL) return;
+
+ if(!type)
+ {
+ dobj = GetIconTags(NULL,
+ ICONGETA_GetDefaultType, WBDRAWER,
+ TAG_DONE);
+ }
+ else
+ {
+ dobj = GetIconTags(NULL, ICONGETA_GetDefaultName, type,
+ ICONGETA_GetDefaultType, WBPROJECT,
+ TAG_DONE);
+ }
+
+ if(dobj == NULL) return;
+
+ IconControl(dobj,
+ ICONCTRLA_GetImageDataFormat,&format,
+ ICONCTRLA_GetImageData1,&icondata1,
+ ICONCTRLA_GetImageData2,&icondata2,
+ ICONCTRLA_GetWidth,&width,
+ ICONCTRLA_GetHeight,&height,
+ TAG_DONE);
+
+ /* If we have a palette-mapped icon, convert it to a 32-bit one */
+ if(format == IDFMT_PALETTEMAPPED)
+ {
+ IconControl(dobj, ICONCTRLA_GetTransparentColor1, &trans1,
+ ICONCTRLA_GetPalette1, &pal1,
+ ICONCTRLA_GetPaletteSize1, &pals1,
+ ICONCTRLA_GetTransparentColor2, &trans2,
+ ICONCTRLA_GetPalette2, &pal2,
+ ICONCTRLA_GetPaletteSize2, &pals2,
+ TAG_DONE);
+
+ icondata1 = amiga_icon_convertcolouricon32((UBYTE *)icondata1,
+ width, height, trans1, pals1, pal1, 0xff);
+
+ icondata2 = amiga_icon_convertcolouricon32((UBYTE *)icondata2,
+ width, height, trans2, pals2, pal2, 0xff);
+
+ IconControl(dobj,
+ ICONCTRLA_SetImageDataFormat, IDFMT_DIRECTMAPPED,
+ ICONCTRLA_SetImageData1, icondata1,
+ ICONCTRLA_SetImageData2, icondata2,
+ TAG_DONE);
+ }
+
+ if((format == IDFMT_DIRECTMAPPED) || (format == IDFMT_PALETTEMAPPED))
+ amiga_icon_superimpose_favicon_internal(icon, dobj);
+
+ PutIconTags(path, dobj,
+ ICONPUTA_NotifyWorkbench, TRUE, TAG_DONE);
+
+ FreeDiskObject(dobj);
+
+ if(format == IDFMT_PALETTEMAPPED)
+ {
+ /* Free the 32-bit data we created */
+ FreeVec(icondata1);
+ FreeVec(icondata2);
+ }
+}
+
+struct DiskObject *amiga_icon_from_bitmap(struct bitmap *bm)
+{
+ struct DiskObject *dobj;
+ struct BitMap *bitmap;
+ ULONG *icondata;
+
+#ifdef __amigaos4__
+ if(bm)
+ {
+ bitmap = ami_bitmap_get_native(bm, THUMBNAIL_WIDTH,
+ THUMBNAIL_HEIGHT, NULL);
+ icondata = AllocVecTagList(THUMBNAIL_WIDTH * 4 * THUMBNAIL_HEIGHT, NULL);
+ ami_bitmap_set_icondata(bm, icondata);
+
+ BltBitMapTags(BLITA_Width, THUMBNAIL_WIDTH,
+ BLITA_Height, THUMBNAIL_HEIGHT,
+ BLITA_SrcType, BLITT_BITMAP,
+ BLITA_Source, bitmap,
+ BLITA_DestType, BLITT_ARGB32,
+ BLITA_DestBytesPerRow, THUMBNAIL_WIDTH * 4,
+ BLITA_Dest, icondata,
+ TAG_DONE);
+ }
+#endif
+ dobj = GetIconTags(NULL, ICONGETA_GetDefaultType, WBPROJECT,
+ ICONGETA_GetDefaultName, "iconify",
+ TAG_DONE);
+#ifdef __amigaos4__
+ if(bm)
+ {
+ IconControl(dobj,
+ ICONCTRLA_SetImageDataFormat, IDFMT_DIRECTMAPPED,
+ ICONCTRLA_SetWidth, THUMBNAIL_WIDTH,
+ ICONCTRLA_SetHeight, THUMBNAIL_HEIGHT,
+ ICONCTRLA_SetImageData1, icondata,
+ ICONCTRLA_SetImageData2, NULL,
+ TAG_DONE);
+ }
+#endif
+ dobj->do_Gadget.UserData = bm;
+
+ LayoutIconA(dobj, (struct Screen *)~0UL, NULL);
+
+ return dobj;
+}
+
+void amiga_icon_free(struct DiskObject *dobj)
+{
+ struct bitmap *bm = dobj->do_Gadget.UserData;
+
+ FreeDiskObject(dobj);
+ if(bm) FreeVec(ami_bitmap_get_icondata(bm));
+}
+
diff --git a/frontends/amiga/icon.h b/frontends/amiga/icon.h
new file mode 100644
index 000000000..e0ea5b734
--- /dev/null
+++ b/frontends/amiga/icon.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2010 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Content for image/x-amiga-icon (icon.library interface).
+ */
+
+#ifndef AMIGA_ICON_H
+#define AMIGA_ICON_H
+
+#include "utils/config.h"
+#include "utils/errors.h"
+
+#ifdef WITH_AMIGA_ICON
+
+nserror amiga_icon_init(void);
+void amiga_icon_fini(void);
+
+#else
+
+#define amiga_icon_init() NSERROR_OK
+#define amiga_icon_fini() ((void) 0)
+
+#endif /* WITH_AMIGA_ICON */
+
+struct hlcache_handle;
+
+void amiga_icon_superimpose_favicon(char *path, struct hlcache_handle *icon, char *type);
+void amiga_icon_superimpose_favicon_internal(struct hlcache_handle *icon, struct DiskObject *dobj);
+struct DiskObject *amiga_icon_from_bitmap(struct bitmap *bm);
+void amiga_icon_free(struct DiskObject *dobj);
+#endif
diff --git a/frontends/amiga/iff_cset.h b/frontends/amiga/iff_cset.h
new file mode 100755
index 000000000..2b8af8f6e
--- /dev/null
+++ b/frontends/amiga/iff_cset.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2008 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_IFF_CSET_H
+#define AMIGA_IFF_CSET_H
+#include <exec/types.h>
+
+/* This structure is for the IFF CSET chunk, registered by Martin Taillefer */
+
+struct CSet {
+ LONG CodeSet; /* 0=ECMA Latin 1 (std Amiga charset) */
+ /* CBM will define additional values */
+ LONG Reserved[7];
+ };
+
+#define ID_CSET MAKE_ID('C','S','E','T')
+
+#endif
diff --git a/frontends/amiga/iff_dr2d.c b/frontends/amiga/iff_dr2d.c
new file mode 100644
index 000000000..a4d17386b
--- /dev/null
+++ b/frontends/amiga/iff_dr2d.c
@@ -0,0 +1,422 @@
+/*
+ * Copyright 2009 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#ifdef WITH_NS_SVG
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <svgtiny.h>
+#include <proto/exec.h>
+#include <string.h>
+#include <proto/dos.h>
+
+#ifndef AMIGA_DR2D_STANDALONE
+#include "amiga/os3support.h"
+#include "amiga/iff_dr2d.h"
+#include "amiga/misc.h"
+#include "content/hlcache.h"
+#else
+#include "os3support.h"
+#include "iff_dr2d.h"
+#include "misc.h"
+#endif
+
+struct ColorRegister cm[1000];
+ULONG numcols;
+
+static ULONG findcolour(ULONG newcol)
+{
+ ULONG i;
+ ULONG colr = 0xFFFFFFFF;
+ UBYTE red,grn,blu;
+
+ red = svgtiny_RED(newcol);
+ grn = svgtiny_GREEN(newcol);
+ blu = svgtiny_BLUE(newcol);
+
+ for(i=0;i<numcols;i++)
+ {
+ if((cm[i].red == red) && (cm[i].green == grn) && (cm[i].blue == blu))
+ colr = i;
+ }
+
+ return colr;
+}
+
+static void addcolour(ULONG newcol)
+{
+ ULONG colr = findcolour(newcol);
+
+ if(colr == 0xFFFFFFFF)
+ {
+ cm[numcols].red = svgtiny_RED(newcol);
+ cm[numcols].green = svgtiny_GREEN(newcol);
+ cm[numcols].blue = svgtiny_BLUE(newcol);
+
+ numcols++;
+ }
+}
+
+bool ami_svg_to_dr2d(struct IFFHandle *iffh, const char *buffer,
+ uint32_t size, const char *url)
+{
+ struct svgtiny_diagram *diagram;
+ svgtiny_code code;
+ unsigned int i;
+ unsigned int j;
+ BOOL fons_written = FALSE;
+ struct fons_struct *fons;
+ struct stxt_struct *stxt;
+ struct attr_struct *attr;
+
+ /* create svgtiny object */
+ diagram = svgtiny_create();
+ if (!diagram) {
+ fprintf(stderr, "svgtiny_create failed\n");
+ return 1;
+ }
+
+ /* parse */
+ code = svgtiny_parse(diagram, buffer, size, url, 1000, 1000);
+ if (code != svgtiny_OK) {
+ fprintf(stderr, "svgtiny_parse failed: ");
+ switch (code) {
+ case svgtiny_OUT_OF_MEMORY:
+ fprintf(stderr, "svgtiny_OUT_OF_MEMORY");
+ break;
+ case svgtiny_LIBDOM_ERROR:
+ fprintf(stderr, "svgtiny_LIBDOM_ERROR");
+ break;
+ case svgtiny_NOT_SVG:
+ fprintf(stderr, "svgtiny_NOT_SVG");
+ break;
+ case svgtiny_SVG_ERROR:
+ fprintf(stderr, "svgtiny_SVG_ERROR: line %i: %s",
+ diagram->error_line,
+ diagram->error_message);
+ break;
+ default:
+ fprintf(stderr, "unknown svgtiny_code %i", code);
+ break;
+ }
+ fprintf(stderr, "\n");
+ }
+
+ if(!(PushChunk(iffh,ID_DR2D,ID_FORM,IFFSIZE_UNKNOWN)))
+ {
+ if(!(PushChunk(iffh,0,ID_NAME,IFFSIZE_UNKNOWN)))
+ {
+ WriteChunkBytes(iffh,url,strlen(url));
+ PopChunk(iffh);
+ }
+
+ if(!(PushChunk(iffh,0,ID_ANNO,19)))
+ {
+ WriteChunkBytes(iffh,"Created by NetSurf\0",19);
+ PopChunk(iffh);
+ }
+
+ if(!(PushChunk(iffh,0,ID_DRHD,16)))
+ {
+ struct drhd_struct drhd;
+ drhd.XLeft = (float) 0.0;
+ drhd.YTop = (float) 0.0;
+ drhd.XRight = (float) diagram->width;
+ drhd.YBot = (float) diagram->height;
+
+ WriteChunkBytes(iffh,&drhd,16);
+ PopChunk(iffh);
+ }
+
+ if(!(PushChunk(iffh,0,ID_DASH,IFFSIZE_UNKNOWN)))
+ {
+ struct dash_struct dash;
+ dash.DashID = 1;
+ dash.NumDashes = 0;
+
+ WriteChunkBytes(iffh,&dash,sizeof(struct dash_struct));
+ PopChunk(iffh);
+ }
+
+ if(!(PushChunk(iffh,0,ID_CMAP,IFFSIZE_UNKNOWN)))
+ {
+ for (i = 0; i != diagram->shape_count; i++) {
+ if(diagram->shape[i].fill != svgtiny_TRANSPARENT)
+ {
+ addcolour(diagram->shape[i].fill);
+ }
+
+ if(diagram->shape[i].stroke != svgtiny_TRANSPARENT)
+ {
+ addcolour(diagram->shape[i].stroke);
+ }
+ }
+
+ WriteChunkBytes(iffh,cm,3*numcols);
+ PopChunk(iffh);
+ }
+
+ for (i = 0; i != diagram->shape_count; i++) {
+ attr = ami_misc_allocvec_clear(sizeof(struct attr_struct), 0);
+ if (diagram->shape[i].fill == svgtiny_TRANSPARENT)
+ attr->FillType = FT_NONE;
+ else
+ {
+ attr->FillType = FT_COLOR;
+ attr->FillValue = findcolour(diagram->shape[i].fill);
+ }
+ if (diagram->shape[i].stroke == svgtiny_TRANSPARENT)
+ attr->DashPattern = 0;
+ else
+ {
+ attr->DashPattern = 1;
+ attr->EdgeValue = findcolour(diagram->shape[i].stroke);
+ }
+ attr->EdgeThick = (float) diagram->shape[i].stroke_width;
+
+ if(!(PushChunk(iffh,0,ID_ATTR,IFFSIZE_UNKNOWN)))
+ {
+ WriteChunkBytes(iffh,attr,14);
+ PopChunk(iffh);
+ }
+ FreeVec(attr);
+
+ if (diagram->shape[i].path) {
+ union {
+ float PolyPoints;
+ ULONG val;
+ } poly[(diagram->shape[i].path_length)*2];
+
+ USHORT NumPoints;
+ long type;
+ float curx,cury;
+
+ curx = 0.0;
+ cury = 0.0;
+ NumPoints = 0;
+ type = ID_OPLY;
+
+ for (j = 0;
+ j != diagram->shape[i].path_length; ) {
+ switch ((int) diagram->shape[i].path[j]) {
+ case svgtiny_PATH_MOVE:
+ if(j != 0)
+ {
+ poly[NumPoints*2].val = INDICATOR;
+ poly[(NumPoints*2)+1].val = IND_MOVETO;
+ NumPoints++;
+ }
+ poly[(NumPoints*2)].PolyPoints = diagram->shape[i].path[j + 1];
+ poly[(NumPoints*2)+1].PolyPoints = diagram->shape[i].path[j + 2];
+ NumPoints++;
+ curx = (float) diagram->shape[i].path[j + 1];
+ cury = (float) diagram->shape[i].path[j + 2];
+
+ j += 3;
+ break;
+ case svgtiny_PATH_CLOSE:
+ type = ID_CPLY;
+ j += 1;
+ break;
+ case svgtiny_PATH_LINE:
+ poly[(NumPoints*2)].PolyPoints = (float) diagram->shape[i].path[j + 1];
+ poly[(NumPoints*2)+1].PolyPoints = (float) diagram->shape[i].path[j + 2];
+ NumPoints++;
+ curx = (float) diagram->shape[i].path[j + 1];
+ cury = (float) diagram->shape[i].path[j + 2];
+ j += 3;
+ break;
+ case svgtiny_PATH_BEZIER:
+ poly[NumPoints*2].val = INDICATOR;
+ poly[(NumPoints*2)+1].val = IND_CURVE;
+ NumPoints++;
+ poly[(NumPoints*2)].PolyPoints = curx;
+ poly[(NumPoints*2)+1].PolyPoints = cury;
+ NumPoints++;
+ poly[(NumPoints*2)].PolyPoints = (float) diagram->shape[i].path[j + 1];
+ poly[(NumPoints*2)+1].PolyPoints = (float) diagram->shape[i].path[j + 2];
+ NumPoints++;
+ poly[(NumPoints*2)].PolyPoints = (float) diagram->shape[i].path[j + 3];
+ poly[(NumPoints*2)+1].PolyPoints = (float) diagram->shape[i].path[j + 4];
+ NumPoints++;
+ poly[(NumPoints*2)].PolyPoints = (float) diagram->shape[i].path[j + 5];
+ poly[(NumPoints*2)+1].PolyPoints = (float) diagram->shape[i].path[j + 6];
+ curx = poly[(NumPoints*2)].PolyPoints;
+ cury = poly[(NumPoints*2)+1].PolyPoints;
+ NumPoints++;
+ j += 7;
+ break;
+ default:
+ printf("error\n");
+ j += 1;
+ }
+ }
+ if(!(PushChunk(iffh,0,type,IFFSIZE_UNKNOWN)))
+ {
+ WriteChunkBytes(iffh,&NumPoints,sizeof(USHORT));
+ WriteChunkBytes(iffh,poly,NumPoints*2*4);
+ PopChunk(iffh);
+ }
+ } else if (diagram->shape[i].text) {
+ stxt = ami_misc_allocvec_clear(sizeof(struct stxt_struct), 0);
+ stxt->BaseX = diagram->shape[i].text_x;
+ stxt->BaseY = diagram->shape[i].text_y;
+ stxt->NumChars = strlen(diagram->shape[i].text);
+ if(!fons_written)
+ {
+ fons = ami_misc_allocvec_clear(sizeof(struct fons_struct), 0);
+ if(!(PushChunk(iffh,0,ID_FONS,IFFSIZE_UNKNOWN)))
+ {
+ WriteChunkBytes(iffh, fons, sizeof(struct fons_struct));
+ WriteChunkBytes(iffh, "Topaz\0", 6);
+ PopChunk(iffh);
+ }
+ FreeVec(fons);
+ fons_written = TRUE;
+ }
+
+ if(!(PushChunk(iffh,0,ID_STXT,IFFSIZE_UNKNOWN)))
+ {
+ WriteChunkBytes(iffh,stxt,26);
+ WriteChunkBytes(iffh,diagram->shape[i].text,strlen(diagram->shape[i].text));
+ PopChunk(iffh);
+ }
+ FreeVec(stxt);
+ }
+ }
+
+ PopChunk(iffh);
+ }
+
+ svgtiny_free(diagram);
+
+ return 0;
+}
+
+#ifndef AMIGA_DR2D_STANDALONE
+bool ami_save_svg(struct hlcache_handle *c,char *filename)
+{
+ struct IFFHandle *iffh;
+ const char *source_data;
+ ULONG source_size;
+
+ if(!ami_download_check_overwrite(filename, NULL, 0)) return false;
+
+ if((iffh = AllocIFF())) {
+ if((iffh->iff_Stream = Open(filename,MODE_NEWFILE))) {
+ InitIFFasDOS(iffh);
+ }
+ else return false;
+ }
+
+ if((OpenIFF(iffh,IFFF_WRITE))) return false;
+
+ if((source_data = content_get_source_data(c, &source_size)))
+ ami_svg_to_dr2d(iffh, source_data, source_size, nsurl_access(hlcache_handle_get_url(c)));
+
+ if(iffh) CloseIFF(iffh);
+ if(iffh->iff_Stream) Close((BPTR)iffh->iff_Stream);
+ if(iffh) FreeIFF(iffh);
+
+ return true;
+}
+#else
+/*
+ * This code can be compiled as a standalone program for testing etc.
+ * Use something like the following line:
+ * gcc -o svg2dr2d iff_dr2d.c -lauto -lsvgtiny -lpthread -lsvgtiny
+ * -ldom -lwapcaplet -lexpat -lparserutils
+ * -DWITH_NS_SVG -DAMIGA_DR2D_STANDALONE -D__USE_INLINE__ -D__NOLIBBASE__
+ */
+
+const char __attribute__((used)) ver[] = "\0$VER: svg2dr2d 1.2 (05.01.2015)\0";
+
+int main(int argc, char **argv)
+{
+ BPTR fh = 0;
+ char *buffer;
+ struct IFFHandle *iffh = NULL;
+ int64_t size;
+ LONG rarray[] = {0,0};
+ struct RDArgs *args;
+ STRPTR template = "SVG=INPUT/A,DR2D=OUTPUT/A";
+ enum
+ {
+ A_SVG,
+ A_DR2D
+ };
+
+#ifndef __amigaos4__
+ DOSBase = OpenLibrary("dos.library", 37);
+ if(!DOSBase) return RETURN_FAIL;
+
+ IFFParseBase = OpenLibrary("iffparse.library", 37);
+ if(!IFFParseBase) return RETURN_FAIL;
+#endif
+
+ args = ReadArgs(template,rarray,NULL);
+
+ if(!args)
+ {
+ printf("Required argument missing\n");
+ return 20;
+ }
+
+ if(fh = Open((char *)rarray[A_SVG],MODE_OLDFILE))
+ {
+ size = GetFileSize(fh);
+
+ buffer = AllocVecTagList((uint32_t)size, NULL);
+
+ Read(fh,buffer,(uint32_t)size);
+ Close(fh);
+ }
+ else
+ {
+ printf("Unable to open file\n");
+ return 20;
+ }
+
+ if(iffh = AllocIFF())
+ {
+ if(iffh->iff_Stream = Open((char *)rarray[A_DR2D],MODE_NEWFILE))
+ {
+ InitIFFasDOS(iffh);
+ }
+ else return 20;
+ }
+
+ if((OpenIFF(iffh,IFFF_WRITE))) return 20;
+
+ ami_svg_to_dr2d(iffh,buffer,size,(char *)rarray[A_SVG]);
+
+ FreeVec(buffer);
+ if(iffh) CloseIFF(iffh);
+ if(iffh->iff_Stream) Close((BPTR)iffh->iff_Stream);
+ if(iffh) FreeIFF(iffh);
+ FreeArgs(args);
+
+#ifndef __amigaos4__
+ if(DOSBase) CloseLibrary(DOSBase);
+ if(IFFParseBase) CloseLibrary(IFFParseBase);
+#endif
+}
+
+#endif // AMIGA_DR2D_STANDALONE
+#endif // WITH_NS_SVG
+
diff --git a/frontends/amiga/iff_dr2d.h b/frontends/amiga/iff_dr2d.h
new file mode 100644
index 000000000..c9981d33a
--- /dev/null
+++ b/frontends/amiga/iff_dr2d.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2009 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#ifdef WITH_NS_SVG
+#ifndef AMIGA_IFF_DR2D_H
+#define AMIGA_IFF_DR2D_H
+#include <proto/iffparse.h>
+#include <datatypes/pictureclass.h>
+#include <stdbool.h>
+#ifndef AMIGA_DR2D_STANDALONE
+#include "content/content.h"
+#include "amiga/download.h"
+#endif
+
+#define ID_DR2D MAKE_ID('D','R','2','D')
+#define ID_DRHD MAKE_ID('D','R','H','D')
+#define ID_ATTR MAKE_ID('A','T','T','R')
+#define ID_CPLY MAKE_ID('C','P','L','Y')
+#define ID_OPLY MAKE_ID('O','P','L','Y')
+#define ID_STXT MAKE_ID('S','T','X','T')
+#define ID_DASH MAKE_ID('D','A','S','H')
+//#define ID_CMAP MAKE_ID('C','M','A','P') in dt/pictureclass
+//#define ID_NAME MAKE_ID('N','A','M','E') in dt/datatypes
+#define ID_ANNO MAKE_ID('A','N','N','O')
+#define ID_FONS MAKE_ID('F','O','N','S')
+
+struct drhd_struct {
+ float XLeft, YTop, XRight, YBot;
+};
+
+struct poly_struct {
+ USHORT NumPoints;
+// float PolyPoints[]; // 2*numpoints
+};
+
+#define INDICATOR 0xFFFFFFFF
+#define IND_SPLINE 0x00000001
+#define IND_MOVETO 0x00000002
+#define IND_CURVE 0x00000001
+
+struct fons_struct {
+ UBYTE FontID; /* ID the font is referenced by */
+ UBYTE Pad1; /* Always 0 */
+ UBYTE Proportional; /* Is it proportional? */
+ UBYTE Serif; /* does it have serifs? */
+};
+
+struct stxt_struct {
+ UBYTE Pad0; /* Always 0 (for future expansion) */
+ UBYTE WhichFont; /* Which font to use */
+ float CharW, CharH, /* W/H of an individual char */
+ BaseX, BaseY, /* Start of baseline */
+ Rotation; /* Angle of text (in degrees) */
+ uint16_t NumChars;
+ //char TextChars[NumChars];
+};
+
+/* Various fill types */
+#define FT_NONE 0 /* No fill */
+#define FT_COLOR 1 /* Fill with color from palette */
+#define FT_OBJECTS 2 /* Fill with tiled objects */
+
+struct attr_struct {
+ UBYTE FillType; /* One of FT_*, above */
+ UBYTE JoinType; /* One of JT_*, below */
+ UBYTE DashPattern; /* ID of edge dash pattern */
+ UBYTE ArrowHead; /* ID of arrowhead to use */
+ USHORT FillValue; /* Color or object with which to fill */
+ USHORT EdgeValue; /* Edge color index */
+ USHORT WhichLayer; /* ID of layer it's in */
+ float EdgeThick; /* Line width */
+};
+
+/* Join types */
+#define JT_NONE 0 /* Don't do line joins */
+#define JT_MITER 1 /* Mitered join */
+#define JT_BEVEL 2 /* Beveled join */
+#define JT_ROUND 3 /* Round join */
+
+struct dash_struct {
+ USHORT DashID; /* ID of the dash pattern */
+ USHORT NumDashes; /* Should always be even */
+// IEEE Dashes[NumDashes]; /* On-off pattern */
+};
+
+bool ami_svg_to_dr2d(struct IFFHandle *iffh, const char *buffer,
+ uint32_t size, const char *url);
+#ifndef AMIGA_DR2D_STANDALONE
+bool ami_save_svg(struct hlcache_handle *c, char *filename);
+#endif
+#endif // AMIGA_IFF_DR2D_H
+#endif // WITH_NS_SVG
diff --git a/frontends/amiga/launch.c b/frontends/amiga/launch.c
new file mode 100755
index 000000000..1f2b9e16c
--- /dev/null
+++ b/frontends/amiga/launch.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2008-10 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Fetching of data from a file (implementation).
+ */
+
+#include "amiga/os3support.h"
+
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/utility.h>
+#include <proto/openurl.h>
+
+#include "amiga/launch.h"
+#include "amiga/object.h" /* for list abstraction */
+#include "utils/nsoption.h"
+#include "utils/nsurl.h"
+
+struct Library *OpenURLBase = NULL;
+struct OpenURLIFace *IOpenURL = NULL;
+
+struct MinList *ami_unsupportedprotocols;
+
+struct ami_protocol
+{
+ struct MinNode node;
+ lwc_string *protocol;
+};
+
+static struct ami_protocol *ami_openurl_add_protocol(const char *url)
+{
+ nsurl *ns_url;
+ struct ami_protocol *ami_p =
+ (struct ami_protocol *)AllocVecTagList(sizeof(struct ami_protocol), NULL);
+
+ if (nsurl_create(url, &ns_url) != NSERROR_OK) {
+ FreeVec(ami_p);
+ return NULL;
+ }
+
+ ami_p->protocol = nsurl_get_component(ns_url, NSURL_SCHEME);
+ nsurl_unref(ns_url);
+ if (ami_p->protocol == NULL)
+ {
+ FreeVec(ami_p);
+ return NULL;
+ }
+
+ AddTail((struct List *)ami_unsupportedprotocols, (struct Node *)ami_p);
+ return ami_p;
+}
+
+static void ami_openurl_free_list(struct MinList *list)
+{
+ struct ami_protocol *node;
+ struct ami_protocol *nnode;
+
+ if(IsMinListEmpty(list)) return;
+ node = (struct ami_protocol *)GetHead((struct List *)list);
+
+ do
+ {
+ nnode=(struct ami_protocol *)GetSucc((struct Node *)node);
+
+ Remove((struct Node *)node);
+ if (node->protocol) lwc_string_unref(node->protocol);
+ FreeVec(node);
+ node = NULL;
+ }while((node=nnode));
+
+ FreeVec(list);
+}
+
+static BOOL ami_openurl_check_list(struct MinList *list, nsurl *url)
+{
+ struct ami_protocol *node;
+ struct ami_protocol *nnode;
+ lwc_string *url_scheme;
+ bool match;
+
+ if(IsMinListEmpty(list)) return FALSE;
+
+ url_scheme = nsurl_get_component(url, NSURL_SCHEME);
+
+ node = (struct ami_protocol *)GetHead((struct List *)list);
+
+ do
+ {
+ nnode=(struct ami_protocol *)GetSucc((struct Node *)node);
+
+ if ((lwc_string_isequal(url_scheme, node->protocol,
+ &match) == lwc_error_ok) && (match == true)) {
+ lwc_string_unref(url_scheme);
+ return TRUE;
+ }
+ }while((node=nnode));
+
+ lwc_string_unref(url_scheme);
+ return FALSE;
+}
+
+/**
+ * Initialise the fetcher.
+ *
+ * Must be called once before any other function.
+ */
+
+void ami_openurl_open(void)
+{
+ if(nsoption_bool(use_openurl_lib)) {
+ if((OpenURLBase = OpenLibrary("openurl.library",0))) {
+#ifdef __amigaos4__
+ IOpenURL = (struct OpenURLIFace *)GetInterface(OpenURLBase,"main",1,NULL);
+#endif
+ }
+ }
+
+ ami_unsupportedprotocols = ami_AllocMinList();
+}
+
+void ami_openurl_close(void)
+{
+#ifdef __amigaos4__
+ if(IOpenURL) DropInterface((struct Interface *)IOpenURL);
+#endif
+ if(OpenURLBase) CloseLibrary(OpenURLBase);
+
+ ami_openurl_free_list(ami_unsupportedprotocols);
+}
+
+nserror gui_launch_url(struct nsurl *url)
+{
+#ifdef __amigaos4__
+ APTR procwin = SetProcWindow((APTR)-1L);
+#endif
+ char *launchurl = NULL;
+
+ if(ami_openurl_check_list(ami_unsupportedprotocols, url) == FALSE)
+ {
+ if(IOpenURL)
+ {
+ URL_OpenA((STRPTR)url,NULL);
+ } else {
+ if((launchurl = ASPrintf("URL:%s", nsurl_access(url)))) {
+ BPTR fptr = Open(launchurl,MODE_OLDFILE);
+ if(fptr)
+ {
+ Close(fptr);
+ } else {
+ ami_openurl_add_protocol(nsurl_access(url));
+ }
+ FreeVec(launchurl);
+ }
+ }
+ }
+#ifdef __amigaos4__
+ SetProcWindow(procwin);
+#endif
+ return NSERROR_OK;
+}
diff --git a/frontends/amiga/launch.h b/frontends/amiga/launch.h
new file mode 100755
index 000000000..e2b355065
--- /dev/null
+++ b/frontends/amiga/launch.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2008-9 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Fetching of data from a URL (Registration).
+ */
+
+#ifndef AMIGA_LAUNCH_H
+#define AMIGA_LAUNCH_H
+#include "utils/errors.h"
+struct nsurl;
+
+void ami_openurl_open(void);
+void ami_openurl_close(void);
+
+nserror gui_launch_url(struct nsurl *url);
+
+#endif
diff --git a/frontends/amiga/libs.c b/frontends/amiga/libs.c
new file mode 100644
index 000000000..993cd3ad0
--- /dev/null
+++ b/frontends/amiga/libs.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright 2014 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "amiga/os3support.h"
+
+#include "amiga/libs.h"
+#include "amiga/misc.h"
+#include "utils/utils.h"
+#include "utils/log.h"
+
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <proto/utility.h>
+
+#include <graphics/gfxbase.h> /* Needed for v54 version check */
+
+#ifndef __amigaos4__
+/* OS3 needs these for the XXXX_GetClass() functions */
+#include <proto/arexx.h>
+#include <proto/bevel.h>
+#include <proto/bitmap.h>
+#include <proto/button.h>
+#include <proto/chooser.h>
+#include <proto/checkbox.h>
+#include <proto/clicktab.h>
+#include <proto/fuelgauge.h>
+#include <proto/getfile.h>
+#include <proto/getfont.h>
+#include <proto/getscreenmode.h>
+#include <proto/integer.h>
+#include <proto/label.h>
+#include <proto/layout.h>
+#include <proto/listbrowser.h>
+#include <proto/radiobutton.h>
+#include <proto/scroller.h>
+#include <proto/space.h>
+#include <proto/speedbar.h>
+#include <proto/string.h>
+#include <proto/window.h>
+#endif
+
+#ifdef __amigaos4__
+#define AMINS_LIB_OPEN(LIB, LIBVER, PREFIX, INTERFACE, INTVER, FAIL) \
+ LOG("Opening %s v%d", LIB, LIBVER); \
+ if((PREFIX##Base = (struct PREFIX##Base *)OpenLibrary(LIB, LIBVER))) { \
+ I##PREFIX = (struct PREFIX##IFace *)GetInterface((struct Library *)PREFIX##Base, INTERFACE, INTVER, NULL); \
+ if(I##PREFIX == NULL) { \
+ LOG("Failed to get %s interface v%d of %s", INTERFACE, INTVER, LIB); \
+ } \
+ } else { \
+ LOG("Failed to open %s v%d", LIB, LIBVER); \
+ if(FAIL == true) { \
+ STRPTR error = ASPrintf("Unable to open %s v%ld (fatal error)", LIB, LIBVER); \
+ ami_misc_fatal_error(error); \
+ FreeVec(error); \
+ return false; \
+ } \
+ }
+
+#define AMINS_LIB_CLOSE(PREFIX) \
+ if(I##PREFIX) DropInterface((struct Interface *)I##PREFIX); \
+ if(PREFIX##Base) CloseLibrary((struct Library *)PREFIX##Base);
+
+#define AMINS_LIB_STRUCT(PREFIX) \
+ struct PREFIX##Base *PREFIX##Base = NULL; \
+ struct PREFIX##IFace *I##PREFIX = NULL;
+
+#define AMINS_CLASS_OPEN(CLASS, CLASSVER, PREFIX, CLASSGET, NEEDINTERFACE) \
+ LOG("Opening %s v%d", CLASS, CLASSVER); \
+ if((PREFIX##Base = OpenClass(CLASS, CLASSVER, &PREFIX##Class))) { \
+ if(NEEDINTERFACE == true) { \
+ LOG(" + interface"); \
+ I##PREFIX = (struct PREFIX##IFace *)GetInterface((struct Library *)PREFIX##Base, "main", 1, NULL); \
+ if(I##PREFIX == NULL) { \
+ LOG("Failed to get main interface v1 of %s", CLASS); \
+ } \
+ } \
+ } \
+ if(PREFIX##Class == NULL) { \
+ STRPTR error = ASPrintf("Unable to open %s v%d (fatal error)", CLASS, CLASSVER); \
+ ami_misc_fatal_error(error); \
+ FreeVec(error); \
+ return false; \
+ }
+
+#define AMINS_CLASS_CLOSE(PREFIX) \
+ if(I##PREFIX) DropInterface((struct Interface *)I##PREFIX); \
+ if(PREFIX##Base) CloseClass(PREFIX##Base);
+
+#define AMINS_CLASS_STRUCT(PREFIX) \
+ struct ClassLibrary *PREFIX##Base = NULL; \
+ struct PREFIX##IFace *I##PREFIX = NULL; \
+ Class *PREFIX##Class = NULL;
+
+#else
+#define AMINS_LIB_OPEN(LIB, LIBVER, PREFIX, INTERFACE, INTVER, FAIL) \
+ LOG("Opening %s v%d", LIB, LIBVER); \
+ if((PREFIX##Base = (struct PREFIX##Base *)OpenLibrary(LIB, LIBVER))) { \
+ } else { \
+ LOG("Failed to open %s v%d", LIB, LIBVER); \
+ if(FAIL == true) { \
+ STRPTR error = ASPrintf("Unable to open %s v%d (fatal error)", LIB, LIBVER); \
+ ami_misc_fatal_error(error); \
+ FreeVec(error); \
+ return false; \
+ } \
+ }
+
+#define AMINS_LIB_CLOSE(PREFIX) \
+ if(PREFIX##Base) CloseLibrary((struct Library *)PREFIX##Base);
+
+#define AMINS_LIB_STRUCT(PREFIX) \
+ struct PREFIX##Base *PREFIX##Base = NULL;
+
+#define AMINS_CLASS_OPEN(CLASS, CLASSVER, PREFIX, CLASSGET, NEEDINTERFACE) \
+ LOG("Opening %s v%d", CLASS, CLASSVER); \
+ if((PREFIX##Base = OpenLibrary(CLASS, CLASSVER))) { \
+ PREFIX##Class = CLASSGET##_GetClass(); \
+ } \
+ if(PREFIX##Class == NULL) { \
+ STRPTR error = ASPrintf("Unable to open %s v%d (fatal error)", CLASS, CLASSVER); \
+ ami_misc_fatal_error(error); \
+ FreeVec(error); \
+ return false; \
+ }
+
+#define AMINS_CLASS_CLOSE(PREFIX) \
+ if(PREFIX##Base) CloseLibrary(PREFIX##Base);
+
+#define AMINS_CLASS_STRUCT(PREFIX) \
+ struct Library *PREFIX##Base = NULL; \
+ Class *PREFIX##Class = NULL;
+
+#endif
+
+#define GraphicsBase GfxBase /* graphics.library is a bit weird */
+
+#ifdef __amigaos4__
+AMINS_LIB_STRUCT(Application);
+#else
+AMINS_LIB_STRUCT(Utility)
+#endif
+AMINS_LIB_STRUCT(Asl);
+AMINS_LIB_STRUCT(DataTypes);
+AMINS_LIB_STRUCT(Diskfont);
+AMINS_LIB_STRUCT(Graphics);
+AMINS_LIB_STRUCT(GadTools);
+AMINS_LIB_STRUCT(Icon);
+AMINS_LIB_STRUCT(IFFParse);
+AMINS_LIB_STRUCT(Intuition);
+AMINS_LIB_STRUCT(Keymap);
+AMINS_LIB_STRUCT(Layers);
+AMINS_LIB_STRUCT(Locale);
+AMINS_LIB_STRUCT(P96);
+AMINS_LIB_STRUCT(Workbench);
+
+AMINS_LIB_STRUCT(GuiGFX);
+
+AMINS_CLASS_STRUCT(ARexx);
+AMINS_CLASS_STRUCT(Bevel);
+AMINS_CLASS_STRUCT(BitMap);
+AMINS_CLASS_STRUCT(Button);
+AMINS_CLASS_STRUCT(Chooser);
+AMINS_CLASS_STRUCT(CheckBox);
+AMINS_CLASS_STRUCT(ClickTab);
+AMINS_CLASS_STRUCT(FuelGauge);
+AMINS_CLASS_STRUCT(GetFile);
+AMINS_CLASS_STRUCT(GetFont);
+AMINS_CLASS_STRUCT(GetScreenMode);
+AMINS_CLASS_STRUCT(Integer);
+AMINS_CLASS_STRUCT(Label);
+AMINS_CLASS_STRUCT(Layout);
+AMINS_CLASS_STRUCT(ListBrowser);
+AMINS_CLASS_STRUCT(RadioButton);
+#ifndef __amigaos4__
+AMINS_CLASS_STRUCT(Page);
+#endif
+AMINS_CLASS_STRUCT(Scroller);
+AMINS_CLASS_STRUCT(Space);
+AMINS_CLASS_STRUCT(SpeedBar);
+AMINS_CLASS_STRUCT(String);
+AMINS_CLASS_STRUCT(Window);
+
+
+bool ami_libs_open(void)
+{
+#ifdef __amigaos4__
+ /* Libraries only needed on OS4 */
+ AMINS_LIB_OPEN("application.library", 53, Application, "application", 2, false)
+#else
+ /* Libraries we get automatically on OS4 but not OS3 */
+ AMINS_LIB_OPEN("utility.library", 37, Utility, "main", 1, true)
+#endif
+ /* Standard libraries for both versions */
+ AMINS_LIB_OPEN("asl.library", 37, Asl, "main", 1, true)
+ AMINS_LIB_OPEN("datatypes.library", 39, DataTypes, "main", 1, true)
+ AMINS_LIB_OPEN("diskfont.library", 40, Diskfont, "main", 1, true)
+ AMINS_LIB_OPEN("gadtools.library", 37, GadTools, "main", 1, true)
+ AMINS_LIB_OPEN("graphics.library", 40, Graphics, "main", 1, true)
+ AMINS_LIB_OPEN("icon.library", 44, Icon, "main", 1, true)
+ AMINS_LIB_OPEN("iffparse.library", 37, IFFParse, "main", 1, true)
+ AMINS_LIB_OPEN("intuition.library", 40, Intuition, "main", 1, true)
+ AMINS_LIB_OPEN("keymap.library", 37, Keymap, "main", 1, true)
+ AMINS_LIB_OPEN("layers.library", 37, Layers, "main", 1, true)
+ AMINS_LIB_OPEN("locale.library", 38, Locale, "main", 1, true)
+ AMINS_LIB_OPEN("workbench.library", 37, Workbench, "main", 1, true)
+
+ /* This is down here as we need to check the graphics.library version
+ * before opening. If it is sufficiently new enough we can avoid using P96.
+ */
+ if(GfxBase->LibNode.lib_Version < 54)
+ AMINS_LIB_OPEN("Picasso96API.library", 0, P96, "main", 1, false)
+
+ /* Non-OS provided libraries */
+ AMINS_LIB_OPEN("guigfx.library", 9, GuiGFX, "main", 1, true)
+
+ /* NB: timer.device is opened in schedule.c (ultimately by the scheduler process).
+ * The library base and interface are obtained there, rather than here, due to
+ * the additional complexities of opening devices, which aren't important here
+ * (as we only need the library interface), but are important for the scheduler
+ * (as it also uses the device interface). We trust that the scheduler has
+ * initialised before any other code requires the timer's library interface,
+ * to avoid opening it twice.
+ */
+
+ /* BOOPSI classes.
+ * Opened using class functions rather than the old-fashioned method.
+ * We get the class pointer once and used our stored copy.
+ * On OS4 these must be opened *after* intuition.library.
+ * NB: the last argument should be "true" only if the class also has
+ * library functions we use.
+ */
+ AMINS_CLASS_OPEN("arexx.class", 44, ARexx, AREXX, false)
+ AMINS_CLASS_OPEN("images/bevel.image", 44, Bevel, BEVEL, false)
+ AMINS_CLASS_OPEN("images/bitmap.image", 44, BitMap, BITMAP, false)
+ AMINS_CLASS_OPEN("gadgets/button.gadget", 44, Button, BUTTON, false)
+ AMINS_CLASS_OPEN("gadgets/checkbox.gadget", 44, CheckBox, CHECKBOX, false)
+ AMINS_CLASS_OPEN("gadgets/chooser.gadget", 44, Chooser, CHOOSER, true)
+ AMINS_CLASS_OPEN("gadgets/clicktab.gadget", 44, ClickTab, CLICKTAB, true)
+ AMINS_CLASS_OPEN("gadgets/fuelgauge.gadget", 44, FuelGauge, FUELGAUGE, false)
+ AMINS_CLASS_OPEN("gadgets/getfile.gadget", 44, GetFile, GETFILE, false)
+ AMINS_CLASS_OPEN("gadgets/getfont.gadget", 44, GetFont, GETFONT, false)
+ AMINS_CLASS_OPEN("gadgets/getscreenmode.gadget", 44, GetScreenMode, GETSCREENMODE, false)
+ AMINS_CLASS_OPEN("gadgets/integer.gadget", 44, Integer, INTEGER, false)
+ AMINS_CLASS_OPEN("images/label.image", 44, Label, LABEL, false)
+ AMINS_CLASS_OPEN("gadgets/layout.gadget", 44, Layout, LAYOUT, true)
+ AMINS_CLASS_OPEN("gadgets/listbrowser.gadget", 44, ListBrowser, LISTBROWSER, true)
+ AMINS_CLASS_OPEN("gadgets/radiobutton.gadget", 44, RadioButton, RADIOBUTTON, false)
+ AMINS_CLASS_OPEN("gadgets/scroller.gadget", 44, Scroller, SCROLLER, false)
+ AMINS_CLASS_OPEN("gadgets/space.gadget", 44, Space, SPACE, false)
+ AMINS_CLASS_OPEN("gadgets/speedbar.gadget", 44, SpeedBar, SPEEDBAR, true)
+ AMINS_CLASS_OPEN("gadgets/string.gadget", 44, String, STRING, false)
+ AMINS_CLASS_OPEN("window.class", 44, Window, WINDOW, false)
+
+#ifndef __amigaos4__
+ PageClass = PAGE_GetClass();
+#endif
+
+ return true;
+}
+
+void ami_libs_close(void)
+{
+ /* BOOPSI Classes.
+ * On OS4 these must be closed *before* intuition.library
+ */
+ AMINS_CLASS_CLOSE(ARexx)
+ AMINS_CLASS_CLOSE(Bevel)
+ AMINS_CLASS_CLOSE(BitMap)
+ AMINS_CLASS_CLOSE(Button)
+ AMINS_CLASS_CLOSE(CheckBox)
+ AMINS_CLASS_CLOSE(Chooser)
+ AMINS_CLASS_CLOSE(ClickTab)
+ AMINS_CLASS_CLOSE(FuelGauge)
+ AMINS_CLASS_CLOSE(GetFile)
+ AMINS_CLASS_CLOSE(GetFont)
+ AMINS_CLASS_CLOSE(GetScreenMode)
+ AMINS_CLASS_CLOSE(Integer)
+ AMINS_CLASS_CLOSE(Label)
+ AMINS_CLASS_CLOSE(Layout)
+ AMINS_CLASS_CLOSE(ListBrowser)
+ AMINS_CLASS_CLOSE(RadioButton)
+ AMINS_CLASS_CLOSE(Scroller)
+ AMINS_CLASS_CLOSE(Space)
+ AMINS_CLASS_CLOSE(SpeedBar)
+ AMINS_CLASS_CLOSE(String)
+ AMINS_CLASS_CLOSE(Window)
+
+ /* Libraries */
+ AMINS_LIB_CLOSE(GuiGFX)
+
+ AMINS_LIB_CLOSE(Asl)
+ AMINS_LIB_CLOSE(DataTypes)
+ AMINS_LIB_CLOSE(Diskfont)
+ AMINS_LIB_CLOSE(GadTools)
+ AMINS_LIB_CLOSE(Graphics)
+ AMINS_LIB_CLOSE(Icon)
+ AMINS_LIB_CLOSE(IFFParse)
+ AMINS_LIB_CLOSE(Intuition)
+ AMINS_LIB_CLOSE(Keymap)
+ AMINS_LIB_CLOSE(Layers)
+ AMINS_LIB_CLOSE(Locale)
+ AMINS_LIB_CLOSE(P96)
+ AMINS_LIB_CLOSE(Workbench)
+#ifdef __amigaos4__
+ AMINS_LIB_CLOSE(Application)
+#else
+ AMINS_LIB_CLOSE(Utility)
+#endif
+}
+
diff --git a/frontends/amiga/libs.h b/frontends/amiga/libs.h
new file mode 100644
index 000000000..aa3622af2
--- /dev/null
+++ b/frontends/amiga/libs.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_LIBS_H
+#include <stdbool.h>
+#include <intuition/classes.h>
+
+/* BOOPSI classes */
+extern Class *ARexxClass;
+extern Class *BevelClass;
+extern Class *BitMapClass;
+extern Class *ButtonClass;
+extern Class *CheckBoxClass;
+extern Class *ChooserClass;
+extern Class *ClickTabClass;
+extern Class *FuelGaugeClass;
+extern Class *GetFileClass;
+extern Class *GetFontClass;
+extern Class *GetScreenModeClass;
+extern Class *IntegerClass;
+extern Class *LabelClass;
+extern Class *LayoutClass;
+extern Class *ListBrowserClass;
+#ifndef __amigaos4__
+/* OS4 uses a public class name instead */
+extern Class *PageClass;
+#endif
+extern Class *RadioButtonClass;
+extern Class *ScrollerClass;
+extern Class *SpaceClass;
+extern Class *SpeedBarClass;
+extern Class *StringClass;
+extern Class *WindowClass;
+
+/* New improved ReAction macros! */
+#define ARexxObj NewObject(ARexxClass, NULL
+#define BevelObj NewObject(BevelClass, NULL
+#define BitMapObj NewObject(BitMapClass, NULL
+#define ButtonObj NewObject(ButtonClass, NULL
+#define CheckBoxObj NewObject(CheckBoxClass, NULL
+#define ChooserObj NewObject(ChooserClass, NULL
+#define ClickTabObj NewObject(ClickTabClass, NULL
+#define FuelGaugeObj NewObject(FuelGaugeClass, NULL
+#define GetFileObj NewObject(GetFileClass, NULL
+#define GetFontObj NewObject(GetFontClass, NULL
+#define GetScreenModeObj NewObject(GetScreenModeClass, NULL
+#define IntegerObj NewObject(IntegerClass, NULL
+#define LabelObj NewObject(LabelClass, NULL
+#define LayoutHObj NewObject(LayoutClass, NULL, LAYOUT_Orientation, LAYOUT_ORIENT_HORIZ
+#define LayoutVObj NewObject(LayoutClass, NULL, LAYOUT_Orientation, LAYOUT_ORIENT_VERT
+#ifdef __amigaos4__
+#define PageObj NewObject(NULL, "page.gadget"
+#else
+#define PageObj NewObject(PageClass, NULL
+#endif
+#define RadioButtonObj NewObject(RadioButtonClass, NULL
+#define ScrollerObj NewObject(ScrollerClass, NULL
+#define SpaceObj NewObject(SpaceClass, NULL
+#define SpeedBarObj NewObject(SpeedBarClass, NULL
+#define StringObj NewObject(StringClass, NULL
+#define WindowObj NewObject(WindowClass, NULL
+
+/* Functions */
+bool ami_libs_open(void);
+void ami_libs_close(void);
+#endif
+
diff --git a/frontends/amiga/login.c b/frontends/amiga/login.c
new file mode 100755
index 000000000..a4c0d62bb
--- /dev/null
+++ b/frontends/amiga/login.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2008 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "amiga/os3support.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <proto/utility.h>
+
+#include <proto/window.h>
+#include <proto/layout.h>
+#include <proto/string.h>
+#include <proto/button.h>
+#include <proto/label.h>
+#include <classes/window.h>
+#include <gadgets/layout.h>
+#include <gadgets/string.h>
+#include <gadgets/button.h>
+#include <images/label.h>
+#include <reaction/reaction_macros.h>
+
+#include "utils/messages.h"
+#include "utils/errors.h"
+#include "content/urldb.h"
+#include "desktop/mouse.h"
+#include "desktop/gui_window.h"
+
+#include "amiga/gui.h"
+#include "amiga/libs.h"
+#include "amiga/misc.h"
+#include "amiga/object.h"
+#include "amiga/login.h"
+
+struct gui_login_window {
+ struct nsObject *node;
+ struct Window *win;
+ Object *objects[GID_LAST];
+ nserror (*cb)(bool proceed, void *pw);
+ void *cbpw;
+ nsurl *url;
+ char *realm;
+ lwc_string *host;
+ char uname[256];
+ char pwd[256];
+};
+
+void gui_401login_open(nsurl *url, const char *realm,
+ nserror (*cb)(bool proceed, void *pw), void *cbpw)
+{
+ const char *auth;
+ struct gui_login_window *lw = ami_misc_allocvec_clear(sizeof(struct gui_login_window), 0);
+ lwc_string *host = nsurl_get_component(url, NSURL_HOST);
+
+ assert(host != NULL);
+
+ lw->host = host;
+ lw->url = nsurl_ref(url);
+ lw->realm = (char *)realm;
+ lw->cb = cb;
+ lw->cbpw = cbpw;
+
+ auth = urldb_get_auth_details(lw->url, realm);
+
+ if (auth == NULL) {
+ lw->uname[0] = '\0';
+ lw->pwd[0] = '\0';
+ } else {
+ const char *pwd;
+ size_t pwd_len;
+
+ pwd = strchr(auth, ':');
+ assert(pwd && pwd < auth + sizeof(lw->uname));
+ memcpy(lw->uname, auth, pwd - auth);
+ lw->uname[pwd - auth] = '\0';
+ ++pwd;
+ pwd_len = strlen(pwd);
+ assert(pwd_len < sizeof(lw->pwd));
+ memcpy(lw->pwd, pwd, pwd_len);
+ lw->pwd[pwd_len] = '\0';
+ }
+
+ lw->objects[OID_MAIN] = WindowObj,
+ WA_ScreenTitle, ami_gui_get_screen_title(),
+ WA_Title, nsurl_access(lw->url),
+ WA_Activate, TRUE,
+ WA_DepthGadget, TRUE,
+ WA_DragBar, TRUE,
+ WA_CloseGadget, FALSE,
+ WA_SizeGadget, TRUE,
+ WA_PubScreen,scrn,
+ WINDOW_SharedPort,sport,
+ WINDOW_UserData,lw,
+ WINDOW_IconifyGadget, FALSE,
+ WINDOW_LockHeight,TRUE,
+ WINDOW_Position, WPOS_CENTERSCREEN,
+ WINDOW_ParentGroup, lw->objects[GID_MAIN] = LayoutVObj,
+ LAYOUT_AddChild, StringObj,
+ STRINGA_TextVal,
+ lwc_string_data(lw->host),
+ GA_ReadOnly,TRUE,
+ StringEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text,messages_get("Host"),
+ LabelEnd,
+ CHILD_WeightedHeight,0,
+ LAYOUT_AddChild, StringObj,
+ STRINGA_TextVal,lw->realm,
+ GA_ReadOnly,TRUE,
+ StringEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text,messages_get("Realm"),
+ LabelEnd,
+ CHILD_WeightedHeight,0,
+ LAYOUT_AddChild, lw->objects[GID_USER] = StringObj,
+ GA_ID,GID_USER,
+ GA_TabCycle,TRUE,
+ STRINGA_TextVal, lw->uname,
+ StringEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text,messages_get("Username"),
+ LabelEnd,
+ CHILD_WeightedHeight,0,
+ LAYOUT_AddChild, lw->objects[GID_PASS] = StringObj,
+ GA_ID,GID_PASS,
+ STRINGA_HookType,SHK_PASSWORD,
+ GA_TabCycle,TRUE,
+ STRINGA_TextVal, lw->pwd,
+ StringEnd,
+ CHILD_Label, LabelObj,
+ LABEL_Text,messages_get("Password"),
+ LabelEnd,
+ CHILD_WeightedHeight,0,
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_AddChild, lw->objects[GID_LOGIN] = ButtonObj,
+ GA_ID,GID_LOGIN,
+ GA_RelVerify,TRUE,
+ GA_Text,messages_get("Login"),
+ GA_TabCycle,TRUE,
+ ButtonEnd,
+ CHILD_WeightedHeight,0,
+ LAYOUT_AddChild, lw->objects[GID_CANCEL] = ButtonObj,
+ GA_ID,GID_CANCEL,
+ GA_RelVerify,TRUE,
+ GA_Text,messages_get("Cancel"),
+ GA_TabCycle,TRUE,
+ ButtonEnd,
+ LayoutEnd,
+ CHILD_WeightedHeight,0,
+ EndGroup,
+ EndWindow;
+
+ lw->win = (struct Window *)RA_OpenWindow(lw->objects[OID_MAIN]);
+
+ lw->node = AddObject(window_list,AMINS_LOGINWINDOW);
+ lw->node->objstruct = lw;
+}
+
+static void ami_401login_close(struct gui_login_window *lw)
+{
+ /* If continuation exists, then forbid refetch */
+ if (lw->cb != NULL)
+ lw->cb(false, lw->cbpw);
+
+ DisposeObject(lw->objects[OID_MAIN]);
+ lwc_string_unref(lw->host);
+ nsurl_unref(lw->url);
+ DelObject(lw->node);
+}
+
+static void ami_401login_login(struct gui_login_window *lw)
+{
+ ULONG *user,*pass;
+ STRPTR userpass;
+
+ GetAttr(STRINGA_TextVal,lw->objects[GID_USER],(ULONG *)&user);
+ GetAttr(STRINGA_TextVal,lw->objects[GID_PASS],(ULONG *)&pass);
+
+ userpass = ASPrintf("%s:%s",user,pass);
+ urldb_set_auth_details(lw->url,lw->realm,userpass);
+ FreeVec(userpass);
+
+ lw->cb(true, lw->cbpw);
+
+ /* Invalidate continuation */
+ lw->cb = NULL;
+ lw->cbpw = NULL;
+
+ ami_401login_close(lw);
+}
+
+BOOL ami_401login_event(struct gui_login_window *lw)
+{
+ /* return TRUE if window destroyed */
+ ULONG result;
+ uint16 code;
+
+ while((result = RA_HandleInput(lw->objects[OID_MAIN], &code)) != WMHI_LASTMSG)
+ {
+ switch(result & WMHI_CLASSMASK) // class
+ {
+ case WMHI_GADGETUP:
+ switch(result & WMHI_GADGETMASK)
+ {
+ case GID_LOGIN:
+ ami_401login_login(lw);
+ return TRUE;
+ break;
+
+ case GID_CANCEL:
+ ami_401login_close(lw);
+ return TRUE;
+ break;
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
+
diff --git a/frontends/amiga/login.h b/frontends/amiga/login.h
new file mode 100755
index 000000000..e3f77901f
--- /dev/null
+++ b/frontends/amiga/login.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2008 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_LOGIN_H
+#define AMIGA_LOGIN_H
+
+#include <stdbool.h>
+
+struct gui_login_window;
+
+BOOL ami_401login_event(struct gui_login_window *lw);
+
+void gui_401login_open(nsurl *url, const char *realm,
+ nserror (*cb)(bool proceed, void *pw), void *cbpw);
+
+#endif
diff --git a/frontends/amiga/menu.c b/frontends/amiga/menu.c
new file mode 100644
index 000000000..4a30c4878
--- /dev/null
+++ b/frontends/amiga/menu.c
@@ -0,0 +1,1146 @@
+/*
+ * Copyright 2008-9,2013 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "amiga/os3support.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <proto/dos.h>
+#include <proto/asl.h>
+#include <proto/exec.h>
+#include <proto/gadtools.h>
+#include <proto/intuition.h>
+#include <proto/utility.h>
+#ifdef __amigaos4__
+#include <dos/anchorpath.h>
+#include <dos/obsolete.h> /* Needed for ExAll() */
+#endif
+
+#include <libraries/gadtools.h>
+
+#include <classes/window.h>
+#include <proto/label.h>
+#include <images/label.h>
+#include <proto/bitmap.h>
+#include <images/bitmap.h>
+
+#include <reaction/reaction_macros.h>
+
+#include "utils/nsoption.h"
+#include "utils/messages.h"
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "utils/nsurl.h"
+#include "content/hlcache.h"
+#include "desktop/hotlist.h"
+#include "desktop/browser.h"
+#include "desktop/mouse.h"
+#include "desktop/gui_window.h"
+#include "desktop/textinput.h"
+#include "desktop/version.h"
+
+#include "amiga/arexx.h"
+#include "amiga/bitmap.h"
+#include "amiga/clipboard.h"
+#include "amiga/cookies.h"
+#include "amiga/file.h"
+#include "amiga/filetype.h"
+#include "amiga/gui.h"
+#include "amiga/gui_options.h"
+#include "amiga/history.h"
+#include "amiga/history_local.h"
+#include "amiga/hotlist.h"
+#include "amiga/libs.h"
+#include "amiga/menu.h"
+#include "amiga/misc.h"
+#include "amiga/print.h"
+#include "amiga/search.h"
+#include "amiga/theme.h"
+#include "amiga/tree.h"
+#include "amiga/utf8.h"
+#include "amiga/schedule.h"
+
+#define NSA_SPACE "blankspace.png"
+#define NSA_MAX_HOTLIST_MENU_LEN 100
+
+enum {
+ NSA_GLYPH_SUBMENU,
+ NSA_GLYPH_AMIGAKEY,
+ NSA_GLYPH_CHECKMARK,
+ NSA_GLYPH_MX,
+ NSA_GLYPH_MAX
+};
+
+BOOL menualreadyinit;
+const char * const netsurf_version;
+const char * const verdate;
+Object *menu_glyph[NSA_GLYPH_MAX];
+int menu_glyph_width[NSA_GLYPH_MAX];
+bool menu_glyphs_loaded = false;
+
+static nserror ami_menu_scan(struct tree *tree, struct gui_window_2 *gwin);
+void ami_menu_arexx_scan(struct gui_window_2 *gwin);
+
+/*
+ * The below functions are called automatically by window.class when menu items are selected.
+ */
+
+HOOKF(void, ami_menu_item_project_newwin, APTR, window, struct IntuiMessage *)
+{
+ nsurl *url;
+ nserror error;
+
+ error = nsurl_create(nsoption_charp(homepage_url), &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ amiga_warn_user(messages_get_errorcode(error), 0);
+ }
+}
+
+HOOKF(void, ami_menu_item_project_newtab, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+ ami_gui_new_blank_tab(gwin);
+}
+
+HOOKF(void, ami_menu_item_project_open, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ ami_file_open(gwin);
+}
+
+HOOKF(void, ami_menu_item_project_save, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+ ULONG type = (ULONG)hook->h_Data;
+
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ ami_file_save_req(type, gwin, browser_window_get_content(gwin->gw->bw));
+}
+
+HOOKF(void, ami_menu_item_project_closetab, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ browser_window_destroy(gwin->gw->bw);
+}
+
+HOOKF(void, ami_menu_item_project_closewin, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ ami_menu_window_close = gwin;
+}
+
+HOOKF(void, ami_menu_item_project_print, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ ami_set_pointer(gwin, GUI_POINTER_WAIT, false);
+ ami_print_ui(browser_window_get_content(gwin->gw->bw));
+ ami_reset_pointer(gwin);
+}
+
+HOOKF(void, ami_menu_item_project_about, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+ char *temp, *temp2;
+ int sel;
+ nsurl *url = NULL;
+ nserror error = NSERROR_OK;
+
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ ami_set_pointer(gwin, GUI_POINTER_WAIT, false);
+
+ temp = ASPrintf("%s|%s|%s", messages_get("OK"),
+ messages_get("HelpCredits"),
+ messages_get("HelpLicence"));
+
+ temp2 = ami_utf8_easy(temp);
+ FreeVec(temp);
+#ifdef __amigaos4__
+ sel = TimedDosRequesterTags(TDR_ImageType,TDRIMAGE_INFO,
+ TDR_TitleString, messages_get("NetSurf"),
+ TDR_Window, gwin->win,
+ TDR_GadgetString, temp2,
+ TDR_FormatString,"NetSurf %s\nBuild date %s\n\nhttp://www.netsurf-browser.org",
+ TDR_Arg1,netsurf_version,
+ TDR_Arg2,verdate,
+ TAG_DONE);
+#else
+ struct EasyStruct about_req = {
+ sizeof(struct EasyStruct),
+ 0,
+ "NetSurf",
+ "NetSurf %s\nBuild date %s\n\nhttp://www.netsurf-browser.org",
+ temp2,
+ };
+
+ sel = EasyRequest(gwin->win, &about_req, NULL, netsurf_version, verdate);
+#endif
+ free(temp2);
+
+ if(sel == 2) {
+ error = nsurl_create("about:credits", &url);
+ } else if(sel == 0) {
+ error = nsurl_create("about:licence", &url);
+ }
+
+ if(url) {
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ amiga_warn_user(messages_get_errorcode(error), 0);
+ }
+ }
+
+ ami_reset_pointer(gwin);
+}
+
+HOOKF(void, ami_menu_item_project_quit, APTR, window, struct IntuiMessage *)
+{
+ ami_menu_window_close = AMI_MENU_WINDOW_CLOSE_ALL;
+}
+
+HOOKF(void, ami_menu_item_edit_cut, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ browser_window_key_press(gwin->gw->bw, NS_KEY_CUT_SELECTION);
+}
+
+HOOKF(void, ami_menu_item_edit_copy, APTR, window, struct IntuiMessage *)
+{
+ struct bitmap *bm;
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ if(browser_window_can_select(gwin->gw->bw)) {
+ browser_window_key_press(gwin->gw->bw, NS_KEY_COPY_SELECTION);
+ browser_window_key_press(gwin->gw->bw, NS_KEY_CLEAR_SELECTION);
+ }
+ else if((bm = content_get_bitmap(browser_window_get_content(gwin->gw->bw)))) {
+ /** @todo It should be checked that the lifetime of
+ * the objects containing the values returned (and the
+ * constness cast away) is safe.
+ */
+ ami_bitmap_set_url(bm, browser_window_get_url(gwin->gw->bw));
+ ami_bitmap_set_title(bm, browser_window_get_title(gwin->gw->bw));
+ ami_easy_clipboard_bitmap(bm);
+ }
+#ifdef WITH_NS_SVG
+ else if(ami_mime_compare(browser_window_get_content(gwin->gw->bw), "svg") == true) {
+ ami_easy_clipboard_svg(browser_window_get_content(gwin->gw->bw));
+ }
+#endif
+}
+
+HOOKF(void, ami_menu_item_edit_paste, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ browser_window_key_press(gwin->gw->bw, NS_KEY_PASTE);
+}
+
+HOOKF(void, ami_menu_item_edit_selectall, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ browser_window_key_press(gwin->gw->bw, NS_KEY_SELECT_ALL);
+ gui_start_selection(gwin->gw);
+}
+
+HOOKF(void, ami_menu_item_edit_clearsel, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ browser_window_key_press(gwin->gw->bw, NS_KEY_CLEAR_SELECTION);
+}
+
+HOOKF(void, ami_menu_item_edit_undo, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ browser_window_key_press(gwin->gw->bw, NS_KEY_UNDO);
+}
+
+HOOKF(void, ami_menu_item_edit_redo, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ browser_window_key_press(gwin->gw->bw, NS_KEY_REDO);
+}
+
+HOOKF(void, ami_menu_item_browser_find, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ ami_search_open(gwin->gw);
+}
+
+HOOKF(void, ami_menu_item_browser_localhistory, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ ami_history_open(gwin->gw);
+}
+
+HOOKF(void, ami_menu_item_browser_globalhistory, APTR, window, struct IntuiMessage *)
+{
+ ami_tree_open(global_history_window,AMI_TREE_HISTORY);
+}
+
+HOOKF(void, ami_menu_item_browser_cookies, APTR, window, struct IntuiMessage *)
+{
+ ami_tree_open(cookies_window,AMI_TREE_COOKIES);
+}
+
+HOOKF(void, ami_menu_item_browser_foreimg, APTR, window, struct IntuiMessage *)
+{
+ struct Menu *menustrip;
+ bool checked = false;
+
+ GetAttr(WINDOW_MenuStrip, (Object *)window, (ULONG *)&menustrip);
+ if(ItemAddress(menustrip, msg->Code)->Flags & CHECKED) checked = true;
+
+ nsoption_set_bool(foreground_images, checked);
+ ami_menu_check_toggled = true;
+}
+
+HOOKF(void, ami_menu_item_browser_backimg, APTR, window, struct IntuiMessage *)
+{
+ struct Menu *menustrip;
+ bool checked = false;
+
+ GetAttr(WINDOW_MenuStrip, (Object *)window, (ULONG *)&menustrip);
+ if(ItemAddress(menustrip, msg->Code)->Flags & CHECKED) checked = true;
+
+ nsoption_set_bool(background_images, checked);
+ ami_menu_check_toggled = true;
+}
+
+HOOKF(void, ami_menu_item_browser_enablejs, APTR, window, struct IntuiMessage *)
+{
+ struct Menu *menustrip;
+ bool checked = false;
+
+ GetAttr(WINDOW_MenuStrip, (Object *)window, (ULONG *)&menustrip);
+ if(ItemAddress(menustrip, msg->Code)->Flags & CHECKED) checked = true;
+
+ nsoption_set_bool(enable_javascript, checked);
+ ami_menu_check_toggled = true;
+}
+
+HOOKF(void, ami_menu_item_browser_scale_decrease, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ ami_gui_set_scale(gwin->gw, gwin->gw->scale - 0.1);
+}
+
+HOOKF(void, ami_menu_item_browser_scale_normal, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ ami_gui_set_scale(gwin->gw, 1.0);
+}
+
+HOOKF(void, ami_menu_item_browser_scale_increase, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ ami_gui_set_scale(gwin->gw, gwin->gw->scale + 0.1);
+}
+
+HOOKF(void, ami_menu_item_browser_redraw, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ ami_schedule_redraw(gwin, true);
+ gwin->new_content = true;
+}
+
+HOOKF(void, ami_menu_item_hotlist_add, APTR, window, struct IntuiMessage *)
+{
+ struct browser_window *bw;
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ bw = gwin->gw->bw;
+
+ if (bw == NULL || browser_window_has_content(bw) == false)
+ return;
+
+ hotlist_add_url(browser_window_get_url(bw));
+ ami_gui_update_hotlist_button(gwin);
+}
+
+HOOKF(void, ami_menu_item_hotlist_show, APTR, window, struct IntuiMessage *)
+{
+ ami_tree_open(hotlist_window, AMI_TREE_HOTLIST);
+}
+
+HOOKF(void, ami_menu_item_hotlist_entries, APTR, window, struct IntuiMessage *)
+{
+ nsurl *url = hook->h_Data;
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ if(url == NULL) return;
+
+ browser_window_navigate(gwin->gw->bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+}
+
+HOOKF(void, ami_menu_item_settings_edit, APTR, window, struct IntuiMessage *)
+{
+ ami_gui_opts_open();
+}
+
+HOOKF(void, ami_menu_item_settings_snapshot, APTR, window, struct IntuiMessage *)
+{
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ nsoption_set_int(window_x, gwin->win->LeftEdge);
+ nsoption_set_int(window_y, gwin->win->TopEdge);
+ nsoption_set_int(window_width, gwin->win->Width);
+ nsoption_set_int(window_height, gwin->win->Height);
+}
+
+HOOKF(void, ami_menu_item_settings_save, APTR, window, struct IntuiMessage *)
+{
+ nsoption_write(current_user_options, NULL, NULL);
+}
+
+HOOKF(void, ami_menu_item_arexx_execute, APTR, window, struct IntuiMessage *)
+{
+ char *temp;
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ if(AslRequestTags(filereq,
+ ASLFR_Window, gwin->win,
+ ASLFR_SleepWindow, TRUE,
+ ASLFR_TitleText, messages_get("NetSurf"),
+ ASLFR_Screen, scrn,
+ ASLFR_DoSaveMode, FALSE,
+ ASLFR_InitialDrawer, nsoption_charp(arexx_dir),
+ ASLFR_InitialPattern, "#?.nsrx",
+ TAG_DONE)) {
+ if((temp = AllocVecTagList(1024, NULL))) {
+ strlcpy(temp, filereq->fr_Drawer, 1024);
+ AddPart(temp, filereq->fr_File, 1024);
+ ami_arexx_execute(temp);
+ FreeVec(temp);
+ }
+ }
+}
+
+HOOKF(void, ami_menu_item_arexx_entries, APTR, window, struct IntuiMessage *)
+{
+ char *script = hook->h_Data;
+ char *temp;
+ struct gui_window_2 *gwin;
+ GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin);
+
+ if(script) {
+ if((temp = AllocVecTagList(1024, NULL))) {
+ BPTR lock;
+ if((lock = Lock(nsoption_charp(arexx_dir), SHARED_LOCK))) {
+ DevNameFromLock(lock, temp, 1024, DN_FULLPATH);
+ AddPart(temp, script, 1024);
+ ami_arexx_execute(temp);
+ FreeVec(temp);
+ UnLock(lock);
+ }
+ }
+ }
+}
+
+
+/* menu creation code */
+
+void ami_free_menulabs(struct gui_window_2 *gwin)
+{
+ int i;
+
+ for(i=0;i<AMI_MENU_AREXX_MAX;i++) {
+ if(gwin->menulab[i] && (gwin->menulab[i] != NM_BARLABEL)) {
+ if(gwin->menutype[i] & MENU_IMAGE) {
+ if(gwin->menuobj[i]) DisposeObject(gwin->menuobj[i]);
+ }
+
+ ami_utf8_free(gwin->menulab[i]);
+
+ if(i >= AMI_MENU_AREXX) {
+ if(gwin->menu_hook[i].h_Data) free(gwin->menu_hook[i].h_Data);
+ gwin->menu_hook[i].h_Data = NULL;
+ }
+ }
+
+ gwin->menulab[i] = NULL;
+ gwin->menuobj[i] = NULL;
+ gwin->menukey[i] = 0;
+ }
+
+ FreeVec(gwin->menutype);
+ gwin->menutype = NULL;
+}
+
+static void ami_menu_alloc_item(struct gui_window_2 *gwin, int num, UBYTE type,
+ const char *label, char key, const char *icon, void *func, void *hookdata)
+{
+ char menu_icon[1024];
+
+ gwin->menutype[num] = type;
+
+ if((label == NM_BARLABEL) || (strcmp(label, "--") == 0)) {
+ gwin->menulab[num] = NM_BARLABEL;
+ } else {
+ if((num >= AMI_MENU_HOTLIST) && (num <= AMI_MENU_HOTLIST_MAX)) {
+ utf8_from_local_encoding(label,
+ (strlen(label) < NSA_MAX_HOTLIST_MENU_LEN) ? strlen(label) : NSA_MAX_HOTLIST_MENU_LEN,
+ &gwin->menulab[num]);
+ } else if((num >= AMI_MENU_AREXX) && (num < AMI_MENU_AREXX_MAX)) {
+ gwin->menulab[num] = strdup(label);
+ } else {
+ gwin->menulab[num] = ami_utf8_easy(messages_get(label));
+ }
+ }
+
+ gwin->menuicon[num] = NULL;
+ if(key) gwin->menukey[num] = key;
+ if(func) gwin->menu_hook[num].h_Entry = (HOOKFUNC)func;
+ if(hookdata) gwin->menu_hook[num].h_Data = hookdata;
+
+#ifdef __amigaos4__
+ if(LIB_IS_AT_LEAST((struct Library *)GadToolsBase, 53, 7)) {
+ if(icon) {
+ if(ami_locate_resource(menu_icon, icon) == true) {
+ gwin->menuicon[num] = (char *)strdup(menu_icon);
+ } else {
+ /* If the requested icon can't be found, put blank space in instead */
+ gwin->menuicon[num] = (char *)strdup(NSA_SPACE);
+ }
+ }
+ }
+#endif
+}
+
+static void ami_init_menulabs(struct gui_window_2 *gwin)
+{
+ int i;
+
+ gwin->menutype = ami_misc_allocvec_clear(sizeof(UBYTE) * (AMI_MENU_AREXX_MAX + 1), 0);
+
+ for(i=0;i <= AMI_MENU_AREXX_MAX;i++)
+ {
+ gwin->menutype[i] = NM_IGNORE;
+ gwin->menulab[i] = NULL;
+ gwin->menuobj[i] = NULL;
+ gwin->menuicon[i] = NULL;
+ }
+
+ ami_menu_alloc_item(gwin, M_PROJECT, NM_TITLE, "Project", 0, NULL, NULL, NULL);
+ ami_menu_alloc_item(gwin, M_NEWWIN, NM_ITEM, "NewWindowNS", 'N', "TBImages:list_app",
+ ami_menu_item_project_newwin, NULL);
+ ami_menu_alloc_item(gwin, M_NEWTAB, NM_ITEM, "NewTab", 'T', "TBImages:list_tab",
+ ami_menu_item_project_newtab, NULL);
+ ami_menu_alloc_item(gwin, M_BAR_P1, NM_ITEM, NM_BARLABEL, 0, NULL, NULL, NULL);
+ ami_menu_alloc_item(gwin, M_OPEN, NM_ITEM, "OpenFile", 'O', "TBImages:list_folder_misc",
+ ami_menu_item_project_open, NULL);
+ ami_menu_alloc_item(gwin, M_SAVEAS, NM_ITEM, "SaveAsNS", 0, "TBImages:list_saveas", NULL, NULL);
+ ami_menu_alloc_item(gwin, M_SAVESRC, NM_SUB, "Source", 'S', NULL,
+ ami_menu_item_project_save, (void *)AMINS_SAVE_SOURCE);
+ ami_menu_alloc_item(gwin, M_SAVETXT, NM_SUB, "TextNS", 0, NULL,
+ ami_menu_item_project_save, (void *)AMINS_SAVE_TEXT);
+ ami_menu_alloc_item(gwin, M_SAVECOMP, NM_SUB, "SaveCompNS", 0, NULL,
+ ami_menu_item_project_save, (void *)AMINS_SAVE_COMPLETE);
+#ifdef WITH_PDF_EXPORT
+ ami_menu_alloc_item(gwin, M_SAVEPDF, NM_SUB, "PDFNS", 0, NULL,
+ ami_menu_item_project_save, (void *)AMINS_SAVE_PDF);
+#endif
+ ami_menu_alloc_item(gwin, M_SAVEIFF, NM_SUB, "IFF", 0, NULL,
+ ami_menu_item_project_save, (void *)AMINS_SAVE_IFF);
+ ami_menu_alloc_item(gwin, M_BAR_P2, NM_ITEM, NM_BARLABEL, 0, NULL, NULL, NULL);
+ ami_menu_alloc_item(gwin, M_PRINT, NM_ITEM, "PrintNS", 'P', "TBImages:list_print",
+ ami_menu_item_project_print, NULL);
+ ami_menu_alloc_item(gwin, M_BAR_P3, NM_ITEM, NM_BARLABEL, 0, NULL, NULL, NULL);
+ ami_menu_alloc_item(gwin, M_CLOSETAB, NM_ITEM, "CloseTab", 'K', "TBImages:list_remove",
+ ami_menu_item_project_closetab, NULL);
+ ami_menu_alloc_item(gwin, M_CLOSEWIN, NM_ITEM, "CloseWindow", 0, "TBImages:list_cancel",
+ ami_menu_item_project_closewin, NULL);
+ ami_menu_alloc_item(gwin, M_BAR_P4, NM_ITEM, NM_BARLABEL, 0, NULL, NULL, NULL);
+ ami_menu_alloc_item(gwin, M_ABOUT, NM_ITEM, "About", '?', "TBImages:list_info",
+ ami_menu_item_project_about, NULL);
+ ami_menu_alloc_item(gwin, M_BAR_P5, NM_ITEM, NM_BARLABEL, 0, NULL, NULL, NULL);
+ ami_menu_alloc_item(gwin, M_QUIT, NM_ITEM, "Quit", 'Q', "TBImages:list_warning",
+ ami_menu_item_project_quit, NULL);
+
+ ami_menu_alloc_item(gwin, M_EDIT, NM_TITLE, "Edit", 0, NULL, NULL, NULL);
+ ami_menu_alloc_item(gwin, M_CUT, NM_ITEM, "CutNS", 'X', "TBImages:list_cut",
+ ami_menu_item_edit_cut, NULL);
+ ami_menu_alloc_item(gwin, M_COPY, NM_ITEM, "CopyNS", 'C', "TBImages:list_copy",
+ ami_menu_item_edit_copy, NULL);
+ ami_menu_alloc_item(gwin, M_PASTE, NM_ITEM, "PasteNS", 'V', "TBImages:list_paste",
+ ami_menu_item_edit_paste, NULL);
+ ami_menu_alloc_item(gwin, M_BAR_E1, NM_ITEM, NM_BARLABEL, 0, NULL, NULL, NULL);
+ ami_menu_alloc_item(gwin, M_SELALL, NM_ITEM, "SelectAllNS", 'A', NSA_SPACE,
+ ami_menu_item_edit_selectall, NULL);
+ ami_menu_alloc_item(gwin, M_CLEAR, NM_ITEM, "ClearNS", 0, NSA_SPACE,
+ ami_menu_item_edit_clearsel, NULL);
+ ami_menu_alloc_item(gwin, M_BAR_E2, NM_ITEM, NM_BARLABEL, 0, NULL, NULL, NULL);
+ ami_menu_alloc_item(gwin, M_UNDO, NM_ITEM, "Undo", 'Z', "TBImages:list_undo",
+ ami_menu_item_edit_undo, NULL);
+ ami_menu_alloc_item(gwin, M_REDO, NM_ITEM, "Redo", 'Y', "TBImages:list_redo",
+ ami_menu_item_edit_redo, NULL);
+
+ ami_menu_alloc_item(gwin, M_BROWSER, NM_TITLE, "Browser", 0, NULL, NULL, NULL);
+ ami_menu_alloc_item(gwin, M_FIND, NM_ITEM, "FindTextNS", 'F', "TBImages:list_search",
+ ami_menu_item_browser_find, NULL);
+ ami_menu_alloc_item(gwin, M_BAR_B1, NM_ITEM, NM_BARLABEL, 0, NULL, NULL, NULL);
+ ami_menu_alloc_item(gwin, M_HISTLOCL, NM_ITEM, "HistLocalNS", 0, "TBImages:list_history",
+ ami_menu_item_browser_localhistory, NULL);
+ ami_menu_alloc_item(gwin, M_HISTGLBL, NM_ITEM, "HistGlobalNS", 0, "TBImages:list_history",
+ ami_menu_item_browser_globalhistory, NULL);
+ ami_menu_alloc_item(gwin, M_BAR_B2, NM_ITEM, NM_BARLABEL, 0, NULL, NULL, NULL);
+ ami_menu_alloc_item(gwin, M_COOKIES, NM_ITEM, "ShowCookiesNS", 0, "TBImages:list_internet",
+ ami_menu_item_browser_cookies, NULL);
+ ami_menu_alloc_item(gwin, M_BAR_B3, NM_ITEM, NM_BARLABEL, 0, NULL, NULL, NULL);
+ ami_menu_alloc_item(gwin, M_SCALE, NM_ITEM, "ScaleNS", 0, "TBImages:list_preview", NULL, NULL);
+ ami_menu_alloc_item(gwin, M_SCALEDEC, NM_SUB, "ScaleDec", '-', "TBImages:list_zoom_out",
+ ami_menu_item_browser_scale_decrease, NULL);
+ ami_menu_alloc_item(gwin, M_SCALENRM, NM_SUB, "ScaleNorm", '=', "TBImages:list_zoom_100",
+ ami_menu_item_browser_scale_normal, NULL);
+ ami_menu_alloc_item(gwin, M_SCALEINC, NM_SUB, "ScaleInc", '+', "TBImages:list_zoom_in",
+ ami_menu_item_browser_scale_increase, NULL);
+ ami_menu_alloc_item(gwin, M_IMAGES, NM_ITEM, "Images", 0, "TBImages:list_image", NULL, NULL);
+ ami_menu_alloc_item(gwin, M_IMGFORE, NM_SUB, "ForeImg", 0, NULL,
+ ami_menu_item_browser_foreimg, NULL);
+ ami_menu_alloc_item(gwin, M_IMGBACK, NM_SUB, "BackImg", 0, NULL,
+ ami_menu_item_browser_backimg, NULL);
+ ami_menu_alloc_item(gwin, M_JS, NM_ITEM, "EnableJS", 0, NULL,
+ ami_menu_item_browser_enablejs, NULL);
+ ami_menu_alloc_item(gwin, M_BAR_B4, NM_ITEM, NM_BARLABEL, 0, NULL, NULL, NULL);
+ ami_menu_alloc_item(gwin, M_REDRAW, NM_ITEM, "Redraw", 0, "TBImages:list_wand",
+ ami_menu_item_browser_redraw, NULL);
+
+ ami_menu_alloc_item(gwin, M_HOTLIST, NM_TITLE, "Hotlist", 0, NULL, NULL, NULL);
+ ami_menu_alloc_item(gwin, M_HLADD, NM_ITEM, "HotlistAdd", 'B', "TBImages:list_favouriteadd",
+ ami_menu_item_hotlist_add, NULL);
+ ami_menu_alloc_item(gwin, M_HLSHOW, NM_ITEM,"HotlistShowNS",'H', "TBImages:list_favourite",
+ ami_menu_item_hotlist_show, NULL);
+ ami_menu_alloc_item(gwin, M_BAR_H1, NM_ITEM, NM_BARLABEL, 0, NULL, NULL, NULL);
+
+ ami_menu_alloc_item(gwin, M_PREFS, NM_TITLE, "Settings", 0, NULL, NULL, NULL);
+ ami_menu_alloc_item(gwin, M_PREDIT, NM_ITEM, "SettingsEdit", 0, "TBImages:list_prefs",
+ ami_menu_item_settings_edit, NULL);
+ ami_menu_alloc_item(gwin, M_BAR_S1, NM_ITEM, NM_BARLABEL, 0, NULL, NULL, NULL);
+ ami_menu_alloc_item(gwin, M_SNAPSHOT, NM_ITEM, "SnapshotWindow",0, "TBImages:list_hold",
+ ami_menu_item_settings_snapshot, NULL);
+ ami_menu_alloc_item(gwin, M_PRSAVE, NM_ITEM, "SettingsSave", 0, "TBImages:list_use",
+ ami_menu_item_settings_save, NULL);
+
+ ami_menu_alloc_item(gwin, M_AREXX, NM_TITLE, "ARexx", 0, NULL, NULL, NULL);
+ ami_menu_alloc_item(gwin, M_AREXXEX, NM_ITEM, "ARexxExecute",'E', "TBImages:list_arexx",
+ ami_menu_item_arexx_execute, NULL);
+ ami_menu_alloc_item(gwin, M_BAR_A1, NM_ITEM, NM_BARLABEL, 0, NULL, NULL, NULL);
+ gwin->menutype[AMI_MENU_AREXX_MAX] = NM_END;
+}
+
+/* Menu refresh for hotlist */
+void ami_menu_refresh(struct gui_window_2 *gwin)
+{
+ return; /**\todo fix this after migrating to menuclass */
+
+ struct Menu *menu;
+
+ LOG("Clearing MenuStrip");
+ SetAttrs(gwin->objects[OID_MAIN],
+ WINDOW_MenuStrip, NULL,
+ TAG_DONE);
+
+ LOG("Freeing menu");
+ ami_menu_free(gwin);
+
+ LOG("Freeing menu labels");
+ ami_free_menulabs(gwin);
+
+ LOG("Creating new menu");
+ menu = ami_menu_create(gwin);
+
+ LOG("Attaching MenuStrip %p to %p", menu, gwin->objects[OID_MAIN]);
+ SetAttrs(gwin->objects[OID_MAIN],
+ WINDOW_MenuStrip, menu,
+ TAG_DONE);
+}
+
+static void ami_menu_load_glyphs(struct DrawInfo *dri)
+{
+#ifdef __amigaos4__
+ if(LIB_IS_AT_LEAST((struct Library *)GadToolsBase, 53, 7)) {
+ for(int i = 0; i < NSA_GLYPH_MAX; i++)
+ menu_glyph[i] = NULL;
+
+ menu_glyph[NSA_GLYPH_SUBMENU] = NewObject(NULL, "sysiclass",
+ SYSIA_Which, MENUSUB,
+ SYSIA_DrawInfo, dri,
+ TAG_DONE);
+ menu_glyph[NSA_GLYPH_AMIGAKEY] = NewObject(NULL, "sysiclass",
+ SYSIA_Which, AMIGAKEY,
+ SYSIA_DrawInfo, dri,
+ TAG_DONE);
+ GetAttr(IA_Width, menu_glyph[NSA_GLYPH_SUBMENU],
+ (ULONG *)&menu_glyph_width[NSA_GLYPH_SUBMENU]);
+ GetAttr(IA_Width, menu_glyph[NSA_GLYPH_AMIGAKEY],
+ (ULONG *)&menu_glyph_width[NSA_GLYPH_AMIGAKEY]);
+
+ menu_glyphs_loaded = true;
+ }
+#endif
+}
+
+void ami_menu_free_glyphs(void)
+{
+#ifdef __amigaos4__
+ if(LIB_IS_AT_LEAST((struct Library *)GadToolsBase, 53, 7)) {
+ int i;
+ if(menu_glyphs_loaded == false) return;
+
+ for(i = 0; i < NSA_GLYPH_MAX; i++) {
+ if(menu_glyph[i]) DisposeObject(menu_glyph[i]);
+ menu_glyph[i] = NULL;
+ };
+
+ menu_glyphs_loaded = false;
+ }
+#endif
+}
+
+static int ami_menu_calc_item_width(struct gui_window_2 *gwin, int j, struct RastPort *rp)
+{
+ int space_width = TextLength(rp, " ", 1);
+ int item_size;
+
+ item_size = TextLength(rp, gwin->menulab[j], strlen(gwin->menulab[j]));
+ item_size += space_width;
+
+ if(gwin->menukey[j]) {
+ item_size += TextLength(rp, &gwin->menukey[j], 1);
+ item_size += menu_glyph_width[NSA_GLYPH_AMIGAKEY];
+ /**TODO: take account of the size of other imagery too
+ */
+ } else {
+ /* assume worst case - it doesn't really matter if we make menus wider */
+ item_size += TextLength(rp, "M", 1);
+ item_size += menu_glyph_width[NSA_GLYPH_AMIGAKEY];
+ }
+
+ if(gwin->menuicon[j]) {
+ item_size += 16;
+ }
+
+ return item_size;
+}
+
+
+static struct gui_window_2 *ami_menu_layout(struct gui_window_2 *gwin)
+{
+ int i, j;
+ int txtlen = 0;
+ int left_posn = 0;
+ struct RastPort *rp = &scrn->RastPort;
+ struct DrawInfo *dri = GetScreenDrawInfo(scrn);
+ int space_width = TextLength(rp, " ", 1);
+
+ if(menu_glyphs_loaded == false)
+ ami_menu_load_glyphs(dri);
+
+ for(i=0; i < AMI_MENU_AREXX_MAX; i++)
+ {
+ if(gwin->menutype[i] == NM_TITLE) {
+ j = i + 1;
+ txtlen = 0;
+ do {
+ if(gwin->menulab[j] != NM_BARLABEL) {
+ if(gwin->menutype[j] == NM_ITEM) {
+ int item_size = ami_menu_calc_item_width(gwin, j, rp);
+ if(item_size > txtlen) {
+ txtlen = item_size;
+ }
+ }
+ }
+ j++;
+ } while((gwin->menutype[j] != NM_TITLE) && (gwin->menutype[j] != 0));
+ }
+#ifdef __amigaos4__
+ if(LIB_IS_AT_LEAST((struct Library *)GadToolsBase, 53, 7)) {
+ /* GadTools 53.7+ only. For now we will only create the menu
+ using label.image if there's a bitmap associated with the item. */
+ if((gwin->menuicon[i] != NULL) && (gwin->menulab[i] != NM_BARLABEL)) {
+ int icon_width = 0;
+ Object *submenuarrow = NULL;
+ Object *icon = BitMapObj,
+ IA_Scalable, TRUE,
+ BITMAP_Screen, scrn,
+ BITMAP_SourceFile, gwin->menuicon[i],
+ BITMAP_Masking, TRUE,
+ BitMapEnd;
+
+ /* \todo make this scale the bitmap to these dimensions */
+ SetAttrs(icon,
+ BITMAP_Width, 16,
+ BITMAP_Height, 16,
+ TAG_DONE);
+
+ GetAttr(IA_Width, icon, (ULONG *)&icon_width);
+
+ if(gwin->menutype[i] != NM_SUB) {
+ left_posn = txtlen;
+ }
+
+ left_posn = left_posn -
+ TextLength(rp, gwin->menulab[i], strlen(gwin->menulab[i])) -
+ icon_width - space_width;
+
+ if((gwin->menutype[i] == NM_ITEM) && (gwin->menutype[i+1] == NM_SUB)) {
+ left_posn -= menu_glyph_width[NSA_GLYPH_SUBMENU];
+
+ submenuarrow = NewObject(NULL, "sysiclass",
+ SYSIA_Which, MENUSUB,
+ SYSIA_DrawInfo, dri,
+ IA_Left, left_posn,
+ TAG_DONE);
+ }
+
+ gwin->menuobj[i] = LabelObj,
+ LABEL_MenuMode, TRUE,
+ LABEL_DrawInfo, dri,
+ LABEL_DisposeImage, TRUE,
+ LABEL_Image, icon,
+ LABEL_Text, " ",
+ LABEL_Text, gwin->menulab[i],
+ LABEL_DisposeImage, TRUE,
+ LABEL_Image, submenuarrow,
+ LabelEnd;
+
+ if(gwin->menuobj[i]) gwin->menutype[i] |= MENU_IMAGE;
+ }
+ }
+#endif
+ gwin->menu[i].nm_Type = gwin->menutype[i];
+
+ if(gwin->menuobj[i])
+ gwin->menu[i].nm_Label = (void *)gwin->menuobj[i];
+ else
+ gwin->menu[i].nm_Label = gwin->menulab[i];
+
+ if(gwin->menukey[i]) gwin->menu[i].nm_CommKey = &gwin->menukey[i];
+ gwin->menu[i].nm_Flags = 0;
+ if(gwin->menu_hook[i].h_Entry) gwin->menu[i].nm_UserData = &gwin->menu_hook[i];
+
+ if(gwin->menuicon[i]) {
+ free(gwin->menuicon[i]);
+ gwin->menuicon[i] = NULL;
+ }
+ }
+
+ FreeScreenDrawInfo(scrn, dri);
+
+ return gwin;
+}
+
+void ami_menu_free(struct gui_window_2 *gwin)
+{
+ FreeMenus(gwin->imenu);
+ FreeVisualInfo(gwin->vi);
+}
+
+struct Menu *ami_menu_create(struct gui_window_2 *gwin)
+{
+ gwin->menu = ami_misc_allocvec_clear(sizeof(struct NewMenu) * (AMI_MENU_AREXX_MAX + 1), 0);
+ ami_init_menulabs(gwin);
+ ami_menu_scan(ami_tree_get_tree(hotlist_window), gwin);
+ ami_menu_arexx_scan(gwin);
+ gwin = ami_menu_layout(gwin);
+
+ gwin->menu[M_JS].nm_Flags = CHECKIT | MENUTOGGLE;
+ if(nsoption_bool(enable_javascript) == true)
+ gwin->menu[M_JS].nm_Flags |= CHECKED;
+
+ gwin->menu[M_PRINT].nm_Flags = NM_ITEMDISABLED;
+
+ gwin->menu[M_IMGFORE].nm_Flags = CHECKIT | MENUTOGGLE;
+ if(nsoption_bool(foreground_images) == true)
+ gwin->menu[M_IMGFORE].nm_Flags |= CHECKED;
+ gwin->menu[M_IMGBACK].nm_Flags = CHECKIT | MENUTOGGLE;
+ if(nsoption_bool(background_images) == true)
+ gwin->menu[M_IMGBACK].nm_Flags |= CHECKED;
+
+ gwin->vi = GetVisualInfo(scrn, TAG_DONE);
+ gwin->imenu = CreateMenus(gwin->menu, TAG_DONE);
+ LayoutMenus(gwin->imenu, gwin->vi,
+ GTMN_NewLookMenus, TRUE, TAG_DONE);
+ FreeVec(gwin->menu); /**\todo this should be local to this function */
+ gwin->menu = NULL;
+
+ return gwin->imenu;
+}
+
+void ami_menu_arexx_scan(struct gui_window_2 *gwin)
+{
+ /**\todo Rewrite this to not use ExAll() **/
+ int item = AMI_MENU_AREXX;
+ BPTR lock = 0;
+ UBYTE *buffer;
+ struct ExAllControl *ctrl;
+ char matchpatt[16];
+ LONG cont;
+ struct ExAllData *ead;
+ char *menu_lab;
+
+ if((lock = Lock(nsoption_charp(arexx_dir), SHARED_LOCK))) {
+ if((buffer = AllocVecTagList(1024, NULL))) {
+ if((ctrl = AllocDosObject(DOS_EXALLCONTROL,NULL))) {
+ ctrl->eac_LastKey = 0;
+
+ if(ParsePatternNoCase("#?.nsrx",(char *)&matchpatt,16) != -1) {
+ ctrl->eac_MatchString = (char *)&matchpatt;
+ }
+
+ do {
+ cont = ExAll(lock,(struct ExAllData *)buffer,1024,ED_COMMENT,ctrl);
+ if((!cont) && (IoErr() != ERROR_NO_MORE_ENTRIES)) break;
+ if(!ctrl->eac_Entries) continue;
+
+ for(ead = (struct ExAllData *)buffer; ead; ead = ead->ed_Next) {
+ if(item >= AMI_MENU_AREXX_MAX) continue;
+ if(EAD_IS_FILE(ead)) {
+ gwin->menu[item].nm_Type = NM_ITEM;
+ if(ead->ed_Comment[0] != '\0')
+ menu_lab = ead->ed_Comment;
+ else
+ menu_lab = ead->ed_Name;
+
+ ami_menu_alloc_item(gwin, item, NM_ITEM, menu_lab, 0, NSA_SPACE,
+ ami_menu_item_arexx_entries, (void *)strdup(ead->ed_Name));
+
+ item++;
+ }
+ }
+ } while(cont);
+ FreeDosObject(DOS_EXALLCONTROL,ctrl);
+ }
+ FreeVec(buffer);
+ }
+ UnLock(lock);
+ }
+
+ gwin->menu[item].nm_Type = NM_END;
+ gwin->menu[item].nm_Label = NULL;
+}
+
+static bool ami_menu_hotlist_add(void *userdata, int level, int item, const char *title, nsurl *url, bool is_folder)
+{
+ UBYTE type;
+ STRPTR icon;
+ struct gui_window_2 *gw = (struct gui_window_2 *)userdata;
+
+ if(item >= AMI_MENU_HOTLIST_MAX) return false;
+
+ switch(level) {
+ case 1:
+ type = NM_ITEM;
+ break;
+ case 2:
+ type = NM_SUB;
+ break;
+ default:
+ /* entries not at level 1 or 2 are not able to be added
+ * \todo construct menus using menuclass instead! */
+ return false;
+ break;
+ }
+
+ if(is_folder == true) {
+ icon = ASPrintf("icons/directory.png");
+ } else {
+ icon = ami_gui_get_cache_favicon_name(url, true);
+ if (icon == NULL) icon = ASPrintf("icons/content.png");
+ }
+
+ ami_menu_alloc_item(gw, item, type, title,
+ 0, icon, ami_menu_item_hotlist_entries, (void *)url);
+ if((is_folder == true) && (type == NM_SUB))
+ gw->menu[item].nm_Flags = NM_ITEMDISABLED;
+
+ if(icon) FreeVec(icon);
+
+ return true;
+}
+
+static nserror ami_menu_scan(struct tree *tree, struct gui_window_2 *gwin)
+{
+ return ami_hotlist_scan((void *)gwin, AMI_MENU_HOTLIST, messages_get("HotlistMenu"), ami_menu_hotlist_add);
+}
+
+void ami_menu_update_checked(struct gui_window_2 *gwin)
+{
+ struct Menu *menustrip;
+
+ GetAttr(WINDOW_MenuStrip, gwin->objects[OID_MAIN], (ULONG *)&menustrip);
+ if(!menustrip) return;
+ if(nsoption_bool(enable_javascript) == true) {
+ if((ItemAddress(menustrip, AMI_MENU_JS)->Flags & CHECKED) == 0)
+ ItemAddress(menustrip, AMI_MENU_JS)->Flags ^= CHECKED;
+ } else {
+ if(ItemAddress(menustrip, AMI_MENU_JS)->Flags & CHECKED)
+ ItemAddress(menustrip, AMI_MENU_JS)->Flags ^= CHECKED;
+ }
+ if(nsoption_bool(foreground_images) == true) {
+ if((ItemAddress(menustrip, AMI_MENU_FOREIMG)->Flags & CHECKED) == 0)
+ ItemAddress(menustrip, AMI_MENU_FOREIMG)->Flags ^= CHECKED;
+ } else {
+ if(ItemAddress(menustrip, AMI_MENU_FOREIMG)->Flags & CHECKED)
+ ItemAddress(menustrip, AMI_MENU_FOREIMG)->Flags ^= CHECKED;
+ }
+
+ if(nsoption_bool(background_images) == true) {
+ if((ItemAddress(menustrip, AMI_MENU_BACKIMG)->Flags & CHECKED) == 0)
+ ItemAddress(menustrip, AMI_MENU_BACKIMG)->Flags ^= CHECKED;
+ } else {
+ if(ItemAddress(menustrip, AMI_MENU_BACKIMG)->Flags & CHECKED)
+ ItemAddress(menustrip, AMI_MENU_BACKIMG)->Flags ^= CHECKED;
+ }
+
+ ResetMenuStrip(gwin->win, menustrip);
+}
+
+void ami_menu_update_disabled(struct gui_window *g, hlcache_handle *c)
+{
+ struct Window *win = g->shared->win;
+
+ if(nsoption_bool(kiosk_mode) == true) return;
+
+ if(content_get_type(c) <= CONTENT_CSS)
+ {
+ OnMenu(win,AMI_MENU_SAVEAS_TEXT);
+ OnMenu(win,AMI_MENU_SAVEAS_COMPLETE);
+#ifdef WITH_PDF_EXPORT
+ OnMenu(win,AMI_MENU_SAVEAS_PDF);
+#endif
+#if 0
+ if(browser_window_get_editor_flags(g->bw) & BW_EDITOR_CAN_COPY) {
+ OnMenu(win,AMI_MENU_COPY);
+ OnMenu(win,AMI_MENU_CLEAR);
+ } else {
+ OffMenu(win,AMI_MENU_COPY);
+ OffMenu(win,AMI_MENU_CLEAR);
+ }
+
+ if(browser_window_get_editor_flags(g->bw) & BW_EDITOR_CAN_CUT)
+ OnMenu(win,AMI_MENU_CUT);
+ else
+ OffMenu(win,AMI_MENU_CUT);
+
+ if(browser_window_get_editor_flags(g->bw) & BW_EDITOR_CAN_PASTE)
+ OnMenu(win,AMI_MENU_PASTE);
+ else
+ OffMenu(win,AMI_MENU_PASTE);
+#else
+ OnMenu(win,AMI_MENU_CUT);
+ OnMenu(win,AMI_MENU_COPY);
+ OnMenu(win,AMI_MENU_PASTE);
+ OnMenu(win,AMI_MENU_CLEAR);
+#endif
+ OnMenu(win,AMI_MENU_SELECTALL);
+ OnMenu(win,AMI_MENU_FIND);
+ OffMenu(win,AMI_MENU_SAVEAS_IFF);
+ }
+ else
+ {
+ OffMenu(win,AMI_MENU_CUT);
+ OffMenu(win,AMI_MENU_PASTE);
+ OffMenu(win,AMI_MENU_CLEAR);
+
+ OffMenu(win,AMI_MENU_SAVEAS_TEXT);
+ OffMenu(win,AMI_MENU_SAVEAS_COMPLETE);
+#ifdef WITH_PDF_EXPORT
+ OffMenu(win,AMI_MENU_SAVEAS_PDF);
+#endif
+ OffMenu(win,AMI_MENU_SELECTALL);
+ OffMenu(win,AMI_MENU_FIND);
+
+#ifdef WITH_NS_SVG
+ if(content_get_bitmap(c) || (ami_mime_compare(c, "svg") == true))
+#else
+ if(content_get_bitmap(c))
+#endif
+ {
+ OnMenu(win,AMI_MENU_COPY);
+ OnMenu(win,AMI_MENU_SAVEAS_IFF);
+ }
+ else
+ {
+ OffMenu(win,AMI_MENU_COPY);
+ OffMenu(win,AMI_MENU_SAVEAS_IFF);
+ }
+ }
+}
+
diff --git a/frontends/amiga/menu.h b/frontends/amiga/menu.h
new file mode 100755
index 000000000..3bec113d0
--- /dev/null
+++ b/frontends/amiga/menu.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2008,2009,2013 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_MENU_H
+#define AMIGA_MENU_H
+
+#include <exec/types.h>
+#include <intuition/intuition.h>
+#include <libraries/gadtools.h>
+
+struct hlcache_handle;
+
+/** Maximum number of hotlist items (somewhat arbitrary value) */
+#define AMI_HOTLIST_ITEMS 60
+
+/** Maximum number of ARexx menu items (somewhat arbitrary value) */
+#define AMI_MENU_AREXX_ITEMS 20
+
+/** enum menu structure, has to be here as we need it below. */
+enum {
+ /* Project menu */
+ M_PROJECT = 0,
+ M_NEWWIN,
+ M_NEWTAB,
+ M_BAR_P1,
+ M_OPEN,
+ M_SAVEAS,
+ M_SAVESRC,
+ M_SAVETXT,
+ M_SAVECOMP,
+ M_SAVEIFF,
+#ifdef WITH_PDF_EXPORT
+ M_SAVEPDF,
+#endif
+ M_BAR_P2,
+ M_PRINT,
+ M_BAR_P3,
+ M_CLOSETAB,
+ M_CLOSEWIN,
+ M_BAR_P4,
+ M_ABOUT,
+ M_BAR_P5,
+ M_QUIT,
+ /* Edit menu */
+ M_EDIT,
+ M_CUT,
+ M_COPY,
+ M_PASTE,
+ M_BAR_E1,
+ M_SELALL,
+ M_CLEAR,
+ M_BAR_E2,
+ M_UNDO,
+ M_REDO,
+ /* Browser menu */
+ M_BROWSER,
+ M_FIND,
+ M_BAR_B1,
+ M_HISTLOCL,
+ M_HISTGLBL,
+ M_BAR_B2,
+ M_COOKIES,
+ M_BAR_B3,
+ M_SCALE,
+ M_SCALEDEC,
+ M_SCALENRM,
+ M_SCALEINC,
+ M_IMAGES,
+ M_IMGFORE,
+ M_IMGBACK,
+ M_JS,
+ M_BAR_B4,
+ M_REDRAW,
+ /* Hotlist menu */
+ M_HOTLIST,
+ M_HLADD,
+ M_HLSHOW,
+ M_BAR_H1, // 47
+ AMI_MENU_HOTLIST, /* Where the hotlist entries start */
+ AMI_MENU_HOTLIST_MAX = AMI_MENU_HOTLIST + AMI_HOTLIST_ITEMS,
+ /* Settings menu */
+ M_PREFS,
+ M_PREDIT,
+ M_BAR_S1,
+ M_SNAPSHOT,
+ M_PRSAVE,
+ /* ARexx menu */
+ M_AREXX,
+ M_AREXXEX,
+ M_BAR_A1,
+ AMI_MENU_AREXX,
+ AMI_MENU_AREXX_MAX = AMI_MENU_AREXX + AMI_MENU_AREXX_ITEMS
+};
+
+/* We can get away with AMI_MENU_MAX falling short as it is
+ * only used for freeing the UTF-8 converted menu labels */
+#define AMI_MENU_MAX AMI_MENU_AREXX
+
+/* The Intuition menu numbers of some menus we might need to modify */
+#define AMI_MENU_SAVEAS_TEXT FULLMENUNUM(0,4,1)
+#define AMI_MENU_SAVEAS_COMPLETE FULLMENUNUM(0,4,2)
+#define AMI_MENU_SAVEAS_IFF FULLMENUNUM(0,4,3)
+#define AMI_MENU_SAVEAS_PDF FULLMENUNUM(0,4,4)
+#define AMI_MENU_CLOSETAB FULLMENUNUM(0,8,0)
+#define AMI_MENU_CUT FULLMENUNUM(1,0,0)
+#define AMI_MENU_COPY FULLMENUNUM(1,1,0)
+#define AMI_MENU_PASTE FULLMENUNUM(1,2,0)
+#define AMI_MENU_SELECTALL FULLMENUNUM(1,4,0)
+#define AMI_MENU_CLEAR FULLMENUNUM(1,5,0)
+#define AMI_MENU_UNDO FULLMENUNUM(1,8,0)
+#define AMI_MENU_REDO FULLMENUNUM(1,9,0)
+#define AMI_MENU_FIND FULLMENUNUM(2,0,0)
+#define AMI_MENU_FOREIMG FULLMENUNUM(2,8,0)
+#define AMI_MENU_BACKIMG FULLMENUNUM(2,8,1)
+#define AMI_MENU_JS FULLMENUNUM(2,9,0)
+
+/** A special value for ami_menu_window_close */
+#define AMI_MENU_WINDOW_CLOSE_ALL (void *)1
+
+struct gui_window;
+struct gui_window_2;
+
+struct gui_window_2 *ami_menu_window_close;
+bool ami_menu_check_toggled;
+
+void ami_free_menulabs(struct gui_window_2 *gwin);
+struct Menu *ami_menu_create(struct gui_window_2 *gwin);
+void ami_menu_refresh(struct gui_window_2 *gwin);
+void ami_menu_update_checked(struct gui_window_2 *gwin);
+void ami_menu_update_disabled(struct gui_window *g, struct hlcache_handle *c);
+void ami_menu_free_glyphs(void);
+void ami_menu_free(struct gui_window_2 *gwin);
+
+#endif
+
diff --git a/frontends/amiga/misc.c b/frontends/amiga/misc.c
new file mode 100755
index 000000000..2c233584d
--- /dev/null
+++ b/frontends/amiga/misc.c
@@ -0,0 +1,506 @@
+/*
+ * Copyright 2008-2010 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <proto/dos.h>
+#include <proto/exec.h>
+#include <proto/utility.h>
+
+#ifndef __amigaos4__
+#include <proto/intuition.h> // for EasyRequest
+#endif
+
+#include "utils/utils.h"
+#include "utils/corestrings.h"
+#include "utils/log.h"
+#include "utils/file.h"
+#include "utils/messages.h"
+#include "utils/nsurl.h"
+#include "utils/url.h"
+
+#include "desktop/gui_window.h"
+
+#include "amiga/gui.h"
+#include "amiga/misc.h"
+#include "amiga/utf8.h"
+
+void *ami_misc_allocvec_clear(int size, UBYTE value)
+{
+#ifdef __amigaos4__
+ return AllocVecTags(size, AVT_ClearWithValue, value, TAG_DONE);
+#else
+ void *mem = AllocVec(size, MEMF_ANY);
+ if (mem) memset(mem, value, size);
+ return mem;
+#endif
+}
+
+APTR ami_misc_itempool_create(int size)
+{
+#ifdef __amigaos4__
+ return AllocSysObjectTags(ASOT_ITEMPOOL,
+ ASOITEM_MFlags, MEMF_PRIVATE,
+ ASOITEM_ItemSize, size,
+ ASOITEM_GCPolicy, ITEMGC_AFTERCOUNT,
+ ASOITEM_GCParameter, 100,
+ TAG_DONE);
+#else
+ return CreatePool(MEMF_ANY, 20 * size, size);
+#endif
+}
+
+void ami_misc_itempool_delete(APTR pool)
+{
+#ifdef __amigaos4__
+ FreeSysObject(ASOT_ITEMPOOL, pool);
+#else
+ DeletePool(pool);
+#endif
+}
+
+APTR ami_misc_itempool_alloc(APTR pool, int size)
+{
+#ifdef __amigaos4__
+ return ItemPoolAlloc(pool);
+#else
+ return AllocPooled(pool, size);
+#endif
+}
+
+void ami_misc_itempool_free(APTR pool, APTR item, int size)
+{
+#ifdef __amigaos4__
+ ItemPoolFree(pool, item);
+#else
+ FreePooled(pool, item, size);
+#endif
+}
+
+static LONG ami_misc_req(const char *message, uint32 type)
+{
+ LONG ret = 0;
+
+ LOG("%s", message);
+#ifdef __amigaos4__
+ ret = TimedDosRequesterTags(
+ TDR_TitleString, messages_get("NetSurf"),
+ TDR_FormatString, message,
+ TDR_GadgetString, messages_get("OK"),
+ TDR_ImageType, type,
+ TDR_Window, cur_gw ? cur_gw->shared->win : NULL,
+ TAG_DONE);
+#else
+ struct EasyStruct easyreq = {
+ sizeof(struct EasyStruct),
+ 0,
+ messages_get("NetSurf"),
+ message,
+ messages_get("OK"),
+ };
+
+ ret = EasyRequest(cur_gw ? cur_gw->shared->win : NULL, &easyreq, NULL);
+#endif
+ return ret;
+}
+
+void ami_misc_fatal_error(const char *message)
+{
+ ami_misc_req(message, TDRIMAGE_ERROR);
+}
+
+/* exported interface documented in amiga/misc.h */
+nserror amiga_warn_user(const char *warning, const char *detail)
+{
+ char *utf8warning = ami_utf8_easy(messages_get(warning));
+ STRPTR bodytext = ASPrintf("\33b%s\33n\n%s",
+ utf8warning != NULL ? utf8warning : warning, detail);
+
+ ami_misc_req(bodytext, TDRIMAGE_WARNING);
+
+ if(bodytext) FreeVec(bodytext);
+ if(utf8warning) free(utf8warning);
+
+ return NSERROR_OK;
+}
+
+int32 amiga_warn_user_multi(const char *body, const char *opt1, const char *opt2, struct Window *win)
+{
+ int res = 0;
+
+ char *utf8text = ami_utf8_easy(body);
+ char *utf8gadget1 = ami_utf8_easy(messages_get(opt1));
+ char *utf8gadget2 = ami_utf8_easy(messages_get(opt2));
+ char *utf8gadgets = ASPrintf("%s|%s", utf8gadget1, utf8gadget2);
+ free(utf8gadget1);
+ free(utf8gadget2);
+
+#ifdef __amigaos4__
+ res = TimedDosRequesterTags(TDR_ImageType, TDRIMAGE_WARNING,
+ TDR_TitleString, messages_get("NetSurf"),
+ TDR_FormatString, utf8text,
+ TDR_GadgetString, utf8gadgets,
+ TDR_Window, win,
+ TAG_DONE);
+#else
+ struct EasyStruct easyreq = {
+ sizeof(struct EasyStruct),
+ 0,
+ messages_get("NetSurf"),
+ utf8text,
+ utf8gadgets,
+ };
+
+ res = EasyRequest(win, &easyreq, NULL);
+#endif
+
+ if(utf8text) free(utf8text);
+ if(utf8gadgets) FreeVec(utf8gadgets);
+
+ return res;
+}
+
+/**
+ * Create a path from a nsurl using amiga file handling.
+ *
+ * @param[in] url The url to encode.
+ * @param[out] path_out A string containing the result path which should
+ * be freed by the caller.
+ * @return NSERROR_OK and the path is written to \a path or error code
+ * on faliure.
+ */
+static nserror amiga_nsurl_to_path(struct nsurl *url, char **path_out)
+{
+ lwc_string *urlpath;
+ char *path;
+ bool match;
+ lwc_string *scheme;
+ nserror res;
+ char *colon;
+ char *slash;
+
+ if ((url == NULL) || (path_out == NULL)) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ scheme = nsurl_get_component(url, NSURL_SCHEME);
+
+ if (lwc_string_caseless_isequal(scheme, corestring_lwc_file,
+ &match) != lwc_error_ok)
+ {
+ return NSERROR_BAD_PARAMETER;
+ }
+ lwc_string_unref(scheme);
+ if (match == false) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ urlpath = nsurl_get_component(url, NSURL_PATH);
+ if (urlpath == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ res = url_unescape(lwc_string_data(urlpath) + 1, &path);
+ lwc_string_unref(urlpath);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ colon = strchr(path, ':');
+ if(colon == NULL)
+ {
+ slash = strchr(path, '/');
+ if(slash)
+ {
+ *slash = ':';
+ }
+ else
+ {
+ int len = strlen(path);
+ path[len] = ':';
+ path[len + 1] = '\0';
+ }
+ }
+
+ *path_out = path;
+
+ return NSERROR_OK;
+}
+
+/**
+ * Create a nsurl from a path using amiga file handling.
+ *
+ * Perform the necessary operations on a path to generate a nsurl.
+ *
+ * @param[in] path The path to convert.
+ * @param[out] url_out pointer to recive the nsurl, The returned url
+ * must be unreferenced by the caller.
+ * @return NSERROR_OK and the url is placed in \a url or error code on
+ * faliure.
+ */
+static nserror amiga_path_to_nsurl(const char *path, struct nsurl **url_out)
+{
+ char *colon = NULL;
+ char *r = NULL;
+ char newpath[1024 + strlen(path)];
+ BPTR lock = 0;
+ nserror ret;
+
+ if((lock = Lock(path, SHARED_LOCK))) {
+ DevNameFromLock(lock, newpath, sizeof newpath, DN_FULLPATH);
+ UnLock(lock);
+ }
+ else strlcpy(newpath, path, sizeof newpath);
+
+ r = malloc(strlen(newpath) + SLEN("file:///") + 1);
+ if (r == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ if((colon = strchr(newpath, ':'))) *colon = '/';
+
+ strcpy(r, "file:///");
+ strcat(r, newpath);
+
+ ret = nsurl_create(r, url_out);
+ free(r);
+
+ return ret;
+}
+
+/**
+ * returns a string with escape chars translated.
+ * (based on remove_underscores from utils.c)
+ */
+
+char *translate_escape_chars(const char *s)
+{
+ size_t i, ii, len;
+ char *ret;
+ len = strlen(s);
+ ret = malloc(len + 1);
+ if (ret == NULL)
+ return NULL;
+ for (i = 0, ii = 0; i < len; i++) {
+ if (s[i] != '\\') {
+ ret[ii++] = s[i];
+ }
+ else if (s[i+1] == 'n') {
+ ret[ii++] = '\n';
+ i++;
+ }
+ }
+ ret[ii] = '\0';
+ return ret;
+}
+
+/**
+ * Generate a posix path from one or more component elemnts.
+ *
+ * If a string is allocated it must be freed by the caller.
+ *
+ * @param[in,out] str pointer to string pointer if this is NULL enough
+ * storage will be allocated for the complete path.
+ * @param[in,out] size The size of the space available if \a str not
+ * NULL on input and if not NULL set to the total
+ * output length on output.
+ * @param[in] nelm The number of elements.
+ * @param[in] ap The elements of the path as string pointers.
+ * @return NSERROR_OK and the complete path is written to str
+ * or error code on faliure.
+ */
+static nserror amiga_vmkpath(char **str, size_t *size, size_t nelm, va_list ap)
+{
+ const char *elm[16];
+ size_t elm_len[16];
+ size_t elm_idx;
+ char *fname;
+ size_t fname_len = 0;
+
+ /* check the parameters are all sensible */
+ if ((nelm == 0) || (nelm > 16)) {
+ return NSERROR_BAD_PARAMETER;
+ }
+ if ((*str != NULL) && (size == NULL)) {
+ /* if the caller is providing the buffer they must say
+ * how much space is available.
+ */
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ /* calculate how much storage we need for the complete path
+ * with all the elements.
+ */
+ for (elm_idx = 0; elm_idx < nelm; elm_idx++) {
+ elm[elm_idx] = va_arg(ap, const char *);
+ /* check the argument is not NULL */
+ if (elm[elm_idx] == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+ elm_len[elm_idx] = strlen(elm[elm_idx]);
+ fname_len += elm_len[elm_idx];
+ }
+ fname_len += nelm; /* allow for separators and terminator */
+
+ /* ensure there is enough space */
+ fname = *str;
+ if (fname != NULL) {
+ if (fname_len > *size) {
+ return NSERROR_NOSPACE;
+ }
+ } else {
+ fname = malloc(fname_len);
+ if (fname == NULL) {
+ return NSERROR_NOMEM;
+ }
+ }
+
+ /* copy the first element complete */
+ memmove(fname, elm[0], elm_len[0]);
+ fname[elm_len[0]] = 0;
+
+ /* add the remaining elements */
+ for (elm_idx = 1; elm_idx < nelm; elm_idx++) {
+ if (!AddPart(fname, elm[elm_idx], fname_len)) {
+ break;
+ }
+ }
+
+ *str = fname;
+ if (size != NULL) {
+ *size = fname_len;
+ }
+
+ return NSERROR_OK;
+}
+
+/**
+ * Get the basename of a file using posix path handling.
+ *
+ * This gets the last element of a path and returns it.
+ *
+ * @param[in] path The path to extract the name from.
+ * @param[in,out] str Pointer to string pointer if this is NULL enough
+ * storage will be allocated for the path element.
+ * @param[in,out] size The size of the space available if \a
+ * str not NULL on input and set to the total
+ * output length on output.
+ * @return NSERROR_OK and the complete path is written to str
+ * or error code on faliure.
+ */
+static nserror amiga_basename(const char *path, char **str, size_t *size)
+{
+ const char *leafname;
+ char *fname;
+
+ if (path == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ leafname = FilePart(path);
+ if (leafname == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ fname = strdup(leafname);
+ if (fname == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ *str = fname;
+ if (size != NULL) {
+ *size = strlen(fname);
+ }
+ return NSERROR_OK;
+}
+
+/**
+ * Ensure that all directory elements needed to store a filename exist.
+ *
+ * @param fname The filename to ensure the path to exists.
+ * @return NSERROR_OK on success or error code on failure.
+ */
+static nserror amiga_mkdir_all(const char *fname)
+{
+ char *dname;
+ char *sep;
+ struct stat sb;
+
+ dname = strdup(fname);
+
+ sep = strrchr(dname, '/');
+ if (sep == NULL) {
+ /* no directory separator path is just filename so its ok */
+ free(dname);
+ return NSERROR_OK;
+ }
+
+ *sep = 0; /* null terminate directory path */
+
+ if (stat(dname, &sb) == 0) {
+ free(dname);
+ if (S_ISDIR(sb.st_mode)) {
+ /* path to file exists and is a directory */
+ return NSERROR_OK;
+ }
+ return NSERROR_NOT_DIRECTORY;
+ }
+ *sep = '/'; /* restore separator */
+
+ sep = dname;
+ while (*sep == '/') {
+ sep++;
+ }
+ while ((sep = strchr(sep, '/')) != NULL) {
+ *sep = 0;
+ if (stat(dname, &sb) != 0) {
+ if (nsmkdir(dname, S_IRWXU) != 0) {
+ /* could not create path element */
+ free(dname);
+ return NSERROR_NOT_FOUND;
+ }
+ } else {
+ if (! S_ISDIR(sb.st_mode)) {
+ /* path element not a directory */
+ free(dname);
+ return NSERROR_NOT_DIRECTORY;
+ }
+ }
+ *sep = '/'; /* restore separator */
+ /* skip directory separators */
+ while (*sep == '/') {
+ sep++;
+ }
+ }
+
+ free(dname);
+ return NSERROR_OK;
+}
+
+/* amiga file handling operations */
+static struct gui_file_table file_table = {
+ .mkpath = amiga_vmkpath,
+ .basename = amiga_basename,
+ .nsurl_to_path = amiga_nsurl_to_path,
+ .path_to_nsurl = amiga_path_to_nsurl,
+ .mkdir_all = amiga_mkdir_all,
+};
+
+struct gui_file_table *amiga_file_table = &file_table;
diff --git a/frontends/amiga/misc.h b/frontends/amiga/misc.h
new file mode 100644
index 000000000..c7ae5af78
--- /dev/null
+++ b/frontends/amiga/misc.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_MISC_H
+#define AMIGA_MISC_H
+
+#include <exec/types.h>
+
+#include "utils/errors.h"
+
+extern struct gui_file_table *amiga_file_table;
+struct Window;
+
+/**
+ * Warn the user of an event.
+ *
+ * \param[in] warning A warning looked up in the message translation table
+ * \param[in] detail Additional text to be displayed or NULL.
+ * \return NSERROR_OK on success or error code if there was a
+ * faliure displaying the message to the user.
+ */
+nserror amiga_warn_user(const char *warning, const char *detail);
+
+void *ami_misc_allocvec_clear(int size, UBYTE value);
+
+/* Itempool cross-compatibility */
+APTR ami_misc_itempool_create(int size);
+void ami_misc_itempool_delete(APTR pool);
+APTR ami_misc_itempool_alloc(APTR pool, int size);
+void ami_misc_itempool_free(APTR pool, APTR item, int size);
+
+char *translate_escape_chars(const char *s);
+void ami_misc_fatal_error(const char *message);
+int32 amiga_warn_user_multi(const char *body,
+ const char *opt1, const char *opt2, struct Window *win);
+#endif
+
diff --git a/frontends/amiga/object.c b/frontends/amiga/object.c
new file mode 100755
index 000000000..8da16394e
--- /dev/null
+++ b/frontends/amiga/object.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2005, 2008, 2016 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "amiga/os3support.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <proto/exec.h>
+#include <exec/lists.h>
+#include <exec/nodes.h>
+
+#include "amiga/misc.h"
+#include "amiga/object.h"
+
+#ifdef __amigaos4__
+#define nsList MinList
+#define NewnsList NewMinList
+#else
+#define nsList List
+#define NewnsList NewList
+#endif
+
+APTR pool_nsobj = NULL;
+
+bool ami_object_init(void)
+{
+ pool_nsobj = ami_misc_itempool_create(sizeof(struct nsObject));
+
+ if(pool_nsobj == NULL) return false;
+ else return true;
+}
+
+void ami_object_fini(void)
+{
+ ami_misc_itempool_delete(pool_nsobj);
+}
+
+/* Slightly abstract MinList initialisation */
+static void ami_NewMinList(struct MinList *list)
+{
+ if(list == NULL) return;
+ NewnsList((struct nsList *)list);
+}
+
+/* Allocate and initialise a new MinList */
+struct MinList *ami_AllocMinList(void)
+{
+ struct MinList *objlist = (struct MinList *)AllocVecTagList(sizeof(struct nsList), NULL);
+ if(objlist == NULL) return NULL;
+ ami_NewMinList(objlist);
+ return objlist;
+}
+
+struct MinList *NewObjList(void)
+{
+ struct MinList *objlist = ami_AllocMinList();
+ return(objlist);
+}
+
+struct nsObject *AddObject(struct MinList *objlist, ULONG otype)
+{
+ struct nsObject *dtzo;
+
+ dtzo = (struct nsObject *)ami_misc_itempool_alloc(pool_nsobj, sizeof(struct nsObject));
+ if(dtzo == NULL) return NULL;
+
+ memset(dtzo, 0, sizeof(struct nsObject));
+ AddTail((struct List *)objlist,(struct Node *)dtzo);
+
+ dtzo->Type = otype;
+
+ return(dtzo);
+}
+
+void ObjectCallback(struct nsObject *dtzo, void (*callback)(void *nso))
+{
+ dtzo->callback = callback;
+}
+
+static void DelObjectInternal(struct nsObject *dtzo, BOOL free_obj)
+{
+ Remove((struct Node *)dtzo);
+ if(dtzo->callback != NULL) dtzo->callback(dtzo->objstruct);
+ if(dtzo->objstruct && free_obj) FreeVec(dtzo->objstruct);
+ if(dtzo->dtz_Node.ln_Name) free(dtzo->dtz_Node.ln_Name);
+ ami_misc_itempool_free(pool_nsobj, dtzo, sizeof(struct nsObject));
+ dtzo = NULL;
+}
+
+void DelObject(struct nsObject *dtzo)
+{
+ DelObjectInternal(dtzo, TRUE);
+}
+
+void DelObjectNoFree(struct nsObject *dtzo)
+{
+ DelObjectInternal(dtzo, FALSE);
+}
+
+void FreeObjList(struct MinList *objlist)
+{
+ struct nsObject *node;
+ struct nsObject *nnode;
+
+ if(IsMinListEmpty((struct MinList *)objlist)) return;
+ node = (struct nsObject *)GetHead((struct List *)objlist);
+
+ do {
+ nnode=(struct nsObject *)GetSucc((struct Node *)node);
+ if(node->Type == AMINS_RECT) {
+ DelObjectNoFree(node);
+ } else {
+ DelObject(node);
+ }
+ } while((node=nnode));
+
+ FreeVec(objlist);
+}
+
diff --git a/frontends/amiga/object.h b/frontends/amiga/object.h
new file mode 100755
index 000000000..be9650413
--- /dev/null
+++ b/frontends/amiga/object.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2008,2009 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_OBJECT_H
+#define AMIGA_OBJECT_H
+
+#include <exec/lists.h>
+
+enum
+{
+ AMINS_UNKNOWN,
+ AMINS_CALLBACK,
+ AMINS_WINDOW,
+ AMINS_DLWINDOW,
+ AMINS_LOGINWINDOW,
+ AMINS_TVWINDOW,
+ AMINS_FINDWINDOW,
+ AMINS_HISTORYWINDOW,
+ AMINS_GUIOPTSWINDOW,
+ AMINS_PRINTWINDOW,
+ AMINS_FONT,
+ AMINS_MIME,
+ AMINS_RECT
+};
+
+struct nsObject
+{
+ struct Node dtz_Node;
+ ULONG Type;
+ void *objstruct;
+ ULONG objstruct_size;
+ void (*callback)(void *nso);
+};
+
+
+struct MinList *NewObjList(void);
+struct nsObject *AddObject(struct MinList *objlist, ULONG otype);
+void ObjectCallback(struct nsObject *dtzo, void (*callback)(void *nso));
+void DelObject(struct nsObject *dtzo);
+void DelObjectNoFree(struct nsObject *dtzo);
+void FreeObjList(struct MinList *objlist);
+
+/** List abstraction as OS3 appears to have problems with NewMinList() **/
+struct MinList *ami_AllocMinList(void);
+
+/** Initialisation for itempool **/
+bool ami_object_init(void);
+void ami_object_fini(void);
+#endif
+
diff --git a/frontends/amiga/options.h b/frontends/amiga/options.h
new file mode 100644
index 000000000..0c4db5c45
--- /dev/null
+++ b/frontends/amiga/options.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2008 - 2012 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_OPTIONS_H
+#define AMIGA_OPTIONS_H
+
+/* currently nothing here */
+
+#endif
+
+
+
+NSOPTION_STRING(url_file, NULL)
+NSOPTION_STRING(hotlist_file, NULL)
+NSOPTION_STRING(pubscreen_name, NULL)
+NSOPTION_STRING(screen_modeid, NULL)
+NSOPTION_INTEGER(screen_compositing, -1)
+NSOPTION_INTEGER(screen_ydpi, 85)
+NSOPTION_INTEGER(cache_bitmaps, 0)
+NSOPTION_STRING(theme, "PROGDIR:Resources/Themes/Default")
+NSOPTION_BOOL(clipboard_write_utf8, false)
+NSOPTION_BOOL(truecolour_mouse_pointers, false)
+NSOPTION_BOOL(os_mouse_pointers, true)
+NSOPTION_BOOL(use_openurl_lib, false)
+NSOPTION_BOOL(new_tab_is_active, false)
+NSOPTION_BOOL(new_tab_last, false)
+NSOPTION_BOOL(tab_close_warn, true)
+NSOPTION_BOOL(tab_always_show, false)
+NSOPTION_BOOL(kiosk_mode, false)
+NSOPTION_STRING(search_engines_file, "PROGDIR:Resources/SearchEngines")
+NSOPTION_STRING(arexx_dir, "PROGDIR:Rexx")
+NSOPTION_STRING(arexx_startup, "Startup.nsrx")
+NSOPTION_STRING(arexx_shutdown, "Shutdown.nsrx")
+NSOPTION_STRING(download_dir, NULL)
+NSOPTION_BOOL(download_notify, true)
+NSOPTION_BOOL(faster_scroll, true)
+NSOPTION_BOOL(scale_quality, false)
+NSOPTION_INTEGER(dither_quality, 0)
+NSOPTION_INTEGER(mask_alpha, 50)
+NSOPTION_BOOL(ask_overwrite, true)
+NSOPTION_INTEGER(printer_unit, 0)
+NSOPTION_INTEGER(print_scale, 100)
+NSOPTION_BOOL(startup_no_window, false)
+NSOPTION_BOOL(close_no_quit, false)
+NSOPTION_BOOL(hide_docky_icon, false)
+NSOPTION_STRING(font_unicode, NULL)
+NSOPTION_STRING(font_surrogate, NULL)
+NSOPTION_STRING(font_unicode_file, NULL)
+NSOPTION_BOOL(font_unicode_only, false)
+NSOPTION_BOOL(font_antialiasing, true)
+NSOPTION_BOOL(bitmap_fonts, false)
+NSOPTION_BOOL(drag_save_icons, true)
+NSOPTION_INTEGER(hotlist_window_xpos, 0)
+NSOPTION_INTEGER(hotlist_window_ypos, 0)
+NSOPTION_INTEGER(hotlist_window_xsize, 0)
+NSOPTION_INTEGER(hotlist_window_ysize, 0)
+NSOPTION_INTEGER(history_window_xpos, 0)
+NSOPTION_INTEGER(history_window_ypos, 0)
+NSOPTION_INTEGER(history_window_xsize, 0)
+NSOPTION_INTEGER(history_window_ysize, 0)
+NSOPTION_INTEGER(cookies_window_xpos, 0)
+NSOPTION_INTEGER(cookies_window_ypos, 0)
+NSOPTION_INTEGER(cookies_window_xsize, 0)
+NSOPTION_INTEGER(cookies_window_ysize, 0)
+NSOPTION_INTEGER(web_search_width, 0)
+NSOPTION_BOOL(direct_render, false)
+NSOPTION_BOOL(window_simple_refresh, false)
+NSOPTION_BOOL(resize_with_contents, false)
+NSOPTION_INTEGER(reformat_delay, 0)
+NSOPTION_INTEGER(redraw_tile_size_x, 0)
+NSOPTION_INTEGER(redraw_tile_size_y, 0)
+NSOPTION_INTEGER(monitor_aspect_x, 0)
+NSOPTION_INTEGER(monitor_aspect_y, 0)
+NSOPTION_BOOL(accept_lang_locale, true)
+/* Options relevant for OS3 only */
+#ifndef __amigaos4__
+NSOPTION_BOOL(friend_bitmap, false)
+NSOPTION_STRING(local_charset, "ISO-8859-1")
+#endif
+
diff --git a/frontends/amiga/os3support.c b/frontends/amiga/os3support.c
new file mode 100644
index 000000000..b251448a6
--- /dev/null
+++ b/frontends/amiga/os3support.c
@@ -0,0 +1,431 @@
+/*
+ * Copyright 2014 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Minimal compatibility header for AmigaOS 3
+ */
+
+#ifndef __amigaos4__
+#include "os3support.h"
+
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <proto/bullet.h>
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <proto/dos.h>
+#include <proto/utility.h>
+
+#include <diskfont/diskfont.h>
+#include <diskfont/diskfonttag.h>
+#include <intuition/gadgetclass.h>
+
+#include "utils/log.h"
+
+#define SUCCESS (TRUE)
+#define FAILURE (FALSE)
+#define NO !
+
+/* Diskfont */
+struct OutlineFont *OpenOutlineFont(STRPTR fileName, struct List *list, ULONG flags)
+{
+ BPTR fh = 0;
+ int64 size = 0;
+ struct TagItem *ti;
+ UBYTE *buffer;
+ STRPTR fname, otagpath, fontpath;
+ struct BulletBase *BulletBase;
+ struct OutlineFont *of = NULL;
+ struct GlyphEngine *gengine;
+ char *p = 0;
+ struct FontContentsHeader fch;
+
+ if(p = strrchr(fileName, '.'))
+ *p = '\0';
+
+ fontpath = (STRPTR)ASPrintf("FONTS:%s.font", fileName);
+ fh = Open(fontpath, MODE_OLDFILE);
+
+ if(fh == 0) {
+ LOG("Unable to open FONT %s", fontpath);
+ FreeVec(fontpath);
+ return NULL;
+ }
+
+ if(Read(fh, &fch, sizeof(struct FontContentsHeader)) != sizeof(struct FontContentsHeader)) {
+ LOG("Unable to read FONT %s", fontpath);
+ FreeVec(fontpath);
+ Close(fh);
+ return NULL;
+ }
+
+ Close(fh);
+
+ if(fch.fch_FileID != OFCH_ID) {
+ LOG("%s is not an outline font!", fontpath);
+ FreeVec(fontpath);
+ return NULL;
+ }
+
+ otagpath = (STRPTR)ASPrintf("FONTS:%s.otag", fileName);
+ fh = Open(otagpath, MODE_OLDFILE);
+
+ if(p) *p = '.';
+
+ if(fh == 0) {
+ LOG("Unable to open OTAG %s", otagpath);
+ FreeVec(otagpath);
+ return NULL;
+ }
+
+ size = GetFileSize(fh);
+ buffer = (struct TagItem *)AllocVec(size, MEMF_ANY);
+ if(buffer == NULL) {
+ LOG("Unable to allocate memory");
+ Close(fh);
+ FreeVec(otagpath);
+ return NULL;
+ }
+
+ Read(fh, buffer, size);
+ Close(fh);
+
+ /* The first tag is supposed to be OT_FileIdent and should equal 'size' */
+ struct TagItem *tag = (struct TagItem *)buffer;
+ if((tag->ti_Tag != OT_FileIdent) || (tag->ti_Data != (ULONG)size)) {
+ LOG("Invalid OTAG file");
+ FreeVec(buffer);
+ FreeVec(otagpath);
+ return NULL;
+ }
+
+ /* Relocate all the OT_Indirect tags */
+ while (ti = NextTagItem(&tag)) {
+ if(ti->ti_Tag & OT_Indirect) {
+ ti->ti_Data += buffer;
+ }
+ }
+
+ /* Find OT_Engine and open the font engine */
+ if(ti = FindTagItem(OT_Engine, buffer)) {
+ LOG("Using font engine %s", ti->ti_Data);
+ fname = ASPrintf("%s.library", ti->ti_Data);
+ } else {
+ LOG("Cannot find OT_Engine tag");
+ FreeVec(buffer);
+ FreeVec(otagpath);
+ return NULL;
+ }
+
+ BulletBase = OpenLibrary(fname, 0L);
+
+ if(BulletBase == NULL) {
+ LOG("Unable to open font engine %s", fname);
+ FreeVec(buffer);
+ FreeVec(fname);
+ FreeVec(otagpath);
+ }
+
+ FreeVec(fname);
+
+ gengine = OpenEngine();
+
+ SetInfo(gengine,
+ OT_OTagPath, otagpath,
+ OT_OTagList, buffer,
+ TAG_DONE);
+
+ of = AllocVec(sizeof(struct OutlineFont), MEMF_CLEAR);
+ if(of == NULL) return NULL;
+
+ of->BulletBase = BulletBase;
+ of->GEngine = gengine;
+ of->OTagPath = otagpath;
+ of->olf_OTagList = buffer;
+
+ return of;
+}
+
+void CloseOutlineFont(struct OutlineFont *of, struct List *list)
+{
+ struct BulletBase *BulletBase = of->BulletBase;
+
+ CloseEngine(of->GEngine);
+ CloseLibrary(BulletBase);
+
+ FreeVec(of->OTagPath);
+ FreeVec(of->olf_OTagList);
+ FreeVec(of);
+}
+
+
+/* DOS */
+int64 GetFileSize(BPTR fh)
+{
+ int32 size = 0;
+ struct FileInfoBlock *fib = AllocVec(sizeof(struct FileInfoBlock), MEMF_ANY);
+ if(fib == NULL) return 0;
+
+ ExamineFH(fh, fib);
+ size = fib->fib_Size;
+
+ FreeVec(fib);
+ return (int64)size;
+}
+
+void FreeSysObject(ULONG type, APTR obj)
+{
+ switch(type) {
+ case ASOT_PORT:
+ DeleteMsgPort(obj);
+ break;
+ case ASOT_IOREQUEST:
+ DeleteIORequest(obj);
+ break;
+ }
+}
+
+
+/* Exec */
+struct Node *GetHead(struct List *list)
+{
+ struct Node *res = NULL;
+
+ if ((NULL != list) && (NULL != list->lh_Head->ln_Succ))
+ {
+ res = list->lh_Head;
+ }
+ return res;
+}
+
+struct Node *GetPred(struct Node *node)
+{
+ if (node->ln_Pred->ln_Pred == NULL) return NULL;
+ return node->ln_Pred;
+}
+
+struct Node *GetSucc(struct Node *node)
+{
+ if (node->ln_Succ->ln_Succ == NULL) return NULL;
+ return node->ln_Succ;
+}
+
+
+/* Intuition */
+uint32 GetAttrs(Object *obj, Tag tag1, ...)
+{
+ va_list ap;
+ Tag tag = tag1;
+ ULONG data = 0;
+ int i = 0;
+
+ va_start(ap, tag1);
+
+ while(tag != TAG_DONE) {
+ data = va_arg(ap, ULONG);
+ i += GetAttr(tag, obj, (void *)data);
+ tag = va_arg(ap, Tag);
+ }
+ va_end(ap);
+
+ return i;
+}
+
+ULONG RefreshSetGadgetAttrsA(struct Gadget *g, struct Window *w, struct Requester *r, struct TagItem *tags)
+{
+ ULONG retval;
+ BOOL changedisabled = FALSE;
+ BOOL disabled;
+
+ if (w) {
+ if (FindTagItem(GA_Disabled,tags)) {
+ changedisabled = TRUE;
+ disabled = g->Flags & GFLG_DISABLED;
+ }
+ }
+ retval = SetGadgetAttrsA(g,w,r,tags);
+ if (w && (retval || (changedisabled && disabled != (g->Flags & GFLG_DISABLED)))) {
+ RefreshGList(g,w,r,1);
+ retval = 1;
+ }
+ return retval;
+}
+
+ULONG RefreshSetGadgetAttrs(struct Gadget *g, struct Window *w, struct Requester *r, Tag tag1, ...)
+{
+ return RefreshSetGadgetAttrsA(g,w,r,(struct TagItem *) &tag1);
+}
+
+APTR NewObject(struct IClass * classPtr, CONST_STRPTR classID, ULONG tagList, ...)
+{
+ return NewObjectA(classPtr, classID, (const struct TagItem *) &tagList);
+}
+
+/* Utility */
+struct FormatContext
+{
+ STRPTR Index;
+ LONG Size;
+ BOOL Overflow;
+};
+
+STATIC VOID ASM
+StuffChar(
+ REG(a3, struct FormatContext * Context),
+ REG(d0, UBYTE Char))
+{
+ /* Is there still room? */
+ if(Context->Size > 0)
+ {
+ (*Context->Index) = Char;
+
+ Context->Index++;
+ Context->Size--;
+
+ /* Is there only a single character left? */
+ if(Context->Size == 1)
+ {
+ /* Provide null-termination. */
+ (*Context->Index) = '\0';
+
+ /* Don't store any further characters. */
+ Context->Size = 0;
+ }
+ }
+ else
+ {
+ Context->Overflow = TRUE;
+ }
+}
+
+BOOL
+VSPrintfN(
+ LONG MaxLen,
+ STRPTR Buffer,
+ const STRPTR FormatString,
+ const va_list VarArgs)
+{
+ BOOL result = FAILURE;
+
+ /* format a text, but place only up to MaxLen
+ * characters in the output buffer (including
+ * the terminating NUL)
+ */
+
+ if (Buffer == NULL || FormatString == NULL) return(result);
+
+ if(MaxLen > 1)
+ {
+ struct FormatContext Context;
+
+ Context.Index = Buffer;
+ Context.Size = MaxLen;
+ Context.Overflow = FALSE;
+
+ RawDoFmt(FormatString,(APTR)VarArgs,(VOID (*)())StuffChar,(APTR)&Context);
+
+ if(NO Context.Overflow)
+ result = SUCCESS;
+ }
+
+ return(result);
+}
+
+BOOL
+SPrintfN(
+ LONG MaxLen,
+ STRPTR Buffer,
+ const STRPTR FormatString,
+ ...)
+{
+ va_list VarArgs;
+ BOOL result = FAILURE;
+
+ /* format a text, varargs version */
+
+ if (Buffer == NULL && FormatString == NULL) return result;
+
+ va_start(VarArgs,FormatString);
+ result = VSPrintfN(MaxLen,Buffer,FormatString,VarArgs);
+ va_end(VarArgs);
+
+ return(result);
+}
+
+char *ASPrintf(const char *fmt, ...)
+{
+ int r;
+ va_list ap;
+ static char buffer[2048];
+ char *rbuf;
+
+ va_start(ap, fmt);
+ r = VSPrintfN(2048, buffer, (const STRPTR)fmt, ap);
+ va_end(ap);
+
+ r = strlen(buffer);
+ rbuf = AllocVec(r+1, MEMF_CLEAR);
+ if (rbuf != NULL)
+ {
+ strncpy(rbuf, buffer, r);
+ }
+ return rbuf;
+}
+
+/* C */
+char *strlwr(char *str)
+{
+ size_t i;
+ size_t len = strlen(str);
+
+ for(i=0; i<len; i++)
+ str[i] = tolower((unsigned char)str[i]);
+
+ return str;
+}
+
+char *strsep(char **s1, const char *s2)
+{
+ char *const p1 = *s1;
+
+ if (p1 != NULL) {
+ *s1 = strpbrk(p1, s2);
+ if (*s1 != NULL) {
+ *(*s1)++ = '\0';
+ }
+ }
+ return p1;
+}
+
+int scandir(const char *dir, struct dirent ***namelist,
+ int (*filter)(const struct dirent *),
+ int (*compar)(const struct dirent **, const struct dirent **))
+{
+ /*\todo stub function, needs writing, preferably into clib2 */
+ return 0;
+}
+
+long long int strtoll(const char *nptr, char **endptr, int base)
+{
+ return (long long int)strtol(nptr, endptr, base);
+}
+
+#endif
+
diff --git a/frontends/amiga/os3support.h b/frontends/amiga/os3support.h
new file mode 100644
index 000000000..94d1d5818
--- /dev/null
+++ b/frontends/amiga/os3support.h
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2010 John-Mark Bell <jmb@netsurf-browser.org>
+ * Copyright 2014 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Minimal compatibility header for AmigaOS 3
+ */
+
+#ifndef AMIGA_OS3SUPPORT_H_
+#define AMIGA_OS3SUPPORT_H_
+
+#ifndef __amigaos4__
+
+#include <stdint.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+
+/* Include prototypes for amigalib */
+#include <clib/alib_protos.h>
+
+#ifndef EXEC_MEMORY_H
+#include <exec/memory.h>
+#endif
+
+/* C macros */
+#ifndef ASM
+#define ASM
+#endif
+
+#ifndef REG
+#define REG(reg,arg) arg __asm(#reg)
+#endif
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+/* Macros */
+#define IsMinListEmpty(L) (L)->mlh_Head->mln_Succ == 0
+#define LIB_IS_AT_LEAST(B,V,R) ((B)->lib_Version>(V)) || \
+ ((B)->lib_Version==(V) && (B)->lib_Revision>=(R))
+#define EAD_IS_FILE(E) ((E)->ed_Type<0)
+
+/* Define extra memory type flags */
+#define MEMF_PRIVATE MEMF_ANY
+#define MEMF_SHARED MEMF_ANY
+
+/* Ignore unsupported tags */
+#define ASO_NoTrack TAG_IGNORE
+#define BITMAP_DisabledSourceFile TAG_IGNORE
+#define BITMAP_HasAlpha TAG_IGNORE
+#define BLITA_UseSrcAlpha TAG_IGNORE
+#define BLITA_MaskPlane TAG_IGNORE
+#define CLICKTAB_CloseImage TAG_IGNORE
+#define CLICKTAB_FlagImage TAG_IGNORE
+#define CLICKTAB_LabelTruncate TAG_IGNORE
+#define CLICKTAB_NodeClosed TAG_IGNORE
+#define GETFONT_OTagOnly TAG_IGNORE
+#define GETFONT_ScalableOnly TAG_IGNORE
+#define PDTA_PromoteMask TAG_IGNORE
+#define RPTAG_APenColor TAG_IGNORE
+#define GA_ContextMenu TAG_IGNORE
+#define GA_HintInfo TAG_IGNORE
+#define GAUGEIA_Level TAG_IGNORE
+#define IA_InBorder TAG_IGNORE
+#define IA_Label TAG_IGNORE
+#define SA_Compositing TAG_IGNORE
+#define SBNA_Text TAG_IGNORE
+#define SBNA_HintInfo TAG_IGNORE
+#define TNA_CloseGadget TAG_IGNORE
+#define TNA_HintInfo TAG_IGNORE
+#define WA_ContextMenuHook TAG_IGNORE
+#define WA_ToolBox TAG_IGNORE
+#define WINDOW_BuiltInScroll TAG_IGNORE
+#define WINDOW_NewMenu TAG_IGNORE
+#define WINDOW_NewPrefsHook TAG_IGNORE
+
+/* raw keycodes */
+#define RAWKEY_BACKSPACE 0x41
+#define RAWKEY_TAB 0x42
+#define RAWKEY_ESC 0x45
+#define RAWKEY_DEL 0x46
+#define RAWKEY_PAGEUP 0x48
+#define RAWKEY_PAGEDOWN 0x49
+#define RAWKEY_CRSRUP 0x4C
+#define RAWKEY_CRSRDOWN 0x4D
+#define RAWKEY_CRSRRIGHT 0x4E
+#define RAWKEY_CRSRLEFT 0x4F
+#define RAWKEY_F5 0x54
+#define RAWKEY_F8 0x57
+#define RAWKEY_F9 0x58
+#define RAWKEY_F10 0x59
+#define RAWKEY_HELP 0x5F
+#define RAWKEY_HOME 0x70
+#define RAWKEY_END 0x71
+
+/* Other constants */
+#define BVS_DISPLAY BVS_NONE
+#define IDCMP_EXTENDEDMOUSE 0
+#define WINDOW_BACKMOST 0
+#define DN_FULLPATH 0
+#define BGBACKFILL JAM1
+#define OFF_OPEN 0
+#define AFF_OTAG 0
+
+/* Renamed structures */
+#define AnchorPathOld AnchorPath
+
+/* ReAction (ClassAct) macros */
+#define GetFileEnd End
+#define GetFontEnd End
+#define GetScreenModeEnd End
+
+/* MinTerm stuff */
+#define MINTERM_SRCMASK (ABC|ABNC|ANBC)
+
+/* Easy compat macros */
+/* application */
+#define Notify(...) (void)0
+
+/* DataTypes */
+#define SaveDTObjectA(O,W,R,F,M,I,A) DoDTMethod(O,W,R,DTM_WRITE,F,M,NULL)
+
+/* diskfont */
+#define EReleaseInfo ReleaseInfo
+#define EObtainInfo ObtainInfo
+#define ESetInfo SetInfo
+
+/* Only used in one place we haven't ifdeffed, where it returns the charset name */
+#define ObtainCharsetInfo(A,B,C) (const char *)nsoption_charp(local_charset)
+
+/* DOS */
+#define AllocSysObjectTags(A,B,C,D) CreateMsgPort() /* Assume ASOT_PORT for now */
+#define FOpen(A,B,C) Open(A,B)
+#define FClose(A) Close(A)
+#define CreateDirTree(D) CreateDir(D) /*\todo This isn't quite right */
+#define SetCurrentDir(L) CurrentDir(L)
+#define DevNameFromLock(A,B,C,D) NameFromLock(A,B,C)
+
+/* Exec */
+#define AllocVecTagList(SZ,TAG) AllocVec(SZ,MEMF_ANY) /* AllocVecTagList with no tags */
+#define FindIName FindName
+
+/* Intuition */
+#define ICoerceMethod CoerceMethod
+#define IDoMethod DoMethod
+#define IDoMethodA DoMethodA
+#define IDoSuperMethodA DoSuperMethodA
+#define ShowWindow(...) (void)0
+
+/* Utility */
+#define SetMem memset
+#define SNPrintf snprintf
+
+/* Integral type definitions */
+typedef int8_t int8;
+typedef uint8_t uint8;
+typedef int16_t int16;
+typedef uint16_t uint16;
+typedef int32_t int32;
+typedef uint32_t uint32;
+typedef int64_t int64;
+typedef uint64_t uint64;
+
+/* TimeVal */
+struct TimeVal {
+ uint32 Seconds;
+ uint32 Microseconds;
+};
+
+/* TimeRequest */
+struct TimeRequest {
+ struct IORequest Request;
+ struct TimeVal Time;
+};
+
+/* OutlineFont */
+struct OutlineFont {
+ struct BulletBase *BulletBase;
+ struct GlyphEngine *GEngine;
+ STRPTR OTagPath;
+ struct TagItem *olf_OTagList;
+};
+
+/* BackFillMessage */
+struct BackFillMessage {
+ struct Layer *Layer;
+ struct Rectangle Bounds;
+ LONG OffsetX;
+ LONG OffsetY;
+};
+
+/* icon.library v51 (ie. AfA_OS version) */
+#define ICONCTRLA_SetImageDataFormat (ICONA_Dummy + 0x67) /*103*/
+#define ICONCTRLA_GetImageDataFormat (ICONA_Dummy + 0x68) /*104*/
+
+#define IDFMT_BITMAPPED (0) /* Bitmapped icon (planar, legacy) */
+#define IDFMT_PALETTEMAPPED (1) /* Palette mapped icon (chunky, V44+) */
+#define IDFMT_DIRECTMAPPED (2) /* Direct mapped icon (truecolor 0xAARRGGBB, V51+) */
+
+/* Object types */
+enum {
+ ASOT_PORT = 1,
+ ASOT_IOREQUEST
+};
+
+/* Requester types */
+enum {
+ TDRIMAGE_ERROR = 1,
+ TDRIMAGE_WARNING
+};
+
+/* Functions */
+/* Diskfont */
+void CloseOutlineFont(struct OutlineFont *of, struct List *list);
+struct OutlineFont *OpenOutlineFont(STRPTR fileName, struct List *list, ULONG flags);
+
+/* DOS */
+int64 GetFileSize(BPTR fh);
+void FreeSysObject(ULONG type, APTR obj);
+
+/* Exec */
+struct Node *GetHead(struct List *list);
+struct Node *GetPred(struct Node *node);
+struct Node *GetSucc(struct Node *node);
+
+/* Intuition */
+uint32 GetAttrs(Object *obj, Tag tag1, ...);
+ULONG RefreshSetGadgetAttrs(struct Gadget *g, struct Window *w, struct Requester *r, Tag tag1, ...);
+ULONG RefreshSetGadgetAttrsA(struct Gadget *g, struct Window *w, struct Requester *r, struct TagItem *tags);
+APTR NewObject(struct IClass * classPtr, CONST_STRPTR classID, ULONG tagList, ...);
+
+/* Utility */
+char *ASPrintf(const char *fmt, ...);
+
+/* C */
+char *strlwr(char *str);
+#endif
+#endif
+
diff --git a/frontends/amiga/pkg/AutoInstall b/frontends/amiga/pkg/AutoInstall
new file mode 100755
index 000000000..6785a5dff
--- /dev/null
+++ b/frontends/amiga/pkg/AutoInstall
@@ -0,0 +1,3 @@
+; AmiUpdate AutoInstall for NetSurf
+cd NetSurf
+Installer Install APPNAME NetSurfAutoInstall DEFUSER NOVICE LOGFILE T:NetSurfAutoInstall.log
diff --git a/frontends/amiga/pkg/drawer.info b/frontends/amiga/pkg/drawer.info
new file mode 100644
index 000000000..0a9548a0d
--- /dev/null
+++ b/frontends/amiga/pkg/drawer.info
Binary files differ
diff --git a/frontends/amiga/pkg/makepackage b/frontends/amiga/pkg/makepackage
new file mode 100755
index 000000000..4f5eb84d8
--- /dev/null
+++ b/frontends/amiga/pkg/makepackage
@@ -0,0 +1,34 @@
+makedir ram:NetSurf/Resources ALL
+list resources LFORMAT="copy %f%n ram:netsurf/resources followlinks" files >t:nslinks
+list resources LFORMAT="makedir ram:netsurf/resources/%n" dirs >>t:nslinks
+list resources/~(Pointers|Themes) LFORMAT="copy %f%n/(Messages|#?,faf) ram:netsurf/resources/%n/ followlinks" dirs >>t:nslinks
+list resources/Themes/~(.svn) LFORMAT="makedir ram:netsurf/resources/Themes/%n" dirs >>t:nslinks
+list resources/Themes/~(.svn) LFORMAT="copy %f%n/~(.svn) ram:netsurf/resources/Themes/%n/" dirs >>t:nslinks
+execute t:nslinks
+delete t:nslinks
+delete ram:netsurf/resources/#?hotlist#?
+delete ram:netsurf/resources/cookies
+delete ram:netsurf/resources/urls
+delete ram:netsurf/resources/options
+delete ram:netsurf/resources/#?.user
+copy resources/Pointers/~(.svn) ram:NetSurf/Resources/Pointers
+copy (COPYING|ChangeLog) ram:NetSurf/
+copy NetSurf ram:NetSurf/NetSurf
+copy amiga/dist/AutoInstall ram:
+copy amiga/dist/~(.svn|AutoInstall) ram:NetSurf/
+copy amiga/pkg/drawer.info ram:NetSurf.info
+copy amiga/pkg/fitr ram:NetSurf/
+makedir ram:NetSurf/Rexx
+copy amiga/dist/Rexx/~(.svn) ram:NetSurf/Rexx/ COM
+makedir ram:NetSurf/Libs
+copy libs:parserutils.library libs:nsgif.library libs:nsbmp.library libs:iconv.library libs:hubbub.library ram:NetSurf/Libs
+; libs:css.library libs:wapcaplet.library
+makedir ram:NetSurf/SObjs
+copy sobjs:libjpeg.so.8 sobjs:libcurl.so.7 sobjs:librtmp.so.0 sobjs:libsvgtiny.so.0 sobjs:libssl.so.1.0.0 sobjs:libcrypto.so.1.0.0 sobjs:libcss.so.0 sobjs:libwapcaplet.so.0 sobjs:libpng12.so sobjs:libdom.so.0 sobjs:libhubbub.so.0 ram:NetSurf/SObjs clone
+;sobjs:libhpdf-2.2.0.so.0.0
+echo "Splitting Messages file"
+perl utils/split-messages.pl -l de -p ami -f messages resources/FatMessages >RAM:NetSurf/Resources/de/Messages
+perl utils/split-messages.pl -l en -p ami -f messages resources/FatMessages >RAM:NetSurf/Resources/en/Messages
+perl utils/split-messages.pl -l fr -p ami -f messages resources/FatMessages >RAM:NetSurf/Resources/fr/Messages
+perl utils/split-messages.pl -l it -p ami -f messages resources/FatMessages >RAM:NetSurf/Resources/it/Messages
+perl utils/split-messages.pl -l nl -p ami -f messages resources/FatMessages >RAM:NetSurf/Resources/nl/Messages
diff --git a/frontends/amiga/pkg/makereslinks b/frontends/amiga/pkg/makereslinks
new file mode 100755
index 000000000..4302dbd23
--- /dev/null
+++ b/frontends/amiga/pkg/makereslinks
@@ -0,0 +1,28 @@
+; Create Resources dir to allow NetSurf to run from build tree
+
+failat 50
+makedir Resources
+cd Resources
+makelink AdBlock.css /!NetSurf/Resources/AdBlock,f79 soft
+makelink default.css /amiga/resources/default.css soft
+makelink ca-bundle /!NetSurf/Resources/ca-bundle soft
+makelink de /!NetSurf/Resources/de soft
+makelink nsdefault.css /!NetSurf/Resources/CSS,f79 soft
+makelink favicon.png /amiga/resources/favicon.png soft
+makelink Resource.map /amiga/resources/Resource.map soft
+makelink en /!NetSurf/Resources/en soft
+makelink fr /!NetSurf/Resources/fr soft
+makelink internal.css /!NetSurf/Resources/internal.css,f79 soft
+makelink it /!NetSurf/Resources/it soft
+makelink LangNames /amiga/resources/LangNames soft
+makelink netsurf.png /!NetSurf/Resources/netsurf.png,b60 soft
+makelink nl /!NetSurf/Resources/nl soft
+makelink Pointers /amiga/resources/Pointers soft
+makelink quirks.css /!NetSurf/Resources/Quirks,f79 soft
+makelink SearchEngines /amiga/resources/SearchEngines soft
+makelink splash.png /amiga/resources/splash.png soft
+makelink Themes /amiga/resources/Themes soft
+makelink mimetypes /amiga/resources/mimetypes soft
+
+/
+makelink amiga/resources/nsdefault.css //!NetSurf/Resources/CSS,f79 soft
diff --git a/frontends/amiga/pkg/netsurf.readme b/frontends/amiga/pkg/netsurf.readme
new file mode 100755
index 000000000..3e929b040
--- /dev/null
+++ b/frontends/amiga/pkg/netsurf.readme
@@ -0,0 +1,25 @@
+Short: Fast CSS capable web browser
+Uploader: chris@unsatisfactorysoftware.co.uk (Chris Young)
+Author: NetSurf contributors (OS4 port by Chris Young)
+Type: comm/www
+Version: 3.6
+Requires: dev/misc/guigfxlib.lha
+Architecture: ppc-amigaos >= 4.0.0
+
+This is NetSurf 3.6 for AmigaOS 4 (native frontend).
+For the latest version, visit http://www.netsurf-browser.org
+
+ *******************************************
+ * Please report bugs on the bugtracker at *
+ * http://bugs.netsurf-browser.org *
+ *******************************************
+
+See http://www.netsurf-browser.org for more information about
+NetSurf.
+
+This software is licensed under the GPL, and the sources are
+available from http://www.netsurf-browser.org. A copy can
+also be obtained directly from the maintainer of this port,
+chris@unsatisfactorysoftware.co.uk, in the event that the
+website is unavailable.
+
diff --git a/frontends/amiga/pkg/netsurf.readme.info b/frontends/amiga/pkg/netsurf.readme.info
new file mode 100644
index 000000000..c0ee5ff44
--- /dev/null
+++ b/frontends/amiga/pkg/netsurf.readme.info
Binary files differ
diff --git a/frontends/amiga/pkg/netsurf_os3.readme b/frontends/amiga/pkg/netsurf_os3.readme
new file mode 100644
index 000000000..d9c0eb58a
--- /dev/null
+++ b/frontends/amiga/pkg/netsurf_os3.readme
@@ -0,0 +1,28 @@
+Short: Fast CSS capable web browser
+Uploader: chris@unsatisfactorysoftware.co.uk (Chris Young)
+Author: NetSurf contributors (OS4 port by Chris Young)
+Type: comm/www
+Version: 3.6 BETA
+Requires: dev/misc/guigfxlib.lha
+Architecture: m68k-amigaos >= 3.5.0
+
+This is NetSurf 3.6 BETA for AmigaOS 3.
+It is built off of the OS4 (Reaction) frontend.
+
+Requirements are AmigaOS 3.5 or 3.9, 32MB RAM.
+
+THIS IS EARLY BETA QUALITY SOFTWARE FOR TESTING ONLY.
+ALL USE IS AT YOUR OWN RISK. IT *WILL* CRASH!
+
+Please report bugs to chris@unsatisfactorysoftware.co.uk,
+or on the amigans.net/amiga.org forums.
+
+See http://www.netsurf-browser.org for more information about
+NetSurf.
+
+This software is licensed under the GPL, and the sources are
+available from http://www.netsurf-browser.org. A copy can
+also be obtained directly from the maintainer of this port,
+chris@unsatisfactorysoftware.co.uk, in the event that the
+website is unavailable.
+
diff --git a/frontends/amiga/pkg/netsurf_os3.readme.info b/frontends/amiga/pkg/netsurf_os3.readme.info
new file mode 100644
index 000000000..c0ee5ff44
--- /dev/null
+++ b/frontends/amiga/pkg/netsurf_os3.readme.info
Binary files differ
diff --git a/frontends/amiga/plotters.c b/frontends/amiga/plotters.c
new file mode 100644
index 000000000..23a73f84a
--- /dev/null
+++ b/frontends/amiga/plotters.c
@@ -0,0 +1,920 @@
+/*
+ * Copyright 2008-09, 2012-13 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "amiga/os3support.h"
+
+#include <proto/exec.h>
+#include <proto/intuition.h>
+
+#include <intuition/intuition.h>
+#include <graphics/rpattr.h>
+#include <graphics/gfxmacros.h>
+#include <graphics/gfxbase.h>
+
+#ifdef __amigaos4__
+#include <graphics/blitattr.h>
+#include <graphics/composite.h>
+#endif
+
+#include <math.h>
+#include <assert.h>
+
+#include "utils/nsoption.h"
+#include "utils/utils.h"
+#include "utils/log.h"
+#include "css/utils.h"
+#include "desktop/mouse.h"
+#include "desktop/gui_window.h"
+
+#include "amiga/plotters.h"
+#include "amiga/bitmap.h"
+#include "amiga/font.h"
+#include "amiga/gui.h"
+#include "amiga/misc.h"
+#include "amiga/rtg.h"
+#include "amiga/utf8.h"
+
+//#define AMI_PLOTTER_DEBUG 1
+
+HOOKF(void, ami_bitmap_tile_hook, struct RastPort *, rp, struct BackFillMessage *);
+
+struct bfbitmap {
+ struct BitMap *bm;
+ ULONG width;
+ ULONG height;
+ int offsetx;
+ int offsety;
+ APTR mask;
+};
+
+struct ami_plot_pen {
+ struct MinNode node;
+ ULONG pen;
+};
+
+struct bez_point {
+ float x;
+ float y;
+};
+
+static int init_layers_count = 0;
+static APTR pool_pens = NULL;
+
+#ifndef M_PI /* For some reason we don't always get this from math.h */
+#define M_PI 3.14159265358979323846
+#endif
+
+#define PATT_DOT 0xAAAA
+#define PATT_DASH 0xCCCC
+#define PATT_LINE 0xFFFF
+
+/* This defines the size of the list for Area* functions.
+ 25000 = 5000 vectors
+ */
+#define AREA_SIZE 25000
+
+/* Define the below to get additional debug */
+#undef AMI_PLOTTER_DEBUG
+
+void ami_init_layers(struct gui_globals *gg, ULONG width, ULONG height, bool force32bit)
+{
+ /* init shared bitmaps *
+ * Height is set to screen width to give enough space for thumbnails *
+ * Also applies to the further gfx/layers functions and memory below */
+
+ int depth = 32;
+ struct BitMap *friend = NULL;
+
+ if(force32bit == false) depth = GetBitMapAttr(scrn->RastPort.BitMap, BMA_DEPTH);
+ LOG("Screen depth = %d", depth);
+
+ if(depth < 16) {
+ gg->palette_mapped = true;
+ } else {
+ gg->palette_mapped = false;
+ }
+
+#ifndef __amigaos4__
+#warning OS3 locked to palette-mapped modes
+ gg->palette_mapped = true;
+ if(depth > 8) depth = 8;
+#endif
+
+ /* Probably need to fix this next line */
+ if(gg->palette_mapped == true) nsoption_set_bool(font_antialiasing, false);
+
+ if(!width) width = nsoption_int(redraw_tile_size_x);
+ if(!height) height = nsoption_int(redraw_tile_size_y);
+ gg->width = width;
+ gg->height = height;
+
+ gg->layerinfo = NewLayerInfo();
+ gg->areabuf = AllocVecTagList(AREA_SIZE, NULL);
+
+#ifdef __amigaos4__
+ gg->tmprasbuf = AllocVecTagList(width * height, NULL);
+#else
+ /* OS3/AGA requires this to be in chip mem. RTG would probably rather it wasn't. */
+ gg->tmprasbuf = AllocVec(width * height, MEMF_CHIP);
+#endif
+
+ /* Friend BitMaps are weird.
+ * For OS4, we shouldn't use a friend BitMap here (see below).
+ * For OS3 AGA, we get no display blitted if we use a friend BitMap,
+ * however on RTG it seems to be a benefit.
+ */
+#ifndef __amigaos4__
+ if(nsoption_bool(friend_bitmap) == true) {
+ friend = scrn->RastPort.BitMap;
+ } else {
+ /* Force friend BitMaps on for obvious RTG screens under OS3.
+ * If we get a bit smarter about this we can lose the user option. */
+ if((depth >= 16) && (force32bit == false)) friend = scrn->RastPort.BitMap;
+ }
+#endif
+
+ if(gg->palette_mapped == true) {
+ gg->bm = AllocBitMap(width, height, depth, 0, friend);
+ } else {
+#ifdef __amigaos4__
+ /* Screen depth is reported as 24 even when it's actually 32-bit.
+ * We get freezes and other problems on OS4 if we befriend at any
+ * other depths, hence this check.
+ * \todo use friend BitMaps but avoid CompositeTags() at non-32-bit
+ * as that seems to be the cause of the problems.
+ */
+ if((depth >= 24) && (force32bit == false)) friend = scrn->RastPort.BitMap;
+#endif
+ gg->bm = ami_rtg_allocbitmap(width, height, 32, 0, friend, RGBFB_A8R8G8B8);
+ }
+
+ if(!gg->bm) amiga_warn_user("NoMemory","");
+
+ gg->rp = AllocVecTagList(sizeof(struct RastPort), NULL);
+ if(!gg->rp) amiga_warn_user("NoMemory","");
+
+ InitRastPort(gg->rp);
+ gg->rp->BitMap = gg->bm;
+
+ SetDrMd(gg->rp,BGBACKFILL);
+
+ gg->rp->Layer = CreateUpfrontLayer(gg->layerinfo,gg->rp->BitMap,0,0,
+ width-1, height-1, LAYERSIMPLE, NULL);
+
+ InstallLayerHook(gg->rp->Layer,LAYERS_NOBACKFILL);
+
+ gg->rp->AreaInfo = AllocVecTagList(sizeof(struct AreaInfo), NULL);
+ if((!gg->areabuf) || (!gg->rp->AreaInfo)) amiga_warn_user("NoMemory","");
+
+ InitArea(gg->rp->AreaInfo,gg->areabuf, AREA_SIZE/5);
+
+ gg->rp->TmpRas = AllocVecTagList(sizeof(struct TmpRas), NULL);
+ if((!gg->tmprasbuf) || (!gg->rp->TmpRas)) amiga_warn_user("NoMemory","");
+
+ InitTmpRas(gg->rp->TmpRas, gg->tmprasbuf, width*height);
+
+ if((gg->palette_mapped == true) && (pool_pens == NULL)) {
+ pool_pens = ami_misc_itempool_create(sizeof(struct ami_plot_pen));
+ }
+
+ gg->apen = 0x00000000;
+ gg->open = 0x00000000;
+ gg->apen_num = -1;
+ gg->open_num = -1;
+
+ init_layers_count++;
+ LOG("Layer initialised (total: %d)", init_layers_count);
+}
+
+void ami_free_layers(struct gui_globals *gg)
+{
+ init_layers_count--;
+
+ if((init_layers_count == 0) && (pool_pens != NULL)) {
+ ami_misc_itempool_delete(pool_pens);
+ pool_pens = NULL;
+ }
+
+ if(gg->rp) {
+ DeleteLayer(0,gg->rp->Layer);
+ FreeVec(gg->rp->TmpRas);
+ FreeVec(gg->rp->AreaInfo);
+ FreeVec(gg->rp);
+ }
+
+ FreeVec(gg->tmprasbuf);
+ FreeVec(gg->areabuf);
+ DisposeLayerInfo(gg->layerinfo);
+ if(gg->palette_mapped == false) {
+ if(gg->bm) ami_rtg_freebitmap(gg->bm);
+ } else {
+ if(gg->bm) FreeBitMap(gg->bm);
+ }
+}
+
+void ami_clearclipreg(struct gui_globals *gg)
+{
+ struct Region *reg = NULL;
+
+ reg = InstallClipRegion(gg->rp->Layer,NULL);
+ if(reg) DisposeRegion(reg);
+
+ gg->rect.MinX = 0;
+ gg->rect.MinY = 0;
+ gg->rect.MaxX = scrn->Width-1;
+ gg->rect.MaxY = scrn->Height-1;
+}
+
+static ULONG ami_plot_obtain_pen(struct MinList *shared_pens, ULONG colr)
+{
+ struct ami_plot_pen *node;
+ LONG pen = ObtainBestPenA(scrn->ViewPort.ColorMap,
+ (colr & 0x000000ff) << 24,
+ (colr & 0x0000ff00) << 16,
+ (colr & 0x00ff0000) << 8,
+ NULL);
+
+ if(pen == -1) LOG("WARNING: Cannot allocate pen for ABGR:%lx", colr);
+
+ if((shared_pens != NULL) && (pool_pens != NULL)) {
+ if((node = (struct ami_plot_pen *)ami_misc_itempool_alloc(pool_pens, sizeof(struct ami_plot_pen)))) {
+ node->pen = pen;
+ AddTail((struct List *)shared_pens, (struct Node *)node);
+ }
+ } else {
+ /* Immediately release the pen if we can't keep track of it. */
+ ReleasePen(scrn->ViewPort.ColorMap, pen);
+ }
+ return pen;
+}
+
+void ami_plot_release_pens(struct MinList *shared_pens)
+{
+ struct ami_plot_pen *node;
+ struct ami_plot_pen *nnode;
+
+ if(shared_pens == NULL) return;
+ if(IsMinListEmpty(shared_pens)) return;
+ node = (struct ami_plot_pen *)GetHead((struct List *)shared_pens);
+
+ do {
+ nnode = (struct ami_plot_pen *)GetSucc((struct Node *)node);
+ ReleasePen(scrn->ViewPort.ColorMap, node->pen);
+ Remove((struct Node *)node);
+ ami_misc_itempool_free(pool_pens, node, sizeof(struct ami_plot_pen));
+ } while((node = nnode));
+
+ glob->apen = 0x00000000;
+ glob->open = 0x00000000;
+ glob->apen_num = -1;
+ glob->open_num = -1;
+}
+
+static void ami_plot_setapen(struct RastPort *rp, ULONG colr)
+{
+ if(glob->apen == colr) return;
+
+#ifdef __amigaos4__
+ if(glob->palette_mapped == false) {
+ SetRPAttrs(rp, RPTAG_APenColor,
+ ns_color_to_nscss(colr),
+ TAG_DONE);
+ } else
+#endif
+ {
+ LONG pen = ami_plot_obtain_pen(glob->shared_pens, colr);
+ if((pen != -1) && (pen != glob->apen_num)) SetAPen(rp, pen);
+ }
+
+ glob->apen = colr;
+}
+
+static void ami_plot_setopen(struct RastPort *rp, ULONG colr)
+{
+ if(glob->open == colr) return;
+
+#ifdef __amigaos4__
+ if(glob->palette_mapped == false) {
+ SetRPAttrs(rp, RPTAG_OPenColor,
+ ns_color_to_nscss(colr),
+ TAG_DONE);
+ } else
+#endif
+ {
+ LONG pen = ami_plot_obtain_pen(glob->shared_pens, colr);
+ if((pen != -1) && (pen != glob->open_num)) SetOPen(rp, pen);
+ }
+
+ glob->open = colr;
+}
+
+void ami_plot_clear_bbox(struct RastPort *rp, struct IBox *bbox)
+{
+ if((bbox == NULL) || (rp == NULL)) return;
+
+ EraseRect(rp, bbox->Left, bbox->Top,
+ bbox->Width + bbox->Left, bbox->Height + bbox->Top);
+}
+
+
+static bool ami_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ #ifdef AMI_PLOTTER_DEBUG
+ LOG("[ami_plotter] Entered ami_rectangle()");
+ #endif
+
+ if (style->fill_type != PLOT_OP_TYPE_NONE) {
+ ami_plot_setapen(glob->rp, style->fill_colour);
+ RectFill(glob->rp, x0, y0, x1-1, y1-1);
+ }
+
+ if (style->stroke_type != PLOT_OP_TYPE_NONE) {
+ glob->rp->PenWidth = style->stroke_width;
+ glob->rp->PenHeight = style->stroke_width;
+
+ switch (style->stroke_type) {
+ case PLOT_OP_TYPE_SOLID: /**< Solid colour */
+ default:
+ glob->rp->LinePtrn = PATT_LINE;
+ break;
+
+ case PLOT_OP_TYPE_DOT: /**< Dotted plot */
+ glob->rp->LinePtrn = PATT_DOT;
+ break;
+
+ case PLOT_OP_TYPE_DASH: /**< dashed plot */
+ glob->rp->LinePtrn = PATT_DASH;
+ break;
+ }
+
+ ami_plot_setapen(glob->rp, style->stroke_colour);
+ Move(glob->rp, x0,y0);
+ Draw(glob->rp, x1, y0);
+ Draw(glob->rp, x1, y1);
+ Draw(glob->rp, x0, y1);
+ Draw(glob->rp, x0, y0);
+
+ glob->rp->PenWidth = 1;
+ glob->rp->PenHeight = 1;
+ glob->rp->LinePtrn = PATT_LINE;
+ }
+
+ return true;
+}
+
+static bool ami_line(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ #ifdef AMI_PLOTTER_DEBUG
+ LOG("[ami_plotter] Entered ami_line()");
+ #endif
+
+ glob->rp->PenWidth = style->stroke_width;
+ glob->rp->PenHeight = style->stroke_width;
+
+ switch (style->stroke_type) {
+ case PLOT_OP_TYPE_SOLID: /**< Solid colour */
+ default:
+ glob->rp->LinePtrn = PATT_LINE;
+ break;
+
+ case PLOT_OP_TYPE_DOT: /**< Doted plot */
+ glob->rp->LinePtrn = PATT_DOT;
+ break;
+
+ case PLOT_OP_TYPE_DASH: /**< dashed plot */
+ glob->rp->LinePtrn = PATT_DASH;
+ break;
+ }
+
+ ami_plot_setapen(glob->rp, style->stroke_colour);
+ Move(glob->rp,x0,y0);
+ Draw(glob->rp,x1,y1);
+
+ glob->rp->PenWidth = 1;
+ glob->rp->PenHeight = 1;
+ glob->rp->LinePtrn = PATT_LINE;
+
+ return true;
+}
+
+static bool ami_polygon(const int *p, unsigned int n, const plot_style_t *style)
+{
+ #ifdef AMI_PLOTTER_DEBUG
+ LOG("[ami_plotter] Entered ami_polygon()");
+ #endif
+
+ ami_plot_setapen(glob->rp, style->fill_colour);
+
+ if(AreaMove(glob->rp,p[0],p[1]) == -1)
+ LOG("AreaMove: vector list full");
+
+ for(uint32 k = 1; k < n; k++) {
+ if(AreaDraw(glob->rp,p[k*2],p[(k*2)+1]) == -1)
+ LOG("AreaDraw: vector list full");
+ }
+
+ if(AreaEnd(glob->rp) == -1)
+ LOG("AreaEnd: error");
+
+ return true;
+}
+
+
+static bool ami_clip(const struct rect *clip)
+{
+ #ifdef AMI_PLOTTER_DEBUG
+ LOG("[ami_plotter] Entered ami_clip()");
+ #endif
+
+ struct Region *reg = NULL;
+
+ if(glob->rp->Layer)
+ {
+ reg = NewRegion();
+
+ glob->rect.MinX = clip->x0;
+ glob->rect.MinY = clip->y0;
+ glob->rect.MaxX = clip->x1-1;
+ glob->rect.MaxY = clip->y1-1;
+
+ OrRectRegion(reg,&glob->rect);
+
+ reg = InstallClipRegion(glob->rp->Layer,reg);
+
+ if(reg) DisposeRegion(reg);
+ }
+
+ return true;
+}
+
+static bool ami_text(int x, int y, const char *text, size_t length,
+ const plot_font_style_t *fstyle)
+{
+ #ifdef AMI_PLOTTER_DEBUG
+ LOG("[ami_plotter] Entered ami_text()");
+ #endif
+
+ if(__builtin_expect(ami_nsfont == NULL, 0)) return false;
+
+ ami_plot_setapen(glob->rp, fstyle->foreground);
+ ami_nsfont->text(glob->rp, text, length, fstyle, x, y, nsoption_bool(font_antialiasing));
+
+ return true;
+}
+
+static bool ami_disc(int x, int y, int radius, const plot_style_t *style)
+{
+ #ifdef AMI_PLOTTER_DEBUG
+ LOG("[ami_plotter] Entered ami_disc()");
+ #endif
+
+ if (style->fill_type != PLOT_OP_TYPE_NONE) {
+ ami_plot_setapen(glob->rp, style->fill_colour);
+ AreaCircle(glob->rp,x,y,radius);
+ AreaEnd(glob->rp);
+ }
+
+ if (style->stroke_type != PLOT_OP_TYPE_NONE) {
+ ami_plot_setapen(glob->rp, style->stroke_colour);
+ DrawEllipse(glob->rp,x,y,radius,radius);
+ }
+
+ return true;
+}
+
+static void ami_arc_gfxlib(int x, int y, int radius, int angle1, int angle2)
+{
+ double angle1_r = (double)(angle1) * (M_PI / 180.0);
+ double angle2_r = (double)(angle2) * (M_PI / 180.0);
+ double angle, b, c;
+ double step = 0.1; //(angle2_r - angle1_r) / ((angle2_r - angle1_r) * (double)radius);
+ int x0, y0, x1, y1;
+
+ x0 = x;
+ y0 = y;
+
+ b = angle1_r;
+ c = angle2_r;
+
+ x1 = (int)(cos(b) * (double)radius);
+ y1 = (int)(sin(b) * (double)radius);
+ Move(glob->rp, x0 + x1, y0 - y1);
+
+ for(angle = (b + step); angle <= c; angle += step) {
+ x1 = (int)(cos(angle) * (double)radius);
+ y1 = (int)(sin(angle) * (double)radius);
+ Draw(glob->rp, x0 + x1, y0 - y1);
+ }
+}
+
+static bool ami_arc(int x, int y, int radius, int angle1, int angle2, const plot_style_t *style)
+{
+ #ifdef AMI_PLOTTER_DEBUG
+ LOG("[ami_plotter] Entered ami_arc()");
+ #endif
+
+ if (angle2 < angle1) angle2 += 360;
+
+ ami_plot_setapen(glob->rp, style->fill_colour);
+ ami_arc_gfxlib(x, y, radius, angle1, angle2);
+
+ return true;
+}
+
+static bool ami_bitmap(int x, int y, int width, int height, struct bitmap *bitmap)
+{
+ #ifdef AMI_PLOTTER_DEBUG
+ LOG("[ami_plotter] Entered ami_bitmap()");
+ #endif
+
+ struct BitMap *tbm;
+
+ if(!width || !height) return true;
+
+ if(((x + width) < glob->rect.MinX) ||
+ ((y + height) < glob->rect.MinY) ||
+ (x > glob->rect.MaxX) ||
+ (y > glob->rect.MaxY))
+ return true;
+
+ tbm = ami_bitmap_get_native(bitmap, width, height, glob->rp->BitMap);
+ if(!tbm) return true;
+
+ #ifdef AMI_PLOTTER_DEBUG
+ LOG("[ami_plotter] ami_bitmap() got native bitmap");
+ #endif
+
+#ifdef __amigaos4__
+ if(__builtin_expect((GfxBase->LibNode.lib_Version >= 53) && (glob->palette_mapped == false) &&
+ (nsoption_bool(direct_render) == false), 1)) {
+ uint32 comptype = COMPOSITE_Src_Over_Dest;
+ uint32 compflags = COMPFLAG_IgnoreDestAlpha;
+ if(amiga_bitmap_get_opaque(bitmap)) {
+ compflags |= COMPFLAG_SrcAlphaOverride;
+ comptype = COMPOSITE_Src;
+ }
+
+ CompositeTags(comptype,tbm,glob->rp->BitMap,
+ COMPTAG_Flags, compflags,
+ COMPTAG_DestX,glob->rect.MinX,
+ COMPTAG_DestY,glob->rect.MinY,
+ COMPTAG_DestWidth,glob->rect.MaxX - glob->rect.MinX + 1,
+ COMPTAG_DestHeight,glob->rect.MaxY - glob->rect.MinY + 1,
+ COMPTAG_SrcWidth,width,
+ COMPTAG_SrcHeight,height,
+ COMPTAG_OffsetX,x,
+ COMPTAG_OffsetY,y,
+ COMPTAG_FriendBitMap, scrn->RastPort.BitMap,
+ TAG_DONE);
+ }
+ else
+#endif
+ {
+ ULONG tag, tag_data, minterm = 0xc0;
+
+ if(glob->palette_mapped == false) {
+ tag = BLITA_UseSrcAlpha;
+ tag_data = !amiga_bitmap_get_opaque(bitmap);
+ minterm = 0xc0;
+ } else {
+ tag = BLITA_MaskPlane;
+ if((tag_data = (ULONG)ami_bitmap_get_mask(bitmap, width, height, tbm)))
+ minterm = MINTERM_SRCMASK;
+ }
+#ifdef __amigaos4__
+ BltBitMapTags(BLITA_Width,width,
+ BLITA_Height,height,
+ BLITA_Source,tbm,
+ BLITA_Dest,glob->rp,
+ BLITA_DestX,x,
+ BLITA_DestY,y,
+ BLITA_SrcType,BLITT_BITMAP,
+ BLITA_DestType,BLITT_RASTPORT,
+ BLITA_Minterm, minterm,
+ tag, tag_data,
+ TAG_DONE);
+#else
+ if(tag_data) {
+ BltMaskBitMapRastPort(tbm, 0, 0, glob->rp, x, y, width, height, minterm, tag_data);
+ } else {
+ BltBitMapRastPort(tbm, 0, 0, glob->rp, x, y, width, height, 0xc0);
+ }
+#endif
+ }
+
+ if((ami_bitmap_is_nativebm(bitmap, tbm) == false)) {
+ ami_rtg_freebitmap(tbm);
+ }
+
+ return true;
+}
+
+static bool ami_bitmap_tile(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg,
+ bitmap_flags_t flags)
+{
+ #ifdef AMI_PLOTTER_DEBUG
+ LOG("[ami_plotter] Entered ami_bitmap_tile()");
+ #endif
+
+ int xf,yf,xm,ym,oy,ox;
+ struct BitMap *tbm = NULL;
+ struct Hook *bfh = NULL;
+ struct bfbitmap bfbm;
+ bool repeat_x = (flags & BITMAPF_REPEAT_X);
+ bool repeat_y = (flags & BITMAPF_REPEAT_Y);
+
+ if((width == 0) || (height == 0)) return true;
+
+ if(!(repeat_x || repeat_y))
+ return ami_bitmap(x, y, width, height, bitmap);
+
+ /* If it is a one pixel transparent image, we are wasting our time */
+ if((amiga_bitmap_get_opaque(bitmap) == false) &&
+ (bitmap_get_width(bitmap) == 1) && (bitmap_get_height(bitmap) == 1))
+ return true;
+
+ tbm = ami_bitmap_get_native(bitmap,width,height,glob->rp->BitMap);
+ if(!tbm) return true;
+
+ ox = x;
+ oy = y;
+
+ /* get left most tile position */
+ for (; ox > 0; ox -= width)
+ ;
+
+ /* get top most tile position */
+ for (; oy > 0; oy -= height)
+ ;
+
+ if(ox<0) ox = -ox;
+ if(oy<0) oy = -oy;
+
+ if(repeat_x)
+ {
+ xf = glob->rect.MaxX;
+ xm = glob->rect.MinX;
+ }
+ else
+ {
+ xf = x + width;
+ xm = x;
+ }
+
+ if(repeat_y)
+ {
+ yf = glob->rect.MaxY;
+ ym = glob->rect.MinY;
+ }
+ else
+ {
+ yf = y + height;
+ ym = y;
+ }
+#ifdef __amigaos4__
+ if(amiga_bitmap_get_opaque(bitmap))
+ {
+ bfh = CreateBackFillHook(BFHA_BitMap,tbm,
+ BFHA_Width,width,
+ BFHA_Height,height,
+ BFHA_OffsetX,ox,
+ BFHA_OffsetY,oy,
+ TAG_DONE);
+ }
+ else
+#endif
+ {
+ bfbm.bm = tbm;
+ bfbm.width = width;
+ bfbm.height = height;
+ bfbm.offsetx = ox;
+ bfbm.offsety = oy;
+ bfbm.mask = ami_bitmap_get_mask(bitmap, width, height, tbm);
+ bfh = ami_misc_allocvec_clear(sizeof(struct Hook), 0); /* NB: Was not MEMF_PRIVATE */
+ bfh->h_Entry = (HOOKFUNC)ami_bitmap_tile_hook;
+ bfh->h_SubEntry = 0;
+ bfh->h_Data = &bfbm;
+ }
+
+ InstallLayerHook(glob->rp->Layer,bfh);
+ EraseRect(glob->rp,xm,ym,xf,yf);
+ InstallLayerHook(glob->rp->Layer,LAYERS_NOBACKFILL);
+
+#ifdef __amigaos4__
+ if(amiga_bitmap_get_opaque(bitmap)) DeleteBackFillHook(bfh);
+ else
+#endif
+ FreeVec(bfh);
+
+ if((ami_bitmap_is_nativebm(bitmap, tbm) == false)) {
+ /**\todo is this logic logical? */
+ ami_rtg_freebitmap(tbm);
+ }
+
+ return true;
+}
+
+HOOKF(void, ami_bitmap_tile_hook, struct RastPort *, rp, struct BackFillMessage *)
+{
+ int xf,yf;
+ struct bfbitmap *bfbm = (struct bfbitmap *)hook->h_Data;
+
+ /* tile down and across to extents (msg->Bounds.MinX)*/
+ for (xf = -bfbm->offsetx; xf < msg->Bounds.MaxX; xf += bfbm->width) {
+ for (yf = -bfbm->offsety; yf < msg->Bounds.MaxY; yf += bfbm->height) {
+#ifdef __amigaos4__
+ if(__builtin_expect((GfxBase->LibNode.lib_Version >= 53) &&
+ (glob->palette_mapped == false), 1)) {
+ CompositeTags(COMPOSITE_Src_Over_Dest, bfbm->bm, rp->BitMap,
+ COMPTAG_Flags, COMPFLAG_IgnoreDestAlpha,
+ COMPTAG_DestX, msg->Bounds.MinX,
+ COMPTAG_DestY, msg->Bounds.MinY,
+ COMPTAG_DestWidth, msg->Bounds.MaxX - msg->Bounds.MinX + 1,
+ COMPTAG_DestHeight, msg->Bounds.MaxY - msg->Bounds.MinY + 1,
+ COMPTAG_SrcWidth, bfbm->width,
+ COMPTAG_SrcHeight, bfbm->height,
+ COMPTAG_OffsetX, xf,
+ COMPTAG_OffsetY, yf,
+ COMPTAG_FriendBitMap, scrn->RastPort.BitMap,
+ TAG_DONE);
+ }
+ else
+#endif
+ {
+ ULONG tag, tag_data, minterm = 0xc0;
+
+ if(glob->palette_mapped == false) {
+ tag = BLITA_UseSrcAlpha;
+ tag_data = TRUE;
+ minterm = 0xc0;
+ } else {
+ tag = BLITA_MaskPlane;
+ if((tag_data = (ULONG)bfbm->mask))
+ minterm = MINTERM_SRCMASK;
+ }
+#ifdef __amigaos4__
+ BltBitMapTags(BLITA_Width, bfbm->width,
+ BLITA_Height, bfbm->height,
+ BLITA_Source, bfbm->bm,
+ BLITA_Dest, rp,
+ BLITA_DestX, xf,
+ BLITA_DestY, yf,
+ BLITA_SrcType, BLITT_BITMAP,
+ BLITA_DestType, BLITT_RASTPORT,
+ BLITA_Minterm, minterm,
+ tag, tag_data,
+ TAG_DONE);
+#else
+ if(tag_data) {
+ BltMaskBitMapRastPort(bfbm->bm, 0, 0, rp, xf, yf,
+ bfbm->width, bfbm->height, minterm, tag_data);
+ } else {
+ BltBitMapRastPort(bfbm->bm, 0, 0, rp, xf, yf,
+ bfbm->width, bfbm->height, minterm);
+ }
+#endif
+ }
+ }
+ }
+}
+
+static void ami_bezier(struct bez_point *a, struct bez_point *b, struct bez_point *c,
+ struct bez_point *d, double t, struct bez_point *p) {
+ p->x = pow((1 - t), 3) * a->x + 3 * t * pow((1 -t), 2) * b->x + 3 * (1-t) * pow(t, 2)* c->x + pow (t, 3)* d->x;
+ p->y = pow((1 - t), 3) * a->y + 3 * t * pow((1 -t), 2) * b->y + 3 * (1-t) * pow(t, 2)* c->y + pow (t, 3)* d->y;
+}
+
+static bool ami_path(const float *p, unsigned int n, colour fill, float width,
+ colour c, const float transform[6])
+{
+ unsigned int i;
+ struct bez_point start_p = {0, 0}, cur_p = {0, 0}, p_a, p_b, p_c, p_r;
+
+ #ifdef AMI_PLOTTER_DEBUG
+ LOG("[ami_plotter] Entered ami_path()");
+ #endif
+
+ if (n == 0)
+ return true;
+
+ if (p[0] != PLOTTER_PATH_MOVE) {
+ LOG("Path does not start with move");
+ return false;
+ }
+
+ if (fill != NS_TRANSPARENT) {
+ ami_plot_setapen(glob->rp, fill);
+ if (c != NS_TRANSPARENT)
+ ami_plot_setopen(glob->rp, c);
+ } else {
+ if (c != NS_TRANSPARENT) {
+ ami_plot_setapen(glob->rp, c);
+ } else {
+ return true; /* wholly transparent */
+ }
+ }
+
+ /* Construct path */
+ for (i = 0; i < n; ) {
+ if (p[i] == PLOTTER_PATH_MOVE) {
+ if (fill != NS_TRANSPARENT) {
+ if(AreaMove(glob->rp, p[i+1], p[i+2]) == -1)
+ LOG("AreaMove: vector list full");
+ } else {
+ Move(glob->rp, p[i+1], p[i+2]);
+ }
+ /* Keep track for future Bezier curves/closes etc */
+ start_p.x = p[i+1];
+ start_p.y = p[i+2];
+ cur_p.x = start_p.x;
+ cur_p.y = start_p.y;
+ i += 3;
+ } else if (p[i] == PLOTTER_PATH_CLOSE) {
+ if (fill != NS_TRANSPARENT) {
+ if(AreaEnd(glob->rp) == -1)
+ LOG("AreaEnd: error");
+ } else {
+ Draw(glob->rp, start_p.x, start_p.y);
+ }
+ i++;
+ } else if (p[i] == PLOTTER_PATH_LINE) {
+ if (fill != NS_TRANSPARENT) {
+ if(AreaDraw(glob->rp, p[i+1], p[i+2]) == -1)
+ LOG("AreaDraw: vector list full");
+ } else {
+ Draw(glob->rp, p[i+1], p[i+2]);
+ }
+ cur_p.x = p[i+1];
+ cur_p.y = p[i+2];
+ i += 3;
+ } else if (p[i] == PLOTTER_PATH_BEZIER) {
+ p_a.x = p[i+1];
+ p_a.y = p[i+2];
+ p_b.x = p[i+3];
+ p_b.y = p[i+4];
+ p_c.x = p[i+5];
+ p_c.y = p[i+6];
+
+ for(double t = 0.0; t <= 1.0; t += 0.1) {
+ ami_bezier(&cur_p, &p_a, &p_b, &p_c, t, &p_r);
+ if (fill != NS_TRANSPARENT) {
+ if(AreaDraw(glob->rp, p_r.x, p_r.y) == -1)
+ LOG("AreaDraw: vector list full");
+ } else {
+ Draw(glob->rp, p_r.x, p_r.y);
+ }
+ }
+ cur_p.x = p_c.x;
+ cur_p.y = p_c.y;
+ i += 7;
+ } else {
+ LOG("bad path command %f", p[i]);
+ /* End path for safety if using Area commands */
+ if (fill != NS_TRANSPARENT) {
+ AreaEnd(glob->rp);
+ BNDRYOFF(glob->rp);
+ }
+ return false;
+ }
+ }
+ if (fill != NS_TRANSPARENT)
+ BNDRYOFF(glob->rp);
+
+ return true;
+}
+
+bool ami_plot_screen_is_palettemapped(void)
+{
+ return glob->palette_mapped;
+}
+
+struct plotter_table plot;
+const struct plotter_table amiplot = {
+ .rectangle = ami_rectangle,
+ .line = ami_line,
+ .polygon = ami_polygon,
+ .clip = ami_clip,
+ .text = ami_text,
+ .disc = ami_disc,
+ .arc = ami_arc,
+ .bitmap = ami_bitmap_tile,
+ .path = ami_path,
+ .option_knockout = true,
+};
+
diff --git a/frontends/amiga/plotters.h b/frontends/amiga/plotters.h
new file mode 100644
index 000000000..7baa21fb5
--- /dev/null
+++ b/frontends/amiga/plotters.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2008, 2009 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_PLOTTERS_H
+#define AMIGA_PLOTTERS_H
+#include "desktop/plotters.h"
+#include <proto/layers.h>
+#include <proto/graphics.h>
+
+struct IBox;
+
+struct gui_globals
+{
+ struct BitMap *bm;
+ struct RastPort *rp;
+ struct Layer_Info *layerinfo;
+ APTR areabuf;
+ APTR tmprasbuf;
+ struct Rectangle rect;
+ struct MinList *shared_pens;
+ bool palette_mapped;
+ ULONG apen;
+ ULONG open;
+ LONG apen_num;
+ LONG open_num;
+ int width; /* size of bm and */
+ int height; /* associated memory */
+};
+
+extern const struct plotter_table amiplot;
+
+void ami_init_layers(struct gui_globals *gg, ULONG width, ULONG height, bool force32bit);
+void ami_free_layers(struct gui_globals *gg);
+void ami_clearclipreg(struct gui_globals *gg);
+void ami_plot_clear_bbox(struct RastPort *rp, struct IBox *bbox);
+void ami_plot_release_pens(struct MinList *shared_pens);
+bool ami_plot_screen_is_palettemapped(void);
+
+struct gui_globals *glob;
+#endif
diff --git a/frontends/amiga/plugin_hack.c b/frontends/amiga/plugin_hack.c
new file mode 100644
index 000000000..21f4da142
--- /dev/null
+++ b/frontends/amiga/plugin_hack.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2011 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Plugin=>external program handler (implementation)
+*/
+
+#include "amiga/os3support.h"
+
+#include <stdlib.h>
+
+#include "amiga/filetype.h"
+#include "amiga/plugin_hack.h"
+#include "content/content_protected.h"
+#include "content/hlcache.h"
+#include "desktop/plotters.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+
+#include <proto/dos.h>
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <proto/utility.h>
+
+typedef struct amiga_plugin_hack_content {
+ struct content base;
+} amiga_plugin_hack_content;
+
+static nserror amiga_plugin_hack_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c);
+static bool amiga_plugin_hack_convert(struct content *c);
+static void amiga_plugin_hack_reformat(struct content *c, int width, int height);
+static void amiga_plugin_hack_destroy(struct content *c);
+static bool amiga_plugin_hack_redraw(struct content *c,
+ struct content_redraw_data *data, const struct rect *clip,
+ const struct redraw_context *ctx);
+static void amiga_plugin_hack_open(struct content *c, struct browser_window *bw,
+ struct content *page, struct object_params *params);
+static void amiga_plugin_hack_close(struct content *c);
+static nserror amiga_plugin_hack_clone(const struct content *old, struct content **newc);
+static content_type amiga_plugin_hack_content_type(void);
+
+static const content_handler amiga_plugin_hack_content_handler = {
+ .create = amiga_plugin_hack_create,
+ .data_complete = amiga_plugin_hack_convert,
+ .reformat = amiga_plugin_hack_reformat,
+ .destroy = amiga_plugin_hack_destroy,
+ .redraw = amiga_plugin_hack_redraw,
+ .open = amiga_plugin_hack_open,
+ .close = amiga_plugin_hack_close,
+ .clone = amiga_plugin_hack_clone,
+ .type = amiga_plugin_hack_content_type,
+ .no_share = false,
+};
+
+nserror amiga_plugin_hack_init(void)
+{
+ struct Node *node = NULL;
+ lwc_string *type;
+ nserror error;
+
+ do {
+ node = ami_mime_has_cmd(&type, node);
+
+ if(node)
+ {
+ LOG("plugin_hack registered %s", lwc_string_data(type));
+
+ error = content_factory_register_handler(
+ lwc_string_data(type),
+ &amiga_plugin_hack_content_handler);
+
+ if (error != NSERROR_OK)
+ return error;
+ }
+
+ }while (node != NULL);
+
+ return NSERROR_OK;
+}
+
+nserror amiga_plugin_hack_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c)
+{
+ amiga_plugin_hack_content *plugin;
+ nserror error;
+
+ plugin = calloc(1, sizeof(amiga_plugin_hack_content));
+ if (plugin == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__init(&plugin->base, handler, imime_type, params,
+ llcache, fallback_charset, quirks);
+ if (error != NSERROR_OK) {
+ free(plugin);
+ return error;
+ }
+
+ *c = (struct content *) plugin;
+
+ return NSERROR_OK;
+}
+
+bool amiga_plugin_hack_convert(struct content *c)
+{
+ LOG("amiga_plugin_hack_convert");
+
+ content_set_ready(c);
+ content_set_done(c);
+
+ content_set_status(c, "");
+
+ return true;
+}
+
+void amiga_plugin_hack_destroy(struct content *c)
+{
+ amiga_plugin_hack_content *plugin = (amiga_plugin_hack_content *) c;
+
+ LOG("amiga_plugin_hack_destroy %p", plugin);
+
+ return;
+}
+
+bool amiga_plugin_hack_redraw(struct content *c,
+ struct content_redraw_data *data, const struct rect *clip,
+ const struct redraw_context *ctx)
+{
+ plot_style_t pstyle = {
+ .fill_type = PLOT_OP_TYPE_SOLID,
+ .fill_colour = 0xffffff,
+ .stroke_colour = 0x000000,
+ .stroke_width = 1,
+ };
+
+ LOG("amiga_plugin_hack_redraw");
+
+ ctx->plot->rectangle(data->x, data->y, data->x + data->width,
+ data->y + data->height, &pstyle);
+
+ return ctx->plot->text(data->x, data->y+20,
+ lwc_string_data(content__get_mime_type(c)),
+ lwc_string_length(content__get_mime_type(c)),
+ plot_style_font);
+}
+
+/**
+ * Handle a window containing a CONTENT_PLUGIN being opened.
+ *
+ * \param c content that has been opened
+ * \param bw browser window containing the content
+ * \param page content of type CONTENT_HTML containing c, or 0 if not an
+ * object within a page
+ * \param params object parameters, or 0 if not an object
+ */
+void amiga_plugin_hack_open(struct content *c, struct browser_window *bw,
+ struct content *page, struct object_params *params)
+{
+ LOG("amiga_plugin_hack_open %s", nsurl_access(content_get_url(c)));
+
+ if(c)
+ {
+ /* TODO: Do we need valid dimensions at this point? */
+ c->width = 0;
+ c->height = 0;
+ }
+
+ return;
+}
+
+void amiga_plugin_hack_close(struct content *c)
+{
+ LOG("amiga_plugin_hack_close");
+ return;
+}
+
+void amiga_plugin_hack_reformat(struct content *c, int width, int height)
+{
+ LOG("amiga_plugin_hack_reformat");
+
+ c->width = width;
+ c->height = height;
+
+ return;
+}
+
+nserror amiga_plugin_hack_clone(const struct content *old, struct content **newc)
+{
+ amiga_plugin_hack_content *plugin;
+ nserror error;
+
+ LOG("amiga_plugin_hack_clone");
+
+ plugin = calloc(1, sizeof(amiga_plugin_hack_content));
+ if (plugin == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__clone(old, &plugin->base);
+ if (error != NSERROR_OK) {
+ content_destroy(&plugin->base);
+ return error;
+ }
+
+ /* We "clone" the old content by replaying conversion */
+ if (old->status == CONTENT_STATUS_READY ||
+ old->status == CONTENT_STATUS_DONE) {
+ if (amiga_plugin_hack_convert(&plugin->base) == false) {
+ content_destroy(&plugin->base);
+ return NSERROR_CLONE_FAILED;
+ }
+ }
+
+ *newc = (struct content *) plugin;
+
+ return NSERROR_OK;
+}
+
+content_type amiga_plugin_hack_content_type(void)
+{
+ return CONTENT_PLUGIN;
+}
+
+void amiga_plugin_hack_execute(struct hlcache_handle *c)
+{
+ lwc_string *plugincmd;
+ char *full_cmd;
+ BPTR in, out;
+
+ if(c == NULL) return;
+
+ plugincmd = ami_mime_content_to_cmd(c);
+ if(plugincmd == NULL) return;
+
+ full_cmd = ASPrintf("%s %s", lwc_string_data(plugincmd), nsurl_access(hlcache_handle_get_url(c)));
+
+ if(full_cmd)
+ {
+#ifdef __amigaos4__
+ LOG("Attempting to execute %s", full_cmd);
+
+ in = Open("NIL:", MODE_OLDFILE);
+ out = Open("NIL:", MODE_NEWFILE);
+
+ SystemTags(full_cmd,
+ SYS_Input, in,
+ SYS_Output, out,
+ SYS_Error, out,
+ SYS_Asynch, TRUE,
+ NP_Name, "NetSurf External Process",
+ TAG_DONE);
+
+ FreeVec(full_cmd);
+#endif
+ }
+}
diff --git a/frontends/amiga/plugin_hack.h b/frontends/amiga/plugin_hack.h
new file mode 100644
index 000000000..3d644ae8b
--- /dev/null
+++ b/frontends/amiga/plugin_hack.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_AMIGA_PLUGIN_HACK_H_
+#define NETSURF_AMIGA_PLUGIN_HACK_H_
+
+#include "utils/config.h"
+#include "utils/errors.h"
+
+nserror amiga_plugin_hack_init(void);
+
+void amiga_plugin_hack_execute(struct hlcache_handle *c);
+#endif
diff --git a/frontends/amiga/print.c b/frontends/amiga/print.c
new file mode 100644
index 000000000..fcccc91e2
--- /dev/null
+++ b/frontends/amiga/print.c
@@ -0,0 +1,589 @@
+/*
+ * Copyright 2009 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "amiga/os3support.h"
+
+#include <string.h>
+#include <proto/utility.h>
+#include <proto/iffparse.h>
+#include <proto/dos.h>
+#include <proto/intuition.h>
+#include <proto/Picasso96API.h>
+#include <devices/printer.h>
+#include <devices/prtbase.h>
+
+#include <prefs/prefhdr.h>
+#include <prefs/printertxt.h>
+#include <libraries/gadtools.h>
+
+#include <proto/window.h>
+#include <proto/layout.h>
+#include <proto/integer.h>
+#include <proto/label.h>
+#include <proto/chooser.h>
+#include <proto/fuelgauge.h>
+#include <classes/window.h>
+#include <gadgets/fuelgauge.h>
+#include <gadgets/layout.h>
+#include <gadgets/integer.h>
+#include <gadgets/chooser.h>
+#include <images/label.h>
+
+#include <reaction/reaction_macros.h>
+
+#include "utils/nsoption.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "desktop/plotters.h"
+#include "desktop/printer.h"
+#include "desktop/print.h"
+#include "desktop/gui_layout.h"
+#include "desktop/mouse.h"
+#include "desktop/gui_window.h"
+#include "content/hlcache.h"
+
+#include "amiga/plotters.h"
+#include "amiga/font.h"
+#include "amiga/gui.h"
+#include "amiga/libs.h"
+#include "amiga/misc.h"
+#include "amiga/print.h"
+#include "amiga/utf8.h"
+
+bool ami_print_begin(struct print_settings *ps);
+bool ami_print_next_page(void);
+void ami_print_end(void);
+bool ami_print_dump(void);
+void ami_print_progress(void);
+void ami_print_close_device(void);
+
+const struct printer amiprinter = {
+ &amiplot,
+ ami_print_begin,
+ ami_print_next_page,
+ ami_print_end,
+};
+
+struct ami_printer_info
+{
+ struct gui_globals *gg;
+ struct IODRPTagsReq *PReq;
+ struct PrinterData *PD;
+ struct PrinterExtendedData *PED;
+ struct MsgPort *msgport;
+ struct hlcache_handle *c;
+ struct print_settings *ps;
+ int page;
+ int pages;
+ Object *gadgets[GID_LAST];
+ Object *objects[OID_LAST];
+ struct Window *win;
+};
+
+enum
+{
+ PGID_MAIN=0,
+ PGID_PRINTER,
+ PGID_SCALE,
+ PGID_COPIES,
+ PGID_PRINT,
+ PGID_CANCEL,
+ PGID_LAST
+};
+
+#define IFFPrefChunkCnt 2
+static LONG IFFPrefChunks[] =
+{
+ ID_PREF, ID_PRHD,
+ ID_PREF, ID_PDEV,
+};
+
+static struct ami_printer_info ami_print_info;
+
+static CONST_STRPTR gadlab[PGID_LAST];
+static STRPTR printers[11];
+
+static void ami_print_ui_setup(void)
+{
+ gadlab[PGID_PRINTER] = (char *)ami_utf8_easy((char *)messages_get("Printer"));
+ gadlab[PGID_SCALE] = (char *)ami_utf8_easy((char *)messages_get("Scale"));
+ gadlab[PGID_COPIES] = (char *)ami_utf8_easy((char *)messages_get("Copies"));
+ gadlab[PGID_PRINT] = (char *)ami_utf8_easy((char *)messages_get("ObjPrint"));
+ gadlab[PGID_CANCEL] = (char *)ami_utf8_easy((char *)messages_get("Cancel"));
+}
+
+static void ami_print_ui_free(void)
+{
+ int i;
+
+ for(i = 0; i < PGID_LAST; i++) {
+ if(gadlab[i]) FreeVec((APTR)gadlab[i]);
+ }
+
+ for(i = 0; i < 10; i++) {
+ if(printers[i]) FreeVec(printers[i]);
+ }
+}
+
+static BOOL ami_print_readunit(CONST_STRPTR filename, char name[],
+ uint32 namesize, int unitnum)
+{
+ /* This is a modified version of a function from the OS4 SDK.
+ * The README says "You can use it in your application",
+ * no licence is specified. (c) 1999 Amiga Inc */
+
+ BPTR fp;
+ BOOL ok;
+ struct IFFHandle *iff;
+ struct ContextNode *cn;
+ struct PrefHeader phead;
+ struct PrinterDeviceUnitPrefs pdev;
+
+ SNPrintf(name,namesize,"Unit %ld",unitnum);
+ fp = Open(filename, MODE_OLDFILE);
+ if (fp)
+ {
+ iff = AllocIFF();
+ if (iff)
+ {
+ iff->iff_Stream = fp;
+ InitIFFasDOS(iff);
+
+ if (!OpenIFF(iff, IFFF_READ))
+ {
+ if (!ParseIFF(iff, IFFPARSE_STEP))
+ {
+ cn = CurrentChunk(iff);
+ if (cn->cn_ID == ID_FORM && cn->cn_Type == ID_PREF)
+ {
+ if (!StopChunks(iff, IFFPrefChunks, IFFPrefChunkCnt))
+ {
+ ok = TRUE;
+ while (ok)
+ {
+ if (ParseIFF(iff, IFFPARSE_SCAN))
+ break;
+ cn = CurrentChunk(iff);
+ if (cn->cn_Type == ID_PREF)
+ {
+ switch (cn->cn_ID)
+ {
+ case ID_PRHD:
+ if (ReadChunkBytes(iff, &phead, sizeof(struct PrefHeader)) != sizeof(struct PrefHeader))
+ {
+ ok = FALSE;
+ break;
+ }
+ if (phead.ph_Version != 0)
+ {
+ ok = FALSE;
+ break;
+ }
+ break;
+ case ID_PDEV:
+ if (ReadChunkBytes(iff, &pdev, sizeof(pdev)) == sizeof(pdev))
+ {
+ if (pdev.pd_UnitName[0])
+ strcpy(name,pdev.pd_UnitName);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ CloseIFF(iff);
+ }
+ FreeIFF(iff);
+ }
+ Close(fp);
+ }
+ else return FALSE;
+
+ return TRUE;
+}
+
+void ami_print_ui(struct hlcache_handle *c)
+{
+ char filename[30];
+ int i;
+
+ struct ami_print_window *pw = ami_misc_allocvec_clear(sizeof(struct ami_print_window), 0);
+
+ pw->c = c;
+
+ printers[0] = ami_misc_allocvec_clear(50, 0);
+ ami_print_readunit("ENV:Sys/printer.prefs", printers[0], 50, 0);
+
+ strcpy(filename,"ENV:Sys/printerN.prefs");
+ for (i = 1; i < 10; i++)
+ {
+ filename[15] = '0' + i;
+ printers[i] = AllocVecTagList(50, NULL);
+ if(!ami_print_readunit(filename, printers[i], 50, i))
+ {
+ FreeVec(printers[i]);
+ printers[i] = NULL;
+ break;
+ }
+
+ }
+
+ ami_print_ui_setup();
+
+ pw->objects[OID_MAIN] = WindowObj,
+ WA_ScreenTitle, ami_gui_get_screen_title(),
+ WA_Title, gadlab[PGID_PRINT],
+ WA_Activate, TRUE,
+ WA_DepthGadget, TRUE,
+ WA_DragBar, TRUE,
+ WA_CloseGadget, TRUE,
+ WA_SizeGadget, FALSE,
+ WA_PubScreen, scrn,
+ WINDOW_SharedPort, sport,
+ WINDOW_UserData, pw,
+ WINDOW_IconifyGadget, FALSE,
+ WINDOW_Position, WPOS_CENTERSCREEN,
+ WINDOW_ParentGroup, pw->gadgets[PGID_MAIN] = LayoutVObj,
+ LAYOUT_AddChild, ChooserObject,
+ GA_ID, PGID_PRINTER,
+ GA_RelVerify, TRUE,
+ GA_TabCycle, TRUE,
+ CHOOSER_LabelArray, printers,
+ CHOOSER_Selected, nsoption_int(printer_unit),
+ ChooserEnd,
+ CHILD_Label, LabelObject,
+ LABEL_Text, gadlab[PGID_PRINTER],
+ LabelEnd,
+ LAYOUT_AddChild, IntegerObject,
+ GA_ID, PGID_COPIES,
+ GA_RelVerify, TRUE,
+ GA_TabCycle, TRUE,
+ INTEGER_Number, 1,
+ INTEGER_Minimum, 1,
+ INTEGER_Maximum, 100,
+ INTEGER_Arrows, TRUE,
+ IntegerEnd,
+ CHILD_Label, LabelObject,
+ LABEL_Text, gadlab[PGID_COPIES],
+ LabelEnd,
+ LAYOUT_AddChild, HGroupObject,
+ LAYOUT_LabelColumn, PLACETEXT_RIGHT,
+ LAYOUT_AddChild, pw->gadgets[PGID_SCALE] = IntegerObj,
+ GA_ID, PGID_SCALE,
+ GA_RelVerify, TRUE,
+ GA_TabCycle, TRUE,
+ INTEGER_Number, nsoption_int(print_scale),
+ INTEGER_Minimum, 0,
+ INTEGER_Maximum, 100,
+ INTEGER_Arrows, TRUE,
+ IntegerEnd,
+ CHILD_WeightedWidth, 0,
+ CHILD_Label, LabelObject,
+ LABEL_Text, "%",
+ LabelEnd,
+ LayoutEnd,
+ CHILD_Label, LabelObject,
+ LABEL_Text, gadlab[PGID_SCALE],
+ LabelEnd,
+ LAYOUT_AddChild, HGroupObject,
+ LAYOUT_AddChild, pw->gadgets[PGID_PRINT] = ButtonObj,
+ GA_ID, PGID_PRINT,
+ GA_RelVerify,TRUE,
+ GA_Text, gadlab[PGID_PRINT],
+ GA_TabCycle,TRUE,
+ ButtonEnd,
+ CHILD_WeightedHeight,0,
+ LAYOUT_AddChild, pw->gadgets[GID_CANCEL] = ButtonObj,
+ GA_ID, PGID_CANCEL,
+ GA_RelVerify, TRUE,
+ GA_Text, gadlab[PGID_CANCEL],
+ GA_TabCycle,TRUE,
+ ButtonEnd,
+ LayoutEnd,
+ CHILD_WeightedHeight,0,
+ EndGroup,
+ EndWindow;
+
+ pw->win = (struct Window *)RA_OpenWindow(pw->objects[OID_MAIN]);
+
+ pw->node = AddObject(window_list, AMINS_PRINTWINDOW);
+ pw->node->objstruct = pw;
+}
+
+static void ami_print_close(struct ami_print_window *pw)
+{
+ DisposeObject(pw->objects[OID_MAIN]);
+ DelObject(pw->node);
+
+ ami_print_ui_free();
+}
+
+BOOL ami_print_event(struct ami_print_window *pw)
+{
+ /* return TRUE if window destroyed */
+ ULONG result;
+ uint16 code;
+ struct hlcache_handle *c;
+ int copies;
+ int print_scale;
+ int printer_unit;
+
+ while((result = RA_HandleInput(pw->objects[OID_MAIN],&code)) != WMHI_LASTMSG)
+ {
+ switch(result & WMHI_CLASSMASK) // class
+ {
+ case WMHI_GADGETUP:
+ switch(result & WMHI_GADGETMASK)
+ {
+ case PGID_PRINT:
+ GetAttr(INTEGER_Number, pw->gadgets[PGID_SCALE],
+ (ULONG *)&print_scale);
+ GetAttr(INTEGER_Number, pw->gadgets[PGID_COPIES],
+ (ULONG *)&copies);
+ GetAttr(CHOOSER_Selected, pw->gadgets[PGID_PRINTER],
+ (ULONG *)&printer_unit);
+
+ nsoption_set_int(print_scale, print_scale);
+ nsoption_set_int(printer_unit, printer_unit);
+
+ c = pw->c;
+ ami_print_close(pw);
+ ami_print(c, copies);
+ return TRUE;
+ break;
+
+ case PGID_CANCEL:
+ ami_print_close(pw);
+ return TRUE;
+ break;
+ }
+ break;
+
+ case WMHI_CLOSEWINDOW:
+ ami_print_close(pw);
+ return TRUE;
+ break;
+ }
+ }
+ return FALSE;
+}
+
+void ami_print(struct hlcache_handle *c, int copies)
+{
+ double height;
+ float scale = nsoption_int(print_scale) / 100.0;
+
+ if(ami_print_info.msgport == NULL)
+ ami_print_init();
+#ifdef __amigaos4__
+ if(!(ami_print_info.PReq =
+ (struct IODRPTagsReq *)AllocSysObjectTags(ASOT_IOREQUEST,
+ ASOIOR_Size, sizeof(struct IODRPTagsReq),
+ ASOIOR_ReplyPort, ami_print_info.msgport,
+ ASO_NoTrack, FALSE,
+ TAG_DONE))) return;
+#else
+ if(!(ami_print_info.PReq =
+ (struct IODRPTagsReq *)CreateIORequest(ami_print_info.msgport,
+ sizeof(struct IODRPTagsReq)))) return;
+#endif
+
+ if(OpenDevice("printer.device", nsoption_int(printer_unit),
+ (struct IORequest *)ami_print_info.PReq, 0))
+ {
+ amiga_warn_user("CompError","printer.device");
+ return;
+ }
+
+ ami_print_info.PD = (struct PrinterData *)ami_print_info.PReq->io_Device;
+ ami_print_info.PED = &ami_print_info.PD->pd_SegmentData->ps_PED;
+
+ ami_print_info.ps = print_make_settings(PRINT_DEFAULT, nsurl_access(hlcache_handle_get_url(c)), ami_layout_table);
+ ami_print_info.ps->page_width = ami_print_info.PED->ped_MaxXDots;
+ ami_print_info.ps->page_height = ami_print_info.PED->ped_MaxYDots;
+ ami_print_info.ps->scale = scale;
+
+ if(!print_set_up(c, &amiprinter, ami_print_info.ps, &height))
+ {
+ amiga_warn_user("PrintError","print_set_up() returned false");
+ ami_print_close_device();
+ return;
+ }
+
+ height *= ami_print_info.ps->scale;
+ ami_print_info.pages = height / ami_print_info.ps->page_height;
+ ami_print_info.c = c;
+
+ ami_print_progress();
+
+ while(ami_print_cont()); /* remove while() for async printing */
+}
+
+bool ami_print_cont(void)
+{
+ bool ret = false;
+
+ if(ami_print_info.page <= ami_print_info.pages)
+ {
+ glob = ami_print_info.gg;
+ print_draw_next_page(&amiprinter, ami_print_info.ps);
+ ami_print_dump();
+ glob = &browserglob;
+ ret = true;
+ }
+ else
+ {
+ print_cleanup(ami_print_info.c, &amiprinter, ami_print_info.ps);
+ ret = false;
+ }
+
+ return ret;
+}
+
+struct MsgPort *ami_print_init(void)
+{
+ ami_print_info.msgport = AllocSysObjectTags(ASOT_PORT,
+ ASO_NoTrack,FALSE,
+ TAG_DONE);
+
+ return ami_print_info.msgport;
+}
+
+void ami_print_free(void)
+{
+ FreeSysObject(ASOT_PORT, ami_print_info.msgport);
+ ami_print_info.msgport = NULL;
+}
+
+struct MsgPort *ami_print_get_msgport(void)
+{
+ return ami_print_info.msgport;
+}
+
+bool ami_print_begin(struct print_settings *ps)
+{
+ ami_print_info.gg = ami_misc_allocvec_clear(sizeof(struct gui_globals), 0);
+ if(!ami_print_info.gg) return false;
+
+ ami_init_layers(ami_print_info.gg,
+ ami_print_info.PED->ped_MaxXDots,
+ ami_print_info.PED->ped_MaxYDots,
+ true);
+
+ ami_print_info.page = 0;
+
+ return true;
+}
+
+bool ami_print_next_page(void)
+{
+ ami_print_info.page++;
+
+ RefreshSetGadgetAttrs((struct Gadget *)ami_print_info.gadgets[GID_STATUS],
+ ami_print_info.win, NULL,
+ FUELGAUGE_Level, ami_print_info.page,
+ TAG_DONE);
+ return true;
+}
+
+void ami_print_end(void)
+{
+ ami_free_layers(ami_print_info.gg);
+ FreeVec(ami_print_info.gg);
+ DisposeObject(ami_print_info.objects[OID_MAIN]);
+ glob = &browserglob;
+
+ ami_print_close_device();
+ ami_print_free();
+}
+
+void ami_print_close_device(void)
+{
+ CloseDevice((struct IORequest *)ami_print_info.PReq);
+ FreeSysObject(ASOT_IOREQUEST,ami_print_info.PReq);
+}
+
+bool ami_print_dump(void)
+{
+ ami_print_info.PReq->io_Command = PRD_DUMPRPORT;
+ ami_print_info.PReq->io_Flags = 0;
+ ami_print_info.PReq->io_Error = 0;
+ ami_print_info.PReq->io_RastPort = ami_print_info.gg->rp;
+ ami_print_info.PReq->io_ColorMap = NULL;
+ ami_print_info.PReq->io_Modes = 0;
+ ami_print_info.PReq->io_SrcX = 0;
+ ami_print_info.PReq->io_SrcY = 0;
+ ami_print_info.PReq->io_SrcWidth = ami_print_info.PED->ped_MaxXDots;
+ ami_print_info.PReq->io_SrcHeight = ami_print_info.PED->ped_MaxYDots;
+ ami_print_info.PReq->io_DestCols = ami_print_info.PED->ped_MaxXDots;
+ ami_print_info.PReq->io_DestRows = ami_print_info.PED->ped_MaxYDots;
+ ami_print_info.PReq->io_Special = 0;
+
+ DoIO((struct IORequest *)ami_print_info.PReq); /* SendIO for async printing */
+
+ return true;
+}
+
+void ami_print_progress(void)
+{
+ ami_print_info.objects[OID_MAIN] = WindowObj,
+ WA_ScreenTitle, ami_gui_get_screen_title(),
+ WA_Title, messages_get("Printing"),
+ WA_Activate, TRUE,
+ WA_DepthGadget, TRUE,
+ WA_DragBar, TRUE,
+ WA_CloseGadget, FALSE,
+ WA_SizeGadget, TRUE,
+ WA_PubScreen,scrn,
+ //WINDOW_SharedPort,sport,
+ WINDOW_UserData, &ami_print_info,
+ WINDOW_IconifyGadget, FALSE,
+ WINDOW_LockHeight,TRUE,
+ WINDOW_Position, WPOS_CENTERSCREEN,
+ WINDOW_ParentGroup, ami_print_info.gadgets[GID_MAIN] = LayoutVObj,
+ LAYOUT_AddChild, ami_print_info.gadgets[GID_STATUS] = FuelGaugeObj,
+ GA_ID,GID_STATUS,
+ FUELGAUGE_Min,0,
+ FUELGAUGE_Max,ami_print_info.pages,
+ FUELGAUGE_Level,0,
+ FUELGAUGE_Ticks,11,
+ FUELGAUGE_ShortTicks,TRUE,
+ FUELGAUGE_Percent,TRUE,
+ FUELGAUGE_Justification,FGJ_CENTER,
+ FuelGaugeEnd,
+ CHILD_NominalSize,TRUE,
+ CHILD_WeightedHeight,0,
+/*
+ LAYOUT_AddChild, ami_print_info.gadgets[GID_CANCEL] = ButtonObj,
+ GA_ID,GID_CANCEL,
+ GA_Disabled,TRUE,
+ GA_RelVerify,TRUE,
+ GA_Text,messages_get("Abort"),
+ GA_TabCycle,TRUE,
+ ButtonEnd,
+*/
+ EndGroup,
+ EndWindow;
+
+ ami_print_info.win = (struct Window *)RA_OpenWindow(ami_print_info.objects[OID_MAIN]);
+}
+
diff --git a/frontends/amiga/print.h b/frontends/amiga/print.h
new file mode 100755
index 000000000..bdd409b50
--- /dev/null
+++ b/frontends/amiga/print.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2009 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_PRINT_H
+#define AMIGA_PRINT_H
+#include <proto/exec.h>
+
+struct content;
+
+struct ami_print_window {
+ struct nsObject *node;
+ struct Window *win;
+ Object *objects[OID_LAST];
+ Object *gadgets[GID_LAST];
+ struct hlcache_handle *c;
+};
+
+void ami_print(struct hlcache_handle *c, int copies);
+void ami_print_ui(struct hlcache_handle *c);
+BOOL ami_print_event(struct ami_print_window *pw);
+bool ami_print_cont(void);
+struct MsgPort *ami_print_init(void);
+void ami_print_free(void);
+struct MsgPort *ami_print_get_msgport(void);
+#endif
diff --git a/frontends/amiga/resources/AdBlock.css b/frontends/amiga/resources/AdBlock.css
new file mode 120000
index 000000000..e3811f62b
--- /dev/null
+++ b/frontends/amiga/resources/AdBlock.css
@@ -0,0 +1 @@
+../../!NetSurf/Resources/AdBlock,f79 \ No newline at end of file
diff --git a/frontends/amiga/resources/LangNames b/frontends/amiga/resources/LangNames
new file mode 100644
index 000000000..6dc145bfc
--- /dev/null
+++ b/frontends/amiga/resources/LangNames
@@ -0,0 +1,74 @@
+# LangNames file for NetSurf AmigaOS
+#
+# Maps AmigaOS locale.library language names to the two character
+# ISO-639 codes, enabling localised text strings files shareable
+# with the RISC OS version of NetSurf, without the hassle of
+# renaming directories, and to specify accept-language encoding
+# with no additional configuration.
+#
+# Format is <Locale language name>:<language tag>
+#
+# NetSurf will use the full AmigaOS language name if it is not
+# present in this file.
+#
+albanian:sq
+belarusian:be
+bosnian:bs
+bulgarian:bg
+catalan:ca
+chinese:zh
+croatian:hr
+czech:cs
+danish:da
+dutch:nl
+english:en
+english-british:en-GB
+esperanto:eo
+estonian:et
+faroese:fo
+finnish:fi
+french:fr
+galician:gl
+german:de
+greek:el
+hebrew:he
+hungarian:hu
+icelandic:is
+irish:ga
+italian:it
+japanese:ja
+korean:ko
+latvian:lv
+lithuanian:lt
+macedonian:mk
+malay-indonesian:id
+malay-malaysian:ms
+maltese:mt
+norwegian:nb
+norwegian-nynorsk:nn
+polish:pl
+portuguese:pt
+portuguese-brazil:pr-BR
+romanian:ro
+russian:ru
+serbian:sr
+slovak:sk
+slovenian:sl
+spanish:es
+swedish:sv
+thai:th
+turkish:tr
+ukrainian:uk
+welsh:cy
+
+# Native language names for AmigaOS 3 compatibility
+dansk:da
+deutsch:de
+español:es
+français:fr
+italiano:it
+nederlands:nl
+norsk:nb
+português:pt
+svenska:sv
+
diff --git a/frontends/amiga/resources/Pointers/Blank b/frontends/amiga/resources/Pointers/Blank
new file mode 100755
index 000000000..419fb503a
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/Blank
@@ -0,0 +1,33 @@
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00 00 \ No newline at end of file
diff --git a/frontends/amiga/resources/Pointers/Blank.info b/frontends/amiga/resources/Pointers/Blank.info
new file mode 100644
index 000000000..5ce812b26
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/Blank.info
Binary files differ
diff --git a/frontends/amiga/resources/Pointers/Caret b/frontends/amiga/resources/Pointers/Caret
new file mode 100755
index 000000000..3b1604e85
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/Caret
@@ -0,0 +1,33 @@
+22022000000000000000000000000000
+00200000000000000000000000000000
+00200000000000000000000000000000
+00200000000000000000000000000000
+00200000000000000000000000000000
+00200000000000000000000000000000
+00200000000000000000000000000000
+00200000000000000000000000000000
+00200000000000000000000000000000
+00200000000000000000000000000000
+00200000000000000000000000000000
+00200000000000000000000000000000
+00200000000000000000000000000000
+00200000000000000000000000000000
+00200000000000000000000000000000
+00200000000000000000000000000000
+00200000000000000000000000000000
+22022000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+02 08 \ No newline at end of file
diff --git a/frontends/amiga/resources/Pointers/Caret.info b/frontends/amiga/resources/Pointers/Caret.info
new file mode 100644
index 000000000..11d9c5fc9
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/Caret.info
Binary files differ
diff --git a/frontends/amiga/resources/Pointers/Cross.info b/frontends/amiga/resources/Pointers/Cross.info
new file mode 100644
index 000000000..7aa433139
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/Cross.info
Binary files differ
diff --git a/frontends/amiga/resources/Pointers/Default b/frontends/amiga/resources/Pointers/Default
new file mode 100755
index 000000000..8f88f3d33
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/Default
@@ -0,0 +1,33 @@
+13000000000000000000000000000000
+21330000000000000000000000000000
+02113300000000000000000000000000
+02111133000000000000000000000000
+00211111330000000000000000000000
+00211111110000000000000000000000
+00021113000000000000000000000000
+00021121300000000000000000000000
+00002102130000000000000000000000
+00002100213000000000000000000000
+00000000021000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00 00 \ No newline at end of file
diff --git a/frontends/amiga/resources/Pointers/Default.info b/frontends/amiga/resources/Pointers/Default.info
new file mode 100644
index 000000000..dc8c2b0ed
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/Default.info
Binary files differ
diff --git a/frontends/amiga/resources/Pointers/Down.info b/frontends/amiga/resources/Pointers/Down.info
new file mode 100644
index 000000000..e4667bca1
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/Down.info
Binary files differ
diff --git a/frontends/amiga/resources/Pointers/Drag.info b/frontends/amiga/resources/Pointers/Drag.info
new file mode 100644
index 000000000..820d75373
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/Drag.info
Binary files differ
diff --git a/frontends/amiga/resources/Pointers/Help.info b/frontends/amiga/resources/Pointers/Help.info
new file mode 100644
index 000000000..ca5947105
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/Help.info
Binary files differ
diff --git a/frontends/amiga/resources/Pointers/Left.info b/frontends/amiga/resources/Pointers/Left.info
new file mode 100644
index 000000000..1a3b4e835
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/Left.info
Binary files differ
diff --git a/frontends/amiga/resources/Pointers/LeftDown.info b/frontends/amiga/resources/Pointers/LeftDown.info
new file mode 100644
index 000000000..e8f39cb24
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/LeftDown.info
Binary files differ
diff --git a/frontends/amiga/resources/Pointers/LeftUp.info b/frontends/amiga/resources/Pointers/LeftUp.info
new file mode 100644
index 000000000..be80018f3
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/LeftUp.info
Binary files differ
diff --git a/frontends/amiga/resources/Pointers/Menu b/frontends/amiga/resources/Pointers/Menu
new file mode 100755
index 000000000..29442146c
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/Menu
@@ -0,0 +1,33 @@
+13000000000000000000000000000000
+21330000000000000000000000000000
+02113300000000000000000000000000
+02111133000000000000000000000000
+00211111330000000000000000000000
+00211111110000000000000000000000
+00021113000000000000000000000000
+00021121300033333333331000000000
+00002102130031111111112000000000
+00002100213031111111112000000000
+00000000021031111111112000000000
+00000000000033333333332000000000
+00000000000033333333332000000000
+00000000000033333333332000000000
+00000000000033333333332000000000
+00000000000033333333332000000000
+00000000000033333333332000000000
+00000000000012222222222000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00 00 \ No newline at end of file
diff --git a/frontends/amiga/resources/Pointers/Menu.info b/frontends/amiga/resources/Pointers/Menu.info
new file mode 100644
index 000000000..2a1143f11
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/Menu.info
Binary files differ
diff --git a/frontends/amiga/resources/Pointers/Move b/frontends/amiga/resources/Pointers/Move
new file mode 100755
index 000000000..16d88e869
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/Move
@@ -0,0 +1,33 @@
+00000000002200000000000000000000
+00000000023320000000000000000000
+00000002223322200000000000000000
+00000023323323320000000000000000
+00000023323323320000000000000000
+00000023323323322200000000000000
+00000023323323323320000000000000
+00000023323323323320000000000000
+02200023323323323320000000000000
+23320023323323323320000000000000
+23332023323323323320000000000000
+02333233323323323320000000000000
+02333333323323323320000000000000
+00233333333333323320000000000000
+00233333333333333320000000000000
+00023333333333333320000000000000
+00023333333333333320000000000000
+00002333333333333320000000000000
+00002333333333333320000000000000
+00000233333333333200000000000000
+00000022333333332000000000000000
+00000000222222220000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+11 15 \ No newline at end of file
diff --git a/frontends/amiga/resources/Pointers/Move.info b/frontends/amiga/resources/Pointers/Move.info
new file mode 100644
index 000000000..606f5d283
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/Move.info
Binary files differ
diff --git a/frontends/amiga/resources/Pointers/NoDrop.info b/frontends/amiga/resources/Pointers/NoDrop.info
new file mode 100644
index 000000000..22e77a4be
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/NoDrop.info
Binary files differ
diff --git a/frontends/amiga/resources/Pointers/NotAllowed.info b/frontends/amiga/resources/Pointers/NotAllowed.info
new file mode 100644
index 000000000..01545bbba
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/NotAllowed.info
Binary files differ
diff --git a/frontends/amiga/resources/Pointers/Point b/frontends/amiga/resources/Pointers/Point
new file mode 100755
index 000000000..25e92be77
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/Point
@@ -0,0 +1,33 @@
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000002200000000000000000000000
+00000023320000000000000000000000
+00000023320000000000000000000000
+00000023320000000000000000000000
+00000023320000000000000000000000
+00000023322200000000000000000000
+02200023323322200000000000000000
+23320023323323320000000000000000
+23332023323323322200000000000000
+02333233323323323320000000000000
+02333333323323323320000000000000
+00233333333333323320000000000000
+00233333333333333320000000000000
+00023333333333333320000000000000
+00023333333333333320000000000000
+00002333333333333320000000000000
+00002333333333333320000000000000
+00000233333333333200000000000000
+00000022333333332000000000000000
+00000000222222220000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+07 02 \ No newline at end of file
diff --git a/frontends/amiga/resources/Pointers/Point.info b/frontends/amiga/resources/Pointers/Point.info
new file mode 100644
index 000000000..685e4f5e0
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/Point.info
Binary files differ
diff --git a/frontends/amiga/resources/Pointers/Progress.info b/frontends/amiga/resources/Pointers/Progress.info
new file mode 100644
index 000000000..c47d52e99
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/Progress.info
Binary files differ
diff --git a/frontends/amiga/resources/Pointers/Right.info b/frontends/amiga/resources/Pointers/Right.info
new file mode 100644
index 000000000..7080c4e77
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/Right.info
Binary files differ
diff --git a/frontends/amiga/resources/Pointers/RightDown.info b/frontends/amiga/resources/Pointers/RightDown.info
new file mode 100644
index 000000000..0f5c181cf
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/RightDown.info
Binary files differ
diff --git a/frontends/amiga/resources/Pointers/RightUp.info b/frontends/amiga/resources/Pointers/RightUp.info
new file mode 100644
index 000000000..140ef15c5
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/RightUp.info
Binary files differ
diff --git a/frontends/amiga/resources/Pointers/Up.info b/frontends/amiga/resources/Pointers/Up.info
new file mode 100644
index 000000000..f4b44421f
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/Up.info
Binary files differ
diff --git a/frontends/amiga/resources/Pointers/Wait b/frontends/amiga/resources/Pointers/Wait
new file mode 100755
index 000000000..6d9421279
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/Wait
@@ -0,0 +1,33 @@
+00002222222200000000000000000000
+00223333332220000000000000000000
+02111133333322000000000000000000
+23331333333322000000000000000000
+23313333333332200000000000000000
+02111131111332200000000000000000
+23333333313322000000000000000000
+02333333133220000000000000000000
+02333331111220000000000000000000
+00233223332200000000000000000000
+00022322222000000000000000000000
+00023332200000000000000000000000
+00222332200000000000000000000000
+02332222000000000000000000000000
+02332200000000000000000000000000
+00222000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+06 07 \ No newline at end of file
diff --git a/frontends/amiga/resources/Pointers/Wait.info b/frontends/amiga/resources/Pointers/Wait.info
new file mode 100644
index 000000000..88aa27a03
--- /dev/null
+++ b/frontends/amiga/resources/Pointers/Wait.info
Binary files differ
diff --git a/frontends/amiga/resources/Resource.map b/frontends/amiga/resources/Resource.map
new file mode 100644
index 000000000..dbe02a4bc
--- /dev/null
+++ b/frontends/amiga/resources/Resource.map
@@ -0,0 +1,4 @@
+# Resource.map
+favicon.ico:favicon.png
+default.ico:icons/search.png
+
diff --git a/frontends/amiga/resources/SearchEngines b/frontends/amiga/resources/SearchEngines
new file mode 100644
index 000000000..f1b705a0a
--- /dev/null
+++ b/frontends/amiga/resources/SearchEngines
@@ -0,0 +1,22 @@
+Google|www.google.com|http://www.google.com/search?q=%s|http://www.google.com/favicon.ico|
+Yahoo|search.yahoo.com|http://search.yahoo.com/search?p=%s|http://www.yahoo.com/favicon.ico|
+Bing|www.bing.com|http://www.bing.com/search?q=%s|http://www.bing.com/favicon.ico|
+Business.com|www.business.com|http://www.business.com/search/rslt_default.asp?query=%s|http://www.business.com/favicon.ico|
+Omgili|www.omgili.com|http://www.omgili.com/AAAAA/%s.html|http://www.omgili.com/favicon.ico|
+BBC News|search.bbc.co.uk|http://search.bbc.co.uk/search?q=%s&tab=ns|http://news.bbc.co.uk/favicon.ico|
+Creative Commons|creativecommons.org|http://creativecommons.org/?s=%s|http://creativecommons.org/favicon.ico|
+Ask.com|www.ask.com|http://www.ask.com/web?q=%s|http://www.ask.com/favicon.ico|
+Answers.com|www.answers.com|http://www.answers.com/%s|http://www.answers.com/favicon.ico|
+Dictionary.com|dictionary.reference.com|http://dictionary.reference.com/browse/%s?jss=0|http://dictionary.reference.com/favicon.ico|
+Youtube|www.youtube.com|http://www.youtube.com/results?search_query=%s|http://www.youtube.com/favicon.ico|
+AeroMp3|www.aeromp3.com|http://www.aeromp3.com/search?q=%s|http://www.aeromp3.com/favicon.ico|
+AOL|search.aol.com|http://search.aol.com/aol/search?query=%s|http://www.aol.com/favicon.ico|
+Baidu|www.baidu.com|http://www.baidu.com/s?wd=%s|http://www.baidu.com/favicon.ico|
+Amazon|www.amazon.com|http://www.amazon.com/s/ref=nb_ss_gw?field-keywords=%s|http://www.amazon.com/favicon.ico|
+Ebay|shop.ebay.com|http://shop.ebay.com/items/%s|http://www.ebay.com/favicon.ico|
+IMDB|www.imdb.com|http://www.imdb.com/find?q=%s|http://www.imdb.com/favicon.ico|
+ESPN|search.espn.go.com|http://search.espn.go.com/%s/|http://www.espn.go.com/favicon.ico|
+Wikipedia|en.wikipedia.org|http://en.wikipedia.org/w/index.php?title=Special%%3ASearch&search=%s|http://en.wikipedia.org/favicon.ico|
+Aminet|www.aminet.net|http://aminet.net/search?query=%s|http://aminet.net/favicon.ico|
+OS4Depot|www.os4depot.net|http://www.os4depot.net/index.php?function=search&tool=simple&f_fields=%s|http://www.os4depot.net/favicon.ico|
+DuckDuckGo|www.duckduckgo.com|http://www.duckduckgo.com/?q=%s|http://www.duckduckgo.com/favicon.ico|
diff --git a/frontends/amiga/resources/Themes/AISS/NetSurf.info b/frontends/amiga/resources/Themes/AISS/NetSurf.info
new file mode 100644
index 000000000..70275756e
--- /dev/null
+++ b/frontends/amiga/resources/Themes/AISS/NetSurf.info
Binary files differ
diff --git a/frontends/amiga/resources/Themes/AISS/Resource.map b/frontends/amiga/resources/Themes/AISS/Resource.map
new file mode 100644
index 000000000..4fe374aa2
--- /dev/null
+++ b/frontends/amiga/resources/Themes/AISS/Resource.map
@@ -0,0 +1,8 @@
+# Resource.map
+favicon.ico:TBimages:list_file
+default.ico:TBimages:list_search
+icons/arrow-l.png:TBimages:list_mailreplied
+icons/search.png:TBimages:list_search
+icons/directory.png:TBimages:list_drawer
+icons/directory2.png:TBimages:list_drawer_s
+icons/content.png:TBimages:list_file
diff --git a/frontends/amiga/resources/Themes/AISS/Theme b/frontends/amiga/resources/Themes/AISS/Theme
new file mode 100755
index 000000000..8c5af103c
--- /dev/null
+++ b/frontends/amiga/resources/Themes/AISS/Theme
@@ -0,0 +1,75 @@
+# AISS theme. Requires:
+# AISS to be installed (see http://www.masonicons.de)
+# defpointers package from OS4Depot (http://www.os4depot.net)
+# Throbber animation by Martin Merz
+#
+theme_nav_west:*TBImages:nav_west
+theme_nav_west_s:*TBImages:nav_west_s
+theme_nav_west_g:*TBImages:nav_west_g
+theme_nav_east:*TBImages:nav_east
+theme_nav_east_s:*TBImages:nav_east_s
+theme_nav_east_g:*TBImages:nav_east_g
+theme_stop:*TBImages:stop
+theme_stop_s:*TBImages:stop_s
+theme_stop_g:*TBImages:stop_g
+theme_reload:*TBImages:reload
+theme_reload_s:*TBImages:reload_s
+theme_reload_g:*TBImages:reload_g
+theme_home:*TBImages:home
+theme_home_s:*TBImages:home_s
+theme_home_g:*TBImages:home_g
+theme_closetab:*TBImages:list_cancel
+theme_closetab_s:*TBImages:list_cancel
+theme_closetab_g:*TBImages:list_cancel
+theme_addtab:*TBImages:list_add
+theme_addtab_s:*TBImages:list_add
+theme_addtab_g:*TBImages:list_add
+theme_throbber:Throbber
+theme_throbber_frames:13
+theme_throbber_delay:100
+theme_tab_loading:*TBImages:list_download
+theme_search:*TBImages:list_search
+theme_fave:*TBImages:list_favouriteadd
+theme_unfave:*TBImages:list_favourite
+ptr_default:*PROGDIR:Resources/Pointers/Default
+ptr_point:*PROGDIR:Resources/Pointers/Point
+ptr_caret:*PROGDIR:Resources/Pointers/Caret
+ptr_menu:*PROGDIR:Resources/Pointers/Menu
+ptr_up:*PROGDIR:Resources/Pointers/Up
+ptr_down:*PROGDIR:Resources/Pointers/Down
+ptr_left:*PROGDIR:Resources/Pointers/Left
+ptr_right:*PROGDIR:Resources/Pointers/Right
+ptr_rightup:*PROGDIR:Resources/Pointers/RightUp
+ptr_leftdown:*PROGDIR:Resources/Pointers/LeftDown
+ptr_leftup:*PROGDIR:Resources/Pointers/LeftUp
+ptr_rightdown:*PROGDIR:Resources/Pointers/RightDown
+ptr_cross:*PROGDIR:Resources/Pointers/Cross
+ptr_move:*PROGDIR:Resources/Pointers/Move
+ptr_wait:*PROGDIR:Resources/Pointers/Wait
+ptr_help:*PROGDIR:Resources/Pointers/Help
+ptr_nodrop:*PROGDIR:Resources/Pointers/NoDrop
+ptr_notallowed:*PROGDIR:Resources/Pointers/NotAllowed
+ptr_progress:*PROGDIR:Resources/Pointers/Progress
+ptr_blank:*PROGDIR:Resources/Pointers/Blank
+ptr_drag:*PROGDIR:Resources/Pointers/Drag
+ptr32_default:*ENV:Sys/def_pointer
+ptr32_point:*ENV:Sys/def_linkpointer
+ptr32_caret:*ENV:Sys/def_textpointer
+ptr32_menu:*ENV:Sys/def_contextmenupointer
+ptr32_up:*ENV:Sys/def_northresizepointer
+ptr32_down:*ENV:Sys/def_southresizepointer
+ptr32_left:*ENV:Sys/def_westresizepointer
+ptr32_right:*ENV:Sys/def_eastresizepointer
+ptr32_rightup:*ENV:Sys/def_northeastresizepointer
+ptr32_leftdown:*ENV:Sys/def_southwestresizepointer
+ptr32_leftup:*ENV:Sys/def_northwestresizepointer
+ptr32_rightdown:*ENV:Sys/def_southeastresizepointer
+ptr32_cross:*ENV:Sys/def_crosspointer
+ptr32_move:*ENV:Sys/def_handpointer
+ptr32_wait:*ENV:Sys/def_busypointer
+ptr32_help:*ENV:Sys/def_helppointer
+ptr32_nodrop:*ENV:Sys/def_nodroppointer
+ptr32_notallowed:*ENV:Sys/def_notallowedpointer
+ptr32_progress:*ENV:Sys/def_progresspointer
+ptr32_blank:*ENV:Sys/def_nonepointer
+ptr32_drag:*ENV:Sys/def_draganddroppointer
diff --git a/frontends/amiga/resources/Themes/AISS/Throbber b/frontends/amiga/resources/Themes/AISS/Throbber
new file mode 100644
index 000000000..a8c38d153
--- /dev/null
+++ b/frontends/amiga/resources/Themes/AISS/Throbber
Binary files differ
diff --git a/frontends/amiga/resources/Themes/Default/NetSurf.info b/frontends/amiga/resources/Themes/Default/NetSurf.info
new file mode 100644
index 000000000..0e6017253
--- /dev/null
+++ b/frontends/amiga/resources/Themes/Default/NetSurf.info
Binary files differ
diff --git a/frontends/amiga/resources/Themes/Default/Theme b/frontends/amiga/resources/Themes/Default/Theme
new file mode 100755
index 000000000..141e84f54
--- /dev/null
+++ b/frontends/amiga/resources/Themes/Default/Theme
@@ -0,0 +1,88 @@
+# Theme description file for AmigaOS NetSurf
+#
+# Format is theme_image:filename
+# Where filename is relative to the directory in which this file
+# is stored.
+#
+# If filename is prefixed by an asterisk, it will be treated as
+# a full path to a file (relative to PROGDIR:)
+#
+# Where theme_image is suffixed by _s or _g, these are the selected
+# and ghosted version of the image.
+#
+# The images can be stored in any format which has a picture datatype
+# available.
+#
+# theme_throbber is a film strip of theme_throbber_frames frames
+# The first frame must be the inactive image.
+#
+theme_nav_west:back.png
+theme_nav_west_s:back_h.png
+theme_nav_west_g:back_g.png
+theme_nav_east:forward.png
+theme_nav_east_s:forward_h.png
+theme_nav_east_g:forward_g.png
+theme_stop:stop.png
+theme_stop_s:stop_h.png
+theme_stop_g:stop_g.png
+theme_reload:reload.png
+theme_reload_s:reload_h.png
+theme_reload_g:reload_g.png
+theme_home:home.png
+theme_home_s:home_h.png
+theme_home_g:home_g.png
+theme_closetab:closetab.png
+theme_closetab_s:closetab.png
+theme_closetab_g:closetab_g.png
+theme_addtab:
+theme_addtab_s:
+theme_addtab_g:
+theme_throbber:Throbber
+theme_throbber_frames:9
+theme_throbber_delay:100
+theme_tab_loading:
+theme_search:search.png
+theme_fave:*PROGDIR:Resources/icons/hotlist-add.png
+theme_unfave:*PROGDIR:Resources/icons/hotlist-rmv.png
+ptr_default:*PROGDIR:Resources/Pointers/Default
+ptr_point:*PROGDIR:Resources/Pointers/Point
+ptr_caret:*PROGDIR:Resources/Pointers/Caret
+ptr_menu:*PROGDIR:Resources/Pointers/Menu
+ptr_up:*PROGDIR:Resources/Pointers/Up
+ptr_down:*PROGDIR:Resources/Pointers/Down
+ptr_left:*PROGDIR:Resources/Pointers/Left
+ptr_right:*PROGDIR:Resources/Pointers/Right
+ptr_rightup:*PROGDIR:Resources/Pointers/RightUp
+ptr_leftdown:*PROGDIR:Resources/Pointers/LeftDown
+ptr_leftup:*PROGDIR:Resources/Pointers/LeftUp
+ptr_rightdown:*PROGDIR:Resources/Pointers/RightDown
+ptr_cross:*PROGDIR:Resources/Pointers/Cross
+ptr_move:*PROGDIR:Resources/Pointers/Move
+ptr_wait:*PROGDIR:Resources/Pointers/Wait
+ptr_help:*PROGDIR:Resources/Pointers/Help
+ptr_nodrop:*PROGDIR:Resources/Pointers/NoDrop
+ptr_notallowed:*PROGDIR:Resources/Pointers/NotAllowed
+ptr_progress:*PROGDIR:Resources/Pointers/Progress
+ptr_blank:*PROGDIR:Resources/Pointers/Blank
+ptr_drag:*PROGDIR:Resources/Pointers/Drag
+ptr32_default:*PROGDIR:Resources/Pointers/Default
+ptr32_point:*PROGDIR:Resources/Pointers/Point
+ptr32_caret:*PROGDIR:Resources/Pointers/Caret
+ptr32_menu:*PROGDIR:Resources/Pointers/Menu
+ptr32_up:*PROGDIR:Resources/Pointers/Up
+ptr32_down:*PROGDIR:Resources/Pointers/Down
+ptr32_left:*PROGDIR:Resources/Pointers/Left
+ptr32_right:*PROGDIR:Resources/Pointers/Right
+ptr32_rightup:*PROGDIR:Resources/Pointers/RightUp
+ptr32_leftdown:*PROGDIR:Resources/Pointers/LeftDown
+ptr32_leftup:*PROGDIR:Resources/Pointers/LeftUp
+ptr32_rightdown:*PROGDIR:Resources/Pointers/RightDown
+ptr32_cross:*PROGDIR:Resources/Pointers/Cross
+ptr32_move:*PROGDIR:Resources/Pointers/Move
+ptr32_wait:*PROGDIR:Resources/Pointers/Wait
+ptr32_help:*PROGDIR:Resources/Pointers/Help
+ptr32_nodrop:*PROGDIR:Resources/Pointers/NoDrop
+ptr32_notallowed:*PROGDIR:Resources/Pointers/NotAllowed
+ptr32_progress:*PROGDIR:Resources/Pointers/Progress
+ptr32_blank:*PROGDIR:Resources/Pointers/Blank
+ptr32_drag:*PROGDIR:Resources/Pointers/Drag
diff --git a/frontends/amiga/resources/Themes/Default/Throbber b/frontends/amiga/resources/Themes/Default/Throbber
new file mode 100644
index 000000000..7dd714248
--- /dev/null
+++ b/frontends/amiga/resources/Themes/Default/Throbber
Binary files differ
diff --git a/frontends/amiga/resources/Themes/Default/back.png b/frontends/amiga/resources/Themes/Default/back.png
new file mode 100755
index 000000000..f219fd807
--- /dev/null
+++ b/frontends/amiga/resources/Themes/Default/back.png
Binary files differ
diff --git a/frontends/amiga/resources/Themes/Default/back_g.png b/frontends/amiga/resources/Themes/Default/back_g.png
new file mode 100644
index 000000000..0796bbbbf
--- /dev/null
+++ b/frontends/amiga/resources/Themes/Default/back_g.png
Binary files differ
diff --git a/frontends/amiga/resources/Themes/Default/back_h.png b/frontends/amiga/resources/Themes/Default/back_h.png
new file mode 100644
index 000000000..35e31386a
--- /dev/null
+++ b/frontends/amiga/resources/Themes/Default/back_h.png
Binary files differ
diff --git a/frontends/amiga/resources/Themes/Default/closetab.png b/frontends/amiga/resources/Themes/Default/closetab.png
new file mode 100644
index 000000000..21d97f11d
--- /dev/null
+++ b/frontends/amiga/resources/Themes/Default/closetab.png
Binary files differ
diff --git a/frontends/amiga/resources/Themes/Default/closetab_g.png b/frontends/amiga/resources/Themes/Default/closetab_g.png
new file mode 100644
index 000000000..a50fa0f45
--- /dev/null
+++ b/frontends/amiga/resources/Themes/Default/closetab_g.png
Binary files differ
diff --git a/frontends/amiga/resources/Themes/Default/forward.png b/frontends/amiga/resources/Themes/Default/forward.png
new file mode 100755
index 000000000..f20c0cdf5
--- /dev/null
+++ b/frontends/amiga/resources/Themes/Default/forward.png
Binary files differ
diff --git a/frontends/amiga/resources/Themes/Default/forward_g.png b/frontends/amiga/resources/Themes/Default/forward_g.png
new file mode 100644
index 000000000..d847543f3
--- /dev/null
+++ b/frontends/amiga/resources/Themes/Default/forward_g.png
Binary files differ
diff --git a/frontends/amiga/resources/Themes/Default/forward_h.png b/frontends/amiga/resources/Themes/Default/forward_h.png
new file mode 100644
index 000000000..90c0fe2c7
--- /dev/null
+++ b/frontends/amiga/resources/Themes/Default/forward_h.png
Binary files differ
diff --git a/frontends/amiga/resources/Themes/Default/home.png b/frontends/amiga/resources/Themes/Default/home.png
new file mode 100755
index 000000000..604796025
--- /dev/null
+++ b/frontends/amiga/resources/Themes/Default/home.png
Binary files differ
diff --git a/frontends/amiga/resources/Themes/Default/home_g.png b/frontends/amiga/resources/Themes/Default/home_g.png
new file mode 100644
index 000000000..a644b0b03
--- /dev/null
+++ b/frontends/amiga/resources/Themes/Default/home_g.png
Binary files differ
diff --git a/frontends/amiga/resources/Themes/Default/home_h.png b/frontends/amiga/resources/Themes/Default/home_h.png
new file mode 100644
index 000000000..2d6be5f34
--- /dev/null
+++ b/frontends/amiga/resources/Themes/Default/home_h.png
Binary files differ
diff --git a/frontends/amiga/resources/Themes/Default/reload.png b/frontends/amiga/resources/Themes/Default/reload.png
new file mode 100755
index 000000000..a81f650b0
--- /dev/null
+++ b/frontends/amiga/resources/Themes/Default/reload.png
Binary files differ
diff --git a/frontends/amiga/resources/Themes/Default/reload_g.png b/frontends/amiga/resources/Themes/Default/reload_g.png
new file mode 100644
index 000000000..5251f206c
--- /dev/null
+++ b/frontends/amiga/resources/Themes/Default/reload_g.png
Binary files differ
diff --git a/frontends/amiga/resources/Themes/Default/reload_h.png b/frontends/amiga/resources/Themes/Default/reload_h.png
new file mode 100644
index 000000000..76e554e49
--- /dev/null
+++ b/frontends/amiga/resources/Themes/Default/reload_h.png
Binary files differ
diff --git a/frontends/amiga/resources/Themes/Default/search.png b/frontends/amiga/resources/Themes/Default/search.png
new file mode 100644
index 000000000..a59c12b8f
--- /dev/null
+++ b/frontends/amiga/resources/Themes/Default/search.png
Binary files differ
diff --git a/frontends/amiga/resources/Themes/Default/stop.png b/frontends/amiga/resources/Themes/Default/stop.png
new file mode 100755
index 000000000..df64c5747
--- /dev/null
+++ b/frontends/amiga/resources/Themes/Default/stop.png
Binary files differ
diff --git a/frontends/amiga/resources/Themes/Default/stop_g.png b/frontends/amiga/resources/Themes/Default/stop_g.png
new file mode 100644
index 000000000..a2efa9e3d
--- /dev/null
+++ b/frontends/amiga/resources/Themes/Default/stop_g.png
Binary files differ
diff --git a/frontends/amiga/resources/Themes/Default/stop_h.png b/frontends/amiga/resources/Themes/Default/stop_h.png
new file mode 100644
index 000000000..3c3377cdf
--- /dev/null
+++ b/frontends/amiga/resources/Themes/Default/stop_h.png
Binary files differ
diff --git a/frontends/amiga/resources/blankspace.png b/frontends/amiga/resources/blankspace.png
new file mode 100644
index 000000000..1f1e672d0
--- /dev/null
+++ b/frontends/amiga/resources/blankspace.png
Binary files differ
diff --git a/frontends/amiga/resources/ca-bundle b/frontends/amiga/resources/ca-bundle
new file mode 120000
index 000000000..ad2dd6b55
--- /dev/null
+++ b/frontends/amiga/resources/ca-bundle
@@ -0,0 +1 @@
+../../!NetSurf/Resources/ca-bundle \ No newline at end of file
diff --git a/frontends/amiga/resources/de b/frontends/amiga/resources/de
new file mode 120000
index 000000000..38128816c
--- /dev/null
+++ b/frontends/amiga/resources/de
@@ -0,0 +1 @@
+../../!NetSurf/Resources/de \ No newline at end of file
diff --git a/frontends/amiga/resources/default.css b/frontends/amiga/resources/default.css
new file mode 100644
index 000000000..7101e191f
--- /dev/null
+++ b/frontends/amiga/resources/default.css
@@ -0,0 +1,7 @@
+@import "nsdefault.css";
+
+/* Make NetSurf look a little more like the default OS4 theme */
+
+input { border: medium inset #ddd; }
+textarea { border: medium inset #ddd; }
+select { background-color: #ddd; border: medium groove #ddd; }
diff --git a/frontends/amiga/resources/default.css.info b/frontends/amiga/resources/default.css.info
new file mode 100644
index 000000000..0f7ab4b66
--- /dev/null
+++ b/frontends/amiga/resources/default.css.info
Binary files differ
diff --git a/frontends/amiga/resources/en b/frontends/amiga/resources/en
new file mode 120000
index 000000000..d1dfaa9d2
--- /dev/null
+++ b/frontends/amiga/resources/en
@@ -0,0 +1 @@
+../../!NetSurf/Resources/en \ No newline at end of file
diff --git a/frontends/amiga/resources/favicon.png b/frontends/amiga/resources/favicon.png
new file mode 100644
index 000000000..53234c38c
--- /dev/null
+++ b/frontends/amiga/resources/favicon.png
Binary files differ
diff --git a/frontends/amiga/resources/fr b/frontends/amiga/resources/fr
new file mode 120000
index 000000000..df1cbe3a1
--- /dev/null
+++ b/frontends/amiga/resources/fr
@@ -0,0 +1 @@
+../../!NetSurf/Resources/fr \ No newline at end of file
diff --git a/frontends/amiga/resources/it b/frontends/amiga/resources/it
new file mode 120000
index 000000000..6177e9176
--- /dev/null
+++ b/frontends/amiga/resources/it
@@ -0,0 +1 @@
+../../!NetSurf/Resources/it \ No newline at end of file
diff --git a/frontends/amiga/resources/mimetypes b/frontends/amiga/resources/mimetypes
new file mode 100644
index 000000000..822e12b82
--- /dev/null
+++ b/frontends/amiga/resources/mimetypes
@@ -0,0 +1,112 @@
+; This file contains a list of MIME types that can be handled by NetSurf,
+; mapping them to DataType and DefIcons format names.
+;
+; It is parsed by ReadArgs with the following template:
+; MIMETYPE/A,DT=DATATYPE/K,TYPE=DEFICON/K,CMD=PLUGINCMD/K
+;
+; Note: DataType name is case sensitive and is the name MultiView
+; displays in the "About" window, *not* the filename of the DataType
+; descriptor.
+;
+; The first entry for a type is the one which will be used for reverse
+; lookups (ie. translating from DataTypes to MIME)
+;
+; Any DataTypes installed that are not present in this list will be
+; assigned a MIME type constructed from the DataTypes superclass
+; and a lowercase version of the full format name (eg. ILBM becomes
+; image/ilbm)
+;
+; Please DO NOT edit this file, any changes will be overwritten on
+; reinstall/upgrade. Instead create a file called mimetypes.user
+; with any additions, and place it in your user data directory.
+
+;
+; Image types
+;
+
+; BMP
+image/bmp DT=BMP TYPE=bmp
+image/ms-bmp DT=BMP TYPE=bmp
+image/x-bitmap DT=BMP TYPE=bmp
+image/x-bmp DT=BMP TYPE=bmp
+image/x-ms-bmp DT=BMP TYPE=bmp
+image/x-win-bitmap DT=BMP TYPE=bmp
+image/x-windows-bmp DT=BMP TYPE=bmp
+image/x-xbitmap DT=BMP TYPE=bmp
+application/bmp DT=BMP TYPE=bmp
+application/preview DT=BMP TYPE=bmp
+application/x-bmp DT=BMP TYPE=bmp
+application/x-win-bitmap DT=BMP TYPE=bmp
+
+; GIF
+; Expect problems with GIF anims if GIFANIM datatype installed
+image/gif DT=GIF TYPE=gif
+;image/gif DT=GIFANIM TYPE=gif
+
+; ICO
+image/ico DT="Windows ICO" TYPE=ico
+image/vnd.microsoft.icon DT="Windows ICO" TYPE=ico
+image/x-icon DT="Windows ICO" TYPE=ico
+application/ico DT="Windows ICO" TYPE=ico
+application/x-ico DT="Windows ICO" TYPE=ico
+
+; .info
+image/x-amiga-icon DT="AmigaOS Icon"
+
+; JNG
+image/jng DT=JNG TYPE=jng
+image/x-jng DT=JNG TYPE=jng
+
+; JPEG
+image/jpeg DT=JPEG TYPE=jpeg
+image/jpg DT=JPEG TYPE=jpeg
+image/pjpeg DT=JPEG TYPE=jpeg
+
+; JPEG2000
+image/jp2 DT=JP2 TYPE=jp2
+image/jpeg2000 DT=JP2 TYPE=jp2
+
+; MNG
+image/mng DT=MNG TYPE=mng
+image/x-mng DT=MNG TYPE=mng
+video/mng DT=MNG TYPE=mng
+video/x-mng DT=MNG TYPE=mng
+
+; PNG
+image/png DT=PNG TYPE=png
+
+; RISC OS Sprite
+image/x-riscos-sprite DT=Sprite TYPE=rosprite
+
+; SVG
+image/svg DT=SVG TYPE=svg
+image/svg+xml DT=SVG TYPE=svg
+
+; WebP
+image/webp DT=WebP TYPE=webp
+
+
+;
+; Audio types
+;
+
+; RIFF WAVE
+audio/wave DT=WAVE TYPE=wav
+audio/x-wav DT=WAVE TYPE=wav
+
+; MP3
+audio/mpeg DT="MPEG Audio" TYPE=mp3 CMD=APPDIR:MPlayer
+
+
+;
+; Experimental
+;
+
+; SWF
+; application/x-shockwave-flash CMD=gnash:aos4-gnash
+
+; Video
+; video/mpeg CMD=APPDIR:MPlayer
+; video/quicktime CMD=APPDIR:MPlayer
+; video/x-msvideo CMD=APPDIR:MPlayer
+; video/x-ms-wmv CMD=APPDIR:MPlayer
diff --git a/frontends/amiga/resources/nl b/frontends/amiga/resources/nl
new file mode 120000
index 000000000..a07bd0469
--- /dev/null
+++ b/frontends/amiga/resources/nl
@@ -0,0 +1 @@
+../../!NetSurf/Resources/nl \ No newline at end of file
diff --git a/frontends/amiga/resources/nsdefault.css b/frontends/amiga/resources/nsdefault.css
new file mode 120000
index 000000000..6d2d4da5b
--- /dev/null
+++ b/frontends/amiga/resources/nsdefault.css
@@ -0,0 +1 @@
+../../!NetSurf/Resources/CSS,f79 \ No newline at end of file
diff --git a/frontends/amiga/resources/quirks.css b/frontends/amiga/resources/quirks.css
new file mode 120000
index 000000000..d9fb80334
--- /dev/null
+++ b/frontends/amiga/resources/quirks.css
@@ -0,0 +1 @@
+../../!NetSurf/Resources/Quirks,f79 \ No newline at end of file
diff --git a/frontends/amiga/resources/splash.png b/frontends/amiga/resources/splash.png
new file mode 100644
index 000000000..569cb08fa
--- /dev/null
+++ b/frontends/amiga/resources/splash.png
Binary files differ
diff --git a/frontends/amiga/rtg.c b/frontends/amiga/rtg.c
new file mode 100644
index 000000000..5e1cac290
--- /dev/null
+++ b/frontends/amiga/rtg.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2015 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Abstract RTG functions for newer/older/non-P96 systems
+ */
+
+#include "amiga/rtg.h"
+
+struct BitMap *ami_rtg_allocbitmap(ULONG width, ULONG height, ULONG depth,
+ ULONG flags, struct BitMap *friend, RGBFTYPE format)
+{
+ if(P96Base == NULL) {
+#ifndef __amigaos4__
+ if(depth > 8) depth = 8;
+#endif
+ return AllocBitMap(width, height, depth, flags, friend);
+ } else {
+ return p96AllocBitMap(width, height, depth, flags, friend, format);
+ }
+}
+
+void ami_rtg_freebitmap(struct BitMap *bm)
+{
+ if(P96Base == NULL) {
+ return FreeBitMap(bm);
+ } else {
+ return p96FreeBitMap(bm);
+ }
+}
+
+void ami_rtg_writepixelarray(UBYTE *pixdata, struct BitMap *bm,
+ ULONG width, ULONG height, ULONG bpr, ULONG format)
+{
+ struct RastPort trp;
+
+ InitRastPort(&trp);
+ trp.BitMap = bm;
+
+ /* This requires P96 or gfx.lib v54 currently */
+ if(P96Base == NULL) {
+#ifdef __amigaos4__
+ if(GfxBase->LibNode.lib_Version >= 54) {
+ WritePixelArray(pixdata, 0, 0, bpr, PIXF_R8G8B8A8, &trp, 0, 0, width, height);
+ }
+#endif
+ } else {
+ struct RenderInfo ri;
+
+ ri.Memory = pixdata;
+ ri.BytesPerRow = bpr;
+ ri.RGBFormat = format;
+
+ p96WritePixelArray((struct RenderInfo *)&ri, 0, 0, &trp, 0, 0, width, height);
+ }
+}
+
diff --git a/frontends/amiga/rtg.h b/frontends/amiga/rtg.h
new file mode 100644
index 000000000..72f5bf38b
--- /dev/null
+++ b/frontends/amiga/rtg.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2015 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Abstract RTG functions for newer/older/non-P96 systems
+ */
+
+#ifndef AMIGA_RTG_H
+#define AMIGA_RTG_H 1
+#include <proto/graphics.h>
+#include <proto/Picasso96API.h>
+
+/* Wrappers for Alloc/FreeBitMap */
+struct BitMap *ami_rtg_allocbitmap(ULONG width, ULONG height, ULONG depth,
+ ULONG flags, struct BitMap *friend, RGBFTYPE format);
+void ami_rtg_freebitmap(struct BitMap *bm);
+
+/* WritePixelArray wrapper. This isn't entirely (at all) equivalent to p96WPA */
+void ami_rtg_writepixelarray(UBYTE *pixdata, struct BitMap *bm,
+ ULONG width, ULONG height, ULONG bpr, ULONG format);
+#endif
+
diff --git a/frontends/amiga/save_pdf.c b/frontends/amiga/save_pdf.c
new file mode 100644
index 000000000..b70fc5716
--- /dev/null
+++ b/frontends/amiga/save_pdf.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2008 John Tytgat <joty@netsurf-browser.org>
+ * Copyright 2008 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Export a content as a PDF file (implementation).
+ */
+
+#include "utils/config.h"
+#ifdef WITH_PDF_EXPORT
+
+#include <stdbool.h>
+#include "content/content.h"
+#include "desktop/print.h"
+#include "desktop/save_pdf/font_haru.h"
+#include "desktop/save_pdf/pdf_plotters.h"
+#include "amiga/save_pdf.h"
+#include "utils/log.h"
+#include "utils/config.h"
+
+/**
+ * Export a content as a PDF file.
+ *
+ * \param c content to export
+ * \param path path to save PDF as
+ * \return true on success, false on error and error reported
+ */
+bool save_as_pdf(struct hlcache_handle *c, const char *path)
+{
+ struct print_settings *psettings;
+
+ if(!ami_download_check_overwrite(path, NULL)) return false;
+
+ psettings = print_make_settings(PRINT_OPTIONS, path, &haru_nsfont);
+ if (psettings == NULL)
+ return false;
+
+ if (!print_basic_run(c, &pdf_printer, psettings))
+ return false;
+
+ return true;
+}
+
+#endif
diff --git a/frontends/amiga/save_pdf.h b/frontends/amiga/save_pdf.h
new file mode 100644
index 000000000..4a5a7edde
--- /dev/null
+++ b/frontends/amiga/save_pdf.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2008 John Tytgat <joty@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_AMIGA_SAVE_PDF_H_
+#define _NETSURF_AMIGA_SAVE_PDF_H_
+
+#include "utils/config.h"
+#ifdef WITH_PDF_EXPORT
+
+struct content;
+
+bool save_as_pdf(struct hlcache_handle *c, const char *path);
+
+#endif /* WITH_PDF_EXPORT */
+
+#endif
diff --git a/frontends/amiga/schedule.c b/frontends/amiga/schedule.c
new file mode 100755
index 000000000..e34c00017
--- /dev/null
+++ b/frontends/amiga/schedule.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2008 - 2014 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "amiga/os3support.h"
+
+#include <proto/dos.h>
+#include <proto/exec.h>
+#include <proto/timer.h>
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <pbl.h>
+
+#include "utils/errors.h"
+#include "utils/log.h"
+
+#include "amiga/misc.h"
+#include "amiga/schedule.h"
+
+static struct TimeRequest *tioreq;
+struct Device *TimerBase;
+#ifdef __amigaos4__
+struct TimerIFace *ITimer;
+#endif
+
+static APTR pool_nscb = NULL;
+static APTR pool_timereq = NULL;
+
+struct nscallback
+{
+ struct TimeVal tv;
+ void *callback;
+ void *p;
+ struct TimeRequest *treq;
+};
+
+static PblHeap *schedule_list;
+
+/**
+ * Remove timer event
+ *
+ * \param nscb callback
+ *
+ * The timer event for the callback is aborted
+ */
+
+static void ami_schedule_remove_timer_event(struct nscallback *nscb)
+{
+ if(!nscb) return;
+
+ if(nscb->treq)
+ {
+ if(CheckIO((struct IORequest *)nscb->treq)==NULL)
+ AbortIO((struct IORequest *)nscb->treq);
+
+ WaitIO((struct IORequest *)nscb->treq);
+ ami_misc_itempool_free(pool_timereq, nscb->treq, sizeof(struct TimeRequest));
+ }
+}
+
+/**
+ * Add timer event
+ *
+ * \param nscb callback
+ * \param t time in ms
+ *
+ * NetSurf will be signalled in t ms for this event.
+ */
+
+static nserror ami_schedule_add_timer_event(struct nscallback *nscb, int t)
+{
+ struct TimeVal tv;
+ ULONG time_us = t * 1000; /* t converted to µs */
+
+ nscb->tv.Seconds = time_us / 1000000;
+ nscb->tv.Microseconds = time_us % 1000000;
+
+ GetSysTime(&tv);
+ AddTime(&nscb->tv,&tv); // now contains time when event occurs
+
+ if((nscb->treq = ami_misc_itempool_alloc(pool_timereq, sizeof(struct TimeRequest)))) {
+ *nscb->treq = *tioreq;
+ nscb->treq->Request.io_Command=TR_ADDREQUEST;
+ nscb->treq->Time.Seconds=nscb->tv.Seconds; // secs
+ nscb->treq->Time.Microseconds=nscb->tv.Microseconds; // micro
+ SendIO((struct IORequest *)nscb->treq);
+ } else {
+ return NSERROR_NOMEM;
+ }
+
+ return NSERROR_OK;
+}
+
+/**
+ * Locate a scheduled callback
+ *
+ * \param callback callback function
+ * \param p user parameter, passed to callback function
+ * \param remove remove callback from the heap
+ *
+ * A scheduled callback matching both callback and p is returned, or NULL if none present.
+ */
+
+static struct nscallback *ami_schedule_locate(void (*callback)(void *p), void *p, bool remove)
+{
+ PblIterator *iterator;
+ struct nscallback *nscb;
+ bool found_cb = false;
+
+ /* check there is something on the list */
+ if (schedule_list == NULL) return NULL;
+ if(pblHeapIsEmpty(schedule_list)) return NULL;
+
+ iterator = pblHeapIterator(schedule_list);
+
+ while ((nscb = pblIteratorNext(iterator)) != -1) {
+ if ((nscb->callback == callback) && (nscb->p == p)) {
+ if (remove == true) pblIteratorRemove(iterator);
+ found_cb = true;
+ break;
+ }
+ };
+
+ pblIteratorFree(iterator);
+
+ if (found_cb == true) return nscb;
+ else return NULL;
+}
+
+/**
+ * Reschedule a callback.
+ *
+ * \param nscb callback
+ * \param t time in ms
+ *
+ * The nscallback will be rescheduled for t ms.
+ */
+
+static nserror ami_schedule_reschedule(struct nscallback *nscb, int t)
+{
+ ami_schedule_remove_timer_event(nscb);
+ if (ami_schedule_add_timer_event(nscb, t) != NSERROR_OK)
+ return NSERROR_NOMEM;
+
+ pblHeapConstruct(schedule_list);
+ return NSERROR_OK;
+}
+
+/**
+ * Unschedule a callback.
+ *
+ * \param callback callback function
+ * \param p user parameter, passed to callback function
+ *
+ * All scheduled callbacks matching both callback and p are removed.
+ */
+
+static nserror schedule_remove(void (*callback)(void *p), void *p)
+{
+ struct nscallback *nscb;
+
+ nscb = ami_schedule_locate(callback, p, true);
+
+ if(nscb != NULL) {
+ ami_schedule_remove_timer_event(nscb);
+ ami_misc_itempool_free(pool_nscb, nscb, sizeof(struct nscallback));
+ pblHeapConstruct(schedule_list);
+ }
+
+ return NSERROR_OK;
+}
+
+static void schedule_remove_all(void)
+{
+ PblIterator *iterator;
+ struct nscallback *nscb;
+
+ if(pblHeapIsEmpty(schedule_list)) return;
+
+ iterator = pblHeapIterator(schedule_list);
+
+ while ((nscb = pblIteratorNext(iterator)) != -1)
+ {
+ ami_schedule_remove_timer_event(nscb);
+ pblIteratorRemove(iterator);
+ ami_misc_itempool_free(pool_nscb, nscb, sizeof(struct nscallback));
+ };
+
+ pblIteratorFree(iterator);
+}
+
+static int ami_schedule_compare(const void *prev, const void *next)
+{
+ struct nscallback *nscb1 = *(struct nscallback **)prev;
+ struct nscallback *nscb2 = *(struct nscallback **)next;
+
+ return CmpTime(&nscb1->tv, &nscb2->tv);
+}
+
+
+/**
+ * Process events up to current time.
+ *
+ * This implementation only takes the top entry off the heap, it does not
+ * venture to later scheduled events until the next time it is called -
+ * immediately afterwards, if we're in a timer signalled loop.
+ */
+static void ami_scheduler_run(void)
+{
+ struct nscallback *nscb;
+ struct TimeVal tv;
+ void (*callback)(void *p);
+ void *p;
+
+ nscb = pblHeapGetFirst(schedule_list);
+ if(nscb == -1) return;
+
+ /* Ensure the scheduled event time has passed (CmpTime<=0)
+ * in case something been deleted between the timer
+ * signalling us and us responding to it.
+ */
+ GetSysTime(&tv);
+ if(CmpTime(&tv, &nscb->tv) > 0) return;
+
+ callback = nscb->callback;
+ p = nscb->p;
+
+ ami_schedule_remove_timer_event(nscb);
+ pblHeapRemoveFirst(schedule_list);
+ ami_misc_itempool_free(pool_nscb, nscb, sizeof(struct nscallback));
+
+ LOG("Running scheduled callback %p with arg %p", callback, p);
+ callback(p);
+
+ return;
+}
+
+static void ami_schedule_open_timer(struct MsgPort *msgport)
+{
+#ifdef __amigaos4__
+ tioreq = (struct TimeRequest *)AllocSysObjectTags(ASOT_IOREQUEST,
+ ASOIOR_Size,sizeof(struct TimeRequest),
+ ASOIOR_ReplyPort,msgport,
+ ASO_NoTrack,FALSE,
+ TAG_DONE);
+#else
+ tioreq = (struct TimeRequest *)CreateIORequest(msgport, sizeof(struct TimeRequest));
+#endif
+
+ OpenDevice("timer.device", UNIT_WAITUNTIL, (struct IORequest *)tioreq, 0);
+
+ TimerBase = (struct Device *)tioreq->Request.io_Device;
+#ifdef __amigaos4__
+ ITimer = (struct TimerIFace *)GetInterface((struct Library *)TimerBase, "main", 1, NULL);
+#endif
+}
+
+static void ami_schedule_close_timer(void)
+{
+#ifdef __amigaos4__
+ if(ITimer) DropInterface((struct Interface *)ITimer);
+#endif
+ CloseDevice((struct IORequest *) tioreq);
+ FreeSysObject(ASOT_IOREQUEST, tioreq);
+}
+
+/* exported interface documented in amiga/schedule.h */
+nserror ami_schedule_create(struct MsgPort *msgport)
+{
+ pool_nscb = ami_misc_itempool_create(sizeof(struct nscallback));
+ pool_timereq = ami_misc_itempool_create(sizeof(struct TimeRequest));
+
+ ami_schedule_open_timer(msgport);
+ schedule_list = pblHeapNew();
+ if(schedule_list == PBL_ERROR_OUT_OF_MEMORY) return NSERROR_NOMEM;
+
+ pblHeapSetCompareFunction(schedule_list, ami_schedule_compare);
+
+ return NSERROR_OK;
+}
+
+/* exported interface documented in amiga/schedule.h */
+void ami_schedule_free(void)
+{
+ schedule_remove_all();
+ pblHeapFree(schedule_list); // this should be empty at this point
+ schedule_list = NULL;
+
+ ami_schedule_close_timer();
+
+ ami_misc_itempool_delete(pool_timereq);
+ ami_misc_itempool_delete(pool_nscb);
+}
+
+/* exported function documented in amiga/schedule.h */
+nserror ami_schedule(int t, void (*callback)(void *p), void *p)
+{
+ struct nscallback *nscb;
+
+ if(schedule_list == NULL) return NSERROR_INIT_FAILED;
+ if(t < 0) return schedule_remove(callback, p);
+
+ if ((nscb = ami_schedule_locate(callback, p, false))) {
+ return ami_schedule_reschedule(nscb, t);
+ }
+
+ nscb = ami_misc_itempool_alloc(pool_nscb, sizeof(struct nscallback));
+ if(!nscb) return NSERROR_NOMEM;
+
+ if (ami_schedule_add_timer_event(nscb, t) != NSERROR_OK)
+ return NSERROR_NOMEM;
+
+ nscb->callback = callback;
+ nscb->p = p;
+
+ pblHeapInsert(schedule_list, nscb);
+
+ return NSERROR_OK;
+
+}
+
+/* exported interface documented in amiga/schedule.h */
+void ami_schedule_handle(struct MsgPort *nsmsgport)
+{
+ /* nsmsgport is the NetSurf message port that the scheduler task
+ * (or timer.device in no-async mode) is sending messages to. */
+
+ struct TimerRequest *timermsg;
+
+ while((timermsg = (struct TimerRequest *)GetMsg(nsmsgport))) {
+ /* reply first, as we don't need the message contents and
+ * it crashes if we reply after schedule_run has executed.
+ */
+ ReplyMsg((struct Message *)timermsg);
+ ami_scheduler_run();
+ }
+}
+
diff --git a/frontends/amiga/schedule.h b/frontends/amiga/schedule.h
new file mode 100755
index 000000000..fb648eaa4
--- /dev/null
+++ b/frontends/amiga/schedule.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2008-2014 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_SCHEDULE_H
+#define AMIGA_SCHEDULE_H
+#include "amiga/os3support.h"
+
+/**
+ * Schedule a callback.
+ *
+ * \param t interval before the callback should be made / ms
+ * \param callback callback function
+ * \param p user parameter, passed to callback function
+ * \return NSERROR_OK on sucess or appropriate error on faliure
+ *
+ * The callback function will be called as soon as possible after t ms have
+ * passed.
+ */
+nserror ami_schedule(int t, void (*callback)(void *p), void *p);
+
+/**
+ * Handle a message received from the scheduler process.
+ *
+ * \param nsmsgport Message port to process.
+ */
+void ami_schedule_handle(struct MsgPort *nsmsgport);
+
+/**
+ * Initialise amiga scheduler
+ *
+ * \param msgport opened message port
+ *
+ * \return error.
+ */
+nserror ami_schedule_create(struct MsgPort *msgport);
+
+/**
+ * Finalise amiga scheduler
+ */
+void ami_schedule_free(void);
+#endif
+
diff --git a/frontends/amiga/search.c b/frontends/amiga/search.c
new file mode 100755
index 000000000..5ca979220
--- /dev/null
+++ b/frontends/amiga/search.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk>
+ * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net>
+ * Copyright 2008 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Free text search (implementation)
+ */
+
+#include "amiga/os3support.h"
+
+#include "utils/config.h"
+#include <ctype.h>
+#include <string.h>
+
+#include <proto/intuition.h>
+#include <proto/exec.h>
+#include <proto/window.h>
+#include <proto/layout.h>
+#include <proto/string.h>
+#include <proto/button.h>
+#include <proto/label.h>
+#include <proto/checkbox.h>
+#include <classes/window.h>
+#include <gadgets/layout.h>
+#include <gadgets/string.h>
+#include <gadgets/button.h>
+#include <gadgets/checkbox.h>
+#include <images/label.h>
+#include <reaction/reaction_macros.h>
+
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "content/content.h"
+#include "desktop/browser.h"
+#include "desktop/search.h"
+#include "desktop/mouse.h"
+#include "desktop/gui_window.h"
+#include "desktop/gui_search.h"
+
+#include "amiga/libs.h"
+#include "amiga/misc.h"
+#include "amiga/search.h"
+#include "amiga/object.h"
+#include "amiga/theme.h"
+
+#ifndef NOF_ELEMENTS
+#define NOF_ELEMENTS(array) (sizeof(array)/sizeof(*(array)))
+#endif
+
+static bool search_insert;
+
+static struct find_window *fwin = NULL;
+
+search_flags_t ami_search_flags(void);
+char *ami_search_string(void);
+static void ami_search_set_status(bool found, void *p);
+static void ami_search_set_hourglass(bool active, void *p);
+static void ami_search_add_recent(const char *string, void *p);
+static void ami_search_set_forward_state(bool active, void *p);
+static void ami_search_set_back_state(bool active, void *p);
+
+static struct gui_search_table search_table = {
+ ami_search_set_status,
+ ami_search_set_hourglass,
+ ami_search_add_recent,
+ ami_search_set_forward_state,
+ ami_search_set_back_state,
+};
+
+struct gui_search_table *amiga_search_table = &search_table;
+
+/**
+ * Change the displayed search status.
+ *
+ * \param gwin gui window to open search for.
+ */
+void ami_search_open(struct gui_window *gwin)
+{
+ search_insert = true;
+
+ if(fwin)
+ {
+ browser_window_search_clear(fwin->gwin->bw);
+ fwin->gwin->shared->searchwin = NULL;
+ fwin->gwin = gwin;
+ gwin->shared->searchwin = fwin;
+ WindowToFront(fwin->win);
+ ActivateWindow(fwin->win);
+ return;
+ }
+
+ fwin = ami_misc_allocvec_clear(sizeof(struct find_window), 0);
+
+ fwin->objects[OID_MAIN] = WindowObj,
+ WA_ScreenTitle, ami_gui_get_screen_title(),
+ WA_Title,messages_get("FindTextNS"),
+ WA_Activate, TRUE,
+ WA_DepthGadget, TRUE,
+ WA_DragBar, TRUE,
+ WA_CloseGadget, TRUE,
+ WA_SizeGadget, TRUE,
+ WA_PubScreen,scrn,
+ WINDOW_SharedPort,sport,
+ WINDOW_UserData,fwin,
+ WINDOW_IconifyGadget, FALSE,
+ WINDOW_LockHeight,TRUE,
+ WINDOW_Position, WPOS_CENTERSCREEN,
+ WINDOW_ParentGroup, fwin->objects[GID_MAIN] = LayoutVObj,
+ LAYOUT_AddChild, fwin->objects[GID_SEARCHSTRING] = StringObj,
+ GA_ID,GID_SEARCHSTRING,
+ GA_TabCycle,TRUE,
+ GA_RelVerify,TRUE,
+ StringEnd,
+ CHILD_WeightedHeight,0,
+ LAYOUT_AddChild, fwin->objects[GID_CASE] = CheckBoxObj,
+ GA_ID,GID_CASE,
+ GA_Text,messages_get("CaseSens"),
+ GA_Selected,FALSE,
+ GA_TabCycle,TRUE,
+ GA_RelVerify,TRUE,
+ CheckBoxEnd,
+ LAYOUT_AddChild, fwin->objects[GID_SHOWALL] = CheckBoxObj,
+ GA_ID,GID_SHOWALL,
+ GA_Text,messages_get("ShowAll"),
+ GA_Selected,FALSE,
+ GA_TabCycle,TRUE,
+ GA_RelVerify,TRUE,
+ CheckBoxEnd,
+
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_AddChild, fwin->objects[GID_PREV] = ButtonObj,
+ GA_ID,GID_PREV,
+ GA_RelVerify,TRUE,
+ GA_Text,messages_get("Prev"),
+ GA_TabCycle,TRUE,
+ GA_Disabled,TRUE,
+ ButtonEnd,
+ CHILD_WeightedHeight,0,
+ LAYOUT_AddChild, fwin->objects[GID_NEXT] = ButtonObj,
+ GA_ID,GID_NEXT,
+ GA_RelVerify,TRUE,
+ GA_Text,messages_get("Next"),
+ GA_TabCycle,TRUE,
+ GA_Disabled,TRUE,
+ ButtonEnd,
+ LayoutEnd,
+ CHILD_WeightedHeight,0,
+ EndGroup,
+ EndWindow;
+
+ fwin->win = (struct Window *)RA_OpenWindow(fwin->objects[OID_MAIN]);
+ fwin->gwin = gwin;
+ fwin->node = AddObject(window_list,AMINS_FINDWINDOW);
+ fwin->node->objstruct = fwin;
+ gwin->shared->searchwin = fwin;
+
+ ActivateLayoutGadget((struct Gadget *)fwin->objects[GID_MAIN], fwin->win,
+ NULL, (ULONG)fwin->objects[GID_SEARCHSTRING]);
+}
+
+void ami_search_close(void)
+{
+ browser_window_search_clear(fwin->gwin->bw);
+ fwin->gwin->shared->searchwin = NULL;
+ DisposeObject(fwin->objects[OID_MAIN]);
+ DelObject(fwin->node);
+ fwin=NULL;
+}
+
+BOOL ami_search_event(void)
+{
+ /* return TRUE if window destroyed */
+ ULONG result;
+ uint16 code;
+ search_flags_t flags;
+
+ while((result = RA_HandleInput(fwin->objects[OID_MAIN],&code)) != WMHI_LASTMSG)
+ {
+ switch(result & WMHI_CLASSMASK) // class
+ {
+ case WMHI_GADGETUP:
+ switch(result & WMHI_GADGETMASK)
+ {
+ case GID_SEARCHSTRING:
+ browser_window_search_clear(fwin->gwin->bw);
+
+ RefreshSetGadgetAttrs((struct Gadget *)fwin->objects[GID_PREV],
+ fwin->win, NULL,
+ GA_Disabled, FALSE,
+ TAG_DONE);
+
+ RefreshSetGadgetAttrs((struct Gadget *)fwin->objects[GID_NEXT],
+ fwin->win, NULL,
+ GA_Disabled, FALSE,
+ TAG_DONE);
+
+ /* fall through */
+
+ case GID_NEXT:
+ search_insert = true;
+ flags = SEARCH_FLAG_FORWARDS |
+ ami_search_flags();
+ browser_window_search(
+ fwin->gwin->bw,
+ NULL,
+ flags, ami_search_string());
+ ActivateWindow(fwin->gwin->shared->win);
+ break;
+
+ case GID_PREV:
+ search_insert = true;
+ flags = ~SEARCH_FLAG_FORWARDS &
+ ami_search_flags();
+ browser_window_search(
+ fwin->gwin->bw,
+ NULL,
+ flags, ami_search_string());
+ ActivateWindow(fwin->gwin->shared->win);
+ break;
+ }
+ break;
+
+ case WMHI_CLOSEWINDOW:
+ ami_search_close();
+ return TRUE;
+ break;
+ }
+ }
+ return FALSE;
+}
+
+/**
+* Change the displayed search status.
+* \param found search pattern matched in text
+* \param p the pointer sent to search_verify_new() / search_create_context()
+*/
+
+void ami_search_set_status(bool found, void *p)
+{
+}
+
+/**
+* display hourglass while searching
+* \param active start/stop indicator
+* \param p the pointer sent to search_verify_new() / search_create_context()
+*/
+
+void ami_search_set_hourglass(bool active, void *p)
+{
+ if(active)
+ ami_update_pointer(fwin->win, GUI_POINTER_WAIT);
+ else
+ ami_update_pointer(fwin->win, GUI_POINTER_DEFAULT);
+}
+
+/**
+* retrieve string being searched for from gui
+*/
+
+char *ami_search_string(void)
+{
+ char *text;
+ GetAttr(STRINGA_TextVal,fwin->objects[GID_SEARCHSTRING],(ULONG *)&text);
+ return text;
+
+}
+
+/**
+* add search string to recent searches list
+* front is at liberty how to implement the bare notification
+* should normally store a strdup() of the string;
+* core gives no guarantee of the integrity of the const char *
+* \param string search pattern
+* \param p the pointer sent to search_verify_new() / search_create_context()
+*/
+
+void ami_search_add_recent(const char *string, void *p)
+{
+}
+
+/**
+* activate search forwards button in gui
+* \param active activate/inactivate
+* \param p the pointer sent to search_verify_new() / search_create_context()
+*/
+
+void ami_search_set_forward_state(bool active, void *p)
+{
+ RefreshSetGadgetAttrs((struct Gadget *)fwin->objects[GID_NEXT],
+ fwin->win, NULL,
+ GA_Disabled, active ? FALSE : TRUE, TAG_DONE);
+
+}
+
+/**
+* activate search forwards button in gui
+* \param active activate/inactivate
+* \param p the pointer sent to search_verify_new() / search_create_context()
+*/
+
+void ami_search_set_back_state(bool active, void *p)
+{
+ RefreshSetGadgetAttrs((struct Gadget *)fwin->objects[GID_PREV],
+ fwin->win, NULL,
+ GA_Disabled, active ? FALSE : TRUE, TAG_DONE);
+}
+
+/**
+* retrieve state of 'case sensitive', 'show all' checks in gui
+*/
+
+search_flags_t ami_search_flags(void)
+{
+ ULONG case_sensitive, showall;
+ search_flags_t flags;
+ GetAttr(GA_Selected,fwin->objects[GID_CASE],(ULONG *)&case_sensitive);
+ GetAttr(GA_Selected,fwin->objects[GID_SHOWALL],(ULONG *)&showall);
+ flags = 0 | (case_sensitive ? SEARCH_FLAG_CASE_SENSITIVE : 0) |
+ (showall ? SEARCH_FLAG_SHOWALL : 0);
+ return flags;
+}
+
diff --git a/frontends/amiga/search.h b/frontends/amiga/search.h
new file mode 100755
index 000000000..c4f30eb01
--- /dev/null
+++ b/frontends/amiga/search.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2008 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_SEARCH_H
+#define AMIGA_SEARCH_H
+
+#include "amiga/gui.h"
+
+struct find_window {
+ struct nsObject *node;
+ struct Window *win;
+ Object *objects[GID_LAST];
+ struct gui_window *gwin;
+};
+
+struct gui_search_table *amiga_search_table;
+
+void ami_search_open(struct gui_window *gwin);
+BOOL ami_search_event(void);
+void ami_search_close(void);
+
+#endif
diff --git a/frontends/amiga/selectmenu.c b/frontends/amiga/selectmenu.c
new file mode 100644
index 000000000..25931783f
--- /dev/null
+++ b/frontends/amiga/selectmenu.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2008 - 2011 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#ifdef __amigaos4__
+
+#include <stdbool.h>
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <proto/popupmenu.h>
+#include <proto/utility.h>
+#include <reaction/reaction_macros.h>
+
+#include "utils/errors.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "render/form.h"
+#include "desktop/mouse.h"
+
+#include "amiga/gui.h"
+#include "amiga/selectmenu.h"
+#include "amiga/theme.h"
+#include "amiga/utf8.h"
+
+/* Maximum number of items for a popupmenu.class select menu.
+ * 50 is about the limit for my screen, and popupmenu doesn't scroll.
+ * We may need to calculate a value for this based on screen/font size.
+ *
+ * Additional entries will be added to a "More" menu...
+ */
+#define AMI_SELECTMENU_PAGE_MAX 40
+
+/* ...limited to the number of menus defined here... */
+#define AMI_SELECTMENU_MENU_MAX 10
+
+/* ...and resulting in this total number of entries. */
+#define AMI_SELECTMENU_MAX (AMI_SELECTMENU_PAGE_MAX * AMI_SELECTMENU_MENU_MAX)
+
+
+/** Exported interface documented in selectmenu.h **/
+BOOL ami_selectmenu_is_safe(void)
+{
+ struct Library *PopupMenuBase;
+ BOOL popupmenu_lib_ok = FALSE;
+
+ if((PopupMenuBase = OpenLibrary("popupmenu.library", 53))) {
+ LOG("popupmenu.library v%d.%d", PopupMenuBase->lib_Version, PopupMenuBase->lib_Revision);
+ if(LIB_IS_AT_LEAST((struct Library *)PopupMenuBase, 53, 11))
+ popupmenu_lib_ok = TRUE;
+ CloseLibrary(PopupMenuBase);
+ }
+
+ return popupmenu_lib_ok;
+}
+
+HOOKF(uint32, ami_popup_hook, Object *, item, APTR)
+{
+ uint32 itemid = 0;
+ struct gui_window *gwin = hook->h_Data;
+
+ if(GetAttr(PMIA_ID, item, &itemid)) {
+ form_select_process_selection(gwin->shared->control, itemid);
+ }
+
+ return itemid;
+}
+
+void gui_create_form_select_menu(struct gui_window *g,
+ struct form_control *control)
+{
+ struct Library *PopupMenuBase = NULL;
+ struct PopupMenuIFace *IPopupMenu = NULL;
+ struct Hook selectmenuhook;
+ Object *selectmenuobj;
+ Object *smenu = NULL;
+ Object *currentmenu;
+ Object *submenu = NULL;
+ char *selectmenu_item[AMI_SELECTMENU_MAX];
+ char *more_label;
+ struct form_option *opt = form_select_get_option(control, 0);
+ int i = 0;
+ int n = 0;
+
+ if(ami_selectmenu_is_safe() == FALSE) return;
+
+ if((PopupMenuBase = OpenLibrary("popupmenu.class", 0))) {
+ IPopupMenu = (struct PopupMenuIFace *)GetInterface(PopupMenuBase, "main", 1, NULL);
+ }
+
+ if(IPopupMenu == NULL) return;
+
+ ClearMem(selectmenu_item, AMI_SELECTMENU_MAX * 4);
+ more_label = ami_utf8_easy(messages_get("More"));
+
+ selectmenuhook.h_Entry = ami_popup_hook;
+ selectmenuhook.h_SubEntry = NULL;
+ selectmenuhook.h_Data = g;
+
+ g->shared->control = control;
+
+ selectmenuobj = PMMENU(form_control_get_name(control)),
+ PMA_MenuHandler, &selectmenuhook, End;
+
+ currentmenu = selectmenuobj;
+
+ while(opt) {
+ selectmenu_item[i] = ami_utf8_easy(opt->text);
+
+ IDoMethod(currentmenu, PM_INSERT,
+ NewObject(POPUPMENU_GetItemClass(), NULL,
+ PMIA_Title, (ULONG)selectmenu_item[i],
+ PMIA_ID, i,
+ PMIA_CheckIt, TRUE,
+ PMIA_Checked, opt->selected,
+ TAG_DONE),
+ ~0);
+
+ opt = opt->next;
+ i++;
+ n++;
+
+ if(n == AMI_SELECTMENU_PAGE_MAX) {
+ if(submenu != NULL) {
+ /* attach the previous submenu */
+ IDoMethod(smenu, PM_INSERT,
+ NewObject(NULL, "popupmenuitem.class",
+ PMIA_Title, more_label,
+ PMIA_CheckIt, TRUE,
+ PMIA_SubMenu, submenu,
+ TAG_DONE),
+ ~0);
+ }
+
+ submenu = NewObject(NULL, "popupmenu.class", TAG_DONE);
+ smenu = currentmenu;
+ currentmenu = submenu;
+ n = 0;
+ }
+
+ if(i >= AMI_SELECTMENU_MAX) break;
+ }
+
+ if((submenu != NULL) && (n != 0)) {
+ /* attach the previous submenu */
+ IDoMethod(smenu, PM_INSERT,
+ NewObject(NULL, "popupmenuitem.class",
+ PMIA_Title, more_label,
+ PMIA_CheckIt, TRUE,
+ PMIA_SubMenu, submenu,
+ TAG_DONE),
+ ~0);
+ }
+
+ ami_set_pointer(g->shared, GUI_POINTER_DEFAULT, false); // Clear the menu-style pointer
+
+ IDoMethod(selectmenuobj, PM_OPEN, g->shared->win);
+
+ /* PM_OPEN is blocking, so dispose menu immediately... */
+ if(selectmenuobj) DisposeObject(selectmenuobj);
+
+ /* ...and get rid of popupmenu.class ASAP */
+ if(IPopupMenu) DropInterface((struct Interface *)IPopupMenu);
+ if(PopupMenuBase) CloseLibrary(PopupMenuBase);
+
+ /* Free the menu labels */
+ if(more_label) ami_utf8_free(more_label);
+ for(i = 0; i < AMI_SELECTMENU_MAX; i++) {
+ if(selectmenu_item[i] != NULL) {
+ ami_utf8_free(selectmenu_item[i]);
+ selectmenu_item[i] = NULL;
+ }
+ }
+}
+
+#else
+#include "amiga/selectmenu.h"
+void gui_create_form_select_menu(struct gui_window *g, struct form_control *control)
+{
+}
+
+BOOL ami_selectmenu_is_safe()
+{
+ return FALSE;
+}
+#endif
+
diff --git a/frontends/amiga/selectmenu.h b/frontends/amiga/selectmenu.h
new file mode 100755
index 000000000..f55b6ca98
--- /dev/null
+++ b/frontends/amiga/selectmenu.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2008-9, 2011 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_SELECTMENU_H
+#define AMIGA_SELECTMENU_H
+
+#include <exec/types.h>
+
+struct gui_window;
+struct form_control;
+
+BOOL popupmenu_lib_ok;
+
+void gui_create_form_select_menu(struct gui_window *g, struct form_control *control);
+
+/**
+ * Opens popupmenu.library to check the version.
+ * Versions older than 53.11 are dangerous!
+ *
+ * \returns TRUE if popupmenu is safe, FALSE otherwise.
+ */
+BOOL ami_selectmenu_is_safe(void);
+#endif
+
diff --git a/frontends/amiga/sslcert.c b/frontends/amiga/sslcert.c
new file mode 100644
index 000000000..699ce71b8
--- /dev/null
+++ b/frontends/amiga/sslcert.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2009 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <proto/exec.h>
+
+#include "utils/nsurl.h"
+#include "content/llcache.h"
+#include "desktop/mouse.h"
+#include "desktop/gui_window.h"
+#include "desktop/sslcert_viewer.h"
+
+#include "amiga/tree.h"
+#include "amiga/sslcert.h"
+
+void gui_cert_verify(nsurl *url,
+ const struct ssl_cert_info *certs, unsigned long num,
+ nserror (*cb)(bool proceed, void *pw), void *cbpw)
+{
+ struct sslcert_session_data *data;
+ struct treeview_window *ssl_window;
+
+ sslcert_viewer_create_session_data(num, url, cb, cbpw,
+ certs, &data);
+ ssl_current_session = data;
+
+ ssl_window = ami_tree_create(TREE_SSLCERT, data);
+ if(!ssl_window) return;
+
+ ami_tree_open(ssl_window, AMI_TREE_SSLCERT);
+}
+
+void ami_ssl_free(struct treeview_window *twin)
+{
+ ami_tree_destroy(twin);
+}
diff --git a/frontends/amiga/sslcert.h b/frontends/amiga/sslcert.h
new file mode 100644
index 000000000..953142e98
--- /dev/null
+++ b/frontends/amiga/sslcert.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2009 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_SSLCERT_H
+#define AMIGA_SSLCERT_H
+
+void gui_cert_verify(nsurl *url,
+ const struct ssl_cert_info *certs, unsigned long num,
+ nserror (*cb)(bool proceed, void *pw), void *cbpw);
+
+void ami_ssl_free(struct treeview_window *twin);
+
+#endif
diff --git a/frontends/amiga/stringview/stringview.c b/frontends/amiga/stringview/stringview.c
new file mode 100755
index 000000000..e875f3b5a
--- /dev/null
+++ b/frontends/amiga/stringview/stringview.c
@@ -0,0 +1,870 @@
+/*
+ * Copyright 2009 Rene W. Olsen <ac@rebels.com>
+ * Copyright 2009 Stephen Fellner <sf.amiga@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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
+
+#include <proto/dos.h>
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <proto/layout.h>
+#include <proto/listbrowser.h>
+#include <proto/utility.h>
+#include <proto/string.h>
+#include <proto/window.h>
+
+#include <classes/window.h>
+#include <gadgets/layout.h>
+#include <gadgets/listbrowser.h>
+
+#include "amiga/os3support.h"
+#include "amiga/libs.h"
+
+#include "stringview.h"
+#include "urlhistory.h"
+
+#include <string.h>
+#include <stdio.h>
+
+#define End TAG_END)
+
+///
+
+/// Proto
+
+static void myStringOpenListview( Class *cl, Object *obj, struct gpInput *msg );
+static void myStringCloseListview( Class *cl, Object *obj );
+static uint32 myStringSearch( Class *cl, Object *obj );
+static void myStringArrowUp( Class *cl, Object *obj );
+static void myStringArrowDown( Class *cl, Object *obj );
+static void myStringHandleListview( Class *cl, Object *obj );
+
+///
+
+/* -- Internal -- */
+
+/// myStringOpenListview
+
+static void myStringOpenListview( Class *cl, Object *obj, struct gpInput *msg )
+{
+struct myStringClassData *data;
+struct Gadget *gad;
+
+ data = INST_DATA( cl, obj );
+
+ gad = (struct Gadget *)obj;
+
+ SetAttrs( data->WindowObject,
+ WA_CustomScreen, msg->gpi_GInfo->gi_Window->WScreen,
+ WA_Left, data->WinXPos,
+ WA_Top, data->WinYPos,
+ WA_Width, data->WinWidth,
+ WA_Height, data->WinHeight,
+ TAG_END
+ );
+
+// IDoMethod( data->WindowObject, WM_RETHINK );
+
+ data->Window = (struct Window *)IDoMethod( data->WindowObject, WM_OPEN );
+
+ if ( data->Window == NULL )
+ {
+ goto bailout;
+ }
+
+// GetAttr( WINDOW_SigMask, MainWindowObject, &MainWindowBits );
+
+bailout:
+
+ return;
+}
+
+///
+/// myStringCloseListview
+
+static void myStringCloseListview( Class *cl, Object *obj )
+{
+struct myStringClassData *data;
+struct Node *node;
+
+ data = INST_DATA( cl, obj );
+
+ if ( data->Window )
+ {
+ IDoMethod( data->WindowObject, WM_CLOSE );
+ data->Window = NULL;
+ }
+
+ while(( node = RemHead( &data->ListviewHeader )))
+ {
+ FreeListBrowserNode( node );
+ }
+}
+
+///
+/// myStringSearch
+
+static uint32 myStringSearch( Class *cl, Object *obj )
+{
+ struct myStringClassData *data;
+ struct Window *win;
+ struct Node *node;
+ struct Node *n;
+ uint32 found;
+ uint32 bufpos;
+ STRPTR searchString;
+ STRPTR compString;
+
+ found = 0;
+
+ data = INST_DATA( cl, obj );
+
+ win = data->Window;
+
+ // Remove List and Free Nodes
+
+ SetGadgetAttrs( (struct Gadget *)data->ListviewObject, win, NULL,
+ LISTBROWSER_Labels, ~0,
+ TAG_END
+ );
+
+ while(( node = RemHead( &data->ListviewHeader )))
+ {
+ FreeListBrowserNode( node );
+ }
+
+ GetAttr( STRINGA_BufferPos, obj, &bufpos );
+
+ if ( bufpos == 0 )
+ {
+ goto bailout;
+ }
+
+//-------------
+
+ searchString = strstr(data->SearchBuffer, "://");
+ if(searchString)
+ {
+ searchString += 3;
+ if (bufpos >= searchString - data->SearchBuffer)
+ bufpos -= searchString - data->SearchBuffer;
+ }
+ else
+ searchString = data->SearchBuffer;
+
+ node = GetHead( data->SearchHeader );
+
+ while( node )
+ {
+ uint32 srcpos;
+ BOOL possible;
+
+ possible = FALSE;
+ srcpos = 0;
+
+ compString = strstr(node->ln_Name, "://");
+ if(compString)
+ compString += 3;
+ else
+ compString = node->ln_Name;
+
+ if( 0 == strncasecmp( compString, searchString, bufpos ) )
+ {
+ // found match after protocol
+ possible = TRUE;
+ }
+ else
+ {
+ // no match after protocol, see if there's a match after www
+ if( 0 == strncasecmp( compString, "www.", 4) ) {
+ // got www, compare it!
+ if( 0 == strncasecmp( &compString[4], searchString, bufpos ) )
+ possible = TRUE;
+ }
+ }
+
+ if ( possible == TRUE )
+ {
+ n = AllocListBrowserNode( 1,
+ LBNA_Column, 0,
+ LBNCA_CopyText, TRUE,
+ LBNCA_Text, node->ln_Name,
+ TAG_END
+ );
+
+ if ( n )
+ {
+ AddTail( &data->ListviewHeader, n );
+ found++;
+ }
+ }
+
+ node = GetSucc( node );
+ }
+
+//-------------
+
+bailout:
+
+ data->ListviewCount = found;
+ data->ListviewSelected = -1;
+
+ // Add List Again
+
+ RefreshSetGadgetAttrs( (struct Gadget *)data->ListviewObject, win, NULL,
+ LISTBROWSER_Labels, &data->ListviewHeader,
+ LISTBROWSER_Selected, data->ListviewSelected,
+ LISTBROWSER_MakeVisible, 0,
+ TAG_END
+ );
+
+ return( found );
+}
+
+///
+/// myStringArrowUp
+
+static void myStringArrowUp( Class *cl, Object *obj )
+{
+struct myStringClassData *data;
+struct Window *win;
+//struct Node *node;
+//uint32 cnt;
+
+ data = INST_DATA( cl, obj );
+
+ win = data->Window;
+
+ if ( data->ListviewCount == 0 )
+ {
+ data->ListviewSelected = -1;
+ goto bailout;
+ }
+ else if (( data->ListviewSelected != -1 ) && ( data->ListviewSelected != 0 ))
+ {
+ data->ListviewSelected--;
+ }
+
+ RefreshSetGadgetAttrs( (struct Gadget *)data->ListviewObject, win, NULL,
+ LISTBROWSER_Selected, data->ListviewSelected,
+ LISTBROWSER_MakeVisible, data->ListviewSelected,
+ TAG_END
+ );
+
+// cnt = data->ListviewSelected;
+// node = GetHead( data->SearchHeader );
+//
+// while( cnt-- > 0 )
+// {
+// node = GetSucc( node );
+// }
+//
+// if ( node )
+// {
+// ISetSuperAttrs( obj,
+//
+// TAG_END
+// );
+// }
+
+bailout:
+
+ return;
+}
+
+///
+/// myStringArrowDown
+
+static void myStringArrowDown( Class *cl, Object *obj )
+{
+struct myStringClassData *data;
+struct Window *win;
+
+ data = INST_DATA( cl, obj );
+
+ win = data->Window;
+
+ if ( data->ListviewCount == 0 )
+ {
+ data->ListviewSelected = -1;
+ }
+ else if ( data->ListviewSelected == -1 )
+ {
+ data->ListviewSelected = 0;
+ }
+ else if ( data->ListviewSelected != data->ListviewCount - 1 )
+ {
+ data->ListviewSelected++;
+ }
+
+ RefreshSetGadgetAttrs( (struct Gadget *)data->ListviewObject, win, NULL,
+ LISTBROWSER_Selected, data->ListviewSelected,
+ LISTBROWSER_MakeVisible, data->ListviewSelected,
+ TAG_END
+ );
+}
+
+///
+/// myStringHandleListview
+
+static void myStringHandleListview( Class *cl, Object *obj )
+{
+struct myStringClassData *data;
+uint32 result;
+uint16 code;
+
+ data = INST_DATA( cl, obj );
+
+ while(( result = IDoMethod( data->WindowObject, WM_HANDLEINPUT, &code )) != WMHI_LASTMSG )
+ {
+// switch( result & WMHI_CLASSMASK )
+// {
+// case WMHI_CLOSEWINDOW:
+// {
+// running = FALSE;
+// break;
+// }
+//
+// default:
+// {
+// break;
+// }
+// }
+ }
+}
+
+///
+
+/* BOOPSI methods */
+
+/// myStringClass_OM_New
+
+static uint32 myStringClass_OM_New( Class *cl, Object *obj, struct opSet *msg )
+{
+ struct myStringClassData *data;
+ struct List *header;
+ struct TagItem *tag, *tags;
+ STRPTR buffer;
+
+ buffer = NULL;
+ header = NULL;
+
+ tags = msg->ops_AttrList;
+
+ while(( tag = NextTagItem( &tags )))
+ {
+ switch ( tag->ti_Tag )
+ {
+ case STRINGVIEW_Header:
+ {
+ header = (struct List *)tag->ti_Data;
+ break;
+ }
+
+ case STRINGA_Buffer:
+ {
+ buffer = (STRPTR)tag->ti_Data;
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ if (( header == NULL ) || ( buffer == NULL ))
+ {
+ return( 0 );
+ }
+
+ obj = (Object *)IDoSuperMethodA( cl, obj, (APTR)msg );
+
+ if ( obj == NULL )
+ {
+ goto bailout;
+ }
+
+ if ( obj )
+ {
+ data = INST_DATA( cl, obj );
+
+ data->SearchHeader = header;
+ data->SearchBuffer = buffer;
+
+ NewList( &data->ListviewHeader );
+
+ InitSemaphore( &data->Semaphore );
+
+ data->WindowObject = NewObject( WindowClass, NULL,
+ WA_Activate, FALSE,
+ WA_Borderless, TRUE,
+ WINDOW_ParentGroup, NewObject( LayoutClass, NULL,
+ LAYOUT_SpaceInner, FALSE,
+ LAYOUT_SpaceOuter, FALSE,
+ LAYOUT_AddChild, data->ListviewObject = NewObject( ListBrowserClass, NULL,
+ LISTBROWSER_Labels, &data->ListviewHeader,
+ LISTBROWSER_MakeVisible, TRUE,
+ LISTBROWSER_ShowSelected, TRUE,
+ End,
+ End,
+ End;
+
+ if ( data->WindowObject == NULL )
+ {
+ goto bailout;
+ }
+ }
+
+ return( (uint32)obj );
+
+bailout:
+
+ if ( obj )
+ {
+ ICoerceMethod( cl, obj, OM_DISPOSE );
+ }
+
+ return( FALSE );
+}
+
+///
+/// myStringClass_OM_Dispose
+
+static uint32 myStringClass_OM_Dispose( Class *cl, Object *obj, struct opSet *msg )
+{
+struct myStringClassData *data;
+
+ data = INST_DATA( cl, obj );
+
+ if ( data->Window )
+ {
+ IDoMethod( data->WindowObject, WM_CLOSE );
+ data->Window = NULL;
+ }
+
+ if ( data->WindowObject )
+ {
+ DisposeObject( data->WindowObject );
+ data->WindowObject = NULL;
+ }
+
+ return( IDoSuperMethodA( cl, obj, (APTR)msg ));
+}
+
+///
+/// myStringClass_OM_Set
+
+//--uint32 myStringClass_OM_Set( Class *cl, Object *obj, struct opSet *msg )
+//--{
+//--struct TorrentClassData *data;
+//--struct TagItem *tag, *tags;
+//--tr_stat_t *tor_s;
+//--
+//-- data = INST_DATA( cl, obj );
+//--
+//-- tags = msg->ops_AttrList;
+//--
+//-- while(( tag = NextTagItem( &tags )))
+//-- {
+//-- switch ( tag->ti_Tag )
+//-- {
+//-- case RAA_Torrent_Stats:
+//-- {
+//-- tor_s = (tr_stat_t *)tag->ti_Data;
+//--
+//-- switch( data->Type )
+//-- {
+//-- case TorType_BitTorrent5:
+//-- {
+//-- BitTorrent5_Stat( data, tor_s );
+//-- break;
+//-- }
+//--
+//-- case TorType_Transmission:
+//-- {
+//-- Transmission_Stat( data, tor_s );
+//-- break;
+//-- }
+//--
+//-- default:
+//-- {
+//-- break;
+//-- }
+//-- }
+//-- break;
+//-- }
+//--
+//-- case RAA_Torrent_Activate:
+//-- {
+//-- switch( data->Type )
+//-- {
+//-- case TorType_BitTorrent5:
+//-- {
+//-- if ( tag->ti_Data == TRUE )
+//-- {
+//-- BitTorrent5_Enable( data );
+//-- }
+//-- else
+//-- {
+//-- BitTorrent5_Disable( data );
+//-- }
+//-- break;
+//-- }
+//--
+//-- case TorType_Transmission:
+//-- {
+//-- if ( tag->ti_Data == TRUE )
+//-- {
+//-- Transmission_Enable( data );
+//-- }
+//-- else
+//-- {
+//-- Transmission_Disable( data );
+//-- }
+//-- break;
+//-- }
+//--
+//-- default:
+//-- {
+//-- break;
+//-- }
+//-- }
+//-- break;
+//-- }
+//--
+//-- default:
+//-- {
+//-- break;
+//-- }
+//-- }
+//-- }
+//--
+//-- return( IDoSuperMethodA( cl, obj, (Msg)msg ));
+//--}
+
+///
+
+/// myStringClass_GM_HandleInput
+
+static uint32 myStringClass_GM_HandleInput( Class *cl, Object *obj, struct gpInput *msg )
+{
+ struct myStringClassData *data;
+ struct Gadget *gad;
+ uint32 retval;
+
+ data = INST_DATA( cl, obj );
+
+ gad = (struct Gadget *)obj;
+
+ //IDoMethod( data->ListviewObject, (APTR)msg );
+
+ if (( gad->Flags & GFLG_SELECTED ) == 0 )
+ {
+ return( GMR_NOREUSE );
+ }
+
+ switch( msg->gpi_IEvent->ie_Class )
+ {
+ case IECLASS_RAWKEY:
+ {
+ switch( msg->gpi_IEvent->ie_Code )
+ {
+ case 0x48: // Page Up (DownKey)
+ case 0x4c: // Up Arrow (DownKey)
+ {
+ myStringArrowUp( cl, obj );
+
+ retval = GMR_MEACTIVE;
+
+ break;
+ }
+
+ case 0x49: // Page Down (DownKey)
+ case 0x4d: // Down Arrow (DownKey)
+ {
+ myStringArrowDown( cl, obj );
+
+ retval = GMR_MEACTIVE;
+
+ break;
+ }
+
+// case 70: // Del
+// case 65: // Backspace
+// {
+// myStringCloseListview( cl, obj );
+//
+// retval = IDoSuperMethodA( cl, obj, (APTR)msg );
+// break;
+// }
+
+ case 68: // Return
+ {
+ // If listview open, and an item is selected, copy selected node's text to the string gadget
+ if( data->Window != NULL && data->ListviewCount > 0 )
+ {
+ struct Node *selected = NULL;
+ STRPTR pText;
+ GetAttr( LISTBROWSER_SelectedNode, data->ListviewObject, (uint32 *) ( &selected ) );
+ if( selected != NULL )
+ {
+ GetListBrowserNodeAttrs( selected, LBNA_Column, 0, LBNCA_Text, &pText, TAG_END );
+ SetGadgetAttrs( (struct Gadget *)obj, data->Window, NULL, STRINGA_TextVal, pText, TAG_DONE );
+ }
+ }
+
+ retval = IDoSuperMethodA( cl, obj, (APTR)msg );
+ break;
+ }
+
+ default:
+ {
+ uint32 oldpos;
+ uint32 newpos;
+
+ GetAttr( STRINGA_BufferPos, obj, &oldpos );
+
+ retval = IDoSuperMethodA( cl, obj, (APTR)msg );
+
+ GetAttr( STRINGA_BufferPos, obj, &newpos );
+
+ if ( oldpos != newpos )
+ {
+ if ( myStringSearch( cl, obj ))
+ {
+ // Atleast one entry found, open window if not open
+ if ( data->Window == NULL )
+ {
+ myStringOpenListview( cl, obj, msg );
+ }
+ }
+ else
+ {
+ // No matches, migth aswell close the window
+ myStringCloseListview( cl, obj );
+ }
+ }
+ break;
+ }
+ }
+
+ myStringHandleListview( cl, obj );
+ break;
+ }
+#ifdef __amigaos4__
+ case IECLASS_MOUSEWHEEL:
+ {
+ struct InputEvent *ie = msg->gpi_IEvent;
+
+ if ( ie->ie_Y < 0 )
+ {
+ myStringArrowUp( cl, obj );
+ }
+ else if ( ie->ie_Y > 0 )
+ {
+ myStringArrowDown( cl, obj );
+ }
+
+ myStringHandleListview( cl, obj );
+
+ retval = GMR_MEACTIVE;
+ break;
+ }
+#endif
+ default:
+ {
+ retval = IDoSuperMethodA( cl, obj, (APTR)msg );
+ break;
+ }
+ }
+
+ return( retval );
+}
+
+///
+/// myStringClass_GM_GoActive
+
+static uint32 myStringClass_GM_GoActive( Class *cl, Object *obj, struct gpInput *msg )
+{
+struct myStringClassData *data;
+struct Window *win;
+struct Gadget *gad;
+uint32 retval;
+
+ data = INST_DATA( cl, obj );
+
+ gad = (struct Gadget *)obj;
+
+ if ( gad->Flags & GFLG_DISABLED )
+ {
+ myStringCloseListview( cl, obj );
+
+ retval = GMR_NOREUSE;
+ }
+ else
+ {
+ // If were not Disabled then set Selected flag
+ gad->Flags |= GFLG_SELECTED;
+
+ win = msg->gpi_GInfo->gi_Window;
+
+ if ( win )
+ {
+ data->WinXPos = win->LeftEdge + gad->LeftEdge;
+ data->WinYPos = win->TopEdge + gad->TopEdge + gad->Height - 1;
+ data->WinWidth = gad->Width;
+ data->WinHeight = 150;
+ }
+
+ if ( myStringSearch( cl, obj ))
+ {
+ // Atleast one entry found, open window if not open
+ if ( data->Window == NULL )
+ {
+ myStringOpenListview( cl, obj, msg );
+ }
+ }
+ else
+ {
+ // No matches, migth aswell close the window
+ myStringCloseListview( cl, obj );
+ }
+
+ retval = IDoSuperMethodA( cl, obj, (APTR)msg );
+ }
+
+ return( retval );
+}
+
+///
+/// myStringClass_GM_GoInactive
+
+static uint32 myStringClass_GM_GoInactive( Class *cl, Object *obj, struct gpGoInactive *msg )
+{
+struct myStringClassData *data;
+
+ data = INST_DATA( cl, obj );
+
+ myStringCloseListview( cl, obj );
+
+ return( IDoSuperMethodA( cl, obj, (APTR)msg ));
+}
+
+///
+
+/* Dispatcher */
+
+/// myStringClassDispatcher
+
+static uint32 myStringClassDispatcher( Class *cl, Object *obj, Msg msg )
+{
+struct myStringClassData *data;
+uint32 ret;
+
+ if ( msg->MethodID == OM_NEW )
+ {
+ return( myStringClass_OM_New( cl, obj, (APTR)msg ));
+ }
+ else if ( msg->MethodID == OM_DISPOSE )
+ {
+ return( myStringClass_OM_Dispose( cl, obj, (APTR)msg ));
+ }
+ else
+ {
+ data = INST_DATA( cl, obj );
+
+ ObtainSemaphore( &data->Semaphore );
+
+ switch( msg->MethodID )
+ {
+ /* BOOPSI methods */
+ case OM_SET:
+ {
+ struct TagItem *tag, *tags;
+ struct opSet *opSet = (struct opSet *)msg;
+ tags = opSet->ops_AttrList;
+ while ((tag = NextTagItem(&tags)))
+ {
+ if (STRINGA_TextVal == tag->ti_Tag)
+ {
+ URLHistory_AddPage((const char *)tag->ti_Data);
+ }
+ }
+
+ ret = IDoSuperMethodA(cl, obj, (APTR)msg);
+ }
+ break;
+
+// case OM_SET: ret = TorrentClass_OM_Set( cl, obj, (APTR)msg ); break;
+
+ /* Only used for Gadgets */
+// case GM_DOMAIN: ret = myStringClass_GM_Domain( cl, obj, (APTR)msg ); break;
+// case GM_LAYOUT: ret = myStringClass_GM_Layout( cl, obj, (APTR)msg ); break;
+// case GM_CLIPRECT: ret = myStringClass_GM_ClipRect( cl, obj, (APTR)msg ); break;
+// case GM_EXTENT: ret = myStringClass_GM_Extent( cl, obj, (APTR)msg ); break;
+// case GM_RENDER: ret = myStringClass_GM_Render( cl, obj, (APTR)msg ); break;
+// case GM_HITTEST: ret = myStringClass_GM_HitTest( cl, obj, (APTR)msg ); break;
+ case GM_HANDLEINPUT: ret = myStringClass_GM_HandleInput( cl, obj, (APTR)msg ); break;
+ case GM_GOACTIVE: ret = myStringClass_GM_GoActive( cl, obj, (APTR)msg ); break;
+ case GM_GOINACTIVE: ret = myStringClass_GM_GoInactive( cl, obj, (APTR)msg ); break;
+
+ /* Unknown method -> delegate to SuperClass */
+ default: ret = IDoSuperMethodA( cl, obj, (APTR)msg ); break;
+ }
+
+ ReleaseSemaphore( &data->Semaphore );
+
+ return( ret );
+ }
+}
+
+///
+
+/* Create Class */
+
+/// Make String Class
+
+Class *MakeStringClass( void )
+{
+ Class *cl;
+ cl = MakeClass( NULL, NULL, StringClass, sizeof(struct myStringClassData), 0 );
+
+ if ( cl )
+ {
+ cl->cl_Dispatcher.h_Entry = (uint32(*)())myStringClassDispatcher;
+ }
+
+ URLHistory_Init();
+
+ return( cl );
+}
+
+/// Free String Class
+
+void FreeStringClass(Class *cl)
+{
+ URLHistory_Free();
+ FreeClass(cl);
+}
+
+///
+
+/* The End */
+
diff --git a/frontends/amiga/stringview/stringview.h b/frontends/amiga/stringview/stringview.h
new file mode 100755
index 000000000..f989b2a0f
--- /dev/null
+++ b/frontends/amiga/stringview/stringview.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2009 Rene W. Olsen <ac@rebels.com>
+ * Copyright 2009 Stephen Fellner <sf.amiga@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "amiga/os3support.h"
+
+#include <exec/semaphores.h>
+#include <intuition/classes.h>
+#include <intuition/classusr.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* --- */
+
+struct myStringClassData
+{
+ struct SignalSemaphore Semaphore;
+ uint32 WinXPos;
+ uint32 WinYPos;
+ uint32 WinWidth;
+ uint32 WinHeight;
+ struct Window * Window;
+ Object * WindowObject;
+ Object * ListviewObject;
+ struct List ListviewHeader;
+ uint32 ListviewCount;
+ uint32 ListviewSelected;
+ struct List * SearchHeader;
+ STRPTR SearchBuffer;
+};
+
+#define STRINGVIEW_Header 0x50000001
+
+/* protos */
+
+Class * MakeStringClass( void );
+void FreeStringClass(Class *);
+
+/* The End */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/frontends/amiga/stringview/urlhistory.c b/frontends/amiga/stringview/urlhistory.c
new file mode 100644
index 000000000..c58a3770b
--- /dev/null
+++ b/frontends/amiga/stringview/urlhistory.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2009 Rene W. Olsen <ac@rebels.com>
+ * Copyright 2009 Stephen Fellner <sf.amiga@gmail.com>
+ * Copyright 2009 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include <proto/exec.h>
+
+#include "amiga/os3support.h"
+
+#include "urlhistory.h"
+
+#include "content/urldb.h"
+#include "utils/nsoption.h"
+
+static struct List PageList;
+
+#ifdef __amigaos4__
+#define ALLOCVEC_SHARED(N) AllocVecTags((N), AVT_Type, MEMF_SHARED, TAG_DONE);
+#else
+#define ALLOCVEC_SHARED(N) AllocVec((N), MEMF_SHARED);
+#endif
+
+void URLHistory_Init( void )
+{
+ // Initialise page list
+ NewList( &PageList );
+}
+
+
+void URLHistory_Free( void )
+{
+ struct Node *node;
+
+ while(( node = RemHead( &PageList )))
+ {
+ if( node->ln_Name) FreeVec( node->ln_Name );
+ FreeVec( node );
+ }
+}
+
+
+void URLHistory_ClearList( void )
+{
+ struct Node *node;
+
+ while(( node = RemHead( &PageList )))
+ {
+ if( node->ln_Name) FreeVec( node->ln_Name );
+ FreeVec( node );
+ }
+}
+
+
+struct List * URLHistory_GetList( void )
+{
+ return &PageList;
+}
+
+static bool URLHistoryFound(nsurl *url, const struct url_data *data)
+{
+ struct Node *node;
+
+ /* skip non-visited pages - disabled for testing
+ if(data->visits <= 0) return true;
+ */
+
+ /* skip this URL if it is already in the list */
+ if(URLHistory_FindPage(nsurl_access(url))) return true;
+
+ node = ALLOCVEC_SHARED(sizeof(struct Node));
+
+ if ( node )
+ {
+ STRPTR urladd = (STRPTR) ALLOCVEC_SHARED( strlen ( nsurl_access(url) ) + 1);
+
+ if ( urladd )
+ {
+ strcpy(urladd, nsurl_access(url));
+ node->ln_Name = urladd;
+ AddTail( &PageList, node );
+ }
+ else
+ {
+ FreeVec(node);
+ }
+ }
+ return true;
+}
+
+struct Node * URLHistory_FindPage( const char *urlString )
+{
+ return FindName(&PageList,urlString);
+}
+
+
+void URLHistory_AddPage( const char * urlString )
+{
+ if(!nsoption_bool(url_suggestion)) return;
+
+ // Only search if length > 0
+ if( strlen( urlString ) > 0 )
+ {
+ urldb_iterate_partial(urlString, URLHistoryFound);
+ }
+}
diff --git a/frontends/amiga/stringview/urlhistory.h b/frontends/amiga/stringview/urlhistory.h
new file mode 100644
index 000000000..b72792db4
--- /dev/null
+++ b/frontends/amiga/stringview/urlhistory.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2009 Rene W. Olsen <ac@rebels.com>
+ * Copyright 2009 Stephen Fellner <sf.amiga@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void URLHistory_Init( void );
+void URLHistory_Free( void );
+struct List * URLHistory_GetList( void );
+void URLHistory_ClearList( void );
+struct Node * URLHistory_FindPage( const char *urlString );
+void URLHistory_AddPage( const char * urlString );
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/frontends/amiga/theme.c b/frontends/amiga/theme.c
new file mode 100644
index 000000000..ab32b23dd
--- /dev/null
+++ b/frontends/amiga/theme.c
@@ -0,0 +1,513 @@
+/*
+ * Copyright 2010 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "amiga/os3support.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <proto/clicktab.h>
+#include <proto/datatypes.h>
+#include <proto/dos.h>
+#include <proto/exec.h>
+#include <proto/graphics.h>
+#include <proto/icon.h>
+#include <proto/intuition.h>
+
+#include <gadgets/clicktab.h>
+#include <gadgets/space.h>
+#ifdef __amigaos4__
+#include <graphics/blitattr.h>
+#endif
+#include <intuition/pointerclass.h>
+#include <workbench/icon.h>
+
+#include "utils/messages.h"
+#include "utils/nsoption.h"
+#include "utils/utils.h"
+#include "desktop/searchweb.h"
+#include "desktop/mouse.h"
+#include "desktop/gui_window.h"
+
+#include "amiga/gui.h"
+#include "amiga/drag.h"
+#include "amiga/bitmap.h"
+#include "amiga/schedule.h"
+#include "amiga/theme.h"
+#include "amiga/misc.h"
+
+struct BitMap *throbber = NULL;
+struct bitmap *throbber_nsbm = NULL;
+int throbber_frames, throbber_update_interval;
+static Object *mouseptrobj[AMI_LASTPOINTER+1];
+static struct BitMap *mouseptrbm[AMI_LASTPOINTER+1];
+
+const char *ptrs[AMI_LASTPOINTER+1] = {
+ "ptr_default",
+ "ptr_point",
+ "ptr_caret",
+ "ptr_menu",
+ "ptr_up",
+ "ptr_down",
+ "ptr_left",
+ "ptr_right",
+ "ptr_rightup",
+ "ptr_leftdown",
+ "ptr_leftup",
+ "ptr_rightdown",
+ "ptr_cross",
+ "ptr_move",
+ "ptr_wait",
+ "ptr_help",
+ "ptr_nodrop",
+ "ptr_notallowed",
+ "ptr_progress",
+ "ptr_blank",
+ "ptr_drag"};
+
+const char *ptrs32[AMI_LASTPOINTER+1] = {
+ "ptr32_default",
+ "ptr32_point",
+ "ptr32_caret",
+ "ptr32_menu",
+ "ptr32_up",
+ "ptr32_down",
+ "ptr32_left",
+ "ptr32_right",
+ "ptr32_rightup",
+ "ptr32_leftdown",
+ "ptr32_leftup",
+ "ptr32_rightdown",
+ "ptr32_cross",
+ "ptr32_move",
+ "ptr32_wait",
+ "ptr32_help",
+ "ptr32_nodrop",
+ "ptr32_notallowed",
+ "ptr32_progress",
+ "ptr32_blank",
+ "ptr32_drag"};
+
+#ifdef __amigaos4__
+/* Mapping from NetSurf to AmigaOS mouse pointers */
+int osmouseptr[AMI_LASTPOINTER+1] = {
+ POINTERTYPE_NORMAL,
+ POINTERTYPE_LINK,
+ POINTERTYPE_TEXT,
+ POINTERTYPE_CONTEXTMENU,
+ POINTERTYPE_NORTHRESIZE,
+ POINTERTYPE_SOUTHRESIZE,
+ POINTERTYPE_WESTRESIZE,
+ POINTERTYPE_EASTRESIZE,
+ POINTERTYPE_NORTHEASTRESIZE,
+ POINTERTYPE_SOUTHWESTRESIZE,
+ POINTERTYPE_NORTHWESTRESIZE,
+ POINTERTYPE_SOUTHEASTRESIZE,
+ POINTERTYPE_CROSS,
+ POINTERTYPE_HAND,
+ POINTERTYPE_BUSY,
+ POINTERTYPE_HELP,
+ POINTERTYPE_NODROP,
+ POINTERTYPE_NOTALLOWED,
+ POINTERTYPE_PROGRESS,
+ POINTERTYPE_NONE,
+ POINTERTYPE_DRAGANDDROP};
+#endif
+
+void ami_theme_init(void)
+{
+ char themefile[1024];
+ BPTR lock = 0;
+
+ strcpy(themefile,nsoption_charp(theme));
+ AddPart(themefile,"Theme",100);
+
+ lock = Lock(themefile,ACCESS_READ);
+
+ if(!lock)
+ {
+ amiga_warn_user("ThemeApplyErr",nsoption_charp(theme));
+ strcpy(themefile,"PROGDIR:Resources/Themes/Default/Theme");
+ nsoption_set_charp(theme, (char *)strdup("PROGDIR:Resources/Themes/Default"));
+ }
+ else
+ {
+ UnLock(lock);
+ }
+
+ lock = Lock(themefile,ACCESS_READ);
+ if(lock)
+ {
+ UnLock(lock);
+ messages_add_from_file(themefile);
+ }
+}
+
+void ami_theme_throbber_setup(void)
+{
+ char throbberfile[1024];
+ struct bitmap *bm;
+
+ ami_get_theme_filename(throbberfile,"theme_throbber",false);
+ throbber_frames=atoi(messages_get("theme_throbber_frames"));
+ throbber_update_interval = atoi(messages_get("theme_throbber_delay"));
+ if(throbber_update_interval == 0) throbber_update_interval = 250;
+
+ bm = ami_bitmap_from_datatype(throbberfile);
+ throbber = ami_bitmap_get_native(bm, bitmap_get_width(bm), bitmap_get_height(bm), NULL);
+
+ throbber_width = bitmap_get_width(bm) / throbber_frames;
+ throbber_height = bitmap_get_height(bm);
+ throbber_nsbm = bm;
+}
+
+void ami_theme_throbber_free(void)
+{
+ amiga_bitmap_destroy(throbber_nsbm);
+ throbber_nsbm = NULL;
+ throbber = NULL;
+}
+
+void ami_get_theme_filename(char *filename, const char *themestring, bool protocol)
+{
+ if(protocol)
+ strcpy(filename,"file:///");
+ else
+ strcpy(filename,"");
+
+ if(messages_get(themestring)[0] == '*')
+ {
+ strncat(filename,messages_get(themestring)+1,100);
+ }
+ else
+ {
+ strcat(filename, nsoption_charp(theme));
+ AddPart(filename, messages_get(themestring), 100);
+ }
+}
+
+void gui_window_set_pointer(struct gui_window *g, gui_pointer_shape shape)
+{
+ ami_set_pointer(g->shared, shape, true);
+}
+
+void ami_set_pointer(struct gui_window_2 *gwin, gui_pointer_shape shape, bool update)
+{
+ if(gwin->mouse_pointer == shape) return;
+ ami_update_pointer(gwin->win, shape);
+ if(update == true) gwin->mouse_pointer = shape;
+}
+
+/* reset the mouse pointer back to what NetSurf last set it as */
+void ami_reset_pointer(struct gui_window_2 *gwin)
+{
+ ami_update_pointer(gwin->win, gwin->mouse_pointer);
+}
+
+void ami_update_pointer(struct Window *win, gui_pointer_shape shape)
+{
+ if(drag_save_data) return;
+
+ if(LIB_IS_AT_LEAST((struct Library *)IntuitionBase, 53, 42)) {
+#ifdef __amigaos4__
+ BOOL ptr_delay = FALSE;
+ if(shape == GUI_POINTER_WAIT) ptr_delay = TRUE;
+
+ SetWindowPointer(win,
+ WA_PointerType, osmouseptr[shape],
+ WA_PointerDelay, ptr_delay,
+ TAG_DONE);
+#endif
+ } else {
+ if(nsoption_bool(os_mouse_pointers))
+ {
+ switch(shape)
+ {
+ case GUI_POINTER_DEFAULT:
+ SetWindowPointer(win, TAG_DONE);
+ break;
+
+ case GUI_POINTER_WAIT:
+ SetWindowPointer(win,
+ WA_BusyPointer, TRUE,
+ WA_PointerDelay, TRUE,
+ TAG_DONE);
+ break;
+
+ default:
+ if(mouseptrobj[shape]) {
+ SetWindowPointer(win, WA_Pointer, mouseptrobj[shape], TAG_DONE);
+ } else {
+ SetWindowPointer(win, TAG_DONE);
+ }
+ break;
+ }
+ }
+ else
+ {
+ if(mouseptrobj[shape])
+ {
+ SetWindowPointer(win, WA_Pointer, mouseptrobj[shape], TAG_DONE);
+ }
+ else
+ {
+ if(shape == GUI_POINTER_WAIT)
+ {
+ SetWindowPointer(win,
+ WA_BusyPointer, TRUE,
+ WA_PointerDelay, TRUE,
+ TAG_DONE);
+ }
+ else
+ {
+ SetWindowPointer(win, TAG_DONE);
+ }
+ }
+ }
+ }
+}
+
+void ami_init_mouse_pointers(void)
+{
+ if(LIB_IS_AT_LEAST((struct Library *)IntuitionBase, 53, 42)) return;
+
+ int i;
+ struct RastPort mouseptr;
+ struct DiskObject *dobj;
+ uint32 format = IDFMT_BITMAPPED;
+ int32 mousexpt=0,mouseypt=0;
+
+ InitRastPort(&mouseptr);
+
+ for(i=0; i<=AMI_LASTPOINTER; i++) {
+ BPTR ptrfile;
+ mouseptrbm[i] = NULL;
+ mouseptrobj[i] = NULL;
+ char ptrfname[1024];
+
+#ifdef __amigaos4__
+ if(nsoption_bool(truecolour_mouse_pointers)) {
+ ami_get_theme_filename((char *)&ptrfname,ptrs32[i], false);
+ if((dobj = GetIconTags(ptrfname,ICONGETA_UseFriendBitMap,TRUE,TAG_DONE))) {
+ if(IconControl(dobj, ICONCTRLA_GetImageDataFormat, &format, TAG_DONE)) {
+ if(IDFMT_DIRECTMAPPED == format) {
+ int32 width = 0, height = 0;
+ uint8* data = 0;
+ IconControl(dobj,
+ ICONCTRLA_GetWidth, &width,
+ ICONCTRLA_GetHeight, &height,
+ ICONCTRLA_GetImageData1, &data,
+ TAG_DONE);
+
+ if ((width > 0) && (width <= 64) && (height > 0) && (height <= 64) && data) {
+ STRPTR tooltype;
+
+ if((tooltype = FindToolType(dobj->do_ToolTypes, "XOFFSET")))
+ mousexpt = atoi(tooltype);
+
+ if((tooltype = FindToolType(dobj->do_ToolTypes, "YOFFSET")))
+ mouseypt = atoi(tooltype);
+
+ if ((mousexpt < 0) || (mousexpt >= width))
+ mousexpt = 0;
+ if ((mouseypt < 0) || (mouseypt >= height))
+ mouseypt = 0;
+
+ static uint8 dummyPlane[64 * 64 / 8];
+ static struct BitMap dummyBitMap = { 64 / 8, 64, 0, 2, 0, { dummyPlane, dummyPlane, 0, 0, 0, 0, 0, 0 }, };
+
+ mouseptrobj[i] = NewObject(NULL, "pointerclass",
+ POINTERA_BitMap, &dummyBitMap,
+ POINTERA_XOffset, -mousexpt,
+ POINTERA_YOffset, -mouseypt,
+ POINTERA_WordWidth, (width + 15) / 16,
+ POINTERA_XResolution, POINTERXRESN_SCREENRES,
+ POINTERA_YResolution, POINTERYRESN_SCREENRESASPECT,
+ POINTERA_ImageData, data,
+ POINTERA_Width, width,
+ POINTERA_Height, height,
+ TAG_DONE);
+ }
+ }
+ }
+ }
+ }
+#endif
+
+ if(!mouseptrobj[i])
+ {
+ ami_get_theme_filename(ptrfname,ptrs[i], false);
+ if((ptrfile = Open(ptrfname,MODE_OLDFILE)))
+ {
+ int mx,my;
+ UBYTE *pprefsbuf = AllocVecTagList(1061, NULL);
+ Read(ptrfile,pprefsbuf,1061);
+
+ mouseptrbm[i]=AllocVecTagList(sizeof(struct BitMap), NULL);
+ InitBitMap(mouseptrbm[i],2,32,32);
+ mouseptrbm[i]->Planes[0] = AllocRaster(32,32);
+ mouseptrbm[i]->Planes[1] = AllocRaster(32,32);
+ mouseptr.BitMap = mouseptrbm[i];
+
+ for(my=0;my<32;my++)
+ {
+ for(mx=0;mx<32;mx++)
+ {
+ SetAPen(&mouseptr,pprefsbuf[(my*(33))+mx]-'0');
+ WritePixel(&mouseptr,mx,my);
+ }
+ }
+
+ mousexpt = ((pprefsbuf[1056]-'0')*10)+(pprefsbuf[1057]-'0');
+ mouseypt = ((pprefsbuf[1059]-'0')*10)+(pprefsbuf[1060]-'0');
+
+ mouseptrobj[i] = NewObject(NULL,"pointerclass",
+ POINTERA_BitMap,mouseptrbm[i],
+ POINTERA_WordWidth,2,
+ POINTERA_XOffset,-mousexpt,
+ POINTERA_YOffset,-mouseypt,
+ POINTERA_XResolution,POINTERXRESN_SCREENRES,
+ POINTERA_YResolution,POINTERYRESN_SCREENRESASPECT,
+ TAG_DONE);
+
+ FreeVec(pprefsbuf);
+ Close(ptrfile);
+ }
+
+ }
+
+ } // for
+}
+
+void ami_mouse_pointers_free(void)
+{
+ if(LIB_IS_AT_LEAST((struct Library *)IntuitionBase, 53, 42)) return;
+
+ int i;
+
+ for(i=0;i<=AMI_LASTPOINTER;i++)
+ {
+ if(mouseptrbm[i])
+ {
+ FreeRaster(mouseptrbm[i]->Planes[0],32,32);
+ FreeRaster(mouseptrbm[i]->Planes[1],32,32);
+ FreeVec(mouseptrbm[i]);
+ }
+ }
+}
+
+void gui_window_start_throbber(struct gui_window *g)
+{
+ if(!g) return;
+ if(nsoption_bool(kiosk_mode)) return;
+
+#ifdef __amigaos4__
+ if(g->tab_node && (g->shared->tabs > 1))
+ {
+ SetClickTabNodeAttrs(g->tab_node, TNA_Flagged, TRUE, TAG_DONE);
+ RefreshGadgets((APTR)g->shared->objects[GID_TABS],
+ g->shared->win, NULL);
+ }
+#endif
+
+ g->throbbing = true;
+ if(g->shared->throbber_frame == 0) g->shared->throbber_frame = 1;
+ ami_throbber_redraw_schedule(throbber_update_interval, g);
+}
+
+void gui_window_stop_throbber(struct gui_window *g)
+{
+ struct IBox *bbox;
+
+ if(!g) return;
+ if(nsoption_bool(kiosk_mode)) return;
+
+#ifdef __amigaos4__
+ if(g->tab_node && (g->shared->tabs > 1))
+ {
+ SetClickTabNodeAttrs(g->tab_node, TNA_Flagged, FALSE, TAG_DONE);
+ RefreshGadgets((APTR)g->shared->objects[GID_TABS],
+ g->shared->win, NULL);
+ }
+#endif
+
+ if(g == g->shared->gw) {
+ if(ami_gui_get_space_box(g->shared->objects[GID_THROBBER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return;
+ }
+
+ BltBitMapRastPort(throbber, 0, 0, g->shared->win->RPort, bbox->Left,
+ bbox->Top, throbber_width, throbber_height, 0x0C0);
+
+ ami_gui_free_space_box(bbox);
+ }
+
+ g->throbbing = false;
+ ami_throbber_redraw_schedule(-1, g);
+}
+
+static void ami_throbber_update(void *p)
+{
+ struct gui_window *g = (struct gui_window *)p;
+ struct IBox *bbox;
+ int frame = 0;
+
+ if(!g) return;
+ if(!g->shared->objects[GID_THROBBER]) return;
+
+ if(g->throbbing == true) {
+ frame = g->shared->throbber_frame;
+ g->shared->throbber_frame++;
+ if(g->shared->throbber_frame > (throbber_frames-1))
+ g->shared->throbber_frame=1;
+ }
+
+ if(g->shared->gw == g) {
+ if(ami_gui_get_space_box(g->shared->objects[GID_THROBBER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return;
+ }
+#ifdef __amigaos4__
+ BltBitMapTags(BLITA_SrcX, throbber_width * frame,
+ BLITA_SrcY, 0,
+ BLITA_DestX, bbox->Left,
+ BLITA_DestY, bbox->Top,
+ BLITA_Width, throbber_width,
+ BLITA_Height, throbber_height,
+ BLITA_Source, throbber,
+ BLITA_Dest, g->shared->win->RPort,
+ BLITA_SrcType, BLITT_BITMAP,
+ BLITA_DestType, BLITT_RASTPORT,
+ // BLITA_UseSrcAlpha, TRUE,
+ TAG_DONE);
+#else
+ BltBitMapRastPort(throbber, throbber_width * frame, 0, g->shared->win->RPort,
+ bbox->Left, bbox->Top, throbber_width, throbber_height, 0xC0);
+#endif
+ ami_gui_free_space_box(bbox);
+ }
+
+ if(frame > 0) ami_throbber_redraw_schedule(throbber_update_interval, g);
+}
+
+void ami_throbber_redraw_schedule(int t, struct gui_window *g)
+{
+ ami_schedule(t, ami_throbber_update, g);
+}
+
diff --git a/frontends/amiga/theme.h b/frontends/amiga/theme.h
new file mode 100644
index 000000000..f295efee1
--- /dev/null
+++ b/frontends/amiga/theme.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2010 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_THEME_H
+#define AMIGA_THEME_H
+
+#define AMI_GUI_POINTER_BLANK GUI_POINTER_PROGRESS+1
+#define AMI_GUI_POINTER_DRAG GUI_POINTER_PROGRESS+2
+#define AMI_LASTPOINTER AMI_GUI_POINTER_DRAG
+
+ULONG throbber_width, throbber_height;
+
+void ami_theme_init(void);
+void ami_get_theme_filename(char *filename, const char *themestring, bool protocol);
+
+void ami_theme_throbber_setup(void);
+void ami_theme_throbber_free(void);
+void ami_update_throbber(struct gui_window_2 *g,bool redraw);
+
+void ami_init_mouse_pointers(void);
+void ami_mouse_pointers_free(void);
+void ami_set_pointer(struct gui_window_2 *gwin, gui_pointer_shape shape, bool update);
+void ami_reset_pointer(struct gui_window_2 *gwin);
+/* Use the following ONLY if nothing other than the Intuition window pointer is available,
+ * and ALWAYS in preference to SetWindowPointer(), as it features more pointers and uses
+ * the correct ones specified in user preferences. */
+void ami_update_pointer(struct Window *win, gui_pointer_shape shape);
+
+void gui_window_start_throbber(struct gui_window *g);
+void gui_window_stop_throbber(struct gui_window *g);
+void ami_throbber_redraw_schedule(int t, struct gui_window *g);
+
+void gui_window_set_pointer(struct gui_window *g, gui_pointer_shape shape);
+#endif
+
diff --git a/frontends/amiga/tree.c b/frontends/amiga/tree.c
new file mode 100644
index 000000000..f147b5e5c
--- /dev/null
+++ b/frontends/amiga/tree.c
@@ -0,0 +1,1473 @@
+/*
+ * Copyright 2008 - 2013 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "amiga/os3support.h"
+
+#include <stdlib.h>
+#include <proto/window.h>
+#include <proto/layout.h>
+#include <proto/space.h>
+#include <proto/label.h>
+#include <proto/scroller.h>
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <proto/button.h>
+#include <proto/bitmap.h>
+#include <proto/graphics.h>
+#include <proto/asl.h>
+#include <proto/utility.h>
+#include <proto/dos.h>
+
+#include <libraries/gadtools.h>
+#include <gadgets/button.h>
+#include <classes/window.h>
+#include <gadgets/space.h>
+#include <images/label.h>
+#include <images/bitmap.h>
+#include <gadgets/layout.h>
+#include <gadgets/scroller.h>
+#include <reaction/reaction_macros.h>
+#include <intuition/icclass.h>
+#ifdef __amigaos4__
+#include <graphics/blitattr.h>
+#endif
+
+#include <assert.h>
+#include <string.h>
+
+#include "utils/nsoption.h"
+#include "utils/utils.h"
+#include "utils/messages.h"
+#include "content/urldb.h"
+#include "content/llcache.h"
+#include "desktop/browser.h"
+#include "desktop/cookie_manager.h"
+#include "desktop/global_history.h"
+#include "desktop/hotlist.h"
+#include "desktop/gui_window.h"
+#include "desktop/sslcert_viewer.h"
+
+#include "amiga/gui.h"
+#include "amiga/tree.h"
+#include "amiga/file.h"
+#include "amiga/libs.h"
+#include "amiga/misc.h"
+#include "amiga/utf8.h"
+#include "amiga/sslcert.h"
+#include "amiga/drag.h" /* drag icon stuff */
+#include "amiga/theme.h" /* pointers */
+#include "amiga/filetype.h"
+#include "amiga/schedule.h"
+
+#define AMI_TREE_MENU_ITEMS 26
+#define AMI_TREE_MENU_NEWDIR FULLMENUNUM(1,0,0)
+#define AMI_TREE_MENU_NEWURL FULLMENUNUM(1,1,0)
+#define AMI_TREE_MENU_EDIT FULLMENUNUM(1,3,0)
+#define AMI_TREE_MENU_DELETE FULLMENUNUM(1,5,0)
+#define AMI_TREE_MENU_CLEAR FULLMENUNUM(1,8,0)
+
+enum {
+ GID_OPEN = GID_LAST,
+ GID_TREE_LAST
+};
+
+
+struct treeview_window {
+ struct nsObject *node;
+ struct Window *win;
+ Object *objects[GID_TREE_LAST];
+ int type;
+ struct NewMenu *menu;
+ char *menu_name[AMI_TREE_MENU_ITEMS];
+ struct tree *tree;
+ struct Hook scrollerhook;
+ uint32 key_state;
+ uint32 mouse_state;
+ int drag_x;
+ int drag_y;
+ struct timeval lastclick;
+ int max_width;
+ int max_height;
+ struct gui_globals globals;
+ struct sslcert_session_data *ssl_data;
+ char *wintitle;
+ char *sslerr;
+ char *sslaccept;
+ char *sslreject;
+ struct MinList *shared_pens;
+};
+
+struct ami_tree_redraw_req {
+ int x;
+ int y;
+ int width;
+ int height;
+ struct treeview_window *twin;
+};
+
+#if 0
+void ami_tree_draw(struct treeview_window *twin);
+static void ami_tree_resized(struct tree *tree, int width,
+ int height, void *data);
+static void ami_tree_scroll_visible(int y, int height, void *data);
+static void ami_tree_get_window_dimensions(int *width, int *height, void *data);
+#endif
+
+void ami_tree_destroy(struct treeview_window *twin)
+{
+ tree_delete(twin->tree);
+ FreeVec(twin->shared_pens);
+ FreeVec(twin);
+}
+
+struct tree *ami_tree_get_tree(struct treeview_window *twin)
+{
+ return twin->tree;
+}
+
+static void ami_tree_resized(struct tree *tree, int width, int height, void *data)
+{
+ struct treeview_window *twin = data;
+ struct IBox *bbox;
+
+ twin->max_height = height;
+ twin->max_width = width;
+
+ if(twin->win)
+ {
+ if(ami_gui_get_space_box(twin->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return;
+ }
+
+ if(height == -1) {
+ SetAttrs((APTR)twin->objects[OID_MAIN],
+ WINDOW_VertProp, -1,
+ TAG_DONE);
+ } else {
+ RefreshSetGadgetAttrs((APTR)twin->objects[OID_VSCROLL], twin->win, NULL,
+ SCROLLER_Total, height,
+ SCROLLER_Visible, bbox->Height,
+ TAG_DONE);
+ }
+
+ if(width == -1) {
+ SetAttrs((APTR)twin->objects[OID_MAIN],
+ WINDOW_HorizProp, -1,
+ TAG_DONE);
+ } else {
+ RefreshSetGadgetAttrs((APTR)twin->objects[OID_HSCROLL], twin->win, NULL,
+ SCROLLER_Total, width,
+ SCROLLER_Visible, bbox->Width,
+ TAG_DONE);
+ }
+ ami_gui_free_space_box(bbox);
+ }
+}
+
+/**
+ * Retrieves the dimensions of the window with the tree
+ *
+ * \param data user data assigned to the tree on tree creation
+ * \param width will be updated to window width if not NULL
+ * \param height will be updated to window height if not NULL
+ */
+static void ami_tree_get_window_dimensions(int *width, int *height, void *data)
+{
+ struct treeview_window *twin = data;
+ struct IBox *bbox;
+
+ if(ami_gui_get_space_box(twin->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return;
+ }
+
+ if(width) *width = bbox->Width;
+ if(height) *height = bbox->Height;
+
+ ami_gui_free_space_box(bbox);
+}
+
+static void ami_tree_redraw_req_dr(void *p)
+{
+ struct ami_tree_redraw_req *atrr_data = (struct ami_tree_redraw_req *)p;
+ int x = atrr_data->x;
+ int y = atrr_data->y;
+ int width = atrr_data->width;
+ int height = atrr_data->height;
+ struct treeview_window *twin = atrr_data->twin;
+ struct IBox *bbox;
+ int pos_x, pos_y;
+ struct RastPort *temprp;
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &amiplot
+ };
+
+ if(!twin->win) return;
+
+ ami_update_pointer(twin->win, GUI_POINTER_WAIT);
+
+ glob = &twin->globals;
+ temprp = glob->rp;
+ glob->rp = twin->win->RPort;
+
+ GetAttr(SCROLLER_Top, twin->objects[OID_HSCROLL], (ULONG *)&pos_x);
+ GetAttr(SCROLLER_Top, twin->objects[OID_VSCROLL], (ULONG *)&pos_y);
+
+ if(ami_gui_get_space_box(twin->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return;
+ }
+
+ x += bbox->Left;
+ y += bbox->Top;
+
+ if(x - pos_x + width > bbox->Width) width = bbox->Width - (x - pos_x);
+ if(y - pos_y + height > bbox->Height) height = bbox->Height - (y - pos_y);
+
+ if(x < pos_x) {
+ width -= pos_x - x;
+ x = pos_x;
+ }
+
+ if(y < pos_y) {
+ height -= pos_y - y;
+ y = pos_y;
+ }
+
+ tree_draw(twin->tree, bbox->Left - pos_x, bbox->Top - pos_y,
+ atrr_data->x, atrr_data->y,
+ atrr_data->width, atrr_data->height, &ctx);
+
+ FreeVec(atrr_data);
+ ami_gui_free_space_box(bbox);
+ ami_update_pointer(twin->win, GUI_POINTER_DEFAULT);
+ ami_clearclipreg(glob);
+ glob->rp = temprp;
+ glob = &browserglob;
+}
+
+static void ami_tree_redraw_req(void *p)
+{
+ struct ami_tree_redraw_req *atrr_data = (struct ami_tree_redraw_req *)p;
+ int x = atrr_data->x;
+ int y = atrr_data->y;
+ int width = atrr_data->width;
+ int height = atrr_data->height;
+ struct treeview_window *twin = atrr_data->twin;
+ struct IBox *bbox;
+ int pos_x, pos_y;
+ int tile_x, tile_y, tile_w, tile_h;
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &amiplot
+ };
+
+ if(!twin->win) return;
+
+ ami_update_pointer(twin->win, GUI_POINTER_WAIT);
+
+ glob = &twin->globals;
+
+ GetAttr(SCROLLER_Top, twin->objects[OID_HSCROLL], (ULONG *)&pos_x);
+ GetAttr(SCROLLER_Top, twin->objects[OID_VSCROLL], (ULONG *)&pos_y);
+
+ if(ami_gui_get_space_box(twin->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return;
+ }
+
+ if(x - pos_x + width > bbox->Width) width = bbox->Width - (x - pos_x);
+ if(y - pos_y + height > bbox->Height) height = bbox->Height - (y - pos_y);
+
+ if(x < pos_x) {
+ width -= pos_x - x;
+ x = pos_x;
+ }
+
+ if(y < pos_y) {
+ height -= pos_y - y;
+ y = pos_y;
+ }
+
+ for(tile_y = y; tile_y < (y + height); tile_y += nsoption_int(redraw_tile_size_y)) {
+ tile_h = nsoption_int(redraw_tile_size_y);
+ if(((y + height) - tile_y) < nsoption_int(redraw_tile_size_y))
+ tile_h = (y + height) - tile_y;
+
+ for(tile_x = x; tile_x < (x + width); tile_x += nsoption_int(redraw_tile_size_x)) {
+ tile_w = nsoption_int(redraw_tile_size_x);
+ if(((x + width) - tile_x) < nsoption_int(redraw_tile_size_x))
+ tile_w = (x + width) - tile_x;
+
+ tree_draw(twin->tree, - tile_x, - tile_y,
+ tile_x, tile_y, tile_w, tile_h, &ctx);
+#ifdef __amigaos4__
+ BltBitMapTags(BLITA_SrcType, BLITT_BITMAP,
+ BLITA_Source, twin->globals.bm,
+ BLITA_SrcX, 0,
+ BLITA_SrcY, 0,
+ BLITA_DestType, BLITT_RASTPORT,
+ BLITA_Dest, twin->win->RPort,
+ BLITA_DestX, bbox->Left + tile_x - pos_x,
+ BLITA_DestY, bbox->Top + tile_y - pos_y,
+ BLITA_Width, tile_w,
+ BLITA_Height, tile_h,
+ TAG_DONE);
+#else
+ BltBitMapRastPort(twin->globals.bm, 0, 0,
+ twin->win->RPort, bbox->Left + tile_x - pos_x, bbox->Top + tile_y - pos_y,
+ tile_w, tile_h, 0xC0);
+#endif
+ }
+ }
+
+ FreeVec(atrr_data);
+ ami_gui_free_space_box(bbox);
+ ami_update_pointer(twin->win, GUI_POINTER_DEFAULT);
+ ami_clearclipreg(glob);
+ glob = &browserglob;
+}
+
+static void ami_tree_redraw_request(int x, int y, int width, int height, void *data)
+{
+ struct ami_tree_redraw_req *atrr_data = AllocVecTagList(sizeof(struct ami_tree_redraw_req), NULL);
+
+ atrr_data->x = x;
+ atrr_data->y = y;
+ atrr_data->width = width;
+ atrr_data->height = height;
+ atrr_data->twin = (struct treeview_window *)data;
+
+ /** /todo Queue these requests properly like the main browser code does
+ **/
+
+ if(nsoption_bool(direct_render) == false)
+ ami_schedule(0, ami_tree_redraw_req, atrr_data);
+ else
+ ami_schedule(0, ami_tree_redraw_req_dr, atrr_data);
+}
+
+static void ami_tree_draw(struct treeview_window *twin)
+{
+ struct IBox *bbox;
+ int x, y;
+
+ if(!twin) return;
+
+ GetAttr(SCROLLER_Top, twin->objects[OID_HSCROLL], (ULONG *)&x);
+ GetAttr(SCROLLER_Top, twin->objects[OID_VSCROLL], (ULONG *)&y);
+
+ if(ami_gui_get_space_box(twin->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return;
+ }
+
+ ami_tree_redraw_request(x, y, bbox->Width, bbox->Height, twin);
+
+ ami_gui_free_space_box(bbox);
+}
+
+/**
+ * Scrolls the tree to make an element visible
+ *
+ * \param y Y coordinate of the element
+ * \param height height of the element
+ * \param data user data assigned to the tree on tree creation
+ */
+static void ami_tree_scroll_visible(int y, int height, void *data)
+{
+ int sy, scrollset;
+ struct IBox *bbox;
+ struct treeview_window *twin = data;
+
+ GetAttr(SCROLLER_Top, twin->objects[OID_VSCROLL], (ULONG *)&sy);
+ if(ami_gui_get_space_box(twin->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ return;
+ }
+
+ if((y > sy) && ((y + height) < (sy + bbox->Height))) {
+ ami_gui_free_space_box(bbox);
+ return;
+ }
+
+ if((y <= sy) || (height > bbox->Height)) scrollset = (ULONG)y;
+ else scrollset = sy + (y + height) - (sy + bbox->Height);
+
+ RefreshSetGadgetAttrs((APTR)twin->objects[OID_VSCROLL], twin->win, NULL,
+ SCROLLER_Top, scrollset,
+ TAG_DONE);
+
+ ami_gui_free_space_box(bbox);
+ ami_tree_draw(twin);
+}
+
+static void ami_tree_scroll(struct treeview_window *twin, int sx, int sy)
+{
+ int x, y;
+
+ if(!twin) return;
+
+ GetAttr(SCROLLER_Top, twin->objects[OID_HSCROLL], (ULONG *)&x);
+ GetAttr(SCROLLER_Top, twin->objects[OID_VSCROLL], (ULONG *)&y);
+
+ x += sx;
+ y += sy;
+
+ if(y < 0) y = 0;
+ if(x < 0) x = 0;
+
+ RefreshSetGadgetAttrs((APTR)twin->objects[OID_VSCROLL], twin->win, NULL,
+ SCROLLER_Top, y,
+ TAG_DONE);
+
+ RefreshSetGadgetAttrs((APTR)twin->objects[OID_HSCROLL], twin->win, NULL,
+ SCROLLER_Top, x,
+ TAG_DONE);
+
+ ami_tree_draw(twin);
+}
+
+static void ami_tree_drag_icon_show(struct treeview_window *twin)
+{
+ const char *type = "project";
+ nsurl *url = NULL;
+ const char *title = NULL;
+
+ if((twin->type == AMI_TREE_COOKIES) ||
+ (twin->type == AMI_TREE_SSLCERT)) return; /* No permissable drag operations */
+
+ if((tree_drag_status(twin->tree) == TREE_SELECT_DRAG) ||
+ (tree_drag_status(twin->tree) == TREE_TEXTAREA_DRAG))
+ return;
+
+ if((twin->type == AMI_TREE_HOTLIST) && (hotlist_has_selection())) {
+ hotlist_get_selection(&url, &title);
+ } else if((twin->type == AMI_TREE_HISTORY) && (global_history_has_selection())) {
+ global_history_get_selection(&url, &title);
+ }
+
+ if(title && (url == NULL))
+ {
+ ami_drag_icon_show(twin->win, "drawer");
+ }
+ else
+ {
+ ami_drag_icon_show(twin->win, type);
+ }
+}
+
+static void ami_tree_drag_end(struct treeview_window *twin, int x, int y)
+{
+ struct gui_window_2 *gwin;
+ struct treeview_window *tw;
+ BOOL drag;
+ nsurl *url = NULL;
+ const char *title = NULL;
+ bool ok = false;
+
+ if((drag = ami_drag_in_progress())) ami_drag_icon_close(twin->win);
+
+ if(drag && (twin != ami_window_at_pointer(AMINS_TVWINDOW)))
+ {
+ if((twin->type == AMI_TREE_HOTLIST) && (hotlist_has_selection())) {
+ ok = hotlist_get_selection(&url, &title);
+ } else if((twin->type == AMI_TREE_HISTORY) && (global_history_has_selection())) {
+ ok = global_history_get_selection(&url, &title);
+ }
+
+ if((ok == false) || (url == NULL)) {
+ DisplayBeep(scrn);
+ } else if(url) {
+ if((gwin = ami_window_at_pointer(AMINS_WINDOW))) {
+ browser_window_navigate(gwin->gw->bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ } else if((tw = ami_window_at_pointer(AMINS_TVWINDOW)) &&
+ (tw != twin) && (tw->type == AMI_TREE_HOTLIST)) {
+ hotlist_add_entry(url, title, true, y);
+ }
+ }
+ tree_mouse_action(twin->tree, twin->mouse_state | twin->key_state,
+ twin->drag_x, twin->drag_y); /* Keep the tree happy */
+ tree_drag_end(twin->tree, twin->mouse_state,
+ twin->drag_x, twin->drag_y,
+ twin->drag_x, twin->drag_y); /* Keep the tree happier */
+ } else {
+ if(tree_drag_status(twin->tree) == TREE_UNKNOWN_DRAG)
+ DisplayBeep(scrn);
+
+ tree_drag_end(twin->tree, twin->mouse_state,
+ twin->drag_x, twin->drag_y, x, y);
+ }
+}
+
+HOOKF(void, ami_tree_scroller_hook, Object *, object, struct IntuiMessage *)
+{
+ ULONG gid;
+ struct treeview_window *twin = hook->h_Data;
+ struct IntuiWheelData *wheel;
+
+ switch(msg->Class)
+ {
+ case IDCMP_IDCMPUPDATE:
+ gid = GetTagData( GA_ID, 0, msg->IAddress );
+
+ switch( gid )
+ {
+ case OID_HSCROLL:
+ case OID_VSCROLL:
+ ami_tree_draw(twin);
+ break;
+ }
+ break;
+#ifdef __amigaos4__
+ case IDCMP_EXTENDEDMOUSE:
+ if(msg->Code == IMSGCODE_INTUIWHEELDATA)
+ {
+ wheel = (struct IntuiWheelData *)msg->IAddress;
+
+ ami_tree_scroll(twin, (wheel->WheelX * 20), (wheel->WheelY * 20));
+ }
+ break;
+#endif
+ }
+}
+
+static void ami_tree_menu(struct treeview_window *twin)
+{
+ if(twin->menu) return;
+
+ if((twin->menu = ami_misc_allocvec_clear(sizeof(struct NewMenu) * AMI_TREE_MENU_ITEMS, 0))) {
+ twin->menu[0].nm_Type = NM_TITLE;
+ twin->menu_name[0] = ami_utf8_easy((char *)messages_get("Tree"));
+ twin->menu[0].nm_Label = twin->menu_name[0];
+
+ twin->menu[1].nm_Type = NM_ITEM;
+ twin->menu_name[1] = ami_utf8_easy((char *)messages_get("TreeExport"));
+ twin->menu[1].nm_Label = twin->menu_name[1];
+ if(twin->type == AMI_TREE_COOKIES)
+ twin->menu[1].nm_Flags = NM_ITEMDISABLED;
+ twin->menu[1].nm_CommKey = "S";
+
+ twin->menu[2].nm_Type = NM_ITEM;
+ twin->menu[2].nm_Label = NM_BARLABEL;
+
+ twin->menu[3].nm_Type = NM_ITEM;
+ twin->menu_name[3] = ami_utf8_easy((char *)messages_get("Expand"));
+ twin->menu[3].nm_Label = twin->menu_name[3];
+
+ twin->menu[4].nm_Type = NM_SUB;
+ twin->menu_name[4] = ami_utf8_easy((char *)messages_get("All"));
+ twin->menu[4].nm_Label = twin->menu_name[4];
+ twin->menu[4].nm_CommKey = "+";
+
+ if(twin->type == AMI_TREE_COOKIES)
+ {
+ twin->menu_name[5] = ami_utf8_easy((char *)messages_get("Domains"));
+ twin->menu_name[6] = ami_utf8_easy((char *)messages_get("Cookies"));
+ }
+ else
+ {
+ twin->menu_name[5] = ami_utf8_easy((char *)messages_get("Folders"));
+ twin->menu_name[6] = ami_utf8_easy((char *)messages_get("Links"));
+ }
+
+ twin->menu[5].nm_Type = NM_SUB;
+ twin->menu[5].nm_Label = twin->menu_name[5]; // tree-specific title
+
+ twin->menu[6].nm_Type = NM_SUB;
+ twin->menu[6].nm_Label = twin->menu_name[6]; // tree-specific title
+
+ twin->menu[7].nm_Type = NM_ITEM;
+ twin->menu_name[7] = ami_utf8_easy((char *)messages_get("Collapse"));
+ twin->menu[7].nm_Label = twin->menu_name[7];
+
+ twin->menu[8].nm_Type = NM_SUB;
+ twin->menu[8].nm_Label = twin->menu_name[4];
+ twin->menu[8].nm_CommKey = "-";
+
+ twin->menu[9].nm_Type = NM_SUB;
+ twin->menu[9].nm_Label = twin->menu_name[5]; // tree-specific title
+
+ twin->menu[10].nm_Type = NM_SUB;
+ twin->menu[10].nm_Label = twin->menu_name[6]; // tree-specific title
+
+ twin->menu[11].nm_Type = NM_ITEM;
+ twin->menu[11].nm_Label = NM_BARLABEL;
+
+ twin->menu[12].nm_Type = NM_ITEM;
+ twin->menu_name[12] = ami_utf8_easy((char *)messages_get("SnapshotWindow"));
+ twin->menu[12].nm_Label = twin->menu_name[12];
+
+ twin->menu[13].nm_Type = NM_ITEM;
+ twin->menu[13].nm_Label = NM_BARLABEL;
+
+ twin->menu[14].nm_Type = NM_ITEM;
+ twin->menu_name[14] = ami_utf8_easy((char *)messages_get("CloseWindow"));
+ twin->menu[14].nm_Label = twin->menu_name[14];
+ twin->menu[14].nm_CommKey = "K";
+
+ twin->menu[15].nm_Type = NM_TITLE;
+ twin->menu_name[15] = ami_utf8_easy((char *)messages_get("Edit"));
+ twin->menu[15].nm_Label = twin->menu_name[15];
+
+ twin->menu[16].nm_Type = NM_ITEM;
+ twin->menu_name[16] = ami_utf8_easy((char *)messages_get("TreeNewFolder"));
+ twin->menu[16].nm_Label = twin->menu_name[16];
+ twin->menu[16].nm_CommKey = "N";
+
+ twin->menu[17].nm_Type = NM_ITEM;
+ twin->menu_name[17] = ami_utf8_easy((char *)messages_get("TreeNewLink"));
+ twin->menu[17].nm_Label = twin->menu_name[17];
+
+ twin->menu[18].nm_Type = NM_ITEM;
+ twin->menu[18].nm_Label = NM_BARLABEL;
+
+ twin->menu[19].nm_Type = NM_ITEM;
+ twin->menu_name[19] = ami_utf8_easy((char *)messages_get("TreeEdit"));
+ twin->menu[19].nm_Label = twin->menu_name[19];
+ twin->menu[19].nm_CommKey = "E";
+
+ twin->menu[20].nm_Type = NM_ITEM;
+ twin->menu[20].nm_Label = NM_BARLABEL;
+
+ twin->menu[21].nm_Type = NM_ITEM;
+ twin->menu_name[21] = ami_utf8_easy((char *)messages_get("TreeDelete"));
+ twin->menu[21].nm_Label = twin->menu_name[21];
+ twin->menu[21].nm_CommKey = "D";
+
+ twin->menu[22].nm_Type = NM_ITEM;
+ twin->menu[22].nm_Label = NM_BARLABEL;
+
+ twin->menu[23].nm_Type = NM_ITEM;
+ twin->menu_name[23] = ami_utf8_easy((char *)messages_get("SelectAllNS"));
+ twin->menu[23].nm_Label = twin->menu_name[23];
+ twin->menu[23].nm_CommKey = "A";
+
+ twin->menu[24].nm_Type = NM_ITEM;
+ twin->menu_name[24] = ami_utf8_easy((char *)messages_get("ClearNS"));
+ twin->menu[24].nm_Label = twin->menu_name[24];
+
+ twin->menu[25].nm_Type = NM_END;
+ twin->menu_name[25] = NULL;
+ }
+}
+
+static void ami_tree_update_buttons(struct treeview_window *twin)
+{
+ if(twin->type == AMI_TREE_SSLCERT) return;
+ if(twin->menu == NULL) return;
+
+ if(twin->type != AMI_TREE_HOTLIST) {
+ OffMenu(twin->win, AMI_TREE_MENU_NEWDIR);
+ OffMenu(twin->win, AMI_TREE_MENU_NEWURL);
+ }
+
+ if((twin->type == AMI_TREE_HOTLIST) && (hotlist_has_selection())) {
+ OnMenu(twin->win, AMI_TREE_MENU_EDIT);
+ } else {
+ OffMenu(twin->win, AMI_TREE_MENU_EDIT);
+ }
+
+ if(((twin->type == AMI_TREE_HOTLIST) && (hotlist_has_selection())) ||
+ ((twin->type == AMI_TREE_COOKIES) && (cookie_manager_has_selection())) ||
+ ((twin->type == AMI_TREE_HISTORY) && (global_history_has_selection()))) {
+ OnMenu(twin->win, AMI_TREE_MENU_DELETE);
+ OnMenu(twin->win, AMI_TREE_MENU_CLEAR);
+ } else {
+ OffMenu(twin->win, AMI_TREE_MENU_DELETE);
+ OffMenu(twin->win, AMI_TREE_MENU_CLEAR);
+ }
+}
+
+void ami_tree_open(struct treeview_window *twin,int type)
+{
+ if(twin->win)
+ {
+ WindowToFront(twin->win);
+ ActivateWindow(twin->win);
+ return;
+ }
+
+ twin->type = type;
+
+ switch(twin->type)
+ {
+ case AMI_TREE_HOTLIST:
+ twin->wintitle = ami_utf8_easy((char *)messages_get("Hotlist"));
+ break;
+ case AMI_TREE_COOKIES:
+ twin->wintitle = ami_utf8_easy((char *)messages_get("Cookies"));
+ break;
+ case AMI_TREE_HISTORY:
+ twin->wintitle = ami_utf8_easy((char *)messages_get("GlobalHistory"));
+ break;
+ case AMI_TREE_SSLCERT:
+ twin->wintitle = ami_utf8_easy((char *)messages_get("SSLCerts"));
+ twin->sslerr = ami_utf8_easy((char *)messages_get("SSLError"));
+ twin->sslaccept = ami_utf8_easy((char *)messages_get("SSL_Certificate_Accept"));
+ twin->sslreject = ami_utf8_easy((char *)messages_get("SSL_Certificate_Reject"));
+ break;
+ }
+
+ twin->scrollerhook.h_Entry = (void *)ami_tree_scroller_hook;
+ twin->scrollerhook.h_Data = twin;
+
+ ami_init_layers(&twin->globals, 0, 0, false);
+ ami_tree_menu(twin);
+
+ if(type == AMI_TREE_SSLCERT)
+ {
+ twin->objects[OID_MAIN] = WindowObj,
+ WA_ScreenTitle, ami_gui_get_screen_title(),
+ WA_Title, twin->wintitle,
+ WA_Activate, TRUE,
+ WA_DepthGadget, TRUE,
+ WA_DragBar, TRUE,
+ WA_CloseGadget, TRUE,
+ WA_SizeGadget, TRUE,
+ WA_SizeBRight, TRUE,
+ WA_Height, scrn->Height / 2,
+ WA_PubScreen,scrn,
+ WA_ReportMouse,TRUE,
+ WA_IDCMP, IDCMP_MOUSEMOVE | IDCMP_MOUSEBUTTONS | IDCMP_NEWSIZE |
+ IDCMP_RAWKEY | IDCMP_GADGETUP | IDCMP_IDCMPUPDATE |
+ IDCMP_EXTENDEDMOUSE | IDCMP_SIZEVERIFY,
+ WINDOW_HorizProp,1,
+ WINDOW_VertProp,1,
+ WINDOW_IDCMPHook,&twin->scrollerhook,
+ WINDOW_IDCMPHookBits,IDCMP_IDCMPUPDATE | IDCMP_EXTENDEDMOUSE,
+ WINDOW_SharedPort,sport,
+ WINDOW_UserData,twin,
+ /* WINDOW_NewMenu, twin->menu, -> No menu for SSL Cert */
+ WINDOW_IconifyGadget, FALSE,
+ WINDOW_Position, WPOS_CENTERSCREEN,
+ WINDOW_ParentGroup, twin->objects[GID_MAIN] = LayoutVObj,
+ LAYOUT_AddImage, LabelObj,
+ LABEL_Text, twin->sslerr,
+ LabelEnd,
+ LAYOUT_AddChild, twin->objects[GID_BROWSER] = SpaceObj,
+ GA_ID, GID_BROWSER,
+ SPACE_Transparent,TRUE,
+ SPACE_BevelStyle, BVS_DISPLAY,
+ SpaceEnd,
+ LAYOUT_AddChild, LayoutHObj,
+ LAYOUT_AddChild, twin->objects[GID_OPEN] = ButtonObj,
+ GA_ID,GID_OPEN,
+ GA_Text, twin->sslaccept,
+ GA_RelVerify,TRUE,
+ ButtonEnd,
+ LAYOUT_AddChild, twin->objects[GID_CANCEL] = ButtonObj,
+ GA_ID,GID_CANCEL,
+ GA_Text, twin->sslreject,
+ GA_RelVerify,TRUE,
+ ButtonEnd,
+ EndGroup,
+ CHILD_WeightedHeight,0,
+ EndGroup,
+ EndWindow;
+ }
+ else
+ {
+ ULONG width = scrn->Width / 2;
+ ULONG height = scrn->Height / 2;
+ ULONG top = (scrn->Height / 2) - (height / 2);
+ ULONG left = (scrn->Width / 2) - (width / 2);
+
+ if((type == AMI_TREE_HOTLIST) && (nsoption_int(hotlist_window_xsize) > 0))
+ {
+ top = nsoption_int(hotlist_window_ypos);
+ left = nsoption_int(hotlist_window_xpos);
+ width = nsoption_int(hotlist_window_xsize);
+ height = nsoption_int(hotlist_window_ysize);
+ }
+ else if((type == AMI_TREE_HISTORY) && (nsoption_int(history_window_xsize) > 0))
+ {
+ top = nsoption_int(history_window_ypos);
+ left = nsoption_int(history_window_xpos);
+ width = nsoption_int(history_window_xsize);
+ height = nsoption_int(history_window_ysize);
+ }
+ else if((type == AMI_TREE_COOKIES) && (nsoption_int(cookies_window_xsize) > 0))
+ {
+ top = nsoption_int(cookies_window_ypos);
+ left = nsoption_int(cookies_window_xpos);
+ width = nsoption_int(cookies_window_xsize);
+ height = nsoption_int(cookies_window_ysize);
+ }
+
+ twin->objects[OID_MAIN] = WindowObj,
+ WA_ScreenTitle, ami_gui_get_screen_title(),
+ WA_Title, twin->wintitle,
+ WA_Activate, TRUE,
+ WA_DepthGadget, TRUE,
+ WA_DragBar, TRUE,
+ WA_CloseGadget, TRUE,
+ WA_SizeGadget, TRUE,
+ WA_SizeBRight, TRUE,
+ WA_Top, top,
+ WA_Left, left,
+ WA_Width, width,
+ WA_Height, height,
+ WA_PubScreen,scrn,
+ WA_ReportMouse,TRUE,
+ WA_IDCMP, IDCMP_MOUSEMOVE | IDCMP_MOUSEBUTTONS | IDCMP_NEWSIZE |
+ IDCMP_RAWKEY | IDCMP_GADGETUP | IDCMP_IDCMPUPDATE |
+ IDCMP_EXTENDEDMOUSE | IDCMP_SIZEVERIFY | IDCMP_INTUITICKS,
+ WINDOW_HorizProp,1,
+ WINDOW_VertProp,1,
+ WINDOW_IDCMPHook,&twin->scrollerhook,
+ WINDOW_IDCMPHookBits,IDCMP_IDCMPUPDATE | IDCMP_EXTENDEDMOUSE,
+ WINDOW_SharedPort,sport,
+ WINDOW_UserData,twin,
+ WINDOW_NewMenu, twin->menu,
+ WINDOW_IconifyGadget, FALSE,
+// WINDOW_Position, WPOS_CENTERSCREEN,
+ WINDOW_ParentGroup, twin->objects[GID_MAIN] = LayoutVObj,
+ LAYOUT_AddChild, twin->objects[GID_BROWSER] = SpaceObj,
+ GA_ID, GID_BROWSER,
+ SPACE_Transparent,TRUE,
+ SPACE_BevelStyle, BVS_DISPLAY,
+ SpaceEnd,
+ EndGroup,
+ EndWindow;
+ }
+
+ twin->win = (struct Window *)RA_OpenWindow(twin->objects[OID_MAIN]);
+
+ GetAttr(WINDOW_HorizObject, twin->objects[OID_MAIN],
+ (ULONG *)&twin->objects[OID_HSCROLL]);
+ GetAttr(WINDOW_VertObject, twin->objects[OID_MAIN],
+ (ULONG *)&twin->objects[OID_VSCROLL]);
+
+ RefreshSetGadgetAttrs((APTR)twin->objects[OID_VSCROLL], twin->win, NULL,
+ GA_ID,OID_VSCROLL,
+ ICA_TARGET,ICTARGET_IDCMP,
+ TAG_DONE);
+
+ RefreshSetGadgetAttrs((APTR)twin->objects[OID_HSCROLL], twin->win, NULL,
+ GA_ID,OID_HSCROLL,
+ ICA_TARGET,ICTARGET_IDCMP,
+ TAG_DONE);
+
+ twin->node = AddObject(window_list,AMINS_TVWINDOW);
+ twin->node->objstruct = twin;
+
+ ami_tree_update_buttons(twin);
+ ami_tree_resized(twin->tree, twin->max_width, twin->max_height, twin);
+ ami_tree_draw(twin);
+}
+
+void ami_tree_close(struct treeview_window *twin)
+{
+ int i;
+
+ twin->win = NULL;
+ DisposeObject(twin->objects[OID_MAIN]);
+ DelObjectNoFree(twin->node);
+ ami_plot_release_pens(twin->shared_pens);
+ ami_free_layers(&twin->globals);
+
+ for(i=0;i<AMI_TREE_MENU_ITEMS;i++) {
+ if(twin->menu_name[i] && (twin->menu_name[i] != NM_BARLABEL))
+ ami_utf8_free(twin->menu_name[i]);
+ twin->menu_name[i] = NULL;
+ }
+
+ FreeVec(twin->menu);
+ twin->menu = NULL;
+ ami_utf8_free(twin->wintitle);
+ twin->wintitle = NULL;
+ if(twin->type == AMI_TREE_SSLCERT) {
+ ami_utf8_free(twin->sslerr);
+ ami_utf8_free(twin->sslaccept);
+ ami_utf8_free(twin->sslreject);
+ ami_ssl_free(twin);
+ }
+
+ if(twin->type == AMI_TREE_HOTLIST)
+ ami_gui_hotlist_update_all();
+}
+
+static void ami_tree_update_quals(struct treeview_window *twin)
+{
+ uint32 quals = 0;
+#ifdef __amigaos4__
+ GetAttr(WINDOW_Qualifier, twin->objects[OID_MAIN], (uint32 *)&quals);
+#else
+#warning FIXME not reading qualifiers on OS3
+#endif
+ twin->key_state = 0;
+
+ if((quals & IEQUALIFIER_LSHIFT) || (quals & IEQUALIFIER_RSHIFT))
+ {
+ twin->key_state |= BROWSER_MOUSE_MOD_1;
+ }
+
+ if(quals & IEQUALIFIER_CONTROL)
+ {
+ twin->key_state |= BROWSER_MOUSE_MOD_2;
+ }
+
+ if((quals & IEQUALIFIER_LALT) || (quals & IEQUALIFIER_RALT))
+ {
+ twin->key_state |= BROWSER_MOUSE_MOD_3;
+ }
+}
+
+BOOL ami_tree_event(struct treeview_window *twin)
+{
+ /* return TRUE if window destroyed */
+ ULONG result,storage = 0;
+ uint16 code;
+ struct MenuItem *item;
+ ULONG menunum=0,itemnum=0,subnum=0;
+ int xs, ys, x, y;
+ struct IBox *bbox;
+ struct timeval curtime;
+ struct InputEvent *ie;
+ int nskey;
+ char fname[1024];
+ static int drag_x_move = 0, drag_y_move = 0;
+
+ while((result = RA_HandleInput(twin->objects[OID_MAIN],&code)) != WMHI_LASTMSG)
+ {
+ switch(result & WMHI_CLASSMASK) // class
+ {
+ case WMHI_GADGETUP:
+ switch(result & WMHI_GADGETMASK)
+ {
+ case GID_OPEN:
+ sslcert_viewer_accept(twin->ssl_data);
+ ami_tree_close(twin);
+ return TRUE;
+ break;
+
+ case GID_CANCEL:
+ sslcert_viewer_reject(twin->ssl_data);
+ ami_tree_close(twin);
+ return TRUE;
+ break;
+ }
+ break;
+
+ case WMHI_MOUSEMOVE:
+ drag_x_move = 0;
+ drag_y_move = 0;
+
+ if(ami_gui_get_space_box(twin->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ break;
+ }
+
+ GetAttr(SCROLLER_Top, twin->objects[OID_HSCROLL], (ULONG *)&xs);
+ x = twin->win->MouseX - bbox->Left + xs;
+
+ GetAttr(SCROLLER_Top, twin->objects[OID_VSCROLL], (ULONG *)&ys);
+ y = twin->win->MouseY - bbox->Top + ys;
+
+ if(twin->mouse_state & BROWSER_MOUSE_DRAG_ON)
+ {
+ ami_drag_icon_move();
+
+ if((twin->win->MouseX < bbox->Left) &&
+ ((twin->win->MouseX - bbox->Left) > -AMI_DRAG_THRESHOLD))
+ drag_x_move = twin->win->MouseX - bbox->Left;
+ if((twin->win->MouseX > (bbox->Left + bbox->Width)) &&
+ ((twin->win->MouseX - (bbox->Left + bbox->Width)) < AMI_DRAG_THRESHOLD))
+ drag_x_move = twin->win->MouseX - (bbox->Left + bbox->Width);
+ if((twin->win->MouseY < bbox->Top) &&
+ ((twin->win->MouseY - bbox->Top) > -AMI_DRAG_THRESHOLD))
+ drag_y_move = twin->win->MouseY - bbox->Top;
+ if((twin->win->MouseY > (bbox->Top + bbox->Height)) &&
+ ((twin->win->MouseY - (bbox->Top + bbox->Height)) < AMI_DRAG_THRESHOLD))
+ drag_y_move = twin->win->MouseY - (bbox->Top + bbox->Height);
+
+ tree_mouse_action(twin->tree, twin->mouse_state | twin->key_state, x, y);
+ }
+
+ if((x >= xs) && (y >= ys) && (x < bbox->Width + xs) &&
+ (y < bbox->Height + ys))
+ {
+ ami_tree_update_quals(twin);
+
+ if(twin->mouse_state & BROWSER_MOUSE_PRESS_1)
+ {
+ if((abs(x - twin->drag_x) + abs(y - twin->drag_y)) > 2)
+ {
+ tree_mouse_action(twin->tree,
+ BROWSER_MOUSE_DRAG_1 | twin->key_state, x, y);
+ twin->mouse_state = BROWSER_MOUSE_HOLDING_1 |
+ BROWSER_MOUSE_DRAG_ON;
+ ami_tree_drag_icon_show(twin);
+ }
+ }
+ else if(twin->mouse_state & BROWSER_MOUSE_PRESS_2)
+ {
+ if((abs(x - twin->drag_x) + abs(y - twin->drag_y)) > 2)
+ {
+ tree_mouse_action(twin->tree,
+ BROWSER_MOUSE_DRAG_2 | twin->key_state, x, y);
+ twin->mouse_state = BROWSER_MOUSE_HOLDING_2 |
+ BROWSER_MOUSE_DRAG_ON;
+ ami_tree_drag_icon_show(twin);
+ }
+ }
+ }
+ ami_gui_free_space_box(bbox);
+
+ twin->lastclick.tv_sec = 0;
+ twin->lastclick.tv_usec = 0;
+ break;
+
+ case WMHI_MOUSEBUTTONS:
+ if(ami_gui_get_space_box((Object *)twin->objects[GID_BROWSER], &bbox) != NSERROR_OK) {
+ amiga_warn_user("NoMemory", "");
+ break;
+ }
+
+ GetAttr(SCROLLER_Top, twin->objects[OID_HSCROLL], (ULONG *)&xs);
+ x = twin->win->MouseX - bbox->Left + xs;
+ GetAttr(SCROLLER_Top, twin->objects[OID_VSCROLL], (ULONG *)&ys);
+ y = twin->win->MouseY - bbox->Top + ys;
+
+ ami_tree_update_quals(twin);
+
+ if((x >= xs) && (y >= ys) && (x < bbox->Width + xs) &&
+ (y < bbox->Height + ys))
+ {
+ switch(code)
+ {
+ case SELECTDOWN:
+ twin->mouse_state = BROWSER_MOUSE_PRESS_1;
+ if(twin->drag_x == 0) twin->drag_x = x;
+ if(twin->drag_y == 0) twin->drag_y = y;
+ break;
+ case MIDDLEDOWN:
+ twin->mouse_state = BROWSER_MOUSE_PRESS_2;
+ if(twin->drag_x == 0) twin->drag_x = x;
+ if(twin->drag_y == 0) twin->drag_y = y;
+ break;
+ case MENUDOWN:
+#if 0
+ if(tree_node_has_selection(tree_get_root(twin->tree)) == false)
+ {
+ tree_set_node_selected_at(twin->tree, x, y, true);
+ }
+ ami_context_menu_show_tree(twin->tree, twin->win, twin->type);
+#endif
+ break;
+ }
+ }
+
+ if(x < xs) x = xs;
+ if(y < ys) y = ys;
+ if(x >= bbox->Width + xs) x = bbox->Width + xs - 1;
+ if(y >= bbox->Height + ys) y = bbox->Height + ys - 1;
+
+ ami_gui_free_space_box(bbox);
+
+ switch(code)
+ {
+ case SELECTUP:
+ if(twin->mouse_state & BROWSER_MOUSE_PRESS_1)
+ {
+ CurrentTime((ULONG *)&curtime.tv_sec, (ULONG *)&curtime.tv_usec);
+
+ twin->mouse_state = BROWSER_MOUSE_CLICK_1;
+
+ if(twin->lastclick.tv_sec)
+ {
+ if(DoubleClick(twin->lastclick.tv_sec,
+ twin->lastclick.tv_usec,
+ curtime.tv_sec, curtime.tv_usec))
+ twin->mouse_state |= BROWSER_MOUSE_DOUBLE_CLICK;
+ }
+ tree_mouse_action(twin->tree,
+ twin->mouse_state | twin->key_state, x, y);
+
+ if(twin->mouse_state & BROWSER_MOUSE_DOUBLE_CLICK)
+ {
+ twin->lastclick.tv_sec = 0;
+ twin->lastclick.tv_usec = 0;
+ }
+ else
+ {
+ twin->lastclick.tv_sec = curtime.tv_sec;
+ twin->lastclick.tv_usec = curtime.tv_usec;
+ }
+ }
+ else ami_tree_drag_end(twin, x, y);
+
+ twin->mouse_state=0;
+ twin->drag_x = 0;
+ twin->drag_y = 0;
+ break;
+
+ case MIDDLEUP:
+ if(twin->mouse_state & BROWSER_MOUSE_PRESS_2)
+ {
+ CurrentTime((ULONG *)&curtime.tv_sec, (ULONG *)&curtime.tv_usec);
+
+ twin->mouse_state = BROWSER_MOUSE_CLICK_2;
+
+ if(twin->lastclick.tv_sec)
+ {
+ if(DoubleClick(twin->lastclick.tv_sec,
+ twin->lastclick.tv_usec,
+ curtime.tv_sec, curtime.tv_usec))
+ twin->mouse_state |= BROWSER_MOUSE_DOUBLE_CLICK;
+ }
+ tree_mouse_action(twin->tree,
+ twin->mouse_state | twin->key_state, x, y);
+
+ if(twin->mouse_state & BROWSER_MOUSE_DOUBLE_CLICK)
+ {
+ twin->lastclick.tv_sec = 0;
+ twin->lastclick.tv_usec = 0;
+ }
+ else
+ {
+ twin->lastclick.tv_sec = curtime.tv_sec;
+ twin->lastclick.tv_usec = curtime.tv_usec;
+ }
+ }
+ else ami_tree_drag_end(twin, x, y);
+
+ twin->mouse_state=0;
+ twin->drag_x = 0;
+ twin->drag_y = 0;
+ break;
+
+ case SELECTDOWN:
+ case MIDDLEDOWN:
+ tree_mouse_action(twin->tree,
+ twin->mouse_state | twin->key_state, x, y);
+ break;
+ }
+ ami_tree_update_buttons(twin);
+ break;
+
+ case WMHI_RAWKEY:
+ storage = result & WMHI_GADGETMASK;
+
+ GetAttr(WINDOW_InputEvent,twin->objects[OID_MAIN],(ULONG *)&ie);
+ nskey = ami_key_to_nskey(storage, ie);
+ tree_keypress(twin->tree, nskey);
+ if(nskey == NS_KEY_COPY_SELECTION) {
+ /* if we've copied a selection we need to clear it - style guide rules */
+ tree_keypress(twin->tree, NS_KEY_CLEAR_SELECTION);
+ }
+ break;
+
+ case WMHI_MENUPICK:
+ item = ItemAddress(twin->win->MenuStrip,code);
+ while (code != MENUNULL)
+ {
+ menunum = MENUNUM(code);
+ itemnum = ITEMNUM(code);
+ subnum = SUBNUM(code);
+
+ switch(menunum)
+ {
+ case 0: // tree
+ switch(itemnum)
+ {
+ case 0: // export
+ if(AslRequestTags(savereq,
+ ASLFR_Window, twin->win,
+ ASLFR_SleepWindow, TRUE,
+ ASLFR_TitleText,messages_get("NetSurf"),
+ ASLFR_Screen,scrn,
+ ASLFR_InitialFile,"tree_export.html",
+ TAG_DONE))
+ {
+ strlcpy(fname,savereq->fr_Drawer,1024);
+ AddPart(fname,savereq->fr_File,1024);
+ ami_update_pointer(twin->win, GUI_POINTER_WAIT);
+ if(twin->type == AMI_TREE_HISTORY)
+ global_history_export(fname, NULL);
+ else if(twin->type == AMI_TREE_HOTLIST)
+ hotlist_export(fname, NULL);
+ ami_update_pointer(twin->win, GUI_POINTER_DEFAULT);
+ }
+ break;
+
+ case 2: // expand
+ switch(subnum)
+ {
+ case 0: // all
+ switch(twin->type)
+ {
+ case AMI_TREE_HISTORY:
+ global_history_expand(false);
+ break;
+ case AMI_TREE_COOKIES:
+ cookie_manager_expand(false);
+ break;
+ case AMI_TREE_HOTLIST:
+ hotlist_expand(false);
+ break;
+ }
+ break;
+
+ case 1: // lev 1
+ switch(twin->type)
+ {
+ case AMI_TREE_HISTORY:
+ global_history_expand(true);
+ break;
+ case AMI_TREE_COOKIES:
+ cookie_manager_expand(true);
+ break;
+ case AMI_TREE_HOTLIST:
+ hotlist_expand(true);
+ break;
+ }
+ break;
+
+ case 2: // lev 2
+ switch(twin->type)
+ {
+ case AMI_TREE_HISTORY:
+ global_history_expand(false);
+ break;
+ case AMI_TREE_COOKIES:
+ cookie_manager_expand(false);
+ break;
+ case AMI_TREE_HOTLIST:
+ hotlist_expand(false);
+ break;
+ }
+ break;
+ }
+ break;
+
+ case 3: // collapse
+ switch(subnum)
+ {
+ case 0: // all
+ switch(twin->type)
+ {
+ case AMI_TREE_HISTORY:
+ global_history_contract(true);
+ break;
+ case AMI_TREE_COOKIES:
+ cookie_manager_contract(true);
+ break;
+ case AMI_TREE_HOTLIST:
+ hotlist_contract(true);
+ break;
+ }
+ break;
+
+ case 1: // lev 1
+ switch(twin->type)
+ {
+ case AMI_TREE_HISTORY:
+ global_history_contract(true);
+ break;
+ case AMI_TREE_COOKIES:
+ cookie_manager_contract(true);
+ break;
+ case AMI_TREE_HOTLIST:
+ hotlist_contract(true);
+ break;
+ }
+ break;
+
+ case 2: // lev 2
+ switch(twin->type)
+ {
+ case AMI_TREE_HISTORY:
+ global_history_contract(false);
+ break;
+ case AMI_TREE_COOKIES:
+ cookie_manager_contract(false);
+ break;
+ case AMI_TREE_HOTLIST:
+ hotlist_contract(false);
+ break;
+ }
+ break;
+ }
+ break;
+
+ case 5: // snapshot
+ switch(twin->type)
+ {
+ case AMI_TREE_HISTORY:
+ nsoption_set_int(history_window_ypos, twin->win->TopEdge);
+ nsoption_set_int(history_window_xpos, twin->win->LeftEdge);
+ nsoption_set_int(history_window_xsize, twin->win->Width);
+ nsoption_set_int(history_window_ysize, twin->win->Height);
+ break;
+ case AMI_TREE_COOKIES:
+ nsoption_set_int(cookies_window_ypos, twin->win->TopEdge);
+ nsoption_set_int(cookies_window_xpos, twin->win->LeftEdge);
+ nsoption_set_int(cookies_window_xsize, twin->win->Width);
+ nsoption_set_int(cookies_window_ysize, twin->win->Height);
+ break;
+ case AMI_TREE_HOTLIST:
+ nsoption_set_int(hotlist_window_ypos, twin->win->TopEdge);
+ nsoption_set_int(hotlist_window_xpos, twin->win->LeftEdge);
+ nsoption_set_int(hotlist_window_xsize, twin->win->Width);
+ nsoption_set_int(hotlist_window_ysize, twin->win->Height);
+ break;
+ }
+ break;
+
+ case 7: // close
+ ami_tree_close(twin);
+ return TRUE;
+ break;
+ }
+ break;
+
+ case 1: // edit
+ switch(itemnum)
+ {
+ case 0: // new folder
+ hotlist_add_folder(NULL, false, 0);
+ break;
+
+ case 1: // new entry
+ hotlist_add_entry(NULL, NULL, false, 0);
+ break;
+
+ case 3: // edit
+ hotlist_edit_selection();
+ break;
+
+ case 5: // delete
+ switch(twin->type)
+ {
+ case AMI_TREE_HISTORY:
+ global_history_keypress(NS_KEY_DELETE_LEFT);
+ break;
+ case AMI_TREE_COOKIES:
+ cookie_manager_keypress(NS_KEY_DELETE_LEFT);
+ break;
+ case AMI_TREE_HOTLIST:
+ hotlist_keypress(NS_KEY_DELETE_LEFT);
+ break;
+ }
+ ami_tree_update_buttons(twin);
+ break;
+
+ case 7: // select all
+ switch(twin->type)
+ {
+ case AMI_TREE_HISTORY:
+ global_history_keypress(NS_KEY_SELECT_ALL);
+ break;
+ case AMI_TREE_COOKIES:
+ cookie_manager_keypress(NS_KEY_SELECT_ALL);
+ break;
+ case AMI_TREE_HOTLIST:
+ hotlist_keypress(NS_KEY_SELECT_ALL);
+ break;
+ }
+ ami_tree_update_buttons(twin);
+ break;
+
+ case 8: // clear
+ switch(twin->type)
+ {
+ case AMI_TREE_HISTORY:
+ global_history_keypress(NS_KEY_CLEAR_SELECTION);
+ break;
+ case AMI_TREE_COOKIES:
+ cookie_manager_keypress(NS_KEY_CLEAR_SELECTION);
+ break;
+ case AMI_TREE_HOTLIST:
+ hotlist_keypress(NS_KEY_CLEAR_SELECTION);
+ break;
+ }
+ ami_tree_update_buttons(twin);
+ break;
+ }
+ break;
+ }
+
+ code = item->NextSelect;
+ }
+ break;
+
+ case WMHI_NEWSIZE:
+ ami_tree_resized(twin->tree, twin->max_width, twin->max_height, twin);
+ ami_tree_draw(twin);
+ break;
+
+ case WMHI_CLOSEWINDOW:
+ if(twin->type == AMI_TREE_SSLCERT)
+ sslcert_viewer_reject(twin->ssl_data);
+ ami_tree_close(twin);
+ return TRUE;
+ break;
+ }
+ }
+
+ if(drag_x_move || drag_y_move)
+ ami_tree_scroll(twin, drag_x_move, drag_y_move);
+
+ return FALSE;
+}
+
+
+
+
+
+const struct treeview_table ami_tree_callbacks = {
+ .redraw_request = ami_tree_redraw_request,
+ .resized = ami_tree_resized,
+ .scroll_visible = ami_tree_scroll_visible,
+ .get_window_dimensions = ami_tree_get_window_dimensions
+};
+
+struct treeview_window *ami_tree_create(int flags,
+ struct sslcert_session_data *ssl_data)
+{
+ struct treeview_window *twin;
+
+ twin = ami_misc_allocvec_clear(sizeof(struct treeview_window), 0);
+
+ if(!twin)
+ {
+ amiga_warn_user("NoMemory", 0);
+ return NULL;
+ }
+
+ twin->shared_pens = ami_AllocMinList();
+ twin->globals.shared_pens = twin->shared_pens;
+
+ twin->ssl_data = ssl_data;
+ twin->tree = tree_create(flags, &ami_tree_callbacks, twin);
+
+ return twin;
+}
+
diff --git a/frontends/amiga/tree.h b/frontends/amiga/tree.h
new file mode 100755
index 000000000..39a71d70d
--- /dev/null
+++ b/frontends/amiga/tree.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2008, 2009 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_TREE_H
+#define AMIGA_TREE_H
+
+#include <exec/types.h>
+#include <intuition/classusr.h>
+#include "amiga/os3support.h"
+#include "desktop/tree.h"
+
+struct treeview_window;
+
+enum
+{
+ AMI_TREE_HOTLIST,
+ AMI_TREE_HISTORY,
+ AMI_TREE_COOKIES,
+ AMI_TREE_SSLCERT
+};
+
+struct treeview_window *ami_tree_create(int flags,
+ struct sslcert_session_data *ssl_data);
+void ami_tree_destroy(struct treeview_window *twin);
+struct tree *ami_tree_get_tree(struct treeview_window *twin);
+
+void ami_tree_open(struct treeview_window *twin,int type);
+void ami_tree_close(struct treeview_window *twin);
+BOOL ami_tree_event(struct treeview_window *twin);
+
+extern const struct treeview_table ami_tree_callbacks;
+
+#endif
diff --git a/frontends/amiga/utf8.c b/frontends/amiga/utf8.c
new file mode 100755
index 000000000..91d7c90c0
--- /dev/null
+++ b/frontends/amiga/utf8.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2008 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <proto/exec.h>
+#include <proto/diskfont.h>
+#include <diskfont/diskfonttag.h>
+
+#include "utils/nsoption.h"
+#include "utils/utf8.h"
+#include "desktop/gui_utf8.h"
+
+#include "amiga/utf8.h"
+
+nserror utf8_from_local_encoding(const char *string, size_t len, char **result)
+{
+ const char *encname = "ISO-8859-1";
+
+#ifdef __amigaos4__
+ LONG charset;
+
+ charset = GetDiskFontCtrl(DFCTRL_CHARSET);
+ encname = (const char *) ObtainCharsetInfo(DFCS_NUMBER, charset, DFCS_MIMENAME);
+#else
+ encname = nsoption_charp(local_charset);
+#endif
+
+ return utf8_from_enc(string,encname,len,result,NULL);
+}
+
+nserror utf8_to_local_encoding(const char *string, size_t len, char **result)
+{
+ const char *encname = "ISO-8859-1";
+
+#ifdef __amigaos4__
+ LONG charset;
+
+ charset = GetDiskFontCtrl(DFCTRL_CHARSET);
+ encname = (const char *) ObtainCharsetInfo(DFCS_NUMBER, charset, DFCS_MIMENAME);
+#else
+ encname = nsoption_charp(local_charset);
+#endif
+
+ return utf8_to_enc(string,encname,len,result);
+}
+
+void ami_utf8_free(char *ptr)
+{
+ if(ptr) free(ptr);
+}
+
+char *ami_utf8_easy(const char *string)
+{
+ char *localtext;
+
+ if(utf8_to_local_encoding(string,strlen(string),&localtext) == NSERROR_OK)
+ {
+ return localtext;
+ }
+ else
+ {
+ return strdup(string);
+ }
+}
+
+char *ami_to_utf8_easy(const char *string)
+{
+ char *localtext;
+
+ if(utf8_from_local_encoding(string,strlen(string),&localtext) == NSERROR_OK)
+ {
+ return localtext;
+ }
+ else
+ {
+ return strdup(string);
+ }
+}
+
+
+static struct gui_utf8_table utf8_table = {
+ .utf8_to_local = utf8_to_local_encoding,
+ .local_to_utf8 = utf8_from_local_encoding,
+};
+
+struct gui_utf8_table *amiga_utf8_table = &utf8_table;
+
diff --git a/frontends/amiga/utf8.h b/frontends/amiga/utf8.h
new file mode 100755
index 000000000..065a149f2
--- /dev/null
+++ b/frontends/amiga/utf8.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2008 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 AMIGA_UTF8_H
+#define AMIGA_UTF8_H
+
+extern struct gui_utf8_table *ami_utf8_table;
+
+char *ami_utf8_easy(const char *string);
+void ami_utf8_free(char *ptr);
+char *ami_to_utf8_easy(const char *string);
+
+nserror utf8_from_local_encoding(const char *string, size_t len, char **result);
+nserror utf8_to_local_encoding(const char *string, size_t len, char **result);
+
+#endif
diff --git a/frontends/amiga/version.c b/frontends/amiga/version.c
new file mode 100644
index 000000000..bc31d0c96
--- /dev/null
+++ b/frontends/amiga/version.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2011 Chris Young <chris@unsatisfactorysoftware.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "testament.h"
+
+/* NB: AmigaOS revision numbers start at 1 (not 0) and are monotonically
+ * incremental (v1.20 is higher than v1.3 and not the same as v1.2).
+ * Consequently, this version pair may not match the user-facing one in
+ * desktop/version.c. Release revisions are prepended with 6000 to ensure
+ * they are higher than CI builds, and make this (slightly) less confusing.
+ */
+#define NETSURF_VERSION_MAJOR "3"
+#define NETSURF_VERSION_MINOR_EXTERNAL "6"
+#if defined(CI_BUILD)
+#define NETSURF_VERSION_MINOR CI_BUILD
+#else
+#define NETSURF_VERSION_MINOR "6000" NETSURF_VERSION_MINOR_EXTERNAL
+#endif
+
+static const __attribute__((used)) char *verstag = "\0$VER: NetSurf " NETSURF_VERSION_MAJOR "." NETSURF_VERSION_MINOR " (" WT_COMPILEDATE ")\0";
+const char * const verdate = WT_COMPILEDATE;
+const char * const verarexx = NETSURF_VERSION_MAJOR "." NETSURF_VERSION_MINOR_EXTERNAL;
+const char * const wt_revid = WT_REVID;
+
diff --git a/frontends/atari/Makefile b/frontends/atari/Makefile
new file mode 100644
index 000000000..60b7f879c
--- /dev/null
+++ b/frontends/atari/Makefile
@@ -0,0 +1,208 @@
+# ----------------------------------------------------------------------------
+# Atari target setup
+# ----------------------------------------------------------------------------
+
+# !!!IMPORTANT!!!
+# On regular m68k-atari-mint installation, these must be set
+# manually via env. variables, because the m68k-atari-mint
+# toolchain is located in /usr/bin and the wildcard matching
+# fails then. So this wildcard matches only works for
+# the netsurf environment!
+STRIP := $(wildcard $(GCCSDK_INSTALL_CROSSBIN)/*strip)
+STACK := $(wildcard $(GCCSDK_INSTALL_CROSSBIN)/*stack)
+FT2CF := $(GCCSDK_INSTALL_ENV)/bin/freetype-config
+
+ifeq ($(ATARIARCH),68000)
+PRGSUFFIX := 000.app
+PKGNAME := ns000.zip
+endif
+
+ifeq ($(ATARIARCH),68020-60)
+CFLAGS += -m68020-60
+LDFLAGS += -m68020-60
+PRGSUFFIX := 020.app
+PKGNAME := ns020.zip
+endif
+
+ifeq ($(ATARIARCH),v4e)
+CFLAGS += -mcpu=5475
+LDFLAGS += -mcpu=5475
+PRGSUFFIX := v4e.app
+PKGNAME := nsv4e.zip
+endif
+
+# non-pkgconfig components
+
+FREETYPE_FONT_CFLAGS := $(shell $(FT2CF) --cflags) -DWITH_FREETYPE_FONT_DRIVER
+SPIDERMONKEY_CFLAGS := -DXP_UNIX -DJS_HAS_FILE_OBJECT=0 -DJSOPTION_JIT=0 -DPOSIX_SOURCE -D_BSD_SOURCE
+
+$(eval $(call feature_enabled,ATARI_FREETYPE_FONT,$(FREETYPE_FONT_CFLAGS),-lfreetype,(Freetype)))
+$(eval $(call feature_enabled,ATARI_NETSURF_FONT,-DWITH_INTERNAL_FONT_DRIVER,,(Internal Font)))
+$(eval $(call feature_enabled,ATARI_VDI_FONT,-DWITH_VDI_FONT_DRIVER,,(VDI Font)))
+$(eval $(call feature_enabled,ATARI_8BPP_SUPPORT,-DWITH_8BPP_SUPPORT,,(Indexed screen format support)))
+
+$(eval $(call pkg_config_find_and_add,libcares,Cares))
+
+CFLAGS += -U__STRICT_ANSI__ -std=c99 -Dsmall -Dnsatari \
+ -D_BSD_SOURCE \
+ -D_XOPEN_SOURCE=600 \
+ -D_POSIX_C_SOURCE=200112L \
+ $(shell $(PKG_CONFIG) --cflags openssl ) \
+ $(shell $(PKG_CONFIG) --cflags libcurl )
+
+LDFLAGS += -lcflib -lcurl
+LDFLAGS += -lssl -lcrypto
+LDFLAGS += -lz -lHermes -lgem -lm
+LDFLAGS += -L$(GCCSDK_INSTALL_ENV)/lib
+
+
+# sources purely for the Atari FreeMiNT build
+S_FRONTEND := \
+ about.c \
+ bitmap.c \
+ clipboard.c \
+ ctxmenu.c \
+ cookies.c \
+ certview.c \
+ deskmenu.c \
+ download.c \
+ encoding.c \
+ file.c \
+ findfile.c \
+ filetype.c \
+ font.c \
+ gui.c \
+ hotlist.c \
+ history.c \
+ login.c \
+ misc.c \
+ osspec.c \
+ redrawslots.c \
+ rootwin.c \
+ schedule.c \
+ search.c \
+ statusbar.c \
+ settings.c \
+ toolbar.c \
+ treeview.c \
+ plot/plot.c \
+ plot/fontplot.c \
+ plot/eddi.s \
+ plot/font_freetype.c \
+ plot/font_internal.c \
+ plot/font_vdi.c \
+ gemtk/aestabs.c \
+ gemtk/dragdrop.c \
+ gemtk/guiwin.c \
+ gemtk/msgbox.c \
+ gemtk/utils.c \
+ gemtk/objc.c
+
+
+# This is the final source build list
+# Note this is deliberately *not* expanded here as common and image
+# are not yet available
+SOURCES = $(S_COMMON) $(S_IMAGE) $(S_BROWSER) $(S_FRONTEND)
+EXETARGET := ns$(SUBTARGET)$(PRGSUFFIX)
+
+# ----------------------------------------------------------------------------
+# Install target
+# ----------------------------------------------------------------------------
+
+ATARI_TARGET_DIR := netsurf/
+ATARI_RES_DIR := $(FRONTEND_RESOURCES_DIR)/
+ATARI_DOC_DIR := $(FRONTEND_SOURCE_DIR)/doc/
+ATARI_FONT_NAME := ttf-bitstream-vera-1.10
+ATARI_FONT_SOURCE_URL := http://ftp.gnome.org/pub/GNOME/sources/ttf-bitstream-vera/1.10/$(ATARI_FONT_NAME).tar.gz
+#ATARI_FONT_SOURCE_URL := http://localhost/$(ATARI_FONT_NAME).tar.gz
+ATARI_FONT_TMP_DIR := $(DEPROOT)/../
+ATARI_FONT_SOURCE_DIR := $(ATARI_FONT_TMP_DIR)$(ATARI_FONT_NAME)/
+ATARI_GENERIC_RESOURCES := de en it ja
+ATARI_RESOURCES := $(addprefix \!NetSurf/Resources/,$(ATARI_GENERIC_RESOURCES))
+
+# ----------------------------------------------------------------------------
+# Install target
+# ----------------------------------------------------------------------------
+
+install-atari:
+
+# ----------------------------------------------------------------------------
+# Package target
+# ----------------------------------------------------------------------------
+
+$(ATARI_FONT_TMP_DIR)$(ATARI_FONT_NAME):
+ $(Q)wget $(ATARI_FONT_SOURCE_URL) -O $(ATARI_FONT_TMP_DIR)$(ATARI_FONT_NAME).tar.gz
+ tar xfz $(ATARI_FONT_TMP_DIR)/$(ATARI_FONT_NAME).tar.gz -C $(ATARI_FONT_TMP_DIR)
+
+package-atari: $(ATARI_FONT_TMP_DIR)$(ATARI_FONT_NAME) $(PKGNAME)
+ $(VQ)echo Creating $(PKGNAME)
+
+$(PKGNAME): $(EXETARGET)
+ifneq ($(strip $(STRIP)),)
+ $(Q)echo Stripping symbols from $(EXETARGET) with $(STRIP)
+ $(Q)$(STRIP) $(EXETARGET)
+endif
+ifneq ($(strip $(STACK)),)
+ $(Q)$(STACK) -S 256k $(EXETARGET)
+endif
+ $(Q)rm -rf $(ATARI_TARGET_DIR)
+ $(Q)rm -rf $(PKGNAME)
+ $(Q)mkdir $(ATARI_TARGET_DIR)
+ $(Q)mkdir $(ATARI_TARGET_DIR)doc
+ $(Q)mkdir $(ATARI_TARGET_DIR)downloads
+ $(Q)mkdir $(ATARI_TARGET_DIR)res
+ $(Q)mkdir $(ATARI_TARGET_DIR)res/fonts
+ $(Q)mkdir $(ATARI_TARGET_DIR)res/icons
+ $(Q)mkdir $(ATARI_TARGET_DIR)res/cache
+ $(Q)touch $(ATARI_TARGET_DIR)cookies
+ $(Q)touch $(ATARI_TARGET_DIR)url.db
+
+ $(Q)cp $(ATARI_DOC_DIR)bugs $(ATARI_TARGET_DIR)doc/
+ $(Q)cp $(ATARI_DOC_DIR)faq.txt $(ATARI_TARGET_DIR)doc/
+ $(Q)cp $(ATARI_DOC_DIR)readme.txt $(ATARI_TARGET_DIR)doc/
+ $(Q)cp $(ATARI_DOC_DIR)todo.txt $(ATARI_TARGET_DIR)doc/
+
+# GUI resources:
+ $(Q)cp -r $(ATARI_RESOURCES) $(ATARI_TARGET_DIR)res/
+ $(Q)cp $(ATARI_RES_DIR)/netsurf.rsc $(ATARI_TARGET_DIR)res/netsurf.rsc
+ $(Q)cp $(ATARI_RES_DIR)/languages $(ATARI_TARGET_DIR)res/languages
+ $(Q)cp $(ATARI_RES_DIR)/icons/toolbar -R $(ATARI_TARGET_DIR)res/icons
+ $(Q)cp \!NetSurf/Resources/Icons/* -R $(ATARI_TARGET_DIR)res/icons/
+
+ $(Q)cp \!NetSurf/Resources/netsurf.png,b60 $(ATARI_TARGET_DIR)res/netsurf.png
+ $(Q)cp \!NetSurf/Resources/AdBlock,f79 $(ATARI_TARGET_DIR)res/adblock.css
+ $(Q)cp \!NetSurf/Resources/CSS,f79 $(ATARI_TARGET_DIR)res/default.css
+ $(Q)cp \!NetSurf/Resources/Quirks,f79 $(ATARI_TARGET_DIR)res/quirks.css
+ $(Q)cp \!NetSurf/Resources/internal.css,f79 $(ATARI_TARGET_DIR)res/internal.css
+ $(Q)cp \!NetSurf/Resources/SearchEngines $(ATARI_TARGET_DIR)res/search
+ $(Q)cp \!NetSurf/Resources/ca-bundle $(ATARI_TARGET_DIR)res/cabundle
+ $(Q)cp \!NetSurf/Resources/ca-bundle $(ATARI_TARGET_DIR)res/cabundle
+ $(Q)$(SPLIT_MESSAGES) -l en -p atari -f messages resources/FatMessages > $(ATARI_TARGET_DIR)res/messages
+ $(Q)cp \!NetSurf/Resources/en/welcome.html,faf $(ATARI_TARGET_DIR)res/welcome.html
+ $(Q)cp \!NetSurf/Resources/en/maps.html,faf $(ATARI_TARGET_DIR)res/maps.html
+ $(Q)cp \!NetSurf/Resources/en/licence.html,faf $(ATARI_TARGET_DIR)res/licence.html
+ $(Q)cp \!NetSurf/Resources/en/credits.html,faf $(ATARI_TARGET_DIR)res/credits.html
+
+# copy "Bitstream Vera" font:
+ $(Q)cp $(ATARI_FONT_SOURCE_DIR)RELEASENOTES.TXT $(ATARI_TARGET_DIR)res/fonts/
+ $(Q)cp $(ATARI_FONT_SOURCE_DIR)README.TXT $(ATARI_TARGET_DIR)res/fonts/
+ $(Q)cp $(ATARI_FONT_SOURCE_DIR)COPYRIGHT.TXT $(ATARI_TARGET_DIR)res/fonts/
+
+ $(Q)cp $(ATARI_FONT_SOURCE_DIR)Vera.ttf $(ATARI_TARGET_DIR)res/fonts/ss.ttf
+ $(Q)cp $(ATARI_FONT_SOURCE_DIR)VeraBd.ttf $(ATARI_TARGET_DIR)res/fonts/ssb.ttf
+ $(Q)cp $(ATARI_FONT_SOURCE_DIR)VeraIt.ttf $(ATARI_TARGET_DIR)res/fonts/ssi.ttf
+ $(Q)cp $(ATARI_FONT_SOURCE_DIR)VeraBI.ttf $(ATARI_TARGET_DIR)res/fonts/ssib.ttf
+ $(Q)cp $(ATARI_FONT_SOURCE_DIR)VeraSe.ttf $(ATARI_TARGET_DIR)res/fonts/s.ttf
+ $(Q)cp $(ATARI_FONT_SOURCE_DIR)VeraSeBd.ttf $(ATARI_TARGET_DIR)res/fonts/sb.ttf
+ $(Q)cp $(ATARI_FONT_SOURCE_DIR)VeraMono.ttf $(ATARI_TARGET_DIR)res/fonts/mono.ttf
+ $(Q)cp $(ATARI_FONT_SOURCE_DIR)VeraMoBd.ttf $(ATARI_TARGET_DIR)res/fonts/monob.ttf
+ $(Q)cp $(ATARI_FONT_SOURCE_DIR)VeraMoIt.ttf $(ATARI_TARGET_DIR)res/fonts/cursive.ttf
+ $(Q)cp $(ATARI_FONT_SOURCE_DIR)VeraMoBI.ttf $(ATARI_TARGET_DIR)res/fonts/fantasy.ttf
+
+ $(Q)cp $(EXETARGET) $(ATARI_TARGET_DIR)$(EXETARGET)
+
+# zip files
+ $(Q)zip $(PKGNAME) -9 -r ./$(ATARI_TARGET_DIR)
+
+# delete temporary folders
+ $(Q)rm -rf $(ATARI_TARGET_DIR)
diff --git a/frontends/atari/Makefile.defaults b/frontends/atari/Makefile.defaults
new file mode 100644
index 000000000..b738b48a2
--- /dev/null
+++ b/frontends/atari/Makefile.defaults
@@ -0,0 +1,58 @@
+# ----------------------------------------------------------------------------
+# Atari-specific options
+# ----------------------------------------------------------------------------
+
+# Force using glibc internal iconv implementation instead of external libiconv
+# Valid options: YES, NO
+NETSURF_USE_LIBICONV_PLUG := NO
+
+# Enable NetSurf's use of librosprite for displaying RISC OS Sprites
+# Valid options: YES, NO, AUTO
+NETSURF_USE_ROSPRITE := NO
+
+# Enable NetSurf's use of librsvg in conjunction with Cairo to display SVGs
+# Valid options: YES, NO, AUTO
+NETSURF_USE_RSVG := AUTO
+
+# Enable NetSurf's use of libsvgtiny for displaying SVGs
+# Valid options: YES, NO, AUTO
+NETSURF_USE_NSSVG := AUTO
+
+# Enable Spidermonkey JavaScript engine
+# Valid options: YES, NO
+NETSURF_USE_MOZJS := NO
+
+# Enable building the source object cache filesystem based backing store.
+# implementation.
+# Valid options: YES, NO
+NETSURF_FS_BACKING_STORE := YES
+
+# enable true type fonts via freetype2
+# Valid options: YES, NO
+NETSURF_USE_ATARI_FREETYPE_FONT := YES
+
+# Enable use of netsurf embedded font
+# Valid options: YES, NO
+NETSURF_USE_ATARI_NETSURF_FONT := YES
+
+# Enable VDI Font rendering
+# Valid options: YES, NO
+NETSURF_USE_ATARI_VDI_FONT := NO
+
+# Configure support for screen drivers with no true colour mode
+# Valid options: YES, NO
+NETSURF_USE_ATARI_8BPP_SUPPORT := NO
+
+# Configure the CPU target
+# Valid options: 68000, 68020-60, 5475 (coldfire)
+ATARIARCH = 68020-60
+
+# enable optimizations
+# -O2 is currently broken with m68000 / m68020-60 builds
+CFLAGS += -O3
+
+# override warning flags removing -Wall
+WARNFLAGS = -W -Wundef -Wpointer-arith \
+ -Wcast-align -Wwrite-strings -Wstrict-prototypes \
+ -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls \
+ -Wnested-externs -Wuninitialized -Wl,-t
diff --git a/frontends/atari/about.c b/frontends/atari/about.c
new file mode 100644
index 000000000..105037aab
--- /dev/null
+++ b/frontends/atari/about.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2013 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <limits.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "testament.h"
+#include "utils/useragent.h"
+#include "utils/nsurl.h"
+#include "utils/messages.h"
+#include "desktop/version.h"
+#include "desktop/browser.h"
+
+#include "curl/curlver.h"
+
+#include "cflib.h"
+#include "atari/gui.h"
+#include "atari/misc.h"
+#include "atari/plot/plot.h"
+#include "atari/gemtk/gemtk.h"
+#include "atari/res/netsurf.rsh"
+#include "atari/about.h"
+
+static OBJECT * about_form = NULL;
+static char * infocontent;
+static char version[32];
+VdiHdl vdihandle;
+
+static short __CDECL about_userdraw(PARMBLK *parmblock)
+{
+ short pxy[8];
+ short dummy;
+ short cheight = 8, cwidth = gl_wchar;
+ char c[2] = {0,0};
+
+ /* Setup area to the full area...: */
+ GRECT area = {
+ .g_x = parmblock->pb_x,
+ .g_y = parmblock->pb_y,
+ .g_w = parmblock->pb_w-8,
+ .g_h = parmblock->pb_h
+ };
+
+ /* Setup clip: */
+ GRECT clip = {
+ .g_x = parmblock->pb_xc,
+ .g_y = parmblock->pb_yc,
+ .g_w = parmblock->pb_wc,
+ .g_h = parmblock->pb_hc
+ };
+
+ if(parmblock->pb_currstate == parmblock->pb_prevstate){
+ short cur_x, cur_y;
+ char *content;
+ int content_len;
+
+ content = (char*)parmblock->pb_parm;
+ content_len = strlen(content);
+ cur_x = area.g_x;
+ cur_y = area.g_y;
+
+
+ if(!rc_intersect(&area, &clip)){
+ return (0);
+ }
+
+ pxy[0] = clip.g_x;
+ pxy[1] = clip.g_y;
+ pxy[2] = pxy[0] + clip.g_w-1;
+ pxy[3] = pxy[1] + clip.g_h-1;
+ vs_clip(vdihandle, 1, pxy);
+ vst_alignment(vdihandle, 0, 5, &dummy, &dummy);
+ vst_height(vdihandle, sys_sml_height, &dummy, &dummy, &dummy, &cheight);
+ vswr_mode(vdihandle, 2);
+
+ for (int i=0; i<content_len; i++) {
+ if (content[i] == '\n') {
+ cur_y += cheight;
+ cur_x = area.g_x;
+ } else {
+ if (cur_x >= clip.g_x
+ && cur_x < (clip.g_x + clip.g_w)
+ && cur_y > clip.g_y
+ && cur_y < (clip.g_w + clip.g_h)) {
+ c[0] = content[i];
+ v_gtext(vdihandle, cur_x, cur_y, c);
+ }
+ cur_x += cwidth;
+ }
+ }
+
+ vs_clip(vdihandle, 0, pxy);
+ }
+ return(0);
+}
+
+void atari_about_show(void)
+{
+ static USERBLK userblk;
+ short elem = 0;
+ const char * goto_url = NULL;
+ nserror nserr;
+ nsurl *url;
+
+ vdihandle = plot_get_vdi_handle();
+
+ infocontent = malloc(8000);
+ memset(infocontent, 0, 8000);
+
+ snprintf(infocontent, 8000,
+ "Netsurf : %s\n"
+ "Build ID : %s\n"
+ "Date : %s\n"
+ "MiNTLib : %d.%d-%d%s\n"
+ "GEMLib : %d.%d-%d%s\n"
+ "CFLib : %d.%d-%d%s\n"
+ "cURL : %s\n",
+ user_agent_string(),
+ WT_REVID,
+ WT_COMPILEDATE,
+ __MINTLIB_MAJOR__, __MINTLIB_MINOR__, __MINTLIB_REVISION__,
+ __MINTLIB_BETATAG__,
+ __GEMLIB_MAJOR__, __GEMLIB_MINOR__, __GEMLIB_REVISION__,
+ __GEMLIB_BETATAG__,
+ __CFLIB_MAJOR__, __CFLIB_MINOR__, __CFLIB_REVISION__,
+ __CFLIB_BETATAG__,
+ LIBCURL_VERSION);
+
+ about_form = gemtk_obj_get_tree(ABOUT);
+ snprintf(version, 32, "%s%s", "NetSurf ", (char*)netsurf_version);
+ set_string(about_form, ABOUT_LBL_VERSION, version);
+
+ userblk.ub_code = about_userdraw;
+ userblk.ub_parm = (long) infocontent;
+ about_form[ABOUT_CONTENT].ob_spec.userblk = &userblk;
+
+ elem = simple_dial(about_form, 0);
+ switch (elem) {
+ case ABOUT_CREDITS:
+ goto_url = "about:credits";
+ break;
+
+ case ABOUT_LICENSE:
+ goto_url = "about:licence";
+ break;
+ };
+
+ free(infocontent);
+
+ if (goto_url != NULL) {
+ nserr = nsurl_create(goto_url, &url);
+ if (nserr == NSERROR_OK) {
+ nserr = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (nserr != NSERROR_OK) {
+ atari_warn_user(messages_get_errorcode(nserr), 0);
+ }
+ }
+/*
+ dlg = open_mdial(about_form, 0);
+ do {
+ elem = do_mdial(dlg);
+ printf ("elem: %d\n", elem);
+ switch (elem) {
+ case ABOUT_OK:
+ close_dlg = true;
+ break;
+
+ case ABOUT_CREDITS:
+ close_dlg = true;
+ break;
+
+ case ABOUT_LICENSE:
+ close_dlg = true;
+ break;
+ }
+ } while (close_dlg == false);
+
+ close_mdial(dlg);
+*/
+
+}
diff --git a/frontends/atari/about.h b/frontends/atari/about.h
new file mode 100644
index 000000000..dff6eaeee
--- /dev/null
+++ b/frontends/atari/about.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2013 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_ATARI_ABOUT_H_INCLUDED
+#define NS_ATARI_ABOUT_H_INCLUDED
+
+void atari_about_show(void);
+
+#endif // NS_ATARI_ABOUT_H_INCLUDED
diff --git a/frontends/atari/bitmap.c b/frontends/atari/bitmap.c
new file mode 100644
index 000000000..a159a6659
--- /dev/null
+++ b/frontends/atari/bitmap.c
@@ -0,0 +1,455 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <inttypes.h>
+#include <sys/types.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "utils/nsoption.h"
+#include "utils/log.h"
+#include "image/bitmap.h"
+#include "desktop/mouse.h"
+
+#include "atari/bitmap.h"
+#include "atari/plot/plot.h"
+
+
+/*
+ * param bpp bits per pixel,
+ * param w width of the buffer (in pixel)
+ * param h height of the buffer (in pixel)
+ * param flags MFDB_FLAG_NOALLOC | MFDB_FLAG_ZEROMEM | MFDB_FLAG_STAND
+ * returns size of the fd_addr buffer required or allocated
+*/
+int init_mfdb(int bpp, int w, int h, uint32_t flags, MFDB * out )
+{
+ int dststride;
+ dststride = MFDB_STRIDE( w );
+ int size = MFDB_SIZE( bpp, dststride, h );
+ if( bpp > 0 ) {
+ if( (flags & MFDB_FLAG_NOALLOC) == 0 ) {
+ out->fd_addr = malloc( size );
+ if( out->fd_addr == NULL ){
+ return( 0 );
+ }
+ if( (flags & MFDB_FLAG_ZEROMEM) ){
+ memset( out->fd_addr, 0, size );
+ }
+ }
+ out->fd_stand = (flags & MFDB_FLAG_STAND) ? 1 : 0;
+ out->fd_nplanes = (short)bpp;
+ out->fd_r1 = out->fd_r2 = out->fd_r3 = 0;
+ } else {
+ memset( out, 0, sizeof(MFDB) );
+ }
+ out->fd_w = dststride;
+ out->fd_h = h;
+ out->fd_wdwidth = dststride >> 4;
+ return( size );
+}
+
+
+/**
+ * Create a bitmap.
+ *
+ * \param w width of image in pixels
+ * \param h height of image in pixels
+ * \param bpp number of BYTES per pixel
+ * \param rowstride linewidth in bytes
+ * \param state a flag word indicating the initial state
+ * \param pixdata NULL or an memory address to use as the bitmap pixdata
+ * \return an opaque struct bitmap, or NULL on memory exhaustion
+ */
+static void *atari_bitmap_create_ex( int w, int h, short bpp, int rowstride, unsigned int state, void * pixdata )
+{
+ struct bitmap * bitmap;
+
+ LOG("width %d (rowstride: %d, bpp: %d), height %d, state %u", w, rowstride, bpp, h, state);
+
+ if( rowstride == 0) {
+ rowstride = bpp * w;
+ }
+
+ assert( rowstride >= (w * bpp) );
+ bitmap = calloc(1 , sizeof(struct bitmap) );
+ if (bitmap) {
+ if( pixdata == NULL) {
+ bitmap->pixdata = calloc(1, (rowstride * h)+128);
+ }
+ else {
+ bitmap->pixdata = pixdata;
+ }
+
+ if (bitmap->pixdata != NULL) {
+ bitmap->width = w;
+ bitmap->height = h;
+ bitmap->opaque = (state & BITMAP_OPAQUE) ? true : false;
+ bitmap->bpp = bpp;
+ bitmap->resized = NULL;
+ bitmap->rowstride = rowstride;
+ } else {
+ free(bitmap);
+ bitmap=NULL;
+ LOG("Out of memory!");
+ }
+ }
+ LOG("bitmap %p", bitmap);
+ return bitmap;
+}
+
+
+/* exported interface documented in atari/bitmap.h */
+void *atari_bitmap_create(int w, int h, unsigned int state)
+{
+ return atari_bitmap_create_ex( w, h, NS_BMP_DEFAULT_BPP, w * NS_BMP_DEFAULT_BPP, state, NULL );
+}
+
+/**
+ * The bitmap image has changed, so flush any persistant cache.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ */
+static void bitmap_modified(void *bitmap)
+{
+ struct bitmap *bm = bitmap;
+ if( bm->resized != NULL ) {
+ atari_bitmap_destroy( bm->resized );
+ bm->resized = NULL;
+ }
+ if( bm->converted ){
+ if( bm->pixdata != bm->native.fd_addr ){
+ free( bm->native.fd_addr );
+ }
+ bm->native.fd_addr = NULL;
+ bm->converted = false;
+ }
+}
+
+
+/* exported interface documented in atari/bitmap.h */
+void *atari_bitmap_realloc( int w, int h, short bpp, int rowstride, unsigned int state, void * bmp )
+{
+ struct bitmap * bitmap = bmp;
+ int newsize = rowstride * h;
+
+ if( bitmap == NULL ) {
+ return( NULL );
+ }
+
+ assert( bitmap->pixdata != NULL );
+ int oldsize = bitmap->rowstride * bitmap->height;
+ bool doalloc = (state & BITMAP_GROW) ? (newsize > oldsize) : (newsize != oldsize);
+ if( newsize > oldsize )
+ assert( doalloc == true );
+ if( doalloc ) {
+ // TODO: set red band to a specific value and check the red band
+ // on bitmap_destroy()
+ bitmap->pixdata = realloc( bitmap->pixdata, newsize + 128 );
+ if( bitmap->pixdata == NULL )
+ return( NULL );
+ }
+
+ if( state & BITMAP_CLEAR ){
+ memset( bitmap->pixdata, 0x00, newsize + 128 );
+ }
+
+ bitmap->width = w;
+ bitmap->height = h;
+ bitmap->bpp = bpp;
+ bitmap->resized = NULL;
+ bitmap->rowstride = rowstride;
+ bitmap_modified( bitmap );
+ return( bitmap );
+}
+
+
+/**
+ * Return a pointer to the pixel data in a bitmap.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ * \return pointer to the pixel buffer
+ *
+ * The pixel data is packed as BITMAP_FORMAT, possibly with padding at the end
+ * of rows. The width of a row in bytes is given by bitmap_get_rowstride().
+ */
+static unsigned char *bitmap_get_buffer(void *bitmap)
+{
+ struct bitmap *bm = bitmap;
+
+ if (bitmap == NULL) {
+ LOG("NULL bitmap!");
+ return NULL;
+ }
+
+ return bm->pixdata;
+}
+
+
+/* exported interface documented in atari/bitmap.h */
+size_t atari_bitmap_buffer_size(void *bitmap)
+{
+ struct bitmap * bm = bitmap;
+ if( bm == NULL )
+ return 0;
+ return( bm->rowstride * bm->height );
+}
+
+
+/* exported interface documented in atari/bitmap.h */
+size_t atari_bitmap_get_rowstride(void *bitmap)
+{
+ struct bitmap *bm = bitmap;
+
+ if (bitmap == NULL) {
+ LOG("NULL bitmap!");
+ return 0;
+ }
+ return bm->rowstride;
+}
+
+
+/* exported interface documented in atari/bitmap.h */
+void atari_bitmap_destroy(void *bitmap)
+{
+ struct bitmap *bm = bitmap;
+
+ if (bitmap == NULL) {
+ LOG("NULL bitmap!");
+ return;
+ }
+
+ if( bm->resized != NULL ) {
+ atari_bitmap_destroy(bm->resized);
+ }
+ if( bm->converted && ( bm->native.fd_addr != bm->pixdata ) ) {
+ free( bm->native.fd_addr );
+ }
+ free(bm->pixdata);
+ free(bm);
+}
+
+
+/**
+ * Save a bitmap in the platform's native format.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ * \param path pathname for file
+ * \param flags flags controlling how the bitmap is saved.
+ * \return true on success, false on error and error reported
+ */
+
+static bool bitmap_save(void *bitmap, const char *path, unsigned flags)
+{
+ return true;
+}
+
+
+/**
+ * Sets whether a bitmap should be plotted opaque
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ * \param opaque whether the bitmap should be plotted opaque
+ */
+static void bitmap_set_opaque(void *bitmap, bool opaque)
+{
+ struct bitmap *bm = bitmap;
+
+ if (bitmap == NULL) {
+ LOG("NULL bitmap!");
+ return;
+ }
+
+ LOG("setting bitmap %p to %s", bm, opaque ? "opaque" : "transparent");
+ bm->opaque = opaque;
+}
+
+
+/**
+ * Tests whether a bitmap has an opaque alpha channel
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ * \return whether the bitmap is opaque
+ */
+static bool bitmap_test_opaque(void *bitmap)
+{
+ int tst;
+ struct bitmap *bm = bitmap;
+
+ if (bitmap == NULL) {
+ LOG("NULL bitmap!");
+ return false;
+ }
+
+ if( nsoption_int(atari_transparency) == 0 ){
+ return( true );
+ }
+
+ tst = bm->width * bm->height;
+
+ while (tst-- > 0) {
+ if (bm->pixdata[(tst << 2) + 3] != 0xff) {
+ LOG("bitmap %p has transparency", bm);
+ return false;
+ }
+ }
+ LOG("bitmap %p is opaque", bm);
+ return true;
+}
+
+
+/* exported interface documented in atari/bitmap.h */
+bool atari_bitmap_get_opaque(void *bitmap)
+{
+ struct bitmap *bm = bitmap;
+
+ if (bitmap == NULL) {
+ LOG("NULL bitmap!");
+ return false;
+ }
+
+ return bm->opaque;
+}
+
+
+/* exported interface documented in atari/bitmap.h */
+int atari_bitmap_get_width(void *bitmap)
+{
+ struct bitmap *bm = bitmap;
+
+ if (bitmap == NULL) {
+ LOG("NULL bitmap!");
+ return 0;
+ }
+
+ return(bm->width);
+}
+
+
+/* exported interface documented in atari/bitmap.h */
+int atari_bitmap_get_height(void *bitmap)
+{
+ struct bitmap *bm = bitmap;
+
+ if (bitmap == NULL) {
+ LOG("NULL bitmap!");
+ return 0;
+ }
+ return(bm->height);
+}
+
+
+/**
+ * Gets the number of BYTES per pixel.
+ */
+static size_t bitmap_get_bpp(void *bitmap)
+{
+ struct bitmap *bm = bitmap;
+ return bm->bpp;
+}
+
+/* exported interface documented in atari/bitmap.h */
+bool atari_bitmap_resize(struct bitmap *img, HermesHandle hermes_h,
+ HermesFormat *fmt, int nw, int nh)
+{
+ unsigned int state = 0;
+ short bpp = bitmap_get_bpp( img );
+ int stride = atari_bitmap_get_rowstride( img );
+ int err;
+
+ if( img->resized != NULL ) {
+ if( img->resized->width != nw || img->resized->height != nh ) {
+ atari_bitmap_destroy( img->resized );
+ img->resized = NULL;
+ } else {
+ /* the bitmap is already resized */
+ return(true);
+ }
+ }
+
+ /* allocate the mem for resized bitmap */
+ if (img->opaque == true) {
+ state |= BITMAP_OPAQUE;
+ }
+ img->resized = atari_bitmap_create_ex( nw, nh, bpp, nw*bpp, state, NULL );
+ if( img->resized == NULL ) {
+ printf("W: %d, H: %d, bpp: %d\n", nw, nh, bpp);
+ assert(img->resized);
+ return(false);
+ }
+
+ /* allocate an converter, only for resizing */
+ err = Hermes_ConverterRequest( hermes_h,
+ fmt,
+ fmt
+ );
+ if( err == 0 ) {
+ return(false);
+ }
+
+ err = Hermes_ConverterCopy( hermes_h,
+ img->pixdata,
+ 0, /* x src coord of top left in pixel coords */
+ 0, /* y src coord of top left in pixel coords */
+ atari_bitmap_get_width( img ),
+ atari_bitmap_get_height( img ),
+ stride, /* stride as bytes */
+ img->resized->pixdata,
+ 0, /* x dst coord of top left in pixel coords */
+ 0, /* y dst coord of top left in pixel coords */
+ nw, nh,
+ atari_bitmap_get_rowstride(img->resized) /* stride as bytes */
+ );
+ if( err == 0 ) {
+ atari_bitmap_destroy( img->resized );
+ img->resized = NULL;
+ return(false);
+ }
+
+ return(true);
+}
+
+static nserror bitmap_render(struct bitmap *bitmap, struct hlcache_handle *content)
+{
+ return NSERROR_NOT_IMPLEMENTED;
+}
+
+static struct gui_bitmap_table bitmap_table = {
+ .create = atari_bitmap_create,
+ .destroy = atari_bitmap_destroy,
+ .set_opaque = bitmap_set_opaque,
+ .get_opaque = atari_bitmap_get_opaque,
+ .test_opaque = bitmap_test_opaque,
+ .get_buffer = bitmap_get_buffer,
+ .get_rowstride = atari_bitmap_get_rowstride,
+ .get_width = atari_bitmap_get_width,
+ .get_height = atari_bitmap_get_height,
+ .get_bpp = bitmap_get_bpp,
+ .save = bitmap_save,
+ .modified = bitmap_modified,
+ .render = bitmap_render,
+};
+
+struct gui_bitmap_table *atari_bitmap_table = &bitmap_table;
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */
diff --git a/frontends/atari/bitmap.h b/frontends/atari/bitmap.h
new file mode 100644
index 000000000..b0fa18069
--- /dev/null
+++ b/frontends/atari/bitmap.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * Atari bitmap handling implementation.
+ */
+
+#ifndef NS_ATARI_BITMAP_H
+#define NS_ATARI_BITMAP_H
+
+#include <gem.h>
+#include <Hermes/Hermes.h>
+
+#define NS_BMP_DEFAULT_BPP 4
+
+/* Flags for init_mfdb function: */
+#define MFDB_FLAG_STAND 0x01
+#define MFDB_FLAG_ZEROMEM 0x02
+#define MFDB_FLAG_NOALLOC 0x04
+
+#define BITMAP_SHRINK 0
+#define BITMAP_GROW 1024 /* Don't realloc when bitmap size shrinks */
+#define BITMAP_CLEAR 2048 /* Zero bitmap memory */
+
+
+/**
+ * Calculates MFDB compatible rowstride (in number of bits)
+ */
+#define MFDB_STRIDE( w ) (((w & 15) != 0) ? (w | 15)+1 : w)
+
+
+/**
+ * Calculate size of an mfdb,
+ *
+ * \param bpp Bits per pixel.
+ * \param stride Word aligned rowstride (width) as returned by MFDB_STRIDE,
+ * \param h Height in pixels.
+*/
+#define MFDB_SIZE( bpp, stride, h ) ( ((stride >> 3) * h) * bpp )
+
+struct gui_bitmap_table *atari_bitmap_table;
+
+struct bitmap {
+ int width;
+ int height;
+ uint8_t *pixdata;
+ bool opaque;
+ short bpp; /* number of BYTES! per pixel */
+ size_t rowstride;
+ struct bitmap * resized;
+ MFDB native;
+ bool converted;
+};
+
+
+
+/**
+ * setup an MFDB struct and allocate memory for it when it is needed.
+ *
+ * If bpp == 0, this function assumes that the MFDB shall point to the
+ * screen and will not allocate any memory (mfdb.fd_addr == 0).
+ *
+ * \return 0 when the memory allocation fails (out of memory),
+ * otherwise it returns the size of the mfdb.fd_addr as number
+ * of bytes.
+ */
+int init_mfdb(int bpp, int w, int h, uint32_t flags, MFDB * out );
+
+/**
+ * Create a bitmap.
+ *
+ * \param w width of image in pixels
+ * \param h width of image in pixels
+ * \param state a flag word indicating the initial state
+ * \return an opaque struct bitmap, or NULL on memory exhaustion
+ */
+void *atari_bitmap_create(int w, int h, unsigned int state);
+
+/**
+ * Find the width of a pixel row in bytes.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ * \return width of a pixel row in the bitmap
+ */
+size_t atari_bitmap_get_rowstride(void *bitmap);
+
+/**
+ * Free a bitmap.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ */
+void atari_bitmap_destroy(void *bitmap);
+
+/**
+ * Get bitmap width
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ */
+int atari_bitmap_get_width(void *bitmap);
+
+/**
+ * Get bitmap height
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ */
+int atari_bitmap_get_height(void *bitmap);
+
+/**
+ * Gets whether a bitmap should be plotted opaque
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ */
+bool atari_bitmap_get_opaque(void *bitmap);
+
+size_t atari_bitmap_buffer_size(void *bitmap);
+
+bool atari_bitmap_resize(struct bitmap *img, HermesHandle hermes_h, HermesFormat *fmt, int nw, int nh);
+
+void *atari_bitmap_realloc( int w, int h, short bpp, int rowstride, unsigned int state, void * bmp );
+
+#endif
diff --git a/frontends/atari/certview.c b/frontends/atari/certview.c
new file mode 100644
index 000000000..7f0a29528
--- /dev/null
+++ b/frontends/atari/certview.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2013 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <ctype.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "utils/nsoption.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "content/urldb.h"
+#include "content/hlcache.h"
+#include "desktop/sslcert_viewer.h"
+#include "desktop/core_window.h"
+
+#include "atari/gui.h"
+#include "atari/misc.h"
+#include "atari/treeview.h"
+#include "atari/certview.h"
+#include "atari/findfile.h"
+#include "atari/gemtk/gemtk.h"
+#include "atari/res/netsurf.rsh"
+
+extern GRECT desk_area;
+
+
+/* Setup Atari Treeview Callbacks: */
+static nserror atari_sslcert_viewer_init_phase2(struct core_window *cw,
+ struct core_window_callback_table * default_callbacks);
+static void atari_sslcert_viewer_finish(struct core_window *cw);
+static void atari_sslcert_viewer_keypress(struct core_window *cw,
+ uint32_t ucs4);
+static void atari_sslcert_viewer_mouse_action(struct core_window *cw,
+ browser_mouse_state mouse,
+ int x, int y);
+static void atari_sslcert_viewer_draw(struct core_window *cw, int x,
+ int y, struct rect *clip,
+ const struct redraw_context *ctx);
+static short handle_event(GUIWIN *win, EVMULT_OUT *ev_out, short msg[8]);
+
+static struct atari_treeview_callbacks atari_sslcert_viewer_treeview_callbacks = {
+ .init_phase2 = atari_sslcert_viewer_init_phase2,
+ .finish = atari_sslcert_viewer_finish,
+ .draw = atari_sslcert_viewer_draw,
+ .keypress = atari_sslcert_viewer_keypress,
+ .mouse_action = atari_sslcert_viewer_mouse_action,
+ .gemtk_user_func = handle_event
+};
+
+/* static functions */
+static void atari_sslcert_viewer_destroy(struct atari_sslcert_viewer_s * cvwin);
+
+
+static nserror atari_sslcert_viewer_init_phase2(struct core_window *cw,
+ struct core_window_callback_table *cb_t)
+{
+ struct atari_sslcert_viewer_s *cvwin;
+ struct sslcert_session_data *ssl_d;
+
+ cvwin = (struct atari_sslcert_viewer_s *)atari_treeview_get_user_data(cw);
+
+ assert(cvwin);
+
+ ssl_d = cvwin->ssl_session_data;
+
+ assert(ssl_d);
+
+ LOG("cw %p", cw);
+
+ return(sslcert_viewer_init(cb_t, cw, ssl_d));
+}
+
+static void atari_sslcert_viewer_finish(struct core_window *cw)
+{
+ struct atari_sslcert_viewer_s *cvwin;
+
+ assert(cw);
+
+ cvwin = (struct atari_sslcert_viewer_s *)atari_treeview_get_user_data(cw);
+
+ /* This will also free the session data: */
+ sslcert_viewer_fini(cvwin->ssl_session_data);
+
+ LOG("cw %p", cw);
+}
+
+static void atari_sslcert_viewer_draw(struct core_window *cw, int x,
+ int y, struct rect *clip,
+ const struct redraw_context *ctx)
+{
+ struct atari_sslcert_viewer_s *cvwin;
+
+ assert(cw);
+
+ cvwin = (struct atari_sslcert_viewer_s *)atari_treeview_get_user_data(cw);
+
+ assert(cvwin);
+
+ sslcert_viewer_redraw(cvwin->ssl_session_data, x, y, clip, ctx);
+}
+
+static void atari_sslcert_viewer_keypress(struct core_window *cw, uint32_t ucs4)
+{
+ struct atari_sslcert_viewer_s *cvwin;
+
+ assert(cw);
+
+ cvwin = (struct atari_sslcert_viewer_s *)atari_treeview_get_user_data(cw);
+
+ LOG("ucs4: %"PRIu32, ucs4);
+ sslcert_viewer_keypress(cvwin->ssl_session_data, ucs4);
+}
+
+static void atari_sslcert_viewer_mouse_action(struct core_window *cw,
+ browser_mouse_state mouse,
+ int x, int y)
+{
+ struct atari_sslcert_viewer_s *cvwin;
+
+ assert(cw);
+
+ cvwin = (struct atari_sslcert_viewer_s *)atari_treeview_get_user_data(cw);
+
+ sslcert_viewer_mouse_action(cvwin->ssl_session_data, mouse, x, y);
+}
+
+
+static short handle_event(GUIWIN *win, EVMULT_OUT *ev_out, short msg[8])
+{
+ struct core_window *tv=NULL;
+ GRECT tb_area;
+ GUIWIN * gemtk_win;
+ struct atari_sslcert_viewer_s *cvwin = NULL;
+ short retval = 0;
+ OBJECT *toolbar;
+
+ LOG("win %p", win);
+
+ if(ev_out->emo_events & MU_MESAG){
+ switch (msg[0]) {
+
+ case WM_TOOLBAR:
+ toolbar = gemtk_obj_get_tree(TOOLBAR_SSL_CERT);
+ LOG("CERTVIEWER WM_TOOLBAR");
+ tv = (struct core_window*) gemtk_wm_get_user_data(win);
+ assert(tv);
+ cvwin = (struct atari_sslcert_viewer_s *)
+ atari_treeview_get_user_data(tv);
+ switch (msg[4]) {
+
+ case TOOLBAR_SSL_CERT_TRUSTED:
+
+ if (toolbar[msg[4]].ob_state & OS_SELECTED) {
+
+ } else {
+
+ }
+ break;
+ }
+
+
+ gemtk_win = atari_treeview_get_gemtk_window(tv);
+ assert(gemtk_win);
+ //gemtk_obj_get_tree(TOOLBAR_HOTLIST)[msg[4]].ob_state &= ~OS_SELECTED;
+ atari_treeview_get_grect(tv, TREEVIEW_AREA_TOOLBAR, &tb_area);
+ evnt_timer(150);
+ gemtk_wm_exec_redraw(gemtk_win, &tb_area);
+ retval = 1;
+ break;
+
+ case WM_CLOSED:
+ // TODO set perrmissions
+ toolbar = gemtk_obj_get_tree(TOOLBAR_SSL_CERT);
+ tv = (struct core_window*) gemtk_wm_get_user_data(win);
+ assert(tv);
+ cvwin = (struct atari_sslcert_viewer_s *)
+ atari_treeview_get_user_data(tv);
+ if (toolbar[TOOLBAR_SSL_CERT_TRUSTED].ob_state & OS_SELECTED) {
+ sslcert_viewer_accept(cvwin->ssl_session_data);
+ } else {
+ sslcert_viewer_reject(cvwin->ssl_session_data);
+ }
+ atari_sslcert_viewer_destroy(cvwin);
+ retval = 1;
+ break;
+
+ default: break;
+ }
+ }
+
+ return(retval);
+}
+
+static void atari_sslcert_viewer_init(struct atari_sslcert_viewer_s * cvwin,
+ struct sslcert_session_data *ssl_d)
+{
+ assert(cvwin->init == false);
+ assert(cvwin->window == NULL);
+ assert(cvwin->tv == NULL);
+
+ int flags = ATARI_TREEVIEW_WIDGETS;
+ short handle = -1;
+ OBJECT * tree = gemtk_obj_get_tree(TOOLBAR_SSL_CERT);
+ assert( tree );
+
+ handle = wind_create(flags, 0, 0, desk_area.g_w, desk_area.g_h);
+ cvwin->window = gemtk_wm_add(handle,
+ GEMTK_WM_FLAG_DEFAULTS, NULL);
+ if (cvwin->window == NULL ) {
+ gemtk_msg_box_show(GEMTK_MSG_BOX_ALERT,
+ "Failed to allocate Treeview:\nCertviewer");
+ return;
+ }
+ wind_set_str(handle, WF_NAME, (char*)"SSL Certificate");
+ gemtk_wm_set_toolbar(cvwin->window, tree, 0, 0);
+ gemtk_wm_unlink(cvwin->window);
+
+ cvwin->ssl_session_data = ssl_d;
+ cvwin->tv = atari_treeview_create(cvwin->window,
+ &atari_sslcert_viewer_treeview_callbacks,
+ cvwin, flags);
+
+ if (cvwin->tv == NULL) {
+ /* handle it properly, clean up previous allocs */
+ LOG("Failed to allocate treeview");
+ return;
+ }
+
+ cvwin->init = true;
+}
+
+/*
+ * documented in certview.h
+ */
+void atari_sslcert_viewer_open(struct sslcert_session_data *ssl_d)
+{
+ struct atari_sslcert_viewer_s * cvwin;
+
+ cvwin = calloc(1, sizeof(struct atari_sslcert_viewer_s));
+
+ assert(cvwin);
+
+ atari_sslcert_viewer_init(cvwin, ssl_d);
+
+ if (atari_treeview_is_open(cvwin->tv) == false) {
+
+ GRECT pos;
+ pos.g_x = desk_area.g_w - desk_area.g_w / 4;
+ pos.g_y = desk_area.g_y;
+ pos.g_w = desk_area.g_w / 4;
+ pos.g_h = desk_area.g_h;
+
+ atari_treeview_open(cvwin->tv, &pos);
+ } else {
+ wind_set(gemtk_wm_get_handle(cvwin->window), WF_TOP, 1, 0,
+ 0, 0);
+ }
+}
+
+
+static void atari_sslcert_viewer_destroy(struct atari_sslcert_viewer_s * cvwin)
+{
+ assert(cvwin);
+ assert(cvwin->init);
+ assert(cvwin->window);
+
+ LOG("cvwin %p", cvwin);
+
+ if (atari_treeview_is_open(cvwin->tv))
+ atari_treeview_close(cvwin->tv);
+ wind_delete(gemtk_wm_get_handle(cvwin->window));
+ gemtk_wm_remove(cvwin->window);
+ cvwin->window = NULL;
+ atari_treeview_delete(cvwin->tv);
+ free(cvwin);
+ LOG("done");
+}
diff --git a/frontends/atari/certview.h b/frontends/atari/certview.h
new file mode 100644
index 000000000..ff15d8076
--- /dev/null
+++ b/frontends/atari/certview.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2013 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 CERTVIEW_H_INCLUDED
+#define CERTVIEW_H_INCLUDED
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <string.h>
+
+
+#include "assert.h"
+#include "utils/nsoption.h"
+#include "utils/log.h"
+#include "desktop/sslcert_viewer.h"
+
+struct core_window;
+
+struct atari_sslcert_viewer_s {
+ GUIWIN * window;
+ //struct atari_treeview_window *tv;/*< The hotlist treeview handle. */
+ struct core_window *tv;
+ struct sslcert_session_data *ssl_session_data;
+ bool init;
+};
+
+/**
+ * Initializes and opens an certificate inspector window
+ * \param ssl_d ssl session data created by sslcert_viewer_create_session_data
+ *
+ * The window takes ownership of the session data and free's the memory on exit.
+ */
+void atari_sslcert_viewer_open(struct sslcert_session_data *ssl_d);
+
+
+#endif // CERTVIEW_H_INCLUDED
diff --git a/frontends/atari/clipboard.c b/frontends/atari/clipboard.c
new file mode 100644
index 000000000..3464e4bca
--- /dev/null
+++ b/frontends/atari/clipboard.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2013 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ *
+ * Module Description:
+ *
+ *
+ *
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <mint/osbind.h>
+#include <cflib.h>
+#include "atari/clipboard.h"
+#include "atari/gemtk/gemtk.h"
+
+
+static int filesize(char * path)
+{
+ FILE *f;
+ int fs;
+
+ f = fopen( path, "r+b");
+ if(!f)
+ return(-1);
+
+ fseek(f, 0L, SEEK_END);
+ fs = ftell(f);
+ fclose(f);
+
+ return(fs);
+}
+
+int scrap_txt_write(char *str)
+{
+ scrap_wtxt(str);
+
+
+ // Send SC_CHANGED message:
+ gemtk_send_msg(SC_CHANGED, 0, 2, 0, 0, 0, 0);
+
+ return(0);
+
+}
+
+char *scrap_txt_read(void)
+{
+ char * buf = NULL;
+ char path[80];
+
+ if (get_scrapdir (path))
+ {
+ int len;
+ strcat (path, "scrap.txt");
+ len = filesize(path);
+ if(len > 0){
+ int file;
+ if ((file = (int) Fopen (path, 0)) >= 0)
+ {
+ buf = malloc(len);
+ if(buf){
+ len = Fread (file, len, buf);
+ Fclose (file);
+ buf[len] = '\0';
+ return buf;
+ }
+ }
+ }
+ }
+ return buf;
+}
+
diff --git a/frontends/atari/clipboard.h b/frontends/atari/clipboard.h
new file mode 100644
index 000000000..aeecc9a5f
--- /dev/null
+++ b/frontends/atari/clipboard.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_ATARI_CLIPBOARD_H
+#define NS_ATARI_CLIPBOARD_H
+
+int scrap_txt_write(char *str);
+char *scrap_txt_read(void);
+
+#endif
diff --git a/frontends/atari/cookies.c b/frontends/atari/cookies.c
new file mode 100644
index 000000000..6019b4730
--- /dev/null
+++ b/frontends/atari/cookies.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2013 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <assert.h>
+
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "desktop/mouse.h"
+#include "desktop/plotters.h"
+#include "desktop/cookie_manager.h"
+#include "desktop/core_window.h"
+
+#include "atari/treeview.h"
+#include "atari/cookies.h"
+#include "atari/gemtk/gemtk.h"
+#include "atari/res/netsurf.rsh"
+
+extern GRECT desk_area;
+
+struct atari_cookie_manager_s atari_cookie_manager;
+
+
+/* Setup Atari Treeview Callbacks: */
+static nserror atari_cookie_manager_init_phase2(struct core_window *cw,
+ struct core_window_callback_table * default_callbacks);
+static void atari_cookie_manager_finish(struct core_window *cw);
+static void atari_cookie_manager_keypress(struct core_window *cw,
+ uint32_t ucs4);
+static void atari_cookie_manager_mouse_action(struct core_window *cw,
+ browser_mouse_state mouse,
+ int x, int y);
+static void atari_cookie_manager_draw(struct core_window *cw, int x,
+ int y, struct rect *clip,
+ const struct redraw_context *ctx);
+static short handle_event(GUIWIN *win, EVMULT_OUT *ev_out, short msg[8]);
+
+static struct atari_treeview_callbacks atari_cookie_manager_treeview_callbacks = {
+ .init_phase2 = atari_cookie_manager_init_phase2,
+ .finish = atari_cookie_manager_finish,
+ .draw = atari_cookie_manager_draw,
+ .keypress = atari_cookie_manager_keypress,
+ .mouse_action = atari_cookie_manager_mouse_action,
+ .gemtk_user_func = handle_event
+};
+
+
+static nserror
+atari_cookie_manager_init_phase2(struct core_window *cw,
+ struct core_window_callback_table *cb_t)
+{
+ LOG("cw %p",cw);
+ return(cookie_manager_init(cb_t, cw));
+}
+
+
+static void
+atari_cookie_manager_finish(struct core_window *cw)
+{
+ LOG("cw %p",cw);
+ cookie_manager_fini();
+}
+
+
+static void
+atari_cookie_manager_draw(struct core_window *cw,
+ int x, int y,
+ struct rect *clip,
+ const struct redraw_context *ctx)
+{
+ cookie_manager_redraw(x, y, clip, ctx);
+}
+
+
+static void
+atari_cookie_manager_keypress(struct core_window *cw, uint32_t ucs4)
+{
+ LOG("ucs4: %"PRIu32, ucs4);
+ cookie_manager_keypress(ucs4);
+}
+
+
+static void
+atari_cookie_manager_mouse_action(struct core_window *cw,
+ browser_mouse_state mouse,
+ int x, int y)
+{
+ cookie_manager_mouse_action(mouse, x, y);
+}
+
+
+
+static short handle_event(GUIWIN *win, EVMULT_OUT *ev_out, short msg[8])
+{
+ short retval = 0;
+
+ LOG("win %p", win);
+
+ if (ev_out->emo_events & MU_MESAG) {
+ switch (msg[0]) {
+
+ case WM_TOOLBAR:
+ LOG("WM_TOOLBAR");
+ break;
+
+ case WM_CLOSED:
+ atari_cookie_manager_close();
+ retval = 1;
+ break;
+
+ default: break;
+ }
+ }
+
+ return(retval);
+}
+
+void atari_cookie_manager_init(void)
+{
+ if (atari_cookie_manager.init == false) {
+
+ if (atari_cookie_manager.window == NULL) {
+ int flags = ATARI_TREEVIEW_WIDGETS;
+ short handle = -1;
+ OBJECT * tree = gemtk_obj_get_tree(TOOLBAR_COOKIES);
+ assert( tree );
+
+ handle = wind_create(flags, 0, 0, desk_area.g_w, desk_area.g_h);
+ atari_cookie_manager.window = gemtk_wm_add(handle,
+ GEMTK_WM_FLAG_DEFAULTS, NULL);
+ if( atari_cookie_manager.window == NULL ) {
+ gemtk_msg_box_show(GEMTK_MSG_BOX_ALERT,
+ "Failed to allocate Treeview:\nCookies");
+ return;
+ }
+ wind_set_str(handle, WF_NAME, (char*)messages_get("Cookies"));
+ gemtk_wm_set_toolbar(atari_cookie_manager.window, tree, 0, 0);
+ gemtk_wm_unlink(atari_cookie_manager.window);
+
+ atari_cookie_manager.tv = atari_treeview_create(
+ atari_cookie_manager.window,
+ &atari_cookie_manager_treeview_callbacks,
+ NULL, flags);
+
+ if (atari_cookie_manager.tv == NULL) {
+ /* handle it properly, clean up previous allocs */
+ LOG("Failed to allocate treeview");
+ return;
+ }
+
+ } else {
+
+ }
+ }
+ atari_cookie_manager.init = true;
+}
+
+void atari_cookie_manager_open(void)
+{
+ assert(atari_cookie_manager.init);
+
+ if (atari_treeview_is_open(atari_cookie_manager.tv) == false) {
+
+ GRECT pos;
+ pos.g_x = desk_area.g_w - desk_area.g_w / 4;
+ pos.g_y = desk_area.g_y;
+ pos.g_w = desk_area.g_w / 4;
+ pos.g_h = desk_area.g_h;
+
+ atari_treeview_open(atari_cookie_manager.tv, &pos);
+ } else {
+ wind_set(gemtk_wm_get_handle(atari_cookie_manager.window), WF_TOP, 1, 0,
+ 0, 0);
+ }
+}
+
+
+void atari_cookie_manager_close(void)
+{
+ atari_treeview_close(atari_cookie_manager.tv);
+}
+
+
+void atari_cookie_manager_destroy(void)
+{
+ if( atari_cookie_manager.init == false) {
+ return;
+ }
+ if( atari_cookie_manager.window != NULL ) {
+ if (atari_treeview_is_open(atari_cookie_manager.tv))
+ atari_cookie_manager_close();
+ wind_delete(gemtk_wm_get_handle(atari_cookie_manager.window));
+ gemtk_wm_remove(atari_cookie_manager.window);
+ atari_cookie_manager.window = NULL;
+ atari_treeview_delete(atari_cookie_manager.tv);
+ atari_cookie_manager.init = false;
+ }
+ LOG("done");
+
+}
+
+
+void atari_cookie_manager_redraw(void)
+{
+ atari_treeview_redraw(atari_cookie_manager.tv);
+}
diff --git a/frontends/atari/cookies.h b/frontends/atari/cookies.h
new file mode 100644
index 000000000..3649c8c60
--- /dev/null
+++ b/frontends/atari/cookies.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2013 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_ATARI_COOKIE_MANAGER_H
+ #define NS_ATARI_COOKIE_MANAGER_H
+
+struct core_window;
+
+struct atari_cookie_manager_s {
+ GUIWIN * window;
+ //struct atari_treeview_window * tv;/*< The hotlist treeview handle. */
+ struct core_window *tv;
+ bool init;
+};
+
+extern struct atari_cookie_manager_s atari_cookie_manager;
+
+void atari_cookie_manager_init(void);
+void atari_cookie_manager_open(void);
+void atari_cookie_manager_close(void);
+void atari_cookie_manager_destroy(void);
+void atari_cookie_manager_redraw(void);
+
+#endif
diff --git a/frontends/atari/ctxmenu.c b/frontends/atari/ctxmenu.c
new file mode 100644
index 000000000..0343b20c7
--- /dev/null
+++ b/frontends/atari/ctxmenu.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "utils/log.h"
+#include "utils/messages.h"
+#include "utils/nsoption.h"
+#include "desktop/browser.h"
+#include "desktop/textinput.h"
+#include "content/hlcache.h"
+
+#include "atari/gui.h"
+#include "atari/misc.h"
+#include "atari/rootwin.h"
+#include "atari/clipboard.h"
+#include "atari/gemtk/gemtk.h"
+#include "atari/res/netsurf.rsh"
+#include "atari/ctxmenu.h"
+
+
+#define CNT_INVALID 0
+#define CNT_BROWSER 64
+#define CNT_HREF 128
+#define CNT_SELECTION 256
+#define CNT_INTERACTIVE 512
+#define CNT_IMG 1024
+
+bool gui_window_get_scroll(struct gui_window *w, int *sx, int *sy);
+
+struct s_context_info {
+ unsigned long flags;
+ struct browser_window_features ccdata;
+};
+
+struct s_context_info ctxinfo;
+
+static struct s_context_info * get_context_info( struct gui_window * gw, short mx, short my )
+{
+ hlcache_handle *h;
+ GRECT area;
+ struct browser_window * bw = gw->browser->bw;
+ int sx, sy;
+
+ h = browser_window_get_content(bw);
+ ctxinfo.flags = 0;
+
+ window_get_grect(gw->root, BROWSER_AREA_CONTENT, &area);
+ if (POINT_WITHIN(mx, my, area)) {
+
+ mx -= area.g_x;
+ my -= area.g_y;
+
+ if (browser_window_has_content(bw) == false ||
+ content_get_type(h) != CONTENT_HTML){
+ return(&ctxinfo);
+ }
+
+ ctxinfo.flags |= CNT_BROWSER;
+
+ gui_window_get_scroll(gw, &sx, &sy);
+
+ browser_window_get_features( gw->browser->bw, mx+sx, my+sy,
+ &ctxinfo.ccdata);
+
+ if( ctxinfo.ccdata.link ){
+ ctxinfo.flags |= CNT_HREF;
+ }
+ if( ctxinfo.ccdata.object) {
+ if( content_get_type(ctxinfo.ccdata.object) == CONTENT_IMAGE ){
+ ctxinfo.flags |= CNT_IMG;
+ }
+ }
+ if ( ctxinfo.ccdata.form_features == CTX_FORM_TEXT )
+ ctxinfo.flags |= (CNT_INTERACTIVE | CNT_SELECTION);
+ }
+
+ return(&ctxinfo);
+
+
+}
+
+/***
+ * Generates an unique filename for temp. files
+ * The generated filename includes the path.
+ * If TMPDIR environmenrt vairable is set, this will be used as path,
+ * otherwise U:\tmp\
+ * \param prefix
+ * \param sufffix
+ * \return pointer to static buffer owned by get_tmpfilename()
+ */
+static char * get_tmpfilename(const char * prefix, const char * suffix)
+{
+ int i=0;
+ static char tmpfilename[PATH_MAX];
+ char * tmpdir;
+ const char * tmp_path_suffix = "";
+
+ // TODO: make function public?
+ tmpdir = getenv("TMPDIR");
+ if(tmpdir == NULL){
+ tmpdir = (char*)"u:\\tmp\\";
+ }
+
+ if(tmpdir[strlen(tmpdir)-1] != '\\'){
+ tmp_path_suffix = "\\";
+ }
+
+ do{
+ /* generate a new filename: */
+ snprintf(tmpfilename, PATH_MAX, "%s%s%s%d%s", tmpdir,
+ tmp_path_suffix, prefix, i++, suffix);
+ /* check with cflib: */
+ } while(file_exists(tmpfilename));
+
+ return(tmpfilename);
+}
+
+//TODO: do not open popup for gui_window, but for a rootwin?
+void context_popup(struct gui_window * gw, short x, short y)
+{
+
+#define POP_FIRST_ITEM POP_CTX_CUT_SEL
+#define POP_LAST_ITEM POP_CTX_SAVE_LINK_AS
+
+ OBJECT * pop;
+ int choice;
+ struct s_context_info * ctx;
+ unsigned long size;
+ const char * data;
+ FILE * fp_tmpfile;
+ char cmdline[128];
+ /* skip first byte, which must hold length of commandline: */
+ char * tempfile = &cmdline[1];
+ char * editor;
+ MENU pop_menu, me_data;
+
+ pop = gemtk_obj_get_tree( POP_CTX );
+ if (pop == NULL)
+ return;
+ ctx = get_context_info(gw, x, y);
+
+ /*
+ Disable all items by default:
+ */
+ for( choice = POP_FIRST_ITEM; choice<=POP_LAST_ITEM; choice++ ){
+ SET_BIT(pop[ choice ].ob_state, OS_DISABLED, 1);
+ }
+
+ if( ctx->flags & CNT_INTERACTIVE ){
+ SET_BIT(pop[ POP_CTX_PASTE_SEL ].ob_state, OS_DISABLED, 0);
+ }
+
+ if( (ctx->flags & CNT_BROWSER) ){
+ SET_BIT(pop[ POP_CTX_SELECT_ALL ].ob_state, OS_DISABLED, 0);
+ SET_BIT(pop[ POP_CTX_COPY_SEL ].ob_state, OS_DISABLED, 0);
+ SET_BIT(pop[ POP_CTX_VIEW_SOURCE ].ob_state, OS_DISABLED, 0);
+ }
+
+ if( ctx->flags & CNT_HREF ){
+ SET_BIT(pop[ POP_CTX_COPY_LINK ].ob_state, OS_DISABLED, 0);
+ SET_BIT(pop[ POP_CTX_OPEN_NEW ].ob_state, OS_DISABLED, 0);
+ SET_BIT(pop[ POP_CTX_SAVE_LINK_AS ].ob_state, OS_DISABLED, 0);
+ }
+
+ if( ctx->flags & CNT_IMG ){
+ SET_BIT(pop[ POP_CTX_SAVE_AS ].ob_state, OS_DISABLED, 0);
+ SET_BIT(pop[ POP_CTX_COPY_URL ].ob_state, OS_DISABLED, 0);
+ SET_BIT(pop[ POP_CTX_OPEN_NEW ].ob_state, OS_DISABLED, 0);
+ }
+
+ // point mn_tree tree to states popup:
+ pop_menu.mn_tree = gemtk_obj_get_tree(POP_CTX);
+ pop_menu.mn_menu = 0;
+ pop_menu.mn_item = POP_CTX_CUT_SEL;
+ pop_menu.mn_scroll = SCROLL_NO;
+ pop_menu.mn_keystate = 0;
+
+ menu_popup(&pop_menu, x, y, &me_data);
+ choice = me_data.mn_item;
+
+ switch( choice ){
+ case POP_CTX_COPY_SEL:
+ browser_window_key_press(gw->browser->bw, NS_KEY_COPY_SELECTION);
+ break;
+
+ case POP_CTX_CUT_SEL:
+ browser_window_key_press(gw->browser->bw, NS_KEY_CUT_SELECTION);
+ break;
+
+ case POP_CTX_PASTE_SEL:
+ browser_window_key_press(gw->browser->bw, NS_KEY_PASTE);
+ break;
+
+ case POP_CTX_SELECT_ALL:
+ browser_window_key_press(gw->browser->bw, NS_KEY_SELECT_ALL);
+ break;
+
+ case POP_CTX_SAVE_AS:
+ if (ctx->ccdata.object != NULL) {
+ if( hlcache_handle_get_url(ctx->ccdata.object) != NULL ) {
+ browser_window_navigate(
+ gw->browser->bw,
+ hlcache_handle_get_url(ctx->ccdata.object),
+ browser_window_get_url(gw->browser->bw),
+ BW_NAVIGATE_DOWNLOAD,
+ NULL,
+ NULL,
+ NULL
+ );
+ }
+ }
+
+ case POP_CTX_SAVE_LINK_AS:
+ if (ctx->ccdata.link != NULL) {
+ nserror error;
+
+ error = browser_window_navigate(
+ gw->browser->bw,
+ ctx->ccdata.link,
+ browser_window_get_url(gw->browser->bw),
+ BW_NAVIGATE_DOWNLOAD,
+ NULL,
+ NULL,
+ NULL);
+
+ if (error != NSERROR_OK) {
+ atari_warn_user(messages_get_errorcode(error), 0);
+ }
+ }
+
+ break;
+
+ case POP_CTX_COPY_URL:
+ if ((ctx->flags & CNT_IMG) && (ctx->ccdata.object != NULL)) {
+ if( hlcache_handle_get_url(ctx->ccdata.object) != NULL ){
+ scrap_txt_write((char*)nsurl_access(
+ hlcache_handle_get_url(ctx->ccdata.object)));
+ }
+ }
+ break;
+
+ case POP_CTX_COPY_LINK:
+ if ((ctx->flags & CNT_HREF) &&
+ (ctx->ccdata.link != NULL)) {
+ scrap_txt_write((char*)nsurl_access(ctx->ccdata.link));
+ }
+ break;
+
+ case POP_CTX_OPEN_NEW:
+ if ((ctx->flags & CNT_HREF) &&
+ (ctx->ccdata.link != NULL)) {
+ nserror error;
+
+ error = browser_window_create(
+ BW_CREATE_HISTORY | BW_CREATE_CLONE,
+ ctx->ccdata.link,
+ browser_window_get_url(gw->browser->bw),
+ gw->browser->bw,
+ NULL);
+ if (error != NSERROR_OK) {
+ atari_warn_user(messages_get_errorcode(error), 0);
+ }
+ }
+ break;
+
+ case POP_CTX_VIEW_SOURCE:
+ editor = nsoption_charp(atari_editor);
+ if (editor != NULL && strlen(editor)>0) {
+ data = content_get_source_data(browser_window_get_content(gw->browser->bw),
+ &size);
+ if (size > 0 && data != NULL){
+ snprintf(tempfile, 127, "%s", get_tmpfilename("ns-", ".html"));
+ /* the GEMDOS cmdline contains the length of the commandline
+ in the first byte: */
+ cmdline[0] = (unsigned char)strlen(tempfile);
+ LOG("Creating temporay source file: %s\n", tempfile);
+ fp_tmpfile = fopen(tempfile, "w");
+ if (fp_tmpfile != NULL){
+ fwrite(data, size, 1, fp_tmpfile);
+ fclose(fp_tmpfile);
+
+ // Send SH_WDRAW to notify files changed:
+ gemtk_send_msg(SH_WDRAW, 0, -1, 0, 0, 0, 0);
+
+ // start application:
+ if(strlen(tempfile)<=125){
+ shel_write(1, 1, 1, editor, cmdline);
+ }
+ } else {
+ printf("Could not open temp file: %s!\n", tempfile );
+ }
+
+ } else {
+ LOG("Invalid content!");
+ }
+ } else {
+ form_alert(0, "[1][Set option \"atari_editor\".][OK]");
+ }
+ break;
+
+ default: break;
+ }
+
+#undef POP_FIRST_ITEM
+#undef POP_LAST_ITEM
+
+}
diff --git a/frontends/atari/ctxmenu.h b/frontends/atari/ctxmenu.h
new file mode 100644
index 000000000..73a96637a
--- /dev/null
+++ b/frontends/atari/ctxmenu.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2012 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 CTXMENU_H_INCLUDED
+#define CTXMENU_H_INCLUDED
+
+void context_popup( struct gui_window * gw, short x, short y );
+
+#endif // CTXMENU_H_INCLUDED
diff --git a/frontends/atari/deskmenu.c b/frontends/atari/deskmenu.c
new file mode 100644
index 000000000..723835873
--- /dev/null
+++ b/frontends/atari/deskmenu.c
@@ -0,0 +1,828 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <cflib.h>
+
+#include "utils/log.h"
+#include "utils/nsurl.h"
+#include "utils/messages.h"
+#include "utils/nsoption.h"
+#include "desktop/browser.h"
+#include "desktop/save_complete.h"
+#include "desktop/textinput.h"
+
+#include "atari/res/netsurf.rsh"
+#include "atari/gemtk/gemtk.h"
+#include "atari/deskmenu.h"
+#include "atari/hotlist.h"
+#include "atari/history.h"
+#include "atari/cookies.h"
+#include "atari/toolbar.h"
+#include "atari/settings.h"
+#include "atari/misc.h"
+#include "atari/gui.h"
+#include "atari/findfile.h"
+#include "atari/about.h"
+#include "atari/plot/plot.h"
+#include "atari/rootwin.h"
+
+typedef void __CDECL (*menu_evnt_func)(short item, short title, void * data);
+
+struct s_accelerator
+{
+ char ascii; /* either ascii or */
+ long keycode; /* normalised keycode is valid */
+ short mod; /* shift / ctrl etc */
+};
+
+struct s_menu_item_evnt {
+ short title; /* to which menu this item belongs */
+ short rid; /* resource ID */
+ menu_evnt_func menu_func; /* click handler */
+ struct s_accelerator accel; /* accelerator info */
+ char * menustr;
+};
+
+static void register_menu_str(struct s_menu_item_evnt * mi);
+//static void __CDECL evnt_menu(WINDOW * win, short buff[8]);
+
+extern void *h_gem_rsrc;
+extern bool html_redraw_debug;
+extern struct gui_window * input_window;
+extern char options[PATH_MAX];
+extern const char * option_homepage_url;
+extern int option_window_width;
+extern int option_window_height;
+extern int option_window_x;
+extern int option_window_y;
+
+static OBJECT * h_gem_menu;
+
+
+/* Zero based resource tree ids: */
+#define T_ABOUT 0
+#define T_FILE MAINMENU_T_FILE - MAINMENU_T_FILE + 1
+#define T_EDIT MAINMENU_T_EDIT - MAINMENU_T_FILE + 1
+#define T_VIEW MAINMENU_T_VIEW - MAINMENU_T_FILE + 1
+#define T_NAV MAINMENU_T_NAVIGATE - MAINMENU_T_FILE + 1
+#define T_UTIL MAINMENU_T_UTIL - MAINMENU_T_FILE + 1
+#define T_HELP MAINMENU_T_NAVIGATE - MAINMENU_T_FILE + 1
+/* Count of the above defines: */
+#define NUM_MENU_TITLES 7
+
+
+static void __CDECL menu_about(short item, short title, void *data);
+static void __CDECL menu_new_win(short item, short title, void *data);
+static void __CDECL menu_open_url(short item, short title, void *data);
+static void __CDECL menu_open_file(short item, short title, void *data);
+static void __CDECL menu_close_win(short item, short title, void *data);
+static void __CDECL menu_save_page(short item, short title, void *data);
+static void __CDECL menu_quit(short item, short title, void *data);
+static void __CDECL menu_cut(short item, short title, void *data);
+static void __CDECL menu_copy(short item, short title, void *data);
+static void __CDECL menu_paste(short item, short title, void *data);
+static void __CDECL menu_find(short item, short title, void *data);
+static void __CDECL menu_choices(short item, short title, void *data);
+static void __CDECL menu_stop(short item, short title, void *data);
+static void __CDECL menu_reload(short item, short title, void *data);
+static void __CDECL menu_dec_scale(short item, short title, void *data);
+static void __CDECL menu_inc_scale(short item, short title, void *data);
+static void __CDECL menu_toolbars(short item, short title, void *data);
+static void __CDECL menu_savewin(short item, short title, void *data);
+static void __CDECL menu_debug_render(short item, short title, void *data);
+static void __CDECL menu_fg_images(short item, short title, void *data);
+static void __CDECL menu_bg_images(short item, short title, void *data);
+static void __CDECL menu_back(short item, short title, void *data);
+static void __CDECL menu_forward(short item, short title, void *data);
+static void __CDECL menu_home(short item, short title, void *data);
+static void __CDECL menu_lhistory(short item, short title, void *data);
+static void __CDECL menu_ghistory(short item, short title, void *data);
+static void __CDECL menu_add_bookmark(short item, short title, void *data);
+static void __CDECL menu_bookmarks(short item, short title, void *data);
+static void __CDECL menu_cookies(short item, short title, void *data);
+static void __CDECL menu_vlog(short item, short title, void *data);
+static void __CDECL menu_help_content(short item, short title, void *data);
+
+struct s_menu_item_evnt menu_evnt_tbl[] =
+{
+ {T_ABOUT,MAINMENU_M_ABOUT, menu_about, {0,0,0}, NULL },
+ {T_FILE, MAINMENU_M_NEWWIN, menu_new_win, {0,0,0}, NULL},
+ {T_FILE, MAINMENU_M_OPENURL, menu_open_url, {'G',0,K_CTRL}, NULL},
+ {T_FILE, MAINMENU_M_OPENFILE, menu_open_file, {'O',0,K_CTRL}, NULL},
+ {T_FILE, MAINMENU_M_CLOSEWIN, menu_close_win, {0,0,0}, NULL},
+ {T_FILE, MAINMENU_M_SAVEPAGE, menu_save_page, {0,NK_F3,0}, NULL},
+ {T_FILE, MAINMENU_M_QUIT, menu_quit, {'Q',0,K_CTRL}, NULL},
+ {T_EDIT, MAINMENU_M_CUT, menu_cut, {'X',0,K_CTRL}, NULL},
+ {T_EDIT, MAINMENU_M_COPY, menu_copy, {'C',0,K_CTRL}, NULL},
+ {T_EDIT, MAINMENU_M_PASTE, menu_paste, {'V',0,K_CTRL}, NULL},
+ {T_EDIT, MAINMENU_M_FIND, menu_find, {0,NK_F4,0}, NULL},
+ {T_VIEW, MAINMENU_M_RELOAD, menu_reload, {0,NK_F5,0}, NULL},
+ {T_VIEW, MAINMENU_INC_SCALE, menu_inc_scale, {'+',0,K_CTRL}, NULL},
+ {T_VIEW, MAINMENU_DEC_SCALE, menu_dec_scale, {'-',0,K_CTRL}, NULL},
+ {T_VIEW, MAINMENU_M_TOOLBARS, menu_toolbars, {0,NK_F1,K_CTRL}, NULL},
+ {T_VIEW, MAINMENU_M_SAVEWIN, menu_savewin, {0,0,0}, NULL},
+ {T_VIEW, MAINMENU_M_DEBUG_RENDER, menu_debug_render, {0,0,0}, NULL},
+ {T_VIEW, MAINMENU_M_FG_IMAGES, menu_fg_images, {0,0,0}, NULL},
+ {T_VIEW, MAINMENU_M_BG_IMAGES, menu_bg_images, {0,0,0}, NULL},
+ {T_VIEW, MAINMENU_M_STOP, menu_stop, {0,NK_ESC,K_ALT}, NULL},
+ {T_NAV, MAINMENU_M_BACK, menu_back, {0,NK_LEFT,K_ALT}, NULL},
+ {T_NAV, MAINMENU_M_FORWARD, menu_forward, {0,NK_RIGHT,K_ALT}, NULL},
+ {T_NAV, MAINMENU_M_HOME, menu_home, {0,0,0}, NULL},
+ {T_UTIL, MAINMENU_M_LHISTORY,menu_lhistory, {0,NK_F7,0}, NULL},
+ {T_UTIL, MAINMENU_M_GHISTORY, menu_ghistory, {0,NK_F7,K_CTRL}, NULL},
+ {T_UTIL, MAINMENU_M_ADD_BOOKMARK, menu_add_bookmark, {'D',0,K_CTRL}, NULL},
+ {T_UTIL, MAINMENU_M_BOOKMARKS, menu_bookmarks, {0,NK_F6,0}, NULL},
+ {T_UTIL, MAINMENU_M_COOKIES, menu_cookies, {0,0,0}, NULL},
+ {T_UTIL, MAINMENU_M_CHOICES, menu_choices, {0,0,0}, NULL},
+ {T_UTIL, MAINMENU_M_VLOG, menu_vlog, {'V',0,K_ALT}, NULL},
+ {T_HELP, MAINMENU_M_HELP_CONTENT, menu_help_content, {0,NK_F1,0}, NULL},
+ {-1, -1, NULL,{0,0,0}, NULL }
+};
+
+
+/*
+ static void __CDECL evnt_menu(WINDOW * win, short buff[8])
+ {
+ int title = buff[3];
+ INT16 x,y;
+ char *str;
+ struct gui_window * gw = window_list;
+ int i=0;
+
+ deskmenu_dispatch_item(buff[3], buff[4]);
+ }
+*/
+
+/*
+ Menu item event handlers:
+*/
+
+static void __CDECL menu_about(short item, short title, void *data)
+{
+ /*
+ nsurl *url;
+ nserror error;
+ char buf[PATH_MAX];
+
+ LOG("%s", __FUNCTION__);
+ strcpy((char*)&buf, "file://");
+ strncat((char*)&buf, (char*)"./doc/README.TXT",
+ PATH_MAX - (strlen("file://")+1) );
+
+ error = nsurl_create(buf, &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ atari_warn_user(messages_get_errorcode(error), 0);
+ }
+ */
+ atari_about_show();
+}
+
+static void __CDECL menu_new_win(short item, short title, void *data)
+{
+ nsurl *url;
+ nserror error;
+ const char *addr;
+
+ LOG("%s", __FUNCTION__);
+
+ if (nsoption_charp(homepage_url) != NULL) {
+ addr = nsoption_charp(homepage_url);
+ } else {
+ addr = NETSURF_HOMEPAGE;
+ }
+
+ /* create an initial browser window */
+ error = nsurl_create(addr, &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+
+ }
+ if (error != NSERROR_OK) {
+ atari_warn_user(messages_get_errorcode(error), 0);
+ }
+}
+
+static void __CDECL menu_open_url(short item, short title, void *data)
+{
+ struct gui_window * gw;
+ struct browser_window * bw ;
+ LOG("%s", __FUNCTION__);
+
+ gw = input_window;
+ if( gw == NULL ) {
+ browser_window_create(BW_CREATE_HISTORY,
+ NULL,
+ NULL,
+ NULL,
+ &bw);
+ }
+}
+
+static void __CDECL menu_open_file(short item, short title, void *data)
+{
+
+ LOG("%s", __FUNCTION__);
+
+ const char * filename = file_select(messages_get("OpenFile"), "");
+ if( filename != NULL ){
+ char * urltxt = local_file_to_url( filename );
+ if( urltxt ){
+ nsurl *url;
+ nserror error;
+
+ error = nsurl_create(urltxt, &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+
+ }
+ if (error != NSERROR_OK) {
+ atari_warn_user(messages_get_errorcode(error), 0);
+ }
+ free( urltxt );
+ }
+ }
+}
+
+static void __CDECL menu_close_win(short item, short title, void *data)
+{
+ LOG("%s", __FUNCTION__);
+ if( input_window == NULL )
+ return;
+ gui_window_destroy( input_window );
+}
+
+static void __CDECL menu_save_page(short item, short title, void *data)
+{
+ LOG("%s", __FUNCTION__);
+ static bool init = true;
+ bool is_folder=false;
+ const char * path;
+
+ if( !input_window )
+ return;
+
+ if( init ){
+ init = false;
+ save_complete_init();
+ }
+
+ do {
+ // TODO: localize string
+ path = file_select("Select folder", "");
+ if (path)
+ is_folder = is_dir(path);
+ } while ((is_folder == false) && (path != NULL));
+
+ if( path != NULL ){
+ save_complete(browser_window_get_content(
+ input_window->browser->bw), path, NULL);
+ }
+
+}
+
+static void __CDECL menu_quit(short item, short title, void *data)
+{
+ short buff[8];
+ memset( &buff, 0, sizeof(short)*8 );
+ LOG("%s", __FUNCTION__);
+ gemtk_wm_send_msg(NULL, AP_TERM, 0, 0, 0, 0);
+}
+
+static void __CDECL menu_cut(short item, short title, void *data)
+{
+ if( input_window != NULL )
+ browser_window_key_press( input_window->browser->bw, NS_KEY_CUT_SELECTION);
+}
+
+static void __CDECL menu_copy(short item, short title, void *data)
+{
+ LOG("%s", __FUNCTION__);
+ if( input_window != NULL )
+ browser_window_key_press( input_window->browser->bw, NS_KEY_COPY_SELECTION);
+}
+
+static void __CDECL menu_paste(short item, short title, void *data)
+{
+ LOG("%s", __FUNCTION__);
+ if( input_window != NULL )
+ browser_window_key_press( input_window->browser->bw, NS_KEY_PASTE);
+}
+
+static void __CDECL menu_find(short item, short title, void *data)
+{
+ LOG("%s", __FUNCTION__);
+ if (input_window != NULL) {
+ if (input_window->search) {
+ window_close_search(input_window->root);
+ }
+ else {
+ window_open_search(input_window->root, true);
+ }
+ }
+}
+
+static void __CDECL menu_choices(short item, short title, void *data)
+{
+ LOG("%s", __FUNCTION__);
+ open_settings();
+}
+
+static void __CDECL menu_stop(short item, short title, void *data)
+{
+ LOG("%s", __FUNCTION__);
+ if( input_window == NULL )
+ return;
+
+ assert(input_window && input_window->root);
+ toolbar_stop_click(input_window->root->toolbar);
+
+}
+
+static void __CDECL menu_reload(short item, short title, void *data)
+{
+ if(input_window == NULL)
+ return;
+ toolbar_reload_click(input_window->root->toolbar);
+ LOG("%s", __FUNCTION__);
+}
+
+
+static void __CDECL menu_inc_scale(short item, short title, void *data)
+{
+ if(input_window == NULL)
+ return;
+
+ browser_window_set_scale(input_window->browser->bw,
+ browser_window_get_scale(input_window->browser->bw) + 0.25,
+ true);
+}
+
+
+static void __CDECL menu_dec_scale(short item, short title, void *data)
+{
+ if(input_window == NULL)
+ return;
+
+ browser_window_set_scale(input_window->browser->bw,
+ browser_window_get_scale(input_window->browser->bw) - 0.25,
+ true);
+}
+
+
+
+static void __CDECL menu_toolbars(short item, short title, void *data)
+{
+ static int state = 0;
+ LOG("%s", __FUNCTION__);
+ if( input_window != null && input_window->root->toolbar != null ){
+ state = !state;
+ // TODO: implement toolbar hide
+ //toolbar_hide(input_window->root->toolbar, state );
+ }
+}
+
+static void __CDECL menu_savewin(short item, short title, void *data)
+{
+ LOG("%s", __FUNCTION__);
+ if (input_window && input_window->browser) {
+ GRECT rect;
+ wind_get_grect(gemtk_wm_get_handle(input_window->root->win), WF_CURRXYWH,
+ &rect);
+ option_window_width = rect.g_w;
+ option_window_height = rect.g_h;
+ option_window_x = rect.g_x;
+ option_window_y = rect.g_y;
+ nsoption_set_int(window_width, rect.g_w);
+ nsoption_set_int(window_height, rect.g_h);
+ nsoption_set_int(window_x, rect.g_x);
+ nsoption_set_int(window_y, rect.g_y);
+ nsoption_write((const char*)&options, NULL, NULL);
+ }
+
+}
+
+static void __CDECL menu_debug_render(short item, short title, void *data)
+{
+ LOG("%s", __FUNCTION__);
+ html_redraw_debug = !html_redraw_debug;
+ if( input_window != NULL ) {
+ if ( input_window->browser != NULL
+ && input_window->browser->bw != NULL) {
+ GRECT rect;
+ window_get_grect(input_window->root, BROWSER_AREA_CONTENT, &rect);
+ browser_window_reformat(input_window->browser->bw, false,
+ rect.g_w, rect.g_h );
+ menu_icheck(h_gem_menu, MAINMENU_M_DEBUG_RENDER,
+ (html_redraw_debug) ? 1 : 0);
+ }
+ }
+}
+
+static void __CDECL menu_fg_images(short item, short title, void *data)
+{
+ nsoption_set_bool(foreground_images, !nsoption_bool(foreground_images));
+ menu_icheck(h_gem_menu, MAINMENU_M_FG_IMAGES,
+ (nsoption_bool(foreground_images)) ? 1 : 0);
+}
+
+static void __CDECL menu_bg_images(short item, short title, void *data)
+{
+ nsoption_set_bool(background_images, !nsoption_bool(background_images));
+ menu_icheck(h_gem_menu, MAINMENU_M_BG_IMAGES,
+ (nsoption_bool(background_images)) ? 1 : 0);
+}
+
+static void __CDECL menu_back(short item, short title, void *data)
+{
+ LOG("%s", __FUNCTION__);
+ if( input_window == NULL )
+ return;
+ toolbar_back_click(input_window->root->toolbar);
+}
+
+static void __CDECL menu_forward(short item, short title, void *data)
+{
+ LOG("%s", __FUNCTION__);
+ if( input_window == NULL )
+ return;
+ toolbar_forward_click(input_window->root->toolbar);
+}
+
+static void __CDECL menu_home(short item, short title, void *data)
+{
+ LOG("%s", __FUNCTION__);
+ if( input_window == NULL )
+ return;
+ toolbar_home_click(input_window->root->toolbar);
+}
+
+static void __CDECL menu_lhistory(short item, short title, void *data)
+{
+ LOG("%s", __FUNCTION__);
+ if( input_window == NULL )
+ return;
+}
+
+static void __CDECL menu_ghistory(short item, short title, void *data)
+{
+ LOG("%s", __FUNCTION__);
+ atari_global_history_open();
+}
+
+static void __CDECL menu_add_bookmark(short item, short title, void *data)
+{
+ LOG("%s", __FUNCTION__);
+ if (input_window) {
+ if( browser_window_has_content(input_window->browser->bw) ){
+ atari_hotlist_add_page(
+ nsurl_access(browser_window_get_url(input_window->browser->bw)),
+ NULL
+ );
+ }
+ }
+}
+
+static void __CDECL menu_bookmarks(short item, short title, void *data)
+{
+ LOG("%s", __FUNCTION__);
+ atari_hotlist_open();
+}
+
+static void __CDECL menu_cookies(short item, short title, void *data)
+{
+ LOG("%s", __FUNCTION__);
+ atari_cookie_manager_open();
+}
+
+static void __CDECL menu_vlog(short item, short title, void *data)
+{
+ LOG("%s", __FUNCTION__);
+ verbose_log = !verbose_log;
+ menu_icheck(h_gem_menu, MAINMENU_M_VLOG, (verbose_log) ? 1 : 0);
+}
+
+static void __CDECL menu_help_content(short item, short title, void *data)
+{
+ LOG("%s", __FUNCTION__);
+}
+
+/*
+ Public deskmenu interface:
+*/
+
+
+/*
+ Parse encoded menu key shortcut
+
+ The format is:
+
+ "[" - marks start of the shortcut
+ "@,^,<" - If the keyshortcut is only valid
+ with modifier keys, one of these characters must directly
+ follow the start mark.
+ Meaning:
+ @ -> Alternate
+ ^ -> Control
+ "#" - keycode or ascii character.
+ The value is handled as keycode if the character value
+ is <= 28 ( Atari chracter table )
+ or if it is interpreted as function key string.
+ (strings: F1 - F10)
+
+*/
+static void register_menu_str( struct s_menu_item_evnt * mi )
+{
+ assert(h_gem_menu != NULL);
+
+ struct s_accelerator * accel = &mi->accel;
+ int i, l=0, x=-1;
+ char str[255];
+ bool is_std_shortcut = false;
+
+ get_string(h_gem_menu, mi->rid, str);
+
+ i = l = strlen(str);
+ while (i > 2) {
+ if ((strncmp(" ", &str[i], 2) == 0) && (strlen(&str[i]) > 2)) {
+ // "Standard" Keyboard Shortcut Element found:
+ LOG("Standard Keyboard Shortcut: \"%s\"\n", &str[i]);
+ x = i+2;
+ is_std_shortcut = true;
+ break;
+ }
+
+ if( str[i] == '['){
+ LOG("Keyboard Shortcut: \"%s\"\n", &str[i]);
+ // "Custom" Keyboard Shortcut Element found (identified by [):
+ x = i;
+ break;
+ }
+ i--;
+ }
+
+ // Parse keyboard shortcut value:
+ if( x > -1 ){
+
+ if (is_std_shortcut == false) {
+ // create a new menu string to hide the "[" mark:
+ mi->menustr = malloc( l+1 );
+ strcpy(mi->menustr, str);
+ mi->menustr[x]=' ';
+ x++;
+ }
+
+ // find & register modifiers:
+ if (str[x] == '@') {
+ accel->mod = K_ALT;
+ if (is_std_shortcut == false) {
+ // only modify menu items when it is malloc'd:
+ mi->menustr[x] = 0x07;
+ }
+ x++;
+ }
+ else if (str[x] == '^') {
+ accel->mod = K_CTRL;
+ x++;
+ }
+ else if (str[x] == 0x01) { // the arrow up chracter (atari-st encoding)
+ accel->mod = K_LSHIFT;
+ x++;
+ }
+
+ // find keycodes / chracters:
+ if( str[x] <= 28 ){
+ // parse symbol
+ switch( str[x] ){
+ case 0x03:
+ accel->keycode = NK_RIGHT;
+ break;
+ case 0x04:
+ accel->keycode = NK_LEFT;
+ break;
+ case 0x1B:
+ accel->keycode = NK_ESC;
+ break;
+ default:
+ break;
+ }
+ } else {
+ if(str[x] == 'F' && ( str[x+1] >= '1' && str[x+1] <= '9') ){
+ // parse function key
+ short fkey = atoi( &str[x+1] );
+ if( (fkey >= 0) && (fkey <= 10) ){
+ accel->keycode = NK_F1 - 1 + fkey;
+ }
+ }
+ else if (strncmp(&str[x], "Home", 4) == 0) {
+ accel->keycode = NK_CLRHOME;
+ }
+ else if (strncmp(&str[x], "Undo", 4) == 0) {
+ accel->keycode = NK_UNDO;
+ }
+ else if (strncmp(&str[x], "Help", 4) == 0) {
+ accel->keycode = NK_HELP;
+ }
+ else {
+ accel->ascii = str[x];
+ }
+ }
+
+ LOG("Registered keyboard shortcut for \"%s\" => mod: %d, ""keycode: %ld, ascii: %c\n",
+ str, accel->mod, accel->keycode, accel->ascii);
+ }
+}
+
+/**
+ * Setup & display an desktop menu.
+ */
+
+void deskmenu_init(void)
+{
+ int i;
+
+ h_gem_menu = gemtk_obj_get_tree(MAINMENU);
+
+
+ /* Install menu: */
+ menu_bar(h_gem_menu, MENU_INSTALL);
+
+ /* parse and update menu items: */
+ i = 0;
+ while (menu_evnt_tbl[i].rid != -1) {
+ if(menu_evnt_tbl[i].rid > 0 && menu_evnt_tbl[i].title > 0){
+ register_menu_str( &menu_evnt_tbl[i] );
+ /* Update menu string if not null: */
+ if( menu_evnt_tbl[i].menustr != NULL ){
+ menu_text(h_gem_menu, menu_evnt_tbl[i].rid,
+ menu_evnt_tbl[i].menustr);
+ }
+ }
+ i++;
+ }
+ deskmenu_update();
+ /* Redraw menu: */
+ menu_bar(h_gem_menu, MENU_UPDATE);
+}
+
+/**
+ * Uninstall the desktop menu
+ */
+void deskmenu_destroy(void)
+{
+ int i;
+
+ /* Remove menu from desktop: */
+ menu_bar(h_gem_menu, MENU_REMOVE);
+
+ /* Free modified menu titles: */
+ i=0;
+ while(menu_evnt_tbl[i].rid != -1) {
+ if( menu_evnt_tbl[i].menustr != NULL )
+ free(menu_evnt_tbl[i].menustr);
+ i++;
+ }
+}
+
+/**
+ * Return the deskmenu AES OBJECT tree
+ */
+OBJECT * deskmenu_get_obj_tree(void)
+{
+ return(h_gem_menu);
+}
+
+/**
+ * Handle an menu item event
+ */
+int deskmenu_dispatch_item(short title, short item)
+{
+ int i=0;
+ int retval = 0;
+ OBJECT * menu_root = deskmenu_get_obj_tree();
+
+ menu_tnormal(menu_root, item, 1);
+ menu_tnormal(menu_root, title, 1);
+ menu_bar(menu_root, 1);
+
+ // legacy code, is this sensible?:
+ /*
+ while( gw ) {
+ window_set_focus( gw, WIDGET_NONE, NULL );
+ gw = gw->next;
+ }
+ */
+
+
+ while (menu_evnt_tbl[i].rid != -1) {
+ if (menu_evnt_tbl[i].rid == item) {
+ if (menu_evnt_tbl[i].menu_func != NULL) {
+ menu_evnt_tbl[i].menu_func(item, title, NULL);
+ }
+ break;
+ }
+ i++;
+ }
+
+ return(retval);
+}
+
+/**
+ * Handle an keypress (check for accelerator)
+ */
+int deskmenu_dispatch_keypress(unsigned short kcode, unsigned short kstate,
+ unsigned short nkc)
+{
+ char sascii;
+ bool done = 0;
+ int i = 0;
+
+ sascii = gemtk_keybd2ascii(kcode, 0);
+ if(sascii >= 'a' && sascii <= 'z'){
+ sascii = gemtk_keybd2ascii(kcode, K_LSHIFT);
+ }
+
+ /* Iterate through the menu function table: */
+ while( menu_evnt_tbl[i].rid != -1 && done == false) {
+ if( kstate == menu_evnt_tbl[i].accel.mod
+ && menu_evnt_tbl[i].accel.ascii != 0) {
+ if( menu_evnt_tbl[i].accel.ascii == sascii) {
+ if (menu_evnt_tbl[i].title > 0 && menu_evnt_tbl[i].rid > 0) {
+ deskmenu_dispatch_item(menu_evnt_tbl[i].title,
+ menu_evnt_tbl[i].rid);
+ }
+ else {
+ /* Keyboard shortcut not displayed within menu: */
+ menu_evnt_tbl[i].menu_func(0, 0, NULL);
+ }
+ done = true;
+ break;
+ }
+ } else {
+ /* the accel code hides in the keycode: */
+ if( menu_evnt_tbl[i].accel.keycode != 0) {
+ if( menu_evnt_tbl[i].accel.keycode == (nkc & 0xFF) &&
+ kstate == menu_evnt_tbl[i].accel.mod) {
+ if (menu_evnt_tbl[i].title > 0 && menu_evnt_tbl[i].rid > 0) {
+ deskmenu_dispatch_item(menu_evnt_tbl[i].title,
+ menu_evnt_tbl[i].rid);
+ }
+ else {
+ /* Keyboard shortcut not displayed within menu: */
+ menu_evnt_tbl[i].menu_func(0, 0, NULL);
+ }
+ done = true;
+ break;
+ }
+ }
+ }
+ i++;
+ }
+ return((done==true) ? 1 : 0);
+}
+
+/**
+ * Refresh the desk menu, reflecting netsurf current state.
+ */
+void deskmenu_update(void)
+{
+ menu_icheck(h_gem_menu, MAINMENU_M_DEBUG_RENDER, (html_redraw_debug) ? 1 : 0);
+ menu_icheck(h_gem_menu, MAINMENU_M_FG_IMAGES,
+ (nsoption_bool(foreground_images)) ? 1 : 0);
+ menu_icheck(h_gem_menu, MAINMENU_M_BG_IMAGES,
+ (nsoption_bool(background_images)) ? 1 : 0);
+ menu_icheck(h_gem_menu, MAINMENU_M_VLOG, ((verbose_log == true) ? 1 : 0));
+}
diff --git a/frontends/atari/deskmenu.h b/frontends/atari/deskmenu.h
new file mode 100644
index 000000000..d1b157a2d
--- /dev/null
+++ b/frontends/atari/deskmenu.h
@@ -0,0 +1,11 @@
+#ifndef DESKMENU_H_INCLUDED
+#define DESKMENU_H_INCLUDED
+
+void deskmenu_init(void);
+void deskmenu_destroy(void);
+int deskmenu_dispatch_item(short title, short item);
+int deskmenu_dispatch_keypress(unsigned short kcode, unsigned short kstate, unsigned short nkc);
+OBJECT * deskmenu_get_obj_tree(void);
+void deskmenu_update( void );
+
+#endif // DESKMENU_H_INCLUDED
diff --git a/frontends/atari/doc/DejaVu.txt b/frontends/atari/doc/DejaVu.txt
new file mode 100755
index 000000000..254e2cc42
--- /dev/null
+++ b/frontends/atari/doc/DejaVu.txt
@@ -0,0 +1,99 @@
+Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
+Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below)
+
+Bitstream Vera Fonts Copyright
+------------------------------
+
+Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
+a trademark of Bitstream, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of the fonts accompanying this license ("Fonts") and associated
+documentation files (the "Font Software"), to reproduce and distribute the
+Font Software, including without limitation the rights to use, copy, merge,
+publish, distribute, and/or sell copies of the Font Software, and to permit
+persons to whom the Font Software is furnished to do so, subject to the
+following conditions:
+
+The above copyright and trademark notices and this permission notice shall
+be included in all copies of one or more of the Font Software typefaces.
+
+The Font Software may be modified, altered, or added to, and in particular
+the designs of glyphs or characters in the Fonts may be modified and
+additional glyphs or characters may be added to the Fonts, only if the fonts
+are renamed to names not containing either the words "Bitstream" or the word
+"Vera".
+
+This License becomes null and void to the extent applicable to Fonts or Font
+Software that has been modified and is distributed under the "Bitstream
+Vera" names.
+
+The Font Software may be sold as part of a larger software package but no
+copy of one or more of the Font Software typefaces may be sold by itself.
+
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
+TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
+FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
+ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
+FONT SOFTWARE.
+
+Except as contained in this notice, the names of Gnome, the Gnome
+Foundation, and Bitstream Inc., shall not be used in advertising or
+otherwise to promote the sale, use or other dealings in this Font Software
+without prior written authorization from the Gnome Foundation or Bitstream
+Inc., respectively. For further information, contact: fonts at gnome dot
+org.
+
+Arev Fonts Copyright
+------------------------------
+
+Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the fonts accompanying this license ("Fonts") and
+associated documentation files (the "Font Software"), to reproduce
+and distribute the modifications to the Bitstream Vera Font Software,
+including without limitation the rights to use, copy, merge, publish,
+distribute, and/or sell copies of the Font Software, and to permit
+persons to whom the Font Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright and trademark notices and this permission notice
+shall be included in all copies of one or more of the Font Software
+typefaces.
+
+The Font Software may be modified, altered, or added to, and in
+particular the designs of glyphs or characters in the Fonts may be
+modified and additional glyphs or characters may be added to the
+Fonts, only if the fonts are renamed to names not containing either
+the words "Tavmjong Bah" or the word "Arev".
+
+This License becomes null and void to the extent applicable to Fonts
+or Font Software that has been modified and is distributed under the
+"Tavmjong Bah Arev" names.
+
+The Font Software may be sold as part of a larger software package but
+no copy of one or more of the Font Software typefaces may be sold by
+itself.
+
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
+TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
+
+Except as contained in this notice, the name of Tavmjong Bah shall not
+be used in advertising or otherwise to promote the sale, use or other
+dealings in this Font Software without prior written authorization
+from Tavmjong Bah. For further information, contact: tavmjong @ free
+. fr.
+
+$Id: LICENSE 2133 2007-11-28 02:46:28Z lechimp $
diff --git a/frontends/atari/doc/bugs b/frontends/atari/doc/bugs
new file mode 100755
index 000000000..74576f67d
--- /dev/null
+++ b/frontends/atari/doc/bugs
@@ -0,0 +1,4 @@
+KNOWN BUGS
+
+- "View Source" only works when the configured editor is already launched.
+- Wrong redraw area when parts of the window move out of the screen area.
diff --git a/frontends/atari/doc/changes.txt b/frontends/atari/doc/changes.txt
new file mode 100755
index 000000000..969a44bff
--- /dev/null
+++ b/frontends/atari/doc/changes.txt
@@ -0,0 +1,53 @@
+
+
+ ===========================
+ FreeMiNT Frontend Changelog
+ ===========================
+
+
+ NetSurf 3.1
+========================================
+
+As there was no atari NetSurf 3.0 Release FreeMiNT, this Changelog contains
+the FreeMint specific changes from
+NetSurf 2.9-PL1 Release (Sunday, April 15 2012) to Netsurf 3.1 Release.
+
+
+ - Removed WinDom dependency (heavy GUI code change)
+ - Implemented "paste clipboard" for toolbar URL textbox.
+ - Implemented file drop on browser window handling when netsurf core
+ ignores the file drop event.
+ - Implemented basic browser window scaling
+ - Integrated "search page" dialog into browser window
+ - New settings dialog
+ - New treeview implementation
+ - Added global history window.
+ - Added SSL information treeview
+ - Added about dialog
+ - Improved keyboard-shortcut handling
+ - Improved caret redraw
+ - Improved favicon display
+ - Improved warning display (now shows a message box)
+ - Improved bitmap blitting
+ - Fixed FreeMiNT's unified filesystem handling (it's not Linux! - but DOS!)
+ - Fixed default download directoy
+ - Fixed integer overflow in download completed calculation
+ - Fixed commandline filepath handling
+
+
+ NetSurf 2.9
+========================================
+ - Fixed redraw of rectangle outlines.
+ - Fixed redraw of Hotlist, looks better now.
+ - Use netsurfs textarea implementation for URL, to reduce code size.
+ - Fixed several redraw glitches.
+ - Added a context menu, offering some handy functions.
+ - NetSurf core now supports frames & iframes.
+ - Debug rendering works now ( fixed redraw of rectangle outlines )
+ - process commandline parameter w (width) / h (height) for default window size
+ - improved mouse drag within treeview, browser win, toolbar.
+ - Fixed file drop in frames
+ - Implemented option dialog
+ - Improved GUI speed by fixing throbber code.
+ - Implemented "Save as" ( no improvements where made to core-functions ).
+ - Implemented settings dialog
diff --git a/frontends/atari/doc/faq.txt b/frontends/atari/doc/faq.txt
new file mode 100755
index 000000000..53dbe81f2
--- /dev/null
+++ b/frontends/atari/doc/faq.txt
@@ -0,0 +1,74 @@
+
+NETSURF ATARI FAQ
+
+Date: 12.04.2012
+
+01.) CAN NETSURF ALSO BE RUN WITHIN AN TOS ENVIRONMENT?
+02.) I DON'T HAVE AN 15 BIT GRAPHICS CARD, HOW TO RUN NETSURF ANYWAY?
+03.) WHEN I WANT TO RUN NETSURF IT SAYS: "SOCKET NOT CONNECTED".
+04.) I'M GETTING "INSUFFICENT MEMORY" MESSAGES WHEN RUNNING NETSURF.
+05.) SSL DOES NOT WORK - WHAT'S WRONG?
+06.) I'M GETTING THE ERROR: "BASE STYLESHEET FAILED TO LOAD" - WHAT'S WRONG?
+05.) A PAGE CRASHES - WHAT CAN I DO?
+
+1.) Question:
+ CAN NETSURF ALSO BE RUN WITHIN AN TOS ENVIRONMENT?
+
+ Answer:
+ Yes NS can run on classic TOS. More or less.
+ It is intended to run within the FreeMint environment.
+ However there is no support for networking
+ right now. Networking support for FireTOS is maybe possible
+ in the future.
+ NS also has problems with Path conversions on all FileSystems
+ running under TOS. Try setting full paths within the Choices file.
+
+
+2.) Question:
+ I DON'T HAVE AN 15 BIT GRAPHICS CARD, HOW TO RUN NETSURF ANYWAY?
+
+ Answer:
+ You need to trigger the config a bit.
+ enable the following Choices settings:
+
+ atari_font_driver:vdi
+ atari_transparency:0
+ suppress_images:1
+
+ This will run netsurf as a bloat textmode browser :)
+
+3.) Question:
+ WHEN I WANT TO RUN NETSURF IT SAYS: "SOCKET NOT CONNECTED"
+
+ Answer:
+ Take a look at the system requirements! I statet NetSurf needs
+ the latest FreeMiNT 1.17 RELEASE. There was a bug within previous
+ Versions. Please update your kernel. OR install polipo proxy
+ provided as RPM within sparemint distribution. This is an
+ workaround, it should work out of the box. But if you want to
+ use the polipo disk-cache you should edit the config file.
+
+4.) Question:
+ I'M GETTING "INSUFFICENT MEMORY" MESSAGES WHEN RUNNING NETSURF.
+
+ Answer:
+ Try to increase the TPA_INITIALMEM Configuration value within MINT.CNF
+
+5.) Question:
+ SSL DOES NOT WORK - WHAT TO DO?
+
+ Answer:
+ Try to move the shipped cabundle.crt file to the path where NetSurf
+ looks for the SSL certificates (Displayed within statusbar and also
+ verbose log).
+
+6.) Question:
+ I'M GETTING THE ERROR: "BASE STYLESHEET FAILED TO LOAD" - WHAT'S WRONG?
+
+ Answer:
+ NetSurf checks if the base stylesheet is modified. For that - it
+ requires that the system clock is returning reasonable valid values.
+ Make your your system clock is set. (Rember empty NVRAM....)
+ If that doesn't help - make sure the netsurf package was unpacked
+ completly and that all files can be read.
+
diff --git a/frontends/atari/doc/readme.txt b/frontends/atari/doc/readme.txt
new file mode 100755
index 000000000..829b59ec8
--- /dev/null
+++ b/frontends/atari/doc/readme.txt
@@ -0,0 +1,125 @@
+
+
+ NETSURF
+
+ -
+
+ NATIVE ATARI ALPHA
+
+ Version 2.9 (Release Version)
+
+Ported by: m0n0
+Release date: xx.xx.xx
+Contact: ole@monochrom.net
+WWW: http://netsurf-browser.org
+
+
+Table of Contents:
+------------------
+
+0x01 - What is it?
+0x02 - System Requirements
+0x03 - Features
+0x04 - Missing features
+0x05 - Things to test
+0x06 - Additional Notes
+0x07 - Known bugs
+0x08 - Technical information
+
+
+What is it? A web browser!
+--------------------------
+
+ NetSurf is a multi-platform web browser which is written with
+ portability and speed in mind.
+ This is the native Port for the FreeMiNT OS.
+ More info at project website: www.netsurf-browser.org
+
+
+Minimum System Requirements:
+----------------------------
+
+ - 32 MB RAM ( 48 MB recommended for demanding websites )
+ - 32 MHz ( 60 Mhz recommended )
+ - At least 15 Bit Graphics card.
+ - FreeMiNT 1.17.0 release kernel (Please look at FAQ to read
+ about TOS support) for full & correct network support.
+
+
+Main Features:
+--------------
+
+ - Very good HTML 4 & CSS 2.1 rendering
+ - HTTPS
+ - Freetype2 font rendering
+
+
+Missing Features:
+-----------------
+
+ This section describes Features that NetSurf-Core offers but which are not
+ handled by the GEM frontend currently.
+
+ - Grapical website history dialog
+
+
+Installation Notes:
+-------------------
+
+ Unpack the compressed archive that you downloaded,
+ change into the new directory and run ns.prg.
+ If something isn't working - run ns.prg within an console and
+ enable logging:
+
+ ./ns.prg -v
+
+ that makes it possible, that you can identify the problem.
+
+
+Additional Notes
+----------------
+
+ If you would like to see the above mentioned features or
+ can't run NetSurf because you only have a 16 or 256 Color system
+ get in contact.
+
+ Please also check the FAQ document.
+
+ Want to have other software ported? Get in contact and make me rich >;-)
+ If you want to help with netsurf, contact me for further info
+ or visit the netsurf svn and add something usefull :)
+
+ This is "just an early" alpha release. I wanted to get things moving on
+ and I think it is good to show the Atari-Users what has been archived
+ so far. This release lacks some features and some of the code written
+ was just coded with an "I have to get this done quickly" attitude.
+ This is especially true for the drawin routines... It doesn't offer
+ offscreen bitmaps, which was one of my goals for a release. But
+ I dropped that in favor of an not-so-delayed release.
+
+
+Known Bugs
+----------
+
+ - "View Source" only works when the configured editor is already launched.
+ - Wrong redraw area when parts of the window move out of the screen area.
+
+Technical info & outlook
+------------------------
+
+
+Greetings & Thanks
+------------------
+
+ - AtFact for providing help with resource files & images
+ - The MiNT Mailing list, they all helped me a lot!
+ - The NetSurf Mailing list guys, especially the Amiga guys.
+ - The NetSurf developers that did a great job!
+ - Everyone that tested this Browser!
+ - Everyone that provides feedback!
+ - The forum.atari-home.de members for giving me much help
+ during setup of my atari!
+
+
+----
+M0N0 - 09.09.2011
diff --git a/frontends/atari/doc/todo.txt b/frontends/atari/doc/todo.txt
new file mode 100755
index 000000000..49f8e6570
--- /dev/null
+++ b/frontends/atari/doc/todo.txt
@@ -0,0 +1,18 @@
+TODO's (no priority order) for NetSurf 3.1 - 4.0
+
+ - Optimize drawing of bitmaps on Low-Memory machines
+ - Restore the Palette when Windows get's the Focus
+ -> only needed for <= 256 colors
+ - Make drawing of tiled bitmaps optional ( they are slooow )
+ -> already optimized, still needed?
+ - Make context menu more stable (grab all available context data at popup display)
+ - Implement SystemColor Choices dialog
+ - Add at least one offscreen plotter implementation
+ - Fix utf8 to atari character conversion (legacy VDI text plotter
+ must be able to handle the text argument characters correctly)
+ - Add URL history when typing URL's into url textarea
+ - Have browser_window specific cursor, window specifc cursor
+ - When minimized (not iconyfied) NetSurf doesn't recognize that.
+ - Implement Tabs (up to 4 tab favicons when iconyfied)
+ - merge treeview and gui_window handling
+
diff --git a/frontends/atari/download.c b/frontends/atari/download.c
new file mode 100644
index 000000000..9ebe78751
--- /dev/null
+++ b/frontends/atari/download.c
@@ -0,0 +1,453 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "utils/nsoption.h"
+#include "utils/string.h"
+#include "content/urldb.h"
+#include "content/fetch.h"
+#include "desktop/save_complete.h"
+#include "desktop/textinput.h"
+#include "desktop/download.h"
+#include "desktop/browser.h"
+#include "desktop/gui_download.h"
+
+#include "atari/gui.h"
+#include "atari/misc.h"
+#include "atari/res/netsurf.rsh"
+#include "atari/download.h"
+#include "atari/osspec.h"
+
+extern struct gui_window * input_window;
+extern GRECT desk_area;
+
+static void gui_download_window_destroy( struct gui_download_window * gdw );
+static void on_abort_click(struct gui_download_window *dw);
+static void on_cbrdy_click(struct gui_download_window *dw);
+static void on_close(struct gui_download_window * dw);
+static void on_redraw(struct gui_download_window *dw, GRECT *clip);
+
+static void toolbar_redraw_cb(GUIWIN *win, uint16_t msg, GRECT *clip)
+{
+ struct gui_download_window *data;
+
+ if (msg != WM_REDRAW) {
+ data = gemtk_wm_get_user_data(win);
+
+ assert(data);
+
+ on_redraw(data, clip);
+ }
+}
+
+static short on_aes_event(GUIWIN *win, EVMULT_OUT *ev_out, short msg[8])
+{
+ short retval = 0;
+ struct gui_download_window *data;
+
+ GRECT clip;
+
+ data = gemtk_wm_get_user_data(win);
+
+ if ((ev_out->emo_events & MU_MESAG) != 0) {
+ // handle message
+ //printf("download win msg: %d\n", msg[0]);
+ switch (msg[0]) {
+
+ case WM_REDRAW:
+ clip.g_x = msg[4];
+ clip.g_y = msg[5];
+ clip.g_w = msg[6];
+ clip.g_h = msg[7];
+ on_redraw(data, &clip);
+ break;
+
+ case WM_CLOSED:
+ // TODO: this needs to iterate through all gui windows and
+ // check if the rootwin is this window...
+ on_close(data);
+ break;
+
+ case WM_TOOLBAR:
+ switch(msg[4]){
+
+ case DOWNLOAD_BT_ABORT:
+ on_abort_click(data);
+ break;
+
+ case DOWNLOAD_CB_CLOSE_RDY:
+ on_cbrdy_click(data);
+ break;
+
+ default: break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ if ((ev_out->emo_events & MU_KEYBD) != 0) {
+
+
+ }
+ if ((ev_out->emo_events & MU_BUTTON) != 0) {
+
+ }
+
+ return(retval);
+}
+
+static void on_redraw(struct gui_download_window *dw, GRECT *clip)
+{
+ OBJECT *tree = dw->tree;
+ GRECT work, visible;
+ uint32_t p = 0;
+
+ gemtk_wm_get_grect(dw->guiwin, GEMTK_WM_AREA_TOOLBAR, &work);
+ tree->ob_x = work.g_x;
+ tree->ob_y = work.g_y;
+
+ if(!rc_intersect(clip, &work)){
+ return;
+ }
+
+ /*
+ Update the AES Object to reflect current state of download:
+ */
+ ((TEDINFO *)get_obspec(tree, DOWNLOAD_FILENAME))->te_ptext = dw->lbl_file;
+ ((TEDINFO *)get_obspec(tree, DOWNLOAD_LBL_BYTES))->te_ptext = dw->lbl_done;
+ ((TEDINFO *)get_obspec(tree, DOWNLOAD_LBL_PERCENT))->te_ptext = dw->lbl_percent;
+ ((TEDINFO *)get_obspec(tree, DOWNLOAD_LBL_SPEED))->te_ptext = dw->lbl_speed;
+
+ if (dw->size_total > 0 ) {
+ p = ((double)dw->size_downloaded / (double)dw->size_total * 100);
+ }
+ tree[DOWNLOAD_PROGRESS_DONE].ob_width = MAX( MIN( p*(DOWNLOAD_BAR_MAX/100),
+ DOWNLOAD_BAR_MAX ), 1);
+ if (dw->close_on_finish) {
+ tree[DOWNLOAD_CB_CLOSE_RDY].ob_state |= (OS_SELECTED | OS_CROSSED);
+ } else {
+ tree[DOWNLOAD_CB_CLOSE_RDY].ob_state &= ~(OS_SELECTED | OS_CROSSED);
+ }
+ tree[DOWNLOAD_BT_ABORT].ob_state &= ~OS_SELECTED;
+
+ /*Walk the AES rectangle list and redraw the visible areas of the window: */
+ wind_get_grect(dw->aes_handle, WF_FIRSTXYWH, &visible);
+ while (visible.g_x && visible.g_y) {
+ if (rc_intersect(&work, &visible)) {
+ objc_draw_grect(tree, 0, 8, &visible);
+ }
+ wind_get_grect(dw->aes_handle, WF_NEXTXYWH, &visible);
+ }
+}
+
+static void on_abort_click(struct gui_download_window *dw)
+{
+ if( dw->status == NSATARI_DOWNLOAD_COMPLETE
+ || dw->status == NSATARI_DOWNLOAD_ERROR ) {
+ gemtk_wm_send_msg(dw->guiwin, WM_CLOSED, 0,0,0,0);
+ }
+ else if( dw->status != NSATARI_DOWNLOAD_CANCELED ){
+ dw->abort = true;
+ }
+}
+
+static void on_cbrdy_click(struct gui_download_window *dw)
+{
+ dw->close_on_finish = !dw->close_on_finish;
+ if (dw->close_on_finish && dw->status == NSATARI_DOWNLOAD_COMPLETE) {
+ gemtk_wm_send_msg(dw->guiwin, WM_CLOSED, 0,0,0,0);
+ }
+ gemtk_wm_exec_redraw(dw->guiwin, NULL);
+ evnt_timer(250);
+}
+
+static void on_close(struct gui_download_window * dw)
+{
+ gui_download_window_destroy(dw);
+}
+
+static void gui_download_window_destroy( struct gui_download_window * gdw)
+{
+ LOG("gdw %p", gdw);
+
+ if (gdw->status == NSATARI_DOWNLOAD_WORKING) {
+ download_context_abort(gdw->ctx);
+ }
+
+ download_context_destroy(gdw->ctx);
+
+ if (gdw->destination) {
+ free( gdw->destination );
+ }
+ if (gdw->fd != NULL) {
+ fclose(gdw->fd);
+ gdw->fd = NULL;
+ }
+ if (gdw->fbuf != NULL) {
+ free( gdw->fbuf );
+ }
+ gemtk_wm_remove(gdw->guiwin);
+ wind_close(gdw->aes_handle);
+ wind_delete(gdw->aes_handle);
+ free(gdw);
+}
+
+static char * select_filepath( const char * path, const char * filename )
+{
+ char tmp[PATH_MAX];
+ char res_path[PATH_MAX];
+ char res_file[PATH_MAX];
+ char * ret = NULL;
+
+ strncpy(res_path, path, PATH_MAX);
+ strncpy(res_file, filename, PATH_MAX);
+ res_file[PATH_MAX-1] = 0;
+ res_path[PATH_MAX-1] = 0;
+
+ if(select_file(res_path, res_file, (char*)"*",
+ (char*)messages_get("SaveAsNS"), NULL)) {
+ snprintf(tmp, PATH_MAX, "%s%s", res_path, res_file);
+ ret = malloc(strlen(tmp)+1);
+ strcpy(ret, tmp);
+ }
+
+ printf("download file: %s\n", ret);
+ return(ret);
+}
+
+static struct gui_download_window *
+gui_download_window_create(download_context *ctx, struct gui_window *parent)
+{
+ const char *filename;
+ char *destination;
+ char gdos_path[PATH_MAX];
+ struct gui_download_window * gdw;
+ int dlgres = 0;
+ OBJECT * tree = gemtk_obj_get_tree(DOWNLOAD);
+ char alert[200];
+
+
+ LOG("Creating download window for gui window: %p", parent);
+
+ /* TODO: Implement real form and use messages file strings! */
+
+ if (tree == NULL){
+ die("Couldn't find AES Object tree for download window!");
+ return(NULL);
+ }
+
+ filename = download_context_get_filename((const download_context*)ctx);
+ snprintf(alert, 200, "[2][Accept download?|%.*s][Yes|Save as...|No]",
+ 40,filename);
+ dlgres = form_alert(2, alert);
+ if( dlgres == 3){
+ return( NULL );
+ }
+ else if( dlgres == 2 ){
+ gemdos_realpath(nsoption_charp(downloads_path), gdos_path);
+ char * tmp = select_filepath( gdos_path, filename );
+ if( tmp == NULL )
+ return( NULL );
+ destination = tmp;
+ } else {
+ int dstsize=0;
+ gemdos_realpath(nsoption_charp(downloads_path), gdos_path);
+ dstsize = strlen(gdos_path) + strlen(filename) + 2;
+ destination = malloc( dstsize );
+ snprintf(destination, dstsize, "%s/%s", gdos_path, filename);
+ }
+
+ gdw = calloc(1, sizeof(struct gui_download_window));
+ if( gdw == NULL ){
+ atari_warn_user(NULL, "Out of memory!");
+ free( destination );
+ return( NULL );
+ }
+
+ gdw->ctx = ctx;
+ gdw->abort = false;
+ gdw->start = clock() / CLOCKS_PER_SEC;
+ gdw->lastrdw = 0;
+ gdw->status = NSATARI_DOWNLOAD_WORKING;
+ gdw->parent = parent;
+ gdw->fbufsize = MAX(BUFSIZ, 48000);
+ gdw->size_downloaded = 0;
+ gdw->size_total = download_context_get_total_length(ctx);
+ gdw->destination = destination;
+ gdw->tree = tree;
+
+ gdw->fd = fopen(gdw->destination, "wb");
+ if( gdw->fd == NULL ){
+ char spare[200];
+ snprintf(spare, 200, "Couldn't open %s for writing!", gdw->destination);
+ gemtk_msg_box_show(GEMTK_MSG_BOX_ALERT, spare);
+ gui_download_window_destroy(gdw);
+ return( NULL );
+ }
+
+ gdw->fbuf = malloc( gdw->fbufsize+1 );
+ if( gdw->fbuf != NULL ){
+ setvbuf( gdw->fd, gdw->fbuf, _IOFBF, gdw->fbufsize );
+ }
+
+ gdw->aes_handle = wind_create_grect(CLOSER | NAME | MOVER, &desk_area);
+ wind_set_str(gdw->aes_handle, WF_NAME, "Download");
+ unsigned long gwflags = GEMTK_WM_FLAG_DEFAULTS;
+ gdw->guiwin = gemtk_wm_add(gdw->aes_handle, gwflags, on_aes_event);
+ if( gdw->guiwin == NULL || gdw->fd == NULL ){
+ die("could not create guiwin");
+ gui_download_window_destroy(gdw);
+ return( NULL );
+ }
+ gemtk_wm_set_user_data(gdw->guiwin, gdw);
+ gemtk_wm_set_toolbar(gdw->guiwin, tree, 0, 0);
+ gemtk_wm_set_toolbar_redraw_func(gdw->guiwin, toolbar_redraw_cb);
+
+ strncpy((char*)&gdw->lbl_file, filename, MAX_SLEN_LBL_FILE-1);
+ LOG("created download: %s (total size: %d)", gdw->destination, gdw->size_total);
+
+ GRECT work, curr;
+ work.g_x = 0;
+ work.g_y = 0;
+ work.g_w = tree->ob_width;
+ work.g_h = tree->ob_height;
+
+ wind_calc_grect(WC_BORDER, CLOSER | MOVER | NAME, &work, &curr);
+
+ curr.g_x = (desk_area.g_w / 2) - (curr.g_w / 2);
+ curr.g_y = (desk_area.g_h / 2) - (curr.g_h / 2);
+
+ wind_open_grect(gdw->aes_handle, &curr);
+ gdw->lastrdw = clock() / (CLOCKS_PER_SEC >> 3);
+
+ return(gdw);
+}
+
+
+static nserror gui_download_window_data(struct gui_download_window *dw,
+ const char *data, unsigned int size)
+{
+ uint32_t clck = clock();
+ uint32_t tnow = clck / (CLOCKS_PER_SEC>>3);
+ uint32_t sdiff = (clck / (CLOCKS_PER_SEC)) - dw->start;
+
+ LOG("dw %p",dw);
+
+ if (dw->abort == true){
+ dw->status = NSATARI_DOWNLOAD_CANCELED;
+ dw->abort = false;
+ download_context_abort(dw->ctx);
+ gemtk_wm_exec_redraw(dw->guiwin, NULL);
+ return(NSERROR_OK);
+ }
+
+ /* save data */
+ fwrite( data , size, sizeof(unsigned char),dw->fd );
+ dw->size_downloaded += size;
+
+ /* Update GUI */
+ if ((tnow - dw->lastrdw) > 1) {
+ float speed;
+
+ dw->lastrdw = tnow;
+ speed = dw->size_downloaded / sdiff;
+
+ if( dw->size_total > 0 ){
+ uint32_t p = 0;
+ p = ((double)dw->size_downloaded / (double)dw->size_total * 100);
+ snprintf( (char*)&dw->lbl_percent, MAX_SLEN_LBL_PERCENT,
+ "%"PRIu32"%s", p, "%"
+ );
+ } else {
+ snprintf( (char*)&dw->lbl_percent, MAX_SLEN_LBL_PERCENT,
+ "%s", "?%"
+ );
+ }
+ snprintf( (char*)&dw->lbl_speed, MAX_SLEN_LBL_SPEED, "%s/s",
+ human_friendly_bytesize(speed)
+ );
+ snprintf( (char*)&dw->lbl_done, MAX_SLEN_LBL_DONE, "%s / %s",
+ human_friendly_bytesize(dw->size_downloaded),
+ (dw->size_total>0) ? human_friendly_bytesize(dw->size_total) : "?"
+ );
+
+ gemtk_wm_exec_redraw(dw->guiwin, NULL);
+ }
+ return NSERROR_OK;
+}
+
+static void gui_download_window_error(struct gui_download_window *dw,
+ const char *error_msg)
+{
+ LOG("%s", error_msg);
+
+ strncpy((char*)&dw->lbl_file, error_msg, MAX_SLEN_LBL_FILE-1);
+ dw->status = NSATARI_DOWNLOAD_ERROR;
+ gemtk_wm_exec_redraw(dw->guiwin, NULL);
+ atari_window_set_status(input_window, messages_get("Done") );
+ // TODO: change abort to close
+}
+
+static void gui_download_window_done(struct gui_download_window *dw)
+{
+ LOG("dw %p", dw);
+
+// TODO: change abort to close
+ dw->status = NSATARI_DOWNLOAD_COMPLETE;
+
+ if( dw->fd != NULL ) {
+ fclose( dw->fd );
+ dw->fd = NULL;
+ }
+
+ if (dw->close_on_finish) {
+ gemtk_wm_send_msg(dw->guiwin, WM_CLOSED, 0, 0, 0, 0);
+ } else {
+ snprintf( (char*)&dw->lbl_percent, MAX_SLEN_LBL_PERCENT,
+ "%u%s", 100, "%"
+ );
+ snprintf( (char*)&dw->lbl_done, MAX_SLEN_LBL_DONE, "%s / %s",
+ human_friendly_bytesize(dw->size_downloaded),
+ (dw->size_total>0) ? human_friendly_bytesize(dw->size_total) : human_friendly_bytesize(dw->size_downloaded)
+ );
+ gemtk_wm_exec_redraw(dw->guiwin, NULL);
+ }
+ atari_window_set_status(input_window, messages_get("Done") );
+}
+
+static struct gui_download_table download_table = {
+ .create = gui_download_window_create,
+ .data = gui_download_window_data,
+ .error = gui_download_window_error,
+ .done = gui_download_window_done,
+};
+
+struct gui_download_table *atari_download_table = &download_table;
diff --git a/frontends/atari/download.h b/frontends/atari/download.h
new file mode 100644
index 000000000..a5be3257e
--- /dev/null
+++ b/frontends/atari/download.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_ATARI_DOWNLOAD_H
+#define NS_ATARI_DOWNLOAD_H
+
+extern struct gui_download_table *atari_download_table;
+
+#define MAX_SLEN_LBL_DONE 64
+#define MAX_SLEN_LBL_PERCENT 5
+#define MAX_SLEN_LBL_SPEED 13
+#define MAX_SLEN_LBL_FILE 256
+#define MAX_SLEN_BT_ABORT 5
+
+#define DOWNLOAD_BAR_MAX 300
+
+typedef enum {
+ NSATARI_DOWNLOAD_NONE,
+ NSATARI_DOWNLOAD_WORKING,
+ NSATARI_DOWNLOAD_ERROR,
+ NSATARI_DOWNLOAD_COMPLETE,
+ NSATARI_DOWNLOAD_CANCELED
+} nsatari_download_status;
+
+struct gui_download_window {
+ struct download_context *ctx;
+ struct gui_window * parent;
+ GUIWIN *guiwin;
+ short aes_handle;
+ OBJECT *tree;
+ nsatari_download_status status;
+ char *destination;
+ FILE * fd;
+ char lbl_done[MAX_SLEN_LBL_DONE];
+ char lbl_percent[MAX_SLEN_LBL_PERCENT];
+ char lbl_speed[MAX_SLEN_LBL_SPEED];
+ char lbl_file[MAX_SLEN_LBL_FILE];
+ uint32_t start;
+ uint32_t lastrdw;
+ uint32_t size_total;
+ uint32_t size_downloaded;
+ char * fbuf;
+ size_t fbufsize;
+ bool abort;
+ bool close_on_finish;
+};
+
+#endif
diff --git a/frontends/atari/encoding.c b/frontends/atari/encoding.c
new file mode 100644
index 000000000..9a16cbd4e
--- /dev/null
+++ b/frontends/atari/encoding.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2012 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+
+#include "utils/errors.h"
+#include "desktop/gui_utf8.h"
+
+#include "atari/encoding.h"
+
+
+/* TODO: this need a rework..., encoding to atari st doesn|t always work.
+( gui_add_to_clipboard...) */
+nserror utf8_to_local_encoding(const char *string,
+ size_t len,
+ char **result)
+{
+ nserror r;
+ r = utf8_to_enc(string, "ATARIST", len, result);
+ if (r != NSERROR_OK) {
+ r = utf8_to_enc(string, "UTF-8", len, result);
+ assert( r == NSERROR_OK );
+ }
+ return r;
+}
+
+
+nserror utf8_from_local_encoding(const char *string, size_t len, char **result)
+{
+ return utf8_from_enc(string, "ATARIST", len, result, NULL);
+}
+
+
+/* borrowed from highwire project: */
+static const uint16_t Atari_to_Unicode[] = {
+ /* .0 .1 .2 .3 .4 .5 .6 .7 .8 .9 .A .B .C .D .E .F */
+ /* 7F */ 0x0394,
+ /* 8. */ 0x00C7,0x00FC,0x00E9,0x00E2,0x00E4,0x00E0,0x00E5,0x00E7,0x00EA,0x00EB,0x00E8,0x00EF,0x00EE,0x00EC,0x00C4,0x00C5,
+ /* 9. */ 0x00C9,0x00E6,0x00C6,0x00F4,0x00F6,0x00F2,0x00FB,0x00F9,0x00FF,0x00D6,0x00DC,0x00A2,0x00A3,0x00A5,0x00DF,0x0192,
+ /* A. */ 0x00E1,0x00ED,0x00F3,0x00FA,0x00F1,0x00D1,0x00AA,0x00BA,0x00BF,0x2310,0x00AC,0x00BD,0x00BC,0x00A1,0x00AB,0x00BB,
+ /* B. */ 0x00C3,0x00F5,0x00D8,0x00F8,0x0153,0x0152,0x00C0,0x00C3,0x00D5,0x00A8,0x00B4,0x2020,0x00B6,0x00A9,0x00AE,0x2122,
+ /* C. */ 0x0133,0x0132,0x05D0,0x05D1,0x05D2,0x05D3,0x05D4,0x05D5,0x05D6,0x05D7,0x05D8,0x05D9,0x05DB,0x05DC,0x05DE,0x05E0,
+ /* D. */ 0x05E1,0x05E2,0x05E4,0x05E6,0x05E7,0x05E8,0x05E9,0x05EA,0x05DF,0x05DA,0x05DD,0x05E3,0x05E5,0x00A7,0x2038,0x221E,
+ /* E. */ 0x03B1,0x03B2,0x0393,0x03C0,0x03A3,0x03C3,0x00B5,0x03C4,0x03A6,0x0398,0x03A9,0x03B4,0x222E,0x03C6,0x2208,0x2229,
+ /* F. */ 0x2261,0x00B1,0x2265,0x2264,0x2320,0x2321,0x00F7,0x2248,0x00B0,0x2022,0x00B7,0x221A,0x207F,0x00B2,0x00B3,0x00AF
+};
+#define BEG_Atari_to_Unicode 0x7F
+
+int atari_to_ucs4(unsigned char atari)
+{
+ uint32_t ucs4 = 0xfffd;
+ if ( atari >= BEG_Atari_to_Unicode && atari <= 0xFE ) {
+ ucs4 = (int)Atari_to_Unicode[(short)atari - BEG_Atari_to_Unicode];
+ } else {
+ ucs4 = (int)atari;
+ }
+ return( ucs4 );
+}
+
+
+static struct gui_utf8_table utf8_table = {
+ .utf8_to_local = utf8_to_local_encoding,
+ .local_to_utf8 = utf8_from_local_encoding,
+};
+
+struct gui_utf8_table *atari_utf8_table = &utf8_table;
diff --git a/frontends/atari/encoding.h b/frontends/atari/encoding.h
new file mode 100644
index 000000000..784aa8273
--- /dev/null
+++ b/frontends/atari/encoding.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2012 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_ATARI_ENCODING_H
+#define NS_ATARI_ENCODING_H
+
+#include <inttypes.h>
+#include <assert.h>
+#include <stdbool.h>
+
+#include "utils/utf8.h"
+
+struct gui_utf8_table *atari_utf8_table;
+
+nserror utf8_to_local_encoding(const char *string, size_t len, char **result);
+nserror utf8_from_local_encoding(const char *string, size_t len, char **result);
+
+int atari_to_ucs4( unsigned char atarichar);
+
+#endif
diff --git a/frontends/atari/extract.php b/frontends/atari/extract.php
new file mode 100755
index 000000000..865b70ed7
--- /dev/null
+++ b/frontends/atari/extract.php
@@ -0,0 +1,10 @@
+#!/usr/bin/php
+<?
+$lines = file("deskmenu.c");
+
+foreach($lines as $line){
+ if(stripos($line, "static void __CDECL menu_") === 0){
+ echo $line;
+ }
+}
+?>
diff --git a/frontends/atari/file.c b/frontends/atari/file.c
new file mode 100644
index 000000000..499edd627
--- /dev/null
+++ b/frontends/atari/file.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2014 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "utils/utils.h"
+#include "utils/corestrings.h"
+#include "utils/url.h"
+#include "utils/nsurl.h"
+#include "utils/file.h"
+#include "utils/string.h"
+
+/**
+ * \file
+ * Atari file handling callbacks.
+ *
+ * Most of this code was taken from windows/gui.c
+ */
+
+/**
+ * Generate a GEMDOS path from one or more component elemnts.
+ *
+ * If a string is allocated it must be freed by the caller.
+ *
+ * @param[in,out] str pointer to string pointer if this is NULL enough
+ * storage will be allocated for the complete path.
+ * @param[in,out] size The size of the space available if \a str not
+ * NULL on input and if not NULL set to the total
+ * output length on output.
+ * @param[in] nelm The number of elements.
+ * @param[in] ap The elements of the path as string pointers.
+ * @return NSERROR_OK and the complete path is written to str
+ * or error code on faliure.
+ */
+static nserror atari_mkpath(char **str, size_t *size, size_t nelm, va_list ap)
+{
+ return vsnstrjoin(str, size, '\\', nelm, ap);
+}
+
+/**
+ * Get the basename of a file using GEMDOS path handling.
+ *
+ * This gets the last element of a path and returns it.
+ *
+ * @param[in] path The path to extract the name from.
+ * @param[in,out] str Pointer to string pointer if this is NULL enough
+ * storage will be allocated for the path element.
+ * @param[in,out] size The size of the space available if \a
+ * str not NULL on input and set to the total
+ * output length on output.
+ * @return NSERROR_OK and the complete path is written to str
+ * or error code on faliure.
+ */
+static nserror atari_basename(const char *path, char **str, size_t *size)
+{
+ const char *leafname;
+ char *fname;
+
+ if (path == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ leafname = strrchr(path, '\\');
+ if (!leafname) {
+ leafname = path;
+ } else {
+ leafname += 1;
+ }
+
+ fname = strdup(leafname);
+ if (fname == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ *str = fname;
+ if (size != NULL) {
+ *size = strlen(fname);
+ }
+ return NSERROR_OK;
+}
+
+/**
+ * Create a path from a nsurl using GEMDOS file handling.
+ *
+ * @param[in] url The url to encode.
+ * @param[out] path_out A string containing the result path which should
+ * be freed by the caller.
+ * @return NSERROR_OK and the path is written to \a path or error code
+ * on faliure.
+ */
+static nserror atari_nsurl_to_path(struct nsurl *url, char **path_out)
+{
+ lwc_string *urlpath;
+ char *path;
+ bool match;
+ lwc_string *scheme;
+ nserror res;
+
+ if ((url == NULL) || (path_out == NULL)) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ scheme = nsurl_get_component(url, NSURL_SCHEME);
+
+ if (lwc_string_caseless_isequal(scheme, corestring_lwc_file,
+ &match) != lwc_error_ok)
+ {
+ return NSERROR_BAD_PARAMETER;
+ }
+ lwc_string_unref(scheme);
+ if (match == false) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ urlpath = nsurl_get_component(url, NSURL_PATH);
+ if (urlpath == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ res = url_unescape(lwc_string_data(urlpath), &path);
+ lwc_string_unref(urlpath);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ /* if there is a drive: prefix treat path as DOS filename */
+ if ((path[2] == ':') || (path[2] == '|')) {
+
+ /* move the string down to remove leading / note the
+ * strlen is *not* copying too much data as we are
+ * moving the null too!
+ */
+ memmove(path, path + 1, strlen(path));
+ }
+ /* if the path does not have a drive letter we return the
+ * complete path.
+ */
+ /** @todo Need to check returning the unaltered path in this
+ * case is correct
+ */
+
+ *path_out = path;
+
+ return NSERROR_OK;
+}
+
+/**
+ * Create a nsurl from a path using GEMDOS file handling.
+ *
+ * Perform the necessary operations on a path to generate a nsurl.
+ *
+ * @param[in] path The path to convert.
+ * @param[out] url_out pointer to recive the nsurl, The returned url
+ * should be unreferenced by the caller.
+ * @return NSERROR_OK and the url is placed in \a url or error code on
+ * faliure.
+ */
+static nserror atari_path_to_nsurl(const char *path, struct nsurl **url_out)
+{
+ nserror ret;
+ int urllen;
+ char *urlstr;
+ char *escpath; /* escaped version of the path */
+ char *escpaths;
+
+ if ((path == NULL) || (url_out == NULL) || (*path == 0)) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ /* escape the path so it can be placed in a url */
+ ret = url_escape(path, 0, false, "/", &escpath);
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+ /* remove unecessary / as file: paths are already absolute */
+ escpaths = escpath;
+ while (*escpaths == '/') {
+ escpaths++;
+ }
+
+ /* build url as a string for nsurl constructor */
+ urllen = strlen(escpaths) + FILE_SCHEME_PREFIX_LEN + 1;
+ urlstr = malloc(urllen);
+ if (urlstr == NULL) {
+ free(escpath);
+ return NSERROR_NOMEM;
+ }
+
+ snprintf(urlstr, urllen, "%s%s", FILE_SCHEME_PREFIX, escpaths);
+ free(escpath);
+
+ ret = nsurl_create(urlstr, url_out);
+ free(urlstr);
+
+ return ret;
+}
+
+/**
+ * Ensure that all directory elements needed to store a filename exist.
+ *
+ * @param fname The filename to ensure the path to exists.
+ * @return NSERROR_OK on success or error code on failure.
+ */
+static nserror atari_mkdir_all(const char *fname)
+{
+ char *dname;
+ char *sep;
+ struct stat sb;
+
+ dname = strdup(fname);
+
+ sep = strrchr(dname, '/');
+ if (sep == NULL) {
+ /* no directory separator path is just filename so its ok */
+ free(dname);
+ return NSERROR_OK;
+ }
+
+ *sep = 0; /* null terminate directory path */
+
+ if (stat(dname, &sb) == 0) {
+ free(dname);
+ if (S_ISDIR(sb.st_mode)) {
+ /* path to file exists and is a directory */
+ return NSERROR_OK;
+ }
+ return NSERROR_NOT_DIRECTORY;
+ }
+ *sep = '/'; /* restore separator */
+
+ sep = dname;
+ while (*sep == '/') {
+ sep++;
+ }
+ while ((sep = strchr(sep, '/')) != NULL) {
+ *sep = 0;
+ if (stat(dname, &sb) != 0) {
+ if (nsmkdir(dname, S_IRWXU) != 0) {
+ /* could not create path element */
+ free(dname);
+ return NSERROR_NOT_FOUND;
+ }
+ } else {
+ if (! S_ISDIR(sb.st_mode)) {
+ /* path element not a directory */
+ free(dname);
+ return NSERROR_NOT_DIRECTORY;
+ }
+ }
+ *sep = '/'; /* restore separator */
+ /* skip directory separators */
+ while (*sep == '/') {
+ sep++;
+ }
+ }
+
+ free(dname);
+ return NSERROR_OK;
+}
+
+
+/* atari file handling table */
+static struct gui_file_table file_table = {
+ .mkpath = atari_mkpath,
+ .basename = atari_basename,
+ .nsurl_to_path = atari_nsurl_to_path,
+ .path_to_nsurl = atari_path_to_nsurl,
+ .mkdir_all = atari_mkdir_all,
+};
+
+struct gui_file_table *atari_file_table = &file_table;
+
+
diff --git a/frontends/atari/file.h b/frontends/atari/file.h
new file mode 100644
index 000000000..b368567c7
--- /dev/null
+++ b/frontends/atari/file.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 FILE_C_INCLUDED
+#define FILE_C_INCLUDED
+
+#include "utils/file.h"
+
+struct gui_file_table *atari_file_table;
+
+
+#endif /* FILE_C_INCLUDED */
diff --git a/frontends/atari/filetype.c b/frontends/atari/filetype.c
new file mode 100644
index 000000000..1cce6fc18
--- /dev/null
+++ b/frontends/atari/filetype.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "utils/log.h"
+#include "content/fetch.h"
+
+#include "atari/filetype.h"
+
+/**
+ * filetype -- determine the MIME type of a local file
+ */
+const char *fetch_filetype(const char *unix_path)
+{
+ int l;
+ char * res = (char*)"text/html";
+ l = strlen(unix_path);
+
+ LOG("unix path: %s", unix_path);
+
+ /* This line is added for devlopment versions running from the root dir: */
+ if( strchr( unix_path, (int)'.' ) ){
+ if (2 < l && strcasecmp(unix_path + l - 3, "f79") == 0)
+ res = (char*)"text/css";
+ else if (2 < l && strcasecmp(unix_path + l - 3, "css") == 0)
+ res = (char*)"text/css";
+ else if (2 < l && strcasecmp(unix_path + l - 3, "jpg") == 0)
+ res = (char*)"image/jpeg";
+ else if (3 < l && strcasecmp(unix_path + l - 4, "jpeg") == 0)
+ res = (char*)"image/jpeg";
+ else if (2 < l && strcasecmp(unix_path + l - 3, "gif") == 0)
+ res = (char*)"image/gif";
+ else if (2 < l && strcasecmp(unix_path + l - 3, "png") == 0)
+ res = (char*)"image/png";
+ else if (2 < l && strcasecmp(unix_path + l - 3, "jng") == 0)
+ res = (char*)"image/jng";
+ else if (2 < l && strcasecmp(unix_path + l - 3, "svg") == 0)
+ res = (char*)"image/svg";
+ else if (2 < l && strcasecmp(unix_path + l - 3, "txt") == 0)
+ res = (char*)"text/plain";
+ } else {
+ FILE * fp;
+ char buffer[16];
+ fp = fopen( unix_path, "r" );
+ if( fp ){
+ int n=0;
+ int c;
+ do {
+ c = fgetc (fp);
+ if( c != EOF )
+ buffer[n] = (char)c;
+ else
+ buffer[n] = 0;
+ n++;
+ } while (c != EOF && n<15);
+ fclose( fp );
+ if( n > 0 ){
+ if( n > 5 && strncasecmp("GIF89", buffer, 5) == 0 )
+ res = (char*)"image/gif";
+ else if( n > 4 && strncasecmp("PNG", &buffer[1], 3) ==0 )
+ res = (char*)"image/png";
+ else if( n > 10 && strncasecmp("JFIF", &buffer[5], 4) == 0 )
+ res = (char*)"image/jpeg";
+ }
+ }
+ }
+
+ LOG("mime type: %s", res);
+ return( res );
+}
diff --git a/frontends/atari/filetype.h b/frontends/atari/filetype.h
new file mode 100644
index 000000000..97ba75b80
--- /dev/null
+++ b/frontends/atari/filetype.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_ATARI_FILETYPE_H_
+#define NS_ATARI_FILETYPE_H_
+
+const char *fetch_filetype(const char *unix_path);
+
+#endif
diff --git a/frontends/atari/findfile.c b/frontends/atari/findfile.c
new file mode 100644
index 000000000..45ca6d916
--- /dev/null
+++ b/frontends/atari/findfile.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <unistd.h>
+
+#include "utils/log.h"
+#include "utils/corestrings.h"
+
+#include "atari/gemtk/gemtk.h"
+#include "atari/findfile.h"
+#include "atari/gui.h"
+#include "atari/osspec.h"
+
+char * local_file_to_url( const char * filename )
+{
+ #define BACKSLASH 0x5C
+ char * url;
+
+ LOG("in: %s", filename);
+
+ if( strlen(filename) <= 2){
+ return( NULL );
+ }
+
+ char * fname_local = malloc( strlen(filename)+1 );
+ char * start = (char*)fname_local;
+ strcpy( start, filename );
+
+ /* convert backslashes: */
+ for( unsigned int i=0; i<strlen(start); i++ ){
+ if( start[i] == BACKSLASH ){
+ start[i] = '/';
+ }
+ }
+
+ // TODO: make file path absolute if it isn't yet.
+ url = malloc( strlen(start) + FILE_SCHEME_PREFIX_LEN + 1);
+ strcpy(url, FILE_SCHEME_PREFIX );
+ strcat(url, start );
+
+ free(fname_local);
+
+ LOG("out: %s", url);
+
+ return( url );
+ #undef BACKSLASH
+}
+
+
+/**
+ * Locate a shared resource file by searching known places in order.
+ * Search order is: ./, NETSURF_GEM_RESPATH, ./$HOME/.netsurf/, $NETSURFRES/
+ * (where NETSURFRES is an environment variable)
+ *
+ * \param buf buffer to write to. must be at least PATH_MAX chars
+ * \param filename file to look for
+ * \param def default to return if file not found
+ * \return buf
+ *
+ */
+#ifndef NETSURF_GEM_RESPATH
+ #define NETSURF_GEM_RESPATH "./res/"
+#endif
+
+char * atari_find_resource(char *buf, const char *filename, const char *def)
+{
+ char *cdir = NULL;
+ char t[PATH_MAX];
+ LOG("%s (def: %s)", filename, def);
+ strcpy(t, NETSURF_GEM_RESPATH);
+ strcat(t, filename);
+ LOG("checking %s", (char *)&t);
+ if (gemdos_realpath(t, buf) != NULL) {
+ if (access(buf, R_OK) == 0) {
+ return buf;
+ }
+ }
+ strcpy(t, "./");
+ strcat(t, filename);
+ LOG("checking %s", (char *)&t);
+ if (gemdos_realpath(t, buf) != NULL) {
+ if (access(buf, R_OK) == 0) {
+ return buf;
+ }
+ }
+
+ cdir = getenv("HOME");
+ if (cdir != NULL) {
+ strcpy(t, cdir);
+ strcat(t, "/.netsurf/");
+ strcat(t, filename);
+ LOG("checking %s", (char *)&t);
+ if (gemdos_realpath(t, buf) != NULL) {
+ if (access(buf, R_OK) == 0)
+ return buf;
+ }
+ }
+
+ cdir = getenv("NETSURFRES");
+ if (cdir != NULL) {
+ if (gemdos_realpath(cdir, buf) != NULL) {
+ strcat(buf, "/");
+ strcat(buf, filename);
+ LOG("checking %s", (char *)&t);
+ if (access(buf, R_OK) == 0)
+ return buf;
+ }
+ }
+ if (def[0] == '~') {
+ snprintf(t, PATH_MAX, "%s%s", getenv("HOME"), def + 1);
+ LOG("checking %s", (char *)&t);
+ if (gemdos_realpath(t, buf) == NULL) {
+ strcpy(buf, t);
+ }
+ } else {
+ LOG("checking %s", (char *)def);
+ if (gemdos_realpath(def, buf) == NULL) {
+ strcpy(buf, def);
+ }
+ }
+
+ return buf;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/frontends/atari/findfile.h b/frontends/atari/findfile.h
new file mode 100644
index 000000000..9cda2a0be
--- /dev/null
+++ b/frontends/atari/findfile.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2008 Daniel Silverstone <dsilvers@netsurf-browser.org>
+ * Copyright 2011 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_ATARI_FINDFILE_H
+#define NS_ATARI_FINDFILE_H
+
+extern char *atari_find_resource(char *buf, const char *filename, const char *def);
+char *local_file_to_url(const char *filename);
+
+#endif /* NETSURF_ATARI_FINDFILE_H */
diff --git a/frontends/atari/font.c b/frontends/atari/font.c
new file mode 100644
index 000000000..cb0c574ea
--- /dev/null
+++ b/frontends/atari/font.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <inttypes.h>
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "utils/utf8.h"
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "desktop/gui_layout.h"
+#include "desktop/mouse.h"
+#include "desktop/plotters.h"
+
+#include "atari/gui.h"
+#include "atari/plot/fontplot.h"
+#include "atari/plot/plot.h"
+#include "atari/findfile.h"
+#include "atari/font.h"
+
+extern FONT_PLOTTER fplotter;
+
+
+/**
+ * Find the position in a string where an x coordinate falls.
+ *
+ * \param[in] fstyle style for this text
+ * \param[in] string UTF-8 string to measure
+ * \param[in] length length of string, in bytes
+ * \param[in] x coordinate to search for
+ * \param[out] char_offset updated to offset in string of actual_x, [0..length]
+ * \param[out] actual_x updated to x coordinate of character closest to x
+ * \return NSERROR_OK and char_offset and actual_x updated or appropriate error code on faliure
+ */
+static nserror
+atari_font_position(const plot_font_style_t *fstyle,
+ const char *string,
+ size_t length,
+ int x,
+ size_t *char_offset,
+ int *actual_x)
+{
+ float scale = plot_get_scale();
+
+ if (scale != 1.0) {
+ plot_font_style_t newstyle = *fstyle;
+ newstyle.size = (int)((float)fstyle->size*scale);
+ fplotter->pixel_pos(fplotter, &newstyle, string, length, x,
+ char_offset, actual_x);
+ } else {
+ fplotter->pixel_pos(fplotter, fstyle, string, length, x,
+ char_offset, actual_x);
+ }
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Find where to split a string to make it fit a width.
+ *
+ * \param[in] fstyle style for this text
+ * \param[in] string UTF-8 string to measure
+ * \param[in] length length of string, in bytes
+ * \param[in] x width available
+ * \param[out] char_offset updated to offset in string of actual_x, [1..length]
+ * \param[out] actual_x updated to x coordinate of character closest to x
+ * \return NSERROR_OK or appropriate error code on faliure
+ *
+ * On exit, char_offset indicates first character after split point.
+ *
+ * \note char_offset of 0 must never be returned.
+ *
+ * Returns:
+ * char_offset giving split point closest to x, where actual_x <= x
+ * else
+ * char_offset giving split point closest to x, where actual_x > x
+ *
+ * Returning char_offset == length means no split possible
+ */
+static nserror
+atari_font_split(const plot_font_style_t *fstyle,
+ const char *string,
+ size_t length,
+ int x,
+ size_t *char_offset,
+ int *actual_x)
+{
+ float scale = plot_get_scale();
+
+ if (scale != 1.0) {
+ plot_font_style_t newstyle = *fstyle;
+ newstyle.size = (int)((float)fstyle->size*scale);
+ fplotter->str_split(fplotter, &newstyle, string, length, x,
+ char_offset, actual_x);
+ } else {
+ fplotter->str_split(fplotter, fstyle, string, length, x,
+ char_offset, actual_x);
+ }
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Measure the width of a string.
+ *
+ * \param[in] fstyle plot style for this text
+ * \param[in] string UTF-8 string to measure
+ * \param[in] length length of string, in bytes
+ * \param[out] width updated to width of string[0..length)
+ * \return NSERROR_OK and width updated or appropriate error code on faliure
+ */
+static nserror
+atari_font_width(const plot_font_style_t *fstyle,
+ const char *str,
+ size_t length,
+ int * width)
+{
+ float scale = plot_get_scale();
+
+ if (scale != 1.0) {
+ plot_font_style_t newstyle = *fstyle;
+ newstyle.size = (int)((float)fstyle->size*scale);
+ fplotter->str_width(fplotter, &newstyle, str, length, width);
+ } else {
+ fplotter->str_width(fplotter, fstyle, str, length, width);
+ }
+
+ return NSERROR_OK;
+}
+
+
+static struct gui_layout_table layout_table = {
+ .width = atari_font_width,
+ .position = atari_font_position,
+ .split = atari_font_split,
+};
+
+struct gui_layout_table *atari_layout_table = &layout_table;
diff --git a/frontends/atari/font.h b/frontends/atari/font.h
new file mode 100644
index 000000000..a01d000c0
--- /dev/null
+++ b/frontends/atari/font.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_ATARI_FONT_H
+#define NS_ATARI_FONT_H
+
+struct gui_layout_table *atari_layout_table;
+
+#endif /* NETSURF_FB_FONT_H */
+
diff --git a/frontends/atari/gemtk/aestabs.c b/frontends/atari/gemtk/aestabs.c
new file mode 100644
index 000000000..519676514
--- /dev/null
+++ b/frontends/atari/gemtk/aestabs.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2013 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <assert.h>
+#include <gem.h>
+#include <cflib.h>
+#include "aestabs.h"
+
+#ifndef NDEBUG
+# define DEBUG_PRINT(x) printf x
+#else
+# define DEBUG_PRINT(x)
+#endif
+
+
+AES_TABLIST * tablist_declare(OBJECT *tree, aes_tablist_user_func user_func)
+{
+ AES_TABLIST * newlist = malloc(sizeof(AES_TABLIST));
+
+ newlist->first = NULL;
+ newlist->tree = tree;
+ newlist->user_func = user_func;
+ DEBUG_PRINT(("aes_tablist_declare: %p\n", newlist));
+ return(newlist);
+}
+
+
+AES_TAB * tablist_add(AES_TABLIST * tablist, short obj_tab, OBJECT * page_tree,
+ short obj_page)
+{
+ AES_TAB * newtab = malloc(sizeof(AES_TAB));
+
+ assert(newtab);
+ assert(tablist);
+
+ newtab->next = NULL;
+ newtab->prev = NULL;
+ newtab->obj_tab = obj_tab;
+ newtab->obj_page = obj_page;
+ newtab->page_tree = page_tree;
+
+ if(newtab->page_tree == NULL){
+ newtab->page_tree = tablist->tree;
+ }
+
+ if (tablist->first == NULL) {
+ tablist->first = newtab;
+ set_state(tablist->tree, newtab->obj_tab, OS_SELECTED, 0);
+ } else {
+ AES_TAB *tmp = tablist->first;
+ while( tmp->next != NULL ) {
+ tmp = tmp->next;
+ }
+ tmp->next = newtab;
+ newtab->prev = tmp;
+ newtab->next = NULL;
+ set_state(tablist->tree, newtab->obj_tab, OS_SELECTED, 0);
+ }
+
+ // TODO: Set the visible flag on that register?
+
+ DEBUG_PRINT(("tablist_add: Tab=%p\n", newtab));
+
+ return(newtab);
+}
+
+
+short tablist_activate(AES_TABLIST * tablist, short tab, short options)
+{
+ AES_TAB *tmp, *activated=NULL, *deactivated=NULL;
+ struct aes_tab_s *active;
+ short activated_pg = -1;
+ short is_tab = 0;
+
+ assert(tablist);
+ assert(tablist->first);
+
+ active = tablist_get_active(tablist);
+
+ if (active != NULL) {
+ if ((options & AES_TABLIST_OPTION_FORCE_EVENTS) == 0) {
+ if(active->obj_tab == tab)
+ return(0);
+ }
+ }
+
+ tmp = tablist->first;
+ while (tmp != NULL) {
+ if(tmp->obj_tab == tab) {
+ is_tab = 1;
+ }
+ tmp = tmp->next;
+ }
+
+ if(is_tab == 0) {
+ return(0);
+ }
+
+ tmp = tablist->first;
+ while ( tmp != NULL ) {
+ if(tab != tmp->obj_tab) {
+ if (get_state(tablist->tree, tmp->obj_tab, OS_SELECTED) != 0) {
+ deactivated = tmp;
+ set_state(tablist->tree, tmp->obj_tab, OS_SELECTED, 0);
+ }
+ // the tab registers can share the same page, consider that:
+ if (tablist->tree == tmp->page_tree
+ && activated_pg != tmp->obj_page) {
+
+ set_flag(tablist->tree, tmp->obj_page, OF_HIDETREE, 1);
+ }
+ } else {
+ activated = tmp;
+ // this tab must the selected / visible
+ set_state(tablist->tree, tmp->obj_tab, OS_SELECTED, 1);
+ if(tablist->tree == tmp->page_tree)
+ set_flag(tablist->tree, tmp->obj_page, OF_HIDETREE, 0);
+ activated_pg = tmp->obj_page;
+ }
+ tmp = tmp->next;
+ }
+
+ if(tablist->user_func != NULL) {
+ AES_TABLIST_FUNC_ARGS args;
+ if(deactivated){
+ args.event = AES_TABLIST_TAB_DEACTIVATED;
+ args.tab = deactivated;
+ tablist->user_func(tablist, &args);
+ }
+ if(activated){
+ args.event = AES_TABLIST_TAB_ACTIVATED;
+ args.tab = activated;
+ tablist->user_func(tablist, &args);
+ }
+ }
+ return(1);
+}
+
+struct aes_tab_s *tablist_get_active(AES_TABLIST * tablist)
+{
+ AES_TAB *tmp = tablist->first;
+ while( tmp != NULL ) {
+ if(get_state(tablist->tree, tmp->obj_tab, OS_SELECTED) != 0) {
+ // that's the one
+ return(tmp);
+ }
+ tmp = tmp->next;
+ }
+ return(NULL);
+}
+
+AES_TAB * tablist_find(AES_TABLIST * tablist, OBJECT * page, short tab)
+{
+ AES_TAB *tmp = tablist->first;
+ while( tmp != NULL ) {
+ if((tmp->page_tree == page) && (tab == tmp->obj_tab)) {
+ return(tmp);
+ }
+ tmp = tmp->next;
+ }
+ return(NULL);
+}
+
+void tablist_delete(AES_TABLIST *tablist)
+{
+ AES_TAB *tmp = tablist->first, *cur;
+ while ( tmp != NULL ) {
+ cur = tmp;
+ tmp = tmp->next;
+ DEBUG_PRINT(("tablist_delete, Freeing tab: %p\n", cur));
+ free(cur);
+ }
+ DEBUG_PRINT(("tablist_delete, Freeing list: %p\n", tablist));
+ free(tablist);
+}
diff --git a/frontends/atari/gemtk/aestabs.h b/frontends/atari/gemtk/aestabs.h
new file mode 100644
index 000000000..c72054acc
--- /dev/null
+++ b/frontends/atari/gemtk/aestabs.h
@@ -0,0 +1,56 @@
+#ifndef AESTABS_H_INCLUDED
+#define AESTABS_H_INCLUDED
+
+struct aes_tab_s;
+struct aes_tablist_s;
+typedef struct aes_tab_s AES_TAB;
+typedef struct aes_tablist_s AES_TABLIST;
+
+#define AES_TABLIST_TAB_ACTIVATED 0x01
+#define AES_TABLIST_TAB_DEACTIVATED 0x02
+
+#define AES_TABLIST_OPTION_FORCE_EVENTS 0x01 // do not eat events which do
+ // not changed the internal state
+ // this is required for tabs which
+ // require "activate" events
+ // for tabs which are already
+ // selected.
+
+
+struct aes_tablist_user_args_s
+{
+ short event;
+ AES_TAB *tab;
+};
+
+typedef struct aes_tablist_user_args_s AES_TABLIST_FUNC_ARGS;
+
+typedef void (*aes_tablist_user_func)(AES_TABLIST * list,
+ AES_TABLIST_FUNC_ARGS * args);
+
+struct aes_tab_s {
+ short obj_tab;
+ short obj_page;
+ OBJECT * page_tree;
+ AES_TAB * next, *prev;
+};
+
+struct aes_tablist_s {
+ OBJECT *tree;
+ AES_TAB * first;
+ aes_tablist_user_func user_func;
+};
+
+
+
+AES_TABLIST * tablist_declare(OBJECT *tree, aes_tablist_user_func user_func);
+void tablist_delete(AES_TABLIST * tablist);
+AES_TAB * tablist_add(AES_TABLIST * tablist, short tab, OBJECT *page_tree,
+ short page);
+short tablist_activate(AES_TABLIST * tablist, short tab, short option);
+struct aes_tab_s *tablist_get_active(AES_TABLIST * tablist);
+AES_TAB * tablist_find(AES_TABLIST * tablist, OBJECT *page, short tab);
+
+#define AES_TAB_IS_ACTIVE(l, x) (tablist_get_active(l) == x)
+
+#endif // AESTABS_H_INCLUDED
diff --git a/frontends/atari/gemtk/dragdrop.c b/frontends/atari/gemtk/dragdrop.c
new file mode 100755
index 000000000..a4b7b82a3
--- /dev/null
+++ b/frontends/atari/gemtk/dragdrop.c
@@ -0,0 +1,515 @@
+/*
+* Routine pour Drag and drop sous MultiTos
+* source: D&D Atari, demo OEP (Alexander Lorenz)
+*
+* Struktur OEP (oep.apid): AES-ID der Applikation
+*
+* (Tab = 4)
+*/
+
+#ifdef __PUREC__
+#include <tos.h>
+#else
+#include <osbind.h>
+#include <mintbind.h>
+#include <signal.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+
+#include "gemtk.h"
+#include "cflib.h"
+
+#ifndef EACCDN
+#define EACCDN (-36)
+#endif
+
+#ifndef FA_HIDDEN
+#define FA_HIDDEN 0x02
+#endif
+
+static char pipename[] = "U:\\PIPE\\DRAGDROP.AA";
+static long pipesig;
+
+/*
+* Routinen fr den Sender
+*/
+
+/*
+* Erzeugt Pipe fr D&D
+*
+* Eingabeparameter:
+* pipe - Pointer auf 2 Byte Buffer fr Pipeextension
+*
+* Ausgabeparameters:
+* keine
+*
+* Returnwert:
+* >0: Filehandle der Pipe
+* -1: Fehler beim Erzeugen der Pipe
+*/
+
+short gemtk_dd_create(short *pipe)
+{
+ long fd = -1;
+
+ pipename[17] = 'A';
+ pipename[18] = 'A' - 1;
+
+ do /* ouvre un pipe inoccup‚ */
+ {
+ pipename[18]++;
+ if (pipename[18] > 'Z')
+ {
+ pipename[17]++;
+ if (pipename[17] > 'Z')
+ break;
+ else
+ pipename[18] = 'A';
+ }
+
+ /* FA_HIDDEN fr Pipe notwendig! */
+
+ fd = Fcreate(pipename, FA_HIDDEN);
+
+ } while (fd == (long) EACCDN);
+
+ if (fd < 0L)
+ return(-1);
+
+ *pipe = (pipename[17] << 8) | pipename[18];
+
+
+ /* Signalhandler konfigurieren */
+
+ gemtk_dd_getsig(&pipesig);
+
+
+ return((short) fd);
+}
+
+
+
+/*
+* Sendet AP_DRAGDROP an Empf„ngerapplikation
+*
+* Eingabeparameter:
+* apid - AES-ID der Emf„ngerapp.
+* fd - Filehandle der D&D-Pipe
+* winid - Handle des Zielfensters (0 fr Desktopfenster)
+* mx/my - Maus X und Y Koord.
+* (-1/-1 fr einen fake Drag&Drop)
+* kstate - Sondertastenstatus
+* pipename - Extension der D&D-Pipe
+*
+* Ausgabeparameter:
+* keine
+*
+* Returnwert:
+* >0: kein Fehler
+* -1: Empf„ngerapp. gibt DD_NAK zurck
+* -2: Empf„ngerapp. antwortet nicht (Timeout)
+* -3: Fehler bei appl_write()
+*/
+
+short gemtk_dd_message(short apid, short fd, short winid, short mx, short my, short kstate, short pipeid)
+{
+ char c;
+ short i, msg[8];
+ long fd_mask;
+
+
+ /* AES-Message define and post */
+
+ msg[0] = AP_DRAGDROP;
+ msg[1] = _AESapid;
+ msg[2] = 0;
+ msg[3] = winid;
+ msg[4] = mx;
+ msg[5] = my;
+ msg[6] = kstate;
+ msg[7] = pipeid;
+
+ i = appl_write(apid, 16, msg);
+
+ if (i == 0)
+ {
+ gemtk_dd_close(fd);
+ return(-3);
+ }
+
+
+ /* receiver reaction */
+
+ fd_mask = (1L << fd);
+ i = Fselect(DD_TIMEOUT, &fd_mask, 0L, 0L);
+ if (!i || !fd_mask)
+ {
+ /* Timeout eingetreten */
+
+ gemtk_dd_close(fd);
+ return(-2);
+ }
+
+
+ /* le recepteur refuse (lecture du pipe) */
+
+ if (Fread(fd, 1L, &c) != 1L)
+ {
+ gemtk_dd_close(fd);
+ return(-1);
+ }
+
+ if (c != DD_OK)
+ {
+ gemtk_dd_close(fd);
+ return(-1);
+ }
+
+ return(1);
+}
+
+
+
+/*
+* Liest die 8 "bevorzugten" Extensionen der Empf„ngerapplikation
+*
+* Eingabeparameter:
+* fd - Filehandle der D&D-Pipe
+*
+* Ausgabeparameters:
+* exts - 32 Bytebuffer fr die 8 bevorzugten Extensionen
+* der Zielapp.
+*
+* Returnwert:
+* >0: kein Fehler
+* -1: Fehler beim Lesen aus der Pipe
+*/
+
+short gemtk_dd_rexts(short fd, char *exts)
+{
+ if (Fread(fd, DD_EXTSIZE, exts) != DD_EXTSIZE)
+ {
+ gemtk_dd_close(fd);
+ return(-1);
+ }
+
+ return(1);
+}
+
+
+
+/*
+* Testet, ob der Empf„nger einen Datentyp akzeptiert
+*
+* Eingabeparameter:
+* fd - Filehandle (von gemtk_dd_create())
+* ext - Zeiger auf Datentyp (4 Bytes zB. "ARGS")
+* text - Zeiger auf Datenbeschreibung (optional, zB. "DESKTOP args")
+* name - Zeiger auf Datendateiname (optional, zB. "SAMPLE.TXT")
+* size - Anzahl Bytes der zu sendenden Daten
+*
+* Ausgabeparameter:
+* keine
+*
+* Returnwert:
+* DD_OK - Empf„nger akzeptiert Datentyp
+* DD_NAK - Empf„nger brach Drag&Drop ab
+* DD_EXT - Empf„nger lehnt Datentyp ab
+* DD_LEN - Empf„nger kann Datenmenge nicht verarbeiten
+* DD_TRASH - Drop erfolgte auf Mlleimer
+* DD_PRINTER - Drop erfolgte auf Drucker
+* DD_CLIPBOARD - Drop erfolgte auf Clipboard
+*/
+
+short gemtk_dd_stry(short fd, char *ext, char *text, char *name, long size)
+{
+ char c;
+ short hdrlen, i;
+
+ /* 4 Bytes fr "ext", 4 Bytes fr "size",
+ 2 Bytes fr Stringendnullen */
+
+ hdrlen = (short) (4 + 4 + strlen(text)+1 + strlen(name)+1);
+
+
+ /* Header senden */
+
+ if (Fwrite(fd, 2L, &hdrlen) != 2L)
+ return(DD_NAK);
+
+ i = (short) Fwrite(fd, 4L, ext);
+ i += (short) Fwrite(fd, 4L, &size);
+ i += (short) Fwrite(fd, strlen(text)+1, text);
+ i += (short) Fwrite(fd, strlen(name)+1, name);
+
+ if (i != hdrlen)
+ return(DD_NAK);
+
+
+ /* auf die Antwort warten */
+
+ if (Fread(fd, 1L, &c) != 1L)
+ return(DD_NAK);
+
+ return(c);
+}
+
+
+
+/* Routinen fr Sender und Empf„nger */
+
+/*
+* Pipe schliežen (Drag&Drop beenden/abbrechen)
+*/
+
+void gemtk_dd_close(short fd)
+{
+ /* Signalhandler restaurieren */
+
+ gemtk_dd_setsig(pipesig);
+
+
+ Fclose(fd);
+}
+
+
+/*
+* Signalhandler fr D&D konfigurieren
+*
+* Eingabeparameter:
+* oldsig - Zeiger auf 4 Byte Puffer fr alten Handlerwert
+*
+* Ausgabeparameter:
+* keine
+*
+* Returnwerte:
+* keine
+*/
+
+void gemtk_dd_getsig(long *oldsig)
+{
+ *oldsig = (long) Psignal(SIGPIPE, (void *) SIG_IGN);
+}
+
+
+/*
+* Signalhandler nach D&D restaurieren
+*
+* Eingabeparameter:
+* oldsig - Alter Handlerwert (von gemtk_dd_getsig)
+*
+* Ausgabeparameter:
+* keine
+*
+* Returnwerte:
+* keine
+*/
+
+void gemtk_dd_setsig(long oldsig)
+{
+ if (oldsig != -32L)
+ Psignal(SIGPIPE, (void *) oldsig);
+}
+
+
+
+/* Routinen fr Empf„nger */
+
+/*
+* Drag&Drop Pipe ”ffnen
+*
+* Eingabeparameter:
+* ddnam - Extension der Pipe (letztes short von AP_DRAGDROP)
+* ddmsg - DD_OK oder DD_NAK
+*
+* Ausgabeparameter:
+* keine
+*
+* Returnwerte:
+* >0 - Filehandle der Drag&Drop pipe
+* -1 - Drag&Drop abgebrochen
+*/
+
+short gemtk_dd_open(short ddnam, char ddmsg)
+{
+ long fd;
+
+ pipename[17] = (ddnam & 0xff00) >> 8;
+ pipename[18] = ddnam & 0x00ff;
+
+ fd = Fopen(pipename, 2);
+
+ if (fd < 0L)
+ return(-1);
+
+
+ /* Signalhandler konfigurieren */
+
+ gemtk_dd_getsig(&pipesig);
+
+
+ if (Fwrite((short) fd, 1L, &ddmsg) != 1L)
+ {
+ gemtk_dd_close((short) fd);
+ return(-1);
+ }
+
+ return((short) fd);
+}
+
+
+
+/*
+* Schreibt die 8 "bevorzugten" Extensionen der Applikation
+*
+* Eingabeparameter:
+* fd - Filehandle der D&D-Pipe
+* exts - Liste aus acht 4 Byte Extensionen die verstanden
+* werden. Diese Liste sollte nach bevorzugten Datentypen
+* sortiert sein. Sollten weniger als DD_NUMEXTS
+* Extensionen untersttzt werden, muž die Liste mit
+* Nullen (0) aufgefllt werden!
+*
+* Ausgabeparameter:
+* keine
+*
+* Returnwert:
+* >0: kein Fehler
+* -1: Fehler beim Schreiben in die Pipe
+*/
+
+short gemtk_dd_sexts(short fd, char *exts)
+{
+ if (Fwrite(fd, DD_EXTSIZE, exts) != DD_EXTSIZE)
+ {
+ gemtk_dd_close(fd);
+ return(-1);
+ }
+
+ return(1);
+}
+
+
+
+/*
+* N„chsten Header vom Sender holen
+*
+* Eingabeparameter:
+* fd - Filehandle der Pipe (von gemtk_dd_open())
+*
+* Ausgabeparameters:
+* name - Zeiger auf Buffer fr Datenbeschreibung (min. DD_NAMEMAX!)
+* file - Zeiger auf Buffer fr Datendateiname (min. DD_NAMEMAX!)
+* whichext- Zeiger auf Buffer fr Extension (4 Bytes)
+* size - Zeiger auf Buffer fr Datengr”že (4 Bytes)
+*
+* Returnwert:
+* >0: kein Fehler
+* -1: Sender brach Drag&Drop ab
+*
+* On lit dans le pipe qui normalement est constitu‚ de:
+* 1 short: taille du header
+* 4 CHAR: type de donn‚e (extension)
+* 1 long: taille des donn‚es
+* STRING: description des donn‚es
+* STRING: nom du fichiers
+* soit au minimun 11 octets (cas ou les string sont r‚duites … \0)
+* les string sont limit‚ a 128 octets
+*/
+
+short gemtk_dd_rtry(short fd, char *name, char *file, char *whichext, long *size)
+{
+ char buf[DD_NAMEMAX * 2];
+ short hdrlen, i, len;
+
+ if (Fread(fd, 2L, &hdrlen) != 2L)
+ return(-1);
+
+
+ if (hdrlen < 9) /* il reste au minimum 11 - 2 = 9 octets a lire */
+ {
+ /* sollte eigentlich nie passieren */
+
+ return(-1); /* erreur taille incorrecte */
+ }
+
+ if (Fread(fd, 4L, whichext) != 4L) /* lecture de l'extension */
+ return(-1);
+
+ if (Fread(fd, 4L, size) != 4L) /* lecture de la longueurs des donn‚es */
+ return(-1);
+
+ hdrlen -= 8; /* on a lu 8 octets */
+
+ if (hdrlen > DD_NAMEMAX*2)
+ i = DD_NAMEMAX*2;
+ else
+ i = hdrlen;
+
+ len = i;
+
+ if (Fread(fd, (long) i, buf) != (long) i)
+ return(-1);
+
+ hdrlen -= i;
+
+ strncpy(name, buf, DD_NAMEMAX);
+
+ i = (short) strlen(name) + 1;
+
+ if (len - i > 0)
+ strncpy(file, buf + i, DD_NAMEMAX);
+ else
+ file[0] = '\0';
+
+
+ /* weitere Bytes im Header in den Mll */
+
+ while (hdrlen > DD_NAMEMAX*2)
+ {
+ if (Fread(fd, DD_NAMEMAX*2, buf) != DD_NAMEMAX*2)
+ return(-1);
+
+ hdrlen -= DD_NAMEMAX*2;
+ }
+
+ if (hdrlen > 0)
+ {
+ if (Fread(fd, (long) hdrlen, buf) != (long) hdrlen)
+ return(-1);
+ }
+
+ return(1);
+}
+
+
+
+/*
+* Sendet der Senderapplikation eine 1 Byte Antwort
+*
+* Eingabeparameter:
+* fd - Filehandle der Pipe (von gemtk_dd_open())
+* ack - Byte das gesendet werden soll (zB. DD_OK)
+*
+* Ausgabeparameter:
+* keine
+*
+* Returnwert:
+* >0: kein Fehler
+* -1: Fehler (die Pipe wird automatisch geschlossen!)
+*/
+
+short gemtk_dd_reply(short fd, char ack)
+{
+ if (Fwrite(fd, 1L, &ack) != 1L)
+ {
+ gemtk_dd_close(fd);
+ return(-1);
+ }
+
+ return(1);
+}
+
+
diff --git a/frontends/atari/gemtk/dragdrop.h b/frontends/atari/gemtk/dragdrop.h
new file mode 100755
index 000000000..38466137b
--- /dev/null
+++ b/frontends/atari/gemtk/dragdrop.h
@@ -0,0 +1,4 @@
+#ifndef DD_H_INCLUDED
+#define DD_H_INCLUDED
+
+#endif
diff --git a/frontends/atari/gemtk/gemtk.h b/frontends/atari/gemtk/gemtk.h
new file mode 100644
index 000000000..e5915e6eb
--- /dev/null
+++ b/frontends/atari/gemtk/gemtk.h
@@ -0,0 +1,298 @@
+#ifndef GEMTK_H_INCLUDED
+#define GEMTK_H_INCLUDED
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <mint/osbind.h>
+#include <mint/cookie.h>
+
+#include <gem.h>
+#include <cflib.h>
+
+
+/* -------------------------------------------------------------------------- */
+/* SYSTEM UTILS */
+/* -------------------------------------------------------------------------- */
+
+/* System type detection added by [GS] */
+/* detect the system type, AES + kernel */
+#define SYS_TOS 0x0001
+#define SYS_MAGIC 0x0002
+#define SYS_MINT 0x0004
+#define SYS_GENEVA 0x0010
+#define SYS_NAES 0x0020
+#define SYS_XAAES 0x0040
+#define sys_type() (_systype_v ? _systype_v : _systype())
+#define sys_MAGIC() ((sys_type() & SYS_MAGIC) != 0)
+#define sys_NAES() ((sys_type() & SYS_NAES) != 0)
+#define sys_XAAES() ((sys_type() & SYS_XAAES) != 0)
+
+#define TOS4VER 0x03300 /* this is assumed to be the last single tasking OS */
+
+extern unsigned short _systype_v;
+unsigned short _systype (void);
+
+/* GEMTK Utils API: */
+
+#define GEMTK_DBG_GRECT(s,g) \
+ printf("%s", s); \
+ printf("\tx0: %d, \n", (g)->g_x); \
+ printf("\ty0: %d, \n", (g)->g_y); \
+ printf("\tx1: %d, \n", (g)->g_x+(g)->g_w); \
+ printf("\ty1: %d, \n", (g)->g_y+(g)->g_h); \
+ printf("\tw: %d, \n", (g)->g_w); \
+ printf("\th: %d \n", (g)->g_h); \
+
+/*
+* Chech for GRECT intersection without modifiend the src rectangles
+* return true when the GRECT's intersect, fals otherwise.
+*/
+bool gemtk_rc_intersect_ro(GRECT *a, GRECT *b);
+
+/*
+* Convert keycode returned by evnt_multi to ascii value
+*/
+int gemtk_keybd2ascii( int keybd, int shift);
+
+/** set VDI clip area by passing an GRECT */
+void gemtk_clip_grect(VdiHdl vh, GRECT *rect);
+
+void gemtk_wind_get_str(short aes_handle, short mode, char *str, int len);
+
+/* send application message */
+void gemtk_send_msg(short msg_type, short data2, short data3, short data4,
+ short data5, short data6, short data7);
+
+
+#ifndef POINT_WITHIN
+# define POINT_WITHIN(_x,_y, r) ((_x >= r.g_x) && (_x <= r.g_x + r.g_w ) \
+ && (_y >= r.g_y) && (_y <= r.g_y + r.g_h))
+#endif
+
+#ifndef RC_WITHIN
+# define RC_WITHIN(a,b) \
+ (((a)->g_x >= (b)->g_x) \
+ && (((a)->g_x + (a)->g_w) <= ((b)->g_x + (b)->g_w))) \
+ && (((a)->g_y >= (b)->g_y) \
+ && (((a)->g_y + (a)->g_h) <= ((b)->g_y + (b)->g_h)))
+#endif
+
+#ifndef MAX
+# define MAX(_a,_b) ((_a>_b) ? _a : _b)
+#endif
+
+#ifndef MIN
+# define MIN(_a,_b) ((_a<_b) ? _a : _b)
+#endif
+
+#ifndef SET_BIT
+# define SET_BIT(field,bit,val) field = (val)?((field)|(bit)):((field) & ~(bit))
+#endif
+
+/* -------------------------------------------------------------------------- */
+/* MultiTOS Drag & Drop */
+/* -------------------------------------------------------------------------- */
+short gemtk_dd_create(short *pipe);
+short gemtk_dd_message(short apid, short fd, short winid, short mx, short my, short kstate, short pipename);
+short gemtk_dd_rexts(short fd, char *exts);
+short gemtk_dd_stry(short fd, char *ext, char *text, char *name, long size);
+void gemtk_dd_close(short fd);
+void gemtk_dd_getsig(long *oldsig);
+void gemtk_dd_setsig(long oldsig);
+short gemtk_dd_open(short ddnam, char ddmsg);
+short gemtk_dd_sexts(short fd, char *exts);
+short gemtk_dd_rtry(short fd, char *name, char *file, char *whichext, long *size);
+short gemtk_dd_reply(short fd, char ack);
+
+/* -------------------------------------------------------------------------- */
+/* AV/VA Protocol Module */
+/* -------------------------------------------------------------------------- */
+int gemtk_av_init(const char *appname);
+void gemtk_av_exit(void);
+bool gemtk_av_send (short message, const char * data1, const char * data2);
+bool gemtk_av_dispatch (short msg[8]);
+
+/* -------------------------------------------------------------------------- */
+/* Message Box module */
+/* -------------------------------------------------------------------------- */
+#define GEMTK_MSG_BOX_ALERT 1
+#define GEMTK_MSG_BOX_CONFIRM 2
+
+short gemtk_msg_box_show(short type, const char * msg);
+
+/* -------------------------------------------------------------------------- */
+/* GUIWIN Module */
+/* -------------------------------------------------------------------------- */
+#define GEMTK_WM_FLAG_PREPROC_WM 0x01 // let guiwin API handle some events
+#define GEMTK_WM_FLAG_RECV_PREPROC_WM 0x02 // get notified even when pre-processed
+#define GEMTK_WM_FLAG_HAS_VTOOLBAR 0x04 // the attached toolbar is vertical
+#define GEMTK_WM_FLAG_CUSTOM_TOOLBAR 0x08 // no internal toolbar handling
+ // (Except considering it's size)
+#define GEMTK_WM_FLAG_CUSTOM_SCROLLING 0x20 // no internal scroller handling
+
+#define GEMTK_WM_FLAG_DEFAULTS \
+ (GEMTK_WM_FLAG_PREPROC_WM | GEMTK_WM_FLAG_RECV_PREPROC_WM)
+
+#define GEMTK_WM_STATUS_ICONIFIED 0x01
+#define GEMTK_WM_STATUS_SHADED 0x02
+
+#define GEMTK_WM_VSLIDER 0x01
+#define GEMTK_WM_HSLIDER 0x02
+#define GEMTK_WM_VH_SLIDER 0x03
+
+/*
+ Message sent to the client application when an AES object is
+ clicked in an window which contains an form.
+
+ Message Parameters:
+ msg[4] = Clicked Object.
+ msg[5] = Number of clicks.
+ msg[6] = Modifier keys.
+*/
+#define GEMTK_WM_WM_FORM_CLICK 1001
+#define GEMTK_WM_WM_FORM_KEY 1002
+
+struct gemtk_window_s;
+
+/** list struct for managing AES windows */
+typedef struct gemtk_window_s GUIWIN;
+
+/** GUIWIN event handler */
+typedef short (*gemtk_wm_event_handler_f)(GUIWIN *gw,
+ EVMULT_OUT *ev_out, short msg[8]);
+
+typedef void (*gemtk_wm_redraw_f)(GUIWIN *win, uint16_t msg, GRECT *clip);
+
+struct gemtk_wm_scroll_info_s {
+
+ /** Definition of a content unit (horizontal) measured in pixel */
+ int x_unit_px;
+
+ /** Definition of content unit (vertical) measured in pixel */
+ int y_unit_px;
+
+ /** Current scroll position (in content units) */
+ int x_pos;
+
+ /** Current scroll position (in content units) */
+ int y_pos;
+
+ /** Size of content (horizontal) measured in content units */
+ int x_units;
+
+ /** Size of content (vertical) measured in content units */
+ int y_units;
+};
+
+/** Well known areas inside the window */
+enum guwin_area_e {
+ GEMTK_WM_AREA_WORK = 0,
+ GEMTK_WM_AREA_TOOLBAR,
+ GEMTK_WM_AREA_CONTENT
+};
+
+/* -------------------------------------------------------------------------- */
+/* GUIWIN functions (document in guiwin.c) */
+/* -------------------------------------------------------------------------- */
+
+short
+gemtk_wm_init(void);
+
+void gemtk_wm_exit(void);
+
+GUIWIN * gemtk_wm_add(short handle, uint32_t flags,
+ gemtk_wm_event_handler_f handler);
+
+GUIWIN * gemtk_wm_find(short handle);
+
+void gemtk_wm_dump_window_info(GUIWIN *win);
+
+short gemtk_wm_remove(GUIWIN *win);
+
+GUIWIN * gemtk_wm_validate_ptr(GUIWIN *win);
+
+GUIWIN *gemtk_wm_link(GUIWIN *win);
+
+GUIWIN *gemtk_wm_unlink(GUIWIN *win);
+
+short gemtk_wm_dispatch_event(EVMULT_IN *ev_in, EVMULT_OUT *ev_out, short msg[8]);
+
+void gemtk_wm_get_grect(GUIWIN *win, enum guwin_area_e mode, GRECT *dest);
+
+short gemtk_wm_get_toolbar_edit_obj(GUIWIN *win);
+
+short gemtk_wm_get_handle(GUIWIN *win);
+
+uint32_t gemtk_wm_get_state(GUIWIN *win);
+
+void gemtk_wm_set_toolbar(GUIWIN *win, OBJECT *toolbar, short idx,
+ uint32_t flags);
+
+void gemtk_wm_set_event_handler(GUIWIN *win,gemtk_wm_event_handler_f cb);
+
+void gemtk_wm_set_user_data(GUIWIN *win, void *data);
+
+void * gemtk_wm_get_user_data(GUIWIN *win);
+
+struct gemtk_wm_scroll_info_s * gemtk_wm_get_scroll_info(GUIWIN *win);
+
+void gemtk_wm_set_scroll_grid(GUIWIN * win, short x, short y);
+
+void gemtk_wm_set_content_units(GUIWIN * win, short x, short y);
+
+void gemtk_wm_set_form(GUIWIN *win, OBJECT *tree, short index);
+
+void gemtk_wm_set_toolbar_size(GUIWIN *win, uint16_t s);
+
+void gemtk_wm_set_toolbar_edit_obj(GUIWIN *win, uint16_t obj, short kreturn);
+
+void gemtk_wm_set_toolbar_redraw_func(GUIWIN *win, gemtk_wm_redraw_f func);
+
+bool gemtk_wm_update_slider(GUIWIN *win, short mode);
+
+void gemtk_wm_scroll(GUIWIN *gw, short orientation, int units, bool refresh);
+
+void gemtk_wm_send_msg(GUIWIN *win, short msgtype, short a, short b, short c,
+ short d);
+
+short gemtk_wm_exec_msg(GUIWIN *win, short msg_type, short a, short b, short c,
+ short d);
+
+void gemtk_wm_exec_redraw(GUIWIN *win, GRECT *area);
+
+VdiHdl gemtk_wm_get_vdi_handle(GUIWIN *win);
+
+short getm_wm_get_toolbar_edit_obj(GUIWIN *win);
+
+bool gemtk_wm_has_intersection(GUIWIN *win, GRECT *work);
+
+void gemtk_wm_toolbar_redraw(GUIWIN *win, uint16_t msg, GRECT *clip);
+
+void gemtk_wm_form_redraw(GUIWIN *gw, GRECT *clip);
+
+void gemtk_wm_clear(GUIWIN *win);
+
+/* -------------------------------------------------------------------------- */
+/* AES SCROLLER MODULE */
+/* -------------------------------------------------------------------------- */
+
+/* -------------------------------------------------------------------------- */
+/* AES TABS MODULE */
+/* -------------------------------------------------------------------------- */
+
+/* -------------------------------------------------------------------------- */
+/* AES OBJECT TREE TOOLS */
+/* -------------------------------------------------------------------------- */
+char gemtk_obj_set_str_safe(OBJECT * tree, short idx, const char *txt);
+char *gemtk_obj_get_text(OBJECT * tree, short idx);
+GRECT * gemtk_obj_screen_rect(OBJECT * tree, short obj);
+bool gemtk_obj_is_inside(OBJECT * tree, short obj, GRECT *area);
+OBJECT *gemtk_obj_get_tree(int idx);
+void gemtk_obj_mouse_sprite(OBJECT *tree, int index);
+OBJECT *gemtk_obj_tree_copy(OBJECT *tree);
+OBJECT * gemtk_obj_create_popup_tree(const char **items, int nitems,
+ char * selected, bool horizontal,
+ int max_width, int max_height);
+void gemtk_obj_destroy_popup_tree(OBJECT * popup);
+#endif // GEMTK_H_INCLUDED
diff --git a/frontends/atari/gemtk/guiwin.c b/frontends/atari/gemtk/guiwin.c
new file mode 100644
index 000000000..ea0f8f917
--- /dev/null
+++ b/frontends/atari/gemtk/guiwin.c
@@ -0,0 +1,1430 @@
+/*
+ * Copyright 2012 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdint.h>
+#include <stdbool.h>
+#include <assert.h>
+
+#include <gem.h>
+#include <gemx.h>
+#include <cflib.h>
+
+#include "gemtk.h"
+#include "vaproto.h"
+
+//#define DEBUG_PRINT(x) printf x
+#define DEBUG_PRINT(x)
+
+struct gemtk_window_s {
+
+ /** The AES handle of the window */
+ short handle;
+
+ /** the generic event handler function for events passed to the client */
+ gemtk_wm_event_handler_f handler_func;
+
+ /** The toolbar redraw function, if any */
+ gemtk_wm_redraw_f toolbar_redraw_func;
+
+ /** window configuration */
+ uint32_t flags;
+
+ /** window state */
+ uint32_t state;
+
+ /** AES Tree used as toolbar */
+ OBJECT *toolbar;
+
+ /** Current edit object selected in the toolbar, if any. */
+ short toolbar_edit_obj;
+
+ /** Current selected object in the toolbar, if any. */
+ short toolbar_focus_obj;
+
+ /** Describes the start of the toolbar tree (usually 0) */
+ short toolbar_idx;
+
+ /** depending on the flag GEMTK_WM_FLAG_HAS_VTOOLBAR this defines the toolbar
+ height or the toolbar width (GEMTK_WM_FLAG_HAS_VTOOLBAR means width).
+ */
+ short toolbar_size;
+
+ /** AES Object tree to be used for windowed dialogs. */
+ OBJECT *form;
+
+ /** Current form edit object, if any. */
+ short form_edit_obj;
+
+ /** Current form focus object, if any */
+ short form_focus_obj;
+
+ /** Describes the start of the form tree */
+ short form_idx;
+
+ /** Scroll state */
+ struct gemtk_wm_scroll_info_s scroll_info;
+
+ /** Arbitary data set by the user */
+ void *user_data;
+
+ /** linked list items */
+ struct gemtk_window_s *next, *prev;
+};
+
+static GUIWIN * winlist;
+static VdiHdl v_vdi_h = -1;
+static short work_out[57];
+
+static void move_rect(GUIWIN * win, GRECT *rect, int dx, int dy)
+{
+ INT16 xy[ 8];
+ long dum = 0L;
+ GRECT g;
+
+ VdiHdl vh = gemtk_wm_get_vdi_handle(win);
+
+ while(!wind_update(BEG_UPDATE));
+ graf_mouse(M_OFF, 0L);
+
+ /* get intersection with screen area */
+ wind_get_grect(0, WF_CURRXYWH, &g);
+ if(!rc_intersect(&g, rect)){
+ goto error;
+ }
+ xy[0] = rect->g_x;
+ xy[1] = rect->g_y;
+ xy[2] = xy[0] + rect->g_w-1;
+ xy[3] = xy[1] + rect->g_h-1;
+ xy[4] = xy[0] + dx;
+ xy[5] = xy[1] + dy;
+ xy[6] = xy[2] + dx;
+ xy[7] = xy[3] + dy;
+ vro_cpyfm(vh, S_ONLY, xy, (MFDB *)&dum, (MFDB *)&dum);
+
+error:
+ graf_mouse(M_ON, 0L);
+ wind_update(END_UPDATE);
+}
+
+/**
+* Handles common events.
+* returns 0 when the event was not handled, 1 otherwise.
+*/
+static short preproc_wm(GUIWIN * gw, EVMULT_OUT *ev_out, short msg[8])
+{
+ GRECT g, g_ro, g2;
+ short retval = 1;
+ int val = 1;
+ struct gemtk_wm_scroll_info_s *slid;
+
+ switch(msg[0]) {
+
+ case WM_HSLID:
+ gemtk_wm_get_grect(gw, GEMTK_WM_AREA_CONTENT, &g);
+ wind_set(gw->handle, WF_HSLIDE, msg[4], 0, 0, 0);
+ slid = gemtk_wm_get_scroll_info(gw);
+ val = (float)(slid->x_units-(g.g_w/slid->x_unit_px))/1000*(float)msg[4];
+ if(val != slid->x_pos) {
+ if (val < slid->x_pos) {
+ val = -(MAX(0, slid->x_pos-val));
+ } else {
+ val = val-slid->x_pos;
+ }
+ gemtk_wm_scroll(gw, GEMTK_WM_HSLIDER, val, false);
+ }
+ break;
+
+ case WM_VSLID:
+ gemtk_wm_get_grect(gw, GEMTK_WM_AREA_CONTENT, &g);
+ wind_set(gw->handle, WF_VSLIDE, msg[4], 0, 0, 0);
+ slid = gemtk_wm_get_scroll_info(gw);
+ val = (float)(slid->y_units-(g.g_h/slid->y_unit_px))/1000*(float)msg[4];
+ if(val != slid->y_pos) {
+ if (val < slid->y_pos) {
+ val = -(slid->y_pos - val);
+ } else {
+ val = val -slid->y_pos;
+ }
+ gemtk_wm_scroll(gw, GEMTK_WM_VSLIDER, val, false);
+ }
+ break;
+
+ case WM_ARROWED:
+ if((gw->flags & GEMTK_WM_FLAG_CUSTOM_SCROLLING) == 0) {
+
+ slid = gemtk_wm_get_scroll_info(gw);
+ gemtk_wm_get_grect(gw, GEMTK_WM_AREA_CONTENT, &g);
+ g_ro = g;
+
+ switch(msg[4]) {
+
+ case WA_UPPAGE:
+ /* scroll page up */
+ gemtk_wm_scroll(gw, GEMTK_WM_VSLIDER, -(g.g_h/slid->y_unit_px),
+ true);
+ break;
+
+ case WA_UPLINE:
+ /* scroll line up */
+ gemtk_wm_scroll(gw, GEMTK_WM_VSLIDER, -1, true);
+ break;
+
+ case WA_DNPAGE:
+ /* scroll page down */
+ gemtk_wm_scroll(gw, GEMTK_WM_VSLIDER, g.g_h/slid->y_unit_px,
+ true);
+ break;
+
+ case WA_DNLINE:
+ /* scroll line down */
+ gemtk_wm_scroll(gw, GEMTK_WM_VSLIDER, +1, true);
+ break;
+
+ case WA_LFPAGE:
+ /* scroll page left */
+ gemtk_wm_scroll(gw, GEMTK_WM_HSLIDER, -(g.g_w/slid->x_unit_px),
+ true);
+ break;
+
+ case WA_LFLINE:
+ /* scroll line left */
+ gemtk_wm_scroll(gw, GEMTK_WM_HSLIDER, -1,
+ true);
+ break;
+
+ case WA_RTPAGE:
+ /* scroll page right */
+ gemtk_wm_scroll(gw, GEMTK_WM_HSLIDER, (g.g_w/slid->x_unit_px),
+ true);
+ break;
+
+ case WA_RTLINE:
+ /* scroll line right */
+ gemtk_wm_scroll(gw, GEMTK_WM_HSLIDER, 1,
+ true);
+ break;
+
+ default:
+ break;
+ }
+ }
+ break;
+
+ case WM_TOPPED:
+ wind_set(gw->handle, WF_TOP, 1, 0, 0, 0);
+ break;
+
+ case WM_MOVED:
+ wind_get_grect(gw->handle, WF_CURRXYWH, &g);
+ wind_set(gw->handle, WF_CURRXYWH, msg[4], msg[5], g.g_w, g.g_h);
+
+ if (gw->form) {
+
+ gemtk_wm_get_grect(gw, GEMTK_WM_AREA_CONTENT, &g);
+ slid = gemtk_wm_get_scroll_info(gw);
+
+ gw->form[gw->form_idx].ob_x = g.g_x -
+ (slid->x_pos * slid->x_unit_px);
+
+ gw->form[gw->form_idx].ob_y = g.g_y -
+ (slid->y_pos * slid->y_unit_px);
+ }
+
+ break;
+
+ case WM_SIZED:
+ case WM_REPOSED:
+ wind_get_grect(gw->handle, WF_FULLXYWH, &g2);
+ wind_get_grect(gw->handle, WF_CURRXYWH, &g);
+ g.g_w = MIN(msg[6], g2.g_w);
+ g.g_h = MIN(msg[7], g2.g_h);
+ if(g2.g_w != g.g_w || g2.g_h != g.g_h) {
+ wind_set(gw->handle, WF_CURRXYWH, g.g_x, g.g_y, g.g_w, g.g_h);
+ if((gw->flags & GEMTK_WM_FLAG_CUSTOM_SCROLLING) == 0) {
+ if(gemtk_wm_update_slider(gw, GEMTK_WM_VH_SLIDER)) {
+ gemtk_wm_exec_redraw(gw, NULL);
+ }
+ }
+ }
+
+
+ break;
+
+ case WM_FULLED:
+ wind_get_grect(DESKTOP_HANDLE, WF_WORKXYWH, &g);
+ wind_get_grect(gw->handle, WF_CURRXYWH, &g2);
+ if(g.g_w == g2.g_w && g.g_h == g2.g_h) {
+ wind_get_grect(gw->handle, WF_PREVXYWH, &g);
+ }
+ wind_set_grect(gw->handle, WF_CURRXYWH, &g);
+ if((gw->flags & GEMTK_WM_FLAG_CUSTOM_SCROLLING) == 0) {
+ if(gemtk_wm_update_slider(gw, GEMTK_WM_VH_SLIDER)) {
+ gemtk_wm_exec_redraw(gw, NULL);
+ }
+ }
+ break;
+
+ case WM_ICONIFY:
+ wind_set(gw->handle, WF_ICONIFY, msg[4], msg[5], msg[6], msg[7]);
+ gw->state |= GEMTK_WM_STATUS_ICONIFIED;
+ break;
+
+ case WM_UNICONIFY:
+ wind_set(gw->handle, WF_UNICONIFY, msg[4], msg[5], msg[6], msg[7]);
+ gw->state &= ~(GEMTK_WM_STATUS_ICONIFIED);
+ break;
+
+ case WM_SHADED:
+ gw->state |= GEMTK_WM_STATUS_SHADED;
+ break;
+
+ case WM_UNSHADED:
+ gw->state &= ~(GEMTK_WM_STATUS_SHADED);
+ break;
+
+ case WM_REDRAW:
+ if ((gw->flags & GEMTK_WM_FLAG_CUSTOM_TOOLBAR) == 0
+ && (gw->toolbar != NULL)) {
+ g.g_x = msg[4];
+ g.g_y = msg[5];
+ g.g_w = msg[6];
+ g.g_h = msg[7];
+ if((gw->state & GEMTK_WM_STATUS_ICONIFIED) == 0){
+ gemtk_wm_toolbar_redraw(gw, WM_REDRAW, &g);
+ }
+ }
+ if (gw->form != NULL) {
+ g.g_x = msg[4];
+ g.g_y = msg[5];
+ g.g_w = msg[6];
+ g.g_h = msg[7];
+ gemtk_wm_form_redraw(gw, &g);
+ }
+ break;
+
+ default:
+ retval = 0;
+ break;
+
+ }
+
+ return(retval);
+}
+
+/**
+* Preprocess mouse events
+*/
+static short preproc_mu_button(GUIWIN * gw, EVMULT_OUT *ev_out, short msg[8])
+{
+ short retval = 0, obj_idx = 0;
+
+ DEBUG_PRINT(("preproc_mu_button\n"));
+
+ // toolbar handling:
+ if ((gw->flags & GEMTK_WM_FLAG_CUSTOM_TOOLBAR) == 0
+ && gw->toolbar != NULL) {
+
+ GRECT tb_area;
+
+ gemtk_wm_get_grect(gw, GEMTK_WM_AREA_TOOLBAR, &tb_area);
+
+ if (POINT_WITHIN(ev_out->emo_mouse.p_x,
+ ev_out->emo_mouse.p_y, tb_area)) {
+
+ gw->toolbar[gw->toolbar_idx].ob_x = tb_area.g_x;
+ gw->toolbar[gw->toolbar_idx].ob_y = tb_area.g_y;
+ obj_idx = objc_find(gw->toolbar,
+ gw->toolbar_idx, 8,
+ ev_out->emo_mouse.p_x,
+ ev_out->emo_mouse.p_y);
+
+ gw->toolbar_focus_obj = obj_idx;
+
+ DEBUG_PRINT(("Toolbar index: %d\n", obj_idx));
+ if (obj_idx > -1
+ && (gw->toolbar[obj_idx].ob_state & OS_DISABLED)== 0
+ && ((gw->flags & GEMTK_WM_FLAG_CUSTOM_TOOLBAR) == 0)) {
+
+ uint16_t type = (gw->toolbar[obj_idx].ob_type & 0xFF);
+ uint16_t nextobj;
+
+ DEBUG_PRINT(("toolbar item type: %d, toolbar_edit_obj: %d\n",
+ type, gw->toolbar_edit_obj));
+ // Report mouse click to the tree:
+ retval = form_wbutton(gw->toolbar, gw->toolbar_focus_obj,
+ ev_out->emo_mclicks, &nextobj,
+ gw->handle);
+ if (nextobj == obj_idx
+ && (type == G_FTEXT || type == G_FBOXTEXT)) {
+ gw->toolbar_edit_obj = obj_idx;
+ }
+ else {
+ gw->toolbar_edit_obj = -1;
+ }
+ }
+
+ // send WM_TOOLBAR message
+ short oldevents = ev_out->emo_events;
+ short msg_out[8] = {WM_TOOLBAR, gl_apid,
+ 0, gw->handle,
+ obj_idx, ev_out->emo_mclicks,
+ ev_out->emo_kmeta, ev_out->emo_mbutton
+ };
+ ev_out->emo_events = MU_MESAG;
+ // notify the window about toolbar click:
+ gw->handler_func(gw, ev_out, msg_out);
+ ev_out->emo_events = oldevents;
+ retval = 1;
+ } else {
+ if (gw->toolbar_edit_obj != -1) {
+ gw->toolbar_edit_obj = -1;
+ }
+ }
+ }
+
+ if (gw->form != NULL) {
+
+ GRECT content_area;
+ struct gemtk_wm_scroll_info_s *slid;
+
+ DEBUG_PRINT(("preproc_mu_button: handling form click.\n"));
+
+ gemtk_wm_get_grect(gw, GEMTK_WM_AREA_CONTENT, &content_area);
+
+ if (POINT_WITHIN(ev_out->emo_mouse.p_x,
+ ev_out->emo_mouse.p_y, content_area)) {
+
+ slid = gemtk_wm_get_scroll_info(gw);
+
+ // adjust form position (considering window and scroll position):
+ gw->form[gw->form_idx].ob_x = content_area.g_x -
+ (slid->x_pos * slid->x_unit_px);
+ gw->form[gw->form_idx].ob_y = content_area.g_y -
+ (slid->y_pos * slid->y_unit_px);
+
+ obj_idx = objc_find(gw->form, gw->form_idx, 8,
+ ev_out->emo_mouse.p_x, ev_out->emo_mouse.p_y);
+ gw->form_focus_obj = obj_idx;
+ DEBUG_PRINT(("Window Form click, obj: %d\n", gw->form_focus_obj));
+ if (obj_idx > -1
+ && (gw->form[obj_idx].ob_state & OS_DISABLED)== 0) {
+
+ uint16_t type = (gw->form[obj_idx].ob_type & 0xFF);
+ uint16_t nextobj;
+
+ DEBUG_PRINT(("type: %d\n", type));
+
+ retval = form_wbutton(gw->form, gw->form_focus_obj,
+ ev_out->emo_mclicks, &nextobj,
+ gw->handle);
+
+ if (nextobj == obj_idx
+ && (type == G_FTEXT || type == G_FBOXTEXT)) {
+ gw->form_edit_obj = obj_idx;
+ }
+ else {
+ gw->form_edit_obj = -1;
+ }
+
+ short oldevents = ev_out->emo_events;
+ short msg_out[8] = { GEMTK_WM_WM_FORM_CLICK, gl_apid,
+ 0, gw->handle,
+ gw->form_focus_obj, ev_out->emo_mclicks,
+ ev_out->emo_kmeta, 0
+ };
+ ev_out->emo_events = MU_MESAG;
+ // notify the window about form click:
+ gw->handler_func(gw, ev_out, msg_out);
+ ev_out->emo_events = oldevents;
+ retval = 1;
+ evnt_timer(150);
+ }
+ }
+ else {
+ gw->form_edit_obj = -1;
+
+ }
+ }
+
+ return(retval);
+}
+
+/**
+* Preprocess keyboard events (for forms/toolbars)
+*/
+static short preproc_mu_keybd(GUIWIN * gw, EVMULT_OUT *ev_out, short msg[8])
+{
+ short retval = 0;
+
+ if ((gw->toolbar != NULL) && (gw->toolbar_edit_obj > -1)) {
+
+ short next_edit_obj = gw->toolbar_edit_obj;
+ short next_char = -1;
+ short edit_idx;
+ short r;
+
+ DEBUG_PRINT(("%s, gw: %p, toolbar_edit_obj: %d\n", __FUNCTION__, gw,
+ gw->toolbar_edit_obj));
+
+ r = form_wkeybd(gw->toolbar, gw->toolbar_edit_obj, next_edit_obj,
+ ev_out->emo_kreturn,
+ &next_edit_obj, &next_char, gw->handle);
+
+ if (next_edit_obj != gw->toolbar_edit_obj) {
+ gemtk_wm_set_toolbar_edit_obj(gw, next_edit_obj,
+ ev_out->emo_kreturn);
+ } else {
+ if (next_char > 13) {
+ r = objc_wedit(gw->toolbar, gw->toolbar_edit_obj,
+ ev_out->emo_kreturn, &edit_idx,
+ EDCHAR, gw->handle);
+ }
+ }
+ //retval = 1;
+ /*gemtk_wm_send_msg(gw, GEMTK_WM_WM_FORM_KEY, gw->toolbar_edit_obj,
+ ev_out->emo_kreturn, 0, 0);*/
+ }
+
+ if((gw->form != NULL) && (gw->form_edit_obj > -1) ) {
+
+ short next_edit_obj = gw->form_edit_obj;
+ short next_char = -1;
+ short edit_idx;
+ short r;
+
+ r = form_wkeybd(gw->form, gw->form_edit_obj, next_edit_obj,
+ ev_out->emo_kreturn,
+ &next_edit_obj, &next_char, gw->handle);
+
+ if (next_edit_obj != gw->form_edit_obj) {
+
+ if(gw->form_edit_obj != -1) {
+ objc_wedit(gw->form, gw->form_edit_obj,
+ ev_out->emo_kreturn, &edit_idx,
+ EDEND, gw->handle);
+ }
+
+ gw->form_edit_obj = next_edit_obj;
+
+ objc_wedit(gw->form, gw->form_edit_obj,
+ ev_out->emo_kreturn, &edit_idx,
+ EDINIT, gw->handle);
+ } else {
+ if(next_char > 13)
+ r = objc_wedit(gw->form, gw->form_edit_obj,
+ ev_out->emo_kreturn, &edit_idx,
+ EDCHAR, gw->handle);
+ }
+ }
+ return(retval);
+}
+
+/**
+* Default toolbar redraw function
+*/
+static void std_toolbar_redraw(GUIWIN *gw, uint16_t msg, GRECT *clip)
+{
+ GRECT g, tb_area;
+
+ gemtk_wm_get_grect(gw, GEMTK_WM_AREA_TOOLBAR, &tb_area);
+
+ assert(gw->toolbar);
+ assert(gw->toolbar_idx >= 0);
+
+ // Update object position:
+ gw->toolbar[gw->toolbar_idx].ob_x = tb_area.g_x;
+ gw->toolbar[gw->toolbar_idx].ob_y = tb_area.g_y;
+ gw->toolbar[gw->toolbar_idx].ob_width = tb_area.g_w;
+ gw->toolbar[gw->toolbar_idx].ob_height = tb_area.g_h;
+
+ wind_get_grect(gw->handle, WF_FIRSTXYWH, &g);
+ while (g.g_h > 0 || g.g_w > 0) {
+ if(rc_intersect(clip, &g)) {
+ objc_draw(gw->toolbar, gw->toolbar_idx, 8, g.g_x, g.g_y,
+ g.g_w, g.g_h);
+
+ }
+ wind_get_grect(gw->handle, WF_NEXTXYWH, &g);
+ }
+}
+
+/**
+* Event Dispatcher function. The guiwin API doesn't own an event loop,
+* so you have to inform it for every event that you want it to handle.
+*/
+short gemtk_wm_dispatch_event(EVMULT_IN *ev_in, EVMULT_OUT *ev_out, short msg[8])
+{
+ GUIWIN *dest;
+ short retval = 0;
+ bool handler_called = false;
+
+ if( (ev_out->emo_events & MU_MESAG) != 0 ) {
+ DEBUG_PRINT(("gemtk_wm_handle_event_multi_fast: %d (%x)\n", msg[0],
+ msg[0]));
+ switch (msg[0]) {
+ case WM_REDRAW:
+ case WM_CLOSED:
+ case WM_TOPPED:
+ case WM_ARROWED:
+ case WM_HSLID:
+ case WM_VSLID:
+ case WM_FULLED:
+ case WM_SIZED:
+ case WM_REPOSED:
+ case WM_MOVED:
+ case WM_NEWTOP:
+ case WM_UNTOPPED:
+ case WM_ONTOP:
+ case WM_BOTTOM:
+ case WM_ICONIFY:
+ case WM_UNICONIFY:
+ case WM_ALLICONIFY:
+ case WM_TOOLBAR:
+ case AP_DRAGDROP:
+ case AP_TERM:
+ case AP_TFAIL:
+ dest = gemtk_wm_find(msg[3]);
+ if (dest) {
+ DEBUG_PRINT(("Found WM_ dest: %p (%d), flags: %d, cb: %p\n",
+ dest, dest->handle, dest->flags,
+ dest->handler_func));
+ if (dest->flags&GEMTK_WM_FLAG_PREPROC_WM) {
+ retval = preproc_wm(dest, ev_out, msg);
+ if(((retval == 0)||(dest->flags&GEMTK_WM_FLAG_RECV_PREPROC_WM))) {
+ retval = dest->handler_func(dest, ev_out, msg);
+ handler_called = true;
+ }
+ } else {
+ if (dest->handler_func) {
+ retval = dest->handler_func(dest, ev_out, msg);
+ handler_called = true;
+ }
+ }
+
+ }
+ break;
+// TODO: check code with Thing! Desktop
+/*
+ We receive VA_PROTOSTATUS but AV_START doesn't seem to cause
+ an TeraDesk response. Check if something happens with Thing!
+ Desktop.
+
+ case VA_PROTOSTATUS:
+ case VA_VIEWED:
+ case AV_STARTED:
+ gemtk_av_dispatch(msg);
+ break;
+*/
+ }
+ } else {
+
+ short h_aes;
+ h_aes = wind_find(ev_out->emo_mouse.p_x, ev_out->emo_mouse.p_y);
+ if(h_aes > 0 && (ev_out->emo_events != MU_TIMER)) {
+
+ dest = gemtk_wm_find(h_aes);
+
+ if (dest == NULL || dest->handler_func == NULL)
+ return(0);
+
+ DEBUG_PRINT(("Found Event receiver GUIWIN: %p (%d), flags: %d, cb: %p\n",
+ dest, dest->handle, dest->flags, dest->handler_func));
+
+ if ((ev_out->emo_events & MU_BUTTON) != 0) {
+ DEBUG_PRINT(("gemtk_wm_handle_event_multi_fast: MU_BUTTON -> "
+ "%d / %d\n", ev_out->emo_mouse.p_x,
+ ev_out->emo_mouse.p_y));
+ retval = preproc_mu_button(dest, ev_out, msg);
+ if(retval != 0) {
+ handler_called = true;
+ }
+ }
+
+ if ((ev_out->emo_events & MU_KEYBD)) {
+ DEBUG_PRINT(("gemtk_wm_handle_event_multi_fast: MU_KEYBD -> %x\n",
+ ev_out->emo_kreturn));
+ retval = preproc_mu_keybd(dest, ev_out, msg);
+ if(retval != 0) {
+ handler_called = true;
+ }
+ }
+
+ if (handler_called==false) {
+ retval = dest->handler_func(dest, ev_out, msg);
+ }
+ }
+ }
+
+ return(retval);
+}
+
+/**
+* Initialises the guiwin API
+*/
+short gemtk_wm_init(void)
+{
+ if(v_vdi_h == -1) {
+ short dummy;
+ short work_in[12] = {Getrez()+2,1,1,1,1,1,1,1,1,1,2,1};
+ v_vdi_h=graf_handle(&dummy, &dummy, &dummy, &dummy);
+ v_opnvwk(work_in, &v_vdi_h, work_out);
+ }
+ return(0);
+}
+
+void gemtk_wm_exit(void)
+{
+ v_clsvwk(v_vdi_h);
+}
+
+/**
+* Adds and AES handle to the guiwin list and creates and GUIWIN management
+* structure.
+*
+* \param handle The AES handle
+* \param flags Creation flags, configures how the AES window is handled
+* \param cb event handler function for that window
+*/
+GUIWIN * gemtk_wm_add(short handle, uint32_t flags, gemtk_wm_event_handler_f cb)
+{
+
+ GUIWIN *win = calloc(1, sizeof(GUIWIN));
+
+ assert(win!=NULL);
+ DEBUG_PRINT(("gemtk_wm_add: %d, %p, cb: %p\n", handle, win, cb));
+
+ win->handle = handle;
+ win->handler_func = cb;
+ win->flags = flags;
+ gemtk_wm_link(win);
+
+ DEBUG_PRINT(("Added guiwin: %p, tb: %p\n", win, win->toolbar));
+ return(win);
+}
+
+/**
+* Returns an GUIWIN* for AES handle, when that AES window is managed by gemtk_wm
+*/
+GUIWIN *gemtk_wm_find(short handle)
+{
+ GUIWIN *g;
+ DEBUG_PRINT(("guiwin search handle: %d\n", handle));
+ for (g = winlist; g != NULL; g=g->next) {
+ if(g->handle == handle) {
+ DEBUG_PRINT(("guiwin found handle: %p\n", g));
+ return(g);
+ }
+ }
+ return(NULL);
+}
+
+void gemtk_wm_dump_window_info(GUIWIN *win)
+{
+
+
+
+ char title[255];
+ GRECT work_area;
+ GRECT curr_area;
+ GRECT gemtk_work_area;
+ GRECT gemtk_toolbar_area;
+ GRECT gemtk_free_area;
+ short handle;
+ struct gemtk_wm_scroll_info_s *slid;
+
+ handle = gemtk_wm_get_handle(win);
+
+ assert(handle);
+
+ gemtk_wind_get_str(handle, WF_NAME, title, 255);
+ wind_get_grect(handle, WF_WORKXYWH, &work_area);
+ wind_get_grect(handle, WF_CURRXYWH, &curr_area);
+ gemtk_wm_get_grect(win, GEMTK_WM_AREA_CONTENT, &gemtk_free_area);
+ gemtk_wm_get_grect(win, GEMTK_WM_AREA_WORK, &gemtk_work_area);
+ gemtk_wm_get_grect(win, GEMTK_WM_AREA_TOOLBAR, &gemtk_toolbar_area);
+ slid = gemtk_wm_get_scroll_info(win);
+
+ printf ("GEMTK Window: %p (AES handle: %d)\n", win, win->handle);
+ printf ("Title: %s\n", title);
+ GEMTK_DBG_GRECT ("WF_WORKXYWH: \n", &work_area)
+ GEMTK_DBG_GRECT ("WF_CURRXYWH: \n", &curr_area)
+ GEMTK_DBG_GRECT ("GEMTK_WM_AREA_CONTENT:\n", &gemtk_free_area)
+ GEMTK_DBG_GRECT ("GEMTK_WM_AREA_WORK:\n", &gemtk_work_area)
+ GEMTK_DBG_GRECT ("GEMTK_WM_AREA_TOOLBAR:\n", &gemtk_toolbar_area)
+ printf ("Slider X pos: %d\n", slid->x_pos);
+ printf ("Slider Y pos: %d\n", slid->y_pos);
+ printf ("Slider X units: %d\n", slid->x_unit_px);
+ printf ("Slider Y units: %d\n", slid->y_unit_px);
+
+
+#undef DBG_GRECT
+};
+
+/**
+* Check's if the pointer is managed by the guiwin API.
+*/
+GUIWIN *gemtk_wm_validate_ptr(GUIWIN *win)
+{
+ GUIWIN *g;
+ for( g = winlist; g != NULL; g=g->next ) {
+ DEBUG_PRINT(("guiwin gemtk_wm_validate_ptr check: %p\n", g));
+ if(g == win) {
+ DEBUG_PRINT(("gemtk_wm_validate_ptr valid: %p\n", g));
+ return(g);
+ }
+ }
+ return(NULL);
+}
+
+/**
+* Add the GUIWIN to the list of handled windows.
+*/
+GUIWIN *gemtk_wm_link(GUIWIN *win)
+{
+ /* Make sure the window is not linked: */
+ GUIWIN *win_val = gemtk_wm_validate_ptr(win);
+ if(win_val){
+ DEBUG_PRINT(("GUIWIN %p is already linked!\n", win));
+ return(NULL);
+ }
+
+ if (winlist == NULL) {
+ winlist = win;
+ win->next = NULL;
+ win->prev = NULL;
+ } else {
+ GUIWIN *tmp = winlist;
+ while( tmp->next != NULL ) {
+ tmp = tmp->next;
+ }
+ tmp->next = win;
+ win->prev = tmp;
+ win->next = NULL;
+ }
+ return(win);
+}
+
+/**
+* Remove the GUIWIN from the list of handled windows.
+*/
+GUIWIN *gemtk_wm_unlink(GUIWIN *win)
+{
+ GUIWIN * win_val;
+
+ /* Make sure the window is linked: */
+ win_val = gemtk_wm_validate_ptr(win);
+ if (win_val == NULL){
+ DEBUG_PRINT(("GUIWIN %p is not linked!\n", win));
+ return(NULL);
+ }
+
+
+ /* unlink the window: */
+ if(win->prev != NULL ) {
+ win->prev->next = win->next;
+ } else {
+ winlist = win->next;
+ }
+ if (win->next != NULL) {
+ win->next->prev = win->prev;
+ }
+ return(win);
+}
+
+/**
+* Remove an GUIWIN from the list of managed windows and free the GUIWIN.
+* Call this when the AES window is closed or deleted.
+*/
+short gemtk_wm_remove(GUIWIN *win)
+{
+ gemtk_wm_unlink(win);
+ DEBUG_PRINT(("guiwin free: %p\n", win));
+ free(win);
+ return(0);
+}
+
+/** Calculate & get a well known area within the GUIWIN.
+* \param win The GUIWIN ptr.
+* \param mode Specifies the area to retrieve.
+* \param dest The calculated rectangle.
+*/
+void gemtk_wm_get_grect(GUIWIN *win, enum guwin_area_e mode, GRECT *dest)
+{
+
+ assert(win != NULL);
+
+ wind_get_grect(win->handle, WF_WORKXYWH, dest);
+
+ if (mode == GEMTK_WM_AREA_CONTENT) {
+ GRECT tb_area;
+ gemtk_wm_get_grect(win, GEMTK_WM_AREA_TOOLBAR, &tb_area);
+ if (win->flags & GEMTK_WM_FLAG_HAS_VTOOLBAR) {
+ dest->g_x += tb_area.g_w;
+ dest->g_w -= tb_area.g_w;
+ }
+ else {
+ dest->g_y += tb_area.g_h;
+ dest->g_h -= tb_area.g_h;
+ }
+ } else if (mode == GEMTK_WM_AREA_TOOLBAR) {
+ if (win->toolbar) {
+ if (win->flags & GEMTK_WM_FLAG_HAS_VTOOLBAR) {
+ dest->g_w = win->toolbar_size;
+ } else {
+ dest->g_h = win->toolbar_size;
+ }
+ }
+ else {
+ dest->g_w = 0;
+ dest->g_h = 0;
+ }
+ }
+}
+
+
+/**
+* Scroll the content area (GEMTK_WM_AREA_CONTENT) in the specified dimension
+* (GEMTK_WM_VSLIDER, GEMTK_WM_HSLIDER)
+* \param win The GUIWIN
+* \param orientation GEMTK_WM_VSLIDER or GEMTK_WM_HSLIDER
+* \param units the amout to scroll (pass negative values to scroll up)
+* \param refresh Sliders will be updated when this flag is set
+*/
+void gemtk_wm_scroll(GUIWIN *win, short orientation, int units, bool refresh)
+{
+ struct gemtk_wm_scroll_info_s *slid = gemtk_wm_get_scroll_info(win);
+ int oldpos = 0, newpos = 0, vis_units=0, pix = 0;
+ int abs_pix = 0;
+ GRECT *redraw=NULL, g, g_ro;
+
+ gemtk_wm_get_grect(win, GEMTK_WM_AREA_CONTENT, &g);
+ g_ro = g;
+
+ if (orientation == GEMTK_WM_VSLIDER) {
+ pix = units*slid->y_unit_px;
+ abs_pix = abs(pix);
+ oldpos = slid->y_pos;
+ vis_units = g.g_h/slid->y_unit_px;
+ newpos = slid->y_pos = MIN(slid->y_units-vis_units,
+ MAX(0, slid->y_pos+units));
+ if(newpos < 0) {
+ newpos = slid->y_pos = 0;
+ }
+ if(oldpos == newpos)
+ return;
+
+ if (units>=vis_units || gemtk_wm_has_intersection(win, &g_ro)) {
+ // send complete redraw
+ redraw = &g_ro;
+ } else {
+ // only adjust ypos when scrolling down:
+ if(pix < 0 ) {
+ // blit screen area:
+ g.g_h -= abs_pix;
+ move_rect(win, &g, 0, abs_pix);
+ g.g_y = g_ro.g_y;
+ g.g_h = abs_pix;
+ redraw = &g;
+ } else {
+ // blit screen area:
+ g.g_y += abs_pix;
+ g.g_h -= abs_pix;
+ move_rect(win, &g, 0, -abs_pix);
+ g.g_y = g_ro.g_y + g_ro.g_h - abs_pix;
+ g.g_h = abs_pix;
+ redraw = &g;
+ }
+ }
+ } else {
+ pix = units*slid->x_unit_px;
+ abs_pix = abs(pix);
+ oldpos = slid->x_pos;
+ vis_units = g.g_w/slid->x_unit_px;
+ newpos = slid->x_pos = MIN(slid->x_units-vis_units,
+ MAX(0, slid->x_pos+units));
+
+ if(newpos < 0) {
+ newpos = slid->x_pos = 0;
+ }
+
+ if(oldpos == newpos)
+ return;
+ if (units>=vis_units || gemtk_wm_has_intersection(win, &g_ro)) {
+ // send complete redraw
+ redraw = &g_ro;
+ } else {
+ // only adjust ypos when scrolling down:
+ if(pix < 0 ) {
+ // blit screen area:
+ g.g_w -= abs_pix;
+ move_rect(win, &g, abs_pix, 0);
+ g.g_x = g_ro.g_x;
+ g.g_w = abs_pix;
+ redraw = &g;
+ } else {
+ // blit screen area:
+ g.g_x += abs_pix;
+ g.g_w -= abs_pix;
+ move_rect(win, &g, -abs_pix, 0);
+ g.g_x = g_ro.g_x + g_ro.g_w - abs_pix;
+ g.g_w = abs_pix;
+ redraw = &g;
+ }
+ }
+ }
+
+ if (refresh) {
+ gemtk_wm_update_slider(win, orientation);
+ }
+
+ if ((redraw != NULL) && (redraw->g_h > 0)) {
+ gemtk_wm_exec_redraw(win, redraw);
+ }
+}
+
+/**
+* Refresh the sliders of the window.
+* \param win the GUIWIN
+* \param mode bitmask, valid bits: GEMTK_WM_VSLIDER, GEMTK_WM_HSLIDER
+*/
+bool gemtk_wm_update_slider(GUIWIN *win, short mode)
+{
+ GRECT viewport;
+ struct gemtk_wm_scroll_info_s * slid;
+ unsigned long size, pos;
+ int old_x, old_y;
+
+ short handle = gemtk_wm_get_handle(win);
+ gemtk_wm_get_grect(win, GEMTK_WM_AREA_CONTENT, &viewport);
+ slid = gemtk_wm_get_scroll_info(win);
+
+ old_x = slid->x_pos;
+ old_y = slid->y_pos;
+
+ // TODO: check if the window has sliders of that direction...?
+
+ if((mode & GEMTK_WM_VSLIDER) && (slid->y_unit_px > 0)) {
+ if ( slid->y_units < (long)viewport.g_h/slid->y_unit_px) {
+ size = 1000L;
+ } else
+ size = MAX( 50L, (unsigned long)viewport.g_h*1000L/
+ (unsigned long)(slid->y_unit_px*slid->y_units));
+ wind_set(handle, WF_VSLSIZE, (int)size, 0, 0, 0);
+
+ if (slid->y_units > (long)viewport.g_h/slid->y_unit_px) {
+ pos = (unsigned long)slid->y_pos *1000L/
+ (unsigned long)(slid->y_units-viewport.g_h/slid->y_unit_px);
+ wind_set(handle, WF_VSLIDE, (int)pos, 0, 0, 0);
+ } else if (slid->y_pos) {
+ slid->y_pos = 0;
+ wind_set(handle, WF_VSLIDE, 0, 0, 0, 0);
+ }
+ }
+ if((mode & GEMTK_WM_HSLIDER) && (slid->x_unit_px > 0)) {
+ if ( slid->x_units < (long)viewport.g_w/slid->x_unit_px)
+ size = 1000L;
+ else
+ size = MAX( 50L, (unsigned long)viewport.g_w*1000L/
+ (unsigned long)(slid->x_unit_px*slid->x_units));
+ wind_set(handle, WF_HSLSIZE, (int)size, 0, 0, 0);
+
+ if( slid->x_units > (long)viewport.g_w/slid->x_unit_px) {
+ pos = (unsigned long)slid->x_pos*1000L/
+ (unsigned long)(slid->x_units-viewport.g_w/slid->x_unit_px);
+ wind_set(handle, WF_HSLIDE, (int)pos, 0, 0, 0);
+ } else if (slid->x_pos) {
+ slid->x_pos = 0;
+ wind_set(handle, WF_HSLIDE, 0, 0, 0, 0);
+ }
+ }
+
+ if(old_x != slid->x_pos || old_y != slid->y_pos) {
+ return(true);
+ }
+ return(false);
+}
+
+/**
+* Return the AES handle for the GUIWIN.
+*/
+short gemtk_wm_get_handle(GUIWIN *win)
+{
+ return(win->handle);
+}
+
+/**
+* Return the VDI handle for an GUIWIN.
+*/
+VdiHdl gemtk_wm_get_vdi_handle(GUIWIN *win)
+{
+ return(v_vdi_h);
+}
+
+/**
+* Returns the state bitmask of the window
+*/
+uint32_t gemtk_wm_get_state(GUIWIN *win)
+{
+ return(win->state);
+}
+
+/**
+* Set and new event handler function.
+*/
+void gemtk_wm_set_event_handler(GUIWIN *win,gemtk_wm_event_handler_f cb)
+{
+ win->handler_func = cb;
+}
+
+/**
+* Configure the window so that it shows an toolbar or at least reserves
+* an area to draw an toolbar.
+* \param win The GUIWIN
+* \param toolbar the AES form
+* \param idx index within the toolbar tree (0 in most cases...)
+* \param flags optional configuration flags
+*/
+//TODO: document flags
+void gemtk_wm_set_toolbar(GUIWIN *win, OBJECT *toolbar, short idx, uint32_t flags)
+{
+ win->toolbar = toolbar;
+ win->toolbar_idx = idx;
+ win->toolbar_edit_obj = -1;
+ if (flags & GEMTK_WM_FLAG_HAS_VTOOLBAR) {
+ win->flags |= GEMTK_WM_FLAG_HAS_VTOOLBAR;
+ win->toolbar_size = win->toolbar[idx].ob_width;
+ }
+ else {
+ win->toolbar_size = win->toolbar[idx].ob_height;
+ }
+ gemtk_wm_set_toolbar_redraw_func(win, std_toolbar_redraw);
+}
+
+/** Update width/height of the toolbar region
+* \param win the GUIWIN
+* \param s the width or height, depending on the flag GEMTK_WM_FLAG_HAS_VTOOLBAR
+*/
+void gemtk_wm_set_toolbar_size(GUIWIN *win, uint16_t s)
+{
+ win->toolbar_size = s;
+}
+
+short gemtk_wm_get_toolbar_edit_obj(GUIWIN *win)
+{
+ return(win->toolbar_edit_obj);
+}
+
+/** Set the current active edit object */
+void gemtk_wm_set_toolbar_edit_obj(GUIWIN *win, uint16_t obj, short kreturn)
+{
+ short edit_idx;
+
+ DEBUG_PRINT(("%s, win: %p, obj: %d, kret: %d\n", __FUNCTION__,
+ win, obj, kreturn));
+
+ if (obj != win->toolbar_edit_obj) {
+
+ DEBUG_PRINT(("%s, current edit obj: %d\n", __FUNCTION__,
+ win->toolbar_edit_obj));
+
+ if(win->toolbar_edit_obj != -1) {
+ objc_wedit(win->toolbar, win->toolbar_edit_obj, kreturn, &edit_idx,
+ EDEND, win->handle);
+ }
+
+ win->toolbar_edit_obj = obj;
+
+ objc_wedit(win->toolbar, win->toolbar_edit_obj, kreturn, &edit_idx,
+ EDINIT, win->handle);
+ } else {
+ DEBUG_PRINT(("%s, nothing to do!\n", __FUNCTION__));
+ }
+}
+
+/** Set an custom toolbar redraw function which is called instead of
+* default drawing routine.
+* \param win the GUIWIN
+* \param func the custom redraw function
+*/
+void gemtk_wm_set_toolbar_redraw_func(GUIWIN *win, gemtk_wm_redraw_f func)
+{
+ win->toolbar_redraw_func = func;
+}
+
+/**
+* Attach an arbitary pointer to the GUIWIN
+*/
+void gemtk_wm_set_user_data(GUIWIN *win, void *data)
+{
+ win->user_data = data;
+}
+
+/**
+* Retrieve the user_data pointer attached to the GUIWIN.
+*/
+void *gemtk_wm_get_user_data(GUIWIN *win)
+{
+ return(win->user_data);
+}
+
+/** Get the scroll management structure for a GUIWIN
+*/
+struct gemtk_wm_scroll_info_s *gemtk_wm_get_scroll_info(GUIWIN *win) {
+ return(&win->scroll_info);
+}
+
+/**
+* Get the amount of content dimensions within the window
+* which is calculated by using the scroll_info attached to the GUIWIN.
+*/
+void gemtk_wm_set_scroll_grid(GUIWIN * win, short x, short y)
+{
+ struct gemtk_wm_scroll_info_s *slid = gemtk_wm_get_scroll_info(win);
+
+ assert(slid != NULL);
+
+ slid->y_unit_px = x;
+ slid->x_unit_px = y;
+}
+
+
+/** Set the size of the content measured in content units
+* \param win the GUIWIN
+* \param x horizontal size
+* \param y vertical size
+*/
+void gemtk_wm_set_content_units(GUIWIN * win, short x, short y)
+{
+ struct gemtk_wm_scroll_info_s *slid = gemtk_wm_get_scroll_info(win);
+
+ assert(slid != NULL);
+
+ slid->x_units = x;
+ slid->y_units = y;
+}
+
+/** Send an Message to a GUIWIN using AES message pipe
+* \param win the GUIWIN which shall receive the message
+* \param msg_type the WM_ message definition
+* \param a the 4th parameter to appl_write
+* \param b the 5th parameter to appl_write
+* \param c the 6th parameter to appl_write
+* \param d the 7th parameter to appl_write
+*/
+void gemtk_wm_send_msg(GUIWIN *win, short msg_type, short a, short b, short c,
+ short d)
+{
+ short msg[8];
+
+ msg[0] = msg_type;
+ msg[1] = gl_apid;
+ msg[2] = 0;
+ msg[3] = (win != NULL) ? win->handle : 0;
+ msg[4] = a;
+ msg[5] = b;
+ msg[6] = c;
+ msg[7] = d;
+
+ appl_write(gl_apid, 16, &msg);
+}
+
+/** Directly execute an Message to a GUIWIN using the internal dispatcher function.
+* This only works for managed windows which have the,
+ GEMTK_WM_FLAG_PREPROC_WM flag set.
+ This call does not send any AES messages.
+* \param win the GUIWIN which shall receive the message
+* \param msg_type the WM_ message definition
+* \param a the 4th parameter to appl_write
+* \param b the 5th parameter to appl_write
+* \param c the 6th parameter to appl_write
+* \param d the 7th parameter to appl_write
+*/
+short gemtk_wm_exec_msg(GUIWIN *win, short msg_type, short a, short b, short c,
+ short d)
+{
+ short msg[8], retval;
+
+ EVMULT_OUT event_out;
+
+ msg[0] = msg_type;
+ msg[1] = gl_apid;
+ msg[2] = 0;
+ msg[3] = win->handle;
+ msg[4] = a;
+ msg[5] = b;
+ msg[6] = c;
+ msg[7] = d;
+
+ event_out.emo_events = MU_MESAG;
+ retval = preproc_wm(win, &event_out, msg);
+ if (retval == 0 || (win->flags & GEMTK_WM_FLAG_PREPROC_WM) != 0){
+ retval = win->handler_func(win, &event_out, msg);
+ }
+
+ return(retval);
+}
+
+void gemtk_wm_exec_redraw(GUIWIN *win, GRECT *area)
+{
+ GRECT work;
+
+ if (area == NULL) {
+ gemtk_wm_get_grect(win, GEMTK_WM_AREA_WORK, &work);
+ if (work.g_w < 1 || work.g_h < 1) {
+ if (win->toolbar != NULL) {
+ gemtk_wm_get_grect(win, GEMTK_WM_AREA_TOOLBAR, &work);
+ if (work.g_w < 1 || work.g_h < 1) {
+ return;
+ }
+ }
+ }
+ area = &work;
+ }
+
+ gemtk_wm_exec_msg(win, WM_REDRAW, area->g_x, area->g_y, area->g_w,
+ area->g_h);
+}
+
+/** Attach an AES FORM to the GUIWIN, similar feature like the toolbar
+*/
+void gemtk_wm_set_form(GUIWIN *win, OBJECT *tree, short index)
+{
+ DEBUG_PRINT(("Setting form %p (%d) for window %p\n", tree, index, win));
+ win->form = tree;
+ win->form_edit_obj = -1;
+ win->form_focus_obj = -1;
+ win->form_idx = index;
+}
+
+/** Checks if a GUIWIN is overlapped by other windows.
+*/
+bool gemtk_wm_has_intersection(GUIWIN *win, GRECT *work)
+{
+ GRECT area, mywork;
+ bool retval = true;
+
+ if (work == NULL) {
+ gemtk_wm_get_grect(win, GEMTK_WM_AREA_CONTENT, &mywork);
+ work = &mywork;
+ }
+
+ wind_get_grect(win->handle, WF_FIRSTXYWH, &area);
+ while (area.g_w) {
+ //GRECT * ptr = &area;
+ if (RC_WITHIN(work, &area)) {
+ retval = false;
+ }
+ wind_get_grect(win->handle, WF_NEXTXYWH, &area);
+ }
+
+ return(retval);
+}
+
+/** Execute an toolbar redraw
+* \param msg specifies the AES message which initiated the redraw, or 0 when
+* the function was called without AES message context.
+*/
+void gemtk_wm_toolbar_redraw(GUIWIN *gw, uint16_t msg, GRECT *clip)
+{
+ GRECT tb_area, tb_area_ro;
+
+ gemtk_wm_get_grect(gw, GEMTK_WM_AREA_TOOLBAR, &tb_area_ro);
+
+ if(clip == NULL) {
+ clip = &tb_area_ro;
+ }
+
+ tb_area = tb_area_ro;
+
+ if (rc_intersect(clip, &tb_area)) {
+ gw->toolbar_redraw_func(gw, msg, &tb_area);
+ }
+}
+
+/** Execute FORM redraw
+*/
+void gemtk_wm_form_redraw(GUIWIN *gw, GRECT *clip)
+{
+ GRECT area, area_ro, g;
+ struct gemtk_wm_scroll_info_s *slid;
+ //int new_x, new_y, old_x, old_y;
+
+ DEBUG_PRINT(("gemtk_wm_form_redraw\n"));
+
+ // calculate form coordinates, include scrolling:
+ gemtk_wm_get_grect(gw, GEMTK_WM_AREA_CONTENT, &area_ro);
+ slid = gemtk_wm_get_scroll_info(gw);
+
+ // Update form position:
+ gw->form[gw->form_idx].ob_x = area_ro.g_x - (slid->x_pos * slid->x_unit_px);
+ gw->form[gw->form_idx].ob_y = area_ro.g_y - (slid->y_pos * slid->y_unit_px);
+
+ if(clip == NULL) {
+ clip = &area_ro;
+ }
+
+ area = area_ro;
+
+ /* Walk the AES rectangle list and redraw the visible areas of the window:*/
+ if(rc_intersect(clip, &area)) {
+
+ wind_get_grect(gw->handle, WF_FIRSTXYWH, &g);
+ while (g.g_h > 0 || g.g_w > 0) {
+ if(rc_intersect(&area, &g)) {
+ objc_draw(gw->form, gw->form_idx, 8, g.g_x, g.g_y,
+ g.g_w, g.g_h);
+
+ }
+ wind_get_grect(gw->handle, WF_NEXTXYWH, &g);
+ }
+ }
+}
+
+
+/** Fill the content area with white color
+*/
+void gemtk_wm_clear(GUIWIN *win)
+{
+ GRECT area, g;
+ short pxy[4];
+ VdiHdl vh;
+
+ vh = gemtk_wm_get_vdi_handle(win);
+
+ if(win->state & GEMTK_WM_STATUS_ICONIFIED) {
+ // also clear the toolbar area when iconified:
+ gemtk_wm_get_grect(win, GEMTK_WM_AREA_WORK, &area);
+ } else {
+ gemtk_wm_get_grect(win, GEMTK_WM_AREA_CONTENT, &area);
+ }
+
+ vsf_interior(vh, FIS_SOLID);
+ vsf_color(vh, 0);
+ vswr_mode(vh, MD_REPLACE);
+ wind_get_grect(win->handle, WF_FIRSTXYWH, &g);
+ while (g.g_h > 0 || g.g_w > 0) {
+ if(rc_intersect(&area, &g)) {
+ pxy[0] = g.g_x;
+ pxy[1] = g.g_y;
+ pxy[2] = g.g_x+g.g_w-1;
+ pxy[3] = g.g_y+g.g_h-1;
+ v_bar(vh, pxy);
+ }
+ wind_get_grect(win->handle, WF_NEXTXYWH, &g);
+ }
+}
diff --git a/frontends/atari/gemtk/guiwin.h b/frontends/atari/gemtk/guiwin.h
new file mode 100644
index 000000000..6daf16ca9
--- /dev/null
+++ b/frontends/atari/gemtk/guiwin.h
@@ -0,0 +1,4 @@
+#ifndef OPKG_GUI_GUIWIN_H
+#define OPKG_GUI_GUIWIN_
+
+#endif /* OPKG_GUIWIN_H */
diff --git a/frontends/atari/gemtk/msgbox.c b/frontends/atari/gemtk/msgbox.c
new file mode 100644
index 000000000..89ca75a46
--- /dev/null
+++ b/frontends/atari/gemtk/msgbox.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2013 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gem.h>
+#include "gemtk.h"
+
+#ifndef min
+# define min(x,y) ((x<y) ? x : y )
+#endif
+
+/***
+ * Display an message box
+ *
+ * \param type Valid values: GEMTK_MSG_BOX_CONFIRM, GEMTK_MSG_BOX_ALERT
+ * \param msg The message / query to display
+ * \return 0 on "No"
+ *
+ */
+short gemtk_msg_box_show(short type, const char * msg)
+{
+ #define GEMTK_MSG_BOX_STR_SIZE 256
+ short retval=0, i=0, z=0, l=0;
+ char c;
+ int len_msg = strlen(msg);
+
+ // TODO: localize strings
+ const char *str_yes = "Yes";
+ const char *str_no = "No";
+ const char *str_ok = "OK";
+ char msg_box_str[GEMTK_MSG_BOX_STR_SIZE];
+ char *dst = msg_box_str;
+
+ memset(msg_box_str, 0, GEMTK_MSG_BOX_STR_SIZE);
+
+ strncat(msg_box_str, "[1]", GEMTK_MSG_BOX_STR_SIZE);
+ strncat(msg_box_str, "[", GEMTK_MSG_BOX_STR_SIZE);
+
+ dst = msg_box_str + strlen(msg_box_str);
+
+ for (i=0; i<min(len_msg,40*5); i++) {
+
+ c = msg[i];
+
+ if(c==0)
+ break;
+
+ if (z==40) {
+ if(l==4){
+ break;
+ }
+ z = 0;
+ l++;
+ *dst = (char)'|';
+ dst++;
+ }
+
+ if ((c=='\r' || c=='\n') && *dst != '|') {
+ if(l==4){
+ break;
+ }
+ z = 0;
+ l++;
+ *dst = '|';
+ dst++;
+ }
+ else {
+ z++;
+ *dst = c;
+ dst++;
+ }
+ }
+ strncat(msg_box_str, "][", GEMTK_MSG_BOX_STR_SIZE);
+
+ if(type == GEMTK_MSG_BOX_CONFIRM){
+ strncat(msg_box_str, str_yes, GEMTK_MSG_BOX_STR_SIZE);
+ strncat(msg_box_str, "|", GEMTK_MSG_BOX_STR_SIZE);
+ strncat(msg_box_str, str_no, GEMTK_MSG_BOX_STR_SIZE);
+ } else {
+ strncat(msg_box_str, str_ok, GEMTK_MSG_BOX_STR_SIZE);
+ }
+ strncat(msg_box_str, "]", GEMTK_MSG_BOX_STR_SIZE);
+
+ retval = form_alert(type, msg_box_str);
+ if(type == GEMTK_MSG_BOX_CONFIRM){
+ if(retval != 1){
+ retval = 0;
+ }
+ }
+ return(retval);
+
+ #undef GEMTK_MSG_BOX_STR_SIZE
+}
diff --git a/frontends/atari/gemtk/msgbox.h b/frontends/atari/gemtk/msgbox.h
new file mode 100644
index 000000000..7a46900ef
--- /dev/null
+++ b/frontends/atari/gemtk/msgbox.h
@@ -0,0 +1,5 @@
+#ifndef GUIMSG_H_INCLUDED
+#define GUIMSG_H_INCLUDED
+
+
+#endif // GUIMSG_H_INCLUDED
diff --git a/frontends/atari/gemtk/objc.c b/frontends/atari/gemtk/objc.c
new file mode 100644
index 000000000..855413e0b
--- /dev/null
+++ b/frontends/atari/gemtk/objc.c
@@ -0,0 +1,522 @@
+/*
+ * Copyright 2013 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ *
+ * Module Description:
+ *
+ * AES Object tree tools.
+ *
+ */
+
+#include <assert.h>
+#include "gemtk.h"
+
+char *gemtk_obj_get_text(OBJECT * tree, short idx)
+{
+ static char p[]="";
+
+ switch (tree[idx].ob_type & 0x00FF) {
+ case G_BUTTON:
+ case G_STRING:
+ case G_TITLE:
+ return( tree[idx].ob_spec.free_string);
+ case G_TEXT:
+ case G_BOXTEXT:
+ case G_FTEXT:
+ case G_FBOXTEXT:
+ return (tree[idx].ob_spec.tedinfo->te_ptext);
+ case G_ICON:
+ case G_CICON:
+ return (tree[idx].ob_spec.iconblk->ib_ptext);
+ break;
+
+ default:
+ break;
+ }
+ return (p);
+}
+
+static void set_text(OBJECT *obj, short idx, char * text, int len)
+{
+ char spare[255];
+
+ if( len > 254 )
+ len = 254;
+ if( text != NULL ) {
+ strncpy(spare, text, 254);
+ } else {
+ strcpy(spare, "");
+ }
+
+ set_string(obj, idx, spare);
+}
+
+char gemtk_obj_set_str_safe(OBJECT * tree, short idx, const char *txt)
+{
+ char spare[204];
+ short type = 0;
+ short maxlen = 0;
+
+
+ type = (tree[idx].ob_type & 0xFF);
+ if (type == G_FTEXT || type == G_FBOXTEXT) {
+ TEDINFO *ted = ((TEDINFO *)get_obspec(tree, idx));
+ maxlen = ted->te_tmplen+1;
+ if (maxlen > 200) {
+ maxlen = 200;
+ } else if (maxlen < 0) {
+ maxlen = 0;
+ }
+ } else {
+ assert((type == G_FTEXT) || (type == G_FBOXTEXT));
+ }
+
+ snprintf(spare, maxlen, "%s", txt);
+ set_string(tree, idx, spare);
+
+ return(0);
+}
+
+OBJECT *gemtk_obj_get_tree(int idx)
+{
+
+ OBJECT *tree;
+
+ rsrc_gaddr(R_TREE, idx, &tree);
+
+ return tree;
+}
+
+bool gemtk_obj_is_inside(OBJECT * tree, short obj, GRECT *area)
+{
+ GRECT obj_screen;
+ bool ret = false;
+
+ objc_offset(tree, obj, &obj_screen.g_x, &obj_screen.g_y);
+ obj_screen.g_w = tree[obj].ob_width;
+ obj_screen.g_h = tree[obj].ob_height;
+
+ ret = RC_WITHIN(&obj_screen, area);
+
+ return(ret);
+}
+
+GRECT * gemtk_obj_screen_rect(OBJECT * tree, short obj)
+{
+ static GRECT obj_screen;
+
+ get_objframe(tree, obj, &obj_screen);
+
+ return(&obj_screen);
+}
+
+
+void gemtk_obj_mouse_sprite(OBJECT *tree, int index)
+{
+ MFORM mform;
+ int dum;
+
+ if ((tree[index].ob_type & 0xFF) != G_ICON)
+ return;
+
+ dum = tree[index].ob_spec.iconblk->ib_char;
+ mform . mf_nplanes = 1;
+ mform . mf_fg = (dum>>8)&0x0F;
+ mform . mf_bg = dum>>12;
+ mform . mf_xhot = 0; /* to prevent the mform to "jump" on the */
+ mform . mf_yhot = 0; /* screen (zebulon rules!) */
+
+ for( dum = 0; dum<16; dum ++) {
+ mform . mf_mask[dum] = tree[index].ob_spec.iconblk->ib_pmask[dum];
+ mform . mf_data[dum] = tree[index].ob_spec.iconblk->ib_pdata[dum];
+ }
+ graf_mouse(USER_DEF, &mform);
+}
+
+
+/*
+ * gemtk_obj_tree_copy
+ *
+ * Copy a complete object-tree including all substructures (optional).
+ *
+ * CAUTION: The object-tree *must* have the LASTOB-flag (0x20) set in
+ * it's physically last member.
+ *
+ * BUG: Up to now tree_copy won't copy the color-icon-structure,
+ * because I'm too lazy ;) Maybe I'll do that one day. If you need it
+ * urgently, contact me and force me to work... Btw, this doesn't mean
+ * that G_CICONs won't be copied at all, but the copied tree will
+ * share the CICONBLKs with the original.
+ *
+ * Input:
+ * tree: Pointer to tree which should be copied
+ * what: Specifies what substructures should be copied, too (see the
+ * C_xxx-definitions in tree-copy.h for details)
+ *
+ * Output:
+ * NULL: Tree couldn't be copied (due to lack of memory)
+ * otherwise: Pointer to copied tree, use free to dealloc it's memory
+ */
+OBJECT *gemtk_obj_tree_copy(OBJECT *tree)
+{
+ int16_t i, objects;
+ size_t to_malloc, size;
+ OBJECT *new_tree;
+ char *area;
+
+ /* Calculate the number of bytes we need for the new tree */
+ to_malloc = (size_t) 0;
+ for (i = 0;;) {
+
+ /* Size of the OBJECT-structure itself */
+ to_malloc += sizeof(OBJECT);
+
+ switch (tree[i].ob_type & 0xff) {
+ case G_TEXT:
+ case G_BOXTEXT:
+ case G_FTEXT:
+ case G_FBOXTEXT:
+ /* Size of a TEDINFO-structure */
+ to_malloc += sizeof(TEDINFO);
+
+ /* Sizes of the strings in the TEDINFO-structure */
+ to_malloc += (size_t)tree[i].ob_spec.tedinfo->te_txtlen;
+ to_malloc += (size_t)tree[i].ob_spec.tedinfo->te_txtlen;
+ to_malloc += (size_t)tree[i].ob_spec.tedinfo->te_tmplen;
+ break;
+
+ case G_IMAGE:
+
+ /* Size of the BITBLK-structure */
+ to_malloc += sizeof(BITBLK);
+
+ /* Size of the image-data in the BITBLK-structure */
+ to_malloc += (size_t)((int32_t)tree[i].ob_spec.bitblk->bi_wb *
+ (int32_t)tree[i].ob_spec.bitblk->bi_hl);
+
+ break;
+
+ case G_USERDEF:
+ /* Size of the USERBLK-structure */
+ to_malloc += sizeof(USERBLK);
+ break;
+
+ case G_BUTTON:
+ case G_STRING:
+ case G_TITLE:
+ /* Size of the string (with one null character at the end) */
+ to_malloc += strlen(tree[i].ob_spec.free_string) + 1L;
+ break;
+
+ case G_ICON:
+ /* Size of the ICONBLK-structure */
+ to_malloc += sizeof(BITBLK);
+
+ /* Sizes of icon-data, icon-mask and icon-text */
+ to_malloc += (size_t)((int32_t)tree[i].ob_spec.iconblk->ib_wicon *
+ (int32_t)tree[i].ob_spec.iconblk->ib_hicon /
+ 4L + 1L +
+ (int32_t)strlen(tree[i].ob_spec.iconblk->ib_ptext));
+
+ break;
+ }
+
+ /* If the size is odd, make it even */
+ if ((long)to_malloc & 1)
+ to_malloc++;
+
+ /* Exit if we've reached the last object in the tree */
+ if (tree[i].ob_flags & OF_LASTOB)
+ break;
+
+ i++;
+ }
+
+ objects = i + 1;
+
+ /* If there's not enough memory left for the new tree, return NULL */
+ if ((new_tree = (OBJECT *)calloc(1, to_malloc)) == NULL) {
+ return(NULL);
+ }
+
+ /*
+ * area contains a pointer to the area where we copy the structures to
+ */
+ area = (char *)((int32_t)new_tree+(int32_t)objects*(int32_t)sizeof(OBJECT));
+
+ for (i = 0; i < objects; i++) {
+
+ /* Copy the contents of the OBJECT-structure */
+ new_tree[i] = tree[i];
+
+ /* This was added to assure true copies of the object type */
+ new_tree[i].ob_type = tree[i].ob_type;
+
+ switch (tree[i].ob_type & 0xff) {
+ case G_TEXT:
+ case G_BOXTEXT:
+ case G_FTEXT:
+ case G_FBOXTEXT:
+
+ /* Copy the contents of the TEDINFO-structure */
+ *(TEDINFO *)area = *tree[i].ob_spec.tedinfo;
+ new_tree[i].ob_spec.tedinfo = (TEDINFO *)area;
+ area += sizeof(TEDINFO);
+
+ /* Copy the strings in the TEDINFO-structure */
+ strncpy(area, tree[i].ob_spec.tedinfo->te_ptext,
+ tree[i].ob_spec.tedinfo->te_txtlen);
+ new_tree[i].ob_spec.tedinfo->te_ptext = area;
+ area += tree[i].ob_spec.tedinfo->te_txtlen;
+ strncpy(area, tree[i].ob_spec.tedinfo->te_ptmplt,
+ tree[i].ob_spec.tedinfo->te_tmplen);
+ new_tree[i].ob_spec.tedinfo->te_ptmplt = area;
+ area += tree[i].ob_spec.tedinfo->te_tmplen;
+ strncpy(area, tree[i].ob_spec.tedinfo->te_pvalid,
+ tree[i].ob_spec.tedinfo->te_txtlen);
+ new_tree[i].ob_spec.tedinfo->te_pvalid = area;
+ area += tree[i].ob_spec.tedinfo->te_txtlen;
+
+ break;
+
+ case G_IMAGE:
+
+ /* Copy the contents of the BITBLK-structure */
+ *(BITBLK *)area = *tree[i].ob_spec.bitblk;
+ new_tree[i].ob_spec.bitblk = (BITBLK *)area;
+ area += sizeof(BITBLK);
+
+ /* Copy the image-data */
+ size = (size_t)((int32_t)tree[i].ob_spec.bitblk->bi_wb *
+ (int32_t)tree[i].ob_spec.bitblk->bi_hl);
+ memcpy(area, tree[i].ob_spec.bitblk->bi_pdata, size);
+ new_tree[i].ob_spec.bitblk->bi_pdata = (int16_t *)area;
+ area += size;
+
+ break;
+
+ case G_USERDEF:
+
+ /* Copy the contents of the USERBLK-structure */
+ *(USERBLK *)area = *tree[i].ob_spec.userblk;
+ new_tree[i].ob_spec.userblk = (USERBLK *)area;
+ area += sizeof(USERBLK);
+
+ break;
+
+ case G_BUTTON:
+ case G_STRING:
+ case G_TITLE:
+
+ /* Copy the string */
+ size = strlen(tree[i].ob_spec.free_string) + 1L;
+ strcpy(area, tree[i].ob_spec.free_string);
+ new_tree[i].ob_spec.free_string = area;
+ area += size;
+
+ break;
+
+ case G_ICON:
+
+ /* Copy the contents of the ICONBLK-structure */
+ *(ICONBLK *)area = *tree[i].ob_spec.iconblk;
+ new_tree[i].ob_spec.iconblk = (ICONBLK *)area;
+ area += sizeof(ICONBLK);
+
+ size = (size_t)((int32_t)tree[i].ob_spec.iconblk->ib_wicon *
+ (int32_t)tree[i].ob_spec.iconblk->ib_hicon /
+ 8L);
+ /* Copy the mask-data */
+ memcpy(area, tree[i].ob_spec.iconblk->ib_pmask, size);
+ new_tree[i].ob_spec.iconblk->ib_pmask = (int16_t *)area;
+ area += size;
+
+ /* Copy the icon-data */
+ memcpy(area, tree[i].ob_spec.iconblk->ib_pdata, size);
+ new_tree[i].ob_spec.iconblk->ib_pdata = (int16_t *)area;
+ area += size;
+ size = strlen(tree[i].ob_spec.iconblk->ib_ptext) + 1L;
+
+ /* Copy the icon-string */
+ strcpy(area, tree[i].ob_spec.iconblk->ib_ptext);
+ new_tree[i].ob_spec.iconblk->ib_ptext = area;
+ area += size;
+
+ break;
+ }
+
+ /* Assure that area contains an even address */
+ if ((int32_t)area & 1)
+ area++;
+ }
+
+ return(new_tree);
+}
+
+/***
+ * Create a simple OBJECT tree from a list of strings, to be used with menu_popup
+ * OBJECT ownership is passed to caller.
+ * Call gemtk_obj_destroy_popup_tree once the popup isn't needed anymore.
+ *
+ * \param items A list of string to be used as items
+ * \param nitems The number of items in the list
+ * \param selected The text of the selected item
+ * \param horizontal Set to true to render the tree horizontally
+ * \param max_width -1: autodetect width
+ * \param max_heigth -1: autodetect height (set to postive value when using a vertical scrolling popup)
+ * \return a pointer to the new OBJECT tree
+ */
+OBJECT * gemtk_obj_create_popup_tree(const char **items, int nitems,
+ char * selected, bool horizontal,
+ int max_width, int max_height)
+{
+ OBJECT * popup = NULL;
+ int box_width = 0;
+ int box_height = 0;
+ int char_width = 10;
+ int char_height = 16;
+ int item_height; // height of each item
+
+ assert(items != NULL);
+
+ item_height = char_height;
+
+ /* Allocate room for n items and the root G_BOX: */
+ popup = calloc(nitems+1, sizeof(OBJECT));
+ assert(popup != null);
+
+ for (int i=0; i<nitems; i++) {
+
+ int len = strlen(items[i]);
+
+ if (horizontal && (max_width<1)) {
+ box_width += (len * char_width)+4;
+ }
+ else if (!horizontal){
+ /* Detect max width, used for vertical rendering: */
+ if(len*char_width > box_width){
+ box_width = (len+2) * char_width;
+ }
+ //if (max_height < 1) {
+ box_height += item_height;
+ //}
+ }
+ }
+
+ if (max_width>0){
+ box_width = max_width;
+ }
+
+ if (horizontal) {
+ box_height = item_height;
+ }
+ else if(max_height > 0){
+ // TODO: validate max_height, shrink values larger than screen height
+ //box_height = max_height;
+ }
+
+/*
+ printf("popup height: %d, popup width: %d\n", box_height, box_width);
+*/
+
+ popup[0].ob_next = -1; /**< object's next sibling */
+ popup[0].ob_head = 1; /**< head of object's children */
+ popup[0].ob_tail = nitems; /**< tail of object's children */
+ popup[0].ob_type = G_BOX; /**< type of object */
+ popup[0].ob_flags = OF_FL3DBAK; /**< flags */
+ popup[0].ob_state = OS_NORMAL; /**< state */
+ popup[0].ob_spec.index = (long) 16650496L; /**< object-specific data */
+ popup[0].ob_x = 0; /**< upper left corner of object */
+ popup[0].ob_y = 0; /**< upper left corner of object */
+ popup[0].ob_width = box_width; /**< width of obj */
+ popup[0].ob_height = box_height;
+
+
+
+ /* Add items to popup: */
+ int xpos = 0, ypos = 0;
+
+ for (int i=0; i<nitems; i++) {
+
+ int state = OS_NORMAL;
+ int flags = OF_NONE;
+ int item_width;
+ char * string = calloc(1, strlen(items[i])+3);
+
+ snprintf(string, strlen(items[i])+3, " %s", items[i]);
+
+ if (selected != NULL) {
+ if (strcmp(selected, items[i]) == 0) {
+ state |= OS_CHECKED;
+ }
+ }
+
+ if (i == nitems-1) {
+ flags |= OF_LASTOB;
+ }
+
+ item_width = (horizontal) ? ((int)strlen(items[i])*char_width)+2 : box_width;
+/*
+ printf("addin popup item \"%s\" (w: %d, h: %d, flags: %x) at %d/%d\n", items[i],
+ item_width, item_height, flags,
+ xpos, ypos);
+*/
+
+ popup[i+1].ob_next = ((flags&OF_LASTOB) != 0) ? 0 : i+2; /**< object's next sibling */
+ popup[i+1].ob_head = -1; /**< head of object's children */
+ popup[i+1].ob_tail = -1; /**< tail of object's children */
+ popup[i+1].ob_type = G_STRING; /**< type of object */
+ popup[i+1].ob_flags = flags; /**< flags */
+ popup[i+1].ob_state = state; /**< state */
+ popup[i+1].ob_spec.free_string = string; /**< object-specific data */
+ popup[i+1].ob_x = xpos; /**< upper left corner of object */
+ popup[i+1].ob_y = ypos; /**< upper left corner of object */
+ popup[i+1].ob_width = item_width; /**< width of obj */
+ popup[i+1].ob_height = item_height;
+
+ if (horizontal) {
+ xpos += item_width;
+ }
+ else {
+ ypos += item_height;
+ }
+
+ }
+
+ return(popup);
+}
+
+/***
+ * Free memory of an OBJECT tree created with gemtk_obj_create_popup_tree.
+ *
+ * \param popup The tree to destroy
+ */
+void gemtk_obj_destroy_popup_tree(OBJECT * popup)
+{
+ int i=0;
+
+ while (1) {
+ if (popup[i].ob_type == G_STRING) {
+ free(popup[i+1].ob_spec.free_string);
+ }
+ if((popup[i].ob_flags & OF_LASTOB) != OF_LASTOB){
+ break;
+ }
+ i++;
+ }
+
+ free(popup);
+}
diff --git a/frontends/atari/gemtk/objc.h b/frontends/atari/gemtk/objc.h
new file mode 100644
index 000000000..6fa1072b7
--- /dev/null
+++ b/frontends/atari/gemtk/objc.h
@@ -0,0 +1,7 @@
+#ifndef GEMTK_OBJC_H
+#define GEMTK_OBJC_H
+
+
+
+#endif // GEMTK_OBJC_H
+
diff --git a/frontends/atari/gemtk/redrawslots.c b/frontends/atari/gemtk/redrawslots.c
new file mode 100644
index 000000000..ee5627daf
--- /dev/null
+++ b/frontends/atari/gemtk/redrawslots.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2011 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#define WITH_RECT
+#include "gemtk.h"
+#undef WITH_RECT
+
+void redraw_slots_init(struct s_redrw_slots * slots, short size)
+{
+ // TODO: allocate slots dynamically!
+ slots->size = MIN( MAX_REDRW_SLOTS , size);
+ slots->areas_used = 0;
+}
+
+void redraw_slots_free(struct s_redrw_slots * slots)
+{
+ // TOOD: free areas...
+}
+
+
+static inline bool rect_intersect( struct rect * box1, struct rect * box2 )
+{
+ if (box2->x1 < box1->x0)
+ return false;
+
+ if (box2->y1 < box1->y0)
+ return false;
+
+ if (box2->x0 > box1->x1)
+ return false;
+
+ if (box2->y0 > box1->y1)
+ return false;
+
+ return true;
+}
+
+
+void redraw_slot_schedule_grect(struct s_redrw_slots * slots, GRECT *area,
+ bool force)
+{
+ redraw_slot_schedule(slots, area->g_x, area->g_y,
+ area->g_x + area->g_w, area->g_y + area->g_h, force);
+}
+
+/*
+ schedule redraw coords.
+*/
+void redraw_slot_schedule(struct s_redrw_slots * slots, short x0, short y0,
+ short x1, short y1, bool force)
+{
+ int i = 0;
+ struct rect area;
+
+ area.x0 = x0;
+ area.y0 = y0;
+ area.x1 = x1;
+ area.y1 = y1;
+
+ if (force == false) {
+ for (i=0; i<slots->areas_used; i++) {
+ if (slots->areas[i].x0 <= x0
+ && slots->areas[i].x1 >= x1
+ && slots->areas[i].y0 <= y0
+ && slots->areas[i].y1 >= y1) {
+ /* the area is already queued for redraw */
+ return;
+ } else {
+ if (rect_intersect(&slots->areas[i], &area )) {
+ slots->areas[i].x0 = MIN(slots->areas[i].x0, x0);
+ slots->areas[i].y0 = MIN(slots->areas[i].y0, y0);
+ slots->areas[i].x1 = MAX(slots->areas[i].x1, x1);
+ slots->areas[i].y1 = MAX(slots->areas[i].y1, y1);
+ return;
+ }
+ }
+ }
+ }
+
+ if (slots->areas_used < slots->size) {
+ slots->areas[slots->areas_used].x0 = x0;
+ slots->areas[slots->areas_used].x1 = x1;
+ slots->areas[slots->areas_used].y0 = y0;
+ slots->areas[slots->areas_used].y1 = y1;
+ slots->areas_used++;
+ } else {
+ /*
+ we are out of available slots, merge box with last slot
+ this is dumb... but also a very rare case.
+ */
+ slots->areas[slots->size-1].x0 = MIN(slots->areas[i].x0, x0);
+ slots->areas[slots->size-1].y0 = MIN(slots->areas[i].y0, y0);
+ slots->areas[slots->size-1].x1 = MAX(slots->areas[i].x1, x1);
+ slots->areas[slots->size-1].y1 = MAX(slots->areas[i].y1, y1);
+ }
+done:
+ return;
+}
+
+void redraw_slots_remove_area(struct s_redrw_slots * slots, int i)
+{
+ int x;
+ for(x = i+1; i<slots->areas_used; x++){
+ slots->areas[x-1] = slots->areas[x];
+ }
+ slots->areas_used--;
+}
diff --git a/frontends/atari/gemtk/redrawslots.h b/frontends/atari/gemtk/redrawslots.h
new file mode 100644
index 000000000..9691fb4bc
--- /dev/null
+++ b/frontends/atari/gemtk/redrawslots.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2012 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 ATARI_REDRAW_SLOTS_H
+#define ATARI_REDRAW_SLOTS_H
+
+#include <mt_gem.h>
+
+#endif
diff --git a/frontends/atari/gemtk/utils.c b/frontends/atari/gemtk/utils.c
new file mode 100644
index 000000000..63e6330ec
--- /dev/null
+++ b/frontends/atari/gemtk/utils.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2013 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <mt_gem.h>
+#include "gemtk.h"
+
+/* -------------------------------------------------------------------------- */
+/* GEM Utillity functions: */
+/* -------------------------------------------------------------------------- */
+
+unsigned short _systype_v;
+unsigned short _systype (void)
+{
+ int32_t * cptr = NULL;
+ _systype_v = SYS_TOS;
+
+ cptr = (int32_t *)Setexc(0x0168, -1L);
+ if (cptr == NULL ) {
+ return _systype_v; /* stone old TOS without any cookie support */
+ }
+ while (*cptr) {
+ if (*cptr == C_MgMc || *cptr == C_MgMx ) {
+ _systype_v = (_systype_v & ~0xF) | SYS_MAGIC;
+ } else if (*cptr == C_MiNT ) {
+ _systype_v = (_systype_v & ~0xF) | SYS_MINT;
+ } else if (*cptr == C_Gnva /* Gnva */ ) {
+ _systype_v |= SYS_GENEVA;
+ } else if (*cptr == C_nAES /* nAES */ ) {
+ _systype_v |= SYS_NAES;
+ }
+ cptr += 2;
+ }
+ if (_systype_v & SYS_MINT) { /* check for XaAES */
+ short out = 0, u;
+ if (wind_get (0, (((short)'X') <<8)|'A', &out, &u,&u,&u) && out) {
+ _systype_v |= SYS_XAAES;
+ }
+ }
+ return _systype_v;
+}
+
+bool gemtk_rc_intersect_ro(GRECT *a, GRECT *b)
+{
+ GRECT r1, r2;
+
+ r1 = *a;
+ r2 = *b;
+
+ return((bool)rc_intersect(&r1, &r2));
+}
+
+
+typedef struct {
+ char *unshift;
+ char *shift;
+ char *capslock;
+} KEYTAB;
+
+int gemtk_keybd2ascii( int keybd, int shift)
+{
+
+ KEYTAB *key;
+ key = (KEYTAB *)Keytbl( (char*)-1, (char*)-1, (char*)-1);
+ return (shift)?key->shift[keybd>>8]:key->unshift[keybd>>8];
+}
+
+
+void gemtk_clip_grect(VdiHdl vh, GRECT *rect)
+{
+ PXY pxy[2];
+
+ pxy[0].p_x = rect->g_x;
+ pxy[0].p_y = rect->g_y;
+ pxy[1].p_x = pxy[0].p_x + rect->g_w - 1;
+ pxy[1].p_y = pxy[0].p_y + rect->g_h - 1;
+
+ vs_clip_pxy(vh, pxy);
+}
+
+/** Send an Message to a GUIWIN using AES message pipe
+* \param win the GUIWIN which shall receive the message
+* \param msg_type the WM_ message definition
+* \param a the 4th parameter to appl_write
+* \param b the 5th parameter to appl_write
+* \param c the 6th parameter to appl_write
+* \param d the 7th parameter to appl_write
+*/
+void gemtk_send_msg(short msg_type, short data2, short data3, short data4,
+ short data5, short data6, short data7)
+{
+ short msg[8];
+
+ msg[0] = msg_type;
+ msg[1] = gl_apid;
+ msg[2] = data2;
+ msg[3] = data3;
+ msg[4] = data4;
+ msg[5] = data5;
+ msg[6] = data6;
+ msg[7] = data7;
+
+ appl_write(gl_apid, 16, &msg);
+}
+
+
+void gemtk_wind_get_str(short aes_handle, short mode, char *str, int len)
+{
+
+ // TODO: remove or implement function
+
+ if(len>255) {
+ len = 255;
+ }
+
+ memset(str, 0, len);
+ return;
+ /*
+
+ wind_get(aes_handle, mode, (short)(((unsigned long)tmp_str)>>16),
+ (short)(((unsigned long)tmp_str) & 0xffff), 0, 0);
+
+ strncpy(str, tmp_str, len);
+ */
+}
+
+
diff --git a/frontends/atari/gemtk/utils.h b/frontends/atari/gemtk/utils.h
new file mode 100644
index 000000000..7ebbcf228
--- /dev/null
+++ b/frontends/atari/gemtk/utils.h
@@ -0,0 +1,5 @@
+#ifndef UTILS_H_INCLUDED
+#define UTILS_H_INCLUDED
+
+
+#endif // UTILS_H_INCLUDED
diff --git a/frontends/atari/gemtk/vaproto.c b/frontends/atari/gemtk/vaproto.c
new file mode 100644
index 000000000..45e47225c
--- /dev/null
+++ b/frontends/atari/gemtk/vaproto.c
@@ -0,0 +1,170 @@
+#include <limits.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <gem.h>
+
+#include "vaproto.h"
+#include "gemtk.h"
+
+#ifndef NDEBUG
+# define DEBUG_PRINT(x) printf x
+#else
+# define DEBUG_PRINT(x)
+#endif
+
+
+/* Global memory, at least 1024 bytes large: */
+static char *va_helpbuf;
+
+/* Desktop's AES ID: */
+static short av_shell_id = -1;
+
+/* What AV commands can desktop do? */
+static short av_shell_status = 0;
+
+/* The application name used for AV/VA messages: */
+static const char * av_clientname = "gemtk_av_app";
+
+
+static short get_avserver(void)
+{
+ short ret = -100;
+ const char * av_env = getenv("AVSERVER");
+ if (av_env) {
+ char av_envname[9];
+ strncpy(av_envname,av_env, 8);
+ av_envname[8] = '\0';
+ while (strlen (av_envname) < 8) {
+ strcat(av_envname, " ");
+ }
+ ret = appl_find (av_envname);
+ }
+ return ret;
+}
+
+/**
+ * initialitze the AV client API
+ * \param appname Name of the application passed to menu_register()
+ * \return returns 1 on success, otherwise an negative error code.
+ *
+ */
+int gemtk_av_init(const char *appname)
+{
+ short mode = 0x0003 /* prefer TT ram */
+ | 0x0020; /* global accesible */
+
+ if(av_shell_id != -1) {
+ /* Already initialized */
+ return(1);
+ }
+
+ va_helpbuf = (char*)Mxalloc(1024, mode);
+
+ if(va_helpbuf == NULL){
+ gemtk_msg_box_show(GEMTK_MSG_BOX_ALERT, "Could not allocate AV memory!");
+ return(-1);
+ }
+
+ if(appname != NULL){
+ av_clientname = appname;
+ }
+
+ av_shell_id = get_avserver();
+ DEBUG_PRINT(("AV Server ID: %d", av_shell_id));
+
+ gemtk_av_send(AV_PROTOKOLL, NULL, NULL);
+
+ va_helpbuf[0] = '\0';
+}
+
+void gemtk_av_exit(void)
+{
+ if(av_shell_id == -1) {
+ /* Nothing to do */
+ return;
+ }
+
+ if (av_shell_status & AA_EXIT) {
+ /* AV server knows AV_EXIT */
+ gemtk_av_send(AV_EXIT, NULL, NULL);
+ }
+
+ if(va_helpbuf != NULL){
+ free(va_helpbuf);
+ va_helpbuf = NULL;
+ }
+
+ av_shell_id = -1;
+
+}
+
+bool gemtk_av_send (short message, const char * data1, const char * data2)
+{
+ short msg[8];
+ short to_ap_id = av_shell_id;
+
+ /* - 100 to ap id would be no AV server */
+ if (to_ap_id == -100){
+ return false;
+ }
+
+ msg[0] = message;
+ msg[1] = gl_apid;
+ msg[7] = msg[6] = msg[5] = msg[4] = msg[3] = msg[2] = 0;
+
+ switch (message)
+ {
+ case AV_EXIT:
+ msg[3] = gl_apid;
+ break;
+ case AV_PROTOKOLL:
+ msg[3] = VV_START | VV_ACC_QUOTING;
+ *(char **)(msg+6) = strcpy (va_helpbuf, av_clientname);
+ break;
+ case AV_STARTPROG:
+ DEBUG_PRINT(("AV_STARTPROG: %s (%s)\n", data1, data2));
+ *(char **)(msg+3) = strcpy(va_helpbuf, data1);
+ *(char **)(msg+5) = strcpy(va_helpbuf, data2);
+ break;
+ case AV_VIEW:
+ DEBUG_PRINT(("AV_VIEW: %s (%d)\n", data1, (short)data2));
+ *(char **)(msg+3) = strcpy(va_helpbuf, data1);
+ msg[5] = (short)data2;
+ break;
+ default:
+ return false; /* not supported */
+ }
+
+ return (appl_write (to_ap_id, 16, msg) > 0);
+}
+
+bool gemtk_av_dispatch (short msg[8])
+{
+
+ if(av_shell_id == -1)
+ return(false);
+
+ switch (msg[0]) {
+ case VA_PROTOSTATUS :
+ DEBUG_PRINT(("AV STATUS: %d for %d\n", msg[3], msg[1]));
+ if (msg[1] == av_shell_id) {
+ av_shell_status = msg[3];
+ if(av_shell_status & AA_STARTPROG){
+ printf(" AA_STARTPROG\n");
+ }
+ }
+ break;
+
+ default:
+ DEBUG_PRINT(("Unknown AV message: %d", msg[0]));
+ break;
+ }
+
+ return(true);
+}
+
+
diff --git a/frontends/atari/gemtk/vaproto.h b/frontends/atari/gemtk/vaproto.h
new file mode 100644
index 000000000..84e0ecf0b
--- /dev/null
+++ b/frontends/atari/gemtk/vaproto.h
@@ -0,0 +1,121 @@
+#ifndef VAPROTO_H_INCLUDED
+#define VAPROTO_H_INCLUDED
+
+
+#define VAPRO 1
+
+/* AES-Messages */
+
+typedef enum
+{
+ AV_PROTOKOLL = 0x4700,
+ VA_PROTOSTATUS = 0x4701,
+ AV_GETSTATUS = 0x4703,
+ AV_STATUS = 0x4704,
+ VA_SETSTATUS = 0x4705,
+ AV_SENDKEY = 0x4710,
+ VA_START = 0x4711,
+ AV_ASKFILEFONT = 0x4712,
+ VA_FILEFONT = 0x4713,
+ AV_ASKCONFONT = 0x4714,
+ VA_CONFONT = 0x4715,
+ AV_ASKOBJECT = 0x4716,
+ VA_OBJECT = 0x4717,
+ AV_OPENCONSOLE = 0x4718,
+ VA_CONSOLEOPEN = 0x4719,
+ AV_OPENWIND = 0x4720,
+ VA_WINDOPEN = 0x4721,
+ AV_STARTPROG = 0x4722,
+ VA_PROGSTART = 0x4723,
+ AV_ACCWINDOPEN = 0x4724,
+ VA_DRAGACCWIND = 0x4725,
+ AV_ACCWINDCLOSED = 0x4726,
+
+ AV_COPY_DRAGGED = 0x4728,
+ VA_COPY_COMPLETE = 0x4729,
+ AV_PATH_UPDATE = 0x4730,
+ AV_WHAT_IZIT = 0x4732,
+ VA_THAT_IZIT = 0x4733,
+ AV_DRAG_ON_WINDOW = 0x4734,
+ VA_DRAG_COMPLETE = 0x4735,
+ AV_EXIT = 0x4736,
+ AV_STARTED = 0x4738,
+ VA_FONTCHANGED = 0x4739,
+ AV_XWIND = 0x4740,
+ VA_XOPEN = 0x4741,
+
+/* Neue Messages seit dem 26.06.1995 */
+
+ AV_VIEW = 0x4751,
+ VA_VIEWED = 0x4752,
+ AV_FILEINFO = 0x4753,
+ VA_FILECHANGED = 0x4754,
+ AV_COPYFILE = 0x4755,
+ VA_FILECOPIED = 0x4756,
+ AV_DELFILE = 0x4757,
+ VA_FILEDELETED = 0x4758,
+ AV_SETWINDPOS = 0x4759,
+ VA_PATH_UPDATE = 0x4760,
+ VA_HIGH /* HR please always do this! */
+} AV_VA;
+
+
+/* Objekttypen fÂr VA_THAT_IZIT */
+
+enum
+{
+ VA_OB_UNKNOWN,
+ VA_OB_TRASHCAN,
+ VA_OB_SHREDDER,
+ VA_OB_CLIPBOARD,
+ VA_OB_FILE,
+ VA_OB_FOLDER,
+ VA_OB_DRIVE,
+ VA_OB_WINDOW,
+ VA_OB_NOTEPAD,
+ VA_OB_NOTE
+};
+
+typedef enum
+{
+ VV_SETSTATUS = 0x0001,
+ VV_START = 0x0002,
+ VV_STARTED = 0x0004,
+ VV_FONTCHANGED = 0x0008,
+ VV_ACC_QUOTING = 0x0010,
+ VV_PATH_UPDATE = 0x0020
+} av_va_give;
+
+typedef enum
+{ /* mp[3]: */
+ AA_SENDKEY = 0x0001, /* b0: MAGXDESK, THING */
+ AA_ASKFILEFONT = 0x0002, /* b1: THING */
+ AA_ASKCONFONT = 0x0004, /* b2: THING */
+ AA_ASKOBJECT = 0x0008,
+ AA_OPENWIND = 0x0010, /* b4: MAGXDESK, THING */
+ AA_STARTPROG = 0x0020, /* b5: MAGXDESK, THING */
+ AA_ACCWIND = 0x0040, /* b6: THING */
+ AA_STATUS = 0x0080, /* b7: THING */
+ AA_COPY_DRAGGED= 0x0100, /* b8: THING */
+ AA_DRAG_ON_WINDOW=0x0200, /* b9: MAGXDESK, THING */
+ AA_EXIT = 0x0400, /* b10: MAGXDESK, THING */
+ AA_XWIND = 0x0800, /* b11: MAGXDESK, THING */
+ AA_FONTCHANGED = 0x1000, /* b2: THING */
+ AA_STARTED = 0x2000, /* b13: MAGXDESK, THING */
+ AA_SRV_QUOTING = 0x4000, /* b14: THING */
+ AA_FILE = 0x8000, /* b15: THING */
+ /* mp[4]: THING */
+ AA_COPY = 0x0001,
+ AA_DELETE = 0x0002,
+ AA_VIEW = 0x0004,
+ AA_SETWINDPOS = 0x0008,
+ AA_COPYFILELINK= 0x0010,
+ AA_SENDCLICK = 0x0020
+} av_va_have;
+
+/* Makros zum Testen auf Quoting */
+
+#define VA_ACC_QUOTING(a) ((a) & VV_ACC_QUOTING)
+#define VA_SERVER_QUOTING(a) ((a) & AA_SRV_QUOTING)
+
+#endif // VAPROTO_H_INCLUDED
diff --git a/frontends/atari/gui.c b/frontends/atari/gui.c
new file mode 100644
index 000000000..810c7646f
--- /dev/null
+++ b/frontends/atari/gui.c
@@ -0,0 +1,1217 @@
+/*
+ * Copyright 2010 <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ *
+ * Provides all the mandatory functions prefixed with gui_ for atari
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/corestrings.h"
+#include "content/urldb.h"
+#include "content/content.h"
+#include "content/backing_store.h"
+#include "content/hlcache.h"
+#include "desktop/treeview.h"
+#include "desktop/browser.h"
+#include "desktop/gui_layout.h"
+#include "desktop/gui_window.h"
+#include "desktop/gui_clipboard.h"
+#include "desktop/gui_fetch.h"
+#include "desktop/gui_misc.h"
+#include "desktop/netsurf.h"
+
+#include "atari/gemtk/gemtk.h"
+#include "atari/gui.h"
+#include "atari/misc.h"
+#include "atari/findfile.h"
+#include "atari/schedule.h"
+#include "atari/rootwin.h"
+#include "atari/statusbar.h"
+#include "atari/toolbar.h"
+#include "atari/hotlist.h"
+#include "atari/cookies.h"
+#include "atari/certview.h"
+#include "atari/history.h"
+#include "atari/login.h"
+#include "atari/encoding.h"
+#include "atari/res/netsurf.rsh"
+#include "atari/plot/plot.h"
+#include "atari/clipboard.h"
+#include "atari/osspec.h"
+#include "atari/search.h"
+#include "atari/deskmenu.h"
+#include "atari/download.h"
+#include "atari/file.h"
+#include "atari/filetype.h"
+#include "atari/bitmap.h"
+#include "atari/font.h"
+#include "cflib.h"
+
+static bool atari_quit = false;
+
+struct gui_window *input_window = NULL;
+struct gui_window *window_list = NULL;
+void *h_gem_rsrc;
+long next_poll;
+bool rendering = false;
+GRECT desk_area;
+
+/* Comandline / Options: */
+int option_window_width;
+int option_window_height;
+int option_window_x;
+int option_window_y;
+
+/* Defaults to option_homepage_url, commandline options overwrites that value */
+const char *option_homepage_url;
+
+/* path to choices file: */
+char options[PATH_MAX];
+
+EVMULT_IN aes_event_in = {
+ .emi_flags = MU_MESAG | MU_TIMER | MU_KEYBD | MU_BUTTON | MU_M1,
+ .emi_bclicks = 258,
+ .emi_bmask = 3,
+ .emi_bstate = 0,
+ .emi_m1leave = MO_ENTER,
+ .emi_m1 = {0,0,0,0},
+ .emi_m2leave = 0,
+ .emi_m2 = {0,0,0,0},
+ .emi_tlow = 0,
+ .emi_thigh = 0
+};
+EVMULT_OUT aes_event_out;
+short aes_msg_out[8];
+
+bool gui_window_get_scroll(struct gui_window *w, int *sx, int *sy);
+static nserror gui_window_set_url(struct gui_window *w, nsurl *url);
+
+/**
+ * Core atari event processing.
+ */
+static void atari_poll(void)
+{
+
+ struct gui_window *tmp;
+ short mx, my, dummy;
+
+ aes_event_in.emi_tlow = schedule_run();
+
+ if(rendering){
+ aes_event_in.emi_tlow = nsoption_int(atari_gui_poll_timeout);
+ }
+
+ if(aes_event_in.emi_tlow < 0) {
+ aes_event_in.emi_tlow = 10000;
+ printf("long poll!\n");
+ }
+
+ if(input_window && input_window->root->redraw_slots.areas_used > 0) {
+ window_process_redraws(input_window->root);
+ }
+
+
+ graf_mkstate(&mx, &my, &dummy, &dummy);
+ aes_event_in.emi_m1.g_x = mx;
+ aes_event_in.emi_m1.g_y = my;
+ evnt_multi_fast(&aes_event_in, aes_msg_out, &aes_event_out);
+ if(gemtk_wm_dispatch_event(&aes_event_in, &aes_event_out, aes_msg_out) == 0) {
+ if( (aes_event_out.emo_events & MU_MESAG) != 0 ) {
+ LOG("WM: %d\n", aes_msg_out[0]);
+ switch(aes_msg_out[0]) {
+
+ case MN_SELECTED:
+ LOG("Menu Item: %d\n", aes_msg_out[4]);
+ deskmenu_dispatch_item(aes_msg_out[3], aes_msg_out[4]);
+ break;
+
+ case AP_TERM:
+ atari_quit = true;
+ break;
+ default:
+ break;
+ }
+ }
+ if((aes_event_out.emo_events & MU_KEYBD) != 0) {
+ uint16_t nkc = gem_to_norm( (short)aes_event_out.emo_kmeta,
+ (short)aes_event_out.emo_kreturn);
+ deskmenu_dispatch_keypress(aes_event_out.emo_kreturn,
+ aes_event_out.emo_kmeta, nkc);
+ }
+ }
+
+ tmp = window_list;
+ while(tmp){
+ if(tmp->root->redraw_slots.areas_used > 0){
+ window_process_redraws(tmp->root);
+ }
+ tmp = tmp->next;
+ }
+
+ /** @todo implement generic treeview redraw function. */
+ /** @todo rename hl to atari_hotlist or create getter for it... */
+
+ atari_treeview_flush_redraws();
+}
+
+/**
+ * Create and open a gui window for a browsing context.
+ *
+ * \param bw bw to create gui_window for
+ * \param existing an existing gui_window, may be NULL
+ * \param flags flags for gui window creation
+ * \return gui window, or NULL on error
+ *
+ * If GW_CREATE_CLONE flag is set existing is non-NULL.
+ *
+ * The created gui_window must include a reference to the browser
+ * window passed in the bw param.
+ */
+static struct gui_window *
+gui_window_create(struct browser_window *bw,
+ struct gui_window *existing,
+ gui_window_create_flags flags)
+{
+ struct gui_window *gw=NULL;
+ LOG("gw: %p, BW: %p, existing %p, flags: %d\n", gw, bw, existing, (int)flags);
+
+ gw = calloc(1, sizeof(struct gui_window));
+ if (gw == NULL)
+ return NULL;
+
+ LOG("new window: %p, bw: %p\n", gw, bw);
+ window_create(gw, bw, existing, WIDGET_STATUSBAR|WIDGET_TOOLBAR|WIDGET_RESIZE\
+ |WIDGET_SCROLL);
+ if (gw->root->win) {
+ GRECT pos = {
+ option_window_x, option_window_y,
+ option_window_width, option_window_height
+ };
+ gui_window_set_url(gw, corestring_nsurl_about_blank);
+ gui_window_set_pointer(gw, BROWSER_POINTER_DEFAULT);
+ gui_set_input_gui_window(gw);
+ window_open(gw->root, gw, pos);
+ }
+
+ /* add the window to the window list: */
+ if( window_list == NULL ) {
+ window_list = gw;
+ gw->next = NULL;
+ gw->prev = NULL;
+ } else {
+ struct gui_window * tmp = window_list;
+ while( tmp->next != NULL ) {
+ tmp = tmp->next;
+ }
+ tmp->next = gw;
+ gw->prev = tmp;
+ gw->next = NULL;
+ }
+
+ /* Loose focus: */
+ window_set_focus(gw->root, WIDGET_NONE, NULL );
+
+ /* trigger on-focus event (select all text): */
+ window_set_focus(gw->root, URL_WIDGET, NULL);
+
+ /* delete selection: */
+ toolbar_key_input(gw->root->toolbar, NK_DEL);
+
+ return( gw );
+
+}
+
+/**
+ * Destroy previously created gui window
+ *
+ * \param gw The gui window to destroy.
+ */
+void gui_window_destroy(struct gui_window *gw)
+{
+ if (gw == NULL)
+ return;
+
+ LOG("%s\n", __FUNCTION__);
+
+ if (input_window == gw) {
+ gui_set_input_gui_window(NULL);
+ }
+
+ nsatari_search_session_destroy(gw->search);
+ free(gw->browser);
+ free(gw->status);
+ free(gw->title);
+ free(gw->url);
+
+ /* unlink the window: */
+ if(gw->prev != NULL ) {
+ gw->prev->next = gw->next;
+ } else {
+ window_list = gw->next;
+ }
+ if( gw->next != NULL ) {
+ gw->next->prev = gw->prev;
+ }
+
+ window_unref_gui_window(gw->root, gw);
+
+ free(gw);
+ gw = NULL;
+
+ if(input_window == NULL) {
+ gw = window_list;
+ while( gw != NULL ) {
+ if(gw->root) {
+ gui_set_input_gui_window(gw);
+ break;
+ }
+ gw = gw->next;
+ }
+ }
+}
+
+/**
+ * Find the current dimensions of a browser window's content area.
+ *
+ * \param w gui_window to measure
+ * \param width receives width of window
+ * \param height receives height of window
+ * \param scaled whether to return scaled values
+ */
+static void
+gui_window_get_dimensions(struct gui_window *w,
+ int *width,
+ int *height,
+ bool scaled)
+{
+ if (w == NULL)
+ return;
+ GRECT rect;
+ window_get_grect(w->root, BROWSER_AREA_CONTENT, &rect);
+ *width = rect.g_w;
+ *height = rect.g_h;
+}
+
+/**
+ * Set the title of a window.
+ *
+ * \param gw window to update
+ * \param title new window title
+ */
+static void gui_window_set_title(struct gui_window *gw, const char *title)
+{
+ if (gw == NULL)
+ return;
+
+ if (gw->root) {
+
+ int l;
+ char * conv;
+ l = strlen(title)+1;
+ if (utf8_to_local_encoding(title, l-1, &conv) == NSERROR_OK ) {
+ l = MIN((uint32_t)atari_sysinfo.aes_max_win_title_len, strlen(conv));
+ if(gw->title == NULL)
+ gw->title = malloc(l);
+ else
+ gw->title = realloc(gw->title, l);
+
+ strncpy(gw->title, conv, l);
+ free( conv );
+ } else {
+ l = MIN((size_t)atari_sysinfo.aes_max_win_title_len, strlen(title));
+ if(gw->title == NULL)
+ gw->title = malloc(l);
+ else
+ gw->title = realloc(gw->title, l);
+ strncpy(gw->title, title, l);
+ }
+ gw->title[l] = 0;
+ if(input_window == gw)
+ window_set_title(gw->root, gw->title);
+ }
+}
+
+/* exported interface documented in atari/gui.h */
+void atari_window_set_status(struct gui_window *w, const char *text)
+{
+ int l;
+ if (w == NULL || text == NULL)
+ return;
+
+ assert(w->root);
+
+ l = strlen(text)+1;
+ if(w->status == NULL)
+ w->status = malloc(l);
+ else
+ w->status = realloc(w->status, l);
+
+ strncpy(w->status, text, l);
+ w->status[l] = 0;
+
+ if(input_window == w)
+ window_set_stauts(w->root, (char*)text);
+}
+
+static void atari_window_reformat(struct gui_window *gw)
+{
+ int width = 0, height = 0;
+
+ if (gw != NULL) {
+ gui_window_get_dimensions(gw, &width, &height, true);
+ browser_window_reformat(gw->browser->bw, false, width, height);
+ }
+}
+
+static void gui_window_redraw_window(struct gui_window *gw)
+{
+ CMP_BROWSER b;
+ GRECT rect;
+ if (gw == NULL)
+ return;
+ b = gw->browser;
+ window_get_grect(gw->root, BROWSER_AREA_CONTENT, &rect);
+ window_schedule_redraw_grect(gw->root, &rect);
+}
+
+static void gui_window_update_box(struct gui_window *gw, const struct rect *rect)
+{
+ GRECT area;
+ struct gemtk_wm_scroll_info_s *slid;
+
+ if (gw == NULL)
+ return;
+
+ slid = gemtk_wm_get_scroll_info(gw->root->win);
+
+ window_get_grect(gw->root, BROWSER_AREA_CONTENT, &area);
+ area.g_x += rect->x0 - (slid->x_pos * slid->x_unit_px);
+ area.g_y += rect->y0 - (slid->y_pos * slid->y_unit_px);
+ area.g_w = rect->x1 - rect->x0;
+ area.g_h = rect->y1 - rect->y0;
+ //dbg_grect("update box", &area);
+ window_schedule_redraw_grect(gw->root, &area);
+}
+
+bool gui_window_get_scroll(struct gui_window *w, int *sx, int *sy)
+{
+ if (w == NULL)
+ return false;
+
+ window_get_scroll(w->root, sx, sy);
+
+ return( true );
+}
+
+static void gui_window_set_scroll(struct gui_window *w, int sx, int sy)
+{
+ if ( (w == NULL)
+ || (w->browser->bw == NULL)
+ || (!browser_window_has_content(w->browser->bw)))
+ return;
+
+ LOG("scroll (gui_window: %p) %d, %d\n", w, sx, sy);
+ window_scroll_by(w->root, sx, sy);
+ return;
+
+}
+
+/**
+ * Update the extent of the inside of a browser window to that of the
+ * current content.
+ *
+ * It seems this method is called when content size got adjusted, so
+ * that we can adjust scroll info. We also have to call it when tab
+ * change occurs.
+ *
+ * \param gw gui_window to update the extent of
+ */
+static void gui_window_update_extent(struct gui_window *gw)
+{
+
+ if(browser_window_has_content(gw->browser->bw)) {
+ /** @todo store content size. */
+ if(window_get_active_gui_window(gw->root) == gw) {
+ int width, height;
+ GRECT area;
+ browser_window_get_extents(gw->browser->bw, false, &width, &height);
+ window_set_content_size(gw->root, width, height);
+ window_update_back_forward(gw->root);
+ window_get_grect(gw->root, BROWSER_AREA_CONTENT, &area);
+ window_schedule_redraw_grect(gw->root, &area);
+ }
+ }
+}
+
+
+/**
+ * set the pointer shape
+ */
+void gui_window_set_pointer(struct gui_window *gw, gui_pointer_shape shape)
+{
+ if (gw == NULL)
+ return;
+
+ switch (shape) {
+ case GUI_POINTER_POINT: /* link */
+ gw->cursor = &gem_cursors.hand;
+ break;
+
+ case GUI_POINTER_MENU:
+ gw->cursor = &gem_cursors.menu;
+ break;
+
+ case GUI_POINTER_CARET: /* input */
+ gw->cursor = &gem_cursors.ibeam;
+ break;
+
+ case GUI_POINTER_CROSS:
+ gw->cursor = &gem_cursors.cross;
+ break;
+
+ case GUI_POINTER_MOVE:
+ gw->cursor = &gem_cursors.sizeall;
+ break;
+
+ case GUI_POINTER_RIGHT:
+ case GUI_POINTER_LEFT:
+ gw->cursor = &gem_cursors.sizewe;
+ break;
+
+ case GUI_POINTER_UP:
+ case GUI_POINTER_DOWN:
+ gw->cursor = &gem_cursors.sizens;
+ break;
+
+ case GUI_POINTER_RU:
+ case GUI_POINTER_LD:
+ gw->cursor = &gem_cursors.sizenesw;
+ break;
+
+ case GUI_POINTER_RD:
+ case GUI_POINTER_LU:
+ gw->cursor = &gem_cursors.sizenwse;
+ break;
+
+ case GUI_POINTER_WAIT:
+ gw->cursor = &gem_cursors.wait;
+ break;
+
+ case GUI_POINTER_PROGRESS:
+ gw->cursor = &gem_cursors.appstarting;
+ break;
+
+ case GUI_POINTER_NO_DROP:
+ gw->cursor = &gem_cursors.nodrop;
+ break;
+
+ case GUI_POINTER_NOT_ALLOWED:
+ gw->cursor = &gem_cursors.deny;
+ break;
+
+ case GUI_POINTER_HELP:
+ gw->cursor = &gem_cursors.help;
+ break;
+
+ default:
+ gw->cursor = &gem_cursors.arrow;
+ break;
+ }
+
+ if (input_window == gw) {
+ gem_set_cursor(gw->cursor);
+ }
+}
+
+
+static nserror gui_window_set_url(struct gui_window *w, nsurl *url)
+{
+ int l;
+
+ if (w == NULL)
+ return NSERROR_OK;
+
+ l = strlen(nsurl_access(url))+1;
+
+ if (w->url == NULL) {
+ w->url = malloc(l);
+ } else {
+ w->url = realloc(w->url, l);
+ }
+ strncpy(w->url, nsurl_access(url), l);
+ w->url[l] = 0;
+ if(input_window == w->root->active_gui_window) {
+ toolbar_set_url(w->root->toolbar, nsurl_access(url));
+ }
+
+ return NSERROR_OK;
+}
+
+char * gui_window_get_url(struct gui_window *gw)
+{
+ if (gw == NULL) {
+ return(NULL);
+ }
+ return(gw->url);
+}
+
+char * gui_window_get_title(struct gui_window *gw)
+{
+ if (gw == NULL) {
+ return(NULL);
+ }
+ return(gw->title);
+}
+
+static void throbber_advance( void * data )
+{
+
+ struct gui_window * gw = (struct gui_window *)data;
+
+ if (gw->root == NULL)
+ return;
+ if (gw->root->toolbar == NULL)
+ return;
+
+ if (gw->root->toolbar->throbber.running == false)
+ return;
+
+ toolbar_throbber_progress(gw->root->toolbar);
+ atari_schedule(1000, throbber_advance, gw );
+}
+
+static void gui_window_start_throbber(struct gui_window *w)
+{
+ if (w == NULL)
+ return;
+
+ toolbar_set_throbber_state(w->root->toolbar, true);
+ atari_schedule(1000, throbber_advance, w );
+ rendering = true;
+}
+
+static void gui_window_stop_throbber(struct gui_window *w)
+{
+ if (w == NULL)
+ return;
+ if (w->root->toolbar->throbber.running == false)
+ return;
+
+ atari_schedule(-1, throbber_advance, w);
+
+ toolbar_set_throbber_state(w->root->toolbar, false);
+
+ rendering = false;
+}
+
+/**
+ * Place caret in window
+ */
+static void
+gui_window_place_caret(struct gui_window *w, int x, int y, int height,
+ const struct rect *clip)
+{
+ window_place_caret(w->root, 1, x, y, height, NULL);
+ w->root->caret.state |= CARET_STATE_ENABLED;
+ return;
+}
+
+
+/**
+ * clear window caret
+ */
+static void
+gui_window_remove_caret(struct gui_window *w)
+{
+ if (w == NULL)
+ return;
+
+ if ((w->root->caret.state & CARET_STATE_ENABLED) != 0) {
+ //printf("gw hide caret\n");
+ window_place_caret(w->root, 0, -1, -1, -1, NULL);
+ w->root->caret.state &= ~CARET_STATE_ENABLED;
+ }
+ return;
+}
+
+/**
+ * Set a favicon for a gui window.
+ *
+ * \param g window to update.
+ * \param icon handle to object to use as icon.
+ */
+static void
+gui_window_set_icon(struct gui_window *g, hlcache_handle *icon)
+{
+ struct bitmap *bmp_icon;
+
+ bmp_icon = (icon != NULL) ? content_get_bitmap(icon) : NULL;
+ g->icon = bmp_icon;
+ if(input_window == g) {
+ window_set_icon(g->root, bmp_icon);
+ }
+}
+
+static void gui_window_new_content(struct gui_window *w)
+{
+ struct gemtk_wm_scroll_info_s *slid = gemtk_wm_get_scroll_info(w->root->win);
+ slid->x_pos = 0;
+ slid->y_pos = 0;
+ gemtk_wm_update_slider(w->root->win, GEMTK_WM_VH_SLIDER);
+ gui_window_redraw_window(w);
+}
+
+
+/**
+ * Core asks front end for clipboard contents.
+ *
+ * \param buffer UTF-8 text, allocated by front end, ownership yeilded to core
+ * \param length Byte length of UTF-8 text in buffer
+ */
+static void gui_get_clipboard(char **buffer, size_t *length)
+{
+ char *clip;
+
+ *length = 0;
+ *buffer = 0;
+
+ clip = scrap_txt_read();
+
+ if(clip == NULL) {
+ return;
+ } else {
+
+ // clipboard is in atari encoding, convert it to utf8:
+
+ size_t clip_len;
+ char *utf8 = NULL;
+
+ clip_len = strlen(clip);
+ if (clip_len > 0) {
+ nserror ret;
+ ret = utf8_to_local_encoding(clip, clip_len, &utf8);
+ if (ret == NSERROR_OK && utf8 != NULL) {
+ *buffer = utf8;
+ *length = strlen(utf8);
+ } else {
+ assert(ret == NSERROR_OK && utf8 != NULL);
+ }
+ }
+
+ free(clip);
+ }
+}
+
+/**
+ * Core tells front end to put given text in clipboard
+ *
+ * \param buffer UTF-8 text, owned by core
+ * \param length Byte length of UTF-8 text in buffer
+ * \param styles Array of styles given to text runs, owned by core, or NULL
+ * \param n_styles Number of text run styles in array
+ */
+static void gui_set_clipboard(const char *buffer, size_t length,
+ nsclipboard_styles styles[], int n_styles)
+{
+ if (length > 0 && buffer != NULL) {
+
+ // convert utf8 input to atari encoding:
+
+ nserror ret;
+ char *clip = NULL;
+
+ ret = utf8_to_local_encoding(buffer,length, &clip);
+ if (ret == NSERROR_OK) {
+ scrap_txt_write(clip);
+ } else {
+ assert(ret == NSERROR_OK);
+ }
+ free(clip);
+ }
+}
+
+static void gui_401login_open(nsurl *url, const char *realm,
+ nserror (*cb)(bool proceed, void *pw), void *cbpw)
+{
+ bool bres;
+ char * out = NULL;
+ bres = login_form_do( url, (char*)realm, &out);
+ if (bres) {
+ LOG("url: %s, realm: %s, auth: %s\n", nsurl_access(url), realm, out);
+ urldb_set_auth_details(url, realm, out);
+ }
+ if (out != NULL) {
+ free( out );
+ }
+ if (cb != NULL) {
+ cb(bres, cbpw);
+ }
+
+}
+
+static void
+gui_cert_verify(nsurl *url, const struct ssl_cert_info *certs,
+ unsigned long num, nserror (*cb)(bool proceed, void *pw),
+ void *cbpw)
+{
+ struct sslcert_session_data *data;
+ LOG("url %s", nsurl_access(url));
+
+ // TODO: localize string
+ int b = form_alert(1, "[2][SSL Verify failed, continue?][Continue|Abort|Details...]");
+ if(b == 1){
+ // Accept
+ urldb_set_cert_permissions(url, true);
+ cb(true, cbpw);
+ } else if(b == 2) {
+ // Reject
+ urldb_set_cert_permissions(url, false);
+ cb(false, cbpw);
+ } else if(b == 3) {
+ // Inspect
+ sslcert_viewer_create_session_data(num, url, cb, cbpw, certs,
+ &data);
+ atari_sslcert_viewer_open(data);
+ }
+
+}
+
+void gui_set_input_gui_window(struct gui_window *gw)
+{
+ LOG("Setting input window from: %p to %p\n", input_window, gw);
+ input_window = gw;
+}
+
+struct gui_window * gui_get_input_window(void)
+{
+ return(input_window);
+}
+
+static void gui_quit(void)
+{
+ LOG("quitting");
+
+ struct gui_window *gw = window_list;
+ struct gui_window *tmp = window_list;
+
+ /* Destroy all remaining browser windows: */
+ while (gw) {
+ tmp = gw->next;
+ browser_window_destroy(gw->browser->bw);
+ gw = tmp;
+ }
+
+ /* destroy the treeview windows: */
+ atari_global_history_destroy();
+ atari_hotlist_destroy();
+ atari_cookie_manager_destroy();
+
+ /* shutdown netsurf treeview framework: */
+ treeview_fini();
+
+ /* shutdown the toolbar framework: */
+ toolbar_exit();
+
+ /* save persistent informations: */
+ urldb_save_cookies(nsoption_charp(cookie_file));
+ urldb_save(nsoption_charp(url_file));
+
+ deskmenu_destroy();
+ gemtk_wm_exit();
+
+ rsrc_free();
+
+ LOG("Shutting down plotter");
+ plot_finalise();
+ LOG("done");
+}
+
+/**
+ * Process commandline parameters.
+ */
+static bool
+process_cmdline(int argc, char** argv)
+{
+ int opt;
+ bool set_default_dimensions = true;
+
+ LOG("argc %d, argv %p", argc, argv);
+
+ if ((nsoption_int(window_width) != 0) && (nsoption_int(window_height) != 0)) {
+
+ option_window_width = nsoption_int(window_width);
+ option_window_height = nsoption_int(window_height);
+ option_window_x = nsoption_int(window_x);
+ option_window_y = nsoption_int(window_y);
+
+ if (option_window_width <= desk_area.g_w
+ && option_window_height < desk_area.g_h) {
+ set_default_dimensions = false;
+ }
+ }
+
+ if (set_default_dimensions) {
+ if( sys_type() == SYS_TOS ) {
+ /* on single tasking OS, start as fulled window: */
+ option_window_width = desk_area.g_w;
+ option_window_height = desk_area.g_h;
+ option_window_x = desk_area.g_w/2-(option_window_width/2);
+ option_window_y = (desk_area.g_h/2)-(option_window_height/2);
+ } else {
+ option_window_width = 600;
+ option_window_height = 360;
+ option_window_x = 10;
+ option_window_y = 30;
+ }
+ }
+
+ if (nsoption_charp(homepage_url) != NULL)
+ option_homepage_url = nsoption_charp(homepage_url);
+ else
+ option_homepage_url = NETSURF_HOMEPAGE;
+
+ while((opt = getopt(argc, argv, "w:h:")) != -1) {
+ switch (opt) {
+ case 'w':
+ option_window_width = atoi(optarg);
+ break;
+
+ case 'h':
+ option_window_height = atoi(optarg);
+ break;
+
+ default:
+ fprintf(stderr,
+ "Usage: %s [w,h,v] url\n",
+ argv[0]);
+ return false;
+ }
+ }
+
+ if (optind < argc) {
+ option_homepage_url = argv[optind];
+ }
+ return true;
+}
+
+static inline void create_cursor(int flags, short mode, void * form,
+ MFORM_EX * m)
+{
+ m->flags = flags;
+ m->number = mode;
+ if( flags & MFORM_EX_FLAG_USERFORM ) {
+ m->number = mode;
+ m->tree = (OBJECT*)form;
+ }
+}
+
+static nsurl *gui_get_resource_url(const char *path)
+{
+ char buf[PATH_MAX];
+ nsurl *url = NULL;
+
+ atari_find_resource((char*)&buf, path, path);
+
+ netsurf_path_to_nsurl(buf, &url);
+
+ return url;
+}
+
+/**
+ * Set option defaults for atari frontend
+ *
+ * @param defaults The option table to update.
+ * @return error status.
+ */
+static nserror set_defaults(struct nsoption_s *defaults)
+{
+ /* Set defaults for absent option strings */
+ nsoption_setnull_charp(cookie_file, strdup("cookies"));
+ if (nsoption_charp(cookie_file) == NULL) {
+ LOG("Failed initialising string options");
+ return NSERROR_BAD_PARAMETER;
+ }
+ return NSERROR_OK;
+}
+
+static char *theapp = (char*)"NetSurf";
+
+/**
+ * Initialise atari gui.
+ */
+static void gui_init(int argc, char** argv)
+{
+ char buf[PATH_MAX];
+ OBJECT * cursors;
+
+ atari_find_resource(buf, "netsurf.rsc", "./res/netsurf.rsc");
+ LOG("Using RSC file: %s ", (char *)&buf);
+ if (rsrc_load(buf)==0) {
+
+ char msg[1024];
+
+ snprintf(msg, 1024, "Unable to open GEM Resource file (%s)!", buf);
+ die(msg);
+ }
+
+ wind_get_grect(0, WF_WORKXYWH, &desk_area);
+
+ create_cursor(0, POINT_HAND, NULL, &gem_cursors.hand );
+ create_cursor(0, TEXT_CRSR, NULL, &gem_cursors.ibeam );
+ create_cursor(0, THIN_CROSS, NULL, &gem_cursors.cross);
+ create_cursor(0, BUSY_BEE, NULL, &gem_cursors.wait);
+ create_cursor(0, ARROW, NULL, &gem_cursors.arrow);
+ create_cursor(0, OUTLN_CROSS, NULL, &gem_cursors.sizeall);
+ create_cursor(0, OUTLN_CROSS, NULL, &gem_cursors.sizenesw);
+ create_cursor(0, OUTLN_CROSS, NULL, &gem_cursors.sizenwse);
+ cursors = gemtk_obj_get_tree(CURSOR);
+ create_cursor(MFORM_EX_FLAG_USERFORM, CURSOR_APPSTART,
+ cursors, &gem_cursors.appstarting);
+ gem_set_cursor( &gem_cursors.appstarting );
+ create_cursor(MFORM_EX_FLAG_USERFORM, CURSOR_SIZEWE,
+ cursors, &gem_cursors.sizewe);
+ create_cursor(MFORM_EX_FLAG_USERFORM, CURSOR_SIZENS,
+ cursors, &gem_cursors.sizens);
+ create_cursor(MFORM_EX_FLAG_USERFORM, CURSOR_NODROP,
+ cursors, &gem_cursors.nodrop);
+ create_cursor(MFORM_EX_FLAG_USERFORM, CURSOR_DENY,
+ cursors, &gem_cursors.deny);
+ create_cursor(MFORM_EX_FLAG_USERFORM, CURSOR_MENU,
+ cursors, &gem_cursors.menu);
+ create_cursor(MFORM_EX_FLAG_USERFORM, CURSOR_HELP,
+ cursors, &gem_cursors.help);
+
+ LOG("Enabling core select menu");
+ nsoption_set_bool(core_select_menu, true);
+
+ LOG("Loading url.db from: %s", nsoption_charp(url_file));
+ if( strlen(nsoption_charp(url_file)) ) {
+ urldb_load(nsoption_charp(url_file));
+ }
+
+ LOG("Loading cookies from: %s", nsoption_charp(cookie_file));
+ if( strlen(nsoption_charp(cookie_file)) ) {
+ urldb_load_cookies(nsoption_charp(cookie_file));
+ }
+
+ if (process_cmdline(argc,argv) != true)
+ die("unable to process command line.\n");
+
+ LOG("Initializing NKC...");
+ nkc_init();
+
+ LOG("Initializing plotters...");
+ plot_init(nsoption_charp(atari_font_driver));
+
+ aes_event_in.emi_m1leave = MO_LEAVE;
+ aes_event_in.emi_m1.g_w = 1;
+ aes_event_in.emi_m1.g_h = 1;
+ //next_poll = clock() + (CLOCKS_PER_SEC>>3);
+
+ deskmenu_init();
+ menu_register( -1, theapp);
+ if (sys_type() & (SYS_MAGIC|SYS_NAES|SYS_XAAES)) {
+ menu_register( _AESapid, (char*)" NetSurf ");
+ }
+ gemtk_wm_init();
+
+ /* Initialize the netsurf treeview framework with default font size: */
+ treeview_init(0);
+
+ /* Initialize the specific treeview windows: */
+ atari_global_history_init();
+ atari_hotlist_init();
+ atari_cookie_manager_init();
+
+ /* Initialize the toolbar framework: */
+ toolbar_init();
+}
+
+static struct gui_window_table atari_window_table = {
+ .create = gui_window_create,
+ .destroy = gui_window_destroy,
+ .redraw = gui_window_redraw_window,
+ .update = gui_window_update_box,
+ .get_scroll = gui_window_get_scroll,
+ .set_scroll = gui_window_set_scroll,
+ .get_dimensions = gui_window_get_dimensions,
+ .update_extent = gui_window_update_extent,
+ .reformat = atari_window_reformat,
+
+ .set_title = gui_window_set_title,
+ .set_url = gui_window_set_url,
+ .set_icon = gui_window_set_icon,
+ .set_status = atari_window_set_status,
+ .set_pointer = gui_window_set_pointer,
+ .place_caret = gui_window_place_caret,
+ .remove_caret = gui_window_remove_caret,
+ .new_content = gui_window_new_content,
+ .start_throbber = gui_window_start_throbber,
+ .stop_throbber = gui_window_stop_throbber,
+};
+
+static struct gui_clipboard_table atari_clipboard_table = {
+ .get = gui_get_clipboard,
+ .set = gui_set_clipboard,
+};
+
+static struct gui_fetch_table atari_fetch_table = {
+ .filetype = fetch_filetype,
+
+ .get_resource_url = gui_get_resource_url,
+};
+
+static struct gui_misc_table atari_misc_table = {
+ .schedule = atari_schedule,
+ .warning = atari_warn_user,
+
+ .quit = gui_quit,
+ .cert_verify = gui_cert_verify,
+ .login = gui_401login_open,
+};
+
+/* #define WITH_DBG_LOGFILE 1 */
+/**
+ * Entry point from OS.
+ *
+ * /param argc The number of arguments in the string vector.
+ * /param argv The argument string vector.
+ * /return The return code to the OS
+ */
+int main(int argc, char** argv)
+{
+ char messages[PATH_MAX];
+ char store[PATH_MAX];
+ const char *addr;
+ char * file_url = NULL;
+ struct stat stat_buf;
+ nsurl *url;
+ nserror ret;
+
+ struct netsurf_table atari_table = {
+ .misc = &atari_misc_table,
+ .window = &atari_window_table,
+ .clipboard = &atari_clipboard_table,
+ .download = atari_download_table,
+ .fetch = &atari_fetch_table,
+ .file = atari_file_table,
+ .utf8 = atari_utf8_table,
+ .search = atari_search_table,
+ .llcache = filesystem_llcache_table,
+ .bitmap = atari_bitmap_table,
+ .layout = atari_layout_table
+ };
+
+ ret = netsurf_register(&atari_table);
+ if (ret != NSERROR_OK) {
+ die("NetSurf operation table failed registration");
+ }
+
+ /** @todo logging file descriptor update belongs in a nslog_init callback */
+ setbuf(stderr, NULL);
+ setbuf(stdout, NULL);
+#ifdef WITH_DBG_LOGFILE
+ freopen("stdout.log", "a+", stdout);
+ freopen("stderr.log", "a+", stderr);
+#endif
+
+ graf_mouse(BUSY_BEE, NULL);
+
+ init_app(NULL);
+
+ init_os_info();
+
+ atari_find_resource((char*)&messages, "messages", "res/messages");
+ atari_find_resource((char*)&options, "Choices", "Choices");
+ atari_find_resource((char*)&store, "cache", "res/cache");
+
+ /* initialise logging - not fatal if it fails but not much we can
+ * do about it
+ */
+ nslog_init(NULL, &argc, argv);
+
+ /* user options setup */
+ ret = nsoption_init(set_defaults, &nsoptions, &nsoptions_default);
+ if (ret != NSERROR_OK) {
+ die("Options failed to initialise");
+ }
+ nsoption_read(options, NULL);
+ nsoption_commandline(&argc, argv, NULL);
+
+ ret = messages_add_from_file(messages);
+
+ /* common initialisation */
+ LOG("Initialising core...");
+ ret = netsurf_init(store);
+ if (ret != NSERROR_OK) {
+ die("NetSurf failed to initialise");
+ }
+
+ LOG("Initializing GUI...");
+ gui_init(argc, argv);
+
+ graf_mouse( ARROW , NULL);
+
+ LOG("Creating initial browser window...");
+ addr = option_homepage_url;
+ if (strncmp(addr, "file://", 7) && strncmp(addr, "http://", 7)) {
+ if (stat(addr, &stat_buf) == 0) {
+ file_url = local_file_to_url(addr);
+ addr = file_url;
+ }
+ }
+
+ /* create an initial browser window */
+ ret = nsurl_create(addr, &url);
+ if (ret == NSERROR_OK) {
+ ret = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (ret != NSERROR_OK) {
+ atari_warn_user(messages_get_errorcode(ret), 0);
+ } else {
+ LOG("Entering Atari event mainloop...");
+ while (!atari_quit) {
+ atari_poll();
+ }
+ }
+
+ netsurf_exit();
+
+ free(file_url);
+
+#ifdef WITH_DBG_LOGFILE
+ fclose(stdout);
+ fclose(stderr);
+#endif
+ LOG("exit_gem");
+ exit_gem();
+
+ return 0;
+}
diff --git a/frontends/atari/gui.h b/frontends/atari/gui.h
new file mode 100644
index 000000000..50184d029
--- /dev/null
+++ b/frontends/atari/gui.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_ATARI_GUI_H_
+#define NS_ATARI_GUI_H_
+
+#include <stdbool.h>
+#include <mt_gem.h>
+
+#include "desktop/mouse.h"
+
+#include "atari/redrawslots.h"
+#include "atari/gemtk/gemtk.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#define CARET_STATE_VISIBLE 0x01
+#define CARET_STATE_ENABLED 0x02
+
+struct s_caret {
+ GRECT dimensions;
+ MFDB symbol;
+ int fd_size;
+ unsigned short state;
+};
+
+struct point_s {
+ int x;
+ int y;
+};
+
+typedef struct point_s POINT;
+
+#define MFORM_EX_FLAG_USERFORM 0x01
+
+struct mform_ex_s
+{
+ unsigned char flags;
+ int number;
+ OBJECT * tree;
+};
+
+typedef struct mform_ex_s MFORM_EX;
+
+struct s_gem_cursors {
+ MFORM_EX hand;
+ MFORM_EX ibeam;
+ MFORM_EX cross;
+ MFORM_EX sizeall;
+ MFORM_EX sizewe;
+ MFORM_EX sizens;
+ MFORM_EX sizenesw;
+ MFORM_EX sizenwse;
+ MFORM_EX wait;
+ MFORM_EX appstarting;
+ MFORM_EX nodrop;
+ MFORM_EX deny;
+ MFORM_EX help;
+ MFORM_EX menu;
+ MFORM_EX arrow;
+} gem_cursors;
+
+enum focus_element_type {
+ WIDGET_NONE=0,
+ URL_WIDGET,
+ SEARCH_INPUT,
+ BROWSER
+};
+
+
+struct s_focus_info
+{
+ enum focus_element_type type;
+ void * element;
+};
+
+/* defines for data attached to components: */
+#define CDT_OBJECT 0x004f424aUL
+#define CDT_OWNER 0x03UL
+#define CDT_ICON 0x04UL
+#define CDT_ICON_TYPE 0x05UL
+# define CDT_ICON_TYPE_NONE 0x00UL
+# define CDT_ICON_TYPE_OBJECT 0x01UL
+# define CDT_ICON_TYPE_BITMAP 0x02UL
+
+
+struct gui_window;
+struct s_browser;
+struct s_statusbar;
+struct s_toolbar;
+
+typedef struct s_toolbar * CMP_TOOLBAR;
+typedef struct s_statusbar * CMP_STATUSBAR;
+typedef struct s_browser * CMP_BROWSER;
+
+/*
+ This is the "main" window. It can consist of several components
+ and also holds information shared by several frames within
+ the window.
+*/
+struct s_gui_win_root
+{
+ short aes_handle;
+ GUIWIN *win;
+ CMP_TOOLBAR toolbar;
+ CMP_STATUSBAR statusbar;
+ struct s_focus_info focus;
+ float scale;
+ char * title;
+ struct bitmap * icon;
+ struct gui_window *active_gui_window;
+ struct s_redrw_slots redraw_slots;
+ struct s_caret caret;
+ /* current size of window on screen: */
+ GRECT loc;
+};
+typedef struct s_gui_win_root ROOTWIN;
+
+struct s_browser
+{
+ struct browser_window * bw;
+ bool attached;
+};
+
+/*
+ This is the part of the gui which is known by netsurf core.
+ You must implement it. Altough, you are free how to do it.
+ Each of the browser "viewports" managed by netsurf are bound
+ to this structure.
+*/
+struct gui_window {
+ struct s_gui_win_root * root;
+ struct s_browser * browser;
+ MFORM_EX *cursor;
+ /* icon to be drawn when iconified, or NULL for default resource. */
+ char * status;
+ char * title;
+ char * url;
+ float scale;
+ struct bitmap * icon;
+ struct s_caret caret;
+ struct s_search_form_session *search;
+ struct gui_window *next, *prev;
+};
+
+extern struct gui_window *window_list;
+
+/* -------------------------------------------------------------------------- */
+/* Public - non core gui window functions */
+/* -------------------------------------------------------------------------- */
+void gui_set_input_gui_window(struct gui_window *gw);
+struct gui_window *gui_get_input_window(void);
+char *gui_window_get_url(struct gui_window *gw);
+char *gui_window_get_title(struct gui_window *gw);
+
+/**
+ * Set the status bar of a browser window.
+ *
+ * \param w The gui_window to update.
+ * \param text new status text
+ */
+void atari_window_set_status(struct gui_window *w, const char *text);
+void gui_window_set_pointer(struct gui_window *gw, gui_pointer_shape shape);
+void gui_window_destroy(struct gui_window *w);
+
+#endif
diff --git a/frontends/atari/history.c b/frontends/atari/history.c
new file mode 100644
index 000000000..ba72c7f00
--- /dev/null
+++ b/frontends/atari/history.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2013 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <assert.h>
+#include <inttypes.h>
+
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "desktop/mouse.h"
+#include "desktop/global_history.h"
+#include "desktop/core_window.h"
+
+#include "atari/treeview.h"
+#include "atari/history.h"
+#include "atari/gemtk/gemtk.h"
+#include "atari/res/netsurf.rsh"
+
+extern GRECT desk_area;
+
+struct atari_global_history_s atari_global_history;
+
+/* Setup Atari Treeview Callbacks: */
+
+static nserror
+atari_global_history_init_phase2(struct core_window *cw,
+ struct core_window_callback_table *cb_t)
+{
+ LOG("cw %p", cw);
+ return(global_history_init(cb_t, cw));
+}
+
+static void atari_global_history_finish(struct core_window *cw)
+{
+ LOG("cw %p", cw);
+ global_history_fini();
+}
+
+static void atari_global_history_draw(struct core_window *cw, int x,
+ int y, struct rect *clip,
+ const struct redraw_context *ctx)
+{
+ global_history_redraw(x, y, clip, ctx);
+}
+
+static void atari_global_history_keypress(struct core_window *cw, uint32_t ucs4)
+{
+ LOG("ucs4: %"PRIu32, ucs4);
+ global_history_keypress(ucs4);
+}
+
+static void
+atari_global_history_mouse_action(struct core_window *cw,
+ browser_mouse_state mouse,
+ int x, int y)
+{
+ LOG("x: %d, y: %d\n", x, y);
+
+ global_history_mouse_action(mouse, x, y);
+}
+
+void atari_global_history_close(void)
+{
+ atari_treeview_close(atari_global_history.tv);
+}
+
+
+static short handle_event(GUIWIN *win, EVMULT_OUT *ev_out, short msg[8])
+{
+ short retval = 0;
+
+ LOG("win %p", win);
+
+ if (ev_out->emo_events & MU_MESAG) {
+ switch (msg[0]) {
+ case WM_CLOSED:
+ atari_global_history_close();
+ retval = 1;
+ break;
+
+ default: break;
+ }
+ }
+
+ return(retval);
+}
+
+static struct atari_treeview_callbacks atari_global_history_treeview_callbacks = {
+ .init_phase2 = atari_global_history_init_phase2,
+ .finish = atari_global_history_finish,
+ .draw = atari_global_history_draw,
+ .keypress = atari_global_history_keypress,
+ .mouse_action = atari_global_history_mouse_action,
+ .gemtk_user_func = handle_event
+};
+
+void atari_global_history_init(void)
+{
+ if (atari_global_history.init == false) {
+ if( atari_global_history.window == NULL ) {
+ int flags = ATARI_TREEVIEW_WIDGETS;
+ short handle = -1;
+ OBJECT * tree = gemtk_obj_get_tree(TOOLBAR_HISTORY);
+ assert( tree );
+
+ handle = wind_create(flags, 0, 0, desk_area.g_w, desk_area.g_h);
+ atari_global_history.window = gemtk_wm_add(handle, GEMTK_WM_FLAG_DEFAULTS, NULL);
+ if( atari_global_history.window == NULL ) {
+ gemtk_msg_box_show(GEMTK_MSG_BOX_ALERT,
+ "Failed to allocate History");
+ return;
+ }
+ wind_set_str(handle, WF_NAME, (char*)messages_get("History"));
+ gemtk_wm_set_toolbar(atari_global_history.window, tree, 0, 0);
+ gemtk_wm_unlink(atari_global_history.window);
+
+ atari_global_history.tv = atari_treeview_create(
+ atari_global_history.window,
+ &atari_global_history_treeview_callbacks,
+ NULL, flags);
+
+ if (atari_global_history.tv == NULL) {
+ /* handle it properly, clean up previous allocs */
+ LOG("Failed to allocate treeview");
+ return;
+ }
+ }
+ }
+ atari_global_history.init = true;
+}
+
+void atari_global_history_open(void)
+{
+ assert(atari_global_history.init);
+
+ if (atari_global_history.init == false) {
+ return;
+ }
+
+ if (atari_treeview_is_open(atari_global_history.tv) == false) {
+
+ GRECT pos;
+ pos.g_x = desk_area.g_w - desk_area.g_w / 4;
+ pos.g_y = desk_area.g_y;
+ pos.g_w = desk_area.g_w / 4;
+ pos.g_h = desk_area.g_h;
+
+ atari_treeview_open(atari_global_history.tv, &pos);
+ } else {
+ wind_set(gemtk_wm_get_handle(atari_global_history.window), WF_TOP, 1, 0, 0, 0);
+ }
+}
+
+
+void atari_global_history_destroy(void)
+{
+
+ if ( atari_global_history.init == false) {
+ return;
+ }
+
+ if ( atari_global_history.window != NULL ) {
+ if (atari_treeview_is_open(atari_global_history.tv))
+ atari_global_history_close();
+ wind_delete(gemtk_wm_get_handle(atari_global_history.window));
+ gemtk_wm_remove(atari_global_history.window);
+ atari_global_history.window = NULL;
+ atari_treeview_delete(atari_global_history.tv);
+ atari_global_history.init = false;
+ }
+ LOG("done");
+}
+
+void atari_global_history_redraw(void)
+{
+ atari_treeview_redraw(atari_global_history.tv);
+}
diff --git a/frontends/atari/history.h b/frontends/atari/history.h
new file mode 100644
index 000000000..06a5d32f2
--- /dev/null
+++ b/frontends/atari/history.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2013 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_ATARI_HISTORY_H
+#define NS_ATARI_HISTORY_H
+
+struct core_window;
+
+struct atari_global_history_s {
+ GUIWIN * window;
+ //struct atari_treeview_window * tv; /*< The hotlist treeview handle. */
+ struct core_window *tv;
+ bool init;
+};
+
+extern struct atari_global_history_s atari_global_history;
+
+void atari_global_history_init(void);
+void atari_global_history_open(void);
+void atari_global_history_close(void);
+void atari_global_history_destroy(void);
+void atari_global_history_redraw(void);
+
+#endif
diff --git a/frontends/atari/hotlist.c b/frontends/atari/hotlist.c
new file mode 100644
index 000000000..1130e6251
--- /dev/null
+++ b/frontends/atari/hotlist.c
@@ -0,0 +1,315 @@
+/*
+ * Copyright 2013 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <ctype.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "content/content.h"
+#include "content/hlcache.h"
+#include "content/urldb.h"
+#include "utils/nsoption.h"
+#include "desktop/hotlist.h"
+#include "desktop/tree.h"
+#include "desktop/core_window.h"
+
+#include "atari/gui.h"
+#include "atari/misc.h"
+#include "atari/treeview.h"
+#include "atari/hotlist.h"
+#include "atari/findfile.h"
+#include "atari/gemtk/gemtk.h"
+#include "atari/res/netsurf.rsh"
+
+extern GRECT desk_area;
+
+struct atari_hotlist hl;
+
+/* Setup Atari Treeview Callbacks: */
+static nserror atari_hotlist_init_phase2(struct core_window *cw,
+ struct core_window_callback_table * default_callbacks);
+static void atari_hotlist_finish(struct core_window *cw);
+static void atari_hotlist_keypress(struct core_window *cw,
+ uint32_t ucs4);
+static void atari_hotlist_mouse_action(struct core_window *cw,
+ browser_mouse_state mouse,
+ int x, int y);
+static void atari_hotlist_draw(struct core_window *cw, int x,
+ int y, struct rect *clip,
+ const struct redraw_context *ctx);
+static short handle_event(GUIWIN *win, EVMULT_OUT *ev_out, short msg[8]);
+
+static struct atari_treeview_callbacks atari_hotlist_treeview_callbacks = {
+ .init_phase2 = atari_hotlist_init_phase2,
+ .finish = atari_hotlist_finish,
+ .draw = atari_hotlist_draw,
+ .keypress = atari_hotlist_keypress,
+ .mouse_action = atari_hotlist_mouse_action,
+ .gemtk_user_func = handle_event
+};
+
+static nserror atari_hotlist_init_phase2(struct core_window *cw,
+ struct core_window_callback_table *cb_t)
+{
+ LOG("cw:%p", cw);
+ return(hotlist_init(cb_t, cw, hl.path));
+}
+
+static void atari_hotlist_finish(struct core_window *cw)
+{
+ LOG("cw:%p", cw);
+ hotlist_fini(hl.path);
+}
+
+static void atari_hotlist_draw(struct core_window *cw, int x,
+ int y, struct rect *clip,
+ const struct redraw_context *ctx)
+{
+ hotlist_redraw(x, y, clip, ctx);
+}
+
+static void atari_hotlist_keypress(struct core_window *cw, uint32_t ucs4)
+{
+ GUIWIN *gemtk_win;
+ GRECT area;
+ LOG("ucs4: %"PRIu32 , ucs4);
+ hotlist_keypress(ucs4);
+ gemtk_win = atari_treeview_get_gemtk_window(cw);
+ atari_treeview_get_grect(cw, TREEVIEW_AREA_CONTENT, &area);
+ //gemtk_wm_exec_redraw(gemtk_win, &area);
+}
+
+static void atari_hotlist_mouse_action(struct core_window *cw,
+ browser_mouse_state mouse,
+ int x, int y)
+{
+ LOG("x: %d, y: %d\n", x, y);
+
+ hotlist_mouse_action(mouse, x, y);
+}
+
+
+
+static short handle_event(GUIWIN *win, EVMULT_OUT *ev_out, short msg[8])
+{
+ char *cur_url = NULL;
+ char *cur_title = NULL;
+ short retval = 0;
+ struct atari_treeview_window *tv = NULL;
+ struct core_window *cw;
+ struct gui_window * gw;
+ OBJECT *toolbar;
+ GRECT tb_area;
+ GUIWIN * gemtk_win;
+
+ LOG("gw:%p", win);
+
+ tv = (struct atari_treeview_window*) gemtk_wm_get_user_data(win);
+ cw = (struct core_window *)tv;
+
+ if (ev_out->emo_events & MU_MESAG) {
+ switch (msg[0]) {
+
+ case WM_TOOLBAR:
+ LOG("WM_TOOLBAR");
+
+ toolbar = gemtk_obj_get_tree(TOOLBAR_HOTLIST);
+
+ assert(toolbar);
+ assert(tv);
+
+ switch (msg[4]) {
+ case TOOLBAR_HOTLIST_CREATE_FOLDER:
+ hotlist_add_folder(NULL, 0, 0);
+ break;
+
+ case TOOLBAR_HOTLIST_ADD:
+ gw = gui_get_input_window();
+ if(gw && gw->browser){
+ cur_url = gui_window_get_url(gw);
+ cur_title = gui_window_get_title(gw);
+ // TODO: read language string.
+ cur_title = (cur_title ? cur_title : (char*)"New bookmark");
+ } else {
+ cur_url = (char*)"http://www";
+ }
+ atari_hotlist_add_page(cur_url, cur_title);
+ break;
+
+ case TOOLBAR_HOTLIST_DELETE:
+ hotlist_keypress(NS_KEY_DELETE_LEFT);
+ break;
+
+ case TOOLBAR_HOTLIST_EDIT:
+ hotlist_edit_selection();
+ break;
+ }
+
+ gemtk_win = atari_treeview_get_gemtk_window(cw);
+ assert(gemtk_win);
+ toolbar[msg[4]].ob_state &= ~OS_SELECTED;
+ atari_treeview_get_grect(cw, TREEVIEW_AREA_TOOLBAR, &tb_area);
+ evnt_timer(150);
+ gemtk_wm_exec_redraw(gemtk_win, &tb_area);
+ retval = 1;
+ break;
+
+ case WM_CLOSED:
+ atari_hotlist_close();
+ retval = 1;
+ break;
+
+ default: break;
+ }
+ }
+
+ return(retval);
+}
+
+
+
+void atari_hotlist_init(void)
+{
+ if (hl.init == false) {
+ if( strcmp(nsoption_charp(hotlist_file), "") == 0 ){
+ atari_find_resource( (char*)&hl.path, "hotlist", "hotlist" );
+ } else {
+ strncpy( (char*)&hl.path, nsoption_charp(hotlist_file), PATH_MAX-1 );
+ }
+
+ LOG("Hotlist: %s", (char *)&hl.path);
+
+ if( hl.window == NULL ){
+ int flags = ATARI_TREEVIEW_WIDGETS;
+ short handle = -1;
+ OBJECT * tree = gemtk_obj_get_tree(TOOLBAR_HOTLIST);
+ assert( tree );
+
+ handle = wind_create(flags, 0, 0, desk_area.g_w, desk_area.g_h);
+ hl.window = gemtk_wm_add(handle, GEMTK_WM_FLAG_DEFAULTS, NULL);
+ if( hl.window == NULL ) {
+ gemtk_msg_box_show(GEMTK_MSG_BOX_ALERT,
+ "Failed to allocate Hotlist");
+ return;
+ }
+ wind_set_str(handle, WF_NAME, (char*)messages_get("Hotlist"));
+ gemtk_wm_set_toolbar(hl.window, tree, 0, 0);
+ gemtk_wm_unlink(hl.window);
+ tree_hotlist_path = (const char*)&hl.path;
+
+ hl.tv = atari_treeview_create(hl.window, &atari_hotlist_treeview_callbacks,
+ NULL, flags);
+
+ if (hl.tv == NULL) {
+ /* handle it properly, clean up previous allocs */
+ LOG("Failed to allocate treeview");
+ return;
+ }
+
+ } else {
+
+ }
+ }
+ hl.init = true;
+}
+
+void atari_hotlist_open(void)
+{
+ assert(hl.init);
+ if (hl.init == false) {
+ return;
+ }
+
+ if (atari_treeview_is_open(hl.tv) == false) {
+
+ GRECT pos;
+ pos.g_x = desk_area.g_w - desk_area.g_w / 4;
+ pos.g_y = desk_area.g_y;
+ pos.g_w = desk_area.g_w / 4;
+ pos.g_h = desk_area.g_h;
+
+ atari_treeview_open(hl.tv, &pos);
+ } else {
+ wind_set(gemtk_wm_get_handle(hl.window), WF_TOP, 1, 0, 0, 0);
+ }
+}
+
+void atari_hotlist_close(void)
+{
+ atari_treeview_close(hl.tv);
+}
+
+void atari_hotlist_destroy(void)
+{
+
+ if( hl.init == false) {
+ return;
+ }
+ if( hl.window != NULL ) {
+ if (atari_treeview_is_open(hl.tv))
+ atari_hotlist_close();
+ wind_delete(gemtk_wm_get_handle(hl.window));
+ gemtk_wm_remove(hl.window);
+ hl.window = NULL;
+ atari_treeview_delete(hl.tv);
+ hl.init = false;
+ }
+ LOG("done");
+}
+
+void atari_hotlist_redraw(void)
+{
+ atari_treeview_redraw(hl.tv);
+}
+
+struct node;
+
+void atari_hotlist_add_page( const char * url, const char * title )
+{
+ nsurl *nsurl;
+
+ if(hl.tv == NULL)
+ return;
+
+ atari_hotlist_open();
+
+ if (nsurl_create(url, &nsurl) != NSERROR_OK)
+ return;
+
+ if (hotlist_has_url(nsurl)) {
+ LOG("URL already added as Bookmark");
+ nsurl_unref(nsurl);
+ return;
+ }
+
+ /* doesn't look nice:
+ if( hl.tv->click.x >= 0 && hl.tv->click.y >= 0 ){
+ hotlist_add_entry( nsurl, title, true, hl.tv->click.y );
+ } else {
+
+ }*/
+ //hotlist_add_url(nsurl);
+ hotlist_add_entry(nsurl, title, 0, 0);
+ nsurl_unref(nsurl);
+}
diff --git a/frontends/atari/hotlist.h b/frontends/atari/hotlist.h
new file mode 100644
index 000000000..159878233
--- /dev/null
+++ b/frontends/atari/hotlist.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_ATARI_HOTLIST_H
+#define NS_ATARI_HOTLIST_H
+
+#include <stdbool.h>
+
+#include "desktop/tree.h"
+#include "atari/gemtk/gemtk.h"
+#include "atari/treeview.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+/* The hotlist window, toolbar and treeview data. */
+
+struct atari_hotlist {
+ GUIWIN * window;
+ //ATARI_TREEVIEW_PTR tv;/*< The hotlist treeview handle. */
+ struct core_window *tv;
+ bool init;
+ char path[PATH_MAX];
+};
+
+extern struct atari_hotlist hl;
+
+void atari_hotlist_init( void );
+void atari_hotlist_open( void );
+void atari_hotlist_close( void );
+void atari_hotlist_destroy( void );
+void atari_hotlist_add_page( const char * url, const char * title );
+void atari_hotlist_redraw( void );
+
+
+#endif
diff --git a/frontends/atari/login.c b/frontends/atari/login.c
new file mode 100644
index 000000000..c44b07750
--- /dev/null
+++ b/frontends/atari/login.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "utils/config.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <cflib.h>
+
+#include "utils/errors.h"
+#include "utils/utils.h"
+#include "utils/messages.h"
+#include "utils/log.h"
+#include "content/content.h"
+#include "content/hlcache.h"
+#include "content/urldb.h"
+#include "content/fetch.h"
+
+#include "atari/gui.h"
+#include "atari/misc.h"
+#include "atari/login.h"
+#include "atari/res/netsurf.rsh"
+
+
+bool login_form_do(nsurl * url, char * realm, char ** out)
+{
+ char user[255];
+ char pass[255];
+ //const char * auth;
+ short exit_obj = 0;
+ OBJECT * tree;
+
+ user[0] = 0;
+ pass[0] = 0;
+
+ // TODO: use auth details for predefined login data
+ // auth = urldb_get_auth_details(url, realm);
+ tree = gemtk_obj_get_tree(LOGIN);
+
+ assert(tree != NULL);
+
+ exit_obj = simple_mdial(tree, 0);
+
+ if(exit_obj == LOGIN_BT_LOGIN) {
+ get_string(tree, LOGIN_TB_USER, user);
+ get_string(tree, LOGIN_TB_PASSWORD, pass);
+ int size = strlen((char*)&user) + strlen((char*)&pass) + 2 ;
+ *out = malloc(size);
+ snprintf(*out, size, "%s:%s", user, pass);
+ } else {
+ *out = NULL;
+ }
+ return((exit_obj == LOGIN_BT_LOGIN));
+}
+
diff --git a/frontends/atari/login.h b/frontends/atari/login.h
new file mode 100644
index 000000000..b61808c71
--- /dev/null
+++ b/frontends/atari/login.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_LOGIN_H_INCLUDED
+#define NS_LOGIN_H_INCLUDED
+
+#include "utils/nsurl.h"
+
+bool login_form_do( nsurl * host, char * realm, char **cbpw );
+
+#endif
diff --git a/frontends/atari/misc.c b/frontends/atari/misc.c
new file mode 100644
index 000000000..8688b93fd
--- /dev/null
+++ b/frontends/atari/misc.c
@@ -0,0 +1,408 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <mint/osbind.h>
+
+#include "utils/dirent.h"
+#include "content/content.h"
+#include "content/hlcache.h"
+#include "desktop/cookie_manager.h"
+#include "desktop/mouse.h"
+#include "desktop/tree.h"
+#include "utils/nsoption.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "utils/log.h"
+#include "utils/file.h"
+#include "content/fetch.h"
+
+#include "atari/gui.h"
+#include "atari/toolbar.h"
+
+#include "atari/misc.h"
+#include "atari/encoding.h"
+#include "atari/gemtk/gemtk.h"
+#include "cflib.h"
+
+extern void * h_gem_rsrc;
+
+struct is_process_running_callback_data {
+ const char * fname;
+ bool found;
+};
+
+/* exported function documented in atari/misc/h */
+nserror atari_warn_user(const char *warning, const char *detail)
+{
+ size_t len = 1 + ((warning != NULL) ? strlen(messages_get(warning)) :
+ 0) + ((detail != 0) ? strlen(detail) : 0);
+ char message[len];
+ snprintf(message, len, messages_get(warning), detail);
+
+ printf("%s\n", message);
+ gemtk_msg_box_show(GEMTK_MSG_BOX_ALERT, message);
+
+ return NSERROR_OK;
+}
+
+void die(const char *error)
+{
+ printf("%s\n", error);
+ gemtk_msg_box_show(GEMTK_MSG_BOX_ALERT, error);
+ exit(1);
+}
+
+
+struct gui_window * find_guiwin_by_aes_handle(short handle){
+
+ struct gui_window * gw;
+ gw = window_list;
+
+ if( handle == 0 ){
+ return( NULL );
+ }
+
+ while(gw != NULL) {
+ if(gw->root->win != NULL
+ && gemtk_wm_get_handle(gw->root->win) == handle) {
+ return(gw);
+ }
+ else
+ gw = gw->next;
+ }
+
+ return( NULL );
+}
+
+
+static int scan_process_list(scan_process_callback cb, void *data)
+{
+ int pid, count = 0;
+ DIR *dir;
+ char*dirname;
+ struct dirent *de;
+
+ if (( dir = opendir("U:/kern")) == NULL)
+ return(0);
+
+ while ((de = readdir( dir)) != NULL) {
+ dirname = de->d_name;
+
+ if( dirname[0] != '1' && dirname[0] != '2' && dirname[0] != '3' && dirname[0] != '4' && dirname[0] != '5'
+ && dirname[0] != '6' && dirname[0] != '7' && dirname[0] != '8' && dirname[0] != '9')
+ continue;
+
+ count++;
+ if (cb != NULL) {
+ /* when callback returns negative value, we stop scanning: */
+ pid = atoi(dirname);
+ if (cb(pid, data)<0) {
+ break;
+ }
+ }
+ }
+
+ closedir(dir);
+
+ return(count);
+}
+
+static int proc_running_callback(int pid, void * arg)
+{
+ char buf[PATH_MAX], fnamepath[256];
+ FILE *fp;
+ int nread;
+ struct is_process_running_callback_data *data;
+
+ data = (struct is_process_running_callback_data *)arg;
+
+ sprintf(fnamepath, "U:\\kern\\%d\\fname", pid);
+ printf("checking: %s\n", fnamepath);
+
+ fp = fopen(fnamepath, "r");
+ if(!fp)
+ return(0);
+
+ nread = fread(buf, 1, PATH_MAX-1, fp);
+ fclose(fp);
+ nread = MIN(PATH_MAX-1, nread);
+
+ if (nread > 0) {
+ buf[nread] = 0;
+
+ char *lastslash = strrchr(buf, '/');
+
+ if(lastslash == NULL)
+ lastslash = strrchr(buf, '\\');
+
+ if(lastslash==NULL)
+ lastslash = buf;
+ else
+ lastslash++;
+
+ if(strcasecmp(lastslash, data->fname)==0){
+ /* found process, check status: */
+ sprintf(fnamepath, "U:\\kern\\%d\\status", pid);
+ fp = fopen(fnamepath, "r");
+ if (fp) {
+ nread = fread(buf, 1, PATH_MAX-1, fp);
+ fclose(fp);
+ if (nread>0) {
+ nread = MIN(PATH_MAX-1,nread);
+ }
+ buf[nread] = 0;
+ if (strstr(buf, "zombie")==NULL) {
+ data->found = true;
+ return(-1);
+ }
+ }
+
+ }
+ }
+ return(0);
+}
+
+bool is_process_running(const char * name)
+{
+ struct is_process_running_callback_data data = {name, false};
+
+ scan_process_list(proc_running_callback, &data);
+
+ return( (data.found==1) ? true : false );
+}
+
+
+void gem_set_cursor( MFORM_EX * cursor )
+{
+ static unsigned char flags = 255;
+ static int number = 255;
+ if( flags == cursor->flags && number == cursor->number )
+ return;
+ if( cursor->flags & MFORM_EX_FLAG_USERFORM ) {
+ gemtk_obj_mouse_sprite(cursor->tree, cursor->number);
+ } else {
+ graf_mouse(cursor->number, NULL );
+ }
+ number = cursor->number;
+ flags = cursor->flags;
+}
+
+
+/* exported interface documented in atari/misc.h */
+long nkc_to_input_key(short nkc, long * ucs4_out)
+{
+ unsigned char ascii = (nkc & 0xFF);
+ long ik = 0;
+
+ // initialize result:
+ *ucs4_out = 0;
+
+ // sanitize input key:
+ nkc = (nkc & (NKF_CTRL|NKF_SHIFT|0xFF));
+
+ /* shift + cntrl key: */
+ if( ((nkc & NKF_CTRL) == NKF_CTRL) && ((nkc & (NKF_SHIFT))!=0) ) {
+
+ }
+ /* cntrl key only: */
+ else if( (nkc & NKF_CTRL) == NKF_CTRL ) {
+ switch ( ascii ) {
+ case 'A':
+ ik = NS_KEY_SELECT_ALL;
+ break;
+
+ case 'C':
+ ik = NS_KEY_COPY_SELECTION;
+ break;
+
+ case 'X':
+ ik = NS_KEY_CUT_SELECTION;
+ break;
+
+ case 'V':
+ ik = NS_KEY_PASTE;
+ break;
+
+ default:
+ break;
+ }
+ }
+ /* shift key only: */
+ else if( (nkc & NKF_SHIFT) != 0 ) {
+ switch( ascii ) {
+ case NK_TAB:
+ ik = NS_KEY_SHIFT_TAB;
+ break;
+
+ case NK_LEFT:
+ ik = NS_KEY_LINE_START;
+ break;
+
+ case NK_RIGHT:
+ ik = NS_KEY_LINE_END;
+ break;
+
+ case NK_UP:
+ ik = NS_KEY_PAGE_UP;
+ break;
+
+ case NK_DOWN:
+ ik = NS_KEY_PAGE_DOWN;
+ break;
+
+ default:
+ break;
+ }
+ }
+ /* No modifier keys: */
+ else {
+ switch( ascii ) {
+
+ case NK_INS:
+ ik = NS_KEY_PASTE;
+ break;
+
+ case NK_BS:
+ ik = NS_KEY_DELETE_LEFT;
+ break;
+
+ case NK_DEL:
+ ik = NS_KEY_DELETE_RIGHT;
+ break;
+
+ case NK_TAB:
+ ik = NS_KEY_TAB;
+ break;
+
+
+ case NK_ENTER:
+ ik = NS_KEY_NL;
+ break;
+
+ case NK_RET:
+ ik = NS_KEY_CR;
+ break;
+
+ case NK_ESC:
+ ik = NS_KEY_ESCAPE;
+ break;
+
+ case NK_CLRHOME:
+ ik = NS_KEY_TEXT_START;
+ break;
+
+ case NK_RIGHT:
+ ik = NS_KEY_RIGHT;
+ break;
+
+ case NK_LEFT:
+ ik = NS_KEY_LEFT;
+ break;
+
+ case NK_UP:
+ ik = NS_KEY_UP;
+ break;
+
+ case NK_UNDO:
+ ik = NS_KEY_UNDO;
+ break;
+
+ case NK_DOWN:
+ ik = NS_KEY_DOWN;
+ break;
+
+ case NK_M_PGUP:
+ ik = NS_KEY_PAGE_UP;
+ break;
+
+ case NK_M_PGDOWN:
+ ik = NS_KEY_PAGE_DOWN;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if( ik == 0 && ( (nkc & NKF_CTRL)==0) ) {
+ if (ascii >= 9 ) {
+ *ucs4_out = atari_to_ucs4(ascii);
+ }
+ }
+ return ( ik );
+}
+
+/**
+ * Show default file selector
+ *
+ * \param title The selector title.
+ * \param name Default file name
+ * \return a static char pointer or null if the user aborted the selection.
+ */
+const char * file_select(const char * title, const char * name) {
+
+ static char path[PATH_MAX]=""; // First usage : current directory
+ static char fullname[PATH_MAX]="";
+ char tmpname[255];
+ char * use_title = (char*)title;
+
+ if( strlen(name)>254)
+ return( NULL );
+
+ strcpy(tmpname, name);
+
+ if( use_title == NULL ){
+ use_title = (char*)"";
+ }
+
+ if (select_file(path, tmpname, (char*)"*", use_title, NULL)) {
+ snprintf(fullname, PATH_MAX, "%s%s", path, tmpname);
+ return((const char*)&fullname);
+ }
+
+ return( NULL );
+}
+
+
+void dbg_grect(const char * str, GRECT * r)
+{
+ printf("%s: x: %d, y: %d, w: %d, h: %d (x2: %d, y2: %d)\n", str,
+ r->g_x, r->g_y, r->g_w, r->g_h, r->g_x + r->g_w, r->g_y + r->g_h);
+}
+
+void dbg_pxy(const char * str, short * pxy )
+{
+ printf("%s: x: %d, y: %d, w: %d, h: %d\n", str,
+ pxy[0], pxy[1], pxy[2], pxy[3] );
+}
+
+void dbg_rect(const char * str, int * pxy)
+{
+ printf("%s: x0: %d, y0: %d, x1: %d, y1: %d (w: %d, h: %d)\n", str,
+ pxy[0], pxy[1], pxy[2], pxy[3],
+ pxy[2] - pxy[0],
+ pxy[3] - pxy[1] );
+}
+
diff --git a/frontends/atari/misc.h b/frontends/atari/misc.h
new file mode 100644
index 000000000..e581c23b2
--- /dev/null
+++ b/frontends/atari/misc.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_ATARI_MISC_H
+#define NS_ATARI_MISC_H
+
+
+#define SBUF8_TO_LBUF8(sbuf,lbuf)\
+ lbuf[0] = (long)sbuf[0];\
+ lbuf[1] = (long)sbuf[1];\
+ lbuf[2] = (long)sbuf[2];\
+ lbuf[3] = (long)sbuf[3];\
+ lbuf[4] = (long)sbuf[4];\
+ lbuf[5] = (long)sbuf[5];\
+ lbuf[6] = (long)sbuf[6];\
+ lbuf[7] = (long)sbuf[7];
+
+#define RECT_TO_GRECT(r,g) \
+ (g)->g_x = (r->x0 < r->x1) ? r->x0 : r->x1 ; \
+ (g)->g_y = (r->y0 < r->y1) ? r->y0 : r->y1 ; \
+ (g)->g_w = (r->x0 < r->x1) ? r->x1 - r->x0 : r->x0 - r->x1 ; \
+ (g)->g_h = (r->y0 < r->y1) ? r->y1 - r->y0 : r->y0 - r->y1 ;
+
+
+
+/* Modes for find_gui_window: */
+#define BY_WINDOM_HANDLE 0x0
+#define BY_GEM_HANDLE 0x1
+
+/**
+ */
+typedef int (*scan_process_callback)(int pid, void *data);
+
+/**
+ */
+struct gui_window * find_guiwin_by_aes_handle(short handle);
+
+/**
+ */
+bool is_process_running(const char * name);
+
+/**
+ */
+void gem_set_cursor( MFORM_EX * cursor );
+
+/**
+ */
+void dbg_grect(const char * str, GRECT * r);
+
+/**
+ */
+void dbg_pxy(const char * str, short * pxy);
+
+/**
+ */
+void dbg_rect(const char * str, int * pxy);
+
+/**
+ */
+const char * file_select(const char * title, const char * name);
+
+/**
+ * Convert NKC to netsurf input key code and/or to ucs4 (depends on keycode).
+ *
+ * \param[in] nkc atari normalized key code
+ * \param[out] ucs4_out The ucs4 converted keycode
+ * \return The netsurf input keycode or 0 and ucs4_out updated with
+ * the NKC converted to UC4 encoding.
+ */
+long nkc_to_input_key(short nkc, long * ucs4_out);
+
+/**
+ * Cause an abnormal program termination.
+ *
+ * \note This never returns and is intended to terminate without any cleanup.
+ *
+ * \param error The message to display to the user.
+ */
+void die(const char * const error) __attribute__ ((noreturn));
+
+/**
+ * Warn the user of an event.
+ *
+ * \param[in] warning A warning looked up in the message translation table
+ * \param[in] detail Additional text to be displayed or NULL.
+ * \return NSERROR_OK on success or error code if there was a
+ * faliure displaying the message to the user.
+ */
+nserror atari_warn_user(const char *warning, const char *detail);
+
+#endif
diff --git a/frontends/atari/options.h b/frontends/atari/options.h
new file mode 100644
index 000000000..4ae847d74
--- /dev/null
+++ b/frontends/atari/options.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_ATARI_OPTIONS_H_
+#define NS_ATARI_OPTIONS_H_
+
+/* setup longer default reflow time */
+#define DEFAULT_REFLOW_PERIOD 350 /* time in cs */
+
+#endif
+
+NSOPTION_STRING(atari_font_driver, "freetype")
+NSOPTION_INTEGER(atari_font_monochrom, 0)
+NSOPTION_INTEGER(atari_transparency, 1)
+NSOPTION_INTEGER(atari_dither, 1)
+NSOPTION_INTEGER(atari_gui_poll_timeout, 0)
+NSOPTION_STRING(atari_editor, NULL)
+NSOPTION_STRING(font_face_sans_serif, NULL)
+NSOPTION_STRING(font_face_sans_serif_bold, NULL)
+NSOPTION_STRING(font_face_sans_serif_italic, NULL)
+NSOPTION_STRING(font_face_sans_serif_italic_bold, NULL)
+NSOPTION_STRING(font_face_monospace, NULL)
+NSOPTION_STRING(font_face_monospace_bold, NULL)
+NSOPTION_STRING(font_face_serif, NULL)
+NSOPTION_STRING(font_face_serif_bold, NULL)
+NSOPTION_STRING(font_face_cursive, NULL)
+NSOPTION_STRING(font_face_fantasy, NULL)
+NSOPTION_STRING(downloads_path, "downloads")
+NSOPTION_STRING(url_file, "url.db")
+NSOPTION_STRING(hotlist_file, "hotlist")
+NSOPTION_STRING(tree_icons_path, "./res/icons")
+
+
diff --git a/frontends/atari/osspec.c b/frontends/atari/osspec.c
new file mode 100644
index 000000000..f64402e8d
--- /dev/null
+++ b/frontends/atari/osspec.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2010 <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <sys/types.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <support.h>
+#include <mint/osbind.h>
+#include <mint/cookie.h>
+
+#include "utils/log.h"
+#include "atari/osspec.h"
+#include "atari/gemtk/gemtk.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+NS_ATARI_SYSINFO atari_sysinfo;
+
+void init_os_info(void)
+{
+ int16_t out[4];
+ long cookie_FSMC = 0;
+
+ atari_sysinfo.gemdos_version = Sversion();
+
+ if( tos_getcookie (C_FSMC, &cookie_FSMC ) == C_FOUND ) {
+ atari_sysinfo.gdos_FSMC = 1;
+ } else {
+ atari_sysinfo.gdos_FSMC = 0;
+ }
+ atari_sysinfo.large_sfont_pxh = 13;
+ atari_sysinfo.medium_sfont_pxh = 6;
+ atari_sysinfo.small_sfont_pxh = 4;
+ /* todo: detect if system font is monospaced */
+ atari_sysinfo.sfont_monospaced = true;
+ if( appl_xgetinfo(AES_LARGEFONT, &out[0], &out[1], &out[2], &out[3] ) > 0 ){
+ atari_sysinfo.large_sfont_pxh = out[0];
+ }
+ if( appl_xgetinfo(AES_SMALLFONT, &out[0], &out[1], &out[2], &out[3] ) > 0 ){
+ atari_sysinfo.small_sfont_pxh = out[0];
+ }
+ atari_sysinfo.aes_max_win_title_len = 79;
+ if (sys_type() & (SYS_MAGIC|SYS_NAES|SYS_XAAES)) {
+ if (sys_NAES()) {
+ atari_sysinfo.aes_max_win_title_len = 127;
+ }
+ if (sys_XAAES()) {
+ atari_sysinfo.aes_max_win_title_len = 200;
+ }
+ }
+}
+
+
+/* exported interface documented in atari/osspec.h */
+int tos_getcookie(long tag, long * value)
+{
+ COOKIE * cptr;
+
+ if( atari_sysinfo.gemdos_version > TOS4VER ){
+ return( Getcookie(tag, value) );
+ }
+
+ cptr = (COOKIE*)Setexc(0x0168, -1L);
+ if(cptr != NULL) {
+ do {
+ if( cptr->c == tag ){
+ if(cptr->v != 0 ){
+ if( value != NULL ){
+ *value = cptr->v;
+ }
+ return( C_FOUND );
+ }
+ }
+ } while( (cptr++)->c != 0L );
+ }
+ return( C_NOTFOUND );
+}
+
+/*
+
+ a fixed version of realpath() which returns valid
+ paths for TOS which have no U: drive
+
+*/
+
+char *gemdos_realpath(const char * path, char * rpath)
+{
+ char work[PATH_MAX+1];
+ char * r;
+
+ if (rpath == NULL) {
+ return (NULL);
+ }
+
+ // Check if the path is already absolute:
+ if(path[1] == ':'){
+ strcpy(rpath, path);
+ return(rpath);
+ }
+
+ LOG("realpath in: %s\n", path);
+ r = realpath(path, work);
+ if (r != NULL) {
+ unx2dos((const char *)r, rpath);
+ LOG("realpath out: %s\n", rpath);
+ return(rpath);
+ } else {
+ LOG("realpath out: NULL!\n");
+ }
+ return (NULL);
+}
diff --git a/frontends/atari/osspec.h b/frontends/atari/osspec.h
new file mode 100644
index 000000000..0c8d4cf66
--- /dev/null
+++ b/frontends/atari/osspec.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2011 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_ATARI_OSSPEC_H
+#define NS_ATARI_OSSPEC_H
+
+typedef struct {
+ long c;
+ long v;
+} COOKIE;
+
+typedef struct {
+ unsigned short gemdos_version;
+ unsigned short gdos_FSMC;
+ unsigned short systype;
+ unsigned short small_sfont_pxh;
+ unsigned short medium_sfont_pxh;
+ unsigned short large_sfont_pxh;
+ bool sfont_monospaced;
+ short aes_max_win_title_len;
+} NS_ATARI_SYSINFO;
+
+extern NS_ATARI_SYSINFO atari_sysinfo;
+
+void init_os_info(void);
+void fix_path(char * path);
+char * gemdos_realpath(const char * path, char * rpath);
+int tos_getcookie( long tag, long * value );
+
+#endif
diff --git a/frontends/atari/plot/eddi.h b/frontends/atari/plot/eddi.h
new file mode 100644
index 000000000..7d6b90d4b
--- /dev/null
+++ b/frontends/atari/plot/eddi.h
@@ -0,0 +1,54 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2009 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+
+#ifndef _atari_eddi_h
+#define _atari_eddi_h
+
+/*--- Defines ---*/
+
+/* EdDI versions */
+
+#define EDDI_10 (0x0100)
+#define EDDI_11 (0x0110)
+
+/* Screen format */
+
+enum {
+ VDI_FORMAT_UNKNOWN=-1,
+ VDI_FORMAT_INTER=0, /* Interleaved bitplanes */
+ VDI_FORMAT_VDI=1, /* VDI independent */
+ VDI_FORMAT_PACK=2 /* Packed pixels */
+};
+
+/* CLUT types */
+enum {
+ VDI_CLUT_NONE=0, /* Monochrome mode */
+ VDI_CLUT_HARDWARE, /* <256 colours mode */
+ VDI_CLUT_SOFTWARE /* True colour mode */
+};
+
+
+/*--- Functions ---*/
+
+unsigned long EdDI_version(void *function_pointer);
+
+#endif /* atari_eddi_s_h */
diff --git a/frontends/atari/plot/eddi.s b/frontends/atari/plot/eddi.s
new file mode 100644
index 000000000..f0ea18a1e
--- /dev/null
+++ b/frontends/atari/plot/eddi.s
@@ -0,0 +1,42 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2009 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+
+/*
+ *Read EdDI version
+ *
+ *Patrice Mandin
+ */
+
+.text
+
+.globl _EdDI_version
+
+/*--- Vector installer ---*/
+
+_EdDI_version:
+movel sp@(4),a0 /* Value of EdDI cookie */
+
+/* Call EdDI function #0 */
+clrl d0
+jsr (a0)
+
+rts
diff --git a/frontends/atari/plot/font_freetype.c b/frontends/atari/plot/font_freetype.c
new file mode 100644
index 000000000..a77aff855
--- /dev/null
+++ b/frontends/atari/plot/font_freetype.c
@@ -0,0 +1,714 @@
+/*
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ * 2008 Vincent Sanders <vince@simtec.co.uk>
+ * 2011 Ole Loots <ole@monochrom.net>
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#ifdef WITH_FREETYPE_FONT_DRIVER
+#include <assert.h>
+#include <ft2build.h>
+#include FT_CACHE_H
+
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "desktop/mouse.h"
+#include "desktop/plot_style.h"
+
+#include "atari/gui.h"
+#include "atari/bitmap.h"
+#include "atari/plot/plot.h"
+#include "atari/plot/fontplot.h"
+#include "atari/plot/font_freetype.h"
+#include "atari/findfile.h"
+#include "atari/gemtk/gemtk.h"
+
+/* -------------------------------------------------------------------------- */
+/* Font Loading & Mapping scheme */
+/* -------------------------------------------------------------------------- */
+/*
+
+Truetype fonts are loaded in the following priority order:
+
+1. Option values.
+2. default resouce names (8.3 compatible).
+3. default font package installation path.
+
+
+Resource font names & their meanings:
+--------------------------------------------
+s.ttf => Serif
+sb.ttf => Serif Bold
+ss.ttf => Sans Serif *Default Font
+ssb.ttf => Sans Serif Bold
+ssi.ttf => Sans Serif Italic
+ssib.ttf => Sans Serif Italic Bold
+mono.ttf => Monospaced
+monob.ttf => Monospaced Bold
+cursive.ttf => Cursive
+fantasy.ttf => Fantasy
+*/
+
+#define FONT_RESOURCE_PATH "fonts/"
+#define DEJAVU_PATH "/usr/share/fonts/truetype/ttf-dejavu/"
+#define BITSTREAM_PATH "/usr/share/fonts/truetype/ttf-bitstream-vera/"
+
+#if !defined(USE_BITSTREAM_FONT_PACKAGE) && !defined(USE_DEJAVU_FONT_PACKAGE)
+# define USE_BITSTREAM_FONT_PACKAGE
+#endif
+
+#if defined(USE_DEJAVU_FONT_PACKAGE)
+# define FONT_PKG_PATH DEJAVU_PATH
+# define FONT_FILE_SANS "DejaVuSans.ttf"
+# define FONT_FILE_SANS_BOLD "DejaVuSans-Bold.ttf"
+# define FONT_FILE_SANS_OBLIQUE "DejaVuSans-Oblique.ttf"
+# define FONT_FILE_SANS_BOLD_OBLIQUE "DejaVuSans-BoldOblique.ttf"
+# define FONT_FILE_SERIF "DejaVuSerif.ttf"
+# define FONT_FILE_SERIF_BOLD "DejaVuSerif-Bold.ttf"
+# define FONT_FILE_MONO "DejaVuSansMono.ttf"
+# define FONT_FILE_MONO_BOLD "DejaVuSerif-Bold.ttf"
+# define FONT_FILE_OBLIQUE "DejaVuSansMono-Oblique.ttf"
+# define FONT_FILE_FANTASY "DejaVuSerifCondensed-Bold.ttf"
+#elif defined(USE_BITSTREAM_FONT_PACKAGE)
+# define FONT_PKG_PATH BITSTREAM_PATH
+# define FONT_FILE_SANS "Vera.ttf"
+# define FONT_FILE_SANS_BOLD "VeraBd.ttf"
+# define FONT_FILE_SANS_OBLIQUE "VeraIt.ttf"
+# define FONT_FILE_SANS_BOLD_OBLIQUE "VeraBI.ttf"
+# define FONT_FILE_SERIF "VeraSe.ttf"
+# define FONT_FILE_SERIF_BOLD "VeraSeBd.ttf"
+# define FONT_FILE_MONO "VeraMono.ttf"
+# define FONT_FILE_MONO_BOLD "VeraMoBd.ttf"
+# define FONT_FILE_OBLIQUE "VeraMoIt.ttf"
+# define FONT_FILE_FANTASY "VeraMoBI.ttf"
+
+#endif
+
+#define CACHE_SIZE 2048
+#define CACHE_MIN_SIZE (100 * 1024)
+#define BOLD_WEIGHT 700
+
+extern unsigned long atari_plot_flags;
+extern int atari_plot_vdi_handle;
+
+static FT_Library library;
+static FTC_Manager ft_cmanager;
+static FTC_CMapCache ft_cmap_cache ;
+static FTC_ImageCache ft_image_cache;
+
+int ft_load_type;
+
+/* cache manager faceID data to create freetype faceid on demand */
+typedef struct ftc_faceid_s {
+ char *fontfile; /* path to font */
+ int index; /* index of font */
+ int cidx; /* character map index for unicode */
+} ftc_faceid_t;
+
+static int dtor( FONT_PLOTTER self );
+static int str_width( FONT_PLOTTER self,const plot_font_style_t *fstyle,
+ const char * str, size_t length, int * width );
+static int str_split( FONT_PLOTTER self, const plot_font_style_t *fstyle,
+ const char *string, size_t length,int x,
+ size_t *char_offset, int *actual_x );
+static int pixel_pos( FONT_PLOTTER self, const plot_font_style_t *fstyle,
+ const char *string, size_t length,int x,
+ size_t *char_offset, int *actual_x );
+static int text( FONT_PLOTTER self, int x, int y, const char *text,
+ size_t length, const plot_font_style_t *fstyle );
+
+static void draw_glyph8(FONT_PLOTTER self, GRECT *clip, GRECT * loc,
+ uint8_t * pixdata, int pitch, uint32_t colour);
+static void draw_glyph1(FONT_PLOTTER self, GRECT * clip, GRECT * loc,
+ uint8_t * pixdata, int pitch, uint32_t colour);
+
+static ftc_faceid_t *font_faces[FONT_FACE_COUNT];
+static MFDB tmp;
+static int tmp_mfdb_size;
+static bool init = false;
+static struct bitmap * fontbmp;
+static size_t fontbmp_stride;
+static int fontbmp_allocated_height;
+static int fontbmp_allocated_width;
+
+
+
+/* map cache manager handle to face id */
+static FT_Error ft_face_requester(FTC_FaceID face_id, FT_Library library, FT_Pointer request_data, FT_Face *face )
+{
+ FT_Error error;
+ ftc_faceid_t *ft_face = (ftc_faceid_t *)face_id;
+ int cidx;
+
+ error = FT_New_Face(library, ft_face->fontfile, ft_face->index, face);
+ if (error) {
+ LOG("Could not find font (code %d)\n", error);
+ } else {
+ error = FT_Select_Charmap(*face, FT_ENCODING_UNICODE);
+ if (error) {
+ LOG("Could not select charmap (code %d)\n", error);
+ } else {
+ for (cidx = 0; cidx < (*face)->num_charmaps; cidx++) {
+ if ((*face)->charmap == (*face)->charmaps[cidx]) {
+ ft_face->cidx = cidx;
+ break;
+ }
+ }
+ }
+ }
+ LOG("Loaded face from %s\n", ft_face->fontfile);
+ return error;
+}
+
+/* create new framebuffer face and cause it to be loaded to check its ok */
+static ftc_faceid_t *
+ft_new_face(const char *option, const char *resname, const char *fontfile)
+{
+ ftc_faceid_t *newf;
+ FT_Error error;
+ FT_Face aface;
+ char buf[PATH_MAX];
+
+ newf = calloc(1, sizeof(ftc_faceid_t));
+ if (option != NULL) {
+ newf->fontfile = strdup(option);
+ } else {
+ atari_find_resource(buf, resname, fontfile);
+ newf->fontfile = strdup(buf);
+ }
+ error = FTC_Manager_LookupFace(ft_cmanager, (FTC_FaceID)newf, &aface);
+ if (error) {
+ LOG("Could not find font face %s (code %d)\n", fontfile, error);
+ free(newf);
+ newf = font_faces[FONT_FACE_DEFAULT]; /* use default */
+ }
+ return newf;
+}
+
+static void ft_fill_scalar(const plot_font_style_t *fstyle, FTC_Scaler srec)
+{
+ int selected_face = FONT_FACE_DEFAULT;
+
+ switch (fstyle->family) {
+
+ case PLOT_FONT_FAMILY_SERIF:
+ if (fstyle->weight >= BOLD_WEIGHT) {
+ selected_face = FONT_FACE_SERIF_BOLD;
+ } else {
+ selected_face = FONT_FACE_SERIF;
+ }
+ break;
+
+ case PLOT_FONT_FAMILY_MONOSPACE:
+ if (fstyle->weight >= BOLD_WEIGHT) {
+ selected_face = FONT_FACE_MONOSPACE_BOLD;
+ } else {
+ selected_face = FONT_FACE_MONOSPACE;
+ }
+ break;
+
+ case PLOT_FONT_FAMILY_CURSIVE:
+ selected_face = FONT_FACE_CURSIVE;
+ break;
+
+ case PLOT_FONT_FAMILY_FANTASY:
+ selected_face = FONT_FACE_FANTASY;
+ break;
+
+ case PLOT_FONT_FAMILY_SANS_SERIF:
+ default:
+ if ((fstyle->flags & FONTF_ITALIC) ||
+ (fstyle->flags & FONTF_OBLIQUE)) {
+ if (fstyle->weight >= BOLD_WEIGHT) {
+ selected_face = FONT_FACE_SANS_SERIF_ITALIC_BOLD;
+ } else {
+ selected_face = FONT_FACE_SANS_SERIF_ITALIC;
+ }
+ } else {
+ if (fstyle->weight >= BOLD_WEIGHT) {
+ selected_face = FONT_FACE_SANS_SERIF_BOLD;
+ } else {
+ selected_face = FONT_FACE_SANS_SERIF;
+ }
+ }
+ }
+
+ srec->face_id = (FTC_FaceID)font_faces[selected_face];
+
+ srec->width = srec->height = (fstyle->size * 64) / FONT_SIZE_SCALE;
+ srec->pixel = 0;
+
+ /* calculate x/y resolution, when browser_get_dpi() isn't available */
+ /* 72 is an good value. */
+ /* TODO: because browser_get_dpi() is to large, calculate that value */
+ /* by VDI values. */
+ srec->x_res = srec->y_res = 72; // browser_get_dpi();
+}
+
+static FT_Glyph ft_getglyph(const plot_font_style_t *fstyle, uint32_t ucs4)
+{
+ FT_UInt glyph_index;
+ FTC_ScalerRec srec;
+ FT_Glyph glyph;
+ FT_Error error;
+ ftc_faceid_t *ft_face;
+
+ ft_fill_scalar(fstyle, &srec);
+ ft_face = (ftc_faceid_t *)srec.face_id;
+ glyph_index = FTC_CMapCache_Lookup(ft_cmap_cache, srec.face_id, ft_face->cidx, ucs4);
+ error = FTC_ImageCache_LookupScaler(ft_image_cache,
+ &srec,
+ FT_LOAD_RENDER |
+ FT_LOAD_FORCE_AUTOHINT |
+ ft_load_type,
+ glyph_index,
+ &glyph,
+ NULL);
+ return glyph;
+}
+
+
+/* initialise font handling */
+static bool ft_font_init(void)
+{
+ FT_Error error;
+ FT_ULong max_cache_size;
+ FT_UInt max_faces = 6;
+ int i;
+
+ /* freetype library initialise */
+ error = FT_Init_FreeType( &library );
+ if (error) {
+ LOG("Freetype could not initialised (code %d)\n", error);
+ return false;
+ }
+
+ /* set the Glyph cache size up */
+ max_cache_size = CACHE_SIZE * 1024;
+ if (max_cache_size < CACHE_MIN_SIZE) {
+ max_cache_size = CACHE_MIN_SIZE;
+ }
+
+ /* cache manager initialise */
+ error = FTC_Manager_New(library,
+ max_faces,
+ 0,
+ max_cache_size,
+ ft_face_requester,
+ NULL,
+ &ft_cmanager);
+ if (error) {
+ LOG("Freetype could not initialise cache manager (code %d)\n", error);
+ FT_Done_FreeType(library);
+ return false;
+ }
+
+ error = FTC_CMapCache_New(ft_cmanager, &ft_cmap_cache);
+ error = FTC_ImageCache_New(ft_cmanager, &ft_image_cache);
+
+ /* Optain font faces */
+
+
+ /* Default font, Sans Serif */
+ font_faces[FONT_FACE_SANS_SERIF] = NULL;
+ font_faces[FONT_FACE_SANS_SERIF] = ft_new_face(
+ nsoption_charp(font_face_sans_serif),
+ FONT_RESOURCE_PATH "ss.ttf",
+ FONT_PKG_PATH FONT_FILE_SANS
+ );
+ if (font_faces[FONT_FACE_SANS_SERIF] == NULL) {
+ LOG("Could not find default font (code %d)\n", error);
+ FTC_Manager_Done(ft_cmanager);
+ FT_Done_FreeType(library);
+ return false;
+ }
+
+ /* Sans Serif Bold*/
+ font_faces[FONT_FACE_SANS_SERIF_BOLD] =
+ ft_new_face(nsoption_charp(font_face_sans_serif_bold),
+ FONT_RESOURCE_PATH "ssb.ttf",
+ FONT_PKG_PATH FONT_FILE_SANS_BOLD);
+
+ /* Sans Serif Italic */
+ font_faces[FONT_FACE_SANS_SERIF_ITALIC] =
+ ft_new_face(nsoption_charp(font_face_sans_serif_italic),
+ FONT_RESOURCE_PATH "ssi.ttf",
+ FONT_PKG_PATH FONT_FILE_SANS_OBLIQUE);
+
+ /* Sans Serif Italic Bold */
+ font_faces[FONT_FACE_SANS_SERIF_ITALIC_BOLD] =
+ ft_new_face(nsoption_charp(font_face_sans_serif_italic_bold),
+ FONT_RESOURCE_PATH "ssib.ttf",
+ FONT_PKG_PATH FONT_FILE_SANS_BOLD_OBLIQUE);
+
+ /* Monospaced */
+ font_faces[FONT_FACE_MONOSPACE] =
+ ft_new_face(nsoption_charp(font_face_monospace),
+ FONT_RESOURCE_PATH "mono.ttf",
+ FONT_PKG_PATH FONT_FILE_MONO);
+
+ /* Mospaced Bold */
+ font_faces[FONT_FACE_MONOSPACE_BOLD] =
+ ft_new_face(nsoption_charp(font_face_monospace_bold),
+ FONT_RESOURCE_PATH "monob.ttf",
+ FONT_PKG_PATH FONT_FILE_MONO_BOLD);
+
+ /* Serif */
+ font_faces[FONT_FACE_SERIF] =
+ ft_new_face(nsoption_charp(font_face_serif),
+ FONT_RESOURCE_PATH "s.ttf",
+ FONT_PKG_PATH FONT_FILE_SERIF);
+
+ /* Serif Bold */
+ font_faces[FONT_FACE_SERIF_BOLD] =
+ ft_new_face(nsoption_charp(font_face_serif_bold),
+ FONT_RESOURCE_PATH "sb.ttf",
+ FONT_PKG_PATH FONT_FILE_SERIF_BOLD);
+
+ /* Cursive */
+ font_faces[FONT_FACE_CURSIVE] =
+ ft_new_face(nsoption_charp(font_face_cursive),
+ FONT_RESOURCE_PATH "cursive.ttf",
+ FONT_PKG_PATH FONT_FILE_OBLIQUE);
+
+ /* Fantasy */
+ font_faces[FONT_FACE_FANTASY] =
+ ft_new_face(nsoption_charp(font_face_fantasy),
+ FONT_RESOURCE_PATH "fantasy.ttf",
+ FONT_PKG_PATH FONT_FILE_FANTASY);
+
+ for (i=1; i<FONT_FACE_COUNT; i++) {
+ if (font_faces[i] == NULL){
+ font_faces[i] = font_faces[FONT_FACE_SANS_SERIF];
+ }
+ }
+
+ return true;
+}
+
+
+static bool ft_font_finalise(void)
+{
+ FTC_Manager_Done(ft_cmanager );
+ FT_Done_FreeType(library);
+ return true;
+}
+
+static int str_width( FONT_PLOTTER self,const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int *width)
+{
+ uint32_t ucs4;
+ size_t nxtchr = 0;
+ FT_Glyph glyph;
+
+ *width = 0;
+ while (nxtchr < length) {
+ ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr);
+ nxtchr = utf8_next(string, length, nxtchr);
+
+ glyph = ft_getglyph(fstyle, ucs4);
+ if (glyph == NULL)
+ continue;
+ *width += glyph->advance.x >> 16;
+ }
+ return(1);
+}
+
+
+static int str_split( FONT_PLOTTER self, const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x)
+{
+ uint32_t ucs4;
+ size_t nxtchr = 0;
+ int last_space_x = 0;
+ int last_space_idx = 0;
+ FT_Glyph glyph;
+
+ *actual_x = 0;
+ while (nxtchr < length) {
+ ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr);
+ glyph = ft_getglyph(fstyle, ucs4);
+ if (glyph == NULL)
+ continue;
+ if (ucs4 == 0x20) {
+ last_space_x = *actual_x;
+ last_space_idx = nxtchr;
+ }
+ *actual_x += glyph->advance.x >> 16;
+ if (*actual_x > x && last_space_idx != 0) {
+ /* string has exceeded available width and we've
+ * found a space; return previous space */
+ *actual_x = last_space_x;
+ *char_offset = last_space_idx;
+ return true;
+ }
+ nxtchr = utf8_next(string, length, nxtchr);
+ }
+ *char_offset = nxtchr;
+ return (1);
+}
+
+
+static int pixel_pos( FONT_PLOTTER self, const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x)
+{
+ uint32_t ucs4;
+ size_t nxtchr = 0;
+ FT_Glyph glyph;
+ int prev_x = 0;
+
+ *actual_x = 0;
+ while (nxtchr < length) {
+ ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr);
+ glyph = ft_getglyph(fstyle, ucs4);
+ if (glyph == NULL)
+ continue;
+ *actual_x += glyph->advance.x >> 16;
+ if (*actual_x > x)
+ break;
+
+ prev_x = *actual_x;
+ nxtchr = utf8_next(string, length, nxtchr);
+ }
+
+ /* choose nearest of previous and last x */
+ if (abs(*actual_x - x) > abs(prev_x - x))
+ *actual_x = prev_x;
+ *char_offset = nxtchr;
+ return ( 1 );
+}
+
+
+static void draw_glyph8(FONT_PLOTTER self, GRECT * clip, GRECT * loc, uint8_t * pixdata, int pitch, uint32_t colour)
+{
+ uint32_t * linebuf;
+ uint32_t fontpix;
+ int xloop,yloop,xoff,yoff;
+ int x,y,w,h;
+
+ x = loc->g_x;
+ y = loc->g_y;
+ w = loc->g_w;
+ h = loc->g_h;
+
+ if( !rc_intersect( clip, loc ) ){
+ return;
+ }
+
+ xoff = loc->g_x - x;
+ yoff = loc->g_y - y;
+
+ assert( loc->g_h <= h );
+ assert( loc->g_w <= w );
+
+ h = loc->g_h;
+ w = loc->g_w;
+
+ assert( h <= fontbmp_allocated_height );
+ assert( w <= fontbmp_allocated_width );
+
+ fontbmp->height = h;
+ fontbmp->width = w;
+ for( yloop = 0; yloop < MIN(fontbmp_allocated_height, h); yloop++) {
+ linebuf = (uint32_t *)(fontbmp->pixdata + (fontbmp_stride * yloop));
+ for(xloop = 0; xloop < MIN(fontbmp_allocated_width, w); xloop++){
+ fontpix = (uint32_t)(pixdata[(( yoff + yloop ) * pitch) + xloop + xoff]);
+ linebuf[xloop] = (uint32_t)(colour | fontpix);
+ }
+ }
+ plot_blit_bitmap(fontbmp, loc->g_x, loc->g_y, 0, BITMAPF_MONOGLYPH);
+}
+
+static void draw_glyph1(FONT_PLOTTER self, GRECT * clip, GRECT * loc, uint8_t * pixdata, int pitch, uint32_t colour)
+{
+ int xloop,yloop,xoff,yoff;
+ int x,y,w,h;
+ uint8_t bitm;
+ const uint8_t *fntd;
+
+ x = loc->g_x;
+ y = loc->g_y;
+ w = loc->g_w;
+ h = loc->g_h;
+
+ if( !rc_intersect( clip, loc ) ){
+ return;
+ }
+
+ xoff = loc->g_x - x;
+ yoff = loc->g_y - y;
+
+ if (h > loc->g_h)
+ h = loc->g_h;
+
+ if (w > loc->g_w)
+ w = loc->g_w;
+
+ int stride = MFDB_STRIDE( w );
+ if( tmp.fd_addr == NULL || tmp_mfdb_size < MFDB_SIZE( 1, stride, h) ){
+ tmp_mfdb_size = init_mfdb( 1, w, h, MFDB_FLAG_STAND | MFDB_FLAG_ZEROMEM, &tmp );
+ } else {
+ void * buf = tmp.fd_addr;
+ int size = init_mfdb( 1, w, h, MFDB_FLAG_STAND | MFDB_FLAG_NOALLOC, &tmp );
+ tmp.fd_addr = buf;
+ memset( tmp.fd_addr, 0, size );
+ }
+ short * buf;
+ for( yloop = 0; yloop < h; yloop++) {
+ fntd = pixdata + (pitch * (yloop+yoff))+(xoff>>3);
+ buf = tmp.fd_addr;
+ buf += (tmp.fd_wdwidth*yloop);
+ for ( xloop = 0, bitm = (1<<(7-(xoff%8))); xloop < w; xloop++, bitm=(bitm>>1) ) {
+ if( (*fntd & bitm) != 0 ){
+ short whichbit = (1<<(15-(xloop%16)));
+ buf[xloop>>4] = ((buf[xloop>>4])|(whichbit));
+ }
+ if( bitm == 1 ) {
+ fntd++;
+ bitm = 128;
+ }
+ }
+ }
+#ifdef WITH_8BPP_SUPPORT
+ if( app.nplanes > 8 ){
+#endif
+ plot_blit_mfdb(loc, &tmp, OFFSET_CUSTOM_COLOR, PLOT_FLAG_TRANS );
+#ifdef WITH_8BPP_SUPPORT
+ } else {
+ plot_blit_mfdb(loc, &tmp, colour, PLOT_FLAG_TRANS );
+ }
+#endif
+
+}
+
+static int text( FONT_PLOTTER self, int x, int y, const char *text, size_t length,
+ const plot_font_style_t *fstyle )
+{
+ uint32_t ucs4;
+ size_t nxtchr = 0;
+ FT_Glyph glyph;
+ FT_BitmapGlyph bglyph;
+ GRECT loc, clip;
+ uint32_t c = fstyle->foreground ;
+ struct rect clipping;
+ /* in -> BGR */
+ /* out -> ARGB */
+ if( !(self->flags & FONTPLOT_FLAG_MONOGLYPH) ){
+ c = ABGR_TO_RGB(c);
+ } else {
+#ifdef WITH_8BPP_SUPPORT
+ if( app.nplanes > 8 ){
+#endif
+ RGB1000 out; /* struct with RGB shorts */
+ rgb_to_vdi1000( (unsigned char*)&c, &out);
+ vs_color(atari_plot_vdi_handle, OFFSET_CUSTOM_COLOR,
+ (short*)&out);
+#ifdef WITH_8BPP_SUPPORT
+ } else {
+ c = RGB_TO_VDI(c);
+ }
+#endif
+ }
+
+ plot_get_clip(&clipping);
+ clip.g_x = clipping.x0;
+ clip.g_y = clipping.y0;
+ clip.g_w = (clipping.x1 - clipping.x0)+1;
+ clip.g_h = (clipping.y1 - clipping.y0)+1;
+
+ fontbmp = atari_bitmap_realloc( clip.g_w, clip.g_h,
+ 4, clip.g_w << 2,
+ BITMAP_GROW, fontbmp );
+ fontbmp_stride = atari_bitmap_get_rowstride(fontbmp);
+ fontbmp_allocated_height = clip.g_h;
+ fontbmp_allocated_width = clip.g_w;
+
+ while (nxtchr < length) {
+ ucs4 = utf8_to_ucs4(text + nxtchr, length - nxtchr);
+ nxtchr = utf8_next(text, length, nxtchr);
+
+ glyph = ft_getglyph(fstyle, ucs4);
+ if (glyph == NULL){
+ continue;
+ }
+
+ if (glyph->format == FT_GLYPH_FORMAT_BITMAP) {
+ bglyph = (FT_BitmapGlyph)glyph;
+ loc.g_x = x + bglyph->left;
+ loc.g_y = y - bglyph->top;
+ loc.g_w = bglyph->bitmap.width;
+ loc.g_h = bglyph->bitmap.rows;
+
+ if( loc.g_w > 0) {
+ self->draw_glyph( self,
+ &clip, &loc,
+ bglyph->bitmap.buffer,
+ bglyph->bitmap.pitch,
+ c
+ );
+ }
+ }
+ x += glyph->advance.x >> 16;
+ }
+ return( 0 );
+}
+
+
+int ctor_font_plotter_freetype( FONT_PLOTTER self )
+{
+ self->dtor = dtor;
+ self->str_width = str_width;
+ self->str_split = str_split;
+ self->pixel_pos = pixel_pos;
+ self->text = text;
+
+ /* set the default render mode */
+ if( (self->flags & FONTPLOT_FLAG_MONOGLYPH) != 0 ){
+ ft_load_type = FT_LOAD_MONOCHROME;
+ self->draw_glyph = draw_glyph1;
+ }
+ else{
+ ft_load_type = 0;
+ self->draw_glyph = draw_glyph8;
+ }
+
+ LOG("%s: %s\n", (char *)__FILE__, __FUNCTION__);
+ if( !init ) {
+ ft_font_init();
+ fontbmp = atari_bitmap_create(48, 48, 0);
+ fontbmp->opaque = false;
+ init = true;
+ }
+
+ return( 1 );
+}
+
+static int dtor( FONT_PLOTTER self )
+{
+ ft_font_finalise();
+ if( fontbmp != NULL ) {
+ atari_bitmap_destroy( fontbmp );
+ fontbmp = NULL;
+ }
+ if( tmp.fd_addr != NULL ){
+ free( tmp.fd_addr );
+ }
+ return( 1 );
+}
+
+#endif
diff --git a/frontends/atari/plot/font_freetype.h b/frontends/atari/plot/font_freetype.h
new file mode 100644
index 000000000..58a5372a4
--- /dev/null
+++ b/frontends/atari/plot/font_freetype.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2011 Ole Loots <ole@monochrom.net>
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 FONT_PLOTTER_FREETYPE
+#define FONT_PLOTTER_FREETYPE
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+#include "utils/utf8.h"
+
+/* defines for accesing the faces */
+#define FONT_FACE_DEFAULT 0
+
+#define FONT_FACE_SANS_SERIF 0
+#define FONT_FACE_SANS_SERIF_BOLD 1
+#define FONT_FACE_SANS_SERIF_ITALIC 2
+#define FONT_FACE_SANS_SERIF_ITALIC_BOLD 3
+#define FONT_FACE_MONOSPACE 4
+#define FONT_FACE_MONOSPACE_BOLD 5
+#define FONT_FACE_SERIF 6
+#define FONT_FACE_SERIF_BOLD 7
+#define FONT_FACE_CURSIVE 8
+#define FONT_FACE_FANTASY 9
+
+#define FONT_FACE_COUNT 10
+
+struct font_desc {
+ const char *name;
+ int width, height;
+ const char *encoding;
+};
+
+/* extern int ft_load_type; */
+
+int ctor_font_plotter_freetype( FONT_PLOTTER self );
+
+#endif
diff --git a/frontends/atari/plot/font_internal.c b/frontends/atari/plot/font_internal.c
new file mode 100644
index 000000000..89a56d0e5
--- /dev/null
+++ b/frontends/atari/plot/font_internal.c
@@ -0,0 +1,2386 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ * Copyright 2011 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#ifdef WITH_INTERNAL_FONT_DRIVER
+
+#include <assert.h>
+#include <unistd.h>
+
+#include "utils/utf8.h"
+#include "utils/log.h"
+#include "desktop/mouse.h"
+#include "desktop/plot_style.h"
+
+#include "atari/gui.h"
+#include "atari/bitmap.h"
+#include "atari/plot/plot.h"
+#include "atari/plot/fontplot.h"
+#include "atari/plot/font_internal.h"
+
+
+extern unsigned long atari_plot_flags;
+extern int atari_plot_vdi_handle;
+
+static int dtor( FONT_PLOTTER self );
+static int str_width( FONT_PLOTTER self,const plot_font_style_t *fstyle, const char * str, size_t length, int * width );
+static int str_split( FONT_PLOTTER self, const plot_font_style_t *fstyle,const char *string,
+ size_t length,int x, size_t *char_offset, int *actual_x );
+static int pixel_pos( FONT_PLOTTER self, const plot_font_style_t *fstyle,const char *string,
+ size_t length,int x, size_t *char_offset, int *actual_x );
+static int text( FONT_PLOTTER self, int x, int y, const char *text, size_t length, const plot_font_style_t *fstyle );
+
+static bool init = false;
+static int vdih;
+static struct bitmap * fontbmp;
+extern struct s_vdi_sysinfo vdi_sysinfo;
+
+const struct fb_font_desc font_regular;
+const struct fb_font_desc font_italic;
+const struct fb_font_desc font_bold;
+const struct fb_font_desc font_italic_bold;
+
+static MFDB tmp;
+static int tmp_mfdb_size;
+
+#define FONTDATAMAX 4096
+
+static const struct fb_font_desc*
+fb_get_font(const plot_font_style_t *fstyle)
+{
+ if (fstyle->weight >= 700) {
+ if ((fstyle->flags & FONTF_ITALIC) ||
+ (fstyle->flags & FONTF_OBLIQUE)) {
+ return &font_italic_bold;
+ } else {
+ return &font_bold;
+ }
+ } else {
+ if ((fstyle->flags & FONTF_ITALIC) ||
+ (fstyle->flags & FONTF_OBLIQUE)) {
+ return &font_italic;
+ } else {
+ return &font_regular;
+ }
+ }
+}
+
+static nserror utf8_to_font_encoding(const struct fb_font_desc* font,
+ const char *string,
+ size_t len,
+ char **result)
+{
+ return utf8_to_enc(string, font->encoding, len, result);
+
+}
+
+int ctor_font_plotter_internal( FONT_PLOTTER self )
+{
+ self->dtor = dtor;
+ self->str_width = str_width;
+ self->str_split = str_split;
+ self->pixel_pos = pixel_pos;
+ self->text = text;
+ LOG("%s: %s\n", (char *)__FILE__, __FUNCTION__);
+ if( !init ) {
+ vdih = self->vdi_handle;
+ fontbmp = atari_bitmap_create(48, 48, 0);
+ fontbmp->opaque = false;
+ }
+ init = true;
+ return( 1 );
+}
+
+static int dtor( FONT_PLOTTER self )
+{
+ if( tmp.fd_addr != NULL ){
+ free( tmp.fd_addr );
+ }
+ return( 1 );
+}
+
+static int str_width( FONT_PLOTTER self,const plot_font_style_t *fstyle, const char * str,
+ size_t length, int * width )
+{
+ const struct fb_font_desc* fb_font = fb_get_font(fstyle);
+ *width = fb_font->width * utf8_bounded_length(str, length);
+ return( 1 );
+}
+
+static int str_split( FONT_PLOTTER self, const plot_font_style_t * fstyle, const char *string,
+ size_t length,int x, size_t *char_offset, int *actual_x )
+{
+ const struct fb_font_desc* fb_font = fb_get_font(fstyle);
+ int c_off = *char_offset = x / fb_font->width;
+ if (*char_offset > length) {
+ *char_offset = length;
+ } else {
+ while (*char_offset > 0) {
+ if (string[*char_offset] == ' ')
+ break;
+ (*char_offset)--;
+ }
+ if (*char_offset == 0) {
+ *char_offset = c_off;
+ while (*char_offset < length &&
+ string[*char_offset] != ' ') {
+ (*char_offset)++;
+ }
+ }
+ }
+ *actual_x = *char_offset * fb_font->width;
+ return( 1 );
+}
+
+static int pixel_pos( FONT_PLOTTER self, const plot_font_style_t * fstyle,const char *string,
+ size_t length,int x, size_t *char_offset, int *actual_x )
+{
+ const struct fb_font_desc* fb_font = fb_get_font(fstyle);
+ *char_offset = x / fb_font->width;
+ if (*char_offset > length)
+ *char_offset = length;
+ *actual_x = *char_offset * fb_font->width;
+ return( 1 );
+}
+
+static void draw_glyph1(FONT_PLOTTER self, GRECT *inloc, uint8_t *chrp, int pitch, uint32_t colour )
+{
+ size_t bmpstride;
+ GRECT clip;
+ uint32_t * fontdata;
+ int xloop,yloop;
+ int stride = pitch / 8;
+ uint32_t * linebuf;
+ GRECT loc = *inloc;
+
+ fontbmp = atari_bitmap_realloc( loc.g_w, loc.g_h, fontbmp->bpp, loc.g_w * fontbmp->bpp, BITMAP_GROW, fontbmp );
+ bmpstride = atari_bitmap_get_rowstride(fontbmp);
+ for( yloop = 0; yloop < loc.g_h; yloop++) {
+ uint32_t pixmask = 1 ;
+ linebuf = (uint32_t *)(fontbmp->pixdata + (bmpstride * yloop));
+ fontdata = (uint32_t*)(chrp + (stride*yloop));
+ for(xloop = loc.g_w-1; xloop>=0; xloop--){
+ linebuf[xloop] = ((pixmask & *fontdata) == 0) ? 0 : colour;
+ pixmask = (pixmask << 1);
+ }
+ }
+ plot_blit_bitmap(fontbmp, loc.g_x, loc.g_y, 0, BITMAPF_MONOGLYPH );
+}
+
+static int text( FONT_PLOTTER self, int x, int y, const char *text, size_t length,
+ const plot_font_style_t *fstyle )
+{
+ const struct fb_font_desc* fb_font = fb_get_font(fstyle);
+ const uint32_t *chrp;
+ char *buffer = NULL;
+ int chr;
+ int blen;
+ GRECT loc;
+ uint32_t c;
+
+ utf8_to_font_encoding(fb_font, text, length, &buffer);
+ if (buffer == NULL)
+ return 1;
+
+ /* y is given to the fonts baseline we need it to the fonts top */
+ y-=((fb_font->height * 75)/100);
+
+ /* needed? */
+ y+=1; /* the coord is the bottom-left of the pixels offset by 1 to make
+ * it work since fb coords are the top-left of pixels
+ */
+ blen = strlen(buffer);
+ if ( blen < 1 ) {
+ return( 1 );
+ }
+
+ if(atari_plot_flags & PLOT_FLAG_OFFSCREEN ){
+ /* when the plotter is an offscreen plotter the call to
+ bitmap() isn't that expensive. Draw an 8 bit bitmap into the
+ offscreen buffer.
+ */
+ c = fstyle->foreground;
+ /* in -> BGR */
+ /* out -> ARGB */
+ c = (ABGR_TO_RGB(c) | 0xFF);
+ loc.g_y = y;
+ loc.g_x = x;
+ loc.g_w = fb_font->width;
+ loc.g_h = fb_font->height;
+ for (chr = 0; chr < blen; chr++) {
+ loc.g_x = x;
+ chrp = fb_font->data + ((unsigned char)buffer[chr] * fb_font->height);
+ draw_glyph1(self, &loc, (uint8_t *)chrp, 32, c);
+ x+=fb_font->width;
+ }
+ } else {
+ /* render the whole string into an monochrom mfdb */
+ /* and plot that to reduce overhead */
+ loc.g_x = x;
+ loc.g_y = y;
+ loc.g_w = blen * fb_font->width;
+ assert( loc.g_w > 0 );
+ loc.g_h = fb_font->height;
+ int stride = MFDB_STRIDE( loc.g_w );
+ if( tmp.fd_addr == NULL || tmp_mfdb_size < MFDB_SIZE( 1, stride, loc.g_h) ){
+ tmp_mfdb_size = init_mfdb( 1, loc.g_w, loc.g_h+1, MFDB_FLAG_STAND | MFDB_FLAG_ZEROMEM, &tmp );
+ } else {
+ void * buf = tmp.fd_addr;
+ int size = init_mfdb( 1, loc.g_w, loc.g_h+1, MFDB_FLAG_STAND | MFDB_FLAG_NOALLOC, &tmp );
+ tmp.fd_addr = buf;
+ memset( tmp.fd_addr, 0, size );
+ }
+ short ypos;
+ int rowsize = tmp.fd_wdwidth << 1;
+ char * d;
+ uint32_t * pp;
+ for (chr = 0; chr < blen; chr++) {
+ pp = (uint32_t*)fb_font->data + ((unsigned char)buffer[chr] * fb_font->height);
+ d = ((uint8_t*)tmp.fd_addr) + chr;
+ for( ypos=0; ypos<loc.g_h; ypos++){
+ *d = (unsigned char)*pp++;
+ d += rowsize;
+ }
+ }
+#ifdef WITH_8BPP_SUPPORT
+ if( app.nplanes > 8 ){
+#endif
+ //unsigned short out[4];
+ RGB1000 out;
+ //rgb_to_vdi1000( (unsigned char*)&fstyle->foreground, (unsigned short*)&out );
+ out.blue = 0;
+ out.green = 1000;
+ out.red = 0;
+ vs_color(atari_plot_vdi_handle, OFFSET_CUSTOM_COLOR, (short *)&out);
+ vq_color(atari_plot_vdi_handle, OFFSET_CUSTOM_COLOR, 1, (short *)&out);
+ //printf("r:%d,g:%d,b:%d", out.red, out.green, out.blue);
+ //vsl_color(atari_plot_vdi_handle, OFFSET_CUSTOM_COLOR);
+ //vsf_color(atari_plot_vdi_handle, OFFSET_CUSTOM_COLOR);
+ plot_blit_mfdb(&loc, &tmp, OFFSET_CUSTOM_COLOR, PLOT_FLAG_TRANS);
+#ifdef WITH_8BPP_SUPPORT
+ } else {
+ unsigned char c = RGB_TO_VDI(fstyle->foreground);
+ plot_blit_mfdb(&loc, &tmp, c, PLOT_FLAG_TRANS );
+ }
+#endif
+ }
+
+ free(buffer);
+ return( 1 );
+}
+
+/* ------------------*/
+/* Fontdata */
+/* ------------------*/
+
+static const uint32_t fontdata_bold[FONTDATAMAX] = {
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xE4, 0xAC, 0xA4, 0xA4, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEC, 0xA2, 0xA4, 0xA8, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEC, 0xA2, 0xAC, 0xA2, 0xEC, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEA, 0xAA, 0xAE, 0xA2, 0xE2, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEE, 0xA8, 0xAE, 0xA2, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xE6, 0xA8, 0xAE, 0xAA, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEE, 0xA2, 0xA4, 0xA4, 0xE4, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEE, 0xAA, 0xAE, 0xAA, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEE, 0xAA, 0xAE, 0xA2, 0xE2, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xE4, 0xAA, 0xAE, 0xAA, 0xEA, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEC, 0xAA, 0xAC, 0xAA, 0xEC, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xE4, 0xAA, 0xA8, 0xAA, 0xE4, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEC, 0xAA, 0xAA, 0xAA, 0xEC, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEE, 0xA8, 0xAC, 0xA8, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEE, 0xA8, 0xAC, 0xA8, 0xE8, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4E, 0xCA, 0x4A, 0x4A, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x44, 0xCC, 0x44, 0x44, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4C, 0xC2, 0x44, 0x48, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4C, 0xC2, 0x4C, 0x42, 0xEC, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4A, 0xCA, 0x4E, 0x42, 0xE2, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4E, 0xC8, 0x4E, 0x42, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x46, 0xC8, 0x4E, 0x4A, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4E, 0xC2, 0x44, 0x44, 0xE4, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4E, 0xCA, 0x4E, 0x4A, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4E, 0xCA, 0x4E, 0x42, 0xE2, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x44, 0xCA, 0x4E, 0x4A, 0xEA, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4C, 0xCA, 0x4C, 0x4A, 0xEC, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x44, 0xCA, 0x48, 0x4A, 0xE4, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4C, 0xCA, 0x4A, 0x4A, 0xEC, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4E, 0xC8, 0x4C, 0x48, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4E, 0xC8, 0x4C, 0x48, 0xE8, 0x00, 0xFE,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C,
+ 0x1C, 0x00, 0x1C, 0x1C, 0x1C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x6C, 0x6C, 0xFE, 0xFE, 0xFE, 0x6C, 0x6C,
+ 0xFE, 0xFE, 0xFE, 0x6C, 0x6C, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x7C, 0xFE, 0xF6, 0xF0, 0xF8, 0x7C,
+ 0x3E, 0x1E, 0xDE, 0xFE, 0x7C, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xE6, 0xEE, 0x1C,
+ 0x38, 0x70, 0xEE, 0xCE, 0x0E, 0x00, 0x00, 0x00,
+ 0x00, 0x70, 0xF8, 0xF8, 0xF8, 0xF8, 0x76, 0xFE,
+ 0xFE, 0xEC, 0xEE, 0xFE, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x0C, 0x1C, 0x38, 0x38, 0x70, 0x70, 0x70,
+ 0x70, 0x70, 0x38, 0x38, 0x1C, 0x0C, 0x06, 0x00,
+ 0x60, 0x30, 0x38, 0x1C, 0x1C, 0x0E, 0x0E, 0x0E,
+ 0x0E, 0x0E, 0x1C, 0x1C, 0x38, 0x30, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x54, 0xFE, 0x7C, 0x38,
+ 0x7C, 0xFE, 0x54, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x38, 0xFE,
+ 0xFE, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x38, 0x78, 0x70, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE,
+ 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x1E, 0x3C,
+ 0x78, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x7C, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0x7C, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x38, 0x78, 0x78, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xEE, 0x0E, 0x0E, 0x7E, 0xFC,
+ 0xE0, 0xE0, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xEE, 0x0E, 0x0E, 0x3C, 0x3C,
+ 0x0E, 0x0E, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x0C, 0x1C, 0x3C, 0x7C, 0x6C, 0xEC,
+ 0xFE, 0xFE, 0x1C, 0x1C, 0x1C, 0x00, 0x00, 0x00,
+ 0x00, 0xFE, 0xFE, 0xE0, 0xE0, 0xFC, 0xFE, 0x0E,
+ 0x0E, 0x0E, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFC, 0xE0, 0xE0, 0xFC, 0xFE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0xFE, 0xFE, 0x0E, 0x0E, 0x1C, 0x1C, 0x1C,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xEE, 0xEE, 0xFE, 0x7C, 0xFE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xEE, 0xEE, 0xEE, 0xFE, 0x7E,
+ 0x0E, 0x0E, 0x0E, 0x7C, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x38, 0x00,
+ 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x38, 0x00,
+ 0x00, 0x18, 0x38, 0x38, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x0E, 0x1C, 0x38, 0x70,
+ 0x70, 0x38, 0x1C, 0x0E, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0x00,
+ 0x00, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x60, 0x70, 0x38, 0x1C, 0x0E,
+ 0x0E, 0x1C, 0x38, 0x70, 0x60, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xEE, 0x0E, 0x0E, 0x3E, 0x7C,
+ 0x70, 0x00, 0x70, 0x70, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xEE, 0xEE, 0xEE, 0xFE, 0xFE,
+ 0xEC, 0xE0, 0xE0, 0xFC, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xEE, 0xEE, 0xEE, 0xEE, 0xFE,
+ 0xFE, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0xFC, 0xFE, 0xEE, 0xEE, 0xEE, 0xFC, 0xFC,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0xFC, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xEE, 0xEE, 0xE0, 0xE0, 0xE0,
+ 0xE0, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0xF8, 0xFC, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFC, 0xF8, 0x00, 0x00, 0x00,
+ 0x00, 0xFE, 0xFE, 0xE0, 0xE0, 0xE0, 0xF8, 0xF8,
+ 0xE0, 0xE0, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0xFE, 0xFE, 0xE0, 0xE0, 0xE0, 0xF8, 0xF8,
+ 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xEE, 0xEE, 0xE0, 0xE0, 0xFE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xFE, 0xFE,
+ 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0xFE, 0xFE, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E,
+ 0x0E, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0xE0, 0xE6, 0xEE, 0xFE, 0xFC, 0xF8, 0xF0,
+ 0xF8, 0xFC, 0xFE, 0xEE, 0xE6, 0x00, 0x00, 0x00,
+ 0x00, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0,
+ 0xE0, 0xE0, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0xC6, 0xEE, 0xFE, 0xFE, 0xFE, 0xFE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0xCE, 0xEE, 0xEE, 0xFE, 0xFE, 0xFE, 0xFE,
+ 0xFE, 0xFE, 0xEE, 0xEE, 0xE6, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0xFC, 0xFE, 0xEE, 0xEE, 0xEE, 0xEE, 0xFE,
+ 0xFC, 0xE0, 0xE0, 0xE0, 0xE0, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
+ 0xEE, 0xFE, 0xFE, 0xFE, 0x7E, 0x06, 0x00, 0x00,
+ 0x00, 0xFC, 0xFE, 0xEE, 0xEE, 0xEE, 0xFE, 0xFC,
+ 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xEE, 0xE0, 0xE0, 0xFC, 0x7E,
+ 0x0E, 0x0E, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0xFE, 0xFE, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
+ 0x6C, 0x7C, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
+ 0xFE, 0xFE, 0xFE, 0xEE, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0xEE, 0xEE, 0xEE, 0xEE, 0x7C, 0x38, 0x38,
+ 0x7C, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0x7C, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0xFE, 0xFE, 0x0E, 0x0E, 0x1C, 0x38, 0x38,
+ 0x70, 0xE0, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x3E, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x3E, 0x3E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x40, 0xE0, 0xF0, 0x78,
+ 0x3C, 0x1E, 0x0E, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xFC, 0xFC, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C,
+ 0x1C, 0x1C, 0x1C, 0xFC, 0xFC, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x7C, 0xEE, 0xC6, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00,
+ 0x00, 0x00, 0x00, 0x30, 0x38, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x7E, 0x0E,
+ 0x7E, 0xFE, 0xEE, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0xE0, 0xE0, 0xE0, 0xE0, 0xFC, 0xFE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0xFC, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xFE, 0xEE,
+ 0xE0, 0xE0, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x0E, 0x0E, 0x0E, 0x0E, 0x7E, 0xFE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xFE, 0xEE,
+ 0xFE, 0xFE, 0xE0, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x7E, 0x70, 0x70, 0x70, 0xF8, 0xF8,
+ 0x70, 0x70, 0x70, 0x70, 0x70, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xFE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7E, 0x0E, 0x7E, 0x7C,
+ 0x00, 0x00, 0xE0, 0xE0, 0xE0, 0xFC, 0xFE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x38, 0x00, 0x78, 0x78, 0x38,
+ 0x38, 0x38, 0x38, 0x7C, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x3C, 0x3C, 0x1C,
+ 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x7C, 0x78,
+ 0x00, 0xE0, 0xE0, 0xE0, 0xE6, 0xEE, 0xFE, 0xFC,
+ 0xF8, 0xFC, 0xFE, 0xEE, 0xE6, 0x00, 0x00, 0x00,
+ 0x00, 0x78, 0x78, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xFE, 0xFE,
+ 0xFE, 0xFE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xFE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0xFC, 0xE0, 0xE0, 0xE0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xFE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7E, 0x0E, 0x0F, 0x0F,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xFE, 0xE0,
+ 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xFE, 0xE0,
+ 0xFC, 0x7E, 0x0E, 0xFE, 0xFC, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x30, 0x70, 0x70, 0xFC, 0xFC, 0x70,
+ 0x70, 0x70, 0x70, 0x7E, 0x3E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0x7C, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xEE, 0xEE,
+ 0xFE, 0xFE, 0xFE, 0xEE, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xEE, 0xEE,
+ 0x7C, 0x7C, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xFE, 0x7E, 0x0E, 0x0E, 0xFE, 0xFC,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0x1E,
+ 0x3C, 0x78, 0xF0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x1E, 0x3E, 0x38, 0x38, 0x38, 0xF0, 0xF0,
+ 0x38, 0x38, 0x38, 0x3E, 0x1E, 0x00, 0x00, 0x00,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x00,
+ 0x00, 0xF0, 0xF8, 0x38, 0x38, 0x38, 0x1E, 0x1E,
+ 0x38, 0x38, 0x38, 0xF8, 0xF0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0xFE, 0xFE, 0xCC, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEE, 0x28, 0x4C, 0x48, 0x48, 0x00, 0xFE,
+ 0x00, 0x38, 0x7C, 0xEE, 0xE0, 0xFC, 0xE0, 0xF8,
+ 0xE0, 0xE0, 0xEE, 0x7C, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x38, 0x6C, 0x54, 0xF6, 0xEE,
+ 0x6C, 0x7C, 0x28, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x38, 0x38, 0x18, 0x30, 0x00,
+ 0x00, 0x3E, 0x7E, 0x70, 0x70, 0x70, 0xF8, 0xF8,
+ 0x70, 0x70, 0x70, 0x70, 0x70, 0xF0, 0xE0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xEE, 0xEE, 0x66, 0xCC, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x38, 0x38, 0x38, 0xFE, 0xFE, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00,
+ 0x00, 0x38, 0x38, 0x38, 0xFE, 0xFE, 0x38, 0xFE,
+ 0xFE, 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00,
+ 0x00, 0x7C, 0xEE, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xCE, 0x1C,
+ 0x38, 0x70, 0xE0, 0xDB, 0x1B, 0x00, 0x00, 0x00,
+ 0xEE, 0x7C, 0x00, 0x7C, 0xFE, 0xEE, 0xE0, 0xFC,
+ 0x7E, 0x0E, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0E, 0x1C,
+ 0x38, 0x70, 0x38, 0x1C, 0x0E, 0x06, 0x00, 0x00,
+ 0x00, 0x7F, 0xFF, 0xEE, 0xEE, 0xEE, 0xEF, 0xEF,
+ 0xEE, 0xEE, 0xEE, 0xFF, 0x7F, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x38, 0x6C, 0x54, 0xF6, 0xEE,
+ 0x6C, 0x7C, 0x28, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0xEE, 0x7C, 0x00, 0xFE, 0xFE, 0x0E, 0x1E, 0x3C,
+ 0x78, 0xF0, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x38, 0x6C, 0x54, 0xF6, 0xEE,
+ 0x6C, 0x7C, 0x28, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x38, 0x6C, 0x54, 0xF6, 0xEE,
+ 0x6C, 0x7C, 0x28, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x30, 0x30, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x36, 0x6C, 0x6C, 0x6C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x36, 0x36, 0x36, 0x6C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x7E,
+ 0x7E, 0x7E, 0x7E, 0x3C, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3C, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x7B, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xF1, 0xFB, 0x5F, 0x55, 0x51, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xEE, 0x7C, 0x38, 0x00, 0x7E, 0xFE, 0xE0,
+ 0xFC, 0x7E, 0x0E, 0xFE, 0xFC, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x70, 0x38,
+ 0x1C, 0x0E, 0x1C, 0x38, 0x70, 0x60, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6C, 0xFE, 0xFA,
+ 0xDE, 0xDE, 0xFC, 0xFE, 0x6E, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x38, 0x6C, 0x54, 0xF6, 0xEE,
+ 0x6C, 0x7C, 0x28, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0xEE, 0x7C, 0x38, 0x00, 0xFE, 0xFE, 0x1E,
+ 0x3C, 0x78, 0xF0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0xEE, 0x00, 0xEE, 0xEE, 0xEE, 0xEE, 0x7C,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x38, 0x00,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x10, 0x7C, 0xFE, 0xD6,
+ 0xD0, 0xD0, 0xD6, 0xFE, 0x7C, 0x10, 0x10, 0x00,
+ 0x00, 0x3C, 0x7E, 0x76, 0x70, 0xFC, 0xFC, 0xFC,
+ 0x70, 0x76, 0xFE, 0xFE, 0xDC, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0xEE, 0x7C, 0xFE,
+ 0xFE, 0xFE, 0xFE, 0x7C, 0xEE, 0xEE, 0x00, 0x00,
+ 0x00, 0x00, 0xEE, 0xEE, 0xEE, 0x7C, 0x38, 0xFE,
+ 0x38, 0xFE, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x38, 0x38, 0x38, 0x38, 0x00,
+ 0x00, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x3C, 0x7C, 0x7C, 0x70, 0x3C, 0x7E, 0x66,
+ 0x7E, 0x3C, 0x0E, 0x3E, 0x3E, 0x3C, 0x00, 0x00,
+ 0x00, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x3C, 0x7E, 0xC3, 0x99, 0xBD, 0xA5, 0xA1,
+ 0xA5, 0xBD, 0x99, 0xC3, 0x7E, 0x3C, 0x00, 0x00,
+ 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x7C, 0x00, 0xFC,
+ 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E,
+ 0x7C, 0xF8, 0xF8, 0x7C, 0x3E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7E, 0x7E, 0x7E, 0x06, 0x02, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7E, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x3C, 0x7E, 0xC3, 0xB9, 0xBD, 0xA5, 0xB9,
+ 0xB9, 0xA5, 0xA5, 0xC3, 0x7E, 0x3C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x78, 0xFC, 0xCC, 0xFC, 0x78, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x38, 0xFE, 0xFE, 0xFE, 0x38,
+ 0x38, 0x00, 0xFE, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xF0, 0xF8, 0x18, 0x78, 0xF0, 0xC0,
+ 0xF8, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xF0, 0xF8, 0x18, 0x30, 0x18, 0xF8,
+ 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x38, 0x70, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0xFC, 0xE0, 0xE0, 0xC0,
+ 0x00, 0x00, 0x00, 0x7F, 0xDB, 0xDB, 0xDB, 0x7B,
+ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x38,
+ 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x1C, 0x0C, 0x1C, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x60, 0xE0, 0x60, 0x60, 0x60, 0xF0,
+ 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x70, 0xF8, 0x88, 0xF8, 0x70, 0x00, 0xF8,
+ 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8,
+ 0x6C, 0x36, 0x36, 0x6C, 0xD8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x60, 0xE0, 0x62, 0x66, 0xFE, 0x1C,
+ 0x38, 0x76, 0xEE, 0xD6, 0x3E, 0x3E, 0x06, 0x00,
+ 0x00, 0x00, 0x60, 0xE0, 0x62, 0x66, 0xFE, 0x1C,
+ 0x38, 0x70, 0xFC, 0xC6, 0x0C, 0x18, 0x1E, 0x00,
+ 0x00, 0x00, 0xE0, 0x30, 0x62, 0x36, 0xEE, 0x1C,
+ 0x3A, 0x76, 0xEE, 0xD6, 0x3E, 0x3E, 0x06, 0x00,
+ 0x00, 0x00, 0x1C, 0x1C, 0x1C, 0x00, 0x1C, 0x1C,
+ 0x7C, 0xF8, 0xE0, 0xEE, 0xEE, 0xFE, 0x7C, 0x00,
+ 0x00, 0xE0, 0x70, 0x00, 0x10, 0x38, 0x7C, 0xEE,
+ 0xFE, 0xFE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x0E, 0x1C, 0x00, 0x10, 0x38, 0x7C, 0xEE,
+ 0xFE, 0xFE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6C, 0xC6, 0x10, 0x38, 0x7C, 0xEE,
+ 0xFE, 0xFE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x76, 0xDC, 0x00, 0x10, 0x38, 0x7C, 0xEE,
+ 0xFE, 0xFE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0xEE, 0xEE, 0x10, 0x38, 0x7C, 0xEE, 0xFE,
+ 0xFE, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6C, 0x38, 0x38, 0x7C, 0xEE, 0xFE,
+ 0xFE, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x1F, 0x3F, 0x7F, 0xFE, 0xEE, 0xFF, 0xFF,
+ 0xFE, 0xEE, 0xEF, 0xEF, 0xEF, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xEE, 0xE0, 0xE0, 0xE0, 0xE0,
+ 0xE0, 0xE0, 0xEE, 0xFE, 0x7C, 0x0E, 0x6E, 0x7C,
+ 0x00, 0x70, 0x38, 0x00, 0xFE, 0xFE, 0xE0, 0xE0,
+ 0xF8, 0xE0, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x1C, 0x38, 0x00, 0xFE, 0xFE, 0xE0, 0xE0,
+ 0xF8, 0xE0, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xEE, 0x00, 0xFE, 0xFE, 0xE0, 0xE0,
+ 0xF8, 0xE0, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0xEE, 0xEE, 0x00, 0xFE, 0xFE, 0xE0, 0xE0, 0xF8,
+ 0xF8, 0xE0, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x70, 0x38, 0x00, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x1C, 0x38, 0x00, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6C, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0xEE, 0xEE, 0x00, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xF8, 0xFC, 0xEE, 0xEE, 0xEE, 0xFE,
+ 0xEE, 0xEE, 0xEE, 0xFC, 0xF8, 0x00, 0x00, 0x00,
+ 0x00, 0x76, 0xDC, 0x00, 0xEE, 0xEE, 0xEE, 0xF6,
+ 0xFE, 0xDE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x70, 0x38, 0x00, 0x7C, 0xFE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x1C, 0x38, 0x00, 0x7C, 0xFE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xEE, 0x00, 0x7C, 0xFE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x76, 0xDC, 0x00, 0x7C, 0xFE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0xEE, 0xEE, 0x00, 0x7C, 0xFE, 0xEE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xEE, 0x7C,
+ 0x38, 0x38, 0x7C, 0xEE, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x7A, 0xFC, 0xEE, 0xEE, 0xEE, 0xFE, 0xFE,
+ 0xFE, 0xEE, 0xEE, 0xFE, 0x7C, 0x80, 0x00, 0x00,
+ 0x00, 0x70, 0x38, 0x00, 0xEE, 0xEE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x1C, 0x38, 0x00, 0xEE, 0xEE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xEE, 0x00, 0xEE, 0xEE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0xEE, 0xEE, 0x00, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x1C, 0x38, 0x00, 0xEE, 0xEE, 0xEE, 0xEE,
+ 0x7C, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xE0, 0xE0, 0xFC, 0xFE, 0xEE, 0xEE,
+ 0xEE, 0xFE, 0xFC, 0xE0, 0xE0, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xEE, 0xEE, 0xEE, 0xFE, 0xFC,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0xFC, 0xE0, 0xC0, 0x00,
+ 0x00, 0x00, 0x70, 0x38, 0x00, 0x7C, 0x7E, 0x0E,
+ 0x7E, 0xFE, 0xEE, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1C, 0x38, 0x00, 0x7C, 0x7E, 0x0E,
+ 0x7E, 0xFE, 0xEE, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x7C, 0xEE, 0x00, 0x7C, 0x7E, 0x0E,
+ 0x7E, 0xFE, 0xEE, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x76, 0xDC, 0x00, 0x7C, 0x7E, 0x0E,
+ 0x7E, 0xFE, 0xEE, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xEE, 0xEE, 0x00, 0x7C, 0x7E, 0x0E,
+ 0x7E, 0xFE, 0xEE, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6C, 0x38, 0x00, 0x7C, 0x7E, 0x0E,
+ 0x7E, 0xFE, 0xEE, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x74, 0x7E, 0x7E, 0x1A,
+ 0x7E, 0xFE, 0xD8, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xFE,
+ 0xEE, 0xE0, 0xEE, 0xFE, 0x7C, 0x1C, 0x7E, 0x7C,
+ 0x00, 0x00, 0x70, 0x38, 0x00, 0x7C, 0xFE, 0xEE,
+ 0xFE, 0xFE, 0xE0, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x70, 0x00, 0x7C, 0xFE, 0xEE,
+ 0xFE, 0xFE, 0xE0, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x7C, 0xEE, 0x00, 0x7C, 0xFE, 0xEE,
+ 0xFE, 0xFE, 0xE0, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xEE, 0xEE, 0x00, 0x7C, 0xFE, 0xEE,
+ 0xFE, 0xFE, 0xE0, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x70, 0x38, 0x00, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1C, 0x38, 0x00, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x7C, 0xEE, 0x00, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xEE, 0xEE, 0x00, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0xEC, 0x7C, 0xF8, 0xFC, 0x7E, 0xFE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x76, 0xFE, 0xDC, 0x00, 0xFC, 0xFE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xE0, 0x70, 0x00, 0x7C, 0xFE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x70, 0x00, 0x7C, 0xFE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x7C, 0xEE, 0x00, 0x7C, 0xFE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x76, 0xFE, 0xDC, 0x00, 0x7C, 0xFE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xEE, 0xEE, 0x00, 0x7C, 0xFE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0xFE,
+ 0xFE, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x7C, 0xFE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0xE0, 0x70, 0x00, 0xEE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x70, 0x00, 0xEE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x7C, 0xEE, 0x00, 0xEE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xEE, 0xEE, 0x00, 0xEE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1C, 0x38, 0x00, 0xEE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7E, 0x0E, 0xFE, 0xFC,
+ 0x00, 0x00, 0xE0, 0xE0, 0xFC, 0xFE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xEE, 0xFE, 0xFC, 0xE0, 0xE0,
+ 0x00, 0x00, 0xEE, 0xEE, 0x00, 0xEE, 0xEE, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7E, 0x0E, 0xFE, 0xFC,
+};
+
+const struct fb_font_desc font_bold = {
+ .name = "NetSurf Bold",
+ .width = 8,
+ .height = 16,
+ .encoding = "CP1252",
+ .data = fontdata_bold,
+};
+
+
+static const uint32_t fontdata_italic[FONTDATAMAX] = {
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xE4, 0xAC, 0xA4, 0xA4, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEC, 0xA2, 0xA4, 0xA8, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEC, 0xA2, 0xAC, 0xA2, 0xEC, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEA, 0xAA, 0xAE, 0xA2, 0xE2, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEE, 0xA8, 0xAE, 0xA2, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xE6, 0xA8, 0xAE, 0xAA, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEE, 0xA2, 0xA4, 0xA4, 0xE4, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEE, 0xAA, 0xAE, 0xAA, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEE, 0xAA, 0xAE, 0xA2, 0xE2, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xE4, 0xAA, 0xAE, 0xAA, 0xEA, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEC, 0xAA, 0xAC, 0xAA, 0xEC, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xE4, 0xAA, 0xA8, 0xAA, 0xE4, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEC, 0xAA, 0xAA, 0xAA, 0xEC, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEE, 0xA8, 0xAC, 0xA8, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEE, 0xA8, 0xAC, 0xA8, 0xE8, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4E, 0xCA, 0x4A, 0x4A, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x44, 0xCC, 0x44, 0x44, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4C, 0xC2, 0x44, 0x48, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4C, 0xC2, 0x4C, 0x42, 0xEC, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4A, 0xCA, 0x4E, 0x42, 0xE2, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4E, 0xC8, 0x4E, 0x42, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x46, 0xC8, 0x4E, 0x4A, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4E, 0xC2, 0x44, 0x44, 0xE4, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4E, 0xCA, 0x4E, 0x4A, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4E, 0xCA, 0x4E, 0x42, 0xE2, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x44, 0xCA, 0x4E, 0x4A, 0xEA, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4C, 0xCA, 0x4C, 0x4A, 0xEC, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x44, 0xCA, 0x48, 0x4A, 0xE4, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4C, 0xCA, 0x4A, 0x4A, 0xEC, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4E, 0xC8, 0x4C, 0x48, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4E, 0xC8, 0x4C, 0x48, 0xE8, 0x00, 0xFE,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x06, 0x06, 0x06, 0x06, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1B, 0x1B, 0x36, 0x36, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x1B, 0x1B, 0x1B, 0x7F, 0x7F, 0x36, 0x36,
+ 0xFF, 0xFF, 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x3E, 0x7F, 0x6B, 0x68, 0x78, 0x3C,
+ 0x1E, 0x16, 0xD6, 0xFE, 0x7C, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xCE, 0x1C,
+ 0x38, 0x70, 0xE6, 0xC6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x1C, 0x3E, 0x36, 0x36, 0x3C, 0x39, 0x7F,
+ 0x6E, 0xCC, 0xCC, 0xFE, 0x7A, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x06, 0x0C, 0x0C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x0E, 0x1C, 0x38, 0x30, 0x60, 0x60, 0xC0,
+ 0xC0, 0xC0, 0xC0, 0xC0, 0xE0, 0x70, 0x30, 0x00,
+ 0x18, 0x1C, 0x0E, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x0C, 0x0C, 0x18, 0x38, 0x70, 0xE0, 0xC0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x1C, 0x7F,
+ 0xFE, 0x38, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E,
+ 0xFC, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x38, 0x70, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xFC,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0E, 0x1C,
+ 0x38, 0x70, 0xE0, 0xC0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x1E, 0x3F, 0x33, 0x33, 0x67, 0x6E, 0x76,
+ 0xE6, 0xCC, 0xCC, 0xFC, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x06, 0x0E, 0x1E, 0x3C, 0x0C, 0x0C, 0x18,
+ 0x18, 0x18, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x1E, 0x3F, 0x33, 0x03, 0x07, 0x1E, 0x7C,
+ 0x60, 0xC0, 0xC0, 0xFC, 0xFC, 0x00, 0x00, 0x00,
+ 0x00, 0x1E, 0x3F, 0x33, 0x03, 0x06, 0x1E, 0x1E,
+ 0x06, 0x06, 0xCC, 0xFC, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1E, 0x36, 0x7F,
+ 0x7F, 0x0C, 0x0C, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x1F, 0x1F, 0x30, 0x30, 0x7C, 0x7E, 0x06,
+ 0x06, 0x06, 0xCE, 0xFC, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x0F, 0x1F, 0x38, 0x30, 0x60, 0x7C, 0x7E,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x3F, 0x7F, 0x63, 0x03, 0x06, 0x06, 0x0C,
+ 0x0C, 0x18, 0x18, 0x30, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x1E, 0x3F, 0x33, 0x33, 0x33, 0x1E, 0x7C,
+ 0x66, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x1E, 0x3F, 0x33, 0x63, 0x63, 0x7E, 0x3E,
+ 0x06, 0x0C, 0x1C, 0xF8, 0xF0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00,
+ 0x00, 0x00, 0x18, 0x38, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x0E, 0x1C, 0x38, 0x70, 0x70,
+ 0x70, 0x38, 0x1C, 0x0C, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x3E, 0x7C, 0x00, 0x00,
+ 0x7C, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x0C, 0x0C, 0x06, 0x0E,
+ 0x1C, 0x38, 0x70, 0x60, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x1E, 0x3F, 0x33, 0x33, 0x03, 0x07, 0x0E,
+ 0x1C, 0x18, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x1E, 0x3F, 0x33, 0x63, 0x6F, 0x6B, 0x6B,
+ 0xCF, 0xCE, 0xC0, 0xFC, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x1E, 0x3F, 0x33, 0x33, 0x66, 0x7E, 0x7E,
+ 0x66, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x3F, 0x33, 0x63, 0x63, 0x7E, 0x7C,
+ 0x66, 0xC6, 0xC6, 0xFE, 0xFC, 0x00, 0x00, 0x00,
+ 0x00, 0x1E, 0x3F, 0x33, 0x30, 0x60, 0x60, 0x60,
+ 0x60, 0xC0, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x3F, 0x33, 0x63, 0x63, 0x63, 0x63,
+ 0xC3, 0xC6, 0xCE, 0xFC, 0xF8, 0x00, 0x00, 0x00,
+ 0x00, 0x3F, 0x3F, 0x30, 0x30, 0x60, 0x7C, 0x7C,
+ 0x60, 0xC0, 0xC0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x3F, 0x3F, 0x30, 0x30, 0x60, 0x7C, 0x7C,
+ 0x60, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00,
+ 0x00, 0x1E, 0x3F, 0x33, 0x30, 0x60, 0x60, 0x6E,
+ 0x6E, 0xC6, 0xCE, 0xFC, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x33, 0x33, 0x33, 0x63, 0x66, 0x7E, 0x7E,
+ 0xC6, 0xC6, 0xCC, 0xCC, 0xCC, 0x00, 0x00, 0x00,
+ 0x00, 0x3F, 0x3F, 0x0C, 0x0C, 0x18, 0x18, 0x18,
+ 0x18, 0x30, 0x30, 0xFC, 0xFC, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x03, 0x03, 0x03, 0x06, 0x06, 0x06,
+ 0x06, 0x0C, 0xCC, 0xFC, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x30, 0x30, 0x33, 0x36, 0x7C, 0x78, 0x70,
+ 0x78, 0xF8, 0xDC, 0xCE, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x30, 0x30, 0x30, 0x30, 0x60, 0x60, 0x60,
+ 0x60, 0xC0, 0xC0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x33, 0x33, 0x3F, 0x3F, 0x7F, 0x6B, 0x66,
+ 0x66, 0xC6, 0xCC, 0xCC, 0xCC, 0x00, 0x00, 0x00,
+ 0x00, 0x33, 0x33, 0x3B, 0x3B, 0x7B, 0x7E, 0x6E,
+ 0x6E, 0xCE, 0xCC, 0xCC, 0xCC, 0x00, 0x00, 0x00,
+ 0x00, 0x1E, 0x3F, 0x33, 0x33, 0x63, 0x66, 0x66,
+ 0xC6, 0xCC, 0xCC, 0xFC, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x3F, 0x33, 0x33, 0x67, 0x7E, 0x7C,
+ 0x60, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00,
+ 0x00, 0x1E, 0x3F, 0x33, 0x33, 0x63, 0x66, 0x66,
+ 0xC6, 0xF6, 0xDC, 0xF8, 0x7C, 0x0C, 0x00, 0x00,
+ 0x00, 0x3E, 0x3F, 0x33, 0x33, 0x63, 0x7E, 0x7C,
+ 0x6E, 0xC6, 0xC6, 0xCC, 0xCC, 0x00, 0x00, 0x00,
+ 0x00, 0x1E, 0x3F, 0x33, 0x60, 0x60, 0x7C, 0x3E,
+ 0x06, 0x06, 0xCE, 0xFC, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x7F, 0x7F, 0x18, 0x18, 0x30, 0x30, 0x30,
+ 0x30, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00,
+ 0x00, 0x33, 0x33, 0x33, 0x33, 0x63, 0x66, 0x66,
+ 0x66, 0xC6, 0xCC, 0xFC, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x33, 0x33, 0x33, 0x33, 0x63, 0x63, 0x66,
+ 0x66, 0x6C, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x33, 0x33, 0x33, 0x33, 0x63, 0x63, 0x6B,
+ 0x6B, 0xFE, 0xFE, 0xEE, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x63, 0x63, 0x63, 0x77, 0x3E, 0x1C, 0x38,
+ 0x7C, 0xEE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x3C, 0x18,
+ 0x18, 0x30, 0x30, 0x60, 0x60, 0x00, 0x00, 0x00,
+ 0x00, 0x7F, 0x7F, 0x03, 0x07, 0x0E, 0x0C, 0x18,
+ 0x38, 0x70, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x0F, 0x0F, 0x0C, 0x0C, 0x18, 0x18, 0x18,
+ 0x18, 0x30, 0x30, 0x3E, 0x3E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xE0, 0x70,
+ 0x38, 0x1C, 0x0E, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x1F, 0x1F, 0x03, 0x03, 0x06, 0x06, 0x06,
+ 0x06, 0x0C, 0x0C, 0x7C, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x0E, 0x1F, 0x3B, 0x63, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0x00,
+ 0x00, 0x00, 0x00, 0x0C, 0x0E, 0x0C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x3E, 0x06,
+ 0x7E, 0xFC, 0xCC, 0xFC, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x18, 0x30, 0x30, 0x3E, 0x7F, 0x63,
+ 0x63, 0xC6, 0xC6, 0xFE, 0xFC, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x7E, 0x66,
+ 0xC0, 0xC0, 0xCC, 0xFC, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x03, 0x03, 0x06, 0x3E, 0x7E, 0x66,
+ 0xC6, 0xCC, 0xCC, 0xFC, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x7E, 0x66,
+ 0xFE, 0xFC, 0xC0, 0xFC, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x07, 0x0F, 0x0C, 0x18, 0x7E, 0x7E, 0x30,
+ 0x30, 0x30, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x7F, 0x63,
+ 0x63, 0xC6, 0xC6, 0xFE, 0x7E, 0x0C, 0xFC, 0xF8,
+ 0x00, 0x00, 0x30, 0x30, 0x30, 0x6E, 0x7F, 0x73,
+ 0x63, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x06, 0x00, 0x1C, 0x3C, 0x0C,
+ 0x0C, 0x18, 0x18, 0x7C, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x03, 0x03, 0x00, 0x0E, 0x1E, 0x06,
+ 0x06, 0x0C, 0x0C, 0x0C, 0x0C, 0x18, 0xF8, 0xF0,
+ 0x00, 0x30, 0x30, 0x30, 0x33, 0x67, 0x6E, 0x7C,
+ 0x7C, 0xDC, 0xCE, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x0E, 0x0E, 0x06, 0x06, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x7F, 0x7F,
+ 0x6B, 0xD6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6C, 0x7E, 0x76,
+ 0x66, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x7E, 0x66,
+ 0x66, 0xCC, 0xCC, 0xFC, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x3F, 0x33,
+ 0x63, 0x66, 0x66, 0xFE, 0xFC, 0xC0, 0xC0, 0xC0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x7F, 0x63,
+ 0x63, 0xC6, 0xC6, 0xFE, 0x7C, 0x0C, 0x0E, 0x0E,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x7F, 0x70,
+ 0x60, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x7F, 0x60,
+ 0x7C, 0x3E, 0x06, 0xFE, 0xFC, 0x00, 0x00, 0x00,
+ 0x00, 0x06, 0x06, 0x0C, 0x0C, 0x3F, 0x3F, 0x18,
+ 0x18, 0x30, 0x30, 0x3E, 0x1C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x33, 0x66,
+ 0x66, 0x66, 0xCC, 0xFE, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66,
+ 0xCC, 0xCC, 0xF8, 0x70, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x63,
+ 0x6B, 0xDE, 0xFE, 0xFE, 0x6C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x77,
+ 0x3E, 0x7C, 0xEE, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x63,
+ 0x63, 0xC6, 0xC6, 0xFE, 0x7E, 0x0C, 0xFC, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x07,
+ 0x1E, 0x78, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x07, 0x0E, 0x0C, 0x0C, 0x18, 0x70, 0x70,
+ 0x18, 0x18, 0x30, 0x38, 0x1C, 0x00, 0x00, 0x00,
+ 0x06, 0x06, 0x06, 0x06, 0x0C, 0x0C, 0x0C, 0x0C,
+ 0x18, 0x18, 0x18, 0x18, 0x30, 0x30, 0x30, 0x00,
+ 0x00, 0x38, 0x1C, 0x0C, 0x18, 0x18, 0x0E, 0x0E,
+ 0x18, 0x30, 0x30, 0x70, 0xE0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1D, 0x3F, 0x37, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEE, 0x28, 0x4C, 0x48, 0x48, 0x00, 0xFE,
+ 0x00, 0x06, 0x0F, 0x1B, 0x18, 0x7E, 0x30, 0x7C,
+ 0x60, 0x60, 0x6C, 0x3C, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x38, 0x6C, 0x54, 0xF6, 0xEE,
+ 0x6C, 0x7C, 0x28, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x10, 0x20, 0x00,
+ 0x00, 0x07, 0x0F, 0x0C, 0x18, 0x7E, 0x7E, 0x30,
+ 0x30, 0x30, 0x60, 0x60, 0x60, 0xC0, 0x80, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x66, 0x66, 0x44, 0x88, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xDB, 0xDB, 0x00, 0x00, 0x00,
+ 0x00, 0x06, 0x06, 0x06, 0x1F, 0x3F, 0x0C, 0x0C,
+ 0x0C, 0x18, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00,
+ 0x00, 0x06, 0x06, 0x06, 0x1F, 0x3F, 0x0C, 0x3F,
+ 0x3F, 0x18, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00,
+ 0x00, 0x3E, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x63, 0x06,
+ 0x0C, 0x30, 0x60, 0xDB, 0x9B, 0x00, 0x00, 0x00,
+ 0x63, 0x3E, 0x00, 0x1E, 0x3F, 0x33, 0x60, 0x7C,
+ 0x3E, 0x06, 0xCE, 0xFC, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0E,
+ 0x1C, 0x30, 0x38, 0x1C, 0x0C, 0x00, 0x00, 0x00,
+ 0x00, 0x1F, 0x3F, 0x33, 0x33, 0x66, 0x67, 0x67,
+ 0x66, 0xCC, 0xCC, 0xFF, 0x7F, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x38, 0x6C, 0x54, 0xF6, 0xEE,
+ 0x6C, 0x7C, 0x28, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x63, 0x3E, 0x00, 0x7F, 0x7F, 0x03, 0x07, 0x0E,
+ 0x18, 0x70, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x38, 0x6C, 0x54, 0xF6, 0xEE,
+ 0x6C, 0x7C, 0x28, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x38, 0x6C, 0x54, 0xF6, 0xEE,
+ 0x6C, 0x7C, 0x28, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x08, 0x10, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0C, 0x04, 0x08, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x09, 0x12, 0x1B, 0x36, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1B, 0x1B, 0x09, 0x12, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x3F,
+ 0x3F, 0x7E, 0x7E, 0x3C, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x19, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xFB, 0xFF, 0x55, 0xA2, 0xA2, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x63, 0x36, 0x1C, 0x00, 0x3F, 0x7F, 0x60,
+ 0x7C, 0x3E, 0x06, 0xFE, 0xFC, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x1C,
+ 0x0E, 0x0C, 0x1C, 0x38, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x7F, 0x6D,
+ 0x6F, 0xDE, 0xD8, 0xFE, 0x6E, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x38, 0x6C, 0x54, 0xF6, 0xEE,
+ 0x6C, 0x7C, 0x28, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x63, 0x36, 0x1C, 0x00, 0x7F, 0x7F, 0x07,
+ 0x1E, 0x78, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x33, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x3C,
+ 0x18, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00,
+ 0x0C, 0x18, 0x18, 0x18, 0x18, 0x30, 0x30, 0x00,
+ 0x00, 0x00, 0x00, 0x04, 0x04, 0x3E, 0x7F, 0x6B,
+ 0x68, 0xD0, 0xD6, 0xFE, 0x7C, 0x20, 0x20, 0x00,
+ 0x00, 0x0F, 0x1F, 0x19, 0x18, 0x30, 0x7C, 0x7C,
+ 0x30, 0x60, 0xE6, 0xFE, 0xDC, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x19, 0x33, 0x1E, 0x3F,
+ 0x33, 0x66, 0x7E, 0x3C, 0x66, 0xCC, 0x00, 0x00,
+ 0x00, 0x00, 0x19, 0x19, 0x19, 0x1E, 0x0C, 0x3F,
+ 0x0C, 0x7E, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x06, 0x0C, 0x0C, 0x00,
+ 0x00, 0x18, 0x18, 0x30, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x07, 0x0E, 0x18, 0x18, 0x3C, 0x7E, 0x66,
+ 0x7E, 0x3C, 0x18, 0x18, 0x70, 0xE0, 0x00, 0x00,
+ 0x00, 0x00, 0x33, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0E, 0x1F, 0x21, 0x21, 0x4D, 0x51, 0x51,
+ 0x91, 0xA1, 0x9A, 0x82, 0x7C, 0x38, 0x00, 0x00,
+ 0x00, 0x1C, 0x02, 0x1E, 0x22, 0x3C, 0x00, 0x7C,
+ 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B,
+ 0x36, 0xD8, 0xD8, 0x6C, 0x36, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1E, 0x3E, 0x06, 0x0C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0E, 0x1F, 0x21, 0x49, 0x55, 0x55, 0x59,
+ 0x99, 0xA5, 0xA5, 0x82, 0x7E, 0x78, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x1E, 0x3F, 0x33, 0x3F, 0x3C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x06, 0x3F, 0x3F, 0x0C,
+ 0x0C, 0x00, 0x7E, 0x7E, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3C, 0x3E, 0x06, 0x3C, 0x78, 0x60,
+ 0x7C, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3C, 0x3E, 0x06, 0x18, 0x0C, 0x7C,
+ 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x0C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x33,
+ 0x33, 0x66, 0x66, 0x7E, 0x7C, 0xC0, 0xC0, 0x80,
+ 0x00, 0x00, 0x00, 0x3F, 0x6F, 0xDB, 0xDB, 0x7B,
+ 0x1B, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C,
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x1C, 0x0C, 0x1C, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x38, 0x18, 0x30, 0x30, 0x78,
+ 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0C, 0x1E, 0x22, 0x3C, 0x18, 0x00, 0x3C,
+ 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6C,
+ 0x36, 0x36, 0x36, 0x6C, 0xD8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x30, 0x10, 0x23, 0x76, 0x0C,
+ 0x18, 0x64, 0xCC, 0x94, 0x3E, 0x08, 0x08, 0x00,
+ 0x00, 0x00, 0x10, 0x30, 0x10, 0x23, 0x76, 0x0C,
+ 0x18, 0x60, 0xDC, 0x82, 0x1C, 0x20, 0x3C, 0x00,
+ 0x00, 0x00, 0x38, 0x04, 0x18, 0x0B, 0x76, 0x0C,
+ 0x18, 0x64, 0xCC, 0x96, 0x3E, 0x08, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x06, 0x00, 0x0C, 0x0C,
+ 0x3C, 0x70, 0xC0, 0xC6, 0xC6, 0xFC, 0x78, 0x00,
+ 0x00, 0x18, 0x0C, 0x00, 0x04, 0x1C, 0x36, 0x63,
+ 0x7F, 0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x06, 0x00, 0x04, 0x1C, 0x36, 0x63,
+ 0x7F, 0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x0E, 0x1B, 0x11, 0x04, 0x1C, 0x36, 0x63,
+ 0x7F, 0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x1D, 0x37, 0x00, 0x04, 0x1C, 0x36, 0x63,
+ 0x7F, 0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x1B, 0x1B, 0x04, 0x0E, 0x36, 0x63, 0x7F,
+ 0x7F, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x0E, 0x11, 0x0E, 0x0E, 0x36, 0x63, 0x7F,
+ 0x7F, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x07, 0x0F, 0x1F, 0x3B, 0x66, 0x7F, 0x7F,
+ 0x66, 0xCC, 0xCC, 0xCF, 0xCF, 0x00, 0x00, 0x00,
+ 0x00, 0x1E, 0x3F, 0x33, 0x30, 0x60, 0x60, 0x60,
+ 0x60, 0xC0, 0xC6, 0xFE, 0x7C, 0x1C, 0xCC, 0x78,
+ 0x00, 0x0C, 0x06, 0x00, 0x3F, 0x7F, 0x60, 0x78,
+ 0x78, 0xC0, 0xC0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x06, 0x00, 0x3F, 0x7F, 0x60, 0x78,
+ 0x78, 0xC0, 0xC0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x0E, 0x1B, 0x00, 0x3F, 0x7F, 0x60, 0x78,
+ 0x78, 0xC0, 0xC0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x1B, 0x1B, 0x00, 0x3F, 0x3F, 0x60, 0x60, 0x78,
+ 0x78, 0xC0, 0xC0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x0C, 0x06, 0x00, 0x1F, 0x1F, 0x0C, 0x0C,
+ 0x0C, 0x18, 0x18, 0x7C, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x06, 0x00, 0x1F, 0x1F, 0x0C, 0x0C,
+ 0x0C, 0x18, 0x18, 0x7C, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x06, 0x09, 0x1F, 0x1F, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x18, 0x18, 0x7C, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x1B, 0x1B, 0x00, 0x1F, 0x1F, 0x0C, 0x0C,
+ 0x0C, 0x18, 0x18, 0x7C, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1E, 0x1F, 0x1B, 0x33, 0x33, 0x7B,
+ 0x33, 0x66, 0x6E, 0x7C, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x1D, 0x37, 0x00, 0x31, 0x63, 0x73, 0x7B,
+ 0x7F, 0xDE, 0xCE, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x0C, 0x06, 0x00, 0x3E, 0x7F, 0x63, 0x63,
+ 0x63, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x06, 0x00, 0x3E, 0x7F, 0x63, 0x63,
+ 0x63, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x0E, 0x1B, 0x00, 0x3E, 0x7F, 0x63, 0x63,
+ 0x63, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x1D, 0x37, 0x00, 0x3E, 0x7F, 0x63, 0x63,
+ 0x63, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x1B, 0x1B, 0x00, 0x3E, 0x7F, 0x63, 0x63,
+ 0x63, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x77, 0x3E,
+ 0x1C, 0x38, 0x7C, 0xEE, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x1D, 0x3E, 0x37, 0x6B, 0x6B, 0x6B, 0xD6,
+ 0xD6, 0xD6, 0xEE, 0x7C, 0xB8, 0x00, 0x00, 0x00,
+ 0x00, 0x0C, 0x06, 0x00, 0x33, 0x33, 0x63, 0x66,
+ 0x66, 0xC6, 0xCE, 0xFC, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x06, 0x00, 0x33, 0x33, 0x63, 0x66,
+ 0x66, 0xC6, 0xCE, 0xFC, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x0E, 0x1B, 0x00, 0x33, 0x33, 0x63, 0x66,
+ 0x66, 0xC6, 0xCE, 0xFC, 0x78, 0x00, 0x00, 0x00,
+ 0x1B, 0x1B, 0x00, 0x33, 0x33, 0x63, 0x63, 0x63,
+ 0x66, 0xC6, 0xCE, 0xFC, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x06, 0x0C, 0x00, 0x33, 0x33, 0x66, 0x6E,
+ 0x7C, 0x38, 0x30, 0x60, 0x60, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x1F, 0x3F, 0x33, 0x33,
+ 0x3F, 0x7C, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00,
+ 0x00, 0x0E, 0x1F, 0x1B, 0x1B, 0x33, 0x3F, 0x3E,
+ 0x33, 0x66, 0x66, 0x7E, 0x7C, 0xC0, 0x80, 0x00,
+ 0x00, 0x00, 0x0C, 0x06, 0x00, 0x3F, 0x7F, 0x63,
+ 0x63, 0xCE, 0xCE, 0xFE, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x0C, 0x00, 0x3F, 0x7F, 0x63,
+ 0x63, 0xC6, 0xCE, 0xFE, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x0E, 0x1B, 0x00, 0x3F, 0x7F, 0x63,
+ 0x63, 0xC6, 0xCE, 0xFE, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1D, 0x37, 0x00, 0x3F, 0x7F, 0x63,
+ 0x63, 0xC6, 0xCE, 0xFE, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1B, 0x1B, 0x00, 0x3F, 0x7F, 0x63,
+ 0x63, 0xC6, 0xCE, 0xFE, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x0E, 0x1B, 0x0E, 0x00, 0x3E, 0x7F, 0x63,
+ 0x63, 0xC6, 0xCE, 0xFE, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x3F, 0x0D,
+ 0x1F, 0x7E, 0xD8, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x7F,
+ 0x63, 0xC0, 0xC6, 0xFE, 0x7C, 0x30, 0xF8, 0xF0,
+ 0x00, 0x00, 0x0C, 0x06, 0x00, 0x3E, 0x7F, 0x63,
+ 0x7F, 0xFE, 0xC0, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x0C, 0x00, 0x3E, 0x7F, 0x63,
+ 0x7F, 0xFE, 0xC0, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x0E, 0x1B, 0x00, 0x3E, 0x7F, 0x63,
+ 0x7F, 0xFE, 0xC0, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1B, 0x1B, 0x00, 0x3E, 0x7F, 0x63,
+ 0x7F, 0xFE, 0xC0, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0C, 0x06, 0x00, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x03, 0x06, 0x00, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x06, 0x0F, 0x19, 0x00, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1B, 0x1B, 0x00, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x1B, 0x1F, 0x3E, 0x37, 0x3E, 0x7F, 0x63,
+ 0x63, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x1D, 0x3F, 0x37, 0x00, 0x7E, 0x7F, 0x63,
+ 0x63, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x0C, 0x00, 0x3E, 0x7F, 0x63,
+ 0x63, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x0C, 0x00, 0x3E, 0x7F, 0x63,
+ 0x63, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x0E, 0x1B, 0x00, 0x3E, 0x7F, 0x63,
+ 0x63, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x1D, 0x3F, 0x37, 0x00, 0x3E, 0x7F, 0x63,
+ 0x63, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1B, 0x1B, 0x00, 0x3E, 0x7F, 0x63,
+ 0x63, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x3F,
+ 0x7E, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x7F, 0x67,
+ 0x6B, 0xD6, 0xE6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x0C, 0x00, 0x63, 0x63, 0x63,
+ 0x63, 0xC6, 0xC6, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x0C, 0x00, 0x63, 0x63, 0x63,
+ 0x63, 0xC6, 0xC6, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x0E, 0x1B, 0x00, 0x63, 0x63, 0x63,
+ 0x63, 0xC6, 0xC6, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1B, 0x1B, 0x00, 0x63, 0x63, 0x63,
+ 0x63, 0xC6, 0xC6, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x03, 0x06, 0x00, 0x63, 0x63, 0x63,
+ 0x63, 0xC6, 0xC6, 0xFE, 0x7E, 0x0C, 0xF8, 0xF0,
+ 0x00, 0x00, 0x18, 0x18, 0x18, 0x3E, 0x3F, 0x33,
+ 0x33, 0x66, 0x66, 0x7E, 0x7C, 0xC0, 0xC0, 0xC0,
+ 0x00, 0x00, 0x00, 0x1B, 0x00, 0x63, 0x63, 0x63,
+ 0x63, 0xC6, 0xC6, 0xFE, 0x7E, 0x0C, 0xF8, 0xF0,
+};
+
+const struct fb_font_desc font_italic = {
+ .name = "NetSurf Italic",
+ .width = 8,
+ .height = 16,
+ .encoding = "CP1252",
+ .data = fontdata_italic,
+};
+
+
+static const uint32_t fontdata_italic_bold[FONTDATAMAX] = {
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xE4, 0xAC, 0xA4, 0xA4, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEC, 0xA2, 0xA4, 0xA8, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEC, 0xA2, 0xAC, 0xA2, 0xEC, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEA, 0xAA, 0xAE, 0xA2, 0xE2, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEE, 0xA8, 0xAE, 0xA2, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xE6, 0xA8, 0xAE, 0xAA, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEE, 0xA2, 0xA4, 0xA4, 0xE4, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEE, 0xAA, 0xAE, 0xAA, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEE, 0xAA, 0xAE, 0xA2, 0xE2, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xE4, 0xAA, 0xAE, 0xAA, 0xEA, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEC, 0xAA, 0xAC, 0xAA, 0xEC, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xE4, 0xAA, 0xA8, 0xAA, 0xE4, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEC, 0xAA, 0xAA, 0xAA, 0xEC, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEE, 0xA8, 0xAC, 0xA8, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEE, 0xA8, 0xAC, 0xA8, 0xE8, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4E, 0xCA, 0x4A, 0x4A, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x44, 0xCC, 0x44, 0x44, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4C, 0xC2, 0x44, 0x48, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4C, 0xC2, 0x4C, 0x42, 0xEC, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4A, 0xCA, 0x4E, 0x42, 0xE2, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4E, 0xC8, 0x4E, 0x42, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x46, 0xC8, 0x4E, 0x4A, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4E, 0xC2, 0x44, 0x44, 0xE4, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4E, 0xCA, 0x4E, 0x4A, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4E, 0xCA, 0x4E, 0x42, 0xE2, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x44, 0xCA, 0x4E, 0x4A, 0xEA, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4C, 0xCA, 0x4C, 0x4A, 0xEC, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x44, 0xCA, 0x48, 0x4A, 0xE4, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4C, 0xCA, 0x4A, 0x4A, 0xEC, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4E, 0xC8, 0x4C, 0x48, 0xEE, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0x4E, 0xC8, 0x4C, 0x48, 0xE8, 0x00, 0xFE,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0E, 0x0E, 0x0E, 0x0E, 0x1C, 0x1C, 0x1C,
+ 0x1C, 0x00, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x77, 0x77, 0x77, 0x77, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x36, 0x36, 0x7F, 0x7F, 0x7F, 0x36, 0x6C,
+ 0xFE, 0xFE, 0xFE, 0x6C, 0x6C, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x3E, 0x7F, 0x7B, 0x78, 0x7C, 0x7C,
+ 0x3E, 0x1E, 0xDE, 0xFE, 0x7C, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x70, 0x73, 0x77, 0x0E,
+ 0x3C, 0x70, 0xEE, 0xCE, 0x0E, 0x00, 0x00, 0x00,
+ 0x00, 0x1C, 0x3E, 0x7E, 0x7C, 0x7C, 0x3B, 0xFE,
+ 0xFE, 0xEC, 0xEE, 0xFE, 0x74, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1C, 0x1C, 0x1C, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x06, 0x0E, 0x1C, 0x1C, 0x38, 0x38, 0x70,
+ 0x70, 0x70, 0x38, 0x38, 0x1C, 0x0C, 0x06, 0x00,
+ 0x60, 0x30, 0x38, 0x1C, 0x1C, 0x0E, 0x0E, 0x0E,
+ 0x1C, 0x1C, 0x38, 0x38, 0x70, 0x60, 0xC0, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x2A, 0x7F, 0x3E, 0x18,
+ 0x7C, 0xFE, 0x54, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1C, 0x7E,
+ 0x7E, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x38, 0x78, 0x70, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE,
+ 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0F, 0x3C,
+ 0x78, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x1C, 0x3E, 0x77, 0x77, 0x77, 0x77, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0x7C, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x0C, 0x1C, 0x3C, 0x3C, 0x1C, 0x1C, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x7F, 0x77, 0x07, 0x07, 0x3E, 0x7C,
+ 0xE0, 0xE0, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x7F, 0x77, 0x07, 0x07, 0x1E, 0x3C,
+ 0x0E, 0x0E, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x06, 0x0E, 0x1E, 0x3E, 0x76, 0xEC,
+ 0xFE, 0xFE, 0x1C, 0x1C, 0x1C, 0x00, 0x00, 0x00,
+ 0x00, 0x7F, 0x7F, 0x70, 0x70, 0x7C, 0x7E, 0x0E,
+ 0x0E, 0x0E, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x7E, 0x70, 0x70, 0x7C, 0x7E, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x7F, 0x7F, 0x07, 0x07, 0x0E, 0x0E, 0x1C,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x7F, 0x77, 0x77, 0x7F, 0x3E, 0x7E,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x7F, 0x77, 0x77, 0x77, 0x7F, 0x3E,
+ 0x0E, 0x0E, 0x0E, 0x7C, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1C, 0x00,
+ 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1C, 0x00,
+ 0x00, 0x18, 0x38, 0x38, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x07, 0x0E, 0x1C, 0x70,
+ 0x70, 0x38, 0x1C, 0x0E, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x00,
+ 0x00, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x30, 0x38, 0x1C, 0x0E, 0x0E,
+ 0x0E, 0x1C, 0x38, 0x70, 0x60, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x7F, 0x77, 0x07, 0x07, 0x1E, 0x7C,
+ 0x70, 0x00, 0x70, 0x70, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x7F, 0x77, 0x77, 0x77, 0x7F, 0xFE,
+ 0xEC, 0xE0, 0xE0, 0xFC, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x7F, 0x77, 0x77, 0x77, 0x76, 0xFE,
+ 0xFE, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x7E, 0x7F, 0x77, 0x77, 0x77, 0x7E, 0xFC,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0xFC, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x7F, 0x77, 0x77, 0x70, 0x70, 0xE0,
+ 0xE0, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0x7E, 0x77, 0x77, 0x77, 0x76, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFC, 0xF8, 0x00, 0x00, 0x00,
+ 0x00, 0x7F, 0x7F, 0x70, 0x70, 0x70, 0x7C, 0xF8,
+ 0xE0, 0xE0, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x7F, 0x7F, 0x70, 0x70, 0x70, 0x7C, 0xF8,
+ 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x7F, 0x77, 0x77, 0x70, 0x70, 0xFE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x77, 0x77, 0x77, 0x77, 0x77, 0x7E, 0xFE,
+ 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x7F, 0x7F, 0x1C, 0x1C, 0x1C, 0x1C, 0x38,
+ 0x38, 0x38, 0x38, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x0E,
+ 0x0E, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x70, 0x70, 0x72, 0x77, 0x7F, 0x7E, 0xF8,
+ 0xF8, 0xFC, 0xFE, 0xEE, 0xE6, 0x00, 0x00, 0x00,
+ 0x00, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0xE0,
+ 0xE0, 0xE0, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x63, 0x77, 0x7F, 0x7F, 0x7F, 0x7E, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x67, 0x77, 0x77, 0x7F, 0x7F, 0x7E, 0xFE,
+ 0xFE, 0xFE, 0xEE, 0xEE, 0xE6, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x7F, 0x77, 0x77, 0x77, 0x76, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x7E, 0x7F, 0x77, 0x77, 0x77, 0x77, 0xFE,
+ 0xFC, 0xE0, 0xE0, 0xE0, 0xE0, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x7F, 0x77, 0x77, 0x77, 0x76, 0xEE,
+ 0xEE, 0xFE, 0xFE, 0xFE, 0x7E, 0x06, 0x00, 0x00,
+ 0x00, 0x7E, 0x7F, 0x77, 0x77, 0x77, 0x7E, 0xFC,
+ 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x7F, 0x77, 0x70, 0x70, 0x7C, 0x3E,
+ 0x0E, 0x0E, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x7F, 0x7F, 0x1C, 0x1C, 0x1C, 0x1C, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x77, 0x77, 0x77, 0x77, 0x77, 0x76, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x77, 0x77, 0x77, 0x77, 0x77, 0x76, 0xEE,
+ 0x6C, 0x7C, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x77, 0x77, 0x77, 0x77, 0x77, 0x76, 0xEE,
+ 0xFE, 0xFE, 0xFE, 0xEE, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x77, 0x77, 0x77, 0x77, 0x3E, 0x1C, 0x38,
+ 0x7C, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x77, 0x77, 0x77, 0x77, 0x77, 0x3E, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x7F, 0x7F, 0x07, 0x07, 0x0E, 0x1C, 0x38,
+ 0x70, 0xE0, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x1F, 0x1F, 0x1C, 0x1C, 0x1C, 0x1C, 0x38,
+ 0x38, 0x38, 0x38, 0x3E, 0x3E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x20, 0x70, 0x78, 0x38,
+ 0x1C, 0x1E, 0x0E, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x7E, 0x7E, 0x0E, 0x0E, 0x0E, 0x0E, 0x1C,
+ 0x1C, 0x1C, 0x1C, 0xFC, 0xFC, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x1C, 0x3E, 0x77, 0x63, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x1C, 0x0C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x3E, 0x0E,
+ 0x3E, 0x7E, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x38, 0x38, 0x70, 0x7C, 0x7E, 0x6E,
+ 0xEE, 0xEE, 0xEE, 0xFC, 0xF8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x7E, 0xEE,
+ 0xE0, 0xE0, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x07, 0x07, 0x07, 0x07, 0x3F, 0x7E, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x7E, 0xEE,
+ 0xFE, 0xFC, 0xE0, 0xFC, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x1F, 0x3F, 0x38, 0x38, 0x70, 0x7C, 0x7C,
+ 0x70, 0xE0, 0xE0, 0xE0, 0xE0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x7E, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x1C, 0xFC, 0xF8,
+ 0x00, 0x00, 0x70, 0x70, 0x70, 0x7C, 0x7E, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0E, 0x0E, 0x00, 0x1E, 0x3E, 0x1C,
+ 0x1C, 0x38, 0x38, 0x7C, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0E, 0x0E, 0x00, 0x1E, 0x3E, 0x0E,
+ 0x1C, 0x1C, 0x1C, 0x1C, 0x38, 0x38, 0xF8, 0xF0,
+ 0x00, 0x38, 0x38, 0x38, 0x3A, 0x7F, 0x7E, 0x7C,
+ 0x70, 0xF8, 0xFC, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x3C, 0x3C, 0x1C, 0x1C, 0x38, 0x38, 0x38,
+ 0x38, 0x70, 0x70, 0x70, 0x70, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x7E, 0xFE,
+ 0xFE, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x7E, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x7E, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFC, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x7E, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFC, 0xF8, 0xE0, 0xE0, 0xE0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x7E, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFC, 0x7C, 0x1C, 0x1E, 0x1E,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x7E, 0x70,
+ 0x70, 0xE0, 0xE0, 0xE0, 0xE0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x3E, 0x70,
+ 0x7C, 0x3E, 0x0E, 0x7C, 0xF8, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x38, 0x38, 0x70, 0x7C, 0x7C, 0x70,
+ 0xE0, 0xE0, 0xE0, 0xFC, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x76, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x76, 0xEE,
+ 0xEE, 0xEE, 0x7C, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x76, 0xEE,
+ 0xFE, 0xFE, 0xFE, 0xEE, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x76, 0x7E,
+ 0x38, 0x7C, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x76, 0xEE,
+ 0xEE, 0xEE, 0xFE, 0x7E, 0x1C, 0x1C, 0xFC, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x7E, 0x1E,
+ 0x3C, 0x78, 0xF0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x0F, 0x1F, 0x1C, 0x1C, 0x1C, 0x78, 0xF0,
+ 0x38, 0x38, 0x38, 0x3E, 0x1E, 0x00, 0x00, 0x00,
+ 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x00,
+ 0x00, 0x78, 0x7C, 0x1C, 0x1C, 0x1C, 0x0F, 0x1E,
+ 0x38, 0x38, 0x38, 0xF8, 0xF0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x66, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x7F, 0x00, 0x77, 0x55, 0x55, 0x55, 0x77,
+ 0x00, 0xEE, 0x28, 0x4C, 0x48, 0x48, 0x00, 0xFE,
+ 0x00, 0x06, 0x1F, 0x3B, 0x70, 0xFE, 0x70, 0xF8,
+ 0xE0, 0xE0, 0xEE, 0xFC, 0x70, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x38, 0x6C, 0x54, 0xF6, 0xEE,
+ 0x6C, 0x7C, 0x28, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x38, 0x38, 0x30, 0x60, 0x00,
+ 0x00, 0x1F, 0x3F, 0x38, 0x38, 0x70, 0x7C, 0x7C,
+ 0x70, 0xE0, 0xE0, 0xE0, 0xE0, 0xC0, 0xC0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xEE, 0xEE, 0xCC, 0x98, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0x00,
+ 0x00, 0x1C, 0x1C, 0x1C, 0x1C, 0x7F, 0x7F, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00,
+ 0x00, 0x1C, 0x1C, 0x1C, 0x7F, 0x7F, 0x1C, 0xFE,
+ 0xFE, 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00,
+ 0x00, 0x3E, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x67, 0x1C,
+ 0x38, 0x70, 0xE0, 0xDB, 0x1B, 0x00, 0x00, 0x00,
+ 0x77, 0x3E, 0x00, 0x3E, 0x7F, 0x77, 0x70, 0x7C,
+ 0x3E, 0x0E, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x1C,
+ 0x38, 0x70, 0x38, 0x1C, 0x0E, 0x06, 0x00, 0x00,
+ 0x00, 0x3F, 0x7F, 0x77, 0x77, 0x77, 0x77, 0xEF,
+ 0xEE, 0xEE, 0xEE, 0xFF, 0x7F, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x38, 0x6C, 0x54, 0xF6, 0xEE,
+ 0x6C, 0x7C, 0x28, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x77, 0x3E, 0x00, 0x7F, 0x7F, 0x07, 0x0E, 0x1C,
+ 0x38, 0x70, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x38, 0x6C, 0x54, 0xF6, 0xEE,
+ 0x6C, 0x7C, 0x28, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x38, 0x6C, 0x54, 0xF6, 0xEE,
+ 0x6C, 0x7C, 0x28, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0C, 0x18, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1B, 0x36, 0x36, 0x36, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1B, 0x1B, 0x1B, 0x36, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x7E,
+ 0x7E, 0x7E, 0x7E, 0x3C, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3C, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x3B, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x79, 0x7F, 0x2F, 0x29, 0x29, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x77, 0x3E, 0x1C, 0x00, 0x1F, 0x3E, 0x70,
+ 0x7C, 0x3E, 0x0E, 0x7C, 0xF8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x38, 0x38,
+ 0x1C, 0x0E, 0x1C, 0x38, 0x70, 0x60, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x7F, 0xFA,
+ 0xDE, 0xDE, 0xFC, 0xFE, 0x6E, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x38, 0x6C, 0x54, 0xF6, 0xEE,
+ 0x6C, 0x7C, 0x28, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x77, 0x3E, 0x1C, 0x00, 0x7F, 0x7E, 0x1E,
+ 0x3C, 0x78, 0xF0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x3B, 0x00, 0x77, 0x77, 0x77, 0x77, 0x3E,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1C, 0x00,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x08, 0x3E, 0x7F, 0xD6,
+ 0xD0, 0xD0, 0xD6, 0xFE, 0x7C, 0x10, 0x10, 0x00,
+ 0x00, 0x1E, 0x3F, 0x3B, 0x38, 0x7E, 0x7E, 0xFC,
+ 0x70, 0x76, 0xFE, 0xFE, 0xDC, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x77, 0x77, 0x3E, 0xFE,
+ 0xFE, 0xFE, 0xFE, 0x7C, 0xEE, 0xEE, 0x00, 0x00,
+ 0x00, 0x00, 0x77, 0x77, 0x77, 0x3E, 0x1C, 0xFE,
+ 0x38, 0xFE, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x1C, 0x1C, 0x1C, 0x00,
+ 0x00, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x1E, 0x3E, 0x3E, 0x38, 0x1E, 0x3F, 0x66,
+ 0x7E, 0x3C, 0x0E, 0x3E, 0x3E, 0x3C, 0x00, 0x00,
+ 0x00, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x1E, 0x3F, 0x61, 0x4D, 0x5D, 0x51, 0xA1,
+ 0xA5, 0xBD, 0x99, 0xC3, 0x7E, 0x3C, 0x00, 0x00,
+ 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, 0xFC,
+ 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E,
+ 0x7C, 0xF8, 0xF8, 0x7C, 0x3E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3F, 0x3F, 0x3F, 0x03, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7E, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x1E, 0x3F, 0x61, 0x59, 0x5D, 0x55, 0xB9,
+ 0xB9, 0xA5, 0xA5, 0xC3, 0x7E, 0x3C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x3C, 0x7E, 0x66, 0x7E, 0x3C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1C, 0x1C, 0x7F, 0x7F, 0x7F, 0x38,
+ 0x38, 0x00, 0xFE, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x7C, 0x0C, 0x3C, 0x78, 0xC0,
+ 0xF8, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x7C, 0x0C, 0x18, 0x0C, 0xF8,
+ 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0xFC, 0xE0, 0xE0, 0xC0,
+ 0x00, 0x00, 0x00, 0x3F, 0x6D, 0x6D, 0x6D, 0x7A,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x38,
+ 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x1C, 0x0C, 0x1C, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x70, 0x30, 0x30, 0x30, 0xF0,
+ 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x7C, 0x44, 0x7C, 0x38, 0x00, 0xF8,
+ 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8,
+ 0x6C, 0x36, 0x36, 0x6C, 0xD8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x70, 0x31, 0x33, 0x7F, 0x1C,
+ 0x38, 0x76, 0xEE, 0xD6, 0x3E, 0x3E, 0x06, 0x00,
+ 0x00, 0x00, 0x30, 0x70, 0x31, 0x33, 0x7F, 0x1C,
+ 0x38, 0x70, 0xFC, 0xC6, 0x0C, 0x18, 0x1E, 0x00,
+ 0x00, 0x00, 0x70, 0x18, 0x31, 0x1B, 0x77, 0x1C,
+ 0x3A, 0x76, 0xEE, 0xD6, 0x3E, 0x3E, 0x06, 0x00,
+ 0x00, 0x00, 0x0E, 0x0E, 0x0E, 0x00, 0x0E, 0x1C,
+ 0x7C, 0xF8, 0xE0, 0xEE, 0xEE, 0xFE, 0x7C, 0x00,
+ 0x00, 0x70, 0x38, 0x00, 0x08, 0x1C, 0x3E, 0xEE,
+ 0xFE, 0xFE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x07, 0x0E, 0x00, 0x08, 0x1C, 0x3E, 0xEE,
+ 0xFE, 0xFE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x1C, 0x36, 0x63, 0x08, 0x1C, 0x3E, 0xEE,
+ 0xFE, 0xFE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x3B, 0x6E, 0x00, 0x08, 0x1C, 0x3E, 0xEE,
+ 0xFE, 0xFE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x77, 0x77, 0x08, 0x1C, 0x3E, 0x76, 0xFE,
+ 0xFE, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x1C, 0x36, 0x1C, 0x1C, 0x3E, 0x76, 0xFE,
+ 0xFE, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x0F, 0x1F, 0x3F, 0x7F, 0x77, 0x7F, 0xFF,
+ 0xFE, 0xEE, 0xEF, 0xEF, 0xEF, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x7F, 0x77, 0x70, 0x70, 0x70, 0xE0,
+ 0xE0, 0xE0, 0xEE, 0xFE, 0x7C, 0x0E, 0x6E, 0x7C,
+ 0x00, 0x38, 0x1C, 0x00, 0x7F, 0x7F, 0x70, 0xE0,
+ 0xF8, 0xE0, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x0E, 0x1C, 0x00, 0x7F, 0x7F, 0x70, 0xE0,
+ 0xF8, 0xE0, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x77, 0x00, 0x7F, 0x7F, 0x70, 0xE0,
+ 0xF8, 0xE0, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x77, 0x77, 0x00, 0x7F, 0x7F, 0x70, 0x70, 0xF8,
+ 0xF8, 0xE0, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x1C, 0x00, 0x1C, 0x1C, 0x1C, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x0E, 0x1C, 0x00, 0x1C, 0x1C, 0x1C, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x1C, 0x36, 0x1C, 0x1C, 0x1C, 0x1C, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x77, 0x77, 0x00, 0x1C, 0x1C, 0x1C, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7C, 0x7E, 0x77, 0x77, 0x76, 0xFE,
+ 0xEE, 0xEE, 0xEE, 0xFC, 0xF8, 0x00, 0x00, 0x00,
+ 0x00, 0x3B, 0x6E, 0x00, 0x77, 0x77, 0x76, 0xF6,
+ 0xFE, 0xDE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x1C, 0x00, 0x3E, 0x7F, 0x76, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x0E, 0x1C, 0x00, 0x3E, 0x7F, 0x76, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x77, 0x00, 0x3E, 0x7F, 0x76, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x3B, 0x6E, 0x00, 0x3E, 0x7F, 0x76, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x77, 0x77, 0x00, 0x3E, 0x7F, 0x77, 0x76, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x76, 0x7C,
+ 0x38, 0x38, 0x7C, 0xEE, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x3D, 0x7E, 0x77, 0x77, 0x77, 0x7E, 0xFE,
+ 0xFE, 0xEE, 0xEE, 0xFE, 0x7C, 0x80, 0x00, 0x00,
+ 0x00, 0x38, 0x1C, 0x00, 0x77, 0x77, 0x76, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x0E, 0x1C, 0x00, 0x77, 0x77, 0x76, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x77, 0x00, 0x77, 0x77, 0x76, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x77, 0x77, 0x00, 0x77, 0x77, 0x77, 0x76, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x0E, 0x1C, 0x00, 0x77, 0x77, 0x76, 0xEE,
+ 0x7C, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x70, 0x70, 0x7E, 0x7F, 0x76, 0xEE,
+ 0xEE, 0xFE, 0xFC, 0xE0, 0xE0, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x7F, 0x77, 0x77, 0x77, 0x7E, 0xFC,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0xFC, 0xE0, 0xC0, 0x00,
+ 0x00, 0x00, 0x70, 0x38, 0x00, 0x3C, 0x3E, 0x0E,
+ 0x3E, 0x7E, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1C, 0x38, 0x00, 0x3C, 0x3E, 0x0E,
+ 0x3E, 0x7E, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x7C, 0xEE, 0x00, 0x3C, 0x3E, 0x0E,
+ 0x3E, 0x7E, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x76, 0xDC, 0x00, 0x3C, 0x3E, 0x0E,
+ 0x3E, 0x7E, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xEE, 0xEE, 0x00, 0x3C, 0x3E, 0x0E,
+ 0x3E, 0x7E, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6C, 0x38, 0x00, 0x3C, 0x3E, 0x0E,
+ 0x3E, 0x7E, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x3A, 0x3F, 0x3F, 0x1A,
+ 0x7E, 0xFE, 0xD8, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0xFE,
+ 0xEE, 0xE0, 0xEE, 0xFE, 0x7C, 0x1C, 0x7E, 0x7C,
+ 0x00, 0x00, 0x38, 0x1C, 0x00, 0x3E, 0x7F, 0xEE,
+ 0xFE, 0xFE, 0xE0, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1C, 0x38, 0x00, 0x3E, 0x7F, 0xEE,
+ 0xFE, 0xFE, 0xE0, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x1C, 0x3E, 0x77, 0x00, 0x3E, 0x7F, 0xEE,
+ 0xFE, 0xFE, 0xE0, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x77, 0x77, 0x00, 0x3E, 0x7F, 0xEE,
+ 0xFE, 0xFE, 0xE0, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x1C, 0x00, 0x1C, 0x1C, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0E, 0x1C, 0x00, 0x1C, 0x1C, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x1C, 0x3E, 0x77, 0x00, 0x1C, 0x1C, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x77, 0x77, 0x00, 0x1C, 0x1C, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x76, 0x3E, 0x7C, 0x7E, 0x3F, 0x7E, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x3B, 0x7F, 0x6E, 0x00, 0x7E, 0x7F, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x70, 0x38, 0x00, 0x3C, 0x7E, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFC, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1C, 0x38, 0x00, 0x3C, 0x7E, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFC, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x1C, 0x3E, 0x77, 0x00, 0x3C, 0x7E, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFC, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x3B, 0x7F, 0x6E, 0x00, 0x3C, 0x7E, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFC, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x77, 0x77, 0x00, 0x3C, 0x7E, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFC, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0xFE,
+ 0xFE, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x3E, 0x7E, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7C, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x70, 0x38, 0x00, 0x77, 0x76, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1C, 0x38, 0x00, 0x77, 0x76, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x1C, 0x3E, 0x77, 0x00, 0x77, 0x76, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x77, 0x77, 0x00, 0x77, 0x76, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0E, 0x1C, 0x00, 0x77, 0x76, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7E, 0x0E, 0xFE, 0xFC,
+ 0x00, 0x00, 0x70, 0x70, 0x7E, 0x7F, 0x76, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xEE, 0xFE, 0xFC, 0xE0, 0xE0,
+ 0x00, 0x00, 0x77, 0x77, 0x00, 0x77, 0x76, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xFE, 0x7E, 0x0E, 0xFE, 0xFC,
+};
+
+const struct fb_font_desc font_italic_bold = {
+ .name = "NetSurf Italic Bold",
+ .width = 8,
+ .height = 16,
+ .encoding = "CP1252",
+ .data = fontdata_italic_bold,
+};
+
+
+static const uint32_t fontdata_regular[FONTDATAMAX] = {
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xE4, 0xAC, 0xA4, 0xA4, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEC, 0xA2, 0xA4, 0xA8, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEC, 0xA2, 0xAC, 0xA2, 0xEC, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEA, 0xAA, 0xAE, 0xA2, 0xE2, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEE, 0xA8, 0xAE, 0xA2, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xE6, 0xA8, 0xAE, 0xAA, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEE, 0xA2, 0xA4, 0xA4, 0xE4, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEE, 0xAA, 0xAE, 0xAA, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEE, 0xAA, 0xAE, 0xA2, 0xE2, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xE4, 0xAA, 0xAE, 0xAA, 0xEA, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEC, 0xAA, 0xAC, 0xAA, 0xEC, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xE4, 0xAA, 0xA8, 0xAA, 0xE4, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEC, 0xAA, 0xAA, 0xAA, 0xEC, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEE, 0xA8, 0xAC, 0xA8, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEE, 0xA8, 0xAC, 0xA8, 0xE8, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4E, 0xCA, 0x4A, 0x4A, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x44, 0xCC, 0x44, 0x44, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4C, 0xC2, 0x44, 0x48, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4C, 0xC2, 0x4C, 0x42, 0xEC, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4A, 0xCA, 0x4E, 0x42, 0xE2, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4E, 0xC8, 0x4E, 0x42, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x46, 0xC8, 0x4E, 0x4A, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4E, 0xC2, 0x44, 0x44, 0xE4, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4E, 0xCA, 0x4E, 0x4A, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4E, 0xCA, 0x4E, 0x42, 0xE2, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x44, 0xCA, 0x4E, 0x4A, 0xEA, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4C, 0xCA, 0x4C, 0x4A, 0xEC, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x44, 0xCA, 0x48, 0x4A, 0xE4, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4C, 0xCA, 0x4A, 0x4A, 0xEC, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4E, 0xC8, 0x4C, 0x48, 0xEE, 0x00, 0xFE,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0x4E, 0xC8, 0x4C, 0x48, 0xE8, 0x00, 0xFE,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x6C, 0x6C, 0x6C, 0x6C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x6C, 0x6C, 0x6C, 0xFE, 0xFE, 0x6C, 0x6C,
+ 0xFE, 0xFE, 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x7C, 0xFE, 0xD6, 0xD0, 0xF0, 0x7C,
+ 0x1E, 0x16, 0xD6, 0xFE, 0x7C, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xCE, 0x1C,
+ 0x38, 0x70, 0xE6, 0xC6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x70, 0xF8, 0xD8, 0xD8, 0xF8, 0x72, 0xFE,
+ 0xDE, 0xCC, 0xCC, 0xFE, 0x7A, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x0E, 0x1C, 0x18, 0x38, 0x30, 0x30, 0x30,
+ 0x30, 0x38, 0x18, 0x1C, 0x0E, 0x06, 0x00, 0x00,
+ 0x60, 0x70, 0x38, 0x18, 0x1C, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x1C, 0x18, 0x38, 0x70, 0x60, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x6C, 0x6C, 0x38, 0xFE,
+ 0xFE, 0x38, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x7E,
+ 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x38, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0E, 0x1C,
+ 0x38, 0x70, 0xE0, 0xC0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xC6, 0xCE, 0xCE, 0xD6, 0xD6,
+ 0xE6, 0xE6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x38, 0x78, 0x78, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xC6, 0x06, 0x06, 0x3E, 0x7C,
+ 0xE0, 0xC0, 0xC0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xC6, 0x06, 0x06, 0x3C, 0x3C,
+ 0x06, 0x06, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x0C, 0x1C, 0x3C, 0x7C, 0xEC, 0xFE,
+ 0xFE, 0x0C, 0x0C, 0x0C, 0x0C, 0x00, 0x00, 0x00,
+ 0x00, 0xFE, 0xFE, 0xC0, 0xC0, 0xC0, 0xFC, 0xFE,
+ 0x06, 0x06, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x3C, 0x7C, 0xE0, 0xC0, 0xC0, 0xFC, 0xFE,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0xFE, 0xFE, 0xC6, 0x0E, 0x0C, 0x1C, 0x18,
+ 0x38, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xC6, 0xC6, 0xC6, 0x7C, 0x7C,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xC6, 0xC6, 0xC6, 0xFE, 0x7E,
+ 0x06, 0x06, 0x0E, 0x7C, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
+ 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
+ 0x00, 0x18, 0x18, 0x38, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x0E, 0x1C, 0x38, 0x70, 0x70,
+ 0x38, 0x1C, 0x0E, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E, 0x00, 0x00,
+ 0x7E, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x60, 0x70, 0x38, 0x1C, 0x0E, 0x0E,
+ 0x1C, 0x38, 0x70, 0x60, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xC6, 0xC6, 0x06, 0x1E, 0x3C,
+ 0x30, 0x30, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xC6, 0xCE, 0xDE, 0xD6, 0xD6,
+ 0xDE, 0xCC, 0xC0, 0xFC, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xC6, 0xC6, 0xC6, 0xFE, 0xFE,
+ 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0xFC, 0xFE, 0xC6, 0xC6, 0xC6, 0xFC, 0xFC,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0xFC, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xC6, 0xC0, 0xC0, 0xC0, 0xC0,
+ 0xC0, 0xC0, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0xF8, 0xFC, 0xCE, 0xC6, 0xC6, 0xC6, 0xC6,
+ 0xC6, 0xC6, 0xCE, 0xFC, 0xF8, 0x00, 0x00, 0x00,
+ 0x00, 0xFE, 0xFE, 0xC0, 0xC0, 0xC0, 0xF8, 0xF8,
+ 0xC0, 0xC0, 0xC0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0xFE, 0xFE, 0xC0, 0xC0, 0xC0, 0xF8, 0xF8,
+ 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xC6, 0xC0, 0xC0, 0xDE, 0xDE,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xFE, 0xFE,
+ 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x7E, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x7E, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x0C, 0xCC, 0xFC, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0xC0, 0xC6, 0xCE, 0xDC, 0xF8, 0xF0, 0xE0,
+ 0xF0, 0xF8, 0xDC, 0xCE, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
+ 0xC0, 0xC0, 0xC0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0xC6, 0xC6, 0xEE, 0xFE, 0xFE, 0xFE, 0xD6,
+ 0xD6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0xC6, 0xC6, 0xE6, 0xE6, 0xF6, 0xF6, 0xDE,
+ 0xDE, 0xCE, 0xCE, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0xFC, 0xFE, 0xC6, 0xC6, 0xC6, 0xFE, 0xFC,
+ 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6,
+ 0xC6, 0xD6, 0xDE, 0xFE, 0x7C, 0x06, 0x00, 0x00,
+ 0x00, 0xFC, 0xFE, 0xC6, 0xC6, 0xC6, 0xFC, 0xFC,
+ 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xC6, 0xC0, 0xC0, 0xFC, 0x7E,
+ 0x06, 0x06, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x7E, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xEE,
+ 0x6C, 0x7C, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xD6,
+ 0xD6, 0xFE, 0xFE, 0xEE, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0xC6, 0xC6, 0xC6, 0xEE, 0x7C, 0x38, 0x38,
+ 0x7C, 0xEE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xEE, 0x7C, 0x38,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0xFE, 0xFE, 0x0E, 0x0C, 0x1C, 0x18, 0x38,
+ 0x30, 0x70, 0x60, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x3E, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x3E, 0x3E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xE0, 0x70,
+ 0x38, 0x1C, 0x0E, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0x7C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x0C, 0x0C, 0x7C, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x7C, 0xEE, 0xC6, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00,
+ 0x00, 0x00, 0x30, 0x30, 0x18, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x7E, 0x06,
+ 0x7E, 0xFE, 0xC6, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xFC, 0xFE, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0xFC, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xFE, 0xC6,
+ 0xC0, 0xC0, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x06, 0x06, 0x06, 0x06, 0x7E, 0xFE, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xFE, 0xC6,
+ 0xFE, 0xFE, 0xC0, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x1E, 0x3E, 0x30, 0x30, 0x30, 0x78, 0x78,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xFE, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7E, 0x06, 0x7E, 0x7C,
+ 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xDC, 0xFE, 0xE6,
+ 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x38, 0x18,
+ 0x18, 0x18, 0x18, 0x3C, 0x3C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x1C, 0x1C, 0x0C,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x7C, 0x78,
+ 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC6, 0xCE, 0xDC,
+ 0xF8, 0xF8, 0xDC, 0xCE, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xFE, 0xFE,
+ 0xD6, 0xD6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0xFE, 0xE6,
+ 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xFE, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFE, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0xFC, 0xC0, 0xC0, 0xC0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xFE, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7E, 0x06, 0x07, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xFE, 0xE0,
+ 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xFE, 0xC0,
+ 0xFC, 0x7C, 0x06, 0xFE, 0xFC, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x30, 0x30, 0x30, 0x7C, 0xFC, 0x30,
+ 0x30, 0x30, 0x30, 0x3E, 0x1E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6,
+ 0xC6, 0xC6, 0xCE, 0xFE, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6,
+ 0xC6, 0xEE, 0x7C, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6,
+ 0xD6, 0xD6, 0xFE, 0xFE, 0x6C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xEE,
+ 0x7C, 0x7C, 0xEE, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7E, 0x06, 0xFE, 0xFC,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0x0E,
+ 0x3C, 0x78, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x0E, 0x1E, 0x18, 0x18, 0x18, 0x70, 0x70,
+ 0x18, 0x18, 0x18, 0x1E, 0x0E, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x00, 0x70, 0x78, 0x18, 0x18, 0x18, 0x0E, 0x0E,
+ 0x18, 0x18, 0x18, 0x78, 0x70, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x76, 0xFE, 0xDC, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xFE, 0x00, 0xEE, 0xAA, 0xAA, 0xAA, 0xEE,
+ 0x00, 0xEE, 0x28, 0x4C, 0x48, 0x48, 0x00, 0xFE,
+ 0x00, 0x18, 0x3C, 0x66, 0x60, 0xFC, 0x60, 0xF8,
+ 0x60, 0x60, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x38, 0x6C, 0x54, 0xF6, 0xEE,
+ 0x6C, 0x7C, 0x28, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x08, 0x10, 0x00,
+ 0x00, 0x1E, 0x3E, 0x30, 0x30, 0x30, 0x78, 0x78,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0xF0, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x66, 0x66, 0x22, 0x44, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xDB, 0xDB, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x18, 0x18, 0x7E, 0x7E, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x18, 0x18, 0x18, 0x7E, 0x7E, 0x18, 0x7E,
+ 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x7C, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xC2, 0xC6, 0x0C,
+ 0x18, 0x30, 0x60, 0xDB, 0x9B, 0x00, 0x00, 0x00,
+ 0xC6, 0x7C, 0x00, 0x7C, 0xFE, 0xC6, 0xC0, 0xFC,
+ 0x7E, 0x06, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x1C,
+ 0x38, 0x30, 0x38, 0x1C, 0x0C, 0x00, 0x00, 0x00,
+ 0x00, 0x7F, 0xFF, 0xCC, 0xCC, 0xCC, 0xCF, 0xCF,
+ 0xCC, 0xCC, 0xCC, 0xFF, 0x7F, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x38, 0x6C, 0x54, 0xF6, 0xEE,
+ 0x6C, 0x7C, 0x28, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0xC6, 0x7C, 0x00, 0xFE, 0xFE, 0x06, 0x0C, 0x18,
+ 0x30, 0x60, 0xC0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x38, 0x6C, 0x54, 0xF6, 0xEE,
+ 0x6C, 0x7C, 0x28, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x38, 0x6C, 0x54, 0xF6, 0xEE,
+ 0x6C, 0x7C, 0x28, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x20, 0x30, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x30, 0x10, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x24, 0x48, 0x6C, 0x6C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x36, 0x36, 0x12, 0x24, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x7E,
+ 0x7E, 0x7E, 0x7E, 0x3C, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x32, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xF1, 0xFB, 0x5F, 0x55, 0x51, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xC6, 0x6C, 0x38, 0x00, 0x7E, 0xFE, 0xC0,
+ 0xFC, 0x7C, 0x06, 0xFE, 0xFC, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x38,
+ 0x1C, 0x0C, 0x1C, 0x38, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6C, 0xFE, 0xDA,
+ 0xDE, 0xDE, 0xD8, 0xFE, 0x6E, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x38, 0x6C, 0x54, 0xF6, 0xEE,
+ 0x6C, 0x7C, 0x28, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0xC6, 0x6C, 0x38, 0x00, 0xFE, 0xFE, 0x0E,
+ 0x3C, 0x78, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x6C, 0x00, 0xC6, 0xC6, 0xC6, 0xEE, 0x7C,
+ 0x38, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x10, 0x7C, 0xFE, 0xD6,
+ 0xD0, 0xD0, 0xD6, 0xFE, 0x7C, 0x10, 0x10, 0x00,
+ 0x00, 0x3C, 0x7E, 0x66, 0x60, 0x60, 0xF8, 0xF8,
+ 0x60, 0x60, 0xE6, 0xFE, 0xDC, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x3C, 0x7E,
+ 0x66, 0x66, 0x7E, 0x3C, 0x66, 0x66, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E,
+ 0x18, 0x7E, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x3C, 0x7C, 0x60, 0x60, 0x3C, 0x7E, 0x66,
+ 0x7E, 0x3C, 0x06, 0x06, 0x3E, 0x3C, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x3C, 0x7E, 0xC3, 0x99, 0xBD, 0xA5, 0xA1,
+ 0xA5, 0xBD, 0x99, 0xC3, 0x7E, 0x3C, 0x00, 0x00,
+ 0x00, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, 0xF8,
+ 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36,
+ 0x6C, 0xD8, 0xD8, 0x6C, 0x36, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7E, 0x7E, 0x06, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x3C, 0x7E, 0xC3, 0xB9, 0xBD, 0xA5, 0xB9,
+ 0xB9, 0xA5, 0xA5, 0xC3, 0x7E, 0x3C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x78, 0xFC, 0xCC, 0xFC, 0x78, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x7E, 0x18,
+ 0x18, 0x00, 0x7E, 0x7E, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xF0, 0xF8, 0x18, 0x78, 0xF0, 0xC0,
+ 0xF8, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xF0, 0xF8, 0x18, 0x30, 0x18, 0xF8,
+ 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66,
+ 0x66, 0x66, 0x66, 0x7E, 0x7C, 0x60, 0x60, 0xC0,
+ 0x00, 0x00, 0x00, 0x7F, 0xDB, 0xDB, 0xDB, 0x7B,
+ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x1C, 0x0C, 0x1C, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x60, 0xE0, 0x60, 0x60, 0x60, 0xF0,
+ 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x70, 0xF8, 0x88, 0xF8, 0x70, 0x00, 0xF8,
+ 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8,
+ 0x6C, 0x36, 0x36, 0x6C, 0xD8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x40, 0xC0, 0x42, 0x46, 0xEC, 0x18,
+ 0x30, 0x64, 0xCC, 0x94, 0x3E, 0x04, 0x04, 0x00,
+ 0x00, 0x00, 0x40, 0xC0, 0x42, 0x46, 0xEC, 0x18,
+ 0x30, 0x60, 0xDC, 0x82, 0x0C, 0x10, 0x1E, 0x00,
+ 0x00, 0x00, 0xE0, 0x10, 0x62, 0x16, 0xEC, 0x18,
+ 0x30, 0x64, 0xCC, 0x96, 0x3E, 0x04, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18,
+ 0x78, 0xF0, 0xC0, 0xC6, 0xC6, 0xFE, 0x7C, 0x00,
+ 0x00, 0x60, 0x30, 0x00, 0x10, 0x38, 0x6C, 0xC6,
+ 0xFE, 0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x0C, 0x18, 0x00, 0x10, 0x38, 0x6C, 0xC6,
+ 0xFE, 0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6C, 0x44, 0x10, 0x38, 0x6C, 0xC6,
+ 0xFE, 0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x76, 0xDC, 0x00, 0x10, 0x38, 0x6C, 0xC6,
+ 0xFE, 0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x6C, 0x6C, 0x00, 0x10, 0x38, 0x6C, 0xC6, 0xFE,
+ 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x44, 0x38, 0x38, 0x6C, 0xC6, 0xFE,
+ 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x1F, 0x3F, 0x7C, 0xEC, 0xCC, 0xFE, 0xFE,
+ 0xCC, 0xCC, 0xCC, 0xCF, 0xCF, 0x00, 0x00, 0x00,
+ 0x00, 0x7C, 0xFE, 0xC6, 0xC0, 0xC0, 0xC0, 0xC0,
+ 0xC0, 0xC0, 0xC6, 0xFE, 0x7C, 0x0E, 0x66, 0x3C,
+ 0x00, 0x30, 0x18, 0x00, 0xFE, 0xFE, 0xC0, 0xF0,
+ 0xF0, 0xC0, 0xC0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x0C, 0x18, 0x00, 0xFE, 0xFE, 0xC0, 0xF0,
+ 0xF0, 0xC0, 0xC0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6C, 0x00, 0xFE, 0xFE, 0xC0, 0xF0,
+ 0xF0, 0xC0, 0xC0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x6C, 0x6C, 0x00, 0xFE, 0xFE, 0xC0, 0xC0, 0xF0,
+ 0xF0, 0xC0, 0xC0, 0xFE, 0xFE, 0x00, 0x00, 0x00,
+ 0x00, 0x30, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x0C, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x24, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x66, 0x66, 0x00, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x7C, 0x6E, 0x66, 0x66, 0xF6,
+ 0x66, 0x66, 0x6E, 0x7C, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x76, 0xDC, 0x00, 0xC6, 0xC6, 0xE6, 0xF6,
+ 0xFE, 0xDE, 0xCE, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x30, 0x18, 0x00, 0x7C, 0xFE, 0xC6, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x0C, 0x18, 0x00, 0x7C, 0xFE, 0xC6, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6C, 0x00, 0x7C, 0xFE, 0xC6, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x76, 0xDC, 0x00, 0x7C, 0xFE, 0xC6, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x6C, 0x00, 0x7C, 0xFE, 0xC6, 0xC6, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xEE, 0x7C,
+ 0x38, 0x38, 0x7C, 0xEE, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x7E, 0xFE, 0xC6, 0xC6, 0xCE, 0xCE, 0xD6,
+ 0xD6, 0xE6, 0xE6, 0xFE, 0x7C, 0x80, 0x00, 0x00,
+ 0x00, 0x30, 0x18, 0x00, 0xC6, 0xC6, 0xC6, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x0C, 0x18, 0x00, 0xC6, 0xC6, 0xC6, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6C, 0x00, 0xC6, 0xC6, 0xC6, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x6C, 0x6C, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x0C, 0x18, 0x00, 0x66, 0x66, 0x66, 0x7E,
+ 0x3C, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x60, 0x60, 0x7C, 0x7E, 0x66, 0x66,
+ 0x7E, 0x7C, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00,
+ 0x00, 0x3C, 0x7E, 0x66, 0x66, 0x66, 0x7E, 0x7C,
+ 0x66, 0x66, 0x66, 0x7E, 0x7C, 0xE0, 0xC0, 0x00,
+ 0x00, 0x00, 0x30, 0x18, 0x00, 0x7E, 0xFE, 0xC6,
+ 0xC6, 0xCE, 0xCE, 0xFE, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x30, 0x00, 0x7E, 0xFE, 0xC6,
+ 0xC6, 0xC6, 0xCE, 0xFE, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6C, 0x00, 0x7E, 0xFE, 0xC6,
+ 0xC6, 0xC6, 0xCE, 0xFE, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x76, 0xDC, 0x00, 0x7E, 0xFE, 0xC6,
+ 0xC6, 0xC6, 0xCE, 0xFE, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x6C, 0x6C, 0x00, 0x7E, 0xFE, 0xC6,
+ 0xC6, 0xC6, 0xCE, 0xFE, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6C, 0x38, 0x00, 0x7E, 0xFE, 0xC6,
+ 0xC6, 0xC6, 0xCE, 0xFE, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x7E, 0x1A,
+ 0x3E, 0x7E, 0xD8, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xFE,
+ 0xC6, 0xC0, 0xC6, 0xFE, 0x7C, 0x18, 0x7C, 0x78,
+ 0x00, 0x00, 0x30, 0x18, 0x00, 0x7C, 0xFE, 0xC6,
+ 0xFE, 0xFE, 0xC0, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x30, 0x00, 0x7C, 0xFE, 0xC6,
+ 0xFE, 0xFE, 0xC0, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6C, 0x00, 0x7C, 0xFE, 0xC6,
+ 0xFE, 0xFE, 0xC0, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x6C, 0x6C, 0x00, 0x7C, 0xFE, 0xC6,
+ 0xFE, 0xFE, 0xC0, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x18, 0x00, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0C, 0x18, 0x00, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x3C, 0x66, 0x00, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x00, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x6C, 0x7C, 0xF8, 0xDC, 0x7C, 0xFE, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x76, 0xFE, 0xDC, 0x00, 0xFC, 0xFE, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x60, 0x30, 0x00, 0x7C, 0xFE, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x30, 0x00, 0x7C, 0xFE, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6C, 0x00, 0x7C, 0xFE, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x76, 0xFE, 0xDC, 0x00, 0x7C, 0xFE, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x6C, 0x6C, 0x00, 0x7C, 0xFE, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7E,
+ 0x7E, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x7C, 0xFE, 0xCE,
+ 0xD6, 0xD6, 0xE6, 0xFE, 0x7C, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x60, 0x30, 0x00, 0xC6, 0xC6, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x30, 0x00, 0xC6, 0xC6, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6C, 0x00, 0xC6, 0xC6, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x6C, 0x6C, 0x00, 0xC6, 0xC6, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0C, 0x18, 0x00, 0xC6, 0xC6, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7E, 0x06, 0xFC, 0xF8,
+ 0x00, 0x00, 0x60, 0x60, 0x60, 0x7C, 0x7E, 0x66,
+ 0x66, 0x66, 0x66, 0x7E, 0x7C, 0x60, 0x60, 0x60,
+ 0x00, 0x00, 0x6C, 0x6C, 0x00, 0xC6, 0xC6, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0x7E, 0x06, 0xFE, 0xFC,
+};
+
+const struct fb_font_desc font_regular = {
+ .name = "NetSurf Regular",
+ .width = 8,
+ .height = 16,
+ .encoding = "CP1252",
+ .data = fontdata_regular,
+};
+
+#endif
diff --git a/frontends/atari/plot/font_internal.h b/frontends/atari/plot/font_internal.h
new file mode 100644
index 000000000..9bfbbedef
--- /dev/null
+++ b/frontends/atari/plot/font_internal.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ * Copyright 2011 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#ifdef WITH_INTERNAL_FONT_DRIVER
+#ifndef FONT_PLOTTER_INTERNAL
+#define FONT_PLOTTER_INTERNAL
+
+#include "atari/plot/plot.h"
+
+int ctor_font_plotter_internal( FONT_PLOTTER self );
+
+struct fb_font_desc {
+ const char *name;
+ int width, height;
+ const char *encoding;
+ const uint32_t *data;
+};
+
+
+#endif
+#endif
diff --git a/frontends/atari/plot/font_vdi.c b/frontends/atari/plot/font_vdi.c
new file mode 100644
index 000000000..ef5499207
--- /dev/null
+++ b/frontends/atari/plot/font_vdi.c
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#ifdef WITH_VDI_FONT_DRIVER
+
+#include <mt_gemx.h>
+
+#include "atari/plot/plot.h"
+#include "atari/plot/font_vdi.h"
+
+#include "utils/utf8.h"
+#include "utils/log.h"
+
+#include "atari/encoding.h"
+
+
+//static char * lstr = NULL;
+
+static int dtor( FONT_PLOTTER self );
+static int str_width( FONT_PLOTTER self,const plot_font_style_t *fstyle, const char * str, size_t length, int * width );
+static int str_split( FONT_PLOTTER self, const plot_font_style_t *fstyle,const char *string,
+ size_t length,int x, size_t *char_offset, int *actual_x );
+static int pixel_pos( FONT_PLOTTER self, const plot_font_style_t *fstyle,const char *string,
+ size_t length,int x, size_t *char_offset, int *actual_x );
+static int text( FONT_PLOTTER self, int x, int y, const char *text, size_t length, const plot_font_style_t *fstyle );
+
+static bool init = false;
+static int vdih;
+
+extern struct s_vdi_sysinfo vdi_sysinfo;
+
+static inline void atari_to_vdi_str(char *lstr, int length)
+{
+ int i, z;
+
+ for (i=z=0; i<length; ) {
+ if (((char)lstr[i]==(char)0xC2) && ((char)lstr[i+1] == (char)0xA0)) {
+ lstr[z] = ' ';
+ lstr[z+1] = ' ';
+ i=i+2;
+ z=z+2;
+ }
+ else {
+ lstr[z] = lstr[i];
+ i++;
+ z++;
+ }
+ }
+}
+
+int ctor_font_plotter_vdi( FONT_PLOTTER self )
+{
+ self->dtor = dtor;
+ self->str_width = str_width;
+ self->str_split = str_split;
+ self->pixel_pos = pixel_pos;
+ self->text = text;
+ LOG("%s: %s\n", (char *)__FILE__, __FUNCTION__);
+ if( !init ) {
+ vdih = self->vdi_handle;
+ }
+ init = true;
+ return( 1 );
+}
+
+static int dtor( FONT_PLOTTER self )
+{
+ return( 1 );
+}
+
+static int str_width( FONT_PLOTTER self,const plot_font_style_t *fstyle, const char * str,
+ size_t length, int * width )
+{
+ short cw, ch, cellw, cellh;
+ short pxsize;
+ short fx=0;
+ char * lstr = NULL;
+
+ utf8_to_local_encoding(str, length, &lstr);
+ assert( lstr != NULL );
+ int slen = strlen(lstr);
+
+
+ atari_to_vdi_str(lstr, slen);
+
+ if( fstyle->flags & FONTF_ITALIC )
+ fx |= 4;
+ if( fstyle->flags & FONTF_OBLIQUE )
+ fx |= 16;
+ if( fstyle->weight > 450 )
+ fx |= 1;
+ vst_effects( self->vdi_handle, fx );
+ /* TODO: replace 90 with global dpi setting */
+ //pxsize = ceil( (fstyle->size/FONT_SIZE_SCALE) * 90 / 72 );
+ //vst_height( self->vdi_handle, pxsize ,&cw, &ch, &cellw, &cellh);
+ pxsize = ceil( (fstyle->size/FONT_SIZE_SCALE) * 90 );
+ vst_point( self->vdi_handle, pxsize, &cw, &ch, &cellw, &cellh);
+/*
+ if(slen != utf8_bounded_length(str, length)){
+ printf("sl: %d, utl: %d\n ", slen, utf8_bounded_length(str, length));
+ printf("s: %s // %s\n", str, lstr );
+ }*/
+
+
+ *width = slen * cellw;
+ free((void*)lstr);
+ return( 0 );
+}
+
+static int str_split( FONT_PLOTTER self, const plot_font_style_t * fstyle, const char *string,
+ size_t length,int x, size_t *char_offset, int *actual_x )
+{
+ short cw, ch, cellw, cellh;
+ short pxsize;
+ short fx=0;
+ char *lstr = NULL;
+ size_t slen = 0;
+ int last_space_x = 0;
+ int last_space_idx = 0;
+ size_t nxtchr = 0;
+
+ utf8_to_local_encoding(string, length, &lstr );
+ assert( lstr != NULL );
+ slen = strlen(lstr);
+
+
+ atari_to_vdi_str(lstr, slen);
+
+ if( fstyle->flags & FONTF_ITALIC )
+ fx |= 4;
+ if( fstyle->flags & FONTF_OBLIQUE )
+ fx |= 16;
+ if( fstyle->weight > 450 )
+ fx |= 1;
+ vst_effects( self->vdi_handle, fx );
+ //pxsize = ceil( (fstyle->size/FONT_SIZE_SCALE) * 90 / 72 );
+ //vst_height( self->vdi_handle, pxsize ,&cw, &ch, &cellw, &cellh);
+
+ pxsize = ceil( (fstyle->size/FONT_SIZE_SCALE) * 90 );
+ vst_point( self->vdi_handle, pxsize, &cw, &ch, &cellw, &cellh);
+ *actual_x = 0;
+ //*char_offset = 0;
+ while (nxtchr < slen) {
+ if( lstr[nxtchr] == ' ' ) {
+ last_space_x = *actual_x;
+ last_space_idx = nxtchr;
+ }
+ *actual_x += cellw;
+ if (*actual_x > x && last_space_idx != 0) {
+ *actual_x = last_space_x;
+ *char_offset = last_space_idx;
+ //printf("at: %s\n", lstr);
+ return(0);
+ }
+
+ nxtchr++;
+ }
+ if(nxtchr >= length){
+ nxtchr = length-1;
+ }
+
+ *char_offset = nxtchr;
+
+// for( i=0; i<slen; i++) {
+// if( lstr[i] == ' ' ) {
+// last_space_x = *actual_x;
+// last_space_idx = cpos;
+// }
+// if( *actual_x > x ) {
+// *actual_x = last_space_x;
+// *char_offset = last_space_idx;
+// return true;
+// }
+// *actual_x += cellw;
+// cpos++;
+// }
+// *char_offset = cpos;
+ free( (void*)lstr );
+ return( 0 );
+}
+
+static int pixel_pos( FONT_PLOTTER self, const plot_font_style_t * fstyle,const char *string,
+ size_t length,int x, size_t *char_offset, int *actual_x )
+{
+ short cw, ch, cellw, cellh;
+ short pxsize=0;
+ short fx=0;
+
+ char *lstr = NULL;
+ int i=0;
+ utf8_to_local_encoding(string, length, &lstr );
+ assert( lstr != NULL );
+ int slen = strlen(lstr);
+
+ atari_to_vdi_str(lstr, slen);
+
+ if( fstyle->flags & FONTF_ITALIC )
+ fx |= 4;
+ if( fstyle->flags & FONTF_OBLIQUE )
+ fx |= 16;
+ if( fstyle->weight > 450 )
+ fx |= 1;
+ vst_effects(self->vdi_handle, fx);
+ pxsize = ceil( (fstyle->size/FONT_SIZE_SCALE) * 90 / 72 );
+ vst_height( self->vdi_handle, pxsize ,&cw, &ch, &cellw, &cellh);
+ *actual_x = 0;
+ *char_offset = 0;
+ for( i=0; i<slen; i++) {
+ *actual_x += cellw;
+ if( *actual_x > x) {
+ *actual_x -= cellw;
+ *char_offset = i;
+ break;
+ }
+ }
+ free((void*)lstr);
+ lstr = NULL;
+ return( 0 );
+}
+
+static inline void vst_rgbcolor( short vdih, uint32_t cin )
+{
+#ifdef WITH_8BPP_SUPPORT
+ if( vdi_sysinfo.scr_bpp > 8 ) {
+#endif
+ //unsigned short c[4];
+ RGB1000 c;
+
+ rgb_to_vdi1000( (unsigned char*)&cin, &c );
+ vs_color( vdih, OFFSET_CUSTOM_COLOR, (unsigned short*)&c);
+ vst_color( vdih, OFFSET_CUSTOM_COLOR );
+#ifdef WITH_8BPP_SUPPORT
+ } else {
+ if( vdi_sysinfo.scr_bpp >= 4 )
+ vst_color( vdih, RGB_TO_VDI(cin) );
+ else
+ vst_color( vdih, BLACK );
+ }
+#endif
+}
+
+static int text( FONT_PLOTTER self, int x, int y, const char *text, size_t length,
+ const plot_font_style_t *fstyle )
+{
+ /* todo: either limit the string to max 80 chars, or use v_ftext instead of v_gtext */
+ short cw, ch, cellw, cellh;
+ short pxsize=8;
+ short fx=0;
+ GRECT canvas;
+ char *lstr = NULL;
+ assert( utf8_to_local_encoding(text, length, &lstr) == NSERROR_OK);
+ assert( lstr != NULL );
+
+ int slen = strlen(lstr);
+ if(slen > 800){
+ lstr[800]=0;
+ }
+
+
+ atari_to_vdi_str(lstr, slen);
+
+ if( fstyle->flags & FONTF_ITALIC )
+ fx |= 4;
+ if( fstyle->flags & FONTF_OBLIQUE )
+ fx |= 4;
+ if( fstyle->weight > 450 )
+ fx |= 1;
+
+ /* TODO: netsurf uses 90 as default dpi ( somewhere defined in libcss),
+ use that value or pass it as arg, to reduce netsurf dependency */
+ //pxsize = ceil( (fstyle->size/FONT_SIZE_SCALE) * 90 / 72 );
+ pxsize = ceil( (fstyle->size/FONT_SIZE_SCALE) * 90 / 72 );
+
+ plot_get_dimensions(&canvas);
+ x += canvas.g_x;
+ y += canvas.g_y;
+ vst_effects( self->vdi_handle, fx );
+ vst_alignment(vdih, 0, 0, &cw, &ch );
+ vst_point( self->vdi_handle, pxsize, &cw, &ch, &cellw, &cellh);
+ //vst_height( self->vdi_handle, pxsize, &cw, &ch, &cellw, &cellh);
+ vswr_mode( self->vdi_handle, MD_TRANS );
+ vst_rgbcolor(self->vdi_handle, fstyle->foreground);
+
+ if( atari_sysinfo.gdos_FSMC ){
+ //printf("\nftext\n");
+ v_ftext( self->vdi_handle, x, y, (char*)lstr );
+ } else {
+ //printf("\ngtext\n");
+ v_gtext( self->vdi_handle, x, y, (char*)lstr );
+ }
+ free( lstr );
+ return( 0 );
+}
+
+#endif
diff --git a/frontends/atari/plot/font_vdi.h b/frontends/atari/plot/font_vdi.h
new file mode 100644
index 000000000..3a1fdb54b
--- /dev/null
+++ b/frontends/atari/plot/font_vdi.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#ifdef WITH_VDI_FONT_DRIVER
+#ifndef FONT_PLOTTER_VDI
+#define FONT_PLOTTER_VDI
+
+int ctor_font_plotter_vdi( FONT_PLOTTER self );
+
+#endif
+#endif
diff --git a/frontends/atari/plot/fontplot.c b/frontends/atari/plot/fontplot.c
new file mode 100644
index 000000000..9f0edd9a4
--- /dev/null
+++ b/frontends/atari/plot/fontplot.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <mt_gem.h>
+
+#include "desktop/mouse.h"
+#include "desktop/plot_style.h"
+
+#include "atari/bitmap.h"
+#include "atari/plot/fontplot.h"
+
+const struct s_font_driver_table_entry font_driver_table[] =
+{
+#ifdef WITH_VDI_FONT_DRIVER
+ {"vdi", ctor_font_plotter_vdi, 0},
+#endif
+#ifdef WITH_FREETYPE_FONT_DRIVER
+ {"freetype", ctor_font_plotter_freetype, 0},
+#endif
+#ifdef WITH_INTERNAL_FONT_DRIVER
+ {"internal", ctor_font_plotter_internal, 0},
+#endif
+ {(char*)NULL, NULL, 0}
+};
+
+void dump_font_drivers(void)
+{
+ int i = 0;
+ while( font_driver_table[i].name != NULL ) {
+ printf("%s -> flags: %d\n", font_driver_table[i].name,
+ font_driver_table[i].flags);
+ i++;
+ }
+}
+
+
+/**
+ * Create an new text plotter object.
+ *
+ * Available: "vdi", "freetype", "internal"
+ *
+ * \param vdihandle the vdi handle to act upon,
+ * \param name selector ID (string) of the font plotter.
+ * \param flags configration flags of the plotter, available flags:
+ * FONTPLOT_FLAG_MONOGLYPH - Enable 1 bit font plotting
+ * \param error set to != 0 when errors occur
+ * \return the new font plotter instance on success, or NULL on failure.
+ */
+FONT_PLOTTER new_font_plotter(int vdihandle, char * name, unsigned long flags,
+ int * error)
+{
+ int i=0;
+ int res = 0-ERR_PLOTTER_NOT_AVAILABLE;
+ FONT_PLOTTER fplotter = NULL;
+
+ /* set the default error code: */
+ *error = 0-ERR_PLOTTER_NOT_AVAILABLE;
+
+
+ /* Find the selector string in the font plotter table, */
+ /* and bail out when the font plotter is not available: */
+ for (i = 0; font_driver_table[i].name != NULL; i++) {
+
+ /* found selector in driver table? */
+ if (strcmp(name, font_driver_table[i].name) == 0) {
+
+ /* allocate the font plotter instance: */
+ fplotter = (FONT_PLOTTER)malloc(sizeof(struct s_font_plotter));
+ if (fplotter == NULL) {
+ *error = 0-ERR_NO_MEM;
+ return(NULL);
+ }
+
+ /* Initialize the font plotter with the requested settings: */
+ memset( fplotter, 0, sizeof(FONT_PLOTTER));
+ fplotter->vdi_handle = vdihandle;
+ fplotter->name = name;
+ fplotter->flags = 0;
+ fplotter->flags |= flags;
+
+ /* Execute the constructor: */
+ assert(font_driver_table[i].ctor);
+ res = font_driver_table[i].ctor(fplotter);
+
+ /* success? */
+ if (res < 0) {
+ /* NO success! */
+ free(fplotter);
+ *error = res;
+ return(NULL);
+ }
+ *error = 0;
+ break;
+ }
+ }
+
+ return(fplotter);
+}
+
+/*
+ Free an font plotter
+*/
+int delete_font_plotter(FONT_PLOTTER p)
+{
+ if (p) {
+ p->dtor(p);
+ free(p);
+ p = NULL;
+ }
+ else
+ return(-1);
+ return(0);
+}
+
diff --git a/frontends/atari/plot/fontplot.h b/frontends/atari/plot/fontplot.h
new file mode 100644
index 000000000..6690bff2c
--- /dev/null
+++ b/frontends/atari/plot/fontplot.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 FONT_PLOT_H
+#define FONT_PLOT_H
+
+typedef struct s_font_plotter * FONT_PLOTTER;
+
+struct s_font_driver_table_entry
+{
+ const char * name;
+ int (*ctor)( FONT_PLOTTER self );
+ int flags;
+};
+
+/* declaration of font plotter member functions: (_fpmf_ prefix) */
+typedef int (*_fpmf_str_width)( FONT_PLOTTER self, const plot_font_style_t *fstyle,
+ const char * str, size_t length, int * width);
+typedef int (*_fpmf_str_split)( FONT_PLOTTER self, const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x);
+typedef int (*_fpmf_pixel_pos)( FONT_PLOTTER self, const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x);
+typedef int (*_fpmf_text)( FONT_PLOTTER self, int x, int y, const char *text,
+ size_t length, const plot_font_style_t *fstyle);
+
+typedef void (*_fpmf_draw_glyph)(FONT_PLOTTER self, GRECT * clip, GRECT * loc,
+ uint8_t * pixdata, int pitch, uint32_t colour);
+typedef int (*_fpmf_dtor)( FONT_PLOTTER self );
+
+
+/* prototype of the font plotter "object" */
+struct s_font_plotter
+{
+ char * name;
+ int flags;
+ int vdi_handle;
+ void * priv_data;
+
+ _fpmf_str_width str_width;
+ _fpmf_str_split str_split;
+ _fpmf_pixel_pos pixel_pos;
+ _fpmf_text text;
+ _fpmf_draw_glyph draw_glyph;
+ _fpmf_dtor dtor;
+};
+
+
+FONT_PLOTTER plot_get_text_plotter(void);
+/* Set the font plotting engine.
+*/
+void plot_set_text_plotter(FONT_PLOTTER font_plotter);
+void dump_font_drivers(void);
+FONT_PLOTTER new_font_plotter( int vdihandle, char * name, unsigned long flags,
+ int * error);
+int delete_font_plotter( FONT_PLOTTER p );
+
+#ifdef WITH_VDI_FONT_DRIVER
+ #include "atari/plot/font_vdi.h"
+#endif
+#ifdef WITH_INTERNAL_FONT_DRIVER
+ #include "atari/plot/font_internal.h"
+#endif
+#ifdef WITH_FREETYPE_FONT_DRIVER
+ #include "atari/plot/font_freetype.h"
+#endif
+
+
+
+#endif
diff --git a/frontends/atari/plot/plot.c b/frontends/atari/plot/plot.c
new file mode 100644
index 000000000..45e4cead2
--- /dev/null
+++ b/frontends/atari/plot/plot.c
@@ -0,0 +1,2247 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <assert.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <string.h>
+#include <limits.h>
+#include <math.h>
+#include <stdbool.h>
+#include <mt_gem.h>
+#include <mint/osbind.h>
+
+#include "utils/log.h"
+#include "utils/utf8.h"
+#include "utils/utils.h"
+#include "image/bitmap.h"
+#include "desktop/plotters.h"
+#include "desktop/mouse.h"
+
+#include "atari/gui.h"
+#include "atari/osspec.h"
+#include "atari/misc.h"
+#include "atari/bitmap.h"
+#include "utils/nsoption.h"
+#include "atari/plot/eddi.h"
+#include "atari/plot/fontplot.h"
+#include "atari/plot/plot.h"
+
+void vq_scrninfo(VdiHdl handle, short *work_out);
+
+struct s_view {
+ short x; /* drawing (screen) offset x */
+ short y; /* drawing (screen) offset y */
+ short w; /* width of buffer, not in sync with vis_w */
+ short h; /* height of buffer, not in sync with vis_w */
+ short vis_x; /* visible rectangle of the screen buffer */
+ short vis_y; /* coords are relative to plot location */
+ short vis_w; /* clipped to screen dimensions */
+ short vis_h; /* visible width */
+ struct rect abs_clipping; /* The toplevel clipping rectangle */
+ struct rect clipping; /* actual clipping rectangle */
+ float scale;
+};
+
+/*
+* Capture the screen at x,y location
+* param self instance
+* param x absolute screen coords
+* param y absolute screen coords
+* param w width
+* param h height
+*
+* This creates an snapshot in RGBA format (NetSurf's native format)
+*
+*/
+static struct bitmap * snapshot_create(int x, int y, int w, int h);
+
+/* Garbage collection of the snapshot routine */
+/* this should be called after you are done with the data returned by snapshot_create */
+/* don't access the screenshot after you called this function */
+static void snapshot_suspend(void);
+
+/* destroy memory used by screenshot */
+static void snapshot_destroy(void);
+
+#ifdef WITH_8BPP_SUPPORT
+static unsigned short sys_pal[256][3]; /*RGB*/
+static unsigned short pal[256][3]; /*RGB*/
+static char rgb_lookup[256][4];
+short web_std_colors[6] = {0, 51, 102, 153, 204, 255};
+
+unsigned short vdi_web_pal[216][3] = {
+ {0x000,0x000,0x000}, {0x0c8,0x000,0x000}, {0x190,0x000,0x000}, {0x258,0x000,0x000}, {0x320,0x000,0x000}, {0x3e8,0x000,0x000},
+ {0x000,0x0c8,0x000}, {0x0c8,0x0c8,0x000}, {0x190,0x0c8,0x000}, {0x258,0x0c8,0x000}, {0x320,0x0c8,0x000}, {0x3e8,0x0c8,0x000},
+ {0x000,0x190,0x000}, {0x0c8,0x190,0x000}, {0x190,0x190,0x000}, {0x258,0x190,0x000}, {0x320,0x190,0x000}, {0x3e8,0x190,0x000},
+ {0x000,0x258,0x000}, {0x0c8,0x258,0x000}, {0x190,0x258,0x000}, {0x258,0x258,0x000}, {0x320,0x258,0x000}, {0x3e8,0x258,0x000},
+ {0x000,0x320,0x000}, {0x0c8,0x320,0x000}, {0x190,0x320,0x000}, {0x258,0x320,0x000}, {0x320,0x320,0x000}, {0x3e8,0x320,0x000},
+ {0x000,0x3e8,0x000}, {0x0c8,0x3e8,0x000}, {0x190,0x3e8,0x000}, {0x258,0x3e8,0x000}, {0x320,0x3e8,0x000}, {0x3e8,0x3e8,0x000},
+ {0x000,0x000,0x0c8}, {0x0c8,0x000,0x0c8}, {0x190,0x000,0x0c8}, {0x258,0x000,0x0c8}, {0x320,0x000,0x0c8}, {0x3e8,0x000,0x0c8},
+ {0x000,0x0c8,0x0c8}, {0x0c8,0x0c8,0x0c8}, {0x190,0x0c8,0x0c8}, {0x258,0x0c8,0x0c8}, {0x320,0x0c8,0x0c8}, {0x3e8,0x0c8,0x0c8},
+ {0x000,0x190,0x0c8}, {0x0c8,0x190,0x0c8}, {0x190,0x190,0x0c8}, {0x258,0x190,0x0c8}, {0x320,0x190,0x0c8}, {0x3e8,0x190,0x0c8},
+ {0x000,0x258,0x0c8}, {0x0c8,0x258,0x0c8}, {0x190,0x258,0x0c8}, {0x258,0x258,0x0c8}, {0x320,0x258,0x0c8}, {0x3e8,0x258,0x0c8},
+ {0x000,0x320,0x0c8}, {0x0c8,0x320,0x0c8}, {0x190,0x320,0x0c8}, {0x258,0x320,0x0c8}, {0x320,0x320,0x0c8}, {0x3e8,0x320,0x0c8},
+ {0x000,0x3e8,0x0c8}, {0x0c8,0x3e8,0x0c8}, {0x190,0x3e8,0x0c8}, {0x258,0x3e8,0x0c8}, {0x320,0x3e8,0x0c8}, {0x3e8,0x3e8,0x0c8},
+ {0x000,0x000,0x190}, {0x0c8,0x000,0x190}, {0x190,0x000,0x190}, {0x258,0x000,0x190}, {0x320,0x000,0x190}, {0x3e8,0x000,0x190},
+ {0x000,0x0c8,0x190}, {0x0c8,0x0c8,0x190}, {0x190,0x0c8,0x190}, {0x258,0x0c8,0x190}, {0x320,0x0c8,0x190}, {0x3e8,0x0c8,0x190},
+ {0x000,0x190,0x190}, {0x0c8,0x190,0x190}, {0x190,0x190,0x190}, {0x258,0x190,0x190}, {0x320,0x190,0x190}, {0x3e8,0x190,0x190},
+ {0x000,0x258,0x190}, {0x0c8,0x258,0x190}, {0x190,0x258,0x190}, {0x258,0x258,0x190}, {0x320,0x258,0x190}, {0x3e8,0x258,0x190},
+ {0x000,0x320,0x190}, {0x0c8,0x320,0x190}, {0x190,0x320,0x190}, {0x258,0x320,0x190}, {0x320,0x320,0x190}, {0x3e8,0x320,0x190},
+ {0x000,0x3e8,0x190}, {0x0c8,0x3e8,0x190}, {0x190,0x3e8,0x190}, {0x258,0x3e8,0x190}, {0x320,0x3e8,0x190}, {0x3e8,0x3e8,0x190},
+ {0x000,0x000,0x258}, {0x0c8,0x000,0x258}, {0x190,0x000,0x258}, {0x258,0x000,0x258}, {0x320,0x000,0x258}, {0x3e8,0x000,0x258},
+ {0x000,0x0c8,0x258}, {0x0c8,0x0c8,0x258}, {0x190,0x0c8,0x258}, {0x258,0x0c8,0x258}, {0x320,0x0c8,0x258}, {0x3e8,0x0c8,0x258},
+ {0x000,0x190,0x258}, {0x0c8,0x190,0x258}, {0x190,0x190,0x258}, {0x258,0x190,0x258}, {0x320,0x190,0x258}, {0x3e8,0x190,0x258},
+ {0x000,0x258,0x258}, {0x0c8,0x258,0x258}, {0x190,0x258,0x258}, {0x258,0x258,0x258}, {0x320,0x258,0x258}, {0x3e8,0x258,0x258},
+ {0x000,0x320,0x258}, {0x0c8,0x320,0x258}, {0x190,0x320,0x258}, {0x258,0x320,0x258}, {0x320,0x320,0x258}, {0x3e8,0x320,0x258},
+ {0x000,0x3e8,0x258}, {0x0c8,0x3e8,0x258}, {0x190,0x3e8,0x258}, {0x258,0x3e8,0x258}, {0x320,0x3e8,0x258}, {0x3e8,0x3e8,0x258},
+ {0x000,0x000,0x320}, {0x0c8,0x000,0x320}, {0x190,0x000,0x320}, {0x258,0x000,0x320}, {0x320,0x000,0x320}, {0x3e8,0x000,0x320},
+ {0x000,0x0c8,0x320}, {0x0c8,0x0c8,0x320}, {0x190,0x0c8,0x320}, {0x258,0x0c8,0x320}, {0x320,0x0c8,0x320}, {0x3e8,0x0c8,0x320},
+ {0x000,0x190,0x320}, {0x0c8,0x190,0x320}, {0x190,0x190,0x320}, {0x258,0x190,0x320}, {0x320,0x190,0x320}, {0x3e8,0x190,0x320},
+ {0x000,0x258,0x320}, {0x0c8,0x258,0x320}, {0x190,0x258,0x320}, {0x258,0x258,0x320}, {0x320,0x258,0x320}, {0x3e8,0x258,0x320},
+ {0x000,0x320,0x320}, {0x0c8,0x320,0x320}, {0x190,0x320,0x320}, {0x258,0x320,0x320}, {0x320,0x320,0x320}, {0x3e8,0x320,0x320},
+ {0x000,0x3e8,0x320}, {0x0c8,0x3e8,0x320}, {0x190,0x3e8,0x320}, {0x258,0x3e8,0x320}, {0x320,0x3e8,0x320}, {0x3e8,0x3e8,0x320},
+ {0x000,0x000,0x3e8}, {0x0c8,0x000,0x3e8}, {0x190,0x000,0x3e8}, {0x258,0x000,0x3e8}, {0x320,0x000,0x3e8}, {0x3e8,0x000,0x3e8},
+ {0x000,0x0c8,0x3e8}, {0x0c8,0x0c8,0x3e8}, {0x190,0x0c8,0x3e8}, {0x258,0x0c8,0x3e8}, {0x320,0x0c8,0x3e8}, {0x3e8,0x0c8,0x3e8},
+ {0x000,0x190,0x3e8}, {0x0c8,0x190,0x3e8}, {0x190,0x190,0x3e8}, {0x258,0x190,0x3e8}, {0x320,0x190,0x3e8}, {0x3e8,0x190,0x3e8},
+ {0x000,0x258,0x3e8}, {0x0c8,0x258,0x3e8}, {0x190,0x258,0x3e8}, {0x258,0x258,0x3e8}, {0x320,0x258,0x3e8}, {0x3e8,0x258,0x3e8},
+ {0x000,0x320,0x3e8}, {0x0c8,0x320,0x3e8}, {0x190,0x320,0x3e8}, {0x258,0x320,0x3e8}, {0x320,0x320,0x3e8}, {0x3e8,0x320,0x3e8},
+ {0x000,0x3e8,0x3e8}, {0x0c8,0x3e8,0x3e8}, {0x190,0x3e8,0x3e8}, {0x258,0x3e8,0x3e8}, {0x320,0x3e8,0x3e8}, {0x3e8,0x3e8,0x3e8}
+};
+#endif
+
+/* Error code translations: */
+static const char * plot_error_codes[] = {
+ "None",
+ "ERR_BUFFERSIZE_EXCEEDS_SCREEN",
+ "ERR_NO_MEM",
+ "ERR_PLOTTER_NOT_AVAILABLE"
+};
+
+FONT_PLOTTER fplotter = NULL;
+
+extern short vdih;
+
+/* temp buffer for bitmap conversion: */
+static void * buf_packed;
+static int size_buf_packed;
+
+/* temp buffer for bitmap conversion: */
+void * buf_planar;
+int size_buf_planar;
+
+/* buffer for plot operations that require device format, */
+/* currently used for transparent mfdb blits and snapshots: */
+static MFDB buf_scr;
+static int size_buf_scr;
+
+/* buffer for std form, used during 8bpp snapshot */
+MFDB buf_std;
+int size_buf_std;
+
+struct bitmap * buf_scr_compat;
+
+/* intermediate bitmap format */
+static HermesFormat vfmt;
+
+/* no screen format here, hermes may not suitable for it */
+
+/* netsurf source bitmap format */
+static HermesFormat nsfmt;
+
+struct s_vdi_sysinfo vdi_sysinfo;
+/* bit depth of framebuffers: */
+static int atari_plot_bpp_virt;
+static struct s_view view;
+
+//static HermesHandle hermes_pal_h; /* hermes palette handle */
+static HermesHandle hermes_cnv_h; /* hermes converter instance handle */
+static HermesHandle hermes_res_h;
+
+//static short prev_vdi_clip[4];
+static struct bitmap snapshot;
+
+VdiHdl atari_plot_vdi_handle = -1;
+unsigned long atari_plot_flags;
+unsigned long atari_font_flags;
+
+typedef bool (*bitmap_convert_fnc)( struct bitmap * img, int x, int y,
+ GRECT * clip, uint32_t bg, uint32_t flags, MFDB *out );
+static bitmap_convert_fnc bitmap_convert;
+
+const char* plot_err_str(int i)
+{
+ return(plot_error_codes[abs(i)]);
+}
+
+/**
+ * Set line drawing color by passing netsurf XBGR "colour" type.
+ *
+ * \param vdih The vdi handle
+ * \param cin The netsurf colour value
+ */
+inline static void vsl_rgbcolor(short vdih, colour cin)
+{
+ #ifdef WITH_8BPP_SUPPORT
+ if( vdi_sysinfo.scr_bpp > 8 ) {
+ #endif
+ RGB1000 c; /* a struct with three (RGB) shorts */
+ rgb_to_vdi1000( (unsigned char*)&cin, &c);
+ vs_color(vdih, OFFSET_CUSTOM_COLOR, (short *)&c);
+ vsl_color(vdih, OFFSET_CUSTOM_COLOR);
+ #ifdef WITH_8BPP_SUPPORT
+ } else {
+ if( vdi_sysinfo.scr_bpp >= 4 ){
+ vsl_color(vdih, RGB_TO_VDI(cin));
+ }
+ else
+ vsl_color(vdih, BLACK);
+ }
+ #endif
+}
+
+/**
+ * Set fill color by passing netsurf XBGR "colour" type.
+ *
+ * \param vdih The vdi handle
+ * \param cin The netsurf colour value
+ */
+inline static void vsf_rgbcolor(short vdih, colour cin)
+{
+ #ifdef WITH_8BPP_SUPPORT
+ if( vdi_sysinfo.scr_bpp > 8 ) {
+ #endif
+ RGB1000 c; /* a struct with three (RGB) shorts */
+ rgb_to_vdi1000( (unsigned char*)&cin, &c);
+ vs_color( vdih, OFFSET_CUSTOM_COLOR, (short *)&c);
+ vsf_color( vdih, OFFSET_CUSTOM_COLOR );
+ #ifdef WITH_8BPP_SUPPORT
+ } else {
+ if( vdi_sysinfo.scr_bpp >= 4 ){
+ vsf_color( vdih, RGB_TO_VDI(cin) );
+ }
+ else
+ vsf_color( vdih, WHITE );
+ }
+ #endif
+}
+
+
+
+/**
+ * Get current visible coords
+ */
+inline static void plot_get_visible_grect(GRECT * out)
+{
+ out->g_x = view.vis_x;
+ out->g_y = view.vis_y;
+ out->g_w = view.vis_w;
+ out->g_h = view.vis_h;
+}
+
+
+
+/* calculate visible area of framebuffer in coords relative to framebuffer */
+/* position */
+/* result: */
+/* this function should calculates an rectangle relative to the plot origin*/
+/* and size. */
+/* If the ploter coords do not fall within the screen region, */
+/* all values of the region are set to zero. */
+inline static void update_visible_rect(void)
+{
+ GRECT screen; // dimensions of the screen
+ GRECT frame; // dimensions of the drawing area
+ GRECT common; // dimensions of intersection of both
+
+ screen.g_x = 0;
+ screen.g_y = 0;
+ screen.g_w = vdi_sysinfo.scr_w;
+ screen.g_h = vdi_sysinfo.scr_h;
+
+ common.g_x = frame.g_x = view.x;
+ common.g_y = frame.g_y = view.y;
+ common.g_w = frame.g_w = view.w;
+ common.g_h = frame.g_h = view.h;
+
+ if (rc_intersect(&screen, &common)) {
+ view.vis_w = common.g_w;
+ view.vis_h = common.g_h;
+ if (view.x < screen.g_x)
+ view.vis_x = frame.g_w - common.g_w;
+ else
+ view.vis_x = 0;
+ if (view.y <screen.g_y)
+ view.vis_y = frame.g_h - common.g_h;
+ else
+ view.vis_y = 0;
+ } else {
+ view.vis_w = view.vis_h = 0;
+ view.vis_x = view.vis_y = 0;
+ }
+}
+
+/* Returns the visible parts of the box (relative coords within framebuffer),*/
+/* relative to screen coords (normally starting at 0,0 ) */
+inline static bool fbrect_to_screen(GRECT box, GRECT * ret)
+{
+ GRECT out, vis, screen;
+
+ screen.g_x = 0;
+ screen.g_y = 0;
+ screen.g_w = vdi_sysinfo.scr_w;
+ screen.g_h = vdi_sysinfo.scr_h;
+
+ /* get visible region: */
+ vis.g_x = view.x;
+ vis.g_y = view.y;
+ vis.g_w = view.w;
+ vis.g_h = view.h;
+
+ if ( !rc_intersect( &screen, &vis ) ) {
+ return( false );
+ }
+ vis.g_x = view.w - vis.g_w;
+ vis.g_y = view.h - vis.g_h;
+
+ /* clip box to visible region: */
+ if( !rc_intersect(&vis, &box) ) {
+ return( false );
+ }
+ out.g_x = box.g_x + view.x;
+ out.g_y = box.g_y + view.y;
+ out.g_w = box.g_w;
+ out.g_h = box.g_h;
+ *ret = out;
+ return ( true );
+}
+
+/* copy an rectangle from the plot buffer to screen */
+/* because this is an on-screen plotter, this is an screen to screen copy. */
+bool plot_copy_rect(GRECT src, GRECT dst)
+{
+ MFDB devmf;
+ MFDB scrmf;
+ short pxy[8];
+ GRECT vis;
+
+ /* clip to visible rect, only needed for onscreen renderer: */
+ plot_get_visible_grect(&vis );
+
+ if( !rc_intersect(&vis, &src) )
+ return(true);
+ if( !rc_intersect(&vis, &dst) )
+ return(true);
+
+ src.g_x = view.x + src.g_x;
+ src.g_y = view.y + src.g_y;
+ dst.g_x = view.x + dst.g_x;
+ dst.g_y = view.y + dst.g_y;
+
+ devmf.fd_addr = NULL;
+ devmf.fd_w = src.g_w;
+ devmf.fd_h = src.g_h;
+ devmf.fd_wdwidth = 0;
+ devmf.fd_stand = 0;
+ devmf.fd_nplanes = 0;
+ devmf.fd_r1 = devmf.fd_r2 = devmf.fd_r3 = 0;
+
+ scrmf.fd_addr = NULL;
+ scrmf.fd_w = dst.g_w;
+ scrmf.fd_h = dst.g_h;
+ scrmf.fd_wdwidth = 0 ;
+ scrmf.fd_stand = 0;
+ scrmf.fd_nplanes = 0;
+ scrmf.fd_r1 = scrmf.fd_r2 = scrmf.fd_r3 = 0;
+
+ pxy[0] = src.g_x;
+ pxy[1] = src.g_y;
+ pxy[2] = pxy[0] + src.g_w-1;
+ pxy[3] = pxy[1] + src.g_h-1;
+ pxy[4] = dst.g_x;
+ pxy[5] = dst.g_y;
+ pxy[6] = pxy[4] + dst.g_w-1;
+ pxy[7] = pxy[5] + dst.g_h-1;
+ plot_lock();
+ vro_cpyfm( atari_plot_vdi_handle, S_ONLY, (short*)&pxy, &devmf, &scrmf);
+ plot_unlock();
+
+ return(true);
+}
+
+/**
+ * Fill the screen info structure.
+ *
+ * \param vdhi The handle
+ * \param[out] info The infor structure to fill.
+ */
+static void read_vdi_sysinfo(short vdih, struct s_vdi_sysinfo * info) {
+
+ unsigned long cookie_EdDI=0; /** \todo this long is being cast to a pointer */
+ short out[300];
+ memset( info, 0, sizeof(struct s_vdi_sysinfo) );
+
+ info->vdi_handle = vdih;
+ if ( tos_getcookie(C_EdDI, (long *)&cookie_EdDI) == C_NOTFOUND ) {
+ info->EdDiVersion = 0;
+ } else {
+ info->EdDiVersion = EdDI_version( (void *)cookie_EdDI );
+ }
+
+ memset( &out, 0, sizeof(short)*300 );
+ vq_extnd( vdih, 0, (short*)&out );
+ info->scr_w = out[0]+1;
+ info->scr_h = out[1]+1;
+ if( out[39] == 2 ) {
+ info->scr_bpp = 1;
+ info->colors = out[39];
+ } else {
+ info->colors = out[39];
+ }
+
+ memset( &out, 0, sizeof(short)*300 );
+ vq_extnd( vdih, 1, (short*)&out );
+ info->scr_bpp = out[4];
+ info->maxpolycoords = out[14];
+ info->maxintin = out[15];
+ if( out[30] & 1 ) {
+ info->rasterscale = true;
+ } else {
+ info->rasterscale = false;
+ }
+
+ switch( info->scr_bpp ) {
+ case 8:
+ info->pixelsize=1;
+ break;
+ case 15:
+ case 16:
+ info->pixelsize=2;
+ break;
+ case 24:
+ info->pixelsize=3;
+ break;
+ case 32:
+ info->pixelsize=4;
+ break;
+ case 64:
+ info->pixelsize=8;
+ break;
+ default:
+ info->pixelsize=1;
+ break;
+
+ }
+ info->pitch = info->scr_w * info->pixelsize;
+ info->vdiformat = ( (info->scr_bpp <= 8) ? VDI_FORMAT_INTER : VDI_FORMAT_PACK);
+ info->screensize = ( info->scr_w * info->pixelsize ) * info->scr_h;
+
+ if( info->EdDiVersion >= EDDI_10 ) {
+ memset( &out, 0, sizeof(short)*300 );
+ vq_scrninfo(vdih, (short*)&out);
+ info->vdiformat = out[0];
+ info->clut = out[1];
+ info->scr_bpp = out[2];
+ info->hicolors = *((unsigned long*) &out[3]);
+ if( info->EdDiVersion >= EDDI_11 ) {
+ info->pitch = out[5];
+ info->screen = (void *) *((unsigned long *) &out[6]);
+ }
+
+ switch( info->clut ) {
+
+ case VDI_CLUT_HARDWARE: {
+
+ }
+ break;
+
+ case VDI_CLUT_SOFTWARE: {
+ int component; /* red, green, blue, alpha, overlay */
+ int num_bit;
+ unsigned short *tmp_p;
+
+ /* We can build masks with info here */
+ tmp_p = (unsigned short *) &out[16];
+ for (component=0; component<5; component++) {
+ for (num_bit=0; num_bit<16; num_bit++) {
+ unsigned short val;
+
+ val = *tmp_p++;
+
+ if (val == 0xffff) {
+ continue;
+ }
+
+ switch(component) {
+ case 0:
+ info->mask_r |= 1<< val;
+ break;
+ case 1:
+ info->mask_g |= 1<< val;
+ break;
+ case 2:
+ info->mask_b |= 1<< val;
+ break;
+ case 3:
+ info->mask_a |= 1<< val;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Remove lower green bits for Intel endian screen */
+ if ((info->mask_g == ((7<<13)|3)) || (info->mask_g == ((7<<13)|7))) {
+ info->mask_g &= ~(7<<13);
+ }
+ break;
+
+ case VDI_CLUT_NONE:
+ break;
+ }
+ }
+}
+
+
+/*
+ Convert an RGB color to an VDI Color
+*/
+inline void rgb_to_vdi1000(unsigned char * in, RGB1000 *out)
+{
+ double r = ((double)in[3]/255); /* prozentsatz red */
+ double g = ((double)in[2]/255); /* prozentsatz green */
+ double b = ((double)in[1]/255); /* prozentsatz blue */
+ out->red = 1000 * r + 0.5;
+ out->green = 1000 * g + 0.5;
+ out->blue = 1000 * b + 0.5;
+ return;
+}
+
+inline void vdi1000_to_rgb(unsigned short * in, unsigned char * out)
+{
+ double r = ((double)in[0]/1000); /* prozentsatz red */
+ double g = ((double)in[1]/1000); /* prozentsatz green */
+ double b = ((double)in[2]/1000); /* prozentsatz blue */
+ out[2] = 255 * r + 0.5;
+ out[1] = 255 * g + 0.5;
+ out[0] = 255 * b + 0.5;
+ return;
+}
+
+
+#ifdef WITH_8BPP_SUPPORT
+/**
+ * Set pixel within an 8 bit VDI standard bitmap.
+ */
+inline static void set_stdpx( MFDB * dst, int wdplanesz, int x, int y, unsigned char val )
+{
+ short * buf;
+ short whichbit = (1<<(15-(x%16)));
+
+ buf = dst->fd_addr;
+ buf += ((dst->fd_wdwidth*(y))+(x>>4));
+
+ *buf = (val&1) ? ((*buf)|(whichbit)) : ((*buf)&~(whichbit));
+
+ buf += wdplanesz;
+ *buf = (val&(1<<1)) ? ((*buf)|(whichbit)) : ((*buf)&~(whichbit));
+
+ buf += wdplanesz;
+ *buf = (val&(1<<2)) ? ((*buf)|(whichbit)) : ((*buf)&~(whichbit));
+
+ buf += wdplanesz;
+ *buf = (val&(1<<3)) ? ((*buf)|(whichbit)) : ((*buf)&~(whichbit));
+
+ buf += wdplanesz;
+ *buf = (val&(1<<4)) ? ((*buf)|(whichbit)) : ((*buf)&~(whichbit));
+
+ buf += wdplanesz;
+ *buf = (val&(1<<5)) ? ((*buf)|(whichbit)) : ((*buf)&~(whichbit));
+
+ buf += wdplanesz;
+ *buf = (val&(1<<6)) ? ((*buf)|(whichbit)) : ((*buf)&~(whichbit));
+
+ buf += wdplanesz;
+ *buf = (val&(1<<7)) ? ((*buf)|(whichbit)) : ((*buf)&~(whichbit));
+}
+
+/**
+ * Read pixel from an 8 bit VDI standard bitmap.
+ */
+inline static unsigned char get_stdpx(MFDB * dst, int wdplanesz, int x, int y)
+{
+ unsigned char ret=0;
+ short * buf;
+ short whichbit = (1<<(15-(x%16)));
+
+ buf = dst->fd_addr;
+ buf += ((dst->fd_wdwidth*(y))+(x>>4));
+
+ if( *buf & whichbit )
+ ret |= 1;
+
+ buf += wdplanesz;
+ if( *buf & whichbit )
+ ret |= 2;
+
+ buf += wdplanesz;
+ if( *buf & whichbit )
+ ret |= 4;
+
+ buf += wdplanesz;
+ if( *buf & whichbit )
+ ret |= 8;
+
+ buf += wdplanesz;
+ if( *buf & whichbit )
+ ret |= 16;
+
+ buf += wdplanesz;
+ if( *buf & whichbit )
+ ret |= 32;
+
+ buf += wdplanesz;
+ if( *buf & whichbit )
+ ret |= 64;
+
+ buf += wdplanesz;
+ if( *buf & whichbit )
+ ret |= 128;
+
+ return( ret );
+}
+
+/*
+ Convert an RGB color into an index into the 216 colors web pallette
+*/
+inline short rgb_to_666_index(unsigned char r, unsigned char g, unsigned char b)
+{
+ short i;
+ unsigned char rgb[3] = {r,g,b};
+ unsigned char tval[3];
+
+ int diff_a, diff_b, diff_c;
+ diff_a = abs(r-g);
+ diff_b = abs(r-b);
+ diff_c = abs(r-b);
+ if( diff_a < 2 && diff_b < 2 && diff_c < 2 ) {
+ if( (r!=0XFF) && (g!=0XFF) && (b!=0XFF) ) {
+ if( ((r&0xF0)>>4) != 0 )
+ //printf("conv gray: %x -> %d\n", ((r&0xF0)>>4) , (OFFSET_CUST_PAL) + ((r&0xF0)>>4) );
+ return( (OFFSET_CUST_PAL - OFFSET_WEB_PAL) + ((r&0xF0)>>4) );
+ }
+ }
+
+ /* convert each 8bit color to 6bit web color: */
+ for( i=0; i<3; i++) {
+ if(0 == rgb[i] % web_std_colors[1] ) {
+ tval[i] = rgb[i] / web_std_colors[1];
+ } else {
+ int pos = ((short)rgb[i] / web_std_colors[1]);
+ if( abs(rgb[i] - web_std_colors[pos]) > abs(rgb[i] - web_std_colors[pos+1]) )
+ tval[i] = pos+1;
+ else
+ tval[i] = pos;
+ }
+ }
+ return(tval[2]*36+tval[1]*6+tval[0]);
+}
+#endif
+
+
+static void dump_vdi_info(short vdih)
+{
+ struct s_vdi_sysinfo temp;
+ read_vdi_sysinfo( vdih, &temp );
+ printf("struct s_vdi_sysinfo {\n");
+ printf(" short vdi_handle: %d\n", temp.vdi_handle);
+ printf(" short scr_w: %d \n", temp.scr_w);
+ printf(" short scr_h: %d\n", temp.scr_h);
+ printf(" short scr_bpp: %d\n", temp.scr_bpp);
+ printf(" int colors: %d\n", temp.colors);
+ printf(" ulong hicolors: %lu\n", temp.hicolors);
+ printf(" short pixelsize: %d\n", temp.pixelsize);
+ printf(" unsigned short pitch: %d\n", temp.pitch);
+ printf(" unsigned short vdiformat: %d\n", temp.vdiformat);
+ printf(" unsigned short clut: %d\n", temp.clut);
+ printf(" void * screen: 0x0%p\n", temp.screen);
+ printf(" unsigned long screensize: %lu\n", temp.screensize);
+ printf(" unsigned long mask_r: 0x0%08lx\n", temp.mask_r);
+ printf(" unsigned long mask_g: 0x0%08lx\n", temp.mask_g);
+ printf(" unsigned long mask_b: 0x0%08lx\n", temp.mask_b);
+ printf(" unsigned long mask_a: 0x0%08lx\n", temp.mask_a);
+ printf(" short maxintin: %d\n", temp.maxintin);
+ printf(" short maxpolycoords: %d\n", temp.maxpolycoords);
+ printf(" unsigned long EdDiVersion: 0x0%03lx\n", temp.EdDiVersion);
+ printf(" unsigned short rasterscale: 0x%2x\n", temp.rasterscale);
+ printf("};\n");
+}
+
+/**
+ * Create an snapshot of the screen image in device format.
+ */
+static MFDB * snapshot_create_native_mfdb(int x, int y, int w, int h)
+{
+ MFDB scr;
+ short pxy[8];
+
+ /* allocate memory for the snapshot */
+ {
+ int scr_stride = MFDB_STRIDE( w );
+ int scr_size = ( ((scr_stride >> 3) * h) * vdi_sysinfo.scr_bpp );
+ if(size_buf_scr == 0 ){
+ /* init screen mfdb */
+ buf_scr.fd_addr = malloc( scr_size );
+ size_buf_scr = scr_size;
+ } else {
+ if( scr_size >size_buf_scr ) {
+ buf_scr.fd_addr = realloc(
+ buf_scr.fd_addr, scr_size
+ );
+ size_buf_scr = scr_size;
+ }
+ }
+ if(buf_scr.fd_addr == NULL ) {
+ size_buf_scr = 0;
+ return( NULL );
+ }
+ buf_scr.fd_nplanes = vdi_sysinfo.scr_bpp;
+ buf_scr.fd_w = scr_stride;
+ buf_scr.fd_h = h;
+ buf_scr.fd_wdwidth = scr_stride >> 4;
+ assert(buf_scr.fd_addr != NULL );
+ }
+ init_mfdb( 0, w, h, 0, &scr );
+ pxy[0] = x;
+ pxy[1] = y;
+ pxy[2] = pxy[0] + w-1;
+ pxy[3] = pxy[1] + h-1;
+ pxy[4] = 0;
+ pxy[5] = 0;
+ pxy[6] = w-1;
+ pxy[7] = h-1;
+ vro_cpyfm(
+ atari_plot_vdi_handle, S_ONLY, (short*)&pxy,
+ &scr, &buf_scr
+ );
+
+ return( &buf_scr );
+}
+
+/**
+ * Create an snapshot of the screen image in VDI standard format (8 bit).
+ */
+static MFDB * snapshot_create_std_mfdb(int x, int y, int w, int h)
+{
+ /* allocate memory for the snapshot */
+ {
+ int scr_stride = MFDB_STRIDE( w );
+ int scr_size = ( ((scr_stride >> 3) * h) * vdi_sysinfo.scr_bpp );
+ if(size_buf_std == 0 ){
+ /* init screen mfdb */
+ buf_std.fd_addr = malloc( scr_size );
+ size_buf_std = scr_size;
+ } else {
+ if( scr_size >size_buf_std ) {
+ buf_std.fd_addr = realloc(
+ buf_std.fd_addr, scr_size
+ );
+ size_buf_std = scr_size;
+ }
+ }
+ if(buf_std.fd_addr == NULL ) {
+ size_buf_std = 0;
+ return( NULL );
+ }
+ buf_std.fd_nplanes = 8;
+ buf_std.fd_w = scr_stride;
+ buf_std.fd_h = h;
+ buf_std.fd_stand = 1;
+ buf_std.fd_wdwidth = scr_stride >> 4;
+ assert(buf_std.fd_addr != NULL );
+ }
+ MFDB * native = snapshot_create_native_mfdb(x,y,w,h );
+ assert( native );
+
+ vr_trnfm(atari_plot_vdi_handle, native, &buf_std);
+ return( &buf_std );
+}
+
+/*
+ * Create an snapshot of the screen in netsurf ABGR format
+ */
+static struct bitmap * snapshot_create(int x, int y, int w, int h)
+{
+ int err;
+ MFDB * native;
+ // uint32_t start = clock();
+
+ // FIXME: This can be optimized a lot.
+ // 1. do not copy the snapshot to the bitmap buffer
+ // when the format of screen and bitmap equals.
+ // just point the bitmap to the native mfdb.
+ // 2. if we have eddi 1.1, we could optimize that further
+ // make snapshot_create_native_mfdb just returning a pointer
+ // to the screen.
+
+ native = snapshot_create_native_mfdb(x, y, w, h );
+
+ if(vfmt.bits == 32 )
+ goto no_copy;
+
+ /* allocate buffer for result bitmap: */
+ if(buf_scr_compat == NULL ) {
+ buf_scr_compat = atari_bitmap_create(w, h, 0);
+ } else {
+ buf_scr_compat = atari_bitmap_realloc( w, h,
+ buf_scr_compat->bpp,
+ w *buf_scr_compat->bpp,
+ BITMAP_GROW,
+ buf_scr_compat );
+ }
+
+ /* convert screen buffer to ns format: */
+ err = Hermes_ConverterRequest( hermes_cnv_h,
+ &vfmt,
+ &nsfmt
+ );
+ assert( err != 0 );
+ err = Hermes_ConverterCopy( hermes_cnv_h,
+ native->fd_addr,
+ 0, /* x src coord of top left in pixel coords */
+ 0, /* y src coord of top left in pixel coords */
+ w, h,
+ native->fd_w * vdi_sysinfo.pixelsize, /* stride as bytes */
+ buf_scr_compat->pixdata,
+ 0, /* x dst coord of top left in pixel coords */
+ 0, /* y dst coord of top left in pixel coords */
+ w, h,
+ atari_bitmap_get_rowstride(buf_scr_compat) /* stride as bytes */
+ );
+ assert( err != 0 );
+ return( (struct bitmap * )buf_scr_compat );
+
+no_copy:
+
+ snapshot.width = w;
+ snapshot.height = h;
+ snapshot.pixdata = native->fd_addr;
+ snapshot.native = *native;
+ snapshot.rowstride = MFDB_STRIDE( w )*4;
+
+ uint32_t row, col;
+ for (row = 0; row<(uint32_t)h; row++) {
+ // fd_w matches stride!
+ uint32_t *rowptr = ((uint32_t*)native->fd_addr + ((row*native->fd_w)));
+ for (col=0; col<(uint32_t)w; col++) {
+ *(rowptr+col) = (*(rowptr+col)<<8);
+ }
+ }
+ return( &snapshot );
+}
+
+/**
+ * Notify the snapshot interface that the last snapshot is no longer in use.
+ */
+static void snapshot_suspend(void)
+{
+ if(size_buf_scr > CONV_KEEP_LIMIT ) {
+ buf_scr.fd_addr = realloc(
+ buf_scr.fd_addr, CONV_KEEP_LIMIT
+ );
+ if(buf_scr.fd_addr != NULL ) {
+ size_buf_scr = CONV_KEEP_LIMIT;
+ } else {
+ size_buf_scr = 0;
+ }
+ }
+
+#ifdef WITH_8BPP_SUPPORT
+ if(size_buf_std > CONV_KEEP_LIMIT ) {
+ buf_std.fd_addr = realloc(
+ buf_std.fd_addr, CONV_KEEP_LIMIT
+ );
+ if(buf_std.fd_addr != NULL ) {
+ size_buf_std = CONV_KEEP_LIMIT;
+ } else {
+ size_buf_std = 0;
+ }
+ }
+#endif
+
+ if(buf_scr_compat != NULL ) {
+ size_t bs = atari_bitmap_buffer_size(buf_scr_compat );
+ if( bs > CONV_KEEP_LIMIT ) {
+ int w = 0;
+ int h = 1;
+ w = (CONV_KEEP_LIMIT /buf_scr_compat->bpp);
+ assert( CONV_KEEP_LIMIT == w*buf_scr_compat->bpp );
+ buf_scr_compat = atari_bitmap_realloc( w, h,
+ buf_scr_compat->bpp,
+ CONV_KEEP_LIMIT, BITMAP_SHRINK,buf_scr_compat
+ );
+ }
+ }
+}
+
+/**
+ * Shut down the snapshot interface.
+ */
+static void snapshot_destroy(void)
+{
+
+ free(buf_scr.fd_addr);
+ if( buf_scr_compat != NULL) {
+ atari_bitmap_destroy(buf_scr_compat);
+ }
+
+ buf_scr.fd_addr = NULL;
+ buf_scr_compat = NULL;
+
+#ifdef WITH_8BPP_SUPPORT
+ free(buf_std.fd_addr);
+ buf_std.fd_addr = NULL;
+#endif
+}
+
+
+inline static uint32_t ablend(uint32_t pixel, uint32_t scrpixel)
+{
+ int opacity = pixel & 0xFF;
+ int transp = 0x100 - opacity;
+ uint32_t rb, g;
+ pixel >>= 8;
+ scrpixel >>= 8;
+ rb = ((pixel & 0xFF00FF) * opacity +
+ (scrpixel & 0xFF00FF) * transp) >> 8;
+ g = ((pixel & 0x00FF00) * opacity +
+ (scrpixel & 0x00FF00) * transp) >> 8;
+
+ return ((rb & 0xFF00FF) | (g & 0xFF00)) << 8;
+}
+
+/*
+ Alpha blends an image, using one pixel as the background.
+ The bitmap receives the result.
+*/
+inline static bool ablend_pixel(struct bitmap * img, uint32_t bg, GRECT * clip)
+{
+ uint32_t * imgrow;
+ int img_x, img_y, img_stride;
+
+ img_stride= atari_bitmap_get_rowstride(img);
+
+ for( img_y = 0; img_y < clip->g_h; img_y++) {
+ imgrow = (uint32_t *)(img->pixdata + (img_stride * img_y));
+ for( img_x = 0; img_x < clip->g_w; img_x++ ) {
+ imgrow[img_x] = ablend( imgrow[img_x], bg );
+ }
+ }
+ return(true);
+}
+
+
+/*
+ Aplha blends the foreground image (img) onto the
+ background images (bg). The background receives the blended
+ image pixels.
+*/
+inline static bool ablend_bitmap( struct bitmap * img, struct bitmap * bg,
+ GRECT * img_clip, GRECT * bg_clip )
+{
+ uint32_t * imgrow;
+ uint32_t * screenrow;
+ int img_x, img_y, bg_x, bg_y, img_stride, bg_stride;
+
+ bg_clip = bg_clip;
+ img_stride = atari_bitmap_get_rowstride(img);
+ bg_stride = atari_bitmap_get_rowstride(bg);
+
+ for( img_y = img_clip->g_y, bg_y = 0; bg_y < img_clip->g_h; bg_y++, img_y++) {
+ imgrow = (uint32_t *)(img->pixdata + (img_stride * img_y));
+ screenrow = (uint32_t *)(bg->pixdata + (bg_stride * bg_y));
+ for( img_x = img_clip->g_x, bg_x = 0; bg_x < img_clip->g_w; bg_x++, img_x++ ) {
+
+ // when the pixel isn't fully transparent,...:
+ if( (imgrow[img_x] & 0x0FF) != 0 ){
+ screenrow[bg_x] = ablend( imgrow[img_x], screenrow[bg_x]);
+ }
+
+ // FIXME, maybe this loop would be faster??:
+ // ---
+ //if( (imgrow[img_x] & 0x0FF) != 0xFF ){
+ // imgrow[bg_x] = ablend( imgrow[img_x], screenrow[bg_x]);
+ //}
+
+ // or maybe even this???
+ // ---
+ //if( (imgrow[img_x] & 0x0FF) == 0xFF ){
+ // screenrow[bg_x] = imgrow[img_x];
+ //} else if( (imgrow[img_x] & 0x0FF) != 0x00 ) {
+ // screenrow[bg_x] = ablend( imgrow[img_x], screenrow[bg_x]);
+ //}
+ }
+ }
+ return(false);
+}
+
+
+#ifdef WITH_8BPP_SUPPORT
+/**
+ * Convert an bitmap to an 8 bit device dependant MFDB
+ * \param img the bitmap (only tested with 32bit bitmaps)
+ * \param x screen coord of the background
+ * \param y screen coord of the background
+ * \param clip the region of the image that get's converted
+ * \param bg the background used for cheap transparency
+ * \param flags
+ * \param out receives the converted bitmap (still owned by the plot API)
+ *
+ */
+static bool bitmap_convert_8(struct bitmap * img, int x,
+ int y, GRECT * clip, uint32_t bg, uint32_t flags,
+ MFDB *out )
+{
+ MFDB native;
+ MFDB stdform;
+ int dststride; /* stride of dest. image */
+ int dstsize; /* size of dest. in byte */
+ int bw, bh;
+ struct bitmap * scrbuf = NULL;
+ bool cache = ( flags & BITMAPF_BUFFER_NATIVE );
+ bool opaque = atari_bitmap_get_opaque( img );
+
+ if( opaque == false ){
+ if( ( (atari_plot_flags & PLOT_FLAG_TRANS) == 0)
+ &&
+ ((flags & (BITMAPF_MONOGLYPH|BITMAPF_BUFFER_NATIVE))==0) ){
+ opaque = true;
+ }
+ }
+
+ assert( clip->g_h > 0 );
+ assert( clip->g_w > 0 );
+
+ bw = atari_bitmap_get_width( img );
+ bh = atari_bitmap_get_height( img );
+
+ // The converted bitmap can be saved for subsequent blits, when
+ // the bitmap is fully opaque
+
+ if( (opaque == true) || (flags & BITMAPF_BUFFER_NATIVE ) ){
+ if( img->converted == true ){
+ *out = img->native;
+ return( 0 );
+ }
+ if( ( flags & BITMAPF_MONOGLYPH ) == 0 ){
+ cache = true;
+ }
+ }
+ if( ( flags & BITMAPF_MONOGLYPH ) != 0 ){
+ assert(cache == false);
+ }
+
+ /* (re)allocate buffer for out image: */
+ /* altough the buffer is named "buf_packed" on 8bit systems */
+ /* it's not... */
+ if( cache == false ){
+ // the size of the output will match the size of the clipping:
+ dststride = MFDB_STRIDE( clip->g_w );
+ dstsize = ( ((dststride >> 3) * clip->g_h) * atari_plot_bpp_virt);
+ if( dstsize > size_buf_packed) {
+ int blocks = (dstsize / (CONV_BLOCK_SIZE-1))+1;
+ if( buf_packed == NULL )
+ buf_packed =(void*)malloc( blocks * CONV_BLOCK_SIZE);
+ else
+ buf_packed =(void*)realloc(buf_packed,blocks * CONV_BLOCK_SIZE);
+ assert( buf_packed );
+ if( buf_packed == NULL ) {
+ return( 0-ERR_NO_MEM );
+ }
+ size_buf_packed = blocks * CONV_BLOCK_SIZE;
+ }
+ native.fd_addr = buf_packed;
+ }
+ else {
+ // the output image will be completly saved, so size of the output
+ // image will match the input image size.
+ dststride = MFDB_STRIDE( bw );
+ dstsize = ( ((dststride >> 3) * bh) * atari_plot_bpp_virt);
+ assert( out->fd_addr == NULL );
+ native.fd_addr = (void*)malloc( dstsize );
+ if (native.fd_addr == NULL){
+ if (scrbuf != NULL)
+ atari_bitmap_destroy(scrbuf);
+ return( 0-ERR_NO_MEM );
+ }
+ }
+
+
+ /*
+ on 8 bit systems we must convert the TC (ABGR) image
+ to vdi standard format. ( only tested for 256 colors )
+ and then convert it to native format with v_trnfm()
+ */
+ // realloc mem for stdform
+ if( opaque == false ){
+ // point image to snapshot buffer, otherwise allocate mem
+ MFDB * bg = snapshot_create_std_mfdb(x, y, clip->g_w, clip->g_h);
+ stdform.fd_addr = bg->fd_addr;
+ bh = clip->g_h;
+ } else {
+ if( dstsize > size_buf_planar) {
+ int blocks = (dstsize / (CONV_BLOCK_SIZE-1))+1;
+ if( buf_planar == NULL )
+ buf_planar =(void*)malloc( blocks * CONV_BLOCK_SIZE );
+ else
+ buf_planar =(void*)realloc(buf_planar, blocks * CONV_BLOCK_SIZE);
+ assert(buf_planar);
+ if( buf_planar == NULL ) {
+ return( 0-ERR_NO_MEM );
+ }
+ size_buf_planar = blocks * CONV_BLOCK_SIZE;
+ }
+ stdform.fd_addr = buf_planar;
+ }
+ stdform.fd_w = dststride;
+ stdform.fd_h = bh;
+ stdform.fd_wdwidth = dststride >> 4;
+ stdform.fd_stand = 1;
+ stdform.fd_nplanes = (short)atari_plot_bpp_virt;
+ stdform.fd_r1 = stdform.fd_r2 = stdform.fd_r3 = 0;
+
+ int img_stride = atari_bitmap_get_rowstride(img);
+ uint32_t prev_pixel = 0x12345678; //TODO: check for collision in first pixel
+ unsigned long col = 0;
+ unsigned char val = 0;
+ uint32_t * row;
+ uint32_t pixel;
+ int wdplanesize = stdform.fd_wdwidth*stdform.fd_h;
+
+ if( opaque == false ){
+ // apply transparency and convert to vdi std format
+ unsigned long bgcol = 0;
+ unsigned char prev_col = 0;
+ for( y=0; y<clip->g_h; y++ ){
+ row = (uint32_t *)(img->pixdata + (img_stride * (y+clip->g_y)));
+ for( x=0; x<clip->g_w; x++ ){
+ pixel = row[x+clip->g_x];
+ if( (pixel&0xFF) == 0 ){
+ continue;
+ }
+ if( (pixel&0xFF) < 0xF0 ){
+ col = get_stdpx( &stdform, wdplanesize,x,y );
+ if( (col != prev_col) || (y == 0) )
+ bgcol = (((rgb_lookup[col][2] << 16) | (rgb_lookup[col][1] << 8) | (rgb_lookup[col][0]))<<8);
+ if( prev_col != col || prev_pixel != pixel ){
+ prev_col = col;
+ pixel = ablend( pixel, bgcol );
+ prev_pixel = pixel;
+ pixel = pixel >> 8;
+ /* convert pixel value to vdi color index: */
+ col = ( ((pixel&0xFF)<<16)
+ | (pixel&0xFF00)
+ | ((pixel&0xFF0000)>>16) );
+ val = RGB_TO_VDI( col );
+ }
+ set_stdpx( &stdform, wdplanesize, x, y, val );
+ } else {
+ if( pixel != prev_pixel ){
+ /* convert pixel value to vdi color index: */
+ pixel = pixel >> 8;
+ col = ( ((pixel&0xFF)<<16)
+ | (pixel&0xFF00)
+ | ((pixel&0xFF0000)>>16) );
+ val = RGB_TO_VDI( col );
+ prev_pixel = pixel;
+ }
+ set_stdpx( &stdform, wdplanesize, x, y, val );
+ }
+ }
+ }
+ // adjust output position:
+ clip->g_x = 0;
+ clip->g_y = 0;
+ } else {
+ // convert the whole image data to vdi std format.
+ for( y=0; y < bh; y++ ){
+ row = (uint32_t *)(img->pixdata + (img_stride * y));
+ for( x=0; x < bw; x++ ){
+ pixel = row[x];
+ if( pixel != prev_pixel ){
+ /* convert pixel value to vdi color index: */
+ pixel = pixel >> 8;
+ col = ( ((pixel&0xFF)<<16)
+ | (pixel&0xFF00)
+ | ((pixel&0xFF0000)>>16) );
+ val = RGB_TO_VDI( col );
+ prev_pixel = pixel;
+ }
+ set_stdpx( &stdform, wdplanesize, x, y, val );
+ }
+ }
+ }
+
+ // convert into native format:
+ native.fd_w = stdform.fd_w;
+ native.fd_h = stdform.fd_h;
+ native.fd_wdwidth = stdform.fd_wdwidth;
+ native.fd_stand = 0;
+ native.fd_nplanes = (short)atari_plot_bpp_virt;
+ native.fd_r1 = native.fd_r2 = native.fd_r3 = 0;
+ vr_trnfm(atari_plot_vdi_handle, &stdform, &native );
+ *out = native;
+ if( cache == true ){
+ img->native = native;
+ img->converted = true;
+ }
+
+ return(0);
+}
+#endif
+
+
+/*
+*
+* Convert bitmap to the native screen format
+* img: the bitmap
+* x: coordinate where the bitmap REGION (described in clip)
+* shall be drawn (screen coords)
+* y: coordinate where the bitmap REGION (described in clip)
+* shall be drawn (screen coords)
+* clip: which area of the bitmap shall be drawn
+* bg: background color
+* flags: blit flags
+* out: the result MFDB
+*/
+static bool bitmap_convert_tc(struct bitmap * img, int x, int y,
+ GRECT * clip, uint32_t bg, uint32_t flags, MFDB *out )
+{
+ int dststride; /* stride of dest. image */
+ int dstsize; /* size of dest. in byte */
+ int err;
+ int bw, bh;
+ struct bitmap * scrbuf = NULL;
+ struct bitmap * source = NULL;
+ bool cache = ( flags & BITMAPF_BUFFER_NATIVE );
+ bool opaque = atari_bitmap_get_opaque( img );
+
+ if( opaque == false ){
+ if( ( (atari_plot_flags & PLOT_FLAG_TRANS) == 0)
+ &&
+ ((flags & (BITMAPF_MONOGLYPH|BITMAPF_BUFFER_NATIVE))==0) ){
+ opaque = true;
+ }
+ }
+
+
+ assert( clip->g_h > 0 );
+ assert( clip->g_w > 0 );
+
+ bw = atari_bitmap_get_width( img );
+ bh = atari_bitmap_get_height( img );
+
+ // The converted bitmap can be saved for subsequent blits, WHEN:
+ // A.) the bitmap is fully opaque OR
+ // B.) the bitmap is completly inside the window
+ // the latter one is important for alpha blits,
+ // because we must get the window background to apply transparency
+ // If the image is not completly within the window,
+ // we can't get the whole background for the image.
+ // this only works if the image isn't used at several different places.
+ // In fact in case of alpha bitmap caching it is only used for the
+ // toolbar buttons right now.
+
+ if( (opaque == true) || (flags & BITMAPF_BUFFER_NATIVE ) ){
+ if( img->converted == true ){
+ *out = img->native;
+ return( 0 );
+ }
+ if( ( flags & BITMAPF_MONOGLYPH ) == 0 ){
+ cache = true;
+ }
+ }
+
+ /* rem. if eddi xy is installed, we could directly access the screen! */
+ /* apply transparency to the image: */
+ if (( opaque == false )) {
+ /* copy the screen to an temp buffer: */
+ if ((flags & BITMAPF_BUFFER_NATIVE) == 0) {
+ scrbuf = snapshot_create(x, y, clip->g_w, clip->g_h);
+ if( scrbuf != NULL ) {
+
+ assert( clip->g_w <= bw );
+ assert( clip->g_h <= bh );
+
+ // copy blended pixels to the screen buffer:
+ ablend_bitmap( img, scrbuf, clip, NULL );
+ /* adjust size which gets converted: */
+ bw = clip->g_w;
+ bh = clip->g_h;
+ /* adjust output position: */
+ clip->g_x = 0;
+ clip->g_y = 0;
+ /* set the source of conversion: */
+ source = scrbuf;
+ }
+ } else {
+ /*
+ The whole bitmap can be transformed to an mfdb
+ (and get's cached)
+ */
+ GRECT region = { 0, 0, bw, bh };
+ ablend_pixel( img, bg, &region );
+ source = img;
+ }
+ } else {
+ source = img;
+ }
+ /* (re)allocate buffer for converted image: */
+ dststride = MFDB_STRIDE(bw);
+ dstsize = ( ((dststride >> 3) * bh) * atari_plot_bpp_virt );
+ if (cache == false) {
+ if (dstsize > size_buf_packed) {
+ int blocks = (dstsize / (CONV_BLOCK_SIZE-1))+1;
+ if( buf_packed == NULL )
+ buf_packed =(void*)malloc( blocks * CONV_BLOCK_SIZE );
+ else
+ buf_packed =(void*)realloc(buf_packed,
+ blocks * CONV_BLOCK_SIZE);
+ assert( buf_packed );
+ if( buf_packed == NULL ) {
+ if( scrbuf != NULL )
+ atari_bitmap_destroy( scrbuf );
+ return( 0-ERR_NO_MEM );
+ }
+ size_buf_packed = blocks * CONV_BLOCK_SIZE;
+ }
+ out->fd_addr = buf_packed;
+ } else {
+ assert( out->fd_addr == NULL );
+ out->fd_addr = (void*)malloc( dstsize );
+ if( out->fd_addr == NULL ){
+ if( scrbuf != NULL )
+ atari_bitmap_destroy( scrbuf );
+ return( 0-ERR_NO_MEM );
+ }
+ }
+
+ out->fd_w = dststride;
+ out->fd_h = bh;
+ out->fd_wdwidth = dststride >> 4;
+ out->fd_stand = 0;
+ out->fd_nplanes = (short)atari_plot_bpp_virt;
+ out->fd_r1 = out->fd_r2 = out->fd_r3 = 0;
+
+ err = Hermes_ConverterRequest(
+ hermes_cnv_h,
+ &nsfmt,
+ &vfmt
+ );
+ assert( err != 0 );
+
+ // FIXME: here we can use the same optimization which is used for
+ // the snapshot creation.
+
+ /* convert image to virtual format: */
+ err = Hermes_ConverterCopy( hermes_cnv_h,
+ source->pixdata,
+ 0, /* x src coord of top left in pixel coords */
+ 0, /* y src coord of top left in pixel coords */
+ bw, bh,
+ source->rowstride, /* stride as bytes */
+ out->fd_addr,
+ 0, /* x dst coord of top left in pixel coords */
+ 0, /* y dst coord of top left in pixel coords */
+ bw, bh,
+ (dststride >> 3) * atari_plot_bpp_virt /* stride as bytes */
+ );
+ assert( err != 0 );
+
+ if( cache == true ){
+ img->native = *out;
+ img->converted = true;
+ }
+ return( 0 );
+
+}
+
+ inline static void convert_bitmap_done(void)
+{
+ if (size_buf_packed > CONV_KEEP_LIMIT) {
+ /* free the mem if it was an large allocation ... */
+ buf_packed = realloc(buf_packed, CONV_KEEP_LIMIT);
+ size_buf_packed = CONV_KEEP_LIMIT;
+ }
+}
+
+
+bool plot_blit_bitmap(struct bitmap * bmp, int x, int y,
+ unsigned long bg, unsigned long flags )
+{
+ MFDB src_mf;
+ MFDB scrmf;
+ short pxy[8];
+ GRECT off, clip, vis;
+ int screen_x, screen_y;
+
+ src_mf.fd_addr = NULL;
+ scrmf.fd_addr = NULL;
+
+ off.g_x = x;
+ off.g_y = y;
+ off.g_h = bmp->height;
+ off.g_w = bmp->width;
+
+ // clip plotter clip rectangle:
+ clip.g_x = view.clipping.x0;
+ clip.g_y = view.clipping.y0;
+ clip.g_w = view.clipping.x1 - view.clipping.x0;
+ clip.g_h = view.clipping.y1 - view.clipping.y0;
+
+ if( !rc_intersect( &clip, &off) ) {
+ return(true);
+ }
+
+ // clip the visible rectangle of the plot area
+ // this is the area of the plotter which falls into
+ // screen region:
+ plot_get_visible_grect(&vis);
+ if( !rc_intersect( &vis, &off) ) {
+ return(true);
+ }
+
+ screen_x = view.x + off.g_x;
+ screen_y = view.y + off.g_y;
+
+ // convert the clipping relative to bitmap:
+ off.g_x = off.g_x - x;
+ off.g_y = off.g_y - y;
+ assert( (off.g_x >= 0) && (off.g_y >= 0) );
+
+ /* Convert the Bitmap to native screen format - ready for output. */
+ /* This includes blending transparent pixels: */
+ if (bitmap_convert(bmp, screen_x, screen_y, &off, bg, flags, &src_mf)
+ != 0 ) {
+ return(true);
+ }
+
+ // setup the src region:
+ pxy[0] = off.g_x;
+ pxy[1] = off.g_y;
+ pxy[2] = off.g_x + off.g_w-1;
+ pxy[3] = off.g_y + off.g_h-1;
+
+ // setup the target region:
+ pxy[4] = screen_x;
+ pxy[5] = screen_y;
+ pxy[6] = screen_x + off.g_w-1;
+ pxy[7] = screen_y + off.g_h-1;
+
+ vro_cpyfm(atari_plot_vdi_handle, S_ONLY, (short*)&pxy, &src_mf, &scrmf);
+ convert_bitmap_done();
+ snapshot_suspend();
+ return(true);
+}
+
+bool plot_blit_mfdb(GRECT * loc, MFDB * insrc, short fgcolor,
+ uint32_t flags)
+{
+ MFDB screen;
+// MFDB tran;
+ MFDB * src;
+ short pxy[8];
+ short c[2] = {fgcolor, 0};
+ GRECT off;
+
+ plot_get_clip_grect(&off);
+ if( rc_intersect(loc, &off) == 0 ){
+ return( 1 );
+ }
+
+ init_mfdb( 0, loc->g_w, loc->g_h, 0, &screen );
+//
+// if( insrc->fd_stand){
+// printf("st\n");
+// int size = init_mfdb( insrc->fd_nplanes, loc->g_w, loc->g_h,
+// MFDB_FLAG_NOALLOC,
+// &tran
+// );
+// if( size_buf_scr == 0 ){
+// buf_scr.fd_addr = malloc( size );
+// size_buf_scr = size;
+// } else {
+// if( size > size_buf_scr ) {
+// buf_scr.fd_addr = realloc(
+// buf_scr.fd_addr, size
+// );
+// size_buf_scr = size;
+// }
+// }
+// tran.fd_addr = buf_scr.fd_addr;
+// vr_trnfm(atari_plot_vdi_handle, insrc, &tran );
+// src = &tran;
+// } else {
+ src = insrc;
+// }
+
+ pxy[0] = off.g_x - loc->g_x;
+ pxy[1] = off.g_y - loc->g_y;
+ pxy[2] = pxy[0] + off.g_w - 1;
+ pxy[3] = pxy[1] + off.g_h - 1;
+ pxy[4] = view.x + off.g_x;
+ pxy[5] = view.y + off.g_y;
+ pxy[6] = pxy[4] + off.g_w-1;
+ pxy[7] = pxy[5] + off.g_h-1;
+
+
+ if( flags & PLOT_FLAG_TRANS && src->fd_nplanes == 1){
+ vrt_cpyfm(atari_plot_vdi_handle, MD_REPLACE/*MD_TRANS*/, (short*)pxy, src, &screen, (short*)&c);
+ } else {
+ /* this method only plots transparent bitmaps, right now... */
+ }
+ return( 1 );
+}
+
+/*
+Init screen and font driver objects.
+Returns non-zero value > -1 when the objects could be succesfully created.
+Returns value < 0 to indicate an error
+*/
+
+int plot_init(char * fdrvrname)
+{
+
+ GRECT loc_pos= {0,0,360,400};
+ int err=0;
+
+ if( nsoption_int(atari_dither) == 1)
+ atari_plot_flags |= PLOT_FLAG_DITHER;
+ if( nsoption_int(atari_transparency) == 1 )
+ atari_plot_flags |= PLOT_FLAG_TRANS;
+ if( nsoption_int(atari_font_monochrom) == 1 )
+ atari_font_flags |= FONTPLOT_FLAG_MONOGLYPH;
+
+ if(atari_plot_vdi_handle == -1) {
+
+ short dummy;
+ short work_in[12] = {Getrez()+2,1,1,1,1,1,1,1,1,1,2,1};
+ short work_out[57];
+ atari_plot_vdi_handle=graf_handle(&dummy, &dummy, &dummy, &dummy);
+ v_opnvwk(work_in, &atari_plot_vdi_handle, work_out);
+ LOG("Plot VDI handle: %d", atari_plot_vdi_handle);
+ }
+ read_vdi_sysinfo(atari_plot_vdi_handle, &vdi_sysinfo);
+ if(verbose_log) {
+ dump_vdi_info(atari_plot_vdi_handle) ;
+ dump_font_drivers();
+ }
+
+ fplotter = new_font_plotter(atari_plot_vdi_handle, fdrvrname,
+ atari_font_flags, &err);
+ if(err) {
+ const char * desc = plot_err_str(err);
+ die(("Unable to load font plotter %s -> %s", fdrvrname, desc ));
+ }
+
+ memset(&view, 0, sizeof(struct s_view));
+ atari_plot_bpp_virt = vdi_sysinfo.scr_bpp;
+ view.x = loc_pos.g_x;
+ view.y = loc_pos.g_y;
+ view.w = loc_pos.g_w;
+ view.h = loc_pos.g_h;
+ size_buf_packed = 0;
+ size_buf_planar = 0;
+ buf_packed = NULL;
+ buf_planar = NULL;
+ if( vdi_sysinfo.vdiformat == VDI_FORMAT_PACK ) {
+ atari_plot_bpp_virt = vdi_sysinfo.scr_bpp;
+ } else {
+ atari_plot_bpp_virt = 8;
+ }
+
+ plot_set_scale(1.0);
+ update_visible_rect();
+
+ struct rect clip;
+ clip.x0 = 0;
+ clip.y0 = 0;
+ clip.x1 = view.w;
+ clip.y1 = view.h;
+ plot_clip(&clip);
+
+ assert(Hermes_Init());
+
+#ifdef WITH_8BPP_SUPPORT
+ bitmap_convert = (vdi_sysinfo.scr_bpp > 8) ? bitmap_convert_tc : bitmap_convert_8;
+
+ /* Setup color lookup tables and palette */
+ unsigned char rgbcol[4];
+ if( vdi_sysinfo.scr_bpp <= 8 ){
+ unsigned char graytone=0;
+ int i;
+ for( i=0; i<=255; i++ ) {
+
+ // get the current color and save it for restore:
+ vq_color(atari_plot_vdi_handle, i, 1, (unsigned short*)&sys_pal[i][0] );
+ if( i<OFFSET_WEB_PAL ) {
+ pal[i][0] = sys_pal[i][0];
+ pal[i][1] = sys_pal[i][1];
+ pal[i][2] = sys_pal[i][2];
+ } else if( vdi_sysinfo.scr_bpp >= 8 ) {
+ if ( i < OFFSET_CUST_PAL ){
+ pal[i][0] = vdi_web_pal[i-OFFSET_WEB_PAL][0];
+ pal[i][1] = vdi_web_pal[i-OFFSET_WEB_PAL][1];
+ pal[i][2] = vdi_web_pal[i-OFFSET_WEB_PAL][2];
+ //set the new palette color to websafe value:
+ vs_color(atari_plot_vdi_handle, i, &pal[i][0]);
+ }
+ if( i >= OFFSET_CUST_PAL && i<OFFSET_CUST_PAL+16 ) {
+ /* here we define 20 additional gray colors... */
+ rgbcol[1] = rgbcol[2] = rgbcol[3] = ((graytone&0x0F) << 4);
+ rgb_to_vdi1000( &rgbcol[0], &pal[i][0] );
+ vs_color(atari_plot_vdi_handle, i, &pal[i][0]);
+ graytone++;
+ }
+
+ }
+ vdi1000_to_rgb( &pal[i][0], &rgb_lookup[i][0] );
+ }
+
+ } else {
+ /* no need to change the palette - its application specific */
+ }
+#else
+ bitmap_convert = bitmap_convert_tc;
+#endif
+
+ /* Setup Hermes conversion handles */
+ unsigned long hermesflags = (atari_plot_flags & PLOT_FLAG_DITHER) ? HERMES_CONVERT_DITHER : 0;
+ hermes_cnv_h = Hermes_ConverterInstance(hermesflags);
+ assert( hermes_cnv_h );
+ hermes_res_h = Hermes_ConverterInstance(hermesflags);
+ assert( hermes_res_h );
+
+ /* set up the src & dst format: */
+ /* netsurf uses RGBA ... */
+ nsfmt.a = 0xFFUL;
+ nsfmt.b = 0x0FF00UL;
+ nsfmt.g = 0x0FF0000UL;
+ nsfmt.r = 0x0FF000000UL;
+ nsfmt.bits = 32;
+ nsfmt.indexed = false;
+ nsfmt.has_colorkey = false;
+
+ vfmt.r = vdi_sysinfo.mask_r;
+ vfmt.g = vdi_sysinfo.mask_g;
+ vfmt.b = vdi_sysinfo.mask_b;
+ vfmt.a = vdi_sysinfo.mask_a;
+ vfmt.bits = atari_plot_bpp_virt;
+ vfmt.indexed = (atari_plot_bpp_virt <= 8) ? 1 : 0;
+ vfmt.has_colorkey = 0;
+
+ return( err );
+}
+
+int plot_finalise( void )
+{
+
+ delete_font_plotter(fplotter);
+
+#ifdef WITH_8BPP_SUPPORT
+ if (vfmt.indexed) {
+ int i;
+ for (i=OFFSET_WEB_PAL; i<OFFSET_CUST_PAL+16; i++) {
+ vs_color(atari_plot_vdi_handle, i, &sys_pal[i][0]);
+ }
+ }
+#endif
+
+ /* close Hermes stuff: */
+ Hermes_ConverterReturn(hermes_cnv_h);
+ Hermes_Done();
+
+ /* free up temporary buffers */
+ free(buf_packed );
+ free(buf_planar);
+ snapshot_destroy();
+
+ return 0;
+}
+
+bool plot_lock(void)
+{
+ if ((atari_plot_flags & PLOT_FLAG_LOCKED) != 0)
+ return(true);
+ if( !wind_update(BEG_UPDATE|0x100) )
+ return(false);
+ if( !wind_update(BEG_MCTRL|0x100) ){
+ wind_update(END_UPDATE);
+ return(false);
+ }
+ atari_plot_flags |= PLOT_FLAG_LOCKED;
+ graf_mouse(M_OFF, NULL);
+ return(true);
+}
+
+bool plot_unlock(void)
+{
+ if( (atari_plot_flags & PLOT_FLAG_LOCKED) == 0 )
+ return(true);
+ wind_update(END_MCTRL);
+ wind_update(END_UPDATE);
+ graf_mouse(M_ON, NULL);
+ vs_clip_off(atari_plot_vdi_handle);
+ atari_plot_flags &= ~PLOT_FLAG_LOCKED;
+ return(false);
+}
+
+bool plot_rectangle(int x0, int y0, int x1, int y1,
+ const plot_style_t *pstyle )
+{
+ short pxy[4];
+ GRECT r, rclip, sclip;
+ int sw = pstyle->stroke_width;
+ uint32_t lt;
+
+ /* current canvas clip: */
+ rclip.g_x = view.clipping.x0;
+ rclip.g_y = view.clipping.y0;
+ rclip.g_w = view.clipping.x1 - view.clipping.x0;
+ rclip.g_h = view.clipping.y1 - view.clipping.y0;
+
+ /* physical clipping: */
+ sclip.g_x = rclip.g_x;
+ sclip.g_y = rclip.g_y;
+ sclip.g_w = view.vis_w;
+ sclip.g_h = view.vis_h;
+
+ rc_intersect(&sclip, &rclip);
+ r.g_x = x0;
+ r.g_y = y0;
+ r.g_w = x1 - x0;
+ r.g_h = y1 - y0;
+
+ if (!rc_intersect( &rclip, &r )) {
+ return(true);
+ }
+ if (pstyle->stroke_type != PLOT_OP_TYPE_NONE) {
+ /*
+ manually draw the line, because we do not need vdi clipping
+ for vertical / horizontal line draws.
+ */
+ if( sw == 0)
+ sw = 1;
+
+ NSLT2VDI(lt, pstyle);
+ vsl_type(atari_plot_vdi_handle, (lt&0x0F));
+ /*
+ if the line style is not available within VDI system,
+ define own style:
+ */
+ if( (lt&0x0F) == 7 ){
+ vsl_udsty(atari_plot_vdi_handle, ((lt&0xFFFF00) >> 8));
+ }
+ vsl_width(atari_plot_vdi_handle, (short)sw );
+ vsl_rgbcolor(atari_plot_vdi_handle, pstyle->stroke_colour);
+ /* top border: */
+ if( r.g_y == y0){
+ pxy[0] = view.x + r.g_x;
+ pxy[1] = view.y + r.g_y ;
+ pxy[2] = view.x + r.g_x + r.g_w;
+ pxy[3] = view.y + r.g_y;
+ v_pline(atari_plot_vdi_handle, 2, (short *)&pxy);
+ }
+
+ /* right border: */
+ if( r.g_x + r.g_w == x1 ){
+ pxy[0] = view.x + r.g_x + r.g_w;
+ pxy[1] = view.y + r.g_y;
+ pxy[2] = view.x + r.g_x + r.g_w;
+ pxy[3] = view.y + r.g_y + r.g_h;
+ v_pline(atari_plot_vdi_handle, 2, (short *)&pxy);
+ }
+
+ /* bottom border: */
+ if( r.g_y+r.g_h == y1 ){
+ pxy[0] = view.x + r.g_x;
+ pxy[1] = view.y + r.g_y+r.g_h;
+ pxy[2] = view.x + r.g_x+r.g_w;
+ pxy[3] = view.y + r.g_y+r.g_h;
+ v_pline(atari_plot_vdi_handle, 2, (short *)&pxy);
+ }
+
+ /* left border: */
+ if( r.g_x == x0 ){
+ pxy[0] = view.x + r.g_x;
+ pxy[1] = view.y + r.g_y;
+ pxy[2] = view.x + r.g_x;
+ pxy[3] = view.y + r.g_y + r.g_h;
+ v_pline(atari_plot_vdi_handle, 2, (short *)&pxy);
+ }
+ }
+
+ if( pstyle->fill_type != PLOT_OP_TYPE_NONE ){
+ short stroke_width = (short)(pstyle->stroke_type != PLOT_OP_TYPE_NONE) ?
+ pstyle->stroke_width : 0;
+
+ vsf_rgbcolor(atari_plot_vdi_handle, pstyle->fill_colour);
+ vsf_perimeter(atari_plot_vdi_handle, 0);
+ vsf_interior(atari_plot_vdi_handle, FIS_SOLID);
+
+
+ pxy[0] = view.x + r.g_x + stroke_width;
+ pxy[1] = view.y + r.g_y + stroke_width;
+ pxy[2] = view.x + r.g_x + r.g_w -1 - stroke_width;
+ pxy[3] = view.y + r.g_y + r.g_h -1 - stroke_width;
+
+ vsf_style(atari_plot_vdi_handle, 1);
+ v_bar(atari_plot_vdi_handle, (short*)&pxy);
+ }
+ return (true);
+}
+
+bool plot_line(int x0, int y0, int x1, int y1,
+ const plot_style_t *pstyle )
+{
+ short pxy[4];
+ uint32_t lt;
+ int sw = pstyle->stroke_width;
+
+ if((x0 < 0 && x1 < 0) || (y0 < 0 && y1 < 0)){
+ return(true);
+ }
+
+ pxy[0] = view.x + MAX(0,x0);
+ pxy[1] = view.y + MAX(0,y0);
+ pxy[2] = view.x + MAX(0,x1);
+ pxy[3] = view.y + MAX(0,y1);
+
+ if((y0 > view.h-1) && (y1 > view.h-1))
+ return(true);
+
+ //printf("view: %d,%d,%d,%d\n", view.x, view.y, view.w, view.h);
+ //printf("line: %d,%d,%d,%d\n", x0, y0, x1, y1);
+
+
+ //plot_vdi_clip(true);
+ if( sw == 0)
+ sw = 1;
+ NSLT2VDI(lt, pstyle)
+ vsl_type(atari_plot_vdi_handle, (lt&0x0F));
+ /* if the line style is not available within VDI system,define own style: */
+ if( (lt&0x0F) == 7 ){
+ vsl_udsty(atari_plot_vdi_handle, ((lt&0xFFFF00) >> 8));
+ }
+ vsl_width(atari_plot_vdi_handle, (short)sw);
+ vsl_rgbcolor(atari_plot_vdi_handle, pstyle->stroke_colour);
+ v_pline(atari_plot_vdi_handle, 2, (short *)&pxy );
+ //plot_vdi_clip(false);
+ return (true);
+}
+
+static bool plot_polygon(const int *p, unsigned int n,
+ const plot_style_t *pstyle)
+{
+ short pxy[n*2];
+ unsigned int i=0;
+ if (vdi_sysinfo.maxpolycoords > 0)
+ assert( (signed int)n < vdi_sysinfo.maxpolycoords);
+
+ vsf_interior(atari_plot_vdi_handle, FIS_SOLID);
+ vsf_style(atari_plot_vdi_handle, 1);
+ for (i = 0; i<n*2; i=i+2) {
+ pxy[i] = (short)view.x+p[i];
+ pxy[i+1] = (short)view.y+p[i+1];
+ }
+ if (pstyle->fill_type == PLOT_OP_TYPE_SOLID) {
+ vsf_rgbcolor(atari_plot_vdi_handle, pstyle->fill_colour);
+ v_fillarea(atari_plot_vdi_handle, n, (short*)&pxy);
+ } else {
+ pxy[n*2]=pxy[0];
+ pxy[n*2+1]=pxy[1];
+ vsl_rgbcolor(atari_plot_vdi_handle, pstyle->stroke_colour);
+ v_pline(atari_plot_vdi_handle, n+1, (short *)&pxy);
+ }
+
+ return ( true );
+}
+
+/***
+ * Set plot origin and canvas size
+ * \param x the x origin
+ * \param y the y origin
+ * \param w the width of the plot area
+ * \param h the height of the plot area
+ */
+bool plot_set_dimensions(int x, int y, int w, int h)
+{
+ bool doupdate = false;
+ struct rect newclip = {0, 0, w, h};
+ GRECT absclip = {x, y, w, h};
+
+ if (!(w == view.w && h == view.h)) {
+ view.w = (short)w;
+ view.h = (short)h;
+ doupdate = true;
+ }
+ if (!(x == view.x && y == view.y)) {
+ view.x = (short)x;
+ view.y = (short)y;
+ doupdate = true;
+ }
+ if (doupdate==true)
+ update_visible_rect();
+
+ //dbg_rect("plot_set_dimensions", &newclip);
+
+ plot_set_abs_clipping(&absclip);
+ plot_clip(&newclip);
+ return(true);
+}
+
+/***
+ * Get current canvas size
+ * \param dst the GRECT * which receives the canvas size
+ *
+ */
+bool plot_get_dimensions(GRECT *dst)
+{
+ dst->g_x = view.x;
+ dst->g_y = view.y;
+ dst->g_w = view.w;
+ dst->g_h = view.h;
+ return(true);
+}
+
+/**
+ * set scale of plotter.
+ * \param scale the new scale value
+ * \return the old scale value
+ */
+
+float plot_set_scale(float scale)
+{
+ float ret = view.scale;
+
+ view.scale = scale;
+
+ return(ret);
+}
+
+float plot_get_scale(void)
+{
+ return(view.scale);
+}
+
+
+/**
+ *
+ * Subsequent calls to plot_clip will be clipped by the absolute clip.
+ * \param area the maximum clipping rectangle (absolute screen coords)
+ *
+*/
+void plot_set_abs_clipping(const GRECT *area)
+{
+ GRECT canvas;
+
+ plot_get_dimensions(&canvas);
+
+ if(!rc_intersect(area, &canvas)){
+ view.abs_clipping.x0 = 0;
+ view.abs_clipping.x1 = 0;
+ view.abs_clipping.y0 = 0;
+ view.abs_clipping.y1 = 0;
+ }
+ else {
+ view.abs_clipping.x0 = area->g_x;
+ view.abs_clipping.x1 = area->g_x + area->g_w;
+ view.abs_clipping.y0 = area->g_y;
+ view.abs_clipping.y1 = area->g_y + area->g_h;
+ }
+}
+
+
+/***
+ * Get the maximum clip extent, in absolute screen coords
+ * \param dst the structure that receives the absolute clipping
+ */
+void plot_get_abs_clipping(struct rect *dst)
+{
+ *dst = view.abs_clipping;
+}
+
+
+/***
+ * Get the maximum clip extent, in absolute screen coords
+ * \param dst the structure that receives the absolute clipping
+ */
+void plot_get_abs_clipping_grect(GRECT *dst)
+{
+ dst->g_x = view.abs_clipping.x0;
+ dst->g_w = view.abs_clipping.x1 - view.abs_clipping.x0;
+ dst->g_y = view.abs_clipping.y0;
+ dst->g_h = view.abs_clipping.y1 - view.abs_clipping.y0;
+}
+
+bool plot_clip(const struct rect *clip)
+{
+ GRECT canvas, screen, gclip, maxclip;
+ short pxy[4];
+
+ screen.g_x = 0;
+ screen.g_y = 0;
+ screen.g_w = vdi_sysinfo.scr_w;
+ screen.g_h = vdi_sysinfo.scr_h;
+
+ plot_get_dimensions(&canvas);
+
+ view.clipping.y0 = clip->y0;
+ view.clipping.y1 = clip->y1;
+ view.clipping.x0 = clip->x0;
+ view.clipping.x1 = clip->x1;
+
+ plot_get_clip_grect(&gclip);
+
+ gclip.g_x += canvas.g_x;
+ gclip.g_y += canvas.g_y;
+
+ rc_intersect(&canvas, &gclip);
+
+ if(gclip.g_h < 0){
+ gclip.g_h = 0;
+ }
+
+ if (!rc_intersect(&screen, &gclip)) {
+ //dbg_rect("cliprect: ", &view.clipping);
+ //dbg_grect("screen: ", &canvas);
+ //dbg_grect("canvas clipped: ", &gclip);
+ //assert(1 == 0);
+ }
+
+ // When setting VDI clipping, obey to maximum cliping rectangle:
+ plot_get_abs_clipping_grect(&maxclip);
+ rc_intersect(&maxclip, &gclip);
+
+ //dbg_grect("canvas clipped to screen", &gclip);
+
+ pxy[0] = gclip.g_x;
+ pxy[1] = gclip.g_y;
+ pxy[2] = pxy[0] + gclip.g_w;
+ pxy[3] = pxy[1] + gclip.g_h;
+
+ vs_clip(atari_plot_vdi_handle, 1, (short*)&pxy);
+
+ return ( true );
+}
+
+VdiHdl plot_get_vdi_handle(void)
+{
+ return(atari_plot_vdi_handle);
+}
+
+long plot_get_flags(void)
+{
+ return(atari_plot_flags);
+}
+
+
+bool plot_get_clip(struct rect * out)
+{
+ out->x0 = view.clipping.x0;
+ out->y0 = view.clipping.y0;
+ out->x1 = view.clipping.x1;
+ out->y1 = view.clipping.y1;
+ return( true );
+}
+
+void plot_get_clip_grect(GRECT * out)
+{
+ struct rect clip={0,0,0,0};
+
+ plot_get_clip(&clip);
+
+ out->g_x = clip.x0;
+ out->g_y = clip.y0;
+ out->g_w = clip.x1 - clip.x0;
+ out->g_h = clip.y1 - clip.y0;
+}
+
+FONT_PLOTTER plot_get_text_plotter()
+{
+ return(fplotter);
+}
+
+void plot_set_text_plotter(FONT_PLOTTER font_plotter)
+{
+ fplotter = font_plotter;
+}
+
+static bool plot_text(int x, int y, const char *text, size_t length, const plot_font_style_t *fstyle )
+{
+ if (view.scale != 1.0) {
+ plot_font_style_t newstyle = *fstyle;
+ newstyle.size = (int)((float)fstyle->size*view.scale);
+ fplotter->text(fplotter, x, y, text, length, &newstyle);
+ } else {
+ fplotter->text(fplotter, x, y, text, length, fstyle);
+ }
+
+ return ( true );
+}
+
+static bool plot_disc(int x, int y, int radius, const plot_style_t *pstyle)
+{
+ if (pstyle->fill_type != PLOT_OP_TYPE_SOLID) {
+ vsf_rgbcolor(atari_plot_vdi_handle, pstyle->stroke_colour);
+ vsf_perimeter(atari_plot_vdi_handle, 1);
+ vsf_interior(atari_plot_vdi_handle, 0);
+ v_circle(atari_plot_vdi_handle, view.x + x, view.y + y, radius);
+ } else {
+ vsf_rgbcolor(atari_plot_vdi_handle, pstyle->fill_colour);
+ vsf_perimeter(atari_plot_vdi_handle, 0);
+ vsf_interior(atari_plot_vdi_handle, FIS_SOLID);
+ v_circle(atari_plot_vdi_handle, view.x + x, view.y + y, radius);
+ }
+
+ return(true);
+}
+
+static bool plot_arc(int x, int y, int radius, int angle1, int angle2,
+ const plot_style_t *pstyle)
+{
+
+ vswr_mode(atari_plot_vdi_handle, MD_REPLACE );
+ if (pstyle->fill_type == PLOT_OP_TYPE_NONE)
+ return(true);
+ if ( pstyle->fill_type != PLOT_OP_TYPE_SOLID) {
+ vsl_rgbcolor(atari_plot_vdi_handle, pstyle->stroke_colour);
+ vsf_perimeter(atari_plot_vdi_handle, 1);
+ vsf_interior(atari_plot_vdi_handle, 1 );
+ v_arc(atari_plot_vdi_handle, view.x + x, view.y + y, radius, angle1*10, angle2*10);
+ } else {
+ vsf_rgbcolor(atari_plot_vdi_handle, pstyle->fill_colour);
+ vsl_width(atari_plot_vdi_handle, 1 );
+ vsf_perimeter(atari_plot_vdi_handle, 1);
+ v_arc(atari_plot_vdi_handle, view.x + x, view.y + y, radius, angle1*10, angle2*10);
+ }
+
+ return (true);
+}
+
+static bool plot_bitmap(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg,
+ bitmap_flags_t flags)
+{
+ struct bitmap * bm = NULL;
+ bool repeat_x = (flags & BITMAPF_REPEAT_X);
+ bool repeat_y = (flags & BITMAPF_REPEAT_Y);
+ int bmpw,bmph;
+ struct rect clip = {0,0,0,0};
+
+ bmpw = atari_bitmap_get_width(bitmap);
+ bmph = atari_bitmap_get_height(bitmap);
+
+ if(view.scale != 1.0){
+ width = (int)(((float)width)*view.scale);
+ height = (int)(((float)height)*view.scale);
+ }
+
+ if ( repeat_x || repeat_y ) {
+ plot_get_clip(&clip);
+ if( repeat_x && width == 1 && repeat_y && height == 1 ) {
+ width = MAX( width, clip.x1 - x );
+ height = MAX( height, clip.y1 - y );
+ } else if( repeat_x && width == 1 ) {
+ width = MAX( width, clip.x1 - x);
+ } else if( repeat_y && height == 1) {
+ height = MAX( height, clip.y1 - y );
+ }
+ }
+
+ if( width != bmpw || height != bmph ) {
+ atari_bitmap_resize(bitmap, hermes_res_h, &nsfmt, width, height );
+ if( bitmap->resized )
+ bm = bitmap->resized;
+ else
+ bm = bitmap;
+ } else {
+ bm = bitmap;
+ }
+
+ /* out of memory? */
+ if( bm == NULL ) {
+ printf("plot: out of memory! bmp: %p, bmpres: %p\n", bitmap, bitmap->resized );
+ return( true );
+ }
+
+ if (!(repeat_x || repeat_y) ) {
+ plot_blit_bitmap(bm, x, y, bg, flags );
+ } else {
+ int xf,yf;
+ int xoff = x;
+ int yoff = y;
+
+ if (yoff > clip.y0 )
+ yoff = (clip.y0 - height) + ((yoff - clip.y0) % height);
+ if (xoff > clip.x0 )
+ xoff = (clip.x0 - width) + ((xoff - clip.x0) % width);
+ /* for now, repeating just works in the rigth / down direction */
+ /*
+ if( repeat_x == true )
+ xoff = clip.x0;
+ if(repeat_y == true )
+ yoff = clip.y0;
+ */
+
+ for( xf = xoff; xf < clip.x1; xf += width ) {
+ for( yf = yoff; yf < clip.y1; yf += height ) {
+ plot_blit_bitmap(bm, xf, yf, bg, flags );
+ if (!repeat_y)
+ break;
+ }
+ if (!repeat_x)
+ break;
+ }
+ }
+ return ( true );
+}
+
+static bool plot_path(const float *p, unsigned int n, colour fill, float width,
+ colour c, const float transform[6])
+{
+ return ( true );
+}
+
+
+
+const struct plotter_table atari_plotters = {
+ .rectangle = plot_rectangle,
+ .line = plot_line,
+ .polygon = plot_polygon,
+ .clip = plot_clip,
+ .text = plot_text,
+ .disc = plot_disc,
+ .arc = plot_arc,
+ .bitmap = plot_bitmap,
+ .path = plot_path,
+ .flush = NULL,
+ .group_start = NULL,
+ .group_end = NULL,
+ .option_knockout = true
+};
diff --git a/frontends/atari/plot/plot.h b/frontends/atari/plot/plot.h
new file mode 100644
index 000000000..22f8781c2
--- /dev/null
+++ b/frontends/atari/plot/plot.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_ATARI_PLOT_H
+#define NS_ATARI_PLOT_H
+
+/** how much memory should be kept allocated for temp. conversion bitmaps: */
+#define CONV_KEEP_LIMIT 512000
+
+/** how much memory to allocate if some is needed: */
+#define CONV_BLOCK_SIZE 32000
+
+/* Plotter Option Flags: */
+#define PLOT_FLAG_DITHER 0x04 /**< set if the plotter shall dither images */
+#define PLOT_FLAG_TRANS 0x08 /**< set if the plotter supports transparent operations */
+
+/* Plotter "feature" flags */
+#define PLOT_FLAG_HAS_DITHER 0x0400
+#define PLOT_FLAG_HAS_ALPHA 0x0800
+#define PLOT_FLAG_OFFSCREEN 0x1000 /**< offscreen plotter should set this flag */
+
+/* Plotter "internal" flags */
+#define PLOT_FLAG_LOCKED 0x08000 /**< plotter should set this flag during screen updates */
+
+/* Font Plotter flags: */
+#define FONTPLOT_FLAG_MONOGLYPH 0x01
+
+/* Flags for init_mfdb function: */
+#define MFDB_FLAG_STAND 0x01
+#define MFDB_FLAG_ZEROMEM 0x02
+#define MFDB_FLAG_NOALLOC 0x04
+
+/* Flags for blit functions: */
+#define BITMAPF_MONOGLYPH 4096 /**< The bitmap is an character bitmap */
+#define BITMAPF_BUFFER_NATIVE 8192 /**< Bitmap shall be kept converted */
+
+/* Error codes: */
+#define ERR_BUFFERSIZE_EXCEEDS_SCREEN 1 /* The buffer allocated is larger than the screen */
+#define ERR_NO_MEM 2 /* Not enough memory for requested operation */
+#define ERR_PLOTTER_NOT_AVAILABLE 3 /* invalid plotter driver name passed */
+
+struct s_vdi_sysinfo {
+ short vdi_handle; /**< vdi handle */
+ short scr_w; /**< resolution horz. */
+ short scr_h; /**< resolution vert. */
+ short scr_bpp; /**< bits per pixel */
+ int colors; /**< 0=hiclor, 2=mono */
+ unsigned long hicolors; /**< if colors = 0 */
+ short pixelsize; /**< bytes per pixel */
+ unsigned short pitch; /**< row pitch */
+ unsigned short vdiformat; /**< pixel format */
+ unsigned short clut; /**< type of clut support */
+ void *screen; /**< pointer to screen, or NULL */
+ unsigned long screensize; /**< size of screen (in bytes) */
+ unsigned long mask_r; /**< red color mask */
+ unsigned long mask_g; /**< green color mask */
+ unsigned long mask_b; /**< blue color mask */
+ unsigned long mask_a; /**< alpha color mask */
+ short maxintin; /* maximum pxy items */
+ short maxpolycoords; /* max coords for p_line etc. */
+ unsigned long EdDiVersion; /* EdDi Version or 0 */
+ bool rasterscale; /* raster scaling support */
+};
+
+struct rect;
+
+extern const struct plotter_table atari_plotters;
+
+int plot_init(char *);
+int plot_finalise(void);
+
+/**
+ * translate an error number
+ */
+const char* plot_err_str(int i) ;
+
+bool plot_lock(void);
+bool plot_unlock(void);
+bool plot_set_dimensions( int x, int y, int w, int h );
+bool plot_get_dimensions(GRECT *dst);
+float plot_get_scale(void);
+float plot_set_scale(float);
+void plot_set_abs_clipping(const GRECT *area);
+void plot_get_abs_clipping(struct rect *dst);
+void plot_get_abs_clipping_grect(GRECT *dst);
+bool plot_get_clip(struct rect * out);
+/* Get clipping for current framebuffer as GRECT */
+void plot_get_clip_grect(GRECT * out);
+bool plot_clip(const struct rect *clip);
+VdiHdl plot_get_vdi_handle(void);
+long plot_get_flags(void);
+bool plot_rectangle( int x0, int y0, int x1, int y1,const plot_style_t *style );
+bool plot_line( int x0, int y0, int x1, int y1, const plot_style_t *style );
+bool plot_blit_bitmap(struct bitmap * bmp, int x, int y,
+ unsigned long bg, unsigned long flags);
+bool plot_blit_mfdb(GRECT * loc, MFDB * insrc, short fgcolor, uint32_t flags);
+bool plot_copy_rect(GRECT src, GRECT dst);
+
+/* convert an vdi color to bgra */
+void vdi1000_to_rgb( unsigned short * in, unsigned char * out );
+
+/* convert an bgra color to vdi1000 color */
+void rgb_to_vdi1000( unsigned char * in, RGB1000 *out);
+
+/* convert an rgb color to an index into the web palette */
+short rgb_to_666_index(unsigned char r, unsigned char g, unsigned char b);
+
+/* assign vdi line style to dst ( netsurf type ) */
+#define NSLT2VDI(dst, src) \
+ dst = 0; \
+ switch( src->stroke_type ) { \
+ case PLOT_OP_TYPE_DOT: \
+ dst = (0xAAAA00 | 7); \
+ break; \
+ case PLOT_OP_TYPE_DASH: \
+ dst = 3; \
+ break; \
+ case PLOT_OP_TYPE_SOLID: \
+ case PLOT_OP_TYPE_NONE: \
+ default: \
+ dst = 1; \
+ break; \
+ }
+
+
+#ifdef WITH_8BPP_SUPPORT
+/* some Well known indexes into the VDI palette */
+/* common indexes into the VDI palette */
+/* (only used when running with 256 colors or less ) */
+#define OFFSET_WEB_PAL 16
+#define OFFSET_CUST_PAL 232
+#define RGB_TO_VDI(c) rgb_to_666_index( (c&0xFF),(c&0xFF00)>>8,(c&0xFF0000)>>16)+OFFSET_WEB_PAL
+#endif /* WITH_8BPP_SUPPORT*/
+
+/* the name of this macro is crap - it should be named bgr_to_rgba ... or so */
+#define ABGR_TO_RGB(c) ( ((c&0xFF)<<16) | (c&0xFF00) | ((c&0xFF0000)>>16) ) << 8
+
+/* this index into the palette is used by the TC renderer to set
+ * current draw color.
+ */
+#define OFFSET_CUSTOM_COLOR 255
+
+#endif
diff --git a/frontends/atari/redrawslots.c b/frontends/atari/redrawslots.c
new file mode 100644
index 000000000..f5351866a
--- /dev/null
+++ b/frontends/atari/redrawslots.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2011 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+
+#include "utils/utils.h"
+
+#include "atari/redrawslots.h"
+#include "atari/gemtk/gemtk.h"
+
+void redraw_slots_init(struct s_redrw_slots * slots, short size)
+{
+ // TODO: allocate slots dynamically!
+ slots->size = MIN( MAX_REDRW_SLOTS , size);
+ slots->areas_used = 0;
+}
+
+void redraw_slots_free(struct s_redrw_slots * slots)
+{
+ // TOOD: free areas...
+}
+
+
+static inline bool rect_intersect( struct rect * box1, struct rect * box2 )
+{
+ if (box2->x1 < box1->x0)
+ return false;
+
+ if (box2->y1 < box1->y0)
+ return false;
+
+ if (box2->x0 > box1->x1)
+ return false;
+
+ if (box2->y0 > box1->y1)
+ return false;
+
+ return true;
+}
+
+
+void redraw_slot_schedule_grect(struct s_redrw_slots * slots, GRECT *area,
+ bool force)
+{
+ redraw_slot_schedule(slots, area->g_x, area->g_y,
+ area->g_x + area->g_w, area->g_y + area->g_h, force);
+}
+
+/*
+ schedule redraw coords.
+*/
+void redraw_slot_schedule(struct s_redrw_slots * slots, short x0, short y0,
+ short x1, short y1, bool force)
+{
+ int i = 0;
+ struct rect area;
+
+ area.x0 = x0;
+ area.y0 = y0;
+ area.x1 = x1;
+ area.y1 = y1;
+
+ if (force == false) {
+ for (i=0; i<slots->areas_used; i++) {
+ if (slots->areas[i].x0 <= x0
+ && slots->areas[i].x1 >= x1
+ && slots->areas[i].y0 <= y0
+ && slots->areas[i].y1 >= y1) {
+ /* the area is already queued for redraw */
+ return;
+ } else {
+ if (rect_intersect(&slots->areas[i], &area )) {
+ slots->areas[i].x0 = MIN(slots->areas[i].x0, x0);
+ slots->areas[i].y0 = MIN(slots->areas[i].y0, y0);
+ slots->areas[i].x1 = MAX(slots->areas[i].x1, x1);
+ slots->areas[i].y1 = MAX(slots->areas[i].y1, y1);
+ return;
+ }
+ }
+ }
+ }
+
+ if (slots->areas_used < slots->size) {
+ slots->areas[slots->areas_used].x0 = x0;
+ slots->areas[slots->areas_used].x1 = x1;
+ slots->areas[slots->areas_used].y0 = y0;
+ slots->areas[slots->areas_used].y1 = y1;
+ slots->areas_used++;
+ } else {
+ /*
+ we are out of available slots, merge box with last slot
+ this is dumb... but also a very rare case.
+ */
+ slots->areas[slots->size-1].x0 = MIN(slots->areas[i].x0, x0);
+ slots->areas[slots->size-1].y0 = MIN(slots->areas[i].y0, y0);
+ slots->areas[slots->size-1].x1 = MAX(slots->areas[i].x1, x1);
+ slots->areas[slots->size-1].y1 = MAX(slots->areas[i].y1, y1);
+ }
+
+ return;
+}
+
+void redraw_slots_remove_area(struct s_redrw_slots * slots, int i)
+{
+ int x;
+ for (x = i+1; i<slots->areas_used; x++) {
+ slots->areas[x-1] = slots->areas[x];
+ }
+ slots->areas_used--;
+}
diff --git a/frontends/atari/redrawslots.h b/frontends/atari/redrawslots.h
new file mode 100644
index 000000000..ca72a0172
--- /dev/null
+++ b/frontends/atari/redrawslots.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2012 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 ATARI_REDRAW_SLOTS_H
+#define ATARI_REDRAW_SLOTS_H
+
+#include <mt_gem.h>
+
+#include "utils/utils.h"
+
+/**
+ * This is the number of redraw requests that the slotlist can store.
+ * If a redraw is scheduled and all slots are used, the rectangle will
+ * be merged to one of the existing slots.
+ */
+#define MAX_REDRW_SLOTS 32
+
+struct rect;
+
+/**
+ * This struct holds scheduled redraw requests.
+ */
+struct s_redrw_slots
+{
+ struct rect areas[MAX_REDRW_SLOTS];
+ short size;
+ short volatile areas_used;
+};
+
+void redraw_slots_init(struct s_redrw_slots * slots, short size);
+void redraw_slot_schedule(struct s_redrw_slots * slots, short x0, short y0,
+ short x1, short y1, bool force);
+void redraw_slot_schedule_grect(struct s_redrw_slots * slots, GRECT *area,
+ bool force);
+void redraw_slots_remove_area(struct s_redrw_slots * slots, int i);
+void redraw_slots_free(struct s_redrw_slots * slots);
+
+#endif
diff --git a/frontends/atari/res/blank b/frontends/atari/res/blank
new file mode 100644
index 000000000..f21f228a1
--- /dev/null
+++ b/frontends/atari/res/blank
@@ -0,0 +1,39 @@
+<html>
+ <head>
+ <title>Blankpage</title>
+ </head>
+ <body>
+ <h1>Welcome to NetSurf!</h1>
+ </body>
+</html>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \ No newline at end of file
diff --git a/frontends/atari/res/favicon.ico b/frontends/atari/res/favicon.ico
new file mode 100644
index 000000000..8b26a278f
--- /dev/null
+++ b/frontends/atari/res/favicon.ico
Binary files differ
diff --git a/frontends/atari/res/icons/toolbar/atfact/main.png b/frontends/atari/res/icons/toolbar/atfact/main.png
new file mode 100644
index 000000000..64cf3bc54
--- /dev/null
+++ b/frontends/atari/res/icons/toolbar/atfact/main.png
Binary files differ
diff --git a/frontends/atari/res/icons/toolbar/atfact/throbber.png b/frontends/atari/res/icons/toolbar/atfact/throbber.png
new file mode 100644
index 000000000..44d949a1c
--- /dev/null
+++ b/frontends/atari/res/icons/toolbar/atfact/throbber.png
Binary files differ
diff --git a/frontends/atari/res/icons/toolbar/default/main.png b/frontends/atari/res/icons/toolbar/default/main.png
new file mode 100644
index 000000000..964218076
--- /dev/null
+++ b/frontends/atari/res/icons/toolbar/default/main.png
Binary files differ
diff --git a/frontends/atari/res/icons/toolbar/default/main.xcf b/frontends/atari/res/icons/toolbar/default/main.xcf
new file mode 100644
index 000000000..48d6d168c
--- /dev/null
+++ b/frontends/atari/res/icons/toolbar/default/main.xcf
Binary files differ
diff --git a/frontends/atari/res/icons/toolbar/default/throbber.png b/frontends/atari/res/icons/toolbar/default/throbber.png
new file mode 100644
index 000000000..be3bf9949
--- /dev/null
+++ b/frontends/atari/res/icons/toolbar/default/throbber.png
Binary files differ
diff --git a/frontends/atari/res/icons/toolbar/default/throbber.xcf b/frontends/atari/res/icons/toolbar/default/throbber.xcf
new file mode 100644
index 000000000..18646c9c9
--- /dev/null
+++ b/frontends/atari/res/icons/toolbar/default/throbber.xcf
Binary files differ
diff --git a/frontends/atari/res/languages b/frontends/atari/res/languages
new file mode 100644
index 000000000..4927e03a0
--- /dev/null
+++ b/frontends/atari/res/languages
@@ -0,0 +1,261 @@
+aa
+ab
+ae
+af
+ak
+am
+an
+ar
+ar-ae
+ar-bh
+ar-dz
+ar-eg
+ar-iq
+ar-jo
+ar-kw
+ar-lb
+ar-ly
+ar-ma
+ar-om
+ar-qa
+ar-sa
+ar-sy
+ar-tn
+ar-ye
+as
+ast
+av
+az
+ba
+be
+bg
+bh
+bi
+bm
+bn
+bo
+br
+bs
+ca
+ce
+ch
+co
+cr
+cs
+cu
+cv
+cy
+da
+de
+de-at
+de-ch
+de-de
+de-li
+de-lu
+dv
+dz
+ee
+el
+en
+en-au
+en-bz
+en-ca
+en-gb
+en-ie
+en-jm
+en-nz
+en-ph
+en-tt
+en-us
+en-za
+en-zw
+eo
+es
+es-ar
+es-bo
+es-cl
+es-co
+es-cr
+es-do
+es-ec
+es-es
+es-gt
+es-hn
+es-mx
+es-ni
+es-pa
+es-pe
+es-pr
+es-py
+es-sv
+es-uy
+es-ve
+et
+eu
+fa
+fa-ir
+ff
+fi
+fj
+fo
+fr
+fr-be
+fr-ca
+fr-ch
+fr-fr
+fr-lu
+fr-mc
+fur
+fy
+ga
+gd
+gl
+gn
+gu
+gv
+ha
+he
+hi
+ho
+hsb
+hr
+ht
+hu
+hy
+hz
+ia
+id
+ie
+ig
+ii
+ik
+io
+is
+it
+it-ch
+iu
+ja
+jv
+ka
+kg
+ki
+kk
+kl
+km
+kn
+ko
+ko-kp
+ko-kr
+kok
+kr
+ks
+ku
+kv
+kw
+ky
+la
+lb
+lg
+li
+ln
+lo
+lt
+lu
+lv
+mg
+mh
+mi
+mk
+mk-mk
+ml
+mn
+mo
+mr
+ms
+mt
+my
+na
+nb
+nd
+ne
+ng
+nl
+nl-be
+nn
+no
+nr
+nso
+nv
+ny
+oc
+oj
+om
+or
+os
+pa
+pa-in
+pa-pk
+pi
+pl
+ps
+pt
+pt-br
+qu
+rm
+rn
+ro
+ro-mo
+ru
+ru-mo
+sa
+sc
+sd
+sg
+si
+sk
+sl
+so
+sq
+sr
+ss
+st
+su
+sv
+sv-fi
+sv-se
+sw
+ta
+te
+tg
+th
+ti
+tig
+tk
+tl
+tlh
+tn
+to
+tr
+ts
+tt
+tw
+ty
+ug
+uk
+ur
+uz
+ve
+vi
+vo
+wa
+wo
+xh
+yi
+yo
+za
+zh
+zh-cn
+zh-hk
+zh-sg
+zh-tw
+zu
diff --git a/frontends/atari/res/netsurf.rsc b/frontends/atari/res/netsurf.rsc
new file mode 100755
index 000000000..9c7d8dd74
--- /dev/null
+++ b/frontends/atari/res/netsurf.rsc
Binary files differ
diff --git a/frontends/atari/res/netsurf.rsh b/frontends/atari/res/netsurf.rsh
new file mode 100755
index 000000000..c856501ae
--- /dev/null
+++ b/frontends/atari/res/netsurf.rsh
@@ -0,0 +1,241 @@
+ /* Resource C-Header-File v1.97 fr ResourceMaster ab v2.06 by ARDISOFT */
+
+#define TOOLBAR 0 /* form/dial */
+#define TOOLBAR_AREA_SEARCH 1 /* BOX in tree TOOLBAR */
+#define TOOLBAR_BT_SEARCH_FWD 2 /* BUTTON in tree TOOLBAR */
+#define TOOLBAR_BT_SEARCH_BACK 3 /* BUTTON in tree TOOLBAR */
+#define TOOLBAR_CB_CASESENSE 5 /* BUTTON in tree TOOLBAR */
+#define TOOLBAR_CB_SHOWALL 6 /* BUTTON in tree TOOLBAR */
+#define TOOLBAR_TB_SRCH 7 /* FTEXT in tree TOOLBAR */
+#define TOOLBAR_SEARCH_ALIGN_RIGHT 8 /* IBOX in tree TOOLBAR */
+#define TOOLBAR_BT_CLOSE_SEARCH 9 /* BUTTON in tree TOOLBAR */
+#define TOOLBAR_AREA_NAVIGATION 10 /* BOX in tree TOOLBAR */
+#define TOOLBAR_AREA_URL 11 /* USERDEF in tree TOOLBAR */
+#define TOOLBAR_AREA_RIGHT 12 /* IBOX in tree TOOLBAR */
+#define TOOLBAR_THROBBER_AREA 13 /* IBOX in tree TOOLBAR */
+#define TOOLBAR_AREA_LEFT 14 /* IBOX in tree TOOLBAR */
+#define TOOLBAR_BT_BACK 15 /* CICON in tree TOOLBAR */
+#define TOOLBAR_BT_HOME 16 /* CICON in tree TOOLBAR */
+#define TOOLBAR_BT_FORWARD 17 /* CICON in tree TOOLBAR */
+#define TOOLBAR_BT_STOP 18 /* CICON in tree TOOLBAR */
+#define TOOLBAR_BT_RELOAD 19 /* CICON in tree TOOLBAR */
+
+#define ICONIFY 1 /* form/dial */
+#define ICONIFY_GLOBE 1 /* CICON in tree ICONIFY */
+
+#define FAVICON 2 /* form/dial */
+
+#define CURSOR 3 /* form/dial */
+#define CURSOR_HELP 1 /* ICON in tree CURSOR */
+#define CURSOR_NODROP 2 /* ICON in tree CURSOR */
+#define CURSOR_APPSTART 3 /* ICON in tree CURSOR */
+#define CURSOR_DENY 4 /* ICON in tree CURSOR */
+#define CURSOR_SIZEWE 5 /* ICON in tree CURSOR */
+#define CURSOR_SIZENS 6 /* ICON in tree CURSOR */
+#define CURSOR_MENU 7 /* ICON in tree CURSOR */
+
+#define LOGIN 4 /* form/dial */
+#define LOGIN_TB_USER 2 /* FTEXT in tree LOGIN */
+#define LOGIN_TB_PASSWORD 4 /* FTEXT in tree LOGIN */
+#define LOGIN_BT_LOGIN 5 /* BUTTON in tree LOGIN */
+#define LOGIN_BT_ABORT 6 /* BUTTON in tree LOGIN */
+
+#define THROBBER 5 /* form/dial */
+
+#define TOOLBAR_HOTLIST 6 /* form/dial */
+#define TOOLBAR_HOTLIST_ADD 1 /* CICON in tree TOOLBAR_HOTLIST */
+#define TOOLBAR_HOTLIST_CREATE_FOLDER 2 /* CICON in tree TOOLBAR_HOTLIST */
+#define TOOLBAR_HOTLIST_DELETE 3 /* CICON in tree TOOLBAR_HOTLIST */
+#define TOOLBAR_HOTLIST_EDIT 4 /* CICON in tree TOOLBAR_HOTLIST */
+
+#define SEARCH 7 /* form/dial */
+#define SEARCH_BT_SEARCH 1 /* BUTTON in tree SEARCH */
+#define SEARCH_CB_FWD 2 /* BUTTON in tree SEARCH */
+#define SEARCH_BT_SEARCH_BACK 3 /* BUTTON in tree SEARCH */
+#define SEARCH_CB_CASESENSE 5 /* BOXCHAR in tree SEARCH */
+#define SEARCH_CB_SHOWALL 6 /* BOXCHAR in tree SEARCH */
+#define SEARCH_LBL_SHOWALL 7 /* STRING in tree SEARCH */
+#define SEARCH_LBL_CASESENSE 8 /* TEXT in tree SEARCH */
+#define SEARCH_TB_SRCH 9 /* FTEXT in tree SEARCH */
+
+#define DOWNLOAD 8 /* form/dial */
+/* Width ist 400, code depends on that! If you change it, change it in download.c */
+#define DOWNLOAD_PROGRESS 1 /* BOX in tree DOWNLOAD */
+#define DOWNLOAD_PROGRESS_DONE 2 /* BOX in tree DOWNLOAD */
+#define DOWNLOAD_FILENAME 3 /* TEXT in tree DOWNLOAD */
+#define DOWNLOAD_BT_ABORT 4 /* BUTTON in tree DOWNLOAD */
+#define DOWNLOAD_LBL_CLOSE_RDY 5 /* STRING in tree DOWNLOAD */
+#define DOWNLOAD_LBL_BYTES 6 /* TEXT in tree DOWNLOAD */
+#define DOWNLOAD_LBL_PERCENT 7 /* TEXT in tree DOWNLOAD */
+#define DOWNLOAD_LBL_SPEED 8 /* TEXT in tree DOWNLOAD */
+#define DOWNLOAD_CB_CLOSE_RDY 9 /* BOXCHAR in tree DOWNLOAD */
+
+#define ABOUT 9 /* form/dial */
+#define ABOUT_LBL_VERSION 1 /* TEXT in tree ABOUT */
+#define ABOUT_OK 4 /* BUTTON in tree ABOUT */
+#define ABOUT_CONTENT 6 /* USERDEF in tree ABOUT */
+#define ABOUT_CREDITS 7 /* BUTTON in tree ABOUT */
+#define ABOUT_LICENSE 8 /* BUTTON in tree ABOUT */
+
+#define POP_CTX 10 /* form/dial */
+#define POP_CTX_CUT_SEL 1 /* TEXT in tree POP_CTX */
+#define POP_CTX_COPY_SEL 2 /* TEXT in tree POP_CTX */
+#define POP_CTX_PASTE_SEL 3 /* TEXT in tree POP_CTX */
+#define POP_CTX_SELECT_ALL 4 /* TEXT in tree POP_CTX */
+#define POP_CTX_OPEN_NEW 6 /* TEXT in tree POP_CTX */
+#define POP_CTX_COPY_LINK 7 /* TEXT in tree POP_CTX */
+#define POP_CTX_COPY_URL 8 /* TEXT in tree POP_CTX */
+#define POP_CTX_SAVE_AS 9 /* TEXT in tree POP_CTX */
+#define POP_CTX_VIEW_SOURCE 10 /* TEXT in tree POP_CTX */
+#define POP_CTX_SAVE_LINK_AS 11 /* TEXT in tree POP_CTX */
+
+#define VSCROLLER 11 /* form/dial */
+#define VSCROLLER_AREA 1 /* BOX in tree VSCROLLER */
+#define VSCROLLER_SLIDER_AREA 2 /* BUTTON in tree VSCROLLER */
+#define VSCROLLER_SLIDER 3 /* BUTTON in tree VSCROLLER */
+#define VSCROLLER_BT_DOWN 7 /* IBOX in tree VSCROLLER */
+#define VSCROLLER_BT_DOWN_PIC 5 /* CICON in tree VSCROLLER */
+#define VSCROLLER_BT_UP 6 /* IBOX in tree VSCROLLER */
+#define VSCROLLER_BT_UP_PIC 4 /* CICON in tree VSCROLLER */
+
+#define POP_LANGUAGE 12 /* form/dial */
+#define POP_LANGUAGE_CS 1 /* STRING in tree POP_LANGUAGE */
+#define POP_LANGUAGE_DE 2 /* STRING in tree POP_LANGUAGE */
+#define POP_LANGUAGE_EN 3 /* STRING in tree POP_LANGUAGE */
+#define POP_LANGUAGE_EN_GB 4 /* STRING in tree POP_LANGUAGE */
+#define POP_LANGUAGE_DE_DE 5 /* STRING in tree POP_LANGUAGE */
+#define POP_LANGUAGE_EN_US 6 /* STRING in tree POP_LANGUAGE */
+#define POP_LANGUAGE_ES 7 /* STRING in tree POP_LANGUAGE */
+#define POP_LANGUAGE_FR 8 /* STRING in tree POP_LANGUAGE */
+#define POP_LANGUAGE_IT 9 /* STRING in tree POP_LANGUAGE */
+#define POP_LANGUAGE_NL 10 /* STRING in tree POP_LANGUAGE */
+#define POP_LANGUAGE_NO 11 /* STRING in tree POP_LANGUAGE */
+#define POP_LANGUAGE_PL 12 /* STRING in tree POP_LANGUAGE */
+#define POP_LANGUAGE_RU 13 /* STRING in tree POP_LANGUAGE */
+#define POP_LANGUAGE_SK 14 /* STRING in tree POP_LANGUAGE */
+#define POP_LANGUAGE_SV 15 /* STRING in tree POP_LANGUAGE */
+
+#define POP_FONT_RENDERER 13 /* form/dial */
+#define POP_FONT_RENDERER_INTERNAL 1 /* STRING in tree POP_FONT_RENDERER */
+#define POP_FONT_RENDERER_FREETYPE 2 /* STRING in tree POP_FONT_RENDERER */
+
+#define TOOLBAR_COOKIES 14 /* form/dial */
+
+#define TOOLBAR_HISTORY 15 /* form/dial */
+
+#define TOOLBAR_SSL_CERT 16 /* form/dial */
+#define TOOLBAR_SSL_CERT_TRUSTED 1 /* BUTTON in tree TOOLBAR_SSL_CERT */
+
+#define SETTINGS 17 /* form/dial */
+#define SETTINGS_EDIT_DOWNLOAD_PATH 3 /* FTEXT in tree SETTINGS */
+#define SETTINGS_EDIT_HOTLIST_FILE 4 /* FTEXT in tree SETTINGS */
+#define SETTINGS_EDIT_CA_BUNDLE 5 /* FTEXT in tree SETTINGS */
+#define SETTINGS_EDIT_CA_CERTS_PATH 6 /* FTEXT in tree SETTINGS */
+#define SETTINGS_EDIT_EDITOR 7 /* FTEXT in tree SETTINGS */
+#define SETTINGS_BT_SEL_DOWNLOAD_DIR 12 /* BUTTON in tree SETTINGS */
+#define SETTINGS_BT_SEL_HOTLIST 13 /* BUTTON in tree SETTINGS */
+#define SETTINGS_BT_SEL_CA_BUNDLE 14 /* BUTTON in tree SETTINGS */
+#define SETTINGS_BT_SEL_CA_CERTS 15 /* BUTTON in tree SETTINGS */
+#define SETTINGS_BT_SEL_EDITOR 16 /* BUTTON in tree SETTINGS */
+/* Make sure that initial value is large enough! */
+#define SETTINGS_BT_SEL_FONT_RENDERER 19 /* BUTTON in tree SETTINGS */
+#define SETTINGS_CB_ANTI_ALIASING 20 /* BUTTON in tree SETTINGS */
+#define SETTINGS_CB_TRANSPARENCY 21 /* BUTTON in tree SETTINGS */
+#define SETTINGS_EDIT_DEF_FONT_SIZE 24 /* FTEXT in tree SETTINGS */
+#define SETTINGS_DEC_DEF_FONT_SIZE 25 /* BOXCHAR in tree SETTINGS */
+#define SETTINGS_INC_DEF_FONT_SIZE 26 /* BOXCHAR in tree SETTINGS */
+#define SETTINGS_EDIT_MIN_FONT_SIZE 28 /* FTEXT in tree SETTINGS */
+#define SETTINGS_DEC_MIN_FONT_SIZE 29 /* BOXCHAR in tree SETTINGS */
+#define SETTINGS_INC_MIN_FONT_SIZE 30 /* BOXCHAR in tree SETTINGS */
+#define SETTINGS_EDIT_MIN_GIF_DELAY 35 /* FTEXT in tree SETTINGS */
+#define SETTINGS_INC_GIF_DELAY 36 /* BOXCHAR in tree SETTINGS */
+#define SETTINGS_DEC_GIF_DELAY 37 /* BOXCHAR in tree SETTINGS */
+#define SETTINGS_CB_ENABLE_ANIMATION 40 /* BUTTON in tree SETTINGS */
+#define SETTINGS_CB_BG_IMAGES 42 /* BUTTON in tree SETTINGS */
+#define SETTINGS_CB_FG_IMAGES 44 /* BUTTON in tree SETTINGS */
+#define SETTINGS_EDIT_MIN_REFLOW_PERIOD 47 /* FTEXT in tree SETTINGS */
+#define SETTINGS_DEC_INCREMENTAL_REFLOW 48 /* BOXCHAR in tree SETTINGS */
+#define SETTINGS_INC_INCREMENTAL_REFLOW 49 /* BOXCHAR in tree SETTINGS */
+#define SETTINGS_CB_USE_PROXY 51 /* BUTTON in tree SETTINGS */
+#define SETTINGS_EDIT_PROXY_HOST 52 /* FTEXT in tree SETTINGS */
+#define SETTINGS_EDIT_PROXY_PORT 54 /* FTEXT in tree SETTINGS */
+#define SETTINGS_CB_PROXY_AUTH 55 /* BUTTON in tree SETTINGS */
+#define SETTINGS_EDIT_PROXY_USERNAME 56 /* FTEXT in tree SETTINGS */
+#define SETTINGS_EDIT_PROXY_PASSWORD 57 /* FTEXT in tree SETTINGS */
+#define SETTINGS_EDIT_MAX_FETCHERS 62 /* FTEXT in tree SETTINGS */
+#define SETTINGS_INC_MAX_FETCHERS 63 /* BOXCHAR in tree SETTINGS */
+#define SETTINGS_DEC_MAX_FETCHERS 64 /* BOXCHAR in tree SETTINGS */
+#define SETTINGS_DEC_MAX_FETCHERS_PER_HOST 66 /* BOXCHAR in tree SETTINGS */
+#define SETTINGS_EDIT_MAX_FETCHERS_PER_HOST 67 /* FTEXT in tree SETTINGS */
+#define SETTINGS_INC_MAX_FETCHERS_PER_HOST 68 /* BOXCHAR in tree SETTINGS */
+#define SETTINGS_DEC_CACHED_CONNECTIONS 70 /* BOXCHAR in tree SETTINGS */
+#define SETTINGS_EDIT_MAX_CACHED_CONNECTIONS 71 /* FTEXT in tree SETTINGS */
+#define SETTINGS_INC_CACHED_CONNECTIONS 72 /* BOXCHAR in tree SETTINGS */
+#define SETTINGS_EDIT_HOMEPAGE 74 /* FTEXT in tree SETTINGS */
+#define SETTINGS_CB_HIDE_ADVERTISEMENT 75 /* BUTTON in tree SETTINGS */
+#define SETTINGS_CB_DISABLE_POPUP_WINDOWS 76 /* BUTTON in tree SETTINGS */
+#define SETTINGS_CB_SEND_HTTP_REFERRER 77 /* BUTTON in tree SETTINGS */
+#define SETTINGS_CB_SEND_DO_NOT_TRACK 78 /* BUTTON in tree SETTINGS */
+#define SETTINGS_BT_CLEAR_HISTORY 80 /* BUTTON in tree SETTINGS */
+#define SETTINGS_BT_SEL_LOCALE 82 /* BUTTON in tree SETTINGS */
+#define SETTINGS_BT_GUI_LANG 84 /* BUTTON in tree SETTINGS */
+#define SETTINGS_INC_MEM_CACHE 87 /* BOXCHAR in tree SETTINGS */
+#define SETTINGS_DEC_MEM_CACHE 88 /* BOXCHAR in tree SETTINGS */
+#define SETTINGS_STR_MAX_MEM_CACHE 89 /* FTEXT in tree SETTINGS */
+#define SETTINGS_DEC_HISTORY_AGE 92 /* BOXCHAR in tree SETTINGS */
+#define SETTINGS_EDIT_HISTORY_AGE 93 /* FTEXT in tree SETTINGS */
+#define SETTINGS_INC_HISTORY_AGE 94 /* BOXCHAR in tree SETTINGS */
+#define SETTINGS_BT_GUI_TOUT 97 /* BUTTON in tree SETTINGS */
+#define SETTINGS_INC_DISC_CACHE 101 /* BOXCHAR in tree SETTINGS */
+#define SETTINGS_DEC_DISC_CACHE 102 /* BOXCHAR in tree SETTINGS */
+#define SETTINGS_STR_MAX_DISC_CACHE 103 /* FTEXT in tree SETTINGS */
+#define SETTINGS_BT_CLEAR_CACHE 105 /* BUTTON in tree SETTINGS */
+#define SETTINGS_INC_CACHE_AGE 107 /* BOXCHAR in tree SETTINGS */
+#define SETTINGS_DEC_CACHE_AGE 108 /* BOXCHAR in tree SETTINGS */
+#define SETTINGS_EDIT_CACHE_AGE 110 /* FTEXT in tree SETTINGS */
+
+#define MAINMENU 18 /* menu */
+#define MAINMENU_T_FILE 4 /* TITLE in tree MAINMENU */
+#define MAINMENU_T_EDIT 5 /* TITLE in tree MAINMENU */
+#define MAINMENU_T_VIEW 6 /* TITLE in tree MAINMENU */
+#define MAINMENU_T_NAVIGATE 7 /* TITLE in tree MAINMENU */
+#define MAINMENU_T_UTIL 8 /* TITLE in tree MAINMENU */
+#define MAINMENU_T_HELP 9 /* TITLE in tree MAINMENU */
+#define MAINMENU_M_ABOUT 12 /* STRING in tree MAINMENU */
+#define MAINMENU_M_NEWWIN 21 /* STRING in tree MAINMENU */
+#define MAINMENU_M_OPENFILE 22 /* STRING in tree MAINMENU */
+#define MAINMENU_M_OPENURL 23 /* STRING in tree MAINMENU */
+#define MAINMENU_M_CLOSEWIN 24 /* STRING in tree MAINMENU */
+#define MAINMENU_SEP0 25 /* STRING in tree MAINMENU */
+#define MAINMENU_M_SAVEPAGE 26 /* STRING in tree MAINMENU */
+#define MAINMENU_SEP1 27 /* STRING in tree MAINMENU */
+#define MAINMENU_M_QUIT 28 /* STRING in tree MAINMENU */
+#define MAINMENU_M_CUT 30 /* STRING in tree MAINMENU */
+#define MAINMENU_M_COPY 31 /* STRING in tree MAINMENU */
+#define MAINMENU_M_PASTE 32 /* STRING in tree MAINMENU */
+#define MAINMENU_SEP3 33 /* STRING in tree MAINMENU */
+#define MAINMENU_M_FIND 34 /* STRING in tree MAINMENU */
+#define MAINMENU_M_STOP 36 /* STRING in tree MAINMENU */
+#define MAINMENU_M_RELOAD 37 /* STRING in tree MAINMENU */
+#define MAINMENU_INC_SCALE 39 /* STRING in tree MAINMENU */
+#define MAINMENU_DEC_SCALE 40 /* STRING in tree MAINMENU */
+#define MAINMENU_M_TOOLBARS 42 /* STRING in tree MAINMENU */
+#define MAINMENU_M_SAVEWIN 44 /* STRING in tree MAINMENU */
+#define MAINMENU_M_DEBUG_RENDER 45 /* STRING in tree MAINMENU */
+#define MAINMENU_M_BG_IMAGES 46 /* STRING in tree MAINMENU */
+#define MAINMENU_M_FG_IMAGES 47 /* STRING in tree MAINMENU */
+#define MAINMENU_M_BACK 49 /* STRING in tree MAINMENU */
+#define MAINMENU_M_FORWARD 50 /* STRING in tree MAINMENU */
+#define MAINMENU_M_HOME 51 /* STRING in tree MAINMENU */
+#define MAINMENU_M_LHISTORY 53 /* STRING in tree MAINMENU */
+#define MAINMENU_M_GHISTORY 54 /* STRING in tree MAINMENU */
+#define MAINMENU_M_ADD_BOOKMARK 56 /* STRING in tree MAINMENU */
+#define MAINMENU_M_BOOKMARKS 57 /* STRING in tree MAINMENU */
+#define MAINMENU_M_COOKIES 59 /* STRING in tree MAINMENU */
+#define MAINMENU_M_CHOICES 61 /* STRING in tree MAINMENU */
+#define MAINMENU_M_VLOG 62 /* STRING in tree MAINMENU */
+#define MAINMENU_M_HELP_CONTENT 64 /* STRING in tree MAINMENU */
+
+#define TOOLBAR_SETTINGS 19 /* form/dial */
+#define TOOLBAR_SETTINGS_ABORT 1 /* BOXTEXT in tree TOOLBAR_SETTINGS */
+#define TOOLBAR_SETTINGS_SAVE 2 /* BOXTEXT in tree TOOLBAR_SETTINGS */
diff --git a/frontends/atari/res/netsurf.rsm b/frontends/atari/res/netsurf.rsm
new file mode 100755
index 000000000..6c240d371
--- /dev/null
+++ b/frontends/atari/res/netsurf.rsm
@@ -0,0 +1,225 @@
+ResourceMaster v3.651
+#C 20@0@0@0@
+#N 99@32@AZAaza___ _@AZAaza090___ _@@_@
+#FoC-Header@rsm2out@C-Header@rsh@@@[C-Header@0@
+#R 0@0@1@1@2@1@
+#M 11110100@0@7728@671@
+#T 0@2@TOOLBAR@@20@@
+#O 1@20@AREA_SEARCH@@
+#O 2@26@BT_SEARCH_FWD@@
+#O 3@26@BT_SEARCH_BACK@@
+#O 5@26@CB_CASESENSE@@
+#O 6@26@CB_SHOWALL@@
+#O 7@29@TB_SRCH@@
+#O 8@25@SEARCH_ALIGN_RIGHT@@
+#O 9@26@BT_CLOSE_SEARCH@@
+#O 10@20@AREA_NAVIGATION@@
+#O 11@24@AREA_URL@@
+#O 12@25@AREA_RIGHT@@
+#O 13@25@THROBBER_AREA@@
+#O 14@25@AREA_LEFT@@
+#O 15@33@BT_BACK@@
+#O 16@33@BT_HOME@@
+#O 17@33@BT_FORWARD@@
+#O 18@33@BT_STOP@@
+#O 19@33@BT_RELOAD@@
+#T 1@2@ICONIFY@@3@@
+#O 1@33@GLOBE@@
+#T 2@2@FAVICON@@2@@
+#T 3@2@CURSOR@@8@@
+#O 1@31@HELP@@
+#O 2@31@NODROP@@
+#O 3@31@APPSTART@@
+#O 4@31@DENY@@
+#O 5@31@SIZEWE@@
+#O 6@31@SIZENS@@
+#O 7@31@MENU@@
+#T 4@2@LOGIN@@7@@
+#O 2@29@TB_USER@@
+#O 4@29@TB_PASSWORD@@
+#O 5@26@BT_LOGIN@@
+#O 6@26@BT_ABORT@@
+#T 5@2@THROBBER@@14@@
+#T 6@2@TOOLBAR_HOTLIST@@5@@
+#O 1@33@ADD@@
+#O 2@33@CREATE_FOLDER@@
+#O 3@33@DELETE@@
+#O 4@33@EDIT@@
+#T 7@2@SEARCH@@10@@
+#O 1@26@BT_SEARCH@@
+#O 2@26@CB_FWD@@
+#O 3@26@BT_SEARCH_BACK@@
+#O 5@27@CB_CASESENSE@@
+#O 6@27@CB_SHOWALL@@
+#O 7@28@LBL_SHOWALL@@
+#O 8@21@LBL_CASESENSE@@
+#O 9@29@TB_SRCH@@
+#T 8@2@DOWNLOAD@@10@@
+#O 1@20@PROGRESS@Width ist 400, code depends on that! If you change it, change it in download.c@
+#O 2@20@PROGRESS_DONE@@
+#O 3@21@FILENAME@@
+#O 4@26@BT_ABORT@@
+#O 5@28@LBL_CLOSE_RDY@@
+#O 6@21@LBL_BYTES@@
+#O 7@21@LBL_PERCENT@@
+#O 8@21@LBL_SPEED@@
+#O 9@27@CB_CLOSE_RDY@@
+#T 9@2@ABOUT@@9@@
+#O 1@21@LBL_VERSION@@
+#O 4@26@OK@@
+#O 6@24@CONTENT@@
+#O 7@26@CREDITS@@
+#O 8@26@LICENSE@@
+#T 10@2@POP_CTX@@12@@
+#O 1@21@CUT_SEL@@
+#O 2@21@COPY_SEL@@
+#O 3@21@PASTE_SEL@@
+#O 4@21@SELECT_ALL@@
+#O 6@21@OPEN_NEW@@
+#O 7@21@COPY_LINK@@
+#O 8@21@COPY_URL@@
+#O 9@21@SAVE_AS@@
+#O 10@21@VIEW_SOURCE@@
+#O 11@21@SAVE_LINK_AS@@
+#T 11@2@VSCROLLER@@8@@
+#O 1@20@AREA@@
+#O 2@26@SLIDER_AREA@@
+#O 3@26@SLIDER@@
+#O 7@25@BT_DOWN@@
+#O 5@33@BT_DOWN_PIC@@
+#O 6@25@BT_UP@@
+#O 4@33@BT_UP_PIC@@
+#T 12@2@POP_LANGUAGE@@16@@
+#O 1@28@CS@@
+#O 2@28@DE@@
+#O 3@28@EN@@
+#O 4@28@EN_GB@@
+#O 5@28@DE_DE@@
+#O 6@28@EN_US@@
+#O 7@28@ES@@
+#O 8@28@FR@@
+#O 9@28@IT@@
+#O 10@28@NL@@
+#O 11@28@NO@@
+#O 12@28@PL@@
+#O 13@28@RU@@
+#O 14@28@SK@@
+#O 15@28@SV@@
+#T 13@2@POP_FONT_RENDERER@@3@@
+#O 1@28@INTERNAL@@
+#O 2@28@FREETYPE@@
+#T 14@2@TOOLBAR_COOKIES@@1@@
+#T 15@2@TOOLBAR_HISTORY@@1@@
+#T 16@2@TOOLBAR_SSL_CERT@@2@@
+#O 1@26@TRUSTED@@
+#T 17@2@SETTINGS@@112@@
+#O 3@29@EDIT_DOWNLOAD_PATH@@
+#O 4@29@EDIT_HOTLIST_FILE@@
+#O 5@29@EDIT_CA_BUNDLE@@
+#O 6@29@EDIT_CA_CERTS_PATH@@
+#O 7@29@EDIT_EDITOR@@
+#O 12@26@BT_SEL_DOWNLOAD_DIR@@
+#O 13@26@BT_SEL_HOTLIST@@
+#O 14@26@BT_SEL_CA_BUNDLE@@
+#O 15@26@BT_SEL_CA_CERTS@@
+#O 16@26@BT_SEL_EDITOR@@
+#O 19@26@BT_SEL_FONT_RENDERER@Make sure that initial value is large enough!@
+#O 20@26@CB_ANTI_ALIASING@@
+#O 21@26@CB_TRANSPARENCY@@
+#O 24@29@EDIT_DEF_FONT_SIZE@@
+#O 25@27@DEC_DEF_FONT_SIZE@@
+#O 26@27@INC_DEF_FONT_SIZE@@
+#O 28@29@EDIT_MIN_FONT_SIZE@@
+#O 29@27@DEC_MIN_FONT_SIZE@@
+#O 30@27@INC_MIN_FONT_SIZE@@
+#O 35@29@EDIT_MIN_GIF_DELAY@@
+#O 36@27@INC_GIF_DELAY@@
+#O 37@27@DEC_GIF_DELAY@@
+#O 40@26@CB_ENABLE_ANIMATION@@
+#O 42@26@CB_BG_IMAGES@@
+#O 44@26@CB_FG_IMAGES@@
+#O 47@29@EDIT_MIN_REFLOW_PERIOD@@
+#O 48@27@DEC_INCREMENTAL_REFLOW@@
+#O 49@27@INC_INCREMENTAL_REFLOW@@
+#O 51@26@CB_USE_PROXY@@
+#O 52@29@EDIT_PROXY_HOST@@
+#O 54@29@EDIT_PROXY_PORT@@
+#O 55@26@CB_PROXY_AUTH@@
+#O 56@29@EDIT_PROXY_USERNAME@@
+#O 57@29@EDIT_PROXY_PASSWORD@@
+#O 62@29@EDIT_MAX_FETCHERS@@
+#O 63@27@INC_MAX_FETCHERS@@
+#O 64@27@DEC_MAX_FETCHERS@@
+#O 66@27@DEC_MAX_FETCHERS_PER_HOST@@
+#O 67@29@EDIT_MAX_FETCHERS_PER_HOST@@
+#O 68@27@INC_MAX_FETCHERS_PER_HOST@@
+#O 70@27@DEC_CACHED_CONNECTIONS@@
+#O 71@29@EDIT_MAX_CACHED_CONNECTIONS@@
+#O 72@27@INC_CACHED_CONNECTIONS@@
+#O 74@29@EDIT_HOMEPAGE@@
+#O 75@26@CB_HIDE_ADVERTISEMENT@@
+#O 76@26@CB_DISABLE_POPUP_WINDOWS@@
+#O 77@26@CB_SEND_HTTP_REFERRER@@
+#O 78@26@CB_SEND_DO_NOT_TRACK@@
+#O 80@26@BT_CLEAR_HISTORY@@
+#O 82@26@BT_SEL_LOCALE@@
+#O 84@26@BT_GUI_LANG@@
+#O 87@27@INC_MEM_CACHE@@
+#O 88@27@DEC_MEM_CACHE@@
+#O 89@29@STR_MAX_MEM_CACHE@@
+#O 92@27@DEC_HISTORY_AGE@@
+#O 93@29@EDIT_HISTORY_AGE@@
+#O 94@27@INC_HISTORY_AGE@@
+#O 97@26@BT_GUI_TOUT@@
+#O 101@27@INC_DISC_CACHE@@
+#O 102@27@DEC_DISC_CACHE@@
+#O 103@29@STR_MAX_DISC_CACHE@@
+#O 105@26@BT_CLEAR_CACHE@@
+#O 107@27@INC_CACHE_AGE@@
+#O 108@27@DEC_CACHE_AGE@@
+#O 110@29@EDIT_CACHE_AGE@@
+#T 18@1@MAINMENU@@65@@
+#O 4@32@T_FILE@@
+#O 5@32@T_EDIT@@
+#O 6@32@T_VIEW@@
+#O 7@32@T_NAVIGATE@@
+#O 8@32@T_UTIL@@
+#O 9@32@T_HELP@@
+#O 12@28@M_ABOUT@@
+#O 21@28@M_NEWWIN@@
+#O 22@28@M_OPENFILE@@
+#O 23@28@M_OPENURL@@
+#O 24@28@M_CLOSEWIN@@
+#O 25@28@SEP0@@
+#O 26@28@M_SAVEPAGE@@
+#O 27@28@SEP1@@
+#O 28@28@M_QUIT@@
+#O 30@28@M_CUT@@
+#O 31@28@M_COPY@@
+#O 32@28@M_PASTE@@
+#O 33@28@SEP3@@
+#O 34@28@M_FIND@@
+#O 36@28@M_STOP@@
+#O 37@28@M_RELOAD@@
+#O 39@28@INC_SCALE@@
+#O 40@28@DEC_SCALE@@
+#O 42@28@M_TOOLBARS@@
+#O 44@28@M_SAVEWIN@@
+#O 45@28@M_DEBUG_RENDER@@
+#O 46@28@M_BG_IMAGES@@
+#O 47@28@M_FG_IMAGES@@
+#O 49@28@M_BACK@@
+#O 50@28@M_FORWARD@@
+#O 51@28@M_HOME@@
+#O 53@28@M_LHISTORY@@
+#O 54@28@M_GHISTORY@@
+#O 56@28@M_ADD_BOOKMARK@@
+#O 57@28@M_BOOKMARKS@@
+#O 59@28@M_COOKIES@@
+#O 61@28@M_CHOICES@@
+#O 62@28@M_VLOG@@
+#O 64@28@M_HELP_CONTENT@@
+#T 19@2@TOOLBAR_SETTINGS@@3@@
+#O 1@22@ABORT@@
+#O 2@22@SAVE@@
+#c 10775@
diff --git a/frontends/atari/rootwin.c b/frontends/atari/rootwin.c
new file mode 100644
index 000000000..605ce45ae
--- /dev/null
+++ b/frontends/atari/rootwin.c
@@ -0,0 +1,1540 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ *
+ * Module Description:
+ *
+ * This File implements the NetSurf Browser window, or passed functionality to
+ * the appropriate widget's.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <math.h>
+#include <osbind.h>
+
+#include <mt_gem.h>
+
+#include "utils/log.h"
+#include "desktop/browser.h"
+#include "desktop/mouse.h"
+#include "desktop/plotters.h"
+#include "desktop/textinput.h"
+#include "content/content.h"
+#include "content/hlcache.h"
+#include "content/urldb.h"
+
+#include "atari/res/netsurf.rsh"
+#include "atari/gemtk/gemtk.h"
+#include "atari/ctxmenu.h"
+#include "atari/gui.h"
+#include "atari/rootwin.h"
+#include "atari/misc.h"
+#include "atari/plot/plot.h"
+#include "atari/toolbar.h"
+#include "atari/statusbar.h"
+#include "atari/search.h"
+#include "atari/osspec.h"
+#include "atari/encoding.h"
+#include "atari/redrawslots.h"
+#include "atari/toolbar.h"
+#include "atari/findfile.h"
+#include "atari/bitmap.h"
+
+extern struct gui_window *input_window;
+extern EVMULT_OUT aes_event_out;
+extern GRECT desk_area;
+
+struct rootwin_data_s {
+ struct s_gui_win_root *rootwin;
+};
+
+/* -------------------------------------------------------------------------- */
+/* Static module event handlers */
+/* -------------------------------------------------------------------------- */
+static void on_redraw(ROOTWIN *rootwin, short msg[8]);
+static void on_resized(ROOTWIN *rootwin);
+static void on_file_dropped(ROOTWIN *rootwin, short msg[8]);
+static short on_window_key_input(ROOTWIN * rootwin, unsigned short nkc);
+static void on_content_mouse_click(ROOTWIN *rootwin);
+static void on_content_mouse_move(ROOTWIN *rootwin, GRECT *content_area);
+static void toolbar_redraw_cb(GUIWIN *win, uint16_t msg, GRECT *clip);
+
+bool gui_window_get_scroll(struct gui_window *w, int *sx, int *sy);
+
+static bool redraw_active = false;
+
+static const struct redraw_context rootwin_rdrw_ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &atari_plotters
+};
+
+static short handle_event(GUIWIN *win, EVMULT_OUT *ev_out, short msg[8])
+{
+ short retval = 0;
+ GRECT area;
+ static bool prev_url = false;
+ struct rootwin_data_s * data = gemtk_wm_get_user_data(win);
+ struct gui_window *tmp;
+
+
+ if ((ev_out->emo_events & MU_MESAG) != 0) {
+ // handle message
+ //printf("root win msg: %d\n", msg[0]);
+ switch (msg[0]) {
+
+ case WM_REDRAW:
+ LOG("WM_REDRAW");
+ on_redraw(data->rootwin, msg);
+ break;
+
+ case WM_REPOSED:
+ case WM_SIZED:
+ case WM_MOVED:
+ case WM_FULLED:
+ LOG("WM_SIZED");
+ on_resized(data->rootwin);
+ break;
+
+ case WM_ICONIFY:
+ // TODO: find next active gui window and schedule redraw for that.
+ tmp = window_list;
+ while(tmp != NULL){
+ if(tmp->root != data->rootwin){
+ gemtk_wm_send_msg(tmp->root->win, WM_TOPPED, 0, 0, 0, 0);
+ break;
+ }
+ tmp = tmp->next;
+ }
+ break;
+
+ case WM_TOPPED:
+ case WM_NEWTOP:
+ case WM_UNICONIFY:
+ LOG("WM_TOPPED");
+ gui_set_input_gui_window(data->rootwin->active_gui_window);
+ //window_restore_active_gui_window(data->rootwin);
+ // TODO: use something like "restore_active_gui_window_state()"
+
+ break;
+
+ case WM_CLOSED:
+ // TODO: this needs to iterate through all gui windows and
+ // check if the rootwin is this window...
+ if (data->rootwin->active_gui_window != NULL) {
+ LOG("WM_CLOSED initiated destroy for bw %p", data->rootwin->active_gui_window->browser->bw);
+ browser_window_destroy(
+ data->rootwin->active_gui_window->browser->bw);
+ }
+ break;
+
+ case AP_DRAGDROP:
+ on_file_dropped(data->rootwin, msg);
+ break;
+
+ case WM_TOOLBAR:
+ toolbar_mouse_input(data->rootwin->toolbar, msg[4], msg[7]);
+ break;
+
+ default:
+ break;
+ }
+ }
+ if ((ev_out->emo_events & MU_KEYBD) != 0) {
+
+ // handle key
+ uint16_t nkc = gem_to_norm( (short)ev_out->emo_kmeta,
+ (short)ev_out->emo_kreturn);
+ LOG("rootwin MU_KEYBD input, nkc: %x\n", nkc);
+ retval = on_window_key_input(data->rootwin, nkc);
+ // printf("on_window_key_input: %d\n", retval);
+
+ }
+ if ((ev_out->emo_events & MU_BUTTON) != 0) {
+ LOG("rootwin MU_BUTTON input, x: %d, y: %d\n", ev_out->emo_mouse.p_x, ev_out->emo_mouse.p_x);
+ window_get_grect(data->rootwin, BROWSER_AREA_CONTENT,
+ &area);
+ if (POINT_WITHIN(ev_out->emo_mouse.p_x, ev_out->emo_mouse.p_y,
+ area)) {
+ on_content_mouse_click(data->rootwin);
+ }
+ }
+ if ((ev_out->emo_events & (MU_M1)) != 0) {
+
+ short ghandle = wind_find(ev_out->emo_mouse.p_x, ev_out->emo_mouse.p_y);
+
+ if (data->rootwin->aes_handle==ghandle) {
+ // The window found at x,y is an gui_window
+ // and it's the input window.
+ window_get_grect(data->rootwin, BROWSER_AREA_CONTENT,
+ &area);
+ if (POINT_WITHIN(ev_out->emo_mouse.p_x, ev_out->emo_mouse.p_y,
+ area)) {
+ on_content_mouse_move(data->rootwin, &area);
+ } else {
+ GRECT tb_area;
+ window_get_grect(data->rootwin, BROWSER_AREA_URL_INPUT, &tb_area);
+ if (POINT_WITHIN(ev_out->emo_mouse.p_x, ev_out->emo_mouse.p_y,
+ tb_area)) {
+ gem_set_cursor(&gem_cursors.ibeam);
+ prev_url = true;
+ } else {
+ if(prev_url) {
+ struct gui_window *gw;
+ gw = window_get_active_gui_window(data->rootwin);
+ gem_set_cursor(gw->cursor);
+ prev_url = false;
+ }
+ }
+ }
+ }
+ }
+
+ return(retval);
+}
+
+/* -------------------------------------------------------------------------- */
+/* Module public functions: */
+/* -------------------------------------------------------------------------- */
+
+int window_create(struct gui_window * gw,
+ struct browser_window * bw,
+ struct gui_window * existing,
+ unsigned long inflags)
+{
+ int err = 0;
+ bool tb, sb;
+ int flags;
+ struct rootwin_data_s *data;
+ struct gemtk_wm_scroll_info_s *slid;
+
+ tb = (inflags & WIDGET_TOOLBAR);
+ sb = (inflags & WIDGET_STATUSBAR);
+
+ flags = CLOSER | MOVER | NAME | FULLER | SMALLER;
+ if( inflags & WIDGET_SCROLL ) {
+ flags |= (UPARROW | DNARROW | LFARROW | RTARROW | VSLIDE | HSLIDE);
+ }
+ if( inflags & WIDGET_RESIZE ) {
+ flags |= ( SIZER );
+ }
+ if( inflags & WIDGET_STATUSBAR ) {
+ flags |= ( INFO );
+ }
+
+ gw->root = malloc(sizeof(struct s_gui_win_root));
+ if (gw->root == NULL)
+ return(-1);
+ memset(gw->root, 0, sizeof(struct s_gui_win_root) );
+ gw->root->title = malloc(atari_sysinfo.aes_max_win_title_len+1);
+
+ redraw_slots_init(&gw->root->redraw_slots, 8);
+
+ gw->root->aes_handle = wind_create(flags, 40, 40, desk_area.g_w,
+ desk_area.g_h);
+ if(gw->root->aes_handle<0) {
+ free(gw->root->title);
+ free(gw->root);
+ return( -1 );
+ }
+ gw->root->win = gemtk_wm_add(gw->root->aes_handle,
+ GEMTK_WM_FLAG_PREPROC_WM | GEMTK_WM_FLAG_RECV_PREPROC_WM, handle_event);
+
+ data = malloc(sizeof(struct rootwin_data_s));
+ data->rootwin = gw->root;
+ gemtk_wm_set_user_data(gw->root->win, (void*)data);
+ slid = gemtk_wm_get_scroll_info(gw->root->win);
+ slid->y_unit_px = 32;
+ slid->x_unit_px = 32;
+
+ /* create */
+ if(tb) {
+ gw->root->toolbar = toolbar_create(gw->root);
+ assert(gw->root->toolbar);
+ gemtk_wm_set_toolbar(gw->root->win, gw->root->toolbar->form, 0, 0);
+ gemtk_wm_set_toolbar_redraw_func(gw->root->win, toolbar_redraw_cb);
+ } else {
+ gw->root->toolbar = NULL;
+ }
+
+ /* create browser component: */
+ gw->browser = (struct s_browser *)malloc( sizeof(struct s_browser));
+
+ assert(gw->browser);
+
+ gw->browser->bw = bw;
+ gw->scale = browser_window_get_scale(bw);
+
+
+ /* create statusbar component: */
+ if(sb) {
+ gw->root->statusbar = sb_create( gw );
+ } else {
+ gw->root->statusbar = NULL;
+ }
+
+ // Setup some window defaults:
+ wind_set_str(gw->root->aes_handle, WF_NAME, (char*)"NetSurf");
+ wind_set(gw->root->aes_handle, WF_OPTS, 1, WO0_FULLREDRAW, 0, 0);
+ wind_set(gw->root->aes_handle, WF_OPTS, 1, WO0_NOBLITW, 0, 0);
+ wind_set(gw->root->aes_handle, WF_OPTS, 1, WO0_NOBLITH, 0, 0);
+
+ if (inflags & WIN_TOP) {
+ window_set_focus(gw->root, BROWSER, gw->browser);
+ }
+
+ return (err);
+}
+
+void window_unref_gui_window(ROOTWIN *rootwin, struct gui_window *gw)
+{
+ struct gui_window *w;
+ input_window = NULL;
+
+ LOG("window: %p, gui_window: %p", rootwin, gw);
+
+ w = window_list;
+ // find the next active tab:
+ while( w != NULL ) {
+ if(w->root == rootwin && w != gw) {
+ LOG("activating next tab %p", w);
+ gui_set_input_gui_window(w);
+ break;
+ }
+ w = w->next;
+ }
+ if(input_window == NULL) {
+ // the last gui window for this rootwin was removed:
+ redraw_slots_free(&rootwin->redraw_slots);
+ window_destroy(rootwin);
+ }
+}
+
+int window_destroy(ROOTWIN *rootwin)
+{
+ int err = 0;
+ struct gui_window *w;
+
+ assert(rootwin != NULL);
+
+ LOG("%p", rootwin);
+
+ if (gemtk_wm_get_user_data(rootwin->win) != NULL) {
+ free(gemtk_wm_get_user_data(rootwin->win));
+ }
+
+ // make sure we do not destroy windows which have gui_windows attached:
+ w = window_list;
+ while( w != NULL ) {
+ if(w->root == rootwin) {
+ assert(rootwin == NULL);
+ }
+ w = w->next;
+ }
+
+ if (rootwin->toolbar)
+ toolbar_destroy(rootwin->toolbar);
+
+ if(rootwin->statusbar)
+ sb_destroy(rootwin->statusbar);
+
+ if(rootwin->title)
+ free(rootwin->title);
+
+ gemtk_wm_remove(rootwin->win);
+ wind_close(rootwin->aes_handle);
+ wind_delete(rootwin->aes_handle);
+ free(rootwin);
+ return(err);
+}
+
+
+void window_open(ROOTWIN *rootwin, struct gui_window *gw, GRECT pos)
+{
+ GRECT g;
+
+ rootwin->active_gui_window = gw;
+
+ assert(rootwin->active_gui_window != NULL);
+
+ wind_open(rootwin->aes_handle, pos.g_x, pos.g_y, pos.g_w, pos.g_h );
+ wind_set_str(rootwin->aes_handle, WF_NAME, (char *)"");
+
+ rootwin->active_gui_window->browser->attached = true;
+ if(rootwin->statusbar != NULL) {
+ sb_attach(rootwin->statusbar, rootwin->active_gui_window);
+ }
+
+ /* Set initial size of the toolbar region: */
+ gemtk_wm_get_grect(rootwin->win, GEMTK_WM_AREA_TOOLBAR, &g);
+ toolbar_set_attached(rootwin->toolbar, true);
+ toolbar_set_dimensions(rootwin->toolbar, &g);
+
+ /* initially hide the search area of the toolbar: */
+ window_close_search(rootwin);
+
+ window_update_back_forward(rootwin);
+
+ window_set_focus(rootwin, BROWSER, rootwin->active_gui_window->browser);
+}
+
+void window_restore_active_gui_window(ROOTWIN *rootwin)
+{
+ GRECT tb_area;
+ struct gui_window *gw;
+
+ LOG("rootwin %p", rootwin);
+
+ assert(rootwin->active_gui_window);
+
+ gw = rootwin->active_gui_window;
+
+ window_set_icon(rootwin, gw->icon);
+ window_set_stauts(rootwin, gw->status);
+ window_set_title(rootwin, gw->title);
+
+ if (gw->search != NULL) {
+ // TODO: update search session (especially browser window)
+ }
+
+ toolbar_get_grect(rootwin->toolbar, 0, &tb_area);
+ gemtk_wm_set_toolbar_size(rootwin->win, tb_area.g_h);
+
+ window_update_back_forward(rootwin);
+
+ toolbar_set_url(rootwin->toolbar, gw->url);
+}
+
+
+/* update back forward buttons (see tb_update_buttons (bug) ) */
+void window_update_back_forward(struct s_gui_win_root *rootwin)
+{
+ struct gui_window * active_gw = rootwin->active_gui_window;
+ toolbar_update_buttons(rootwin->toolbar, active_gw->browser->bw, -1);
+}
+
+void window_set_stauts(struct s_gui_win_root *rootwin, char * text)
+{
+ assert(rootwin != NULL);
+
+ CMP_STATUSBAR sb = rootwin->statusbar;
+
+ if( sb == NULL)
+ return;
+
+ if(text != NULL)
+ sb_set_text(sb, text);
+ else
+ sb_set_text(sb, "");
+}
+
+void window_set_title(struct s_gui_win_root * rootwin, char *title)
+{
+ wind_set_str(rootwin->aes_handle, WF_NAME, title);
+}
+
+void window_scroll_by(ROOTWIN *root, int sx, int sy)
+{
+ struct gemtk_wm_scroll_info_s *slid = gemtk_wm_get_scroll_info(root->win);
+
+ if (sx < 0) {
+ sx = 0;
+ }
+ if (sy < 0) {
+ sy = 0;
+ }
+ int xunits = sx / slid->x_unit_px;
+ int yunits = sy / slid->y_unit_px;
+
+ gemtk_wm_scroll(root->win, GEMTK_WM_VSLIDER, yunits - slid->y_pos, false);
+ gemtk_wm_scroll(root->win, GEMTK_WM_HSLIDER, xunits - slid->x_pos, false);
+ gemtk_wm_update_slider(root->win, GEMTK_WM_VH_SLIDER);
+}
+
+/**
+* Set the dimensions of the scrollable content.
+*
+*/
+void window_set_content_size(ROOTWIN *rootwin, int width, int height)
+{
+ GRECT area;
+ struct gemtk_wm_scroll_info_s *slid = gemtk_wm_get_scroll_info(rootwin->win);
+
+ window_get_grect(rootwin, BROWSER_AREA_CONTENT, &area);
+
+ slid->x_units = (width/slid->x_unit_px);
+ slid->y_units = (height/slid->y_unit_px);
+ if(slid->x_units < slid->x_pos)
+ slid->x_pos = 0;
+ if(slid->y_units < slid->y_pos)
+ slid->y_pos = 0;
+ gemtk_wm_update_slider(rootwin->win, GEMTK_WM_VH_SLIDER);
+}
+
+/* set focus to an arbitary element */
+void window_set_focus(struct s_gui_win_root *rootwin,
+ enum focus_element_type type, void * element)
+{
+ assert(rootwin != NULL);
+
+ if (rootwin->focus.type != type || rootwin->focus.element != element) {
+ LOG("Set focus: %p (%d)\n", element, type);
+ rootwin->focus.type = type;
+ rootwin->focus.element = element;
+ switch( type ) {
+
+ case URL_WIDGET:
+ // TODO: make something like: toolbar_text_select_all();
+ toolbar_key_input(rootwin->toolbar, (short)(NKF_CTRL | 'A') );
+/*
+ ta = toolbar_get_textarea(rootwin->toolbar,
+ URL_INPUT_TEXT_AREA);
+ textarea_keypress(ta, NS_KEY_SELECT_ALL);
+ */
+ break;
+
+ case SEARCH_INPUT:
+ gemtk_wm_set_toolbar_edit_obj(rootwin->win, TOOLBAR_TB_SRCH, 0);
+ break;
+
+ default:
+ break;
+
+ }
+ }
+}
+
+/* check if the url widget has focus */
+bool window_url_widget_has_focus(struct s_gui_win_root *rootwin)
+{
+ assert(rootwin != NULL);
+
+ if (rootwin->focus.type == URL_WIDGET) {
+ return true;
+ }
+ return false;
+}
+
+/* check if an arbitary window widget / or frame has the focus */
+bool window_widget_has_focus(struct s_gui_win_root *rootwin,
+ enum focus_element_type t, void * element)
+{
+ assert(rootwin != NULL);
+ if( element == NULL ) {
+ return((rootwin->focus.type == t));
+ }
+
+ return((element == rootwin->focus.element && t == rootwin->focus.type));
+}
+
+void window_set_icon(ROOTWIN *rootwin, struct bitmap * bmp )
+{
+ rootwin->icon = bmp;
+ /* redraw window when it is iconyfied: */
+ if (rootwin->icon != NULL) {
+ if (gemtk_wm_get_state(rootwin->win) & GEMTK_WM_STATUS_ICONIFIED) {
+ window_redraw_favicon(rootwin, NULL);
+ }
+ }
+}
+
+void window_set_active_gui_window(ROOTWIN *rootwin, struct gui_window *gw)
+{
+ struct gui_window *old_gw = rootwin->active_gui_window;
+
+ LOG("gw %p",gw);
+
+ if (rootwin->active_gui_window != NULL) {
+ if(rootwin->active_gui_window == gw) {
+ LOG("nothing to do...");
+ return;
+ }
+ }
+
+ // TODO: when the window isn't on top, initiate WM_TOPPED.
+
+ rootwin->active_gui_window = gw;
+ if (old_gw != NULL) {
+ LOG("restoring window...");
+ window_restore_active_gui_window(rootwin);
+ }
+}
+
+struct gui_window * window_get_active_gui_window(ROOTWIN * rootwin)
+{
+ return(rootwin->active_gui_window);
+}
+
+void window_get_scroll(ROOTWIN *rootwin, int *x, int *y)
+{
+ struct gemtk_wm_scroll_info_s *slid;
+
+ slid = gemtk_wm_get_scroll_info(rootwin->win);
+
+ *x = slid->x_pos * slid->x_unit_px;
+ *y = slid->y_pos * slid->y_unit_px;
+}
+
+void window_get_grect(ROOTWIN *rootwin, enum browser_area_e which, GRECT *d)
+{
+
+ d->g_x = 0;
+ d->g_y = 0;
+ d->g_w = 0;
+ d->g_h = 0;
+
+ if (which == BROWSER_AREA_TOOLBAR) {
+ // gemtk_wm_get_grect(rootwin->win, GEMTK_WM_AREA_TOOLBAR, d);
+ toolbar_get_grect(rootwin->toolbar, 0, d);
+
+ } else if (which == BROWSER_AREA_CONTENT) {
+
+ GRECT tb_area;
+
+ gemtk_wm_get_grect(rootwin->win, GEMTK_WM_AREA_WORK, d);
+ toolbar_get_grect(rootwin->toolbar, 0, &tb_area);
+
+ d->g_y += tb_area.g_h;
+ d->g_h -= tb_area.g_h;
+
+ } else if (which == BROWSER_AREA_URL_INPUT) {
+
+ toolbar_get_grect(rootwin->toolbar, TOOLBAR_AREA_URL, d);
+
+ } else if (which == BROWSER_AREA_SEARCH) {
+ // todo: check if search is visible
+ toolbar_get_grect(rootwin->toolbar, TOOLBAR_AREA_SEARCH, d);
+ } else {
+
+ }
+
+
+ // sanitize the results
+ if (d->g_h < 0) {
+ d->g_h = 0;
+ }
+ if (d->g_w < 0) {
+ d->g_w = 0;
+ }
+
+ //printf("window_get_grect %d:", which);
+ //dbg_grect("", d);
+
+}
+
+
+void window_open_search(ROOTWIN *rootwin, bool reformat)
+{
+ struct browser_window *bw;
+ struct gui_window *gw;
+ GRECT area;
+ OBJECT *obj;
+
+ LOG("rootwin %p", rootwin);
+
+ gw = rootwin->active_gui_window;
+ bw = gw->browser->bw;
+ obj = toolbar_get_form(rootwin->toolbar);
+
+ if (gw->search == NULL) {
+ gw->search = nsatari_search_session_create(obj, gw);
+ }
+
+ toolbar_set_visible(rootwin->toolbar, TOOLBAR_AREA_SEARCH, true);
+ window_get_grect(rootwin, BROWSER_AREA_TOOLBAR, &area);
+ gemtk_wm_set_toolbar_size(rootwin->win, area.g_h);
+ window_get_grect(rootwin, BROWSER_AREA_SEARCH, &area);
+ window_schedule_redraw_grect(rootwin, &area);
+ window_process_redraws(rootwin);
+ window_set_focus(rootwin, SEARCH_INPUT, NULL);
+
+ window_get_grect(rootwin, BROWSER_AREA_CONTENT, &area);
+ if (reformat) {
+ browser_window_reformat(bw, false, area.g_w, area.g_h);
+ }
+}
+
+void window_close_search(ROOTWIN *rootwin)
+{
+ struct browser_window *bw;
+ struct gui_window *gw;
+ GRECT area;
+ OBJECT *obj;
+
+
+ gw = rootwin->active_gui_window;
+ bw = gw->browser->bw;
+ obj = gemtk_obj_get_tree(TOOLBAR);
+
+ if (gw->search != NULL) {
+ nsatari_search_session_destroy(gw->search);
+ gw->search = NULL;
+ }
+
+ toolbar_set_visible(rootwin->toolbar, TOOLBAR_AREA_SEARCH, false);
+ window_get_grect(rootwin, BROWSER_AREA_TOOLBAR, &area);
+ gemtk_wm_set_toolbar_size(rootwin->win, area.g_h);
+ window_get_grect(rootwin, BROWSER_AREA_CONTENT, &area);
+ browser_window_reformat(bw, false, area.g_w, area.g_h);
+}
+
+/**
+ * Redraw the favicon
+*/
+void window_redraw_favicon(ROOTWIN *rootwin, GRECT *clip_ro)
+{
+ GRECT work, visible, clip;
+
+ assert(rootwin);
+
+ //printf("window_redraw_favicon: root: %p, win: %p\n", rootwin, rootwin->win);
+
+ gemtk_wm_clear(rootwin->win);
+ gemtk_wm_get_grect(rootwin->win, GEMTK_WM_AREA_WORK, &work);
+
+ if (clip_ro == NULL) {
+ clip = work;
+ } else {
+ clip = *clip_ro;
+ if(!rc_intersect(&work, &clip)) {
+ return;
+ }
+ }
+
+ //dbg_grect("favicon redrw area", clip);
+ //dbg_grect("favicon work area", &work);
+
+ if (rootwin->icon == NULL) {
+ //printf("window_redraw_favicon OBJCTREE\n");
+
+ OBJECT * tree = gemtk_obj_get_tree(ICONIFY);
+ tree->ob_x = work.g_x;
+ tree->ob_y = work.g_y;
+ tree->ob_width = work.g_w;
+ tree->ob_height = work.g_h;
+
+ wind_get_grect(rootwin->aes_handle, WF_FIRSTXYWH, &visible);
+ while (visible.g_h > 0 && visible.g_w > 0) {
+
+ if (rc_intersect(&clip, &visible)) {
+ //dbg_grect("redraw vis area", &visible);
+ objc_draw(tree, 0, 8, visible.g_x, visible.g_y, visible.g_w,
+ visible.g_h);
+ } else {
+ //dbg_grect("redraw vis area outside", &visible);
+ }
+
+ wind_get_grect(rootwin->aes_handle, WF_NEXTXYWH, &visible);
+ }
+
+ } else {
+ //printf("window_redraw_favicon image %p\n", rootwin->icon);
+ VdiHdl plot_vdi_handle = plot_get_vdi_handle();
+ short pxy[4];
+ int xoff=0;
+
+ if (work.g_w > work.g_h) {
+ xoff = ((work.g_w-work.g_h)/2);
+ work.g_w = work.g_h;
+ }
+ plot_set_dimensions( work.g_x+xoff, work.g_y, work.g_w,
+ work.g_h);
+
+ wind_get_grect(rootwin->aes_handle, WF_FIRSTXYWH, &visible);
+ while (visible.g_h > 0 && visible.g_w > 0) {
+
+ if (rc_intersect(&clip, &visible)) {
+
+ //dbg_grect("redraw vis area", &visible);
+
+ // Manually clip drawing region:
+ pxy[0] = visible.g_x;
+ pxy[1] = visible.g_y;
+ pxy[2] = pxy[0] + visible.g_w-1;
+ pxy[3] = pxy[1] + visible.g_h-1;
+ vs_clip(plot_vdi_handle, 1, (short*)&pxy);
+ //dbg_pxy("vdi clip", (short*)&pxy);
+
+ atari_plotters.bitmap(0, 0, work.g_w, work.g_h,
+ rootwin->icon, 0xffffff, 0);
+ } else {
+ //dbg_grect("redraw vis area outside", &visible);
+ }
+
+ wind_get_grect(rootwin->aes_handle, WF_NEXTXYWH, &visible);
+ }
+ }
+}
+
+/***
+* Schedule an redraw area, redraw requests during redraw are
+* not optimized (merged) into other areas, so that the redraw
+* functions can spot the change.
+*
+*/
+void window_schedule_redraw_grect(ROOTWIN *rootwin, GRECT *area)
+{
+ GRECT work;
+
+
+ //dbg_grect("window_schedule_redraw_grect input ", area);
+
+ gemtk_wm_get_grect(rootwin->win, GEMTK_WM_AREA_WORK, &work);
+ if(!rc_intersect(area, &work))
+ return;
+
+ //dbg_grect("window_schedule_redraw_grect intersection ", &work);
+
+ redraw_slot_schedule_grect(&rootwin->redraw_slots, &work, redraw_active);
+}
+
+static void window_redraw_content(ROOTWIN *rootwin, GRECT *content_area,
+ GRECT *clip,
+ struct gemtk_wm_scroll_info_s * slid,
+ struct browser_window *bw)
+{
+
+ struct rect redraw_area;
+ GRECT content_area_rel;
+ float oldscale = 1.0;
+
+ //dbg_grect("browser redraw, content area", content_area);
+ //dbg_grect("browser redraw, content clip", clip);
+
+ plot_set_dimensions(content_area->g_x, content_area->g_y,
+ content_area->g_w, content_area->g_h);
+ oldscale = plot_set_scale(browser_window_get_scale(rootwin->active_gui_window->browser->bw));
+
+ /* first, we make the coords relative to the content area: */
+ content_area_rel.g_x = clip->g_x - content_area->g_x;
+ content_area_rel.g_y = clip->g_y - content_area->g_y;
+ content_area_rel.g_w = clip->g_w;
+ content_area_rel.g_h = clip->g_h;
+
+ if (content_area_rel.g_x < 0) {
+ content_area_rel.g_w += content_area_rel.g_x;
+ content_area_rel.g_x = 0;
+ }
+
+ if (content_area_rel.g_y < 0) {
+ content_area_rel.g_h += content_area_rel.g_y;
+ content_area_rel.g_y = 0;
+ }
+
+ //dbg_grect("browser redraw, relative plot coords:", &content_area_rel);
+
+ redraw_area.x0 = content_area_rel.g_x;
+ redraw_area.y0 = content_area_rel.g_y;
+ redraw_area.x1 = content_area_rel.g_x + content_area_rel.g_w;
+ redraw_area.y1 = content_area_rel.g_y + content_area_rel.g_h;
+
+ plot_clip(&redraw_area);
+
+ //dbg_rect("rdrw area", &redraw_area);
+
+ browser_window_redraw( bw, -(slid->x_pos*slid->x_unit_px),
+ -(slid->y_pos*slid->y_unit_px), &redraw_area, &rootwin_rdrw_ctx);
+
+ plot_set_scale(oldscale);
+}
+
+
+void window_place_caret(ROOTWIN *rootwin, short mode, int content_x,
+ int content_y, int h, GRECT *work)
+{
+ struct s_caret *caret = &rootwin->caret;
+ VdiHdl vh = gemtk_wm_get_vdi_handle(rootwin->win);
+ short pxy[8];
+ GRECT mywork, caret_pos;
+ MFDB screen;
+ int scroll_x, scroll_y;
+ uint16_t *fd_addr;
+ struct gemtk_wm_scroll_info_s *slid;
+ short colors[2] = {G_BLACK, G_WHITE};
+ bool render_required = false;
+
+ // avoid duplicate draw of the caret:
+ if (mode == 1 &&(caret->state&CARET_STATE_VISIBLE)!=0) {
+ if (caret->dimensions.g_x == content_x
+ && caret->dimensions.g_y == content_y
+ && caret->dimensions.g_h == h) {
+ return;
+ }
+ }
+
+ if(work == NULL) {
+ window_get_grect(rootwin, BROWSER_AREA_CONTENT, &mywork);
+ work = &mywork;
+ }
+ slid = gemtk_wm_get_scroll_info(rootwin->win);
+
+ scroll_x = slid->x_pos * slid->x_unit_px;
+ scroll_y = slid->y_pos * slid->y_unit_px;
+
+ init_mfdb(0, 1, h, 0, &screen);
+
+ // enable clipping:
+ pxy[0] = work->g_x;
+ pxy[1] = work->g_y;
+ pxy[2] = pxy[0] + work->g_w - 1;
+ pxy[3] = pxy[1] + work->g_h - 1;
+ vs_clip(vh, 1, pxy);
+
+ // when the caret is visible, undraw it:
+ if (caret->symbol.fd_addr != NULL
+ && (caret->state&CARET_STATE_VISIBLE)!=0) {
+
+ caret_pos.g_x = work->g_x + (caret->dimensions.g_x - scroll_x);
+ caret_pos.g_y = work->g_y + (caret->dimensions.g_y - scroll_y);
+ caret_pos.g_w = caret->dimensions.g_w;
+ caret_pos.g_h = caret->dimensions.g_h;
+
+ if (rc_intersect(work, &caret_pos)) {
+
+ pxy[0] = 0;
+ pxy[1] = 0;
+ pxy[2] = caret->dimensions.g_w-1;
+ pxy[3] = caret->dimensions.g_h-1;
+
+ pxy[4] = caret_pos.g_x;
+ pxy[5] = caret_pos.g_y;
+ pxy[6] = pxy[4] + caret_pos.g_w-1;
+ pxy[7] = pxy[5] + caret_pos.g_h-1;
+
+ vrt_cpyfm(vh, MD_XOR, pxy, &caret->symbol, &screen, colors);
+ }
+ }
+ if (mode == 0) {
+ // update state:
+ caret->state &= ~CARET_STATE_VISIBLE;
+ goto exit;
+ }
+
+ // when the caret isn't allocated, create it:
+ if (caret->symbol.fd_addr == NULL) {
+ caret->fd_size = init_mfdb(1, 16, h, MFDB_FLAG_ZEROMEM,
+ &caret->symbol);
+ render_required = true;
+ } else {
+ // the caret may need more memory:
+ if (caret->dimensions.g_h < h) {
+ caret->fd_size = init_mfdb(1, 16, h, MFDB_FLAG_NOALLOC,
+ &caret->symbol);
+ realloc(caret->symbol.fd_addr, caret->fd_size);
+ render_required = true;
+ }
+ }
+
+ // set new caret position:
+ caret->dimensions.g_x = content_x;
+ caret->dimensions.g_y = content_y;
+ caret->dimensions.g_w = 1;
+ caret->dimensions.g_h = h;
+
+ // draw the caret into the mfdb buffer:
+ if (render_required) {
+ int i;
+
+ assert(caret->symbol.fd_nplanes == 1);
+ assert(caret->symbol.fd_w == 16);
+
+ // draw an vertical line into the mfdb buffer
+ fd_addr = (uint16_t*)caret->symbol.fd_addr;
+ for(i = 0; i<caret->symbol.fd_h; i++) {
+ fd_addr[i] = 0xFFFF;
+ }
+ }
+
+ // convert content coords to screen coords:
+
+ caret_pos.g_x = work->g_x + (content_x - scroll_x);
+ caret_pos.g_y = work->g_y + (content_y - scroll_y);
+ caret_pos.g_w = caret->dimensions.g_w;
+ caret_pos.g_h = caret->dimensions.g_h;
+
+ if (rc_intersect(work, &caret_pos) && redraw_active == false) {
+
+ pxy[0] = 0;
+ pxy[1] = 0;
+ pxy[2] = caret->dimensions.g_w-1;
+ pxy[3] = caret->dimensions.g_h-1;
+
+ pxy[4] = caret_pos.g_x;
+ pxy[5] = caret_pos.g_y;
+ pxy[6] = pxy[4] + caret_pos.g_w-1;
+ pxy[7] = pxy[5] + caret_pos.g_h-1;
+
+ //dbg_pxy("caret screen coords (md_repl)", &pxy[4]);
+
+ // TODO: walk rectangle list (use MD_REPLACE then)
+ // draw caret to screen coords:
+ vrt_cpyfm(vh, /*MD_REPLACE*/ MD_XOR, pxy, &caret->symbol, &screen, colors);
+
+ // update state:
+ caret->state |= CARET_STATE_VISIBLE;
+ }
+
+exit:
+ // disable clipping:
+ vs_clip(gemtk_wm_get_vdi_handle(rootwin->win), 0, pxy);
+}
+
+void window_process_redraws(ROOTWIN * rootwin)
+{
+ GRECT visible_ro, tb_area, content_area;
+ short i;
+ short scroll_x=0, scroll_y=0;
+ bool caret_rdrw_required = false;
+ struct gemtk_wm_scroll_info_s *slid =NULL;
+ int caret_h = 0;
+ struct s_caret *caret = &rootwin->caret;
+
+ redraw_active = true;
+
+ window_get_grect(rootwin, BROWSER_AREA_TOOLBAR, &tb_area);
+ //gemtk_wm_set_toolbar_size(rootwin->win, tb_area.g_h);
+ window_get_grect(rootwin, BROWSER_AREA_CONTENT, &content_area);
+
+ //dbg_grect("content area", &content_area);
+ //dbg_grect("window_process_redraws toolbar area", &tb_area);
+
+ while(plot_lock() == false);
+
+ if (((rootwin->caret.state & CARET_STATE_ENABLED)!=0)
+ && rootwin->caret.dimensions.g_h > 0) {
+ // hide caret:
+ window_place_caret(rootwin, 0, -1, -1, -1, &content_area);
+ }
+/*
+ short pxy_clip[4];
+ pxy_clip[0] = tb_area.g_x;
+ pxy_clip[0] = tb_area.g_y;
+ pxy_clip[0] = pxy_clip[0] + tb_area.g_w + content_area.g_w - 1;
+ pxy_clip[0] = pxy_clip[1] + tb_area.g_h + content_area.g_h - 1;
+ vs_clip(gemtk_wm_get_vdi_handle(rootwin->win), 1, pxy_clip);
+ //gemtk_wm_clear(rootwin->win);
+*/
+ wind_get_grect(rootwin->aes_handle, WF_FIRSTXYWH, &visible_ro);
+ while (visible_ro.g_w > 0 && visible_ro.g_h > 0) {
+ plot_set_abs_clipping(&visible_ro);
+
+ //dbg_grect("visible ", &visible_ro);
+
+ // TODO: optimze the rectangle list -
+ // remove rectangles which were completly inside the visible area.
+ // that way we don't have to loop over again...
+ for(i=0; i<rootwin->redraw_slots.areas_used; i++) {
+
+ GRECT rdrw_area_ro = {
+ rootwin->redraw_slots.areas[i].x0,
+ rootwin->redraw_slots.areas[i].y0,
+ rootwin->redraw_slots.areas[i].x1 -
+ rootwin->redraw_slots.areas[i].x0,
+ rootwin->redraw_slots.areas[i].y1 -
+ rootwin->redraw_slots.areas[i].y0
+ };
+
+ if (!rc_intersect(&visible_ro, &rdrw_area_ro)) {
+ continue;
+ }
+ GRECT rdrw_area = rdrw_area_ro;
+
+ if (rc_intersect(&tb_area, &rdrw_area)) {
+ toolbar_redraw(rootwin->toolbar, &rdrw_area);
+ }
+
+ rdrw_area = rdrw_area_ro;
+ if (rc_intersect(&content_area, &rdrw_area)) {
+
+ if(slid == NULL) {
+ slid = gemtk_wm_get_scroll_info(rootwin->win);
+
+ scroll_x = slid->x_pos * slid->x_unit_px;
+ scroll_y = slid->y_pos * slid->y_unit_px;
+ }
+
+ window_redraw_content(rootwin, &content_area, &rdrw_area,
+ slid,
+ rootwin->active_gui_window->browser->bw);
+ if (((rootwin->caret.state & CARET_STATE_ENABLED)!=0)) {
+
+ GRECT caret_pos;
+
+ caret_pos.g_x = content_area.g_x +
+ (caret->dimensions.g_x - scroll_x);
+ caret_pos.g_y = content_area.g_y +
+ (caret->dimensions.g_y - scroll_y);
+ caret_pos.g_w = caret->dimensions.g_w;
+ caret_pos.g_h = caret->dimensions.g_h;
+
+ if (gemtk_rc_intersect_ro(&caret_pos, &content_area)) {
+ caret_rdrw_required = true;
+ }
+ }
+
+ }
+ }
+ wind_get_grect(rootwin->aes_handle, WF_NEXTXYWH, &visible_ro);
+ }
+
+
+ // disable clipping:
+ //vs_clip(gemtk_wm_get_vdi_handle(rootwin->win), 0, pxy_clip);
+
+ if (caret_rdrw_required && ((rootwin->caret.state & CARET_STATE_ENABLED)!=0)) {
+
+ // force redraw of caret:
+ caret_h = rootwin->caret.dimensions.g_h;
+ rootwin->caret.dimensions.g_h = 0;
+ redraw_active = false;
+ window_place_caret(rootwin, 1, rootwin->caret.dimensions.g_x,
+ rootwin->caret.dimensions.g_y,
+ caret_h, &content_area);
+ }
+
+ rootwin->redraw_slots.areas_used = 0;
+ redraw_active = false;
+
+ plot_unlock();
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* Event Handlers: */
+/* -------------------------------------------------------------------------- */
+static void on_content_mouse_move(ROOTWIN *rootwin, GRECT *content_area)
+{
+ int mx, my, sx, sy;
+ struct gemtk_wm_scroll_info_s *slid;
+ struct gui_window *gw;
+ struct browser_window *bw;
+
+ // make relative mouse coords:
+ mx = aes_event_out.emo_mouse.p_x - content_area->g_x;
+ my = aes_event_out.emo_mouse.p_y - content_area->g_y;
+
+ slid = gemtk_wm_get_scroll_info(rootwin->win);
+ gw = window_get_active_gui_window(rootwin);
+ bw = gw->browser->bw;
+
+ // calculate scroll pos. in pixel:
+ sx = slid->x_pos * slid->x_unit_px;
+ sy = slid->y_pos * slid->y_unit_px;
+
+ browser_window_mouse_track(bw, 0, mx + sx, my + sy);
+}
+
+static void on_content_mouse_click(ROOTWIN *rootwin)
+{
+ short dummy, mbut, mx, my;
+ GRECT cwork;
+ browser_mouse_state bmstate = 0;
+ struct gui_window *gw;
+ struct gemtk_wm_scroll_info_s *slid;
+
+ gw = window_get_active_gui_window(rootwin);
+ if(input_window != gw) {
+ gui_set_input_gui_window(gw);
+ }
+
+ window_set_focus(gw->root, BROWSER, (void*)gw->browser );
+ window_get_grect(gw->root, BROWSER_AREA_CONTENT, &cwork);
+
+ /* convert screen coords to component coords: */
+ mx = aes_event_out.emo_mouse.p_x - cwork.g_x;
+ my = aes_event_out.emo_mouse.p_y - cwork.g_y;
+ //printf("content click at %d,%d\n", mx, my);
+
+ /* Translate GEM key state to netsurf mouse modifier */
+ if ( aes_event_out.emo_kmeta & (K_RSHIFT | K_LSHIFT)) {
+ bmstate |= BROWSER_MOUSE_MOD_1;
+ } else {
+ bmstate &= ~(BROWSER_MOUSE_MOD_1);
+ }
+ if ( (aes_event_out.emo_kmeta & K_CTRL) ) {
+ bmstate |= BROWSER_MOUSE_MOD_2;
+ } else {
+ bmstate &= ~(BROWSER_MOUSE_MOD_2);
+ }
+ if ( (aes_event_out.emo_kmeta & K_ALT) ) {
+ bmstate |= BROWSER_MOUSE_MOD_3;
+ } else {
+ bmstate &= ~(BROWSER_MOUSE_MOD_3);
+ }
+
+ /* convert component coords to scrolled content coords: */
+ slid = gemtk_wm_get_scroll_info(rootwin->win);
+ int sx_origin = mx;
+ int sy_origin = my;
+
+ short rel_cur_x, rel_cur_y;
+ short prev_x=sx_origin, prev_y=sy_origin;
+ bool dragmode = false;
+
+ /* Detect left mouse button state and compare with event state: */
+ graf_mkstate(&rel_cur_x, &rel_cur_y, &mbut, &dummy);
+ if( (mbut & 1) && (aes_event_out.emo_mbutton & 1) ) {
+ /* Mouse still pressed, report drag */
+ rel_cur_x = (rel_cur_x - cwork.g_x);
+ rel_cur_y = (rel_cur_y - cwork.g_y);
+ browser_window_mouse_click( gw->browser->bw,
+ BROWSER_MOUSE_DRAG_ON|BROWSER_MOUSE_DRAG_1,
+ rel_cur_x + slid->x_pos * slid->x_unit_px,
+ rel_cur_y + slid->y_pos * slid->y_unit_px);
+ do {
+ // only consider movements of 5px or more as drag...:
+ if( abs(prev_x-rel_cur_x) > 5 || abs(prev_y-rel_cur_y) > 5 ) {
+ browser_window_mouse_track( gw->browser->bw,
+ BROWSER_MOUSE_DRAG_ON|BROWSER_MOUSE_DRAG_1,
+ rel_cur_x + slid->x_pos * slid->x_unit_px,
+ rel_cur_y + slid->y_pos * slid->y_unit_px);
+ prev_x = rel_cur_x;
+ prev_y = rel_cur_y;
+ dragmode = true;
+ } else {
+ if( dragmode == false ) {
+ browser_window_mouse_track( gw->browser->bw,BROWSER_MOUSE_PRESS_1,
+ rel_cur_x + slid->x_pos * slid->x_unit_px,
+ rel_cur_y + slid->y_pos * slid->y_unit_px);
+ }
+ }
+
+ // we may need to process scrolling:
+ // TODO: this doesn't work, because gemtk schedules redraw via
+ // AES window messages but we do not process them right here...
+ if (rootwin->redraw_slots.areas_used > 0) {
+ window_process_redraws(rootwin);
+ }
+ evnt_timer(150);
+
+ graf_mkstate(&rel_cur_x, &rel_cur_y, &mbut, &dummy);
+ rel_cur_x = (rel_cur_x - cwork.g_x);
+ rel_cur_y = (rel_cur_y - cwork.g_y);
+ } while( mbut & 1 );
+ browser_window_mouse_track(gw->browser->bw, 0,
+ rel_cur_x + slid->x_pos * slid->x_unit_px,
+ rel_cur_y + slid->y_pos * slid->y_unit_px);
+ } else {
+ /* Right button pressed? */
+ if ((aes_event_out.emo_mbutton & 2 ) ) {
+ context_popup(gw, aes_event_out.emo_mouse.p_x,
+ aes_event_out.emo_mouse.p_y);
+ } else {
+ browser_window_mouse_click(gw->browser->bw,
+ bmstate|BROWSER_MOUSE_PRESS_1,
+ sx_origin + slid->x_pos * slid->x_unit_px,
+ sy_origin + slid->y_pos * slid->y_unit_px);
+ browser_window_mouse_click(gw->browser->bw,
+ bmstate|BROWSER_MOUSE_CLICK_1,
+ sx_origin + slid->x_pos * slid->x_unit_px,
+ sy_origin + slid->y_pos * slid->y_unit_px);
+ }
+ }
+ if (rootwin->redraw_slots.areas_used > 0) {
+ window_process_redraws(rootwin);
+ }
+}
+
+/*
+ Report keypress to browser component.
+ parameter:
+ - unsigned short nkc ( CFLIB normalised key code )
+*/
+static bool on_content_keypress(struct gui_window *gw, unsigned short nkc)
+{
+ bool r = false;
+ unsigned char ascii = (nkc & 0xFF);
+ long ucs4;
+ long ik = nkc_to_input_key( nkc, &ucs4 );
+
+ // pass event to specific control?
+
+ if (ik == 0) {
+ if (ascii >= 9) {
+ r = browser_window_key_press(gw->browser->bw, ucs4);
+ }
+ } else {
+ r = browser_window_key_press(gw->browser->bw, ik);
+ if (r == false) {
+
+ GRECT g;
+ GUIWIN * w = gw->root->win;
+ window_get_grect(gw->root, BROWSER_AREA_CONTENT, &g);
+
+ struct gemtk_wm_scroll_info_s *slid = gemtk_wm_get_scroll_info(w);
+
+ switch( ik ) {
+ case NS_KEY_LINE_START:
+ gemtk_wm_scroll(w, GEMTK_WM_HSLIDER, -(g.g_w/slid->x_unit_px),
+ false);
+ r = true;
+ break;
+
+ case NS_KEY_LINE_END:
+ gemtk_wm_scroll(w, GEMTK_WM_HSLIDER, (g.g_w/slid->x_unit_px),
+ false);
+ r = true;
+ break;
+
+ case NS_KEY_PAGE_UP:
+ gemtk_wm_scroll(w, GEMTK_WM_VSLIDER, -(g.g_h/slid->y_unit_px),
+ false);
+ r = true;
+ break;
+
+ case NS_KEY_PAGE_DOWN:
+ gemtk_wm_scroll(w, GEMTK_WM_VSLIDER, (g.g_h/slid->y_unit_px),
+ false);
+ r = true;
+ break;
+
+ case NS_KEY_RIGHT:
+ gemtk_wm_scroll(w, GEMTK_WM_HSLIDER, -1, false);
+ r = true;
+ break;
+
+ case NS_KEY_LEFT:
+ gemtk_wm_scroll(w, GEMTK_WM_HSLIDER, 1, false);
+ r = true;
+ break;
+
+ case NS_KEY_UP:
+ gemtk_wm_scroll(w, GEMTK_WM_VSLIDER, -1, false);
+ r = true;
+ break;
+
+ case NS_KEY_DOWN:
+ gemtk_wm_scroll(w, GEMTK_WM_VSLIDER, 1, false);
+ r = true;
+ break;
+
+ case NS_KEY_TEXT_START:
+ window_scroll_by(gw->root, 0, 0);
+ r = true;
+ break;
+
+ default:
+ break;
+ }
+ gemtk_wm_update_slider(w, GEMTK_WM_VSLIDER|GEMTK_WM_HSLIDER);
+ }
+ }
+
+ return(r);
+}
+
+static short on_window_key_input(ROOTWIN *rootwin, unsigned short nkc)
+{
+ bool done = false;
+ struct gui_window * gw = window_get_active_gui_window(rootwin);
+
+ if( gw == NULL )
+ return(false);
+
+ if(window_url_widget_has_focus((void*)gw->root)) {
+ /* make sure we report for the root window and report...: */
+ done = toolbar_key_input(gw->root->toolbar, nkc);
+ } else {
+ if( window_widget_has_focus(input_window->root, BROWSER,
+ (void*)input_window->browser)) {
+ done = on_content_keypress(input_window, nkc);
+ }
+ else if(window_widget_has_focus(input_window->root, SEARCH_INPUT, NULL)) {
+ OBJECT * obj;
+ obj = toolbar_get_form(input_window->root->toolbar);
+ obj[TOOLBAR_BT_SEARCH_FWD].ob_state &= ~OS_DISABLED;
+ obj[TOOLBAR_BT_SEARCH_BACK].ob_state &= ~OS_DISABLED;
+ window_schedule_redraw_grect(input_window->root,
+ gemtk_obj_screen_rect(obj, TOOLBAR_BT_SEARCH_FWD));
+ window_schedule_redraw_grect(input_window->root,
+ gemtk_obj_screen_rect(obj, TOOLBAR_BT_SEARCH_BACK));
+ }
+ }
+ return((done==true) ? 1 : 0);
+}
+
+
+static void on_redraw(ROOTWIN *rootwin, short msg[8])
+{
+ GRECT clip = {msg[4], msg[5], msg[6], msg[7]};
+
+ //dbg_grect("on_redraw", &clip);
+
+ if(gemtk_wm_get_state(rootwin->win) & GEMTK_WM_STATUS_ICONIFIED) {
+ // TODO: remove asignment:
+ window_redraw_favicon(rootwin, NULL);
+ } else {
+ window_schedule_redraw_grect(rootwin, &clip);
+ }
+}
+
+static void on_resized(ROOTWIN *rootwin)
+{
+ GRECT g, work;
+ struct gui_window *gw;
+
+ gw = window_get_active_gui_window(rootwin);
+
+ //printf("resized...\n");
+
+ assert(gw != NULL);
+
+ if(gw == NULL)
+ return;
+
+ wind_get_grect(rootwin->aes_handle, WF_CURRXYWH, &g);
+ gemtk_wm_get_grect(rootwin->win, GEMTK_WM_AREA_WORK, &work);
+
+ if (rootwin->loc.g_w != g.g_w || rootwin->loc.g_h != g.g_h) {
+
+ /* resized */
+ toolbar_set_width(rootwin->toolbar, work.g_w);
+
+ if ( browser_window_has_content(gw->browser->bw) ) {
+ browser_window_reformat(gw->browser->bw, true, work.g_w, work.g_h);
+ }
+ }
+ if (rootwin->loc.g_x != g.g_x || rootwin->loc.g_y != g.g_y) {
+ /* moved */
+ toolbar_set_origin(rootwin->toolbar, work.g_x, work.g_y);
+ }
+
+ rootwin->loc = g;
+}
+
+static void on_file_dropped(ROOTWIN *rootwin, short msg[8])
+{
+ char file[DD_NAMEMAX];
+ char name[DD_NAMEMAX];
+ char *buff=NULL;
+ int dd_hdl;
+ int dd_msg; /* pipe-handle */
+ long size;
+ char ext[32];
+ short mx,my,bmstat,mkstat;
+ struct gui_window *gw;
+
+ graf_mkstate(&mx, &my, &bmstat, &mkstat);
+
+ gw = window_get_active_gui_window(rootwin);
+
+ if( gw == NULL )
+ return;
+
+ if(gemtk_wm_get_state(rootwin->win) & GEMTK_WM_STATUS_ICONIFIED)
+ return;
+
+ dd_hdl = gemtk_dd_open( msg[7], DD_OK);
+ if( dd_hdl<0)
+ return; /* pipe not open */
+ memset( ext, 0, 32);
+ strcpy( ext, "ARGS");
+ dd_msg = gemtk_dd_sexts( dd_hdl, ext);
+ if( dd_msg<0)
+ goto error;
+ dd_msg = gemtk_dd_rtry( dd_hdl, (char*)&name[0], (char*)&file[0], (char*)&ext[0], &size);
+ if( size+1 >= PATH_MAX )
+ goto error;
+ if( !strncmp( ext, "ARGS", 4) && dd_msg > 0) {
+ gemtk_dd_reply(dd_hdl, DD_OK);
+ buff = (char*)malloc(sizeof(char)*(size+1));
+ if (buff != NULL) {
+ if (Fread(dd_hdl, size, buff ) == size) {
+
+ int sx, sy;
+ bool processed = false;
+ GRECT content_area;
+
+ buff[size] = 0;
+
+ LOG("file: %s, ext: %s, size: %ld dropped at: %d,%d\n", (char *)buff, (char *)&ext, size, mx, my);
+
+ gui_window_get_scroll(gw, &sx, &sy);
+
+ window_get_grect(rootwin, BROWSER_AREA_CONTENT, &content_area);
+ mx = mx - content_area.g_x;
+ my = my - content_area.g_y;
+ if((mx < 0 || mx > content_area.g_w)
+ || (my < 0 || my > content_area.g_h))
+ return;
+
+ processed = browser_window_drop_file_at_point(gw->browser->bw,
+ mx+sx, my+sy,
+ NULL);
+ if(processed == true) {
+ nserror ret;
+ char *utf8_fn;
+
+ ret = utf8_from_local_encoding(buff, 0, &utf8_fn);
+ if (ret != NSERROR_OK) {
+ free(buff);
+ /* A bad encoding should never happen */
+ LOG("utf8_from_local_encoding failed");
+ assert(ret != NSERROR_BAD_ENCODING);
+ /* no memory */
+ goto error;
+ }
+ processed = browser_window_drop_file_at_point(gw->browser->bw,
+ mx+sx, my+sy,
+ utf8_fn);
+ free(utf8_fn);
+ }
+
+ if(processed == false) {
+ // TODO: use localized string:
+ if(gemtk_msg_box_show(GEMTK_MSG_BOX_CONFIRM, "Open File?") > 0) {
+ nsurl * ns_url = NULL;
+ char * tmp_url = local_file_to_url(buff);
+ if ((tmp_url != NULL)
+ && nsurl_create(tmp_url, &ns_url) == NSERROR_OK) {
+ browser_window_navigate(gw->browser->bw, ns_url, NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL, NULL, NULL);
+ nsurl_unref(ns_url);
+ }
+ }
+ }
+ }
+ }
+ }
+error:
+ if (buff) {
+ free(buff);
+ }
+ gemtk_dd_close( dd_hdl);
+}
+
+static void toolbar_redraw_cb(GUIWIN *win, uint16_t msg, GRECT *clip)
+{
+ struct rootwin_data_s * ud;
+
+ if (msg != WM_REDRAW) {
+ ud = gemtk_wm_get_user_data(win);
+
+ assert(ud);
+
+ toolbar_redraw(ud->rootwin->toolbar, clip);
+ }
+}
diff --git a/frontends/atari/rootwin.h b/frontends/atari/rootwin.h
new file mode 100644
index 000000000..99b286f70
--- /dev/null
+++ b/frontends/atari/rootwin.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_ATARI_BROWSER_WIN_H
+#define NS_ATARI_BROWSER_WIN_H
+
+#include <atari/gui.h>
+
+#define GEMTK_WM_VISIBLE(gw) (gw->root->handle->status & WS_OPEN)
+#define GEMWIN_VISIBLE(win) (win->status & WS_OPEN)
+
+#define WIDGET_STATUSBAR 0x1
+#define WIDGET_TOOLBAR 0x2
+#define WIDGET_SCROLL 0x4
+#define WIDGET_RESIZE 0x8
+#define WIN_TOP 0x100
+
+enum browser_area_e {
+ BROWSER_AREA_CONTENT = 1,
+ BROWSER_AREA_STATUSBAR,
+ BROWSER_AREA_TOOLBAR,
+ BROWSER_AREA_URL_INPUT,
+ BROWSER_AREA_SEARCH
+};
+
+
+/* -------------------------------------------------------------------------- */
+/* Public module functions: */
+/* -------------------------------------------------------------------------- */
+
+/* Creates an normal Browser window with [toolbar], [statusbar] */
+int window_create(struct gui_window * gw,
+ struct browser_window * bw,
+ struct gui_window * existing,
+ unsigned long flags );
+/* Destroys WinDom part of gui_window */
+int window_destroy(ROOTWIN *rootwin);
+
+/** show the window at specified position and make gw the active tab. */
+void window_open(ROOTWIN *rootwin, struct gui_window *gw, GRECT pos);
+
+void window_snd_redraw(ROOTWIN *rootwin, short x, short y, short w, short h );
+/* Update Shade / Unshade state of the fwd/back buttons*/
+void window_update_back_forward(struct s_gui_win_root * rootwin);
+/* set root browser component: */
+void window_attach_browser(ROOTWIN *rootwin, CMP_BROWSER b);
+
+/* set focus element */
+void window_set_focus(ROOTWIN *rootwin, enum focus_element_type type,
+ void * element );
+/* Shade / Unshade the forward/back bt. of toolbar, depending on history.*/
+bool window_widget_has_focus(ROOTWIN *rootwin, enum focus_element_type t,
+ void * element);
+bool window_url_widget_has_focus(ROOTWIN *rootwin);
+void window_set_url(ROOTWIN *rootwin, const char * text);
+void window_set_stauts(ROOTWIN *rootwin, char * text);
+void window_set_title(ROOTWIN *rootwin, char * text);
+void window_set_content_size(ROOTWIN *rootwin, int w, int h);
+void window_set_icon(ROOTWIN *rootwin, struct bitmap * bmp );
+void window_set_active_gui_window(ROOTWIN *rootwin, struct gui_window *gw);
+void window_restore_active_gui_window(ROOTWIN *rootwin);
+void window_open_search(ROOTWIN *rootwin, bool reformat);
+void window_close_search(ROOTWIN *rootwin);
+void window_scroll_by(ROOTWIN *rootwin, int x, int y);
+void window_schedule_redraw_grect(ROOTWIN *rootwin, GRECT *area);
+void window_process_redraws(ROOTWIN * rootwin);
+void window_place_caret(ROOTWIN *rootwin, short mode, int content_x,
+ int content_y, int h, GRECT *work);
+struct gui_window * window_get_active_gui_window(ROOTWIN * rootwin);
+void window_get_scroll(ROOTWIN *rootwin, int *x, int *y);
+void window_get_grect(ROOTWIN *rootwin, enum browser_area_e which, GRECT *d);
+void window_redraw_favicon(struct s_gui_win_root * rootwin, GRECT *clip);
+void window_unref_gui_window(ROOTWIN *rootwin, struct gui_window *gw);
+bool window_key_input(unsigned short kcode, unsigned short kstate,
+ unsigned short nkc);
+
+
+/* -------------------------------------------------------------------------- */
+/* Public event handlers: */
+/* -------------------------------------------------------------------------- */
+
+#endif
diff --git a/frontends/atari/save.h b/frontends/atari/save.h
new file mode 100644
index 000000000..01086be4b
--- /dev/null
+++ b/frontends/atari/save.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2012 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NSATARI_SAVE_H
+#define NSATARI_SAVE_H
+
+#endif
diff --git a/frontends/atari/schedule.c b/frontends/atari/schedule.c
new file mode 100644
index 000000000..48980426d
--- /dev/null
+++ b/frontends/atari/schedule.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ * Copyright 2011 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "utils/sys_time.h"
+#include "utils/errors.h"
+
+#include "atari/schedule.h"
+
+#ifdef DEBUG_SCHEDULER
+#include "utils/log.h"
+#else
+#define LOG(x...)
+#endif
+
+#define MS_NOW() ((clock() * 1000) / CLOCKS_PER_SEC)
+
+/* linked list of scheduled callbacks */
+static struct nscallback *schedule_list = NULL;
+
+/**
+ * scheduled callback.
+ */
+struct nscallback
+{
+ struct nscallback *next;
+ unsigned long timeout;
+ void (*callback)(void *p);
+ void *p;
+};
+
+static int max_scheduled;
+static int cur_scheduled;
+
+/**
+ * Unschedule a callback.
+ *
+ * \param callback callback function
+ * \param p user parameter, passed to callback function
+ *
+ * All scheduled callbacks matching both callback and p are removed.
+ */
+
+static nserror schedule_remove(void (*callback)(void *p), void *p)
+{
+ struct nscallback *cur_nscb;
+ struct nscallback *prev_nscb;
+ struct nscallback *unlnk_nscb;
+
+ /* check there is something on the list to remove */
+ if (schedule_list == NULL) {
+ return NSERROR_OK;
+ }
+
+ LOG("removing %p, %p", callback, p);
+
+ cur_nscb = schedule_list;
+ prev_nscb = NULL;
+
+ while (cur_nscb != NULL) {
+ if ((cur_nscb->callback == callback) &&
+ (cur_nscb->p == p)) {
+ /* item to remove */
+ LOG("callback entry %p removing %p(%p)", cur_nscb, cur_nscb->callback, cur_nscb->p);
+
+ /* remove callback */
+ unlnk_nscb = cur_nscb;
+ cur_nscb = unlnk_nscb->next;
+
+ if (prev_nscb == NULL) {
+ schedule_list = cur_nscb;
+ } else {
+ prev_nscb->next = cur_nscb;
+ }
+ free (unlnk_nscb);
+ cur_scheduled--;
+ } else {
+ /* move to next element */
+ prev_nscb = cur_nscb;
+ cur_nscb = prev_nscb->next;
+ }
+ }
+ return NSERROR_OK;
+}
+
+/* exported function documented in atari/schedule.h */
+nserror atari_schedule(int ival, void (*callback)(void *p), void *p)
+{
+ struct nscallback *nscb;
+ nserror ret;
+
+ /* remove any existing callback of this kind */
+ ret = schedule_remove(callback, p);
+ if ((ival < 0) || (ret != NSERROR_OK)) {
+ return ret;
+ }
+
+ nscb = calloc(1, sizeof(struct nscallback));
+
+ nscb->timeout = MS_NOW() + ival;
+
+ LOG("adding callback %p for %p(%p) at %d ms", nscb, callback, p, nscb->timeout);
+
+ nscb->callback = callback;
+ nscb->p = p;
+
+ /* add to list front */
+ nscb->next = schedule_list;
+ schedule_list = nscb;
+ cur_scheduled++;
+ if( cur_scheduled > max_scheduled ) {
+ max_scheduled = cur_scheduled;
+ }
+
+ return NSERROR_OK;
+}
+
+
+/* exported function documented in atari/schedule.h */
+int schedule_run(void)
+{
+ unsigned long nexttime;
+ struct nscallback *cur_nscb;
+ struct nscallback *prev_nscb;
+ struct nscallback *unlnk_nscb;
+ unsigned long now = MS_NOW();
+
+ if (schedule_list == NULL)
+ return -1;
+
+ /* reset enumeration to the start of the list */
+ cur_nscb = schedule_list;
+ prev_nscb = NULL;
+ nexttime = cur_nscb->timeout;
+
+ while (cur_nscb != NULL) {
+ if (now > cur_nscb->timeout) {
+ /* scheduled time */
+
+ /* remove callback */
+ unlnk_nscb = cur_nscb;
+ if (prev_nscb == NULL) {
+ schedule_list = unlnk_nscb->next;
+ } else {
+ prev_nscb->next = unlnk_nscb->next;
+ }
+
+ LOG("callback entry %p running %p(%p)", unlnk_nscb, unlnk_nscb->callback, unlnk_nscb->p);
+
+ /* call callback */
+ unlnk_nscb->callback(unlnk_nscb->p);
+ free(unlnk_nscb);
+ cur_scheduled--;
+
+ /* need to deal with callback modifying the list. */
+ if (schedule_list == NULL) {
+ LOG("schedule_list == NULL");
+
+ return -1; /* no more callbacks scheduled */
+ }
+
+ /* reset enumeration to the start of the list */
+ cur_nscb = schedule_list;
+ prev_nscb = NULL;
+ nexttime = cur_nscb->timeout;
+ } else {
+ /* if the time to the event is sooner than the
+ * currently recorded soonest event record it
+ */
+ if (nexttime > cur_nscb->timeout) {
+ nexttime = cur_nscb->timeout;
+ }
+ /* move to next element */
+ prev_nscb = cur_nscb;
+ cur_nscb = prev_nscb->next;
+ }
+ }
+
+ /* make rettime relative to now and convert to ms */
+ nexttime = nexttime - now;
+
+ LOG("returning time to next event as %ldms", nexttime);
+
+ /*return next event time in milliseconds (24days max wait) */
+ return nexttime;
+}
+
+
+/* exported function documented in atari/schedule.h */
+void list_schedule(void)
+{
+ struct nscallback *cur_nscb;
+
+ LOG("schedule list at ms clock %ld", MS_NOW());
+
+ cur_nscb = schedule_list;
+ while (cur_nscb != NULL) {
+ LOG("Schedule %p at %ld", cur_nscb, cur_nscb->timeout);
+ cur_nscb = cur_nscb->next;
+ }
+ LOG("Maxmium callbacks scheduled: %d", max_scheduled);
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */
diff --git a/frontends/atari/schedule.h b/frontends/atari/schedule.h
new file mode 100644
index 000000000..05eebb2d7
--- /dev/null
+++ b/frontends/atari/schedule.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ * 2012 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_ATARI_SCHEDULE_H
+#define NS_ATARI_SCHEDULE_H
+
+/**
+ * Process events up to current time.
+ *
+ * \return The number of miliseconds until the next scheduled event.
+ */
+int schedule_run(void);
+
+/**
+ * Schedule a callback.
+ *
+ * \param ival interval before the callback should be made in miliseconds.
+ * \param callback callback function.
+ * \param p user parameter, passed to callback function.
+ * \return NSERROR_OK on success or appropriate error code.
+ *
+ * The callback function will be called as soon as possible after \a ival
+ * ms have passed.
+ */
+nserror atari_schedule(int ival, void (*callback)(void *p), void *p);
+
+/**
+ * LOG all current scheduled events.
+ */
+void list_schedule(void);
+
+#endif
diff --git a/frontends/atari/scripts/env-v4e.sh b/frontends/atari/scripts/env-v4e.sh
new file mode 100755
index 000000000..6e353ffe4
--- /dev/null
+++ b/frontends/atari/scripts/env-v4e.sh
@@ -0,0 +1,7 @@
+export GCCSDK_INSTALL_CROSSBIN=/opt/netsurf-v4e/m5475-atari-mint/cross/bin/
+export GCCSDK_INSTALL_ENV="/opt/netsurf-v4e/m5475-atari-mint/env"
+export PATH=/opt/netsurf-v4e/m5475-atari-mint/cross/bin:$PATH
+export PREFIX=/opt/netsurf-v4e/
+export LD_LIBRARY_PATH=/opt/netsurf-v4e/m5475-atari-mint/lib:/opt/netsurf-v4e/m5475-atari-mint/env/lib/
+export PKG_CONFIG_PATH=/opt/netsurf-v4e/m5475-atari-mint/env/lib/pkgconfig:/opt/netsurf-v4e/lib/pkgconfig
+export C_INCLUDE_PATH=/opt/netsurf-v4e/include/:/opt/netsurf-v4e/m5475-atari-mint/env/include/
diff --git a/frontends/atari/scripts/env-x86.sh b/frontends/atari/scripts/env-x86.sh
new file mode 100755
index 000000000..027da3f3b
--- /dev/null
+++ b/frontends/atari/scripts/env-x86.sh
@@ -0,0 +1,9 @@
+#export PKG_CONFIG_PATH=${HOME}/netsurf/workspace_gtk/inst/lib/pkgconfig::
+#export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${HOME}/netsurf/workspace_gtk/inst/lib
+#export PREFIX=${HOME}/netsurf/workspace_gtk/inst
+
+export PATH=/opt/netsurf/x86/bin:$PATH
+export PREFIX=/opt/netsurf/x86
+export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/opt/netsurf/x86/lib
+export PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:/opt/netsurf/x86/lib/pkgconfig
+
diff --git a/frontends/atari/search.c b/frontends/atari/search.c
new file mode 100644
index 000000000..d72d3caaa
--- /dev/null
+++ b/frontends/atari/search.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2013 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdbool.h>
+#include <assert.h>
+
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "desktop/browser.h"
+#include "desktop/search.h"
+#include "desktop/gui_search.h"
+
+#include "atari/gui.h"
+#include "atari/rootwin.h"
+#include "atari/misc.h"
+#include "atari/toolbar.h"
+#include "atari/search.h"
+#include "atari/gemtk/gemtk.h"
+#include "atari/res/netsurf.rsh"
+
+extern struct gui_window * input_window;
+
+
+static void nsatari_search_set_status(bool found, void *p);
+static void nsatari_search_set_hourglass(bool active, void *p);
+static void nsatari_search_add_recent(const char *string, void *p);
+void nsatari_search_set_forward_state(bool active, void *p);
+void nsatari_search_set_back_state(bool active, void *p);
+
+static struct gui_search_table search_table = {
+ nsatari_search_set_status,
+ nsatari_search_set_hourglass,
+ nsatari_search_add_recent,
+ nsatari_search_set_forward_state,
+ nsatari_search_set_back_state,
+};
+
+struct gui_search_table *atari_search_table = &search_table;
+
+
+
+/**
+ * Change the displayed search status.
+ * \param found search pattern matched in text
+ * \param p the pointer sent to search_verify_new() / search_create_context()
+ */
+void nsatari_search_set_status(bool found, void *p)
+{
+ LOG("%p set status: %d\n", p, found);
+ // TODO: maybe update GUI
+}
+
+
+/**
+ * display hourglass while searching
+ * \param active start/stop indicator
+ * \param p the pointer sent to search_verify_new() / search_create_context()
+ */
+void nsatari_search_set_hourglass(bool active, void *p)
+{
+ SEARCH_FORM_SESSION s = (SEARCH_FORM_SESSION)p;
+ LOG("active: %d, session: %p", active, p);
+ if (active) {
+ gui_window_set_pointer(s->g, GUI_POINTER_PROGRESS);
+ } else {
+ gui_window_set_pointer(s->g, GUI_POINTER_DEFAULT);
+ }
+}
+
+
+/**
+ * add search string to recent searches list
+ * front is at liberty how to implement the bare notification
+ * should normally store a strdup() of the string;
+ * core gives no guarantee of the integrity of the const char *
+ * \param string search pattern
+ * \param p the pointer sent to search_verify_new() / search_create_context()
+ */
+void nsatari_search_add_recent(const char *string, void *p)
+{
+ LOG("%p add recent: %s\n", p, string);
+}
+
+
+/**
+ * activate search forwards button in gui
+ * \param active activate/inactivate
+ * \param p the pointer sent to search_verify_new() / search_create_context()
+ */
+void nsatari_search_set_forward_state(bool active, void *p)
+{
+ struct gui_window *gw;
+ OBJECT *toolbar;
+ GRECT area;
+ SEARCH_FORM_SESSION s = (SEARCH_FORM_SESSION)p;
+ /* deactivate back cb */
+ LOG("%p: set forward state: %d\n", p, active);
+
+ gw = s->g;
+
+ toolbar = toolbar_get_form(gw->root->toolbar);
+ if (active) {
+ toolbar[TOOLBAR_BT_SEARCH_FWD].ob_state &= ~OS_DISABLED;
+ } else {
+ toolbar[TOOLBAR_BT_SEARCH_FWD].ob_state |= OS_DISABLED;
+ }
+ window_get_grect(gw->root, BROWSER_AREA_SEARCH, &area);
+ window_schedule_redraw_grect(gw->root, &area);
+}
+
+
+/**
+ * activate search back button in gui
+ * \param active activate/inactivate
+ * \param p the pointer sent to search_verify_new() / search_create_context()
+ */
+void nsatari_search_set_back_state(bool active, void *p)
+{
+ struct gui_window *gw;
+ OBJECT *toolbar;
+ GRECT area;
+ SEARCH_FORM_SESSION s = (SEARCH_FORM_SESSION)p;
+ /* deactivate back cb */
+ LOG("%p: set back state: %d\n", p, active);
+
+ s->state.back_avail = active;
+ gw = s->g;
+
+ toolbar = toolbar_get_form(gw->root->toolbar);
+ if (active) {
+ toolbar[TOOLBAR_BT_SEARCH_BACK].ob_state &= ~OS_DISABLED;
+ } else {
+ toolbar[TOOLBAR_BT_SEARCH_BACK].ob_state |= OS_DISABLED;
+ }
+ window_get_grect(gw->root, BROWSER_AREA_SEARCH, &area);
+ window_schedule_redraw_grect(gw->root, &area);
+}
+
+
+static int apply_form(OBJECT *obj, struct s_search_form_state *s)
+{
+ char * cstr;
+
+ assert(s != NULL);
+
+ s->flags = 0;
+
+ if ((obj[TOOLBAR_CB_CASESENSE].ob_state & OS_SELECTED) != 0 ) {
+ s->flags |= SEARCH_FLAG_CASE_SENSITIVE;
+ }
+ if ((obj[TOOLBAR_CB_SHOWALL].ob_state & OS_SELECTED) != 0 ) {
+ s->flags |= SEARCH_FLAG_SHOWALL;
+ }
+
+ cstr = gemtk_obj_get_text(obj, TOOLBAR_TB_SRCH);
+ snprintf(s->text, 32, "%s", cstr);
+ return ( 0 );
+
+}
+
+static void set_text(OBJECT *obj, short idx, char * text, int len)
+{
+ char spare[255];
+
+ if (len > 254) {
+ len = 254;
+ }
+ if (text != NULL) {
+ strncpy(spare, text, 254);
+ } else {
+ strcpy(spare, "");
+ }
+
+ set_string(obj, idx, spare);
+}
+
+
+void nsatari_search_restore_form( struct s_search_form_session *s, OBJECT *obj)
+{
+ if ((s->state.flags & SEARCH_FLAG_SHOWALL) != 0) {
+ obj[TOOLBAR_CB_SHOWALL].ob_state |= OS_SELECTED;
+ } else {
+ obj[TOOLBAR_CB_SHOWALL].ob_state &= ~OS_SELECTED;
+ }
+
+ if ((s->state.flags & SEARCH_FLAG_CASE_SENSITIVE) != 0) {
+ obj[TOOLBAR_CB_CASESENSE].ob_state |= OS_SELECTED;
+ } else {
+ obj[TOOLBAR_CB_CASESENSE].ob_state &= ~OS_SELECTED;
+ }
+
+ if (s->state.back_avail == false) {
+ obj[TOOLBAR_BT_SEARCH_BACK].ob_state |= OS_DISABLED;
+ } else {
+ obj[TOOLBAR_BT_SEARCH_BACK].ob_state &= ~OS_DISABLED;
+ }
+
+ TEDINFO *t = ((TEDINFO *)get_obspec(obj, TOOLBAR_TB_SRCH));
+ set_text(obj, TOOLBAR_TB_SRCH, s->state.text, t->te_txtlen);
+}
+
+
+void nsatari_search_session_destroy(struct s_search_form_session *s)
+{
+ if (s != NULL) {
+ LOG("session %p", s);
+ browser_window_search_clear(s->g->browser->bw);
+ free(s);
+ }
+}
+
+
+/** checks for search parameters changes */
+static bool search_session_compare(struct s_search_form_session *s, OBJECT *obj)
+{
+ uint32_t flags_old;
+ uint32_t flags_mask = SEARCH_FLAG_SHOWALL | SEARCH_FLAG_CASE_SENSITIVE;
+ struct s_search_form_state cur;
+
+ assert(s != NULL && obj != NULL);
+
+ flags_old = s->state.flags;
+
+ apply_form(obj, &cur);
+ if ((cur.flags&flags_mask) != (flags_old&flags_mask)) {
+ return( true );
+ }
+
+ char * cstr;
+ cstr = gemtk_obj_get_text(obj, TOOLBAR_TB_SRCH);
+ if (cstr != NULL) {
+ if (strcmp(cstr, (char*)&s->state.text) != 0) {
+ return (true);
+ }
+ }
+
+ return( false );
+}
+
+
+void nsatari_search_perform(struct s_search_form_session *s, OBJECT *obj,
+ search_flags_t f)
+{
+ assert(s!=null);
+ assert(input_window->browser->bw == s->g->browser->bw);
+
+ if (search_session_compare(s, obj)) {
+ browser_window_search_clear(s->g->browser->bw);
+ apply_form(obj, &s->state);
+ }
+
+ /* get search direction manually: */
+ if ( (f&SEARCH_FLAG_FORWARDS) != 0 ) {
+ s->state.flags |= SEARCH_FLAG_FORWARDS;
+ } else {
+ s->state.flags &= (~SEARCH_FLAG_FORWARDS);
+ }
+
+ browser_window_search(s->g->browser->bw, s,
+ s->state.flags,
+ gemtk_obj_get_text(obj, TOOLBAR_TB_SRCH));
+}
+
+
+struct s_search_form_session * nsatari_search_session_create(OBJECT * obj,
+ struct gui_window *gw)
+{
+ struct s_search_form_session *sfs;
+
+ sfs = calloc(1, sizeof(struct s_search_form_session));
+
+ assert(obj);
+ assert(sfs);
+
+ sfs->g = gw;
+
+ apply_form(obj, &sfs->state);
+
+ browser_window_search_clear(gw->browser->bw);
+
+ return(sfs);
+}
diff --git a/frontends/atari/search.h b/frontends/atari/search.h
new file mode 100644
index 000000000..04dfed4eb
--- /dev/null
+++ b/frontends/atari/search.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2013 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_ATARI_SEARCH_H
+#define NS_ATARI_SEARCH_H
+
+#define SEARCH_MAX_SLEN 24
+
+struct gui_window;
+struct browser_window;
+
+#include "desktop/search.h"
+
+struct s_search_form_state {
+ char text[32];
+ uint32_t flags;
+ bool back_avail;
+};
+
+struct s_search_form_session {
+ struct gui_window *g;
+ struct s_search_form_state state;
+};
+
+typedef struct s_search_form_session * SEARCH_FORM_SESSION;
+
+struct s_search_form_session * nsatari_search_session_create(OBJECT * obj,
+ struct gui_window *gw);
+
+struct gui_search_table *atari_search_table;
+
+void nsatari_search_session_destroy(struct s_search_form_session *s);
+void nsatari_search_perform(struct s_search_form_session *s, OBJECT *obj,
+ search_flags_t f);
+void nsatari_search_restore_form( struct s_search_form_session *s, OBJECT *obj);
+
+#endif
diff --git a/frontends/atari/settings.c b/frontends/atari/settings.c
new file mode 100644
index 000000000..b6df12760
--- /dev/null
+++ b/frontends/atari/settings.c
@@ -0,0 +1,990 @@
+/*
+ * Copyright 2013 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <limits.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdbool.h>
+#include <cflib.h>
+#include <gem.h>
+
+#include "utils/dirent.h"
+#include "utils/nsoption.h"
+#include "utils/log.h"
+#include "desktop/plot_style.h"
+
+#include "atari/gui.h"
+#include "atari/res/netsurf.rsh"
+#include "atari/settings.h"
+#include "atari/deskmenu.h"
+#include "atari/misc.h"
+#include "atari/plot/plot.h"
+#include "atari/bitmap.h"
+#include "atari/findfile.h"
+#include "atari/gemtk/gemtk.h"
+
+extern char options[PATH_MAX];
+extern GRECT desk_area;
+
+static float tmp_option_minimum_gif_delay;
+static unsigned tmp_option_memory_cache_size;
+static unsigned int tmp_option_disc_cache_size;
+static unsigned int tmp_option_expire_url;
+static unsigned int tmp_option_disc_cache_age;
+static unsigned int tmp_option_font_min_size;
+static unsigned int tmp_option_font_size;
+static unsigned int tmp_option_min_reflow_period;
+static unsigned int tmp_option_max_fetchers;
+static unsigned int tmp_option_max_fetchers_per_host;
+static unsigned int tmp_option_max_cached_fetch_handles;
+
+static int num_locales = 0;
+static char **locales = NULL;
+static short h_aes_win = 0;
+static GUIWIN * settings_guiwin = NULL;
+static OBJECT * dlgtree;
+
+/* Available font engines for the font engine selection popup: */
+static const char *font_engines[] = {
+
+#ifdef WITH_FREETYPE_FONT_DRIVER
+ "freetype",
+#endif
+
+#ifdef WITH_INTERNAL_FONT_DRIVER
+ "internal",
+#endif
+
+#ifdef WITH_VDI_FONT_DRIVER
+ "vdi",
+#endif
+};
+
+/* Available GUI timeouts for the timeout selection popup: */
+static const char *gui_timeouts[] = {
+ "0", "5", "10"
+};
+
+#define OBJ_SELECTED(idx) ((bool)((dlgtree[idx].ob_state & OS_SELECTED)!=0))
+
+#define OBJ_CHECK(idx) (dlgtree[idx].ob_state |= (OS_SELECTED));
+
+#define OBJ_UNCHECK(idx) (dlgtree[idx].ob_state &= ~(OS_SELECTED));
+
+#define OBJ_REDRAW(idx) gemtk_wm_exec_redraw(settings_guiwin, \
+ gemtk_obj_screen_rect(dlgtree, idx));
+
+#define DISABLE_OBJ(idx) (dlgtree[idx].ob_state |= OS_DISABLED); \
+ gemtk_wm_exec_redraw(settings_guiwin, \
+ gemtk_obj_screen_rect(dlgtree, idx));
+
+#define ENABLE_OBJ(idx) (dlgtree[idx].ob_state &= ~(OS_DISABLED)); \
+ gemtk_wm_exec_redraw(settings_guiwin, \
+ gemtk_obj_screen_rect(dlgtree, idx));
+
+#define FORMEVENT(idx) form_event(idx, 0);
+
+#define INPUT_HOMEPAGE_URL_MAX_LEN 44
+#define INPUT_LOCALE_MAX_LEN 6
+#define INPUT_PROXY_HOST_MAX_LEN 31
+#define INPUT_PROXY_USERNAME_MAX_LEN 36
+#define INPUT_PROXY_PASSWORD_MAX_LEN 36
+#define INPUT_PROXY_PORT_MAX_LEN 5
+#define INPUT_MIN_REFLOW_PERIOD_MAX_LEN 4
+#define LABEL_FONT_RENDERER_MAX_LEN 8
+#define LABEL_PATH_MAX_LEN 40
+#define LABEL_ICONSET_MAX_LEN 8
+#define INPUT_TOOLBAR_COLOR_MAX_LEN 6
+
+
+static void display_settings(void);
+static void form_event(int index, int external);
+static void apply_settings(void);
+static void save_settings(void);
+
+
+static void set_text( short idx, char * text, int len )
+{
+ char spare[255];
+
+ if( len > 254 )
+ len = 254;
+ if( text != NULL ) {
+ strncpy(spare, text, 254);
+ } else {
+ strcpy(spare, "");
+ }
+
+ set_string( dlgtree, idx, spare);
+}
+
+
+
+
+/**
+ * Toogle all objects which are directly influenced by other GUI elements
+ * ( like checkbox )
+ */
+static void toggle_objects(void)
+{
+ /* enable / disable (refresh) objects depending on radio button values: */
+ /* Simulate GUI events which trigger refresh of bound elements: */
+ FORMEVENT(SETTINGS_CB_USE_PROXY);
+ FORMEVENT(SETTINGS_CB_PROXY_AUTH);
+ FORMEVENT(SETTINGS_BT_SEL_FONT_RENDERER);
+}
+
+static char **read_locales(void)
+{
+ char buf[PATH_MAX];
+ char tmp_locale[16];
+ char **locales = NULL;
+
+ FILE * fp_locales = NULL;
+
+ atari_find_resource(buf, "languages", "./res/languages");
+
+ fp_locales = fopen(buf, "r");
+
+ if (fp_locales == NULL) {
+ atari_warn_user("Failed to load locales: %s",buf);
+ return(NULL);
+ } else {
+ LOG("Reading locales from: %s...", buf);
+ }
+
+ /* Count items: */
+ num_locales = 0;
+ while (fgets(tmp_locale, 16, fp_locales) != NULL) {
+ num_locales++;
+ }
+
+ locales = malloc(sizeof(char*)*num_locales);
+
+ rewind(fp_locales);
+ int i = 0;
+ while (fgets(tmp_locale, 16, fp_locales) != NULL) {
+ int len = strlen(tmp_locale);
+ tmp_locale[len-1] = 0;
+ len--;
+ locales[i] = malloc(len+1);
+ // do not copy the last \n
+ snprintf(locales[i], 16, "%s", tmp_locale);
+ i++;
+ }
+
+ fclose(fp_locales);
+
+ return(locales);
+}
+
+
+static void save_settings(void)
+{
+ apply_settings();
+ // Save settings
+ nsoption_write( (const char*)&options, NULL, NULL);
+ nsoption_read( (const char*)&options , NULL);
+ close_settings();
+ form_alert(1, "[1][Some options require an netsurf restart!][OK]");
+ deskmenu_update();
+}
+
+/* this gets called each time the settings dialog is opened: */
+static void display_settings(void)
+{
+ char spare[255];
+ // read current settings and display them
+
+
+ /* "Browser" tab: */
+ set_text( SETTINGS_EDIT_HOMEPAGE, nsoption_charp(homepage_url),
+ INPUT_HOMEPAGE_URL_MAX_LEN );
+
+ if( nsoption_bool(block_advertisements) ) {
+ OBJ_CHECK( SETTINGS_CB_HIDE_ADVERTISEMENT );
+ } else {
+ OBJ_UNCHECK( SETTINGS_CB_HIDE_ADVERTISEMENT );
+ }
+ if( nsoption_bool(target_blank) ) {
+ OBJ_UNCHECK( SETTINGS_CB_DISABLE_POPUP_WINDOWS );
+ } else {
+ OBJ_CHECK( SETTINGS_CB_DISABLE_POPUP_WINDOWS );
+ }
+ if( nsoption_bool(send_referer) ) {
+ OBJ_CHECK( SETTINGS_CB_SEND_HTTP_REFERRER );
+ } else {
+ OBJ_UNCHECK( SETTINGS_CB_SEND_HTTP_REFERRER );
+ }
+ if( nsoption_bool(do_not_track) ) {
+ OBJ_CHECK( SETTINGS_CB_SEND_DO_NOT_TRACK );
+ } else {
+ OBJ_UNCHECK( SETTINGS_CB_SEND_DO_NOT_TRACK );
+ }
+
+ set_text( SETTINGS_BT_SEL_LOCALE,
+ nsoption_charp(accept_language) ? nsoption_charp(accept_language) : (char*)"en",
+ INPUT_LOCALE_MAX_LEN );
+
+ sprintf(spare, "%d", nsoption_int(atari_gui_poll_timeout));
+ set_text(SETTINGS_BT_GUI_TOUT, spare, 2);
+
+ tmp_option_expire_url = nsoption_int(expire_url);
+ snprintf( spare, 255, "%02d", nsoption_int(expire_url) );
+ set_text( SETTINGS_EDIT_HISTORY_AGE, spare, 3);
+
+ /* "Cache" tab: */
+ tmp_option_memory_cache_size = nsoption_int(memory_cache_size) / (1024*1024);
+ snprintf( spare, 255, "%d", tmp_option_memory_cache_size );
+ set_text( SETTINGS_STR_MAX_MEM_CACHE, spare, 4 );
+
+ tmp_option_disc_cache_size = nsoption_int(disc_cache_size) / (1024*1024);
+ snprintf( spare, 255, "%d", tmp_option_disc_cache_size );
+ set_text( SETTINGS_STR_MAX_DISC_CACHE, spare, 4 );
+
+ tmp_option_disc_cache_age = nsoption_int(disc_cache_age);
+ snprintf( spare, 255, "%02d", tmp_option_disc_cache_age );
+ set_text( SETTINGS_EDIT_CACHE_AGE, spare, 3 );
+
+ /* "Paths" tab: */
+ set_text( SETTINGS_EDIT_DOWNLOAD_PATH, nsoption_charp(downloads_path),
+ LABEL_PATH_MAX_LEN );
+ set_text( SETTINGS_EDIT_HOTLIST_FILE, nsoption_charp(hotlist_file),
+ LABEL_PATH_MAX_LEN );
+ set_text( SETTINGS_EDIT_CA_BUNDLE, nsoption_charp(ca_bundle),
+ LABEL_PATH_MAX_LEN );
+ set_text( SETTINGS_EDIT_CA_CERTS_PATH, nsoption_charp(ca_path),
+ LABEL_PATH_MAX_LEN );
+ set_text( SETTINGS_EDIT_EDITOR, nsoption_charp(atari_editor),
+ LABEL_PATH_MAX_LEN );
+
+ /* "Rendering" tab: */
+ set_text( SETTINGS_BT_SEL_FONT_RENDERER, nsoption_charp(atari_font_driver),
+ LABEL_FONT_RENDERER_MAX_LEN );
+ SET_BIT(dlgtree[SETTINGS_CB_TRANSPARENCY].ob_state,
+ OS_SELECTED, nsoption_int(atari_transparency) ? 1 : 0 );
+ SET_BIT(dlgtree[SETTINGS_CB_ENABLE_ANIMATION].ob_state,
+ OS_SELECTED, nsoption_bool(animate_images) ? 1 : 0 );
+ SET_BIT(dlgtree[SETTINGS_CB_FG_IMAGES].ob_state,
+ OS_SELECTED, nsoption_bool(foreground_images) ? 1 : 0 );
+ SET_BIT(dlgtree[SETTINGS_CB_BG_IMAGES].ob_state,
+ OS_SELECTED, nsoption_bool(background_images) ? 1 : 0 );
+
+
+ // TODO: enable this option?
+ /* SET_BIT(dlgtree[SETTINGS_CB_INCREMENTAL_REFLOW].ob_state,
+ OS_SELECTED, nsoption_bool(incremental_reflow) ? 1 : 0 );*/
+
+ SET_BIT(dlgtree[SETTINGS_CB_ANTI_ALIASING].ob_state,
+ OS_SELECTED, nsoption_int(atari_font_monochrom) ? 0 : 1 );
+
+
+ // TODO: activate this option?
+ tmp_option_min_reflow_period = nsoption_int(min_reflow_period);
+ snprintf( spare, 255, "%04d", tmp_option_min_reflow_period );
+ set_text( SETTINGS_EDIT_MIN_REFLOW_PERIOD, spare,
+ INPUT_MIN_REFLOW_PERIOD_MAX_LEN );
+
+
+ tmp_option_minimum_gif_delay = (float)nsoption_int(minimum_gif_delay) / (float)100;
+ snprintf( spare, 255, "%01.1f", tmp_option_minimum_gif_delay );
+ set_text( SETTINGS_EDIT_MIN_GIF_DELAY, spare, 3 );
+
+ /* "Network" tab: */
+ set_text( SETTINGS_EDIT_PROXY_HOST, nsoption_charp(http_proxy_host),
+ INPUT_PROXY_HOST_MAX_LEN );
+ snprintf( spare, 255, "%5d", nsoption_int(http_proxy_port) );
+ set_text( SETTINGS_EDIT_PROXY_PORT, spare,
+ INPUT_PROXY_PORT_MAX_LEN );
+
+ set_text( SETTINGS_EDIT_PROXY_USERNAME, nsoption_charp(http_proxy_auth_user),
+ INPUT_PROXY_USERNAME_MAX_LEN );
+ set_text( SETTINGS_EDIT_PROXY_PASSWORD, nsoption_charp(http_proxy_auth_pass),
+ INPUT_PROXY_PASSWORD_MAX_LEN );
+ SET_BIT(dlgtree[SETTINGS_CB_USE_PROXY].ob_state,
+ OS_SELECTED, nsoption_bool(http_proxy) ? 1 : 0 );
+ SET_BIT(dlgtree[SETTINGS_CB_PROXY_AUTH].ob_state,
+ OS_SELECTED, nsoption_int(http_proxy_auth) ? 1 : 0 );
+
+ tmp_option_max_cached_fetch_handles = nsoption_int(max_cached_fetch_handles);
+ snprintf( spare, 255, "%2d", nsoption_int(max_cached_fetch_handles) );
+ set_text( SETTINGS_EDIT_MAX_CACHED_CONNECTIONS, spare , 2 );
+
+ tmp_option_max_fetchers = nsoption_int(max_fetchers);
+ snprintf( spare, 255, "%2d", nsoption_int(max_fetchers) );
+ set_text( SETTINGS_EDIT_MAX_FETCHERS, spare , 2 );
+
+ tmp_option_max_fetchers_per_host = nsoption_int(max_fetchers_per_host);
+ snprintf( spare, 255, "%2d", nsoption_int(max_fetchers_per_host) );
+ set_text( SETTINGS_EDIT_MAX_FETCHERS_PER_HOST, spare , 2 );
+
+
+ /* "Style" tab: */
+ tmp_option_font_min_size = nsoption_int(font_min_size);
+ snprintf( spare, 255, "%3d", nsoption_int(font_min_size) );
+ set_text( SETTINGS_EDIT_MIN_FONT_SIZE, spare , 3 );
+
+ tmp_option_font_size = nsoption_int(font_size);
+ snprintf( spare, 255, "%3d", nsoption_int(font_size) );
+ set_text( SETTINGS_EDIT_DEF_FONT_SIZE, spare , 3 );
+
+ toggle_objects();
+}
+
+static void handle_filesystem_select_button(short rsc_bt)
+{
+ bool require_path = false;
+ bool is_folder = false;
+ short rsc_te = 0; // The textarea that is bound to the button
+ const char * title = "";
+ const char * path = NULL;
+
+ // TODO: localize String:
+ switch (rsc_bt) {
+
+
+ case SETTINGS_BT_SEL_DOWNLOAD_DIR:
+ title = "Select Download Directory:";
+ rsc_te = SETTINGS_EDIT_DOWNLOAD_PATH;
+ require_path = true;
+ break;
+
+ case SETTINGS_BT_SEL_HOTLIST:
+ title = "Select Hotlist File:";
+ rsc_te = SETTINGS_EDIT_HOTLIST_FILE;
+ break;
+
+ case SETTINGS_BT_SEL_CA_BUNDLE:
+ title = "Select CA Bundle File:";
+ rsc_te = SETTINGS_EDIT_CA_BUNDLE;
+ break;
+
+ case SETTINGS_BT_SEL_CA_CERTS:
+ title = "Select Certs Directory:";
+ rsc_te = SETTINGS_EDIT_CA_CERTS_PATH;
+ require_path = true;
+ break;
+
+ case SETTINGS_BT_SEL_EDITOR:
+ title = "Select Editor Application:";
+ rsc_te = SETTINGS_EDIT_EDITOR;
+ break;
+
+ default:
+ break;
+ };
+
+ assert(rsc_te != 0);
+
+ if (require_path == false) {
+ path = file_select(title, "");
+ if (path != NULL) {
+ gemtk_obj_set_str_safe(dlgtree, rsc_te, path);
+ }
+ }
+ else {
+ do {
+ /* display file selector: */
+ path = file_select(title, "");
+ if (path) {
+ is_folder = is_dir(path);
+ }
+ if ((is_folder == false) && (path != NULL)) {
+ gemtk_msg_box_show(GEMTK_MSG_BOX_ALERT, "Folder Required!");
+ }
+ } while ((is_folder == false) && (path != NULL));
+
+ if ((is_folder == true) && (path != NULL)) {
+ gemtk_obj_set_str_safe(dlgtree, rsc_te, path);
+ }
+ }
+
+ OBJ_UNCHECK(rsc_bt);
+ OBJ_REDRAW(rsc_bt);
+ OBJ_REDRAW(rsc_te);
+}
+
+static void form_event(int index, int external)
+{
+ char spare[255];
+ bool is_button = false;
+ bool checked = OBJ_SELECTED(index);
+ char * tmp;
+ MENU pop_menu, me_data;
+
+ short x, y;
+ int choice;
+
+ switch(index) {
+
+ case SETTINGS_CB_USE_PROXY:
+ if( checked ) {
+ ENABLE_OBJ(SETTINGS_EDIT_PROXY_HOST);
+ ENABLE_OBJ(SETTINGS_EDIT_PROXY_PORT);
+ ENABLE_OBJ(SETTINGS_CB_PROXY_AUTH);
+ } else {
+ DISABLE_OBJ(SETTINGS_EDIT_PROXY_HOST);
+ DISABLE_OBJ(SETTINGS_EDIT_PROXY_PORT);
+ DISABLE_OBJ(SETTINGS_CB_PROXY_AUTH);
+ }
+ FORMEVENT(SETTINGS_CB_PROXY_AUTH);
+ OBJ_REDRAW(SETTINGS_CB_USE_PROXY);
+ break;
+
+ case SETTINGS_CB_PROXY_AUTH:
+ if( checked && OBJ_SELECTED( SETTINGS_CB_USE_PROXY ) ) {
+ ENABLE_OBJ(SETTINGS_EDIT_PROXY_USERNAME);
+ ENABLE_OBJ(SETTINGS_EDIT_PROXY_PASSWORD);
+ } else {
+ DISABLE_OBJ(SETTINGS_EDIT_PROXY_USERNAME);
+ DISABLE_OBJ(SETTINGS_EDIT_PROXY_PASSWORD);
+ }
+ break;
+
+ case SETTINGS_CB_ENABLE_ANIMATION:
+ if( checked ) {
+ ENABLE_OBJ( SETTINGS_EDIT_MIN_GIF_DELAY );
+ } else {
+ DISABLE_OBJ( SETTINGS_EDIT_MIN_GIF_DELAY );
+ }
+ break;
+
+ case SETTINGS_BT_SEL_FONT_RENDERER:
+ if( external ) {
+ objc_offset(dlgtree, SETTINGS_BT_SEL_FONT_RENDERER, &x, &y);
+ tmp = gemtk_obj_get_text(dlgtree, SETTINGS_BT_SEL_FONT_RENDERER);
+ // point mn_tree tree to font renderer popup:
+ pop_menu.mn_tree = gemtk_obj_create_popup_tree(font_engines,
+ NOF_ELEMENTS(font_engines), tmp, false,
+ -1, -1);
+
+ assert(pop_menu.mn_tree != NULL);
+
+ pop_menu.mn_menu = 0;
+ pop_menu.mn_item = 1;
+ pop_menu.mn_scroll = SCROLL_NO;
+ pop_menu.mn_keystate = 0;
+
+ /* Show the popup: */
+ menu_popup(&pop_menu, x, y, &me_data);
+ choice = me_data.mn_item;
+
+ /* Process selection: */
+ if (choice > 0 && choice <= (short)NOF_ELEMENTS(font_engines)) {
+ get_string(pop_menu.mn_tree, choice, spare);
+ set_text(SETTINGS_BT_SEL_FONT_RENDERER,
+ (char*)&spare[2],
+ LABEL_FONT_RENDERER_MAX_LEN);
+ OBJ_REDRAW(SETTINGS_BT_SEL_FONT_RENDERER);
+ }
+
+ gemtk_obj_destroy_popup_tree(pop_menu.mn_tree);
+ }
+ tmp = gemtk_obj_get_text(dlgtree, SETTINGS_BT_SEL_FONT_RENDERER);
+ if (strcasecmp(tmp, "freetype") == 0) {
+ ENABLE_OBJ(SETTINGS_CB_ANTI_ALIASING);
+ } else {
+ DISABLE_OBJ(SETTINGS_CB_ANTI_ALIASING);
+ }
+ break;
+
+ case SETTINGS_BT_SEL_LOCALE:
+ objc_offset(dlgtree, SETTINGS_BT_SEL_LOCALE, &x, &y);
+
+ if(num_locales < 1 || locales == NULL){
+ locales = read_locales();
+ }
+
+ if (num_locales < 1 || locales == NULL) {
+ // point mn_tree tree to states popup:
+ num_locales = 15;
+ pop_menu.mn_tree = gemtk_obj_get_tree(POP_LANGUAGE);
+ pop_menu.mn_item = POP_LANGUAGE_CS;
+ } else {
+ // point mn_tree tree to dynamic list:
+ tmp = gemtk_obj_get_text(dlgtree, SETTINGS_BT_SEL_LOCALE);
+ pop_menu.mn_tree = gemtk_obj_create_popup_tree((const char**)locales,
+ num_locales, tmp, false,
+ -1, 100);
+
+ pop_menu.mn_item = 0;
+ }
+
+ pop_menu.mn_menu = 0;
+ pop_menu.mn_scroll = SCROLL_YES;
+ pop_menu.mn_keystate = 0;
+
+ /* display popup: */
+ menu_popup(&pop_menu, x, y, &me_data);
+
+ /* Process user selection: */
+ choice = me_data.mn_item;
+ if( choice > 0 && choice <= num_locales ) {
+ get_string(pop_menu.mn_tree, choice, spare);
+ set_text(SETTINGS_BT_SEL_LOCALE, (char*)&spare[2], 5);
+ }
+
+ gemtk_obj_destroy_popup_tree(pop_menu.mn_tree);
+
+ OBJ_REDRAW(SETTINGS_BT_SEL_LOCALE);
+ break;
+
+ case SETTINGS_BT_GUI_TOUT:
+ objc_offset(dlgtree, SETTINGS_BT_GUI_TOUT, &x, &y);
+ tmp = gemtk_obj_get_text(dlgtree, SETTINGS_BT_GUI_TOUT);
+ pop_menu.mn_tree = gemtk_obj_create_popup_tree(gui_timeouts,
+ NOF_ELEMENTS(gui_timeouts), tmp, false, -1,
+ 100);
+
+ pop_menu.mn_item = 0;
+ pop_menu.mn_menu = 0;
+ pop_menu.mn_scroll = SCROLL_NO;
+ pop_menu.mn_keystate = 0;
+
+ /* Display popup: */
+ menu_popup(&pop_menu, x, y, &me_data);
+
+ /* Process user selection: */
+ choice = me_data.mn_item;
+ if( choice > 0 && choice <= (int)NOF_ELEMENTS(gui_timeouts)) {
+ get_string(pop_menu.mn_tree, choice, spare);
+ set_text(SETTINGS_BT_GUI_TOUT, (char*)&spare[2], 5);
+ }
+
+ gemtk_obj_destroy_popup_tree(pop_menu.mn_tree);
+
+ OBJ_REDRAW(SETTINGS_BT_GUI_TOUT);
+ break;
+
+ case SETTINGS_BT_SEL_DOWNLOAD_DIR:
+ case SETTINGS_BT_SEL_HOTLIST:
+ case SETTINGS_BT_SEL_CA_BUNDLE:
+ case SETTINGS_BT_SEL_CA_CERTS:
+ case SETTINGS_BT_SEL_EDITOR:
+ handle_filesystem_select_button(index);
+ break;
+
+ case SETTINGS_INC_MEM_CACHE:
+ case SETTINGS_DEC_MEM_CACHE:
+ if( index == SETTINGS_DEC_MEM_CACHE )
+ tmp_option_memory_cache_size -= 1;
+ else
+ tmp_option_memory_cache_size += 1;
+
+ if( tmp_option_memory_cache_size < 0 )
+ tmp_option_memory_cache_size = 1;
+ if( tmp_option_memory_cache_size > 999 )
+ tmp_option_memory_cache_size = 999;
+ snprintf( spare, 255, "%02d", tmp_option_memory_cache_size );
+ set_text( SETTINGS_STR_MAX_MEM_CACHE, spare, 5 );
+ is_button = true;
+ OBJ_REDRAW(SETTINGS_STR_MAX_MEM_CACHE);
+ break;
+
+ case SETTINGS_INC_DISC_CACHE:
+ case SETTINGS_DEC_DISC_CACHE:
+ if( index == SETTINGS_DEC_DISC_CACHE )
+ tmp_option_disc_cache_size -= 1;
+ else
+ tmp_option_disc_cache_size += 1;
+
+ if( tmp_option_disc_cache_size < 0 )
+ tmp_option_disc_cache_size = 1;
+ if( tmp_option_disc_cache_size > 9999 )
+ tmp_option_disc_cache_size = 9999;
+ snprintf( spare, 255, "%02d", tmp_option_disc_cache_size );
+ set_text( SETTINGS_STR_MAX_DISC_CACHE, spare, 5 );
+ is_button = true;
+ OBJ_REDRAW(SETTINGS_STR_MAX_DISC_CACHE);
+ break;
+
+ case SETTINGS_INC_CACHE_AGE:
+ case SETTINGS_DEC_CACHE_AGE:
+ if( index == SETTINGS_INC_CACHE_AGE )
+ tmp_option_disc_cache_age += 1;
+ else
+ tmp_option_disc_cache_age -= 1;
+
+ if( tmp_option_disc_cache_age > 99 )
+ tmp_option_disc_cache_age = 0;
+
+ snprintf( spare, 255, "%02d", tmp_option_disc_cache_age );
+ set_text( SETTINGS_EDIT_CACHE_AGE, spare, 2 );
+ is_button = true;
+ OBJ_REDRAW(SETTINGS_EDIT_CACHE_AGE);
+ break;
+
+ case SETTINGS_INC_CACHED_CONNECTIONS:
+ case SETTINGS_DEC_CACHED_CONNECTIONS:
+ if( index == SETTINGS_INC_CACHED_CONNECTIONS )
+ tmp_option_max_cached_fetch_handles += 1;
+ else
+ tmp_option_max_cached_fetch_handles -= 1;
+ if( tmp_option_max_cached_fetch_handles > 31 )
+ tmp_option_max_cached_fetch_handles = 31;
+
+ snprintf( spare, 255, "%02d", tmp_option_max_cached_fetch_handles );
+ set_text( SETTINGS_EDIT_MAX_CACHED_CONNECTIONS, spare, 2 );
+ is_button = true;
+ OBJ_REDRAW(SETTINGS_EDIT_MAX_CACHED_CONNECTIONS);
+ break;
+
+ case SETTINGS_INC_MAX_FETCHERS:
+ case SETTINGS_DEC_MAX_FETCHERS:
+ if( index == SETTINGS_INC_MAX_FETCHERS )
+ tmp_option_max_fetchers += 1;
+ else
+ tmp_option_max_fetchers -= 1;
+ if( tmp_option_max_fetchers > 31 )
+ tmp_option_max_fetchers = 31;
+
+ snprintf( spare, 255, "%02d", tmp_option_max_fetchers );
+ set_text( SETTINGS_EDIT_MAX_FETCHERS, spare, 2 );
+ is_button = true;
+ OBJ_REDRAW(SETTINGS_EDIT_MAX_FETCHERS);
+ break;
+
+ case SETTINGS_INC_MAX_FETCHERS_PER_HOST:
+ case SETTINGS_DEC_MAX_FETCHERS_PER_HOST:
+ if( index == SETTINGS_INC_MAX_FETCHERS_PER_HOST )
+ tmp_option_max_fetchers_per_host += 1;
+ else
+ tmp_option_max_fetchers_per_host -= 1;
+ if( tmp_option_max_fetchers_per_host > 31 )
+ tmp_option_max_fetchers_per_host = 31;
+
+ snprintf( spare, 255, "%02d", tmp_option_max_fetchers_per_host );
+ set_text( SETTINGS_EDIT_MAX_FETCHERS_PER_HOST, spare, 2 );
+ is_button = true;
+ OBJ_REDRAW(SETTINGS_EDIT_MAX_FETCHERS_PER_HOST);
+ break;
+
+ case SETTINGS_INC_HISTORY_AGE:
+ case SETTINGS_DEC_HISTORY_AGE:
+ if( index == SETTINGS_INC_HISTORY_AGE )
+ tmp_option_expire_url += 1;
+ else
+ tmp_option_expire_url -= 1;
+
+ if( tmp_option_expire_url > 99 )
+ tmp_option_expire_url = 0;
+
+ snprintf( spare, 255, "%02d", tmp_option_expire_url );
+ set_text( SETTINGS_EDIT_HISTORY_AGE, spare, 2 );
+ is_button = true;
+ OBJ_REDRAW(SETTINGS_EDIT_HISTORY_AGE);
+ break;
+
+ case SETTINGS_INC_GIF_DELAY:
+ case SETTINGS_DEC_GIF_DELAY:
+ if( index == SETTINGS_INC_GIF_DELAY )
+ tmp_option_minimum_gif_delay += 0.1;
+ else
+ tmp_option_minimum_gif_delay -= 0.1;
+
+ if( tmp_option_minimum_gif_delay < 0.1 )
+ tmp_option_minimum_gif_delay = 0.1;
+ if( tmp_option_minimum_gif_delay > 9.0 )
+ tmp_option_minimum_gif_delay = 9.0;
+ snprintf( spare, 255, "%01.1f", tmp_option_minimum_gif_delay );
+ set_text( SETTINGS_EDIT_MIN_GIF_DELAY, spare, 3 );
+ is_button = true;
+ OBJ_REDRAW(SETTINGS_EDIT_MIN_GIF_DELAY);
+ break;
+
+ case SETTINGS_INC_MIN_FONT_SIZE:
+ case SETTINGS_DEC_MIN_FONT_SIZE:
+ if( index == SETTINGS_INC_MIN_FONT_SIZE )
+ tmp_option_font_min_size += 1;
+ else
+ tmp_option_font_min_size -= 1;
+
+ if( tmp_option_font_min_size > 500 )
+ tmp_option_font_min_size = 500;
+ if( tmp_option_font_min_size < 10 )
+ tmp_option_font_min_size = 10;
+
+ snprintf( spare, 255, "%03d", tmp_option_font_min_size );
+ set_text( SETTINGS_EDIT_MIN_FONT_SIZE, spare, 3 );
+ is_button = true;
+ OBJ_REDRAW(SETTINGS_EDIT_MIN_FONT_SIZE);
+ break;
+
+ case SETTINGS_INC_DEF_FONT_SIZE:
+ case SETTINGS_DEC_DEF_FONT_SIZE:
+ if( index == SETTINGS_INC_DEF_FONT_SIZE )
+ tmp_option_font_size += 1;
+ else
+ tmp_option_font_size -= 1;
+
+ if( tmp_option_font_size > 999 )
+ tmp_option_font_size = 999;
+ if( tmp_option_font_size < 50 )
+ tmp_option_font_size = 50;
+
+ snprintf( spare, 255, "%03d", tmp_option_font_size );
+ set_text( SETTINGS_EDIT_DEF_FONT_SIZE, spare, 3 );
+ is_button = true;
+ OBJ_REDRAW(SETTINGS_EDIT_DEF_FONT_SIZE);
+ break;
+
+ case SETTINGS_INC_INCREMENTAL_REFLOW:
+ case SETTINGS_DEC_INCREMENTAL_REFLOW:
+ if( index == SETTINGS_INC_INCREMENTAL_REFLOW )
+ tmp_option_min_reflow_period += 1;
+ else
+ tmp_option_min_reflow_period -= 1;
+
+ if( tmp_option_min_reflow_period > 9999 )
+ tmp_option_min_reflow_period = 10;
+
+ snprintf( spare, 255, "%04d", tmp_option_min_reflow_period );
+ set_text( SETTINGS_EDIT_MIN_REFLOW_PERIOD, spare, 4 );
+ is_button = true;
+ OBJ_REDRAW(SETTINGS_EDIT_MIN_REFLOW_PERIOD);
+ break;
+
+ default:
+ break;
+ }
+
+ if( is_button ) {
+ // remove selection indicator from button element:
+ OBJ_UNCHECK(index);
+ OBJ_REDRAW(index);
+ }
+}
+
+
+static void apply_settings(void)
+{
+ /* "Network" tab: */
+ nsoption_set_bool(http_proxy, OBJ_SELECTED(SETTINGS_CB_USE_PROXY));
+ if ( OBJ_SELECTED(SETTINGS_CB_PROXY_AUTH) ) {
+ nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_BASIC);
+ } else {
+ nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NONE);
+ }
+ nsoption_set_charp(http_proxy_auth_pass,
+ gemtk_obj_get_text(dlgtree, SETTINGS_EDIT_PROXY_PASSWORD));
+ nsoption_set_charp(http_proxy_auth_user,
+ gemtk_obj_get_text(dlgtree, SETTINGS_EDIT_PROXY_USERNAME));
+ nsoption_set_charp(http_proxy_host,
+ gemtk_obj_get_text(dlgtree, SETTINGS_EDIT_PROXY_HOST));
+ nsoption_set_int(http_proxy_port,
+ atoi( gemtk_obj_get_text(dlgtree, SETTINGS_EDIT_PROXY_PORT) ));
+ nsoption_set_int(max_fetchers_per_host,
+ atoi( gemtk_obj_get_text(dlgtree, SETTINGS_EDIT_MAX_FETCHERS_PER_HOST)));
+ nsoption_set_int(max_cached_fetch_handles,
+ atoi( gemtk_obj_get_text(dlgtree, SETTINGS_EDIT_MAX_CACHED_CONNECTIONS)));
+ nsoption_set_int(max_fetchers,
+ atoi( gemtk_obj_get_text(dlgtree, SETTINGS_EDIT_MAX_FETCHERS) ));
+ nsoption_set_bool(foreground_images,
+ OBJ_SELECTED( SETTINGS_CB_FG_IMAGES ));
+ nsoption_set_bool(background_images,
+ OBJ_SELECTED( SETTINGS_CB_BG_IMAGES ));
+
+ /* "Style" tab: */
+ nsoption_set_int(font_min_size, tmp_option_font_min_size);
+ nsoption_set_int(font_size, tmp_option_font_size);
+
+ /* "Rendering" tab: */
+ nsoption_set_charp(atari_font_driver,
+ gemtk_obj_get_text(dlgtree, SETTINGS_BT_SEL_FONT_RENDERER));
+ nsoption_set_int(atari_transparency,
+ OBJ_SELECTED(SETTINGS_CB_TRANSPARENCY));
+ nsoption_set_bool(animate_images,
+ OBJ_SELECTED(SETTINGS_CB_ENABLE_ANIMATION));
+ nsoption_set_int(minimum_gif_delay,
+ (int)(tmp_option_minimum_gif_delay*100+0.5));
+ /* nsoption_set_bool(incremental_reflow,
+ OBJ_SELECTED(SETTINGS_CB_INCREMENTAL_REFLOW));*/
+ nsoption_set_int(min_reflow_period, tmp_option_min_reflow_period);
+ nsoption_set_int(atari_font_monochrom,
+ !OBJ_SELECTED( SETTINGS_CB_ANTI_ALIASING ));
+
+ /* "Paths" tabs: */
+ nsoption_set_charp(ca_bundle,
+ gemtk_obj_get_text(dlgtree, SETTINGS_EDIT_CA_BUNDLE));
+ nsoption_set_charp(ca_path,
+ gemtk_obj_get_text(dlgtree, SETTINGS_EDIT_CA_CERTS_PATH));
+ nsoption_set_charp(homepage_url,
+ gemtk_obj_get_text(dlgtree, SETTINGS_EDIT_CA_CERTS_PATH));
+ nsoption_set_charp(hotlist_file,
+ gemtk_obj_get_text(dlgtree, SETTINGS_EDIT_HOTLIST_FILE));
+ nsoption_set_charp(atari_editor,
+ gemtk_obj_get_text(dlgtree, SETTINGS_EDIT_EDITOR));
+ nsoption_set_charp(downloads_path,
+ gemtk_obj_get_text(dlgtree, SETTINGS_EDIT_DOWNLOAD_PATH));
+
+ /* "Cache" tab: */
+ tmp_option_memory_cache_size = atoi(gemtk_obj_get_text(dlgtree,
+ SETTINGS_STR_MAX_MEM_CACHE));
+ tmp_option_expire_url = atoi(gemtk_obj_get_text(dlgtree,
+ SETTINGS_EDIT_HISTORY_AGE));
+ tmp_option_disc_cache_size = atoi(gemtk_obj_get_text(dlgtree,
+ SETTINGS_STR_MAX_DISC_CACHE));
+ tmp_option_disc_cache_age = atoi(gemtk_obj_get_text(dlgtree,
+ SETTINGS_EDIT_CACHE_AGE));
+ nsoption_set_int(memory_cache_size,
+ tmp_option_memory_cache_size * (1024*1024));
+
+ nsoption_set_int(expire_url, tmp_option_expire_url);
+
+ nsoption_set_int(disc_cache_size, tmp_option_disc_cache_size * (1024*1024));
+
+ nsoption_set_int(disc_cache_age, tmp_option_disc_cache_age);
+
+
+ /* "Browser" tab: */
+ nsoption_set_bool(target_blank,
+ !OBJ_SELECTED(SETTINGS_CB_DISABLE_POPUP_WINDOWS));
+ nsoption_set_bool(block_advertisements,
+ OBJ_SELECTED(SETTINGS_CB_HIDE_ADVERTISEMENT));
+ nsoption_set_charp(accept_language,
+ gemtk_obj_get_text(dlgtree, SETTINGS_BT_SEL_LOCALE));
+ nsoption_set_int(atari_gui_poll_timeout,
+ atoi(gemtk_obj_get_text(dlgtree, SETTINGS_BT_GUI_TOUT)));
+ nsoption_set_bool(send_referer,
+ OBJ_SELECTED(SETTINGS_CB_SEND_HTTP_REFERRER));
+ nsoption_set_bool(do_not_track,
+ OBJ_SELECTED(SETTINGS_CB_SEND_DO_NOT_TRACK));
+ nsoption_set_charp(homepage_url,
+ gemtk_obj_get_text(dlgtree, SETTINGS_EDIT_HOMEPAGE));
+}
+
+static short on_aes_event(GUIWIN *win, EVMULT_OUT *ev_out, short msg[8])
+{
+ short retval = 0;
+
+ if ((ev_out->emo_events & MU_MESAG) != 0) {
+ // handle message
+ // printf("settings win msg: %d\n", msg[0]);
+ switch (msg[0]) {
+
+
+ case WM_CLOSED:
+ // TODO: this needs to iterate through all gui windows and
+ // check if the rootwin is this window...
+ close_settings();
+ break;
+
+ case WM_SIZED:
+ gemtk_wm_update_slider(win, GEMTK_WM_VH_SLIDER);
+ break;
+
+ case WM_MOVED:
+ break;
+
+ case WM_TOOLBAR:
+ switch(msg[4]) {
+ case TOOLBAR_SETTINGS_SAVE:
+ save_settings();
+ break;
+
+ case TOOLBAR_SETTINGS_ABORT:
+ close_settings();
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case GEMTK_WM_WM_FORM_CLICK:
+ form_event(msg[4], 1);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if ((ev_out->emo_events & MU_KEYBD) != 0) {
+ }
+
+ if ((ev_out->emo_events & MU_BUTTON) != 0) {
+ }
+
+ return(retval);
+}
+
+void open_settings(void)
+{
+ if (h_aes_win == 0) {
+
+ GRECT curr, area;
+ OBJECT * toolbartree;
+ struct gemtk_wm_scroll_info_s *slid;
+ uint32_t kind = CLOSER | NAME | MOVER | VSLIDE | HSLIDE | UPARROW
+ | DNARROW | LFARROW | RTARROW | SIZER;
+
+ dlgtree = gemtk_obj_get_tree(SETTINGS);
+ toolbartree = gemtk_obj_get_tree(TOOLBAR_SETTINGS);
+ area.g_x = area.g_y = 0;
+ area.g_w = MIN(dlgtree->ob_width, desk_area.g_w);
+ area.g_h = MIN(dlgtree->ob_height, desk_area.g_h);
+ wind_calc_grect(WC_BORDER, kind, &area, &area);
+ h_aes_win = wind_create_grect(kind, &area);
+ wind_set_str(h_aes_win, WF_NAME, "Settings");
+ settings_guiwin = gemtk_wm_add(h_aes_win, GEMTK_WM_FLAG_DEFAULTS,
+ on_aes_event);
+ curr.g_w = MIN(dlgtree->ob_width, desk_area.g_w);
+ curr.g_h = MIN(dlgtree->ob_height, desk_area.g_h-64);
+ curr.g_x = 1;
+ curr.g_y = (desk_area.g_h / 2) - (curr.g_h / 2);
+
+ wind_calc_grect(WC_BORDER, kind, &curr, &curr);
+
+ dlgtree->ob_x = curr.g_x;
+ dlgtree->ob_y = curr.g_y;
+
+ /* set current config values: */
+ display_settings();
+
+ wind_open_grect(h_aes_win, &curr);
+
+ gemtk_wm_set_toolbar(settings_guiwin, toolbartree, 0, 0);
+ gemtk_wm_set_form(settings_guiwin, dlgtree, 0);
+ gemtk_wm_set_scroll_grid(settings_guiwin, 32, 32);
+ gemtk_wm_get_grect(settings_guiwin, GEMTK_WM_AREA_CONTENT, &area);
+
+ slid = gemtk_wm_get_scroll_info(settings_guiwin);
+ gemtk_wm_set_content_units(settings_guiwin,
+ (dlgtree->ob_width/slid->x_unit_px),
+ (dlgtree->ob_height/slid->y_unit_px));
+ gemtk_wm_update_slider(settings_guiwin, GEMTK_WM_VH_SLIDER);
+ }
+}
+
+void close_settings(void)
+{
+ LOG("closing");
+ gemtk_wm_remove(settings_guiwin);
+ settings_guiwin = NULL;
+ wind_close(h_aes_win);
+ wind_delete(h_aes_win);
+ h_aes_win = 0;
+ LOG("Done");
+}
+
diff --git a/frontends/atari/settings.h b/frontends/atari/settings.h
new file mode 100644
index 000000000..ac7bd8aa4
--- /dev/null
+++ b/frontends/atari/settings.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2013 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_ATARI_SETTINGS
+#define NS_ATARI_SETTINGS
+
+void open_settings(void);
+void close_settings(void);
+
+#endif
diff --git a/frontends/atari/statusbar.c b/frontends/atari/statusbar.c
new file mode 100644
index 000000000..c9d0f78aa
--- /dev/null
+++ b/frontends/atari/statusbar.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <math.h>
+
+#include "utils/log.h"
+#include "desktop/mouse.h"
+#include "desktop/plotters.h"
+
+#include "atari/gui.h"
+#include "atari/statusbar.h"
+#include "atari/rootwin.h"
+#include "atari/misc.h"
+#include "atari/res/netsurf.rsh"
+#include "atari/plot/plot.h"
+#include "atari/osspec.h"
+
+#ifdef WITH_CUSTOM_STATUSBAR
+extern int atari_plot_vdi_handle;
+
+static
+void __CDECL evnt_sb_redraw( COMPONENT *c, long buff[8] )
+{
+ struct gui_window * gw = (struct gui_window *)mt_CompDataSearch(&app, c, CDT_OWNER);
+ assert(gw != NULL);
+ CMP_STATUSBAR sb = gw->root->statusbar;
+ assert( sb != NULL );
+ if( sb == NULL )
+ return;
+
+ if( sb->attached == false )
+ return;
+
+ LGRECT work, lclip;
+ short pxy[8], d, pxyclip[4];
+
+ mt_CompGetLGrect(&app, sb->comp, WF_WORKXYWH, &work);
+ lclip = work;
+ if ( !rc_lintersect( (LGRECT*)&buff[4], &lclip ) ) {
+ return;
+ }
+ vsf_interior(atari_plot_vdi_handle, FIS_SOLID );
+ vsl_color(atari_plot_vdi_handle, BLACK );
+ vsl_type(atari_plot_vdi_handle, 1);
+ vsl_width(atari_plot_vdi_handle, 1 );
+ vst_color(atari_plot_vdi_handle, BLACK);
+
+ vst_height(atari_plot_vdi_handle, atari_sysinfo.medium_sfont_pxh, &pxy[0], &pxy[1], &pxy[2], &pxy[3] );
+ vst_alignment(atari_plot_vdi_handle, 0, 5, &d, &d );
+ vst_effects(atari_plot_vdi_handle, 0 );
+ pxyclip[0] = lclip.g_x;
+ pxyclip[1] = lclip.g_y;
+ pxyclip[2] = lclip.g_x + lclip.g_w-1;
+ pxyclip[3] = lclip.g_y + lclip.g_h-1;
+
+ vs_clip(atari_plot_vdi_handle, 1, (short*)&pxyclip );
+ vswr_mode(atari_plot_vdi_handle, MD_REPLACE );
+
+ if( lclip.g_y <= work.g_y ) {
+ pxy[0] = work.g_x;
+ pxy[1] = work.g_y;
+ pxy[2] = MIN( work.g_x + work.g_w, lclip.g_x + lclip.g_w );
+ pxy[3] = work.g_y;
+ v_pline(atari_plot_vdi_handle, 2, (short*)&pxy );
+ }
+
+ if(app.nplanes > 2) {
+ vsf_color(atari_plot_vdi_handle, LWHITE);
+ } else {
+ vsf_color(atari_plot_vdi_handle, WHITE );
+ }
+
+ pxy[0] = work.g_x;
+ pxy[1] = work.g_y+1;
+ pxy[2] = work.g_x + work.g_w-1;
+ pxy[3] = work.g_y + work.g_h-1;
+ v_bar(atari_plot_vdi_handle, pxy );
+
+
+ if( sb->textlen > 0 ) {
+ size_t i;
+ short curx;
+ short vqw[4];
+ char t[2];
+ short cw = 8;
+ t[1]=0;
+ if( atari_sysinfo.sfont_monospaced ) {
+ t[0]='A';
+ int r = vqt_width(atari_plot_vdi_handle, t[0], &vqw[0], &vqw[1], &vqw[2] );
+ cw = vqw[0];
+ }
+ vswr_mode(atari_plot_vdi_handle, MD_TRANS );
+ for( curx = work.g_x + 2, i=0 ; (curx+cw < work.g_x+work.g_w ) && i < sb->textlen; i++ ){
+ t[0] = sb->text[i];
+ if( !atari_sysinfo.sfont_monospaced ) {
+ vqt_width(atari_plot_vdi_handle, t[0], &vqw[0], &vqw[1], &vqw[2] );
+ cw = vqw[0];
+ }
+ if( curx >= lclip.g_x - cw ) {
+ v_gtext(atari_plot_vdi_handle, curx, work.g_y + 5, (char*)&t );
+ }
+ curx += cw;
+ if( curx >= lclip.g_x + lclip.g_w )
+ break;
+ }
+ }
+ vswr_mode(atari_plot_vdi_handle, MD_REPLACE );
+ pxy[0] = work.g_x + work.g_w;
+ pxy[1] = work.g_y + work.g_h;
+ pxy[2] = work.g_x + work.g_w;
+ pxy[3] = work.g_y + work.g_h-work.g_h;
+ v_pline(atari_plot_vdi_handle, 2, (short*)&pxy );
+
+ vs_clip(atari_plot_vdi_handle, 0, (short*)&pxyclip );
+ return;
+}
+
+static void __CDECL evnt_sb_click( COMPONENT *c, long buff[8] )
+{
+ static bool prevstate;
+ LGRECT work;
+ mt_CompGetLGrect(&app, c, WF_WORKXYWH, &work);
+ if( evnt.mx >= work.g_x + (work.g_w) && evnt.mx <= work.g_x + work.g_w &&
+ evnt.my >= work.g_y + (work.g_h) && evnt.my <= work.g_y + work.g_h ) {
+ // click within sb button
+ }
+}
+
+CMP_STATUSBAR sb_create( struct gui_window * gw )
+{
+ CMP_STATUSBAR s = malloc( sizeof(struct s_statusbar) );
+ s->attached = false;
+ s->comp = (COMPONENT*)mt_CompCreate(&app, CLT_HORIZONTAL, STATUSBAR_HEIGHT, 0);
+ s->comp->rect.g_h = STATUSBAR_HEIGHT;
+ s->comp->bounds.max_height = STATUSBAR_HEIGHT;
+ mt_CompDataAttach( &app, s->comp, CDT_OWNER, gw );
+ mt_CompEvntAttach( &app, s->comp, WM_REDRAW, evnt_sb_redraw );
+ mt_CompEvntAttach( &app, s->comp, WM_XBUTTON, evnt_sb_click );
+ sb_set_text( s, (char*)"" );
+ return( s );
+}
+
+void sb_destroy( CMP_STATUSBAR s )
+{
+ LOG("%s\n", __FUNCTION__);
+ if( s ) {
+ if( s->comp ){
+ mt_CompDelete( &app, s->comp );
+ }
+ free( s );
+ }
+}
+
+void sb_set_text(CMP_STATUSBAR sb , const char * text)
+{
+
+ LGRECT work;
+ assert( sb != NULL );
+ assert( sb->comp != NULL );
+ strncpy( (char*)&sb->text, text, STATUSBAR_MAX_SLEN );
+ sb->text[STATUSBAR_MAX_SLEN]=0;
+ sb->textlen = strlen( (char*)&sb->text );
+ if( sb->attached ){
+ struct gui_window * gw = (struct gui_window *)mt_CompDataSearch(&app, sb->comp, CDT_OWNER);
+ if( gw != NULL ){
+ mt_CompGetLGrect(&app, sb->comp, WF_WORKXYWH, &work);
+ ApplWrite( _AESapid, WM_REDRAW, gw->root->handle->handle,
+ work.g_x, work.g_y, work.g_w, work.g_h );
+ }
+ }
+}
+
+#else
+
+CMP_STATUSBAR sb_create( struct gui_window * gw )
+{
+ CMP_STATUSBAR s = malloc( sizeof(struct s_statusbar) );
+ s->attached = false;
+ sb_set_text( s, (char*)"" );
+ return( s );
+}
+
+void sb_destroy( CMP_STATUSBAR s )
+{
+ LOG("%s\n", __FUNCTION__);
+ if( s ) {
+ free( s );
+ }
+}
+
+void sb_attach(CMP_STATUSBAR sb, struct gui_window * gw)
+{
+ sb->aes_win = gemtk_wm_get_handle(gw->root->win);
+ sb->attached = true;
+}
+
+void sb_set_text(CMP_STATUSBAR sb, const char * text )
+{
+ assert( sb != NULL );
+ strncpy(sb->text, text, STATUSBAR_MAX_SLEN);
+ sb->text[STATUSBAR_MAX_SLEN]=0;
+ sb->textlen = strlen(sb->text);
+ if(sb->attached){
+ wind_set_str(sb->aes_win, WF_INFO, sb->text);
+ }
+}
+
+#endif
diff --git a/frontends/atari/statusbar.h b/frontends/atari/statusbar.h
new file mode 100644
index 000000000..8ac090497
--- /dev/null
+++ b/frontends/atari/statusbar.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_ATARI_STATUSBAR
+#define NS_ATARI_STATUSBAR
+
+#define STATUSBAR_HEIGHT 16
+#define STATUSBAR_MAX_SLEN 255
+
+struct s_statusbar {
+#ifdef WITH_COMPONENT_STATUSBAR
+ COMPONENT * comp;
+#endif
+ char text[STATUSBAR_MAX_SLEN+1];
+ size_t textlen;
+ bool attached;
+ short aes_win;
+};
+
+CMP_STATUSBAR sb_create( struct gui_window * gw );
+void sb_destroy( CMP_STATUSBAR s );
+void sb_set_text( CMP_STATUSBAR sb , const char * text );
+void sb_attach(CMP_STATUSBAR sb, struct gui_window * gw);
+
+#endif
diff --git a/frontends/atari/toolbar.c b/frontends/atari/toolbar.c
new file mode 100644
index 000000000..f630332da
--- /dev/null
+++ b/frontends/atari/toolbar.c
@@ -0,0 +1,1041 @@
+/*
+ * Copyright 2012 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <math.h>
+
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "utils/nsurl.h"
+#include "utils/utf8.h"
+#include "desktop/browser_history.h"
+#include "desktop/browser.h"
+#include "desktop/mouse.h"
+#include "desktop/plot_style.h"
+#include "desktop/plotters.h"
+#include "desktop/tree.h"
+#include "desktop/hotlist.h"
+#include "desktop/textarea.h"
+#include "desktop/textinput.h"
+#include "content/hlcache.h"
+
+#include "atari/clipboard.h"
+#include "atari/gui.h"
+#include "atari/search.h"
+#include "atari/toolbar.h"
+#include "atari/rootwin.h"
+#include "atari/clipboard.h"
+#include "atari/misc.h"
+#include "atari/plot/plot.h"
+#include "cflib.h"
+#include "atari/res/netsurf.rsh"
+#include "atari/encoding.h"
+
+
+#define TB_BUTTON_WIDTH 32
+#define THROBBER_WIDTH 32
+#define THROBBER_MIN_INDEX 1
+#define THROBBER_MAX_INDEX 12
+#define THROBBER_INACTIVE_INDEX 13
+
+enum e_toolbar_button_states {
+ button_on = 0,
+ button_off = 1
+};
+#define TOOLBAR_BUTTON_NUM_STATES 2
+
+struct s_toolbar;
+
+struct s_tb_button {
+ short rsc_id;
+ void (*cb_click)(struct s_toolbar *tb);
+ hlcache_handle *icon[TOOLBAR_BUTTON_NUM_STATES];
+ struct s_toolbar *owner;
+ enum e_toolbar_button_states state;
+ short index;
+ GRECT area;
+};
+
+
+extern char * option_homepage_url;
+extern void * h_gem_rsrc;
+extern struct gui_window * input_window;
+extern long atari_plot_flags;
+extern int atari_plot_vdi_handle;
+extern EVMULT_OUT aes_event_out;
+
+static OBJECT * aes_toolbar = NULL;
+static OBJECT * throbber_form = NULL;
+static bool init = false;
+static int area_navigation_height = 0;
+static int area_search_height = 0;
+static int area_full_height = 0;
+static float toolbar_url_scale = 1.0;
+
+static plot_font_style_t font_style_url = {
+ .family = PLOT_FONT_FAMILY_SANS_SERIF,
+ .size = 14*FONT_SIZE_SCALE,
+ .weight = 400,
+ .flags = FONTF_NONE,
+ .background = 0xffffff,
+ .foreground = 0x0
+};
+
+
+/* prototypes & order for button widgets: */
+
+static struct s_tb_button tb_buttons[] = {
+ {
+ TOOLBAR_BT_BACK,
+ toolbar_back_click,
+ {0,0},
+ 0, 0, 0, {0,0,0,0}
+ },
+ {
+ TOOLBAR_BT_HOME,
+ toolbar_home_click,
+ {0,0},
+ 0, 0, 0, {0,0,0,0}
+ },
+ {
+ TOOLBAR_BT_FORWARD,
+ toolbar_forward_click,
+ {0,0},
+ 0, 0, 0, {0,0,0,0}
+ },
+ {
+ TOOLBAR_BT_STOP,
+ toolbar_stop_click,
+ {0,0},
+ 0, 0, 0, {0,0,0,0}
+ },
+ {
+ TOOLBAR_BT_RELOAD,
+ toolbar_reload_click,
+ {0,0},
+ 0, 0, 0, {0,0,0,0}
+ },
+ { 0, 0, {0,0}, 0, -1, 0, {0,0,0,0}}
+};
+
+struct s_toolbar_style {
+ int font_height_pt;
+};
+
+static struct s_toolbar_style toolbar_styles[] = {
+ /* small (18 px height) */
+ {9},
+ /* medium (default - 26 px height) */
+ {14},
+ /* large ( 49 px height ) */
+ {18},
+ /* custom style: */
+ {18}
+};
+
+static const struct redraw_context toolbar_rdrw_ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &atari_plotters
+};
+
+static void tb_txt_request_redraw(void *data, int x, int y, int w, int h );
+
+/**
+ * Find a button for a specific resource ID
+ */
+static struct s_tb_button *find_button(struct s_toolbar *tb, int rsc_id)
+{
+ int i = 0;
+ while (i < tb->btcnt) {
+ if (tb->buttons[i].rsc_id == rsc_id) {
+ return(&tb->buttons[i]);
+ }
+ i++;
+ }
+ return(NULL);
+}
+
+/**
+ * Callback for textarea redraw
+ */
+static void tb_txt_request_redraw(void *data, int x, int y, int w, int h)
+{
+
+ GRECT area;
+ struct s_toolbar * tb = (struct s_toolbar *)data;
+
+ if (tb->attached == false) {
+ return;
+ }
+
+ toolbar_get_grect(tb, TOOLBAR_AREA_URL, &area);
+ area.g_x += x;
+ area.g_y += y;
+ area.g_w = w;
+ area.g_h = h;
+ /* dbg_grect("tb_txt_request_redraw", &area); */
+ window_schedule_redraw_grect(tb->owner, &area);
+ return;
+}
+
+static void tb_txt_callback(void *data, struct textarea_msg *msg)
+{
+ switch (msg->type) {
+
+ case TEXTAREA_MSG_DRAG_REPORT:
+ break;
+
+ case TEXTAREA_MSG_REDRAW_REQUEST:
+ tb_txt_request_redraw(data,
+ msg->data.redraw.x0, msg->data.redraw.y0,
+ msg->data.redraw.x1 - msg->data.redraw.x0,
+ msg->data.redraw.y1 - msg->data.redraw.y0);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static struct s_tb_button *button_init(struct s_toolbar *tb, OBJECT * tree, int index,
+ struct s_tb_button * instance)
+{
+ *instance = tb_buttons[index];
+ instance->owner = tb;
+
+ return(instance);
+}
+
+
+static short __CDECL toolbar_url_userdraw(PARMBLK *parmblock)
+{
+ return(0);
+}
+
+void toolbar_init( void )
+{
+ static USERBLK userblk;
+
+ aes_toolbar = gemtk_obj_get_tree(TOOLBAR);
+ throbber_form = gemtk_obj_get_tree(THROBBER);
+
+ userblk.ub_code = toolbar_url_userdraw;
+ userblk.ub_parm = (long) aes_toolbar[TOOLBAR_AREA_URL].ob_spec.userblk;
+ aes_toolbar[TOOLBAR_AREA_URL].ob_spec.userblk = &userblk;
+
+ aes_toolbar[TOOLBAR_CB_SHOWALL].ob_state &= ~OS_SELECTED;
+ aes_toolbar[TOOLBAR_CB_CASESENSE].ob_state &= ~OS_SELECTED;
+
+ /* init default values: */
+ gemtk_obj_set_str_safe(aes_toolbar, TOOLBAR_TB_SRCH, (char*)"");
+
+ area_full_height = aes_toolbar->ob_height;
+ area_search_height = aes_toolbar[TOOLBAR_AREA_SEARCH].ob_height;
+ area_navigation_height = aes_toolbar[TOOLBAR_AREA_NAVIGATION].ob_height;
+
+ init = true;
+}
+
+
+void toolbar_exit(void)
+{
+
+}
+
+
+struct s_toolbar *toolbar_create(struct s_gui_win_root *owner)
+{
+ int i;
+ struct s_toolbar *t;
+
+ LOG("owner %p", owner);
+
+ assert(init == true);
+
+ t = calloc(1, sizeof(struct s_toolbar));
+
+ assert(t);
+
+ /* initialize the toolbar values: */
+ t->owner = owner;
+ t->style = 1;
+ t->search_visible = false;
+ t->visible = true;
+ t->reflow = true;
+
+ /* dublicate the form template: */
+ t->form = gemtk_obj_tree_copy(aes_toolbar);
+
+
+ /* count buttons and add them as components: */
+ i = 0;
+ while(tb_buttons[i].rsc_id > 0) {
+ i++;
+ }
+ t->btcnt = i;
+ t->buttons = malloc(t->btcnt * sizeof(struct s_tb_button));
+ memset(t->buttons, 0, t->btcnt * sizeof(struct s_tb_button));
+ for (i=0; i < t->btcnt; i++) {
+ button_init(t, aes_toolbar, i, &t->buttons[i]);
+ }
+
+ /* create the url widget: */
+ font_style_url.size =
+ toolbar_styles[t->style].font_height_pt * FONT_SIZE_SCALE;
+
+ textarea_flags ta_flags = TEXTAREA_INTERNAL_CARET;
+ textarea_setup ta_setup;
+ ta_setup.width = 300;
+ ta_setup.height = t->form[TOOLBAR_AREA_URL].ob_height;
+ ta_setup.pad_top = 0;
+ ta_setup.pad_right = 4;
+ ta_setup.pad_bottom = 0;
+ ta_setup.pad_left = 4;
+ ta_setup.border_width = 1;
+ ta_setup.border_col = 0x000000;
+ ta_setup.selected_text = 0xffffff;
+ ta_setup.selected_bg = 0x000000;
+ ta_setup.text = font_style_url;
+ ta_setup.text.foreground = 0x000000;
+ ta_setup.text.background = 0xffffff;
+ t->url.textarea = textarea_create(ta_flags, &ta_setup,
+ tb_txt_callback, t);
+
+ /* create the throbber widget: */
+ t->throbber.index = THROBBER_INACTIVE_INDEX;
+ t->throbber.max_index = THROBBER_MAX_INDEX;
+ t->throbber.running = false;
+
+ LOG("created toolbar: %p, root: %p, textarea: %p, throbber: %p",
+ t, owner, t->url.textarea, &t->throbber);
+ return( t );
+}
+
+
+void toolbar_destroy(struct s_toolbar *tb)
+{
+ free(tb->buttons);
+ free(tb->form);
+
+ textarea_destroy(tb->url.textarea);
+
+ free(tb);
+}
+
+static int toolbar_calculate_height(struct s_toolbar *tb)
+{
+ int r = 0;
+
+ if (tb->visible == false) {
+ return(0);
+ }
+
+ r += area_navigation_height;
+
+ if (tb->search_visible) {
+ r += area_search_height;
+ }
+
+ return(r);
+}
+
+static void toolbar_reflow(struct s_toolbar *tb)
+{
+ int i;
+ short offx, offy;
+
+ // position toolbar areas:
+ tb->form->ob_x = tb->area.g_x;
+ tb->form->ob_y = tb->area.g_y;
+ tb->form->ob_width = tb->area.g_w;
+ tb->form->ob_height = toolbar_calculate_height(tb);
+
+ // expand the "main" areas to the current width:
+ tb->form[TOOLBAR_AREA_NAVIGATION].ob_width = tb->area.g_w;
+ tb->form[TOOLBAR_AREA_SEARCH].ob_width = tb->area.g_w;
+
+ if (tb->search_visible) {
+ tb->form[TOOLBAR_AREA_SEARCH].ob_state &= ~OF_HIDETREE;
+ } else {
+ tb->form[TOOLBAR_AREA_SEARCH].ob_state |= OF_HIDETREE;
+
+ }
+
+ // align TOOLBAR_AREA_RIGHT IBOX at right edge:
+ tb->form[TOOLBAR_AREA_RIGHT].ob_x = tb->area.g_w
+ - tb->form[TOOLBAR_AREA_RIGHT].ob_width;
+
+ // center the URL area:
+ tb->form[TOOLBAR_AREA_URL].ob_width = tb->area.g_w
+ - (tb->form[TOOLBAR_AREA_LEFT].ob_width
+ + tb->form[TOOLBAR_AREA_RIGHT].ob_width);
+
+ // position throbber image above IBOX:
+ objc_offset(tb->form, TOOLBAR_THROBBER_AREA, &offx, &offy);
+ throbber_form[tb->throbber.index].ob_x = offx;
+ throbber_form[tb->throbber.index].ob_y = offy;
+
+ // align the search button:
+ tb->form[TOOLBAR_SEARCH_ALIGN_RIGHT].ob_x = tb->area.g_w
+ - tb->form[TOOLBAR_SEARCH_ALIGN_RIGHT].ob_width;
+
+ // set button states:
+ for (i=0; i < tb->btcnt; i++ ) {
+ if (tb->buttons[i].state == button_off) {
+ tb->form[tb->buttons[i].rsc_id].ob_state |= OS_DISABLED;
+ } else if (tb->buttons[i].state == button_on) {
+ tb->form[tb->buttons[i].rsc_id].ob_state &= ~OS_DISABLED;
+ }
+ }
+ tb->reflow = false;
+ // TODO: iterate through all other toolbars and set reflow = true
+}
+
+void toolbar_redraw(struct s_toolbar *tb, GRECT *clip)
+{
+ GRECT area, area_ro;
+
+ if (tb->attached == false) {
+ return;
+ }
+
+ if(tb->reflow == true)
+ toolbar_reflow(tb);
+
+ //dbg_grect("toolbar redraw clip", clip);
+
+ /* Redraw the AES objects: */
+ objc_draw_grect(tb->form,0,8,clip);
+ objc_draw_grect(&throbber_form[tb->throbber.index], 0, 1, clip);
+
+ toolbar_get_grect(tb, TOOLBAR_AREA_URL, &area_ro);
+ area = area_ro;
+
+ if (rc_intersect(clip, &area)) {
+ float old_scale;
+
+ plot_set_dimensions(area_ro.g_x, area_ro.g_y, area_ro.g_w, area_ro.g_h);
+ struct rect r = {
+ .x0 = MAX(0,area.g_x - area_ro.g_x),
+ .y0 = MAX(0,area.g_y - area_ro.g_y),
+ .x1 = MAX(0,area.g_x - area_ro.g_x) + area.g_w,
+ .y1 = MAX(0,area.g_y - area_ro.g_y) + area.g_h
+ };
+ //dbg_rect("tb textarea clip: ", &r);
+ // TODO: let this be handled by an userdef object redraw function:
+ /* Redraw the url input: */
+ old_scale = plot_set_scale(toolbar_url_scale);
+ textarea_redraw(tb->url.textarea, 0, 0, 0xffffff, 1.0, &r,
+ &toolbar_rdrw_ctx);
+ plot_set_scale(old_scale);
+ }
+}
+
+
+void
+toolbar_update_buttons(struct s_toolbar *tb,
+ struct browser_window *bw,
+ short button)
+{
+ LOG("tb %p", tb);
+
+ struct s_tb_button * bt;
+ bool enable = false;
+ GRECT area;
+
+ assert(bw != NULL);
+
+ if (button == TOOLBAR_BT_BACK || button <= 0 ) {
+ bt = find_button(tb, TOOLBAR_BT_BACK);
+ enable = browser_window_back_available(bw);
+ if (enable) {
+ bt->state = button_on;
+ } else {
+ bt->state = button_off;
+ }
+ }
+
+ if (button == TOOLBAR_BT_HOME || button <= 0 ) {
+
+ }
+
+ if (button == TOOLBAR_BT_FORWARD || button <= 0 ) {
+ bt = find_button(tb, TOOLBAR_BT_FORWARD);
+ enable = browser_window_forward_available(bw);
+ if (enable) {
+ bt->state = button_on;
+ } else {
+ bt->state = button_off;
+ }
+ }
+
+ if (button == TOOLBAR_BT_RELOAD || button <= 0 ) {
+ bt = find_button(tb, TOOLBAR_BT_RELOAD);
+ enable = browser_window_reload_available(bw);
+ if (enable) {
+ bt->state = button_on;
+ } else {
+ bt->state = button_off;
+ }
+ }
+
+ if (button == TOOLBAR_BT_STOP || button <= 0) {
+ bt = find_button(tb, TOOLBAR_BT_STOP);
+ enable = browser_window_stop_available(bw);
+ if (enable) {
+ bt->state = button_on;
+ } else {
+ bt->state = button_off;
+ }
+ }
+
+ if (tb->attached) {
+ if (button > 0) {
+ toolbar_get_grect(tb, button, &area);
+ window_schedule_redraw_grect(tb->owner, &area);
+ } else {
+ toolbar_get_grect(tb, TOOLBAR_AREA_LEFT, &area);
+ window_schedule_redraw_grect(tb->owner, &area);
+
+ toolbar_get_grect(tb, TOOLBAR_AREA_RIGHT, &area);
+ window_schedule_redraw_grect(tb->owner, &area);
+ }
+ }
+}
+
+void toolbar_set_width(struct s_toolbar *tb, short w)
+{
+ GRECT cur;
+
+ toolbar_get_grect(tb, 0, &cur);
+
+ if (w != cur.g_w) {
+
+ tb->area.g_w = w;
+
+ /* reflow now, just for url input calucation: */
+ toolbar_reflow(tb);
+ /* this will request an textarea redraw: */
+ textarea_set_dimensions(tb->url.textarea,
+ tb->form[TOOLBAR_AREA_URL].ob_width,
+ tb->form[TOOLBAR_AREA_URL].ob_height);
+ tb->reflow = true;
+ }
+}
+
+void toolbar_set_origin(struct s_toolbar *tb, short x, short y)
+{
+ GRECT cur;
+
+ toolbar_get_grect(tb, 0, &cur);
+
+ if (x != cur.g_x || y != cur.g_y) {
+ tb->area.g_x = x;
+ tb->area.g_y = y;
+ tb->reflow = true;
+ }
+}
+
+void toolbar_set_dimensions(struct s_toolbar *tb, GRECT *area)
+{
+ if (area->g_w != tb->area.g_w) {
+
+ tb->area = *area;
+
+ /* reflow now, just for url input calucation: */
+ toolbar_reflow(tb);
+ /* this will request an textarea redraw: */
+ textarea_set_dimensions(tb->url.textarea,
+ tb->form[TOOLBAR_AREA_URL].ob_width,
+ tb->form[TOOLBAR_AREA_URL].ob_height-1);
+ }
+ else {
+ tb->area = *area;
+ }
+ /* reflow for next redraw: */
+ /* TODO: that's only required because we do not reset others toolbars reflow
+ state on reflow */
+ tb->reflow = true;
+}
+
+
+void toolbar_set_url(struct s_toolbar *tb, const char *text)
+{
+ LOG("tb %p", tb);
+
+ textarea_set_text(tb->url.textarea, text);
+
+ if (tb->attached && tb->visible) {
+ GRECT area;
+ toolbar_get_grect(tb, TOOLBAR_AREA_URL, &area);
+ window_schedule_redraw_grect(tb->owner, &area);
+ struct gui_window * gw = window_get_active_gui_window(tb->owner);
+ assert(gw != NULL);
+ toolbar_update_buttons(tb, gw->browser->bw , 0);
+ }
+}
+
+void toolbar_set_throbber_state(struct s_toolbar *tb, bool active)
+{
+ GRECT throbber_area;
+
+ tb->throbber.running = active;
+ if (active) {
+ tb->throbber.index = THROBBER_MIN_INDEX;
+ } else {
+ tb->throbber.index = THROBBER_INACTIVE_INDEX;
+ }
+
+ tb->reflow = true;
+ toolbar_get_grect(tb, TOOLBAR_THROBBER_AREA, &throbber_area);
+ window_schedule_redraw_grect(tb->owner, &throbber_area);
+}
+
+void toolbar_set_visible(struct s_toolbar *tb, short area, bool visible)
+{
+ if (area == 0) {
+ if ((visible == false) && (tb->visible == true)) {
+ tb->visible = false;
+ tb->reflow = true;
+ } else if((visible == true) && (tb->visible == false)) {
+ tb->visible = false;
+ tb->reflow = true;
+ }
+ } else if (area == TOOLBAR_AREA_SEARCH) {
+ tb->search_visible = visible;
+ tb->reflow = true;
+ OBJECT *frm = toolbar_get_form(tb);
+ if(visible == false){
+ frm[TOOLBAR_AREA_SEARCH].ob_flags |= OF_HIDETREE;
+ } else {
+ frm[TOOLBAR_AREA_SEARCH].ob_flags &= ~OF_HIDETREE;
+ }
+ }
+}
+
+void toolbar_set_reflow(struct s_toolbar *tb, bool do_reflow)
+{
+ tb->reflow = do_reflow;
+}
+
+void toolbar_set_attached(struct s_toolbar *tb, bool attached)
+{
+ tb->attached = attached;
+
+}
+
+void toolbar_throbber_progress(struct s_toolbar *tb)
+{
+ GRECT throbber_area;
+
+ assert(tb->throbber.running == true);
+
+ if(tb->throbber.running == false)
+ return;
+
+ tb->throbber.index++;
+ if (tb->throbber.index > THROBBER_MAX_INDEX) {
+ tb->throbber.index = THROBBER_MIN_INDEX;
+ }
+
+ tb->reflow = true;
+ toolbar_get_grect(tb, TOOLBAR_THROBBER_AREA, &throbber_area);
+ window_schedule_redraw_grect(tb->owner, &throbber_area);
+}
+
+bool toolbar_text_input(struct s_toolbar *tb, char *text)
+{
+ bool handled = true;
+
+ LOG("tb %p", tb);
+
+ return(handled);
+}
+
+bool toolbar_key_input(struct s_toolbar *tb, short nkc)
+{
+
+ assert(tb!=NULL);
+ bool ret = false;
+ struct gui_window *gw = window_get_active_gui_window(tb->owner);
+
+ assert( gw != NULL );
+
+ long ucs4;
+ long ik = nkc_to_input_key(nkc, &ucs4);
+
+ if (ik == 0) {
+ if ((nkc&0xFF) >= 9) {
+ ret = textarea_keypress(tb->url.textarea, ucs4);
+ }
+
+ } else if (ik == NS_KEY_CR || ik == NS_KEY_NL) {
+ nsurl *url;
+ char tmp_url[PATH_MAX];
+ if ( textarea_get_text( tb->url.textarea, tmp_url, PATH_MAX) > 0 ) {
+ window_set_focus(tb->owner, BROWSER, gw->browser);
+ if (nsurl_create((const char*)&tmp_url, &url) != NSERROR_OK) {
+ atari_warn_user("NoMemory", 0);
+ } else {
+ browser_window_navigate(gw->browser->bw, url, NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL, NULL, NULL);
+ nsurl_unref(url);
+ }
+
+ ret = true;
+ }
+
+ } else if (ik == NS_KEY_COPY_SELECTION) {
+ // copy whole text
+ char * text;
+ int len;
+ len = textarea_get_text( tb->url.textarea, NULL, 0 );
+ text = malloc( len+1 );
+ if (text){
+ textarea_get_text( tb->url.textarea, text, len+1 );
+ scrap_txt_write(text);
+ free( text );
+ }
+
+ } else if ( ik == NS_KEY_PASTE) {
+ char * clip = scrap_txt_read();
+ if ( clip != NULL ){
+ int clip_length = strlen( clip );
+ if ( clip_length > 0 ) {
+ char *utf8;
+ nserror res;
+ /* Clipboard is in local encoding so
+ * convert to UTF8 */
+ res = utf8_from_local_encoding( clip, clip_length, &utf8 );
+ if ( res == NSERROR_OK ) {
+ toolbar_set_url(tb, utf8);
+ free(utf8);
+ ret = true;
+ }
+ }
+ free( clip );
+ }
+
+ } else if (ik == NS_KEY_ESCAPE) {
+ textarea_keypress( tb->url.textarea, NS_KEY_SELECT_ALL );
+ textarea_keypress( tb->url.textarea, NS_KEY_DELETE_LEFT );
+
+ } else {
+ ret = textarea_keypress( tb->url.textarea, ik );
+
+ }
+
+ return( ret );
+}
+
+
+void toolbar_mouse_input(struct s_toolbar *tb, short obj, short button)
+{
+ GRECT work;
+ short mx, my, mb, kstat;
+ struct gui_window * gw;
+
+ LOG("tb %p", tb);
+
+
+ if (obj==TOOLBAR_AREA_URL) {
+ graf_mkstate(&mx, &my, &mb, &kstat);
+ toolbar_get_grect(tb, TOOLBAR_AREA_URL, &work);
+ mx -= work.g_x;
+ my -= work.g_y;
+
+ /* TODO: reset mouse state of browser window? */
+ /* select whole text when newly focused, otherwise set caret to
+ end of text */
+ if (!window_url_widget_has_focus(tb->owner)) {
+ window_set_focus(tb->owner, URL_WIDGET, (void*)&tb->url);
+
+ } else if (mb & 1) {
+ /* url widget has focus and mouse button is still pressed... */
+
+ textarea_mouse_action(tb->url.textarea, BROWSER_MOUSE_DRAG_1,
+ mx, my );
+ short prev_x = mx;
+ short prev_y = my;
+ do {
+ if (abs(prev_x-mx) > 5 || abs(prev_y-my) > 5) {
+ textarea_mouse_action( tb->url.textarea,
+ BROWSER_MOUSE_HOLDING_1, mx, my );
+ prev_x = mx;
+ prev_y = my;
+ window_schedule_redraw_grect(tb->owner, &work);
+ window_process_redraws(tb->owner);
+ }
+ graf_mkstate( &mx, &my, &mb, &kstat );
+ mx = mx - (work.g_x);
+ my = my - (work.g_y);
+ } while (mb & 1);
+
+ textarea_mouse_action( tb->url.textarea, BROWSER_MOUSE_HOVER, mx,
+ my);
+
+ } else if (button & 2) {
+ // TODO: open a context popup
+
+ } else {
+ /* when execution reaches here, mouse input is a click or dclick */
+ /* TODO: recognize click + shitoolbar_update_buttonsft key */
+ if (aes_event_out.emo_mclicks == 2 ) {
+ textarea_mouse_action( tb->url.textarea,
+ BROWSER_MOUSE_DOUBLE_CLICK | BROWSER_MOUSE_CLICK_1, mx,
+ my);
+ toolbar_get_grect(tb, TOOLBAR_AREA_URL, &work);
+ window_schedule_redraw_grect(tb->owner, &work);
+ } else {
+ textarea_mouse_action(tb->url.textarea,
+ BROWSER_MOUSE_PRESS_1, mx, my );
+ }
+ }
+
+ } else if(obj==TOOLBAR_TB_SRCH) {
+ window_set_focus(tb->owner, SEARCH_INPUT, NULL);
+
+ } else if (obj==TOOLBAR_BT_SEARCH_FWD) {
+ gw = tb->owner->active_gui_window;
+ assert(gw->search);
+ nsatari_search_perform(gw->search, tb->form, SEARCH_FLAG_FORWARDS);
+
+ } else if (obj==TOOLBAR_BT_SEARCH_BACK) {
+ gw = tb->owner->active_gui_window;
+ assert(gw->search);
+ nsatari_search_perform(gw->search, tb->form, 0);
+
+ } else if (obj==TOOLBAR_BT_CLOSE_SEARCH) {
+ tb->form[TOOLBAR_BT_CLOSE_SEARCH].ob_state &= ~OS_SELECTED;
+ window_close_search(tb->owner);
+ } else {
+ struct s_tb_button *bt = find_button(tb, obj);
+ if (bt != NULL && bt->state != button_off) {
+ bt->cb_click(tb);
+ struct gui_window * gw = window_get_active_gui_window(tb->owner);
+ toolbar_update_buttons(tb, gw->browser->bw, 0);
+ }
+ }
+}
+
+
+/**
+ * Receive a specific region of the toolbar.
+ * @param tb - the toolbar pointer
+ * @param which - the area to retrieve: 0 to receive the workarea,
+ all other values must be
+ an resource ID of the TOOLBAR tree.
+ * @param dst - GRECT pointer receiving the area.
+ */
+
+void toolbar_get_grect(struct s_toolbar *tb, short which, GRECT *dst)
+{
+#define LAST_TOOLBAR_AREA TOOLBAR_AREA_SEARCH
+
+ if (tb->reflow == true) {
+ toolbar_reflow(tb);
+ }
+
+ objc_offset(tb->form, which, &dst->g_x, &dst->g_y);
+
+ dst->g_w = tb->form[which].ob_width;
+ dst->g_h = tb->form[which].ob_height;
+ //tb->form[which].ob_height;
+
+ //printf("Toolbar get grect (%d): ", which);
+ //dbg_grect("", dst);
+
+#undef LAST_TOOLBAR_AREA
+}
+
+
+struct textarea *toolbar_get_textarea(struct s_toolbar *tb,
+ enum toolbar_textarea which)
+{
+ return(tb->url.textarea);
+}
+
+char *toolbar_get_url(struct s_toolbar *tb)
+{
+ char * c_url = NULL;
+ int c_url_len = 0;
+
+ c_url_len = textarea_get_text(tb->url.textarea, NULL, 0);
+
+ if (c_url_len > -1) {
+ c_url = malloc(c_url_len+1);
+ textarea_get_text(tb->url.textarea, c_url, c_url_len+1);
+ }
+
+ return(c_url);
+}
+
+nsurl * toolbar_get_nsurl(struct s_toolbar * tb)
+{
+ nsurl * ns_url = NULL;
+ char * c_url;
+
+ c_url = toolbar_get_url(tb);
+ if (c_url) {
+ nsurl_create(c_url, &ns_url);
+ }
+
+ return(ns_url);
+}
+
+
+OBJECT *toolbar_get_form(struct s_toolbar *tb)
+{
+ return(tb->form);
+}
+
+
+/* public event handler */
+void toolbar_back_click(struct s_toolbar *tb)
+{
+ struct browser_window * bw;
+ struct gui_window * gw;
+
+ gw = window_get_active_gui_window(tb->owner);
+ assert(gw != NULL);
+ bw = gw->browser->bw;
+ assert(bw != NULL);
+
+ if( browser_window_back_available(bw) ) {
+ browser_window_history_back(bw, false);
+ }
+}
+
+void toolbar_reload_click(struct s_toolbar *tb)
+{
+ struct browser_window * bw;
+ struct gui_window * gw;
+
+ gw = window_get_active_gui_window(tb->owner);
+ assert(gw != NULL);
+ bw = gw->browser->bw;
+ assert(bw != NULL);
+
+ browser_window_reload(bw, true);
+}
+
+void toolbar_forward_click(struct s_toolbar *tb)
+{
+ struct browser_window * bw;
+ struct gui_window * gw;
+
+ gw = window_get_active_gui_window(tb->owner);
+ assert(gw != NULL);
+ bw = gw->browser->bw;
+ assert(bw != NULL);
+
+ if (browser_window_forward_available(bw)) {
+ browser_window_history_forward(bw, false);
+ }
+}
+
+void toolbar_home_click(struct s_toolbar *tb)
+{
+ struct browser_window * bw;
+ struct gui_window * gw;
+ nsurl *url;
+ char * use_url = NULL;
+
+ gw = window_get_active_gui_window(tb->owner);
+ assert(gw != NULL);
+ bw = gw->browser->bw;
+ assert(bw != NULL);
+
+ use_url = nsoption_charp(homepage_url);
+ if (use_url == NULL || strlen(use_url) == 0){
+ use_url = (char*)"about:welcome";
+ }
+
+ if (nsurl_create(use_url, &url) != NSERROR_OK) {
+ atari_warn_user("NoMemory", 0);
+ } else {
+ browser_window_navigate(bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+}
+
+
+void toolbar_stop_click(struct s_toolbar *tb)
+{
+ struct browser_window * bw;
+ struct gui_window * gw;
+
+ gw = window_get_active_gui_window(tb->owner);
+
+ assert(gw != NULL);
+
+ bw = gw->browser->bw;
+
+ assert(bw != NULL);
+
+ browser_window_stop(bw);
+}
+
+void toolbar_favorite_click(struct s_toolbar *tb)
+{
+ nsurl * ns_url = NULL;
+ char * c_url;
+ int c_url_len = 0;
+
+ c_url = toolbar_get_url(tb);
+ c_url_len = strlen(c_url);
+
+ nsurl_create(c_url, &ns_url);
+
+ if (hotlist_has_url(ns_url)) {
+ char msg[c_url_len+100];
+ snprintf(msg, c_url_len+100, "Really delete from favorites: \"%s\"",
+ c_url);
+ if (gemtk_msg_box_show(GEMTK_MSG_BOX_CONFIRM, msg)) {
+ hotlist_remove_url(ns_url);
+ }
+
+ } else {
+ hotlist_add_url(ns_url);
+
+ }
+
+ nsurl_unref(ns_url);
+ free(c_url);
+}
+
+void toolbar_crypto_click(struct s_toolbar *tb)
+{
+
+}
diff --git a/frontends/atari/toolbar.h b/frontends/atari/toolbar.h
new file mode 100644
index 000000000..61ae48c82
--- /dev/null
+++ b/frontends/atari/toolbar.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2013 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_ATARI_TOOLBAR_H
+#define NS_ATARI_TOOLBAR_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+struct s_toolbar;
+struct nsurl;
+
+enum toolbar_textarea {
+ URL_INPUT_TEXT_AREA = 1
+};
+
+struct s_url_widget {
+ struct textarea *textarea;
+ GRECT area;
+};
+
+struct s_throbber_widget {
+ short index;
+ short max_index;
+ bool running;
+};
+
+struct s_toolbar {
+ struct s_gui_win_root *owner;
+ struct s_url_widget url;
+ struct s_throbber_widget throbber;
+ OBJECT *form;
+ GRECT area;
+
+ /* size & location of buttons: */
+ struct s_tb_button * buttons;
+ int btcnt;
+ int style;
+ bool attached;
+ bool reflow;
+ bool visible;
+ bool search_visible;
+};
+
+
+void toolbar_init(void);
+struct s_toolbar *toolbar_create(struct s_gui_win_root *owner);
+void toolbar_destroy(struct s_toolbar * tb);
+void toolbar_exit( void );
+bool toolbar_text_input(struct s_toolbar *tb, char *text);
+bool toolbar_key_input(struct s_toolbar *tb, short nkc);
+void toolbar_mouse_input(struct s_toolbar *tb, short obj, short mbut);
+void toolbar_update_buttons(struct s_toolbar *tb, struct browser_window *bw,
+ short idx);
+void toolbar_get_grect(struct s_toolbar *tb, short which, GRECT *g);
+OBJECT *toolbar_get_form(struct s_toolbar *tb);
+struct textarea *toolbar_get_textarea(struct s_toolbar *tb,
+ enum toolbar_textarea which);
+char *toolbar_get_url(struct s_toolbar *tb);
+struct nsurl * toolbar_get_nsurl(struct s_toolbar * tb);
+void toolbar_set_throbber_state(struct s_toolbar *tb, bool active);
+void toolbar_set_attached(struct s_toolbar *tb, bool attached);
+void toolbar_set_visible(struct s_toolbar *tb, short area, bool visible);
+void toolbar_set_reflow(struct s_toolbar *tb, bool do_reflow);
+void toolbar_set_width(struct s_toolbar *tb, short w);
+void toolbar_set_origin(struct s_toolbar *tb, short x, short y);
+void toolbar_set_dimensions(struct s_toolbar *tb, GRECT *area);
+void toolbar_set_url(struct s_toolbar *tb, const char *text);
+void toolbar_redraw(struct s_toolbar *tb, GRECT *clip);
+void toolbar_throbber_progress(struct s_toolbar *tb);
+/* public events handlers: */
+void toolbar_back_click(struct s_toolbar *tb);
+void toolbar_reload_click(struct s_toolbar *tb);
+void toolbar_forward_click(struct s_toolbar *tb);
+void toolbar_home_click(struct s_toolbar *tb);
+void toolbar_stop_click(struct s_toolbar *tb);
+void toolbar_favorite_click(struct s_toolbar *tb);
+void toolbar_crypto_click(struct s_toolbar *tb);
+
+
+#endif
diff --git a/frontends/atari/treeview.c b/frontends/atari/treeview.c
new file mode 100644
index 000000000..513e2dfe9
--- /dev/null
+++ b/frontends/atari/treeview.c
@@ -0,0 +1,794 @@
+/*
+ * Copyright 2013 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <inttypes.h>
+#include <sys/types.h>
+#include <string.h>
+
+#include "assert.h"
+#include "cflib.h"
+
+#include "utils/nsoption.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "content/urldb.h"
+#include "desktop/plotters.h"
+#include "desktop/mouse.h"
+#include "desktop/treeview.h"
+
+#include "atari/gui.h"
+#include "atari/plot/plot.h"
+#include "atari/misc.h"
+#include "atari/gemtk/gemtk.h"
+#include "atari/treeview.h"
+#include "atari/res/netsurf.rsh"
+
+
+/**
+ * Declare Core Window Callbacks:
+ */
+
+void atari_treeview_redraw_request(struct core_window *cw,
+ const struct rect *r);
+void atari_treeview_update_size(struct core_window *cw, int width, int height);
+void atari_treeview_scroll_visible(struct core_window *cw,
+ const struct rect *r);
+void atari_treeview_get_window_dimensions(struct core_window *cw,
+ int *width, int *height);
+ // TODO: implement drag status!
+void atari_treeview_drag_status(struct core_window *cw,
+ core_window_drag_status ds);
+
+static struct core_window_callback_table cw_t = {
+ .redraw_request = atari_treeview_redraw_request,
+ .update_size = atari_treeview_update_size,
+ .scroll_visible = atari_treeview_scroll_visible,
+ .get_window_dimensions = atari_treeview_get_window_dimensions,
+ .drag_status = atari_treeview_drag_status
+};
+
+
+struct atari_treeview_window {
+ struct atari_treeview_window * prev_open;
+ struct atari_treeview_window * next_open;
+ GUIWIN * window;
+ bool disposing;
+ bool redraw;
+ bool is_open;
+ GRECT rdw_area;
+ POINT extent;
+ POINT click;
+ POINT startdrag;
+ struct atari_treeview_callbacks *io;
+ void * user_data;
+};
+
+static struct atari_treeview_window * treeviews_open;
+
+/* native GUI event handlers: */
+static void on_mbutton_event(struct core_window *cw, EVMULT_OUT *ev_out,
+ short msg[8]);
+static void on_keybd_event(struct core_window *cw, EVMULT_OUT *ev_out,
+ short msg[8]);
+static void on_redraw_event(struct core_window *cw, EVMULT_OUT *ev_out,
+ short msg[8]);
+
+/* static utils: */
+//static void atari_treeview_dump_info(struct atari_treeview_window *tv, char *s);
+
+/**
+ * Schedule a redraw of the treeview content
+ *
+ */
+static void atari_treeview_redraw_grect_request(struct core_window *cw,
+ GRECT *area)
+{
+ struct atari_treeview_window *tv = (struct atari_treeview_window *)cw;
+ if (cw != NULL) {
+ if( tv->redraw == false ){
+ tv->redraw = true;
+ tv->rdw_area.g_x = area->g_x;
+ tv->rdw_area.g_y = area->g_y;
+ tv->rdw_area.g_w = area->g_w;
+ tv->rdw_area.g_h = area->g_h;
+ } else {
+ /* merge the redraw area to the new area.: */
+ int newx1 = area->g_x+area->g_w;
+ int newy1 = area->g_y+area->g_h;
+ int oldx1 = tv->rdw_area.g_x + tv->rdw_area.g_w;
+ int oldy1 = tv->rdw_area.g_y + tv->rdw_area.g_h;
+ tv->rdw_area.g_x = MIN(tv->rdw_area.g_x, area->g_x);
+ tv->rdw_area.g_y = MIN(tv->rdw_area.g_y, area->g_y);
+ tv->rdw_area.g_w = ( oldx1 > newx1 ) ? oldx1 - tv->rdw_area.g_x : newx1 - tv->rdw_area.g_x;
+ tv->rdw_area.g_h = ( oldy1 > newy1 ) ? oldy1 - tv->rdw_area.g_y : newy1 - tv->rdw_area.g_y;
+ }
+ //dbg_grect("atari_treeview_request_redraw_grect", &tv->rdw_area);
+ }
+}
+
+
+void atari_treeview_get_grect(struct core_window *cw, enum treeview_area_e mode,
+ GRECT *dest)
+{
+
+ struct atari_treeview_window *tv = (struct atari_treeview_window *)cw;
+
+ if (mode == TREEVIEW_AREA_CONTENT) {
+ gemtk_wm_get_grect(tv->window, GEMTK_WM_AREA_CONTENT, dest);
+ }
+ else if (mode == TREEVIEW_AREA_TOOLBAR) {
+ gemtk_wm_get_grect(tv->window, GEMTK_WM_AREA_TOOLBAR, dest);
+ }
+}
+
+GUIWIN * atari_treeview_get_gemtk_window(struct core_window *cw)
+{
+ struct atari_treeview_window *tv = (struct atari_treeview_window *)cw;
+ return(tv->window);
+}
+
+/*
+static void atari_treeview_dump_info(struct atari_treeview_window *tv,
+ char * title)
+{
+ printf("Treeview Dump (%s)\n", title);
+ printf("=================================\n");
+ gemtk_wm_dump_window_info(atari_treeview_get_gemtk_window((struct core_window *)tv));
+ GEMTK_DBG_GRECT("Redraw Area: \n", &tv->rdw_area)
+ dbg_grect("Redraw Area2: \n", &tv->rdw_area);
+ printf("Extent: x: %d, y: %d\n", tv->extent.x, tv->extent.y);
+}
+*/
+
+static bool atari_treeview_is_iconified(struct core_window *cw){
+
+ struct atari_treeview_window *tv = (struct atari_treeview_window *)cw;
+
+ return((gemtk_wm_get_state(tv->window)&GEMTK_WM_STATUS_ICONIFIED) != 0);
+}
+
+static void atari_treeview_redraw_icon(struct core_window *cw, GRECT *clip)
+{
+ struct atari_treeview_window *tv = (struct atari_treeview_window *)cw;
+ GRECT visible, work;
+ OBJECT * tree = gemtk_obj_get_tree(ICONIFY);
+ short aesh = gemtk_wm_get_handle(tv->window);
+
+ gemtk_wm_get_grect(tv->window, GEMTK_WM_AREA_WORK, &work);
+
+ tree->ob_x = work.g_x;
+ tree->ob_y = work.g_y;
+ tree->ob_width = work.g_w;
+ tree->ob_height = work.g_h;
+
+ wind_get_grect(aesh, WF_FIRSTXYWH, &visible);
+ while (visible.g_h > 0 && visible.g_w > 0) {
+
+ if (rc_intersect(&work, &visible)) {
+ objc_draw(tree, 0, 8, visible.g_x, visible.g_y, visible.g_w,
+ visible.g_h);
+ } else {
+ //dbg_grect("redraw vis area outside", &visible);
+ }
+
+ wind_get_grect(aesh, WF_NEXTXYWH, &visible);
+ }
+}
+
+void atari_treeview_redraw(struct core_window *cw)
+{
+ struct atari_treeview_window *tv = (struct atari_treeview_window *)cw;
+ short pxy[4];
+
+ if (tv != NULL && tv->is_open) {
+ if( tv->redraw && ((plot_get_flags() & PLOT_FLAG_OFFSCREEN) == 0) ) {
+
+ short todo[4];
+ GRECT work;
+ short handle = gemtk_wm_get_handle(tv->window);
+ struct gemtk_wm_scroll_info_s *slid;
+
+ gemtk_wm_get_grect(tv->window, GEMTK_WM_AREA_CONTENT, &work);
+ slid = gemtk_wm_get_scroll_info(tv->window);
+
+// // Debug code: this 3 lines help to inspect the redraw
+// // areas...
+// pxy[0] = work.g_x;
+// pxy[1] = work.g_y;
+// pxy[2] = pxy[0] + work.g_w-1;
+// pxy[3] = pxy[1] + work.g_h-1;
+//
+// vsf_color(plot_get_vdi_handle(), 0);
+// v_bar(plot_get_vdi_handle(), (short*)&pxy);
+// evnt_timer(500);
+
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &atari_plotters
+ };
+ plot_set_dimensions(work.g_x, work.g_y, work.g_w, work.g_h);
+ if (plot_lock() == false)
+ return;
+
+ if( wind_get(handle, WF_FIRSTXYWH,
+ &todo[0], &todo[1], &todo[2], &todo[3] )!=0 ) {
+ while (todo[2] && todo[3]) {
+
+ if(!rc_intersect(&work, (GRECT*)&todo)){
+ if (wind_get(handle, WF_NEXTXYWH,
+ &todo[0], &todo[1], &todo[2], &todo[3])==0) {
+ break;
+ }
+ continue;
+ }
+ pxy[0] = todo[0];
+ pxy[1] = todo[1];
+ pxy[2] = todo[0] + todo[2]-1;
+ pxy[3] = todo[1] + todo[3]-1;
+ vs_clip(plot_get_vdi_handle(), 1, (short*)&pxy);
+
+ // Debug code: this 3 lines help to inspect the redraw
+ // areas...
+
+// vsf_color(plot_get_vdi_handle(), 3);
+// v_bar(plot_get_vdi_handle(), (short*)&pxy);
+// evnt_timer(500);
+
+
+ /* convert screen to treeview coords: */
+ todo[0] = todo[0] - work.g_x ;//+ slid->x_pos*slid->x_unit_px;
+ todo[1] = todo[1] - work.g_y ;//+ slid->y_pos*slid->y_unit_px;
+ if( todo[0] < 0 ){
+ todo[2] = todo[2] + todo[0];
+ todo[0] = 0;
+ }
+ if( todo[1] < 0 ){
+ todo[3] = todo[3] + todo[1];
+ todo[1] = 0;
+ }
+
+ if (rc_intersect((GRECT *)&tv->rdw_area,(GRECT *)&todo)) {
+ struct rect clip;
+
+ clip.x0 = todo[0]+(slid->x_pos*slid->x_unit_px);
+ clip.y0 = todo[1]+(slid->y_pos*slid->y_unit_px);
+ clip.x1 = clip.x0 + todo[2]+(slid->x_pos*slid->x_unit_px);
+ clip.y1 = clip.y0 + todo[3]+(slid->y_pos*slid->y_unit_px);
+
+ tv->io->draw(cw, -(slid->x_pos*slid->x_unit_px),
+ -(slid->y_pos*slid->y_unit_px),
+ &clip, &ctx);
+ }
+ vs_clip(plot_get_vdi_handle(), 0, (short*)&pxy);
+ if (wind_get(handle, WF_NEXTXYWH,
+ &todo[0], &todo[1], &todo[2], &todo[3])==0) {
+ break;
+ }
+ }
+ } else {
+ plot_unlock();
+ return;
+ }
+ plot_unlock();
+ tv->redraw = false;
+ tv->rdw_area.g_x = 65000;
+ tv->rdw_area.g_y = 65000;
+ tv->rdw_area.g_w = -1;
+ tv->rdw_area.g_h = -1;
+ } else {
+ /* just copy stuff from the offscreen buffer */
+ }
+ }
+}
+
+
+/**
+ * GEMTK (netsurf's GEM toolkit) event sink
+ *
+*/
+static short handle_event(GUIWIN *win, EVMULT_OUT *ev_out, short msg[8])
+{
+ short retval = 0;
+ struct atari_treeview_window *tv = (struct atari_treeview_window *)
+ gemtk_wm_get_user_data(win);
+ struct core_window *cw = (struct core_window *)tv;
+
+ if( (ev_out->emo_events & MU_MESAG) != 0 ) {
+ // handle message
+ switch (msg[0]) {
+
+ case WM_REDRAW:
+ on_redraw_event(cw, ev_out, msg);
+ break;
+
+ default:
+ break;
+ }
+ }
+ if( (ev_out->emo_events & MU_KEYBD) != 0 ) {
+ on_keybd_event(cw, ev_out, msg);
+ }
+ if( (ev_out->emo_events & MU_BUTTON) != 0 ) {
+ LOG("Treeview click at: %d,%d\n", ev_out->emo_mouse.p_x, ev_out->emo_mouse.p_y);
+ on_mbutton_event(cw, ev_out, msg);
+ }
+
+ if(tv != NULL && tv->io->gemtk_user_func != NULL){
+ tv->io->gemtk_user_func(win, ev_out, msg);
+ }
+
+ // TODO: evaluate return values of event handler functions and pass them on:
+ return(retval);
+}
+
+
+static void __CDECL on_keybd_event(struct core_window *cw, EVMULT_OUT *ev_out,
+ short msg[8])
+{
+ long kstate = 0;
+ long kcode = 0;
+ long ucs4;
+ long ik;
+ unsigned short nkc = 0;
+ unsigned char ascii;
+ struct atari_treeview_window *tv = (struct atari_treeview_window *)cw;
+
+ kstate = ev_out->emo_kmeta;
+ kcode = ev_out->emo_kreturn;
+ nkc= gem_to_norm( (short)kstate, (short)kcode );
+ ascii = (nkc & 0xFF);
+ ik = nkc_to_input_key(nkc, &ucs4);
+
+ if (ik == 0) {
+ if (ascii >= 9) {
+ tv->io->keypress(cw, ucs4);
+ }
+ } else {
+ tv->io->keypress(cw, ik);
+ }
+}
+
+
+static void __CDECL on_redraw_event(struct core_window *cw, EVMULT_OUT *ev_out,
+ short msg[8])
+{
+ GRECT work, clip;
+ struct gemtk_wm_scroll_info_s *slid;
+ struct atari_treeview_window *tv = (struct atari_treeview_window *)cw;
+
+ if (tv == NULL)
+ return;
+
+ gemtk_wm_get_grect(tv->window, GEMTK_WM_AREA_CONTENT, &work);
+ //dbg_grect("treeview work: ", &work);
+
+ atari_treeview_get_grect(cw, TREEVIEW_AREA_CONTENT, &work);
+ //dbg_grect("treeview work: ", &work);
+ slid = gemtk_wm_get_scroll_info(tv->window);
+
+ clip = work;
+
+ /* check if the redraw area intersects with the content area: */
+ if ( !rc_intersect( (GRECT*)&msg[4], &clip)) {
+ return;
+ }
+
+ if (atari_treeview_is_iconified(cw) == true) {
+ atari_treeview_redraw_icon(cw, &clip);
+ return;
+ }
+
+ /* make redraw coords relative to content viewport */
+ clip.g_x -= work.g_x;
+ clip.g_y -= work.g_y;
+
+ /* normalize the redraw coords: */
+ if( clip.g_x < 0 ) {
+ clip.g_w = work.g_w + clip.g_x;
+ clip.g_x = 0;
+ }
+ if( clip.g_y < 0 ) {
+ clip.g_h = work.g_h + clip.g_y;
+ clip.g_y = 0;
+ }
+
+ /* Merge redraw coords: */
+ if( clip.g_h > 0 && clip.g_w > 0 ) {
+
+ GRECT rdrw_area;
+
+ rdrw_area.g_x = clip.g_x;
+ rdrw_area.g_y = clip.g_y;
+ rdrw_area.g_w = clip.g_w;
+ rdrw_area.g_h = clip.g_h;
+
+ //dbg_grect("treeview on_redraw_event ", &rdrw_area);
+
+ atari_treeview_redraw_grect_request(cw, &rdrw_area);
+ }
+}
+
+static void __CDECL on_mbutton_event(struct core_window *cw, EVMULT_OUT *ev_out,
+ short msg[8])
+{
+ struct atari_treeview_window *tv = (struct atari_treeview_window *)cw;
+ struct gemtk_wm_scroll_info_s *slid;
+ GRECT work;
+ short mx, my;
+ short cur_rel_x, cur_rel_y, dummy, mbut;
+
+ assert(tv);
+
+ gemtk_wm_get_grect(tv->window, GEMTK_WM_AREA_CONTENT, &work);
+ slid = gemtk_wm_get_scroll_info(tv->window);
+ mx = ev_out->emo_mouse.p_x;
+ my = ev_out->emo_mouse.p_y;
+
+ /* mouse click relative origin: */
+
+ short origin_rel_x = (mx-work.g_x) +
+ (slid->x_pos*slid->x_unit_px);
+ short origin_rel_y = (my-work.g_y) +
+ (slid->y_pos*slid->y_unit_px);
+
+ /* Only pass on events in the content area: */
+ if( origin_rel_x >= 0 && origin_rel_y >= 0
+ && mx < work.g_x + work.g_w
+ && my < work.g_y + work.g_h )
+ {
+ if (ev_out->emo_mclicks == 2) {
+ tv->io->mouse_action(cw,
+ BROWSER_MOUSE_CLICK_1|BROWSER_MOUSE_DOUBLE_CLICK,
+ origin_rel_x, origin_rel_y);
+ return;
+ }
+
+ graf_mkstate(&cur_rel_x, &cur_rel_y, &mbut, &dummy);
+ /* check for click or hold: */
+ if( (mbut&1) == 0 ){
+ int bms;
+ bms = BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_PRESS_1;
+ if(ev_out->emo_mclicks == 2 ) {
+ bms = BROWSER_MOUSE_DOUBLE_CLICK;
+ }
+ tv->io->mouse_action(cw, bms, origin_rel_x, origin_rel_y);
+ } else {
+ /* button still pressed */
+ short prev_x = origin_rel_x;
+ short prev_y = origin_rel_y;
+
+ cur_rel_x = origin_rel_x;
+ cur_rel_y = origin_rel_y;
+
+ gem_set_cursor(&gem_cursors.hand);
+
+ tv->startdrag.x = origin_rel_x;
+ tv->startdrag.y = origin_rel_y;
+ /* First, report mouse press, to trigger entry selection */
+ tv->io->mouse_action(cw, BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_PRESS_1, cur_rel_x,
+ cur_rel_y);
+ atari_treeview_redraw(cw);
+ tv->io->mouse_action(cw, BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_ON,
+ cur_rel_x, cur_rel_y);
+ do{
+ if (abs(prev_x-cur_rel_x) > 5 || abs(prev_y-cur_rel_y) > 5) {
+ tv->io->mouse_action(cw,
+ BROWSER_MOUSE_HOLDING_1 | BROWSER_MOUSE_DRAG_ON,
+ cur_rel_x, cur_rel_y);
+ prev_x = cur_rel_x;
+ prev_y = cur_rel_y;
+ }
+
+ if (tv->redraw) {
+ // TODO: maybe GUI poll would fit better here?
+ // ... is gui_poll re-entrance save?
+ atari_treeview_redraw(cw);
+ }
+
+ /* sample mouse button state: */
+ graf_mkstate(&cur_rel_x, &cur_rel_y, &mbut, &dummy);
+ cur_rel_x = (cur_rel_x-work.g_x)+(slid->x_pos*slid->x_unit_px);
+ cur_rel_y = (cur_rel_y-work.g_y)+(slid->y_pos*slid->y_unit_px);
+ } while( mbut & 1 );
+
+ /* End drag: */
+ tv->io->mouse_action(cw, BROWSER_MOUSE_HOVER, cur_rel_x, cur_rel_y);
+ gem_set_cursor(&gem_cursors.arrow);
+ }
+ }
+}
+
+
+struct core_window *
+atari_treeview_create(GUIWIN *win, struct atari_treeview_callbacks * callbacks,
+ void * user_data, uint32_t flags)
+{
+
+ /* allocate the core_window struct: */
+ struct atari_treeview_window * tv;
+ struct gemtk_wm_scroll_info_s *slid;
+
+ tv = calloc(1, sizeof(struct atari_treeview_window));
+ if (tv == NULL) {
+ LOG("calloc failed");
+ atari_warn_user(messages_get_errorcode(NSERROR_NOMEM), 0);
+ return NULL;
+ }
+
+ /* Store the window ref inside the new treeview: */
+ tv->window = win;
+ tv->io = callbacks;
+ tv->user_data = user_data;
+
+ // Setup gemtk event handler function:
+ gemtk_wm_set_event_handler(win, handle_event);
+
+ // bind window user data to treeview ref:
+ gemtk_wm_set_user_data(win, (void*)tv);
+
+ // Get acces to the gemtk scroll info struct:
+ slid = gemtk_wm_get_scroll_info(tv->window);
+
+ // Setup line and column height/width of the window,
+ // each scroll takes the configured steps:
+ slid->y_unit_px = 16;
+ slid->x_unit_px = 16;
+
+ assert(tv->io);
+ assert(tv->io->init_phase2);
+
+ /* Now that the window is configured for treeview content, */
+ /* call init_phase2 which must create the treeview */
+ /* descriptor, and at least setup the the default */
+ /* event handlers of the treeview: */
+ /* It would be more simple to not pass around the callbacks */
+ /* but the treeview constructor requires them for initialization... */
+ nserror err = tv->io->init_phase2((struct core_window *)tv, &cw_t);
+ if (err != NSERROR_OK) {
+ free(tv);
+ tv = NULL;
+ }
+
+ return((struct core_window *)tv);
+}
+
+void atari_treeview_delete(struct core_window * cw)
+{
+ struct atari_treeview_window *tv = (struct atari_treeview_window*)cw;
+
+ assert(tv);
+ assert(tv->io->finish);
+
+ tv->io->finish(cw);
+
+ free(tv);
+}
+
+
+void atari_treeview_open(struct core_window *cw, GRECT *pos)
+{
+ struct atari_treeview_window *tv = (struct atari_treeview_window*)cw;
+ if (tv->window != NULL && tv->is_open == false) {
+ tv->is_open = true;
+ wind_open_grect(gemtk_wm_get_handle(tv->window), pos);
+ gemtk_wm_link(tv->window);
+ if (treeviews_open == NULL) {
+ treeviews_open = tv;
+ treeviews_open->next_open = NULL;
+ treeviews_open->prev_open = NULL;
+ } else {
+ struct atari_treeview_window * tmp;
+ tmp = treeviews_open;
+ while(tmp->next_open != NULL){
+ tmp = tmp->next_open;
+ }
+ tmp->next_open = tv;
+ tv->prev_open = tmp;
+ tv->next_open = NULL;
+ }
+ }
+}
+
+bool atari_treeview_is_open(struct core_window *cw)
+{
+ struct atari_treeview_window *tv = (struct atari_treeview_window*)cw;
+ return(tv->is_open);
+}
+
+void atari_treeview_set_user_data(struct core_window * cw,
+ void *user_data_ptr)
+{
+ struct atari_treeview_window *tv = (struct atari_treeview_window*)cw;
+ tv->user_data = user_data_ptr;
+}
+
+void * atari_treeview_get_user_data(struct core_window * cw)
+{
+ struct atari_treeview_window *tv = (struct atari_treeview_window*)cw;
+ return(tv->user_data);
+}
+
+void atari_treeview_close(struct core_window *cw)
+{
+ struct atari_treeview_window *tv = (struct atari_treeview_window*)cw;
+ if (tv->window != NULL) {
+ tv->is_open = false;
+ wind_close(gemtk_wm_get_handle(tv->window));
+ gemtk_wm_unlink(tv->window);
+ /* unlink the window: */
+ if (tv->prev_open != NULL) {
+ tv->prev_open->next_open = tv->next_open;
+ } else {
+ treeviews_open = tv->next_open;
+ }
+ if (tv->next_open != NULL) {
+ tv->next_open->prev_open = tv->prev_open;
+ }
+ }
+}
+
+
+/**
+ * Core Window Callbacks:
+ */
+
+/**
+ * Request a redraw of the window
+ *
+ * \param cw the core window object
+ * \param r rectangle to redraw
+ */
+void atari_treeview_redraw_request(struct core_window *cw, const struct rect *r)
+{
+ GRECT area;
+ struct gemtk_wm_scroll_info_s * slid;
+ struct atari_treeview_window * tv = (struct atari_treeview_window *)cw;
+
+ RECT_TO_GRECT(r, &area)
+
+ assert(tv);
+
+ slid = gemtk_wm_get_scroll_info(tv->window);
+
+ //dbg_rect("redraw rect request", r);
+
+ // treeview redraw is always full window width:
+ area.g_x = 0;
+ area.g_w = 8000;
+ // but vertical redraw region is clipped:
+ area.g_y = r->y0 - (slid->y_pos*slid->y_unit_px);
+ area.g_h = r->y1 - r->y0;
+ atari_treeview_redraw_grect_request(cw, &area);
+}
+
+/**
+ * Update the limits of the window
+ *
+ * \param cw the core window object
+ * \param width the width in px, or negative if don't care
+ * \param height the height in px, or negative if don't care
+ */
+void atari_treeview_update_size(struct core_window *cw, int width, int height)
+{
+ GRECT area;
+ struct gemtk_wm_scroll_info_s *slid;
+ struct atari_treeview_window *tv = (struct atari_treeview_window *)cw;
+
+ if (tv != NULL) {
+
+ if (tv->disposing)
+ return;
+
+ /* Get acces to the gemtk window slider settings: */
+ slid = gemtk_wm_get_scroll_info(tv->window);
+
+ /* recalculate and refresh sliders: */
+ atari_treeview_get_grect(cw, TREEVIEW_AREA_CONTENT, &area);
+ if (width > -1) {
+ slid->x_units = (width/slid->x_unit_px);
+ } else {
+ slid->x_units = 1;
+ }
+
+ if (height > -1) {
+ slid->y_units = (height/slid->y_unit_px);
+ } else {
+ slid->y_units = 1;
+ }
+
+ tv->extent.x = width;
+ tv->extent.y = height;
+
+
+ /*printf("units content: %d, units viewport: %d\n", (height/slid->y_unit_px),
+ (area.g_h/slid->y_unit_px));*/
+ gemtk_wm_update_slider(tv->window, GEMTK_WM_VH_SLIDER);
+ }
+}
+
+
+/**
+ * Scroll the window to make area visible
+ *
+ * \param cw the core window object
+ * \param r rectangle to make visible
+ */
+void atari_treeview_scroll_visible(struct core_window *cw, const struct rect *r)
+{
+ /* atari frontend doesn't support dragging outside the treeview */
+ /* so there is no need to implement this? */
+}
+
+
+/**
+ * Get window viewport dimensions
+ *
+ * \param cw the core window object
+ * \param width to be set to viewport width in px, if non NULL
+ * \param height to be set to viewport height in px, if non NULL
+ */
+void atari_treeview_get_window_dimensions(struct core_window *cw,
+ int *width, int *height)
+{
+ if (cw != NULL && (width != NULL || height != NULL)) {
+ GRECT work;
+ atari_treeview_get_grect(cw, TREEVIEW_AREA_CONTENT, &work);
+ *width = work.g_w;
+ *height = work.g_h;
+ }
+}
+
+
+/**
+ * Inform corewindow owner of drag status
+ *
+ * \param cw the core window object
+ * \param ds the current drag status
+ */
+void atari_treeview_drag_status(struct core_window *cw,
+ core_window_drag_status ds)
+{
+
+}
+
+void atari_treeview_flush_redraws(void)
+{
+ struct atari_treeview_window *tmp;
+
+ tmp = treeviews_open;
+
+ if(tmp){
+ while(tmp){
+ assert(tmp->is_open);
+ if(tmp->redraw){
+ if (atari_treeview_is_iconified((struct core_window *)tmp)) {
+ /* No content redraw for iconified windows */
+ /* because otherwise the icon draw function would */
+ /* have to deal with plot canvas coords */
+ continue;
+ }
+
+ atari_treeview_redraw((struct core_window *)tmp);
+ }
+ tmp = tmp->next_open;
+ }
+ }
+}
+
diff --git a/frontends/atari/treeview.h b/frontends/atari/treeview.h
new file mode 100644
index 000000000..a6b4a62e9
--- /dev/null
+++ b/frontends/atari/treeview.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2013 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NSATARI_TREEVIEW_H
+#define NSATARI_TREEVIEW_H
+
+#include "desktop/core_window.h"
+#include "atari/gui.h"
+#include "atari/gemtk/gemtk.h"
+
+/**
+ * Default AES Window widgets for a treeview window, can be passed to
+ * atari_treeview_create as the flags parameter to have an standardized treeview
+ * window.
+ */
+#define ATARI_TREEVIEW_WIDGETS (CLOSER | MOVER | SIZER| NAME | FULLER | \
+ SMALLER | VSLIDE | HSLIDE | UPARROW | DNARROW | \
+ LFARROW | RTARROW)
+
+enum treeview_area_e {
+ TREEVIEW_AREA_WORK = 0,
+ TREEVIEW_AREA_TOOLBAR,
+ TREEVIEW_AREA_CONTENT
+};
+
+struct core_window;
+struct atari_treeview_window;
+
+/**
+ * The atari treeview implementation wraps the core_window callbacks
+ * So that it can process parameters and then it passes the event further
+ * To the specific implementation window.
+ * These callbacks must be implemented by any atari treeview window.
+ */
+
+/** \todo atari add drag_status callback */
+typedef nserror (*atari_treeview_init2_callback)(struct core_window *cw, struct core_window_callback_table * default_callbacks);
+typedef void (*atari_treeview_finish_callback)(struct core_window *cw);
+typedef void (*atari_treeview_keypress_callback)(struct core_window *cw, uint32_t ucs4);
+typedef void (*atari_treeview_mouse_action_callback)(struct core_window *cw, browser_mouse_state mouse, int x, int y);
+typedef void (*atari_treeview_draw_callback)(struct core_window *cw, int x, int y, struct rect *clip, const struct redraw_context *ctx);
+
+struct atari_treeview_callbacks {
+ atari_treeview_init2_callback init_phase2;
+ atari_treeview_finish_callback finish;
+ atari_treeview_draw_callback draw;
+ atari_treeview_keypress_callback keypress;
+ atari_treeview_mouse_action_callback mouse_action;
+ gemtk_wm_event_handler_f gemtk_user_func;
+};
+
+/**
+ * Initalize an window to be an treeview window.
+ *
+*/
+struct core_window *atari_treeview_create(GUIWIN *win, struct atari_treeview_callbacks * callbacks, void * user_data, uint32_t flags);
+
+/**
+ * Free the Treeview, but not the gemtk window used for the treeview.
+ */
+void atari_treeview_delete(struct core_window *cw);
+
+/**
+ * Open the treeview window.
+ */
+void atari_treeview_open(struct core_window *cw, GRECT *pos);
+
+/**
+ * Returns the window "open" state.
+ */
+bool atari_treeview_is_open(struct core_window *cw);
+
+/**
+ * Closes (hides) the treeview window.
+ */
+void atari_treeview_close(struct core_window *cw);
+
+/**
+ * Get the window manager window handle
+ */
+
+GUIWIN * atari_treeview_get_gemtk_window(struct core_window *cw);
+
+/**
+ * Get an specific area inside the window.
+ */
+void atari_treeview_get_grect(struct core_window *cw, enum treeview_area_e mode, GRECT *dest);
+
+/**
+ * Process all pending redraw requests for a single treeview
+ */
+void atari_treeview_redraw(struct core_window *cw);
+
+/**
+ * Attach arbitary user data to the treeview.
+ */
+void atari_treeview_set_user_data(struct core_window *cw, void *user_data_ptr);
+
+/**
+ * Return the arbitary user data set by atari_treeview_set_user_data()
+ */
+void *atari_treeview_get_user_data(struct core_window *cw);
+
+/**
+ * Process all redraw request of all open Treeview windows
+ */
+void atari_treeview_flush_redraws(void);
+
+#endif //NSATARI_TREEVIEW_H
diff --git a/frontends/atari/verify_ssl.c b/frontends/atari/verify_ssl.c
new file mode 100644
index 000000000..cbd9d0bf6
--- /dev/null
+++ b/frontends/atari/verify_ssl.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <windom.h>
+
+#include "utils/errors.h"
+#include "utils/utils.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "content/urldb.h"
+#include "content/fetch.h"
+#include "atari/res/netsurf.rsh"
+#include "atari/verify_ssl.h"
+
+/*
+ todo: this file need to use the treeview api - complete rework,
+ current implementation is not used in any way.
+*/
+
+extern void * h_gem_rsrc;
+extern short atari_plot_vdi_handle;
+
+
+#define CERT_INF_LINES 8
+
+static struct ssl_info_draw_param
+{
+ struct ssl_cert_info * cert_infos_n;
+ unsigned long num_certs;
+ int current;
+ int scrollx;
+ int cols;
+ int scrolly;
+ int rows; /* assumed to be 8 */
+ OBJECT * tree;
+} dp;
+
+
+static int cert_display_width( struct ssl_cert_info * cert_info )
+{
+ int l1, l2;
+ int add = 16; /* strlen("Issuer: "); */
+
+ l1 = strlen(cert_info->issuer) + add;
+ l2 = strlen(cert_info->subject) + add;
+ return( MAX(l1, l2) );
+}
+
+
+static void __CDECL cert_info_draw( WINDOW * win, short buf[8], void * data)
+{
+ struct ssl_info_draw_param * dp = (struct ssl_info_draw_param *)data;
+ GRECT work;
+ short pxy[4];
+ int maxchars;
+ short d, cbh, cbw;
+ int i = 0;
+ short x,y,w,h;
+ int px_ypos;
+ char * line = malloc(512);
+ if( line == NULL )
+ return;
+
+ LOG("Cert info draw, win: %p, data: %p, scrollx: %d", win, data, dp->scrollx );
+
+ WindGet( win, WF_WORKXYWH, &x, &y, &w, &h );
+ /*using static values here, as RsrcUserDraw has mem leaks & a very small stack */
+ pxy[0] = work.g_x = x + 8;
+ pxy[1] = work.g_y = y + 80;
+ pxy[2] = x + 8 + 272;
+ pxy[3] = y + 80 + 176;
+ work.g_w = 272;
+ work.g_h = 176;
+
+ maxchars = (work.g_w / 8)+1;
+ vs_clip( atari_plot_vdi_handle, 1,(short*) &pxy );
+ vswr_mode( atari_plot_vdi_handle, MD_REPLACE );
+ vsf_interior( atari_plot_vdi_handle, 1 );
+ vsf_color( atari_plot_vdi_handle, LWHITE );
+ v_bar( atari_plot_vdi_handle, (short*)&pxy );
+ vst_height( atari_plot_vdi_handle, 16, &d, &d, &cbw, &cbh );
+ vst_alignment(atari_plot_vdi_handle, 0, 5, &d, &d );
+ vst_color( atari_plot_vdi_handle, BLACK );
+ vst_effects( atari_plot_vdi_handle, 0 );
+ px_ypos = 0;
+ for(i=0; i<CERT_INF_LINES; i++ ) {
+ switch( i ) {
+ case 0:
+ sprintf(line, "Cert Version: %d", dp->cert_infos_n[dp->current].version );
+ break;
+
+ case 1:
+ sprintf(line, "Invalid before: %s", &dp->cert_infos_n[dp->current].not_before );
+ break;
+
+ case 2:
+ sprintf(line, "Invalid after: %s", &dp->cert_infos_n[dp->current].not_after );
+ break;
+
+ case 3:
+ sprintf(line, "Signature type: %d", dp->cert_infos_n[dp->current].sig_type );
+ break;
+
+ case 4:
+ sprintf(line, "Serial: %d", dp->cert_infos_n[dp->current].serial );
+ break;
+
+ case 5:
+ sprintf(line, "Issuer: %s", &dp->cert_infos_n[dp->current].issuer );
+ break;
+
+ case 6:
+ sprintf(line, "Subject: %s", &dp->cert_infos_n[dp->current].subject );
+ break;
+
+ case 7:
+ sprintf(line, "Cert type: %d", dp->cert_infos_n[dp->current].cert_type );
+ break;
+
+ default:
+ break;
+ }
+ if( (int)strlen(line) > dp->scrollx ) {
+ if( dp->scrollx + maxchars < 511 && ( (signed int)strlen(line) - dp->scrollx) > maxchars )
+ line[dp->scrollx + maxchars] = 0;
+ v_gtext(atari_plot_vdi_handle, work.g_x + 1, work.g_y + px_ypos, &line[dp->scrollx]);
+ }
+ px_ypos += cbh;
+ }
+ vst_alignment(atari_plot_vdi_handle, 0, 0, &d, &d );
+ vs_clip( atari_plot_vdi_handle, 0, (short*)&pxy );
+ free( line );
+}
+
+
+static void do_popup( WINDOW *win, int index, int mode, void *data)
+{
+ struct ssl_info_draw_param * dp = (struct ssl_info_draw_param *)data;
+ char * items[dp->num_certs];
+ short x, y;
+ unsigned int i;
+ LOG("do_popup: num certs: %d", dp->num_certs);
+ for( i = 0; i<dp->num_certs; i++) {
+ items[i] = malloc( 48 );
+ strncpy(items[i], (char*)&dp->cert_infos_n[i].issuer, 46 );
+ }
+ objc_offset( FORM(win), index, &x, &y );
+ dp->current = MenuPopUp( items, x, y,
+ dp->num_certs, MIN( 3, dp->num_certs), 0,
+ P_LIST + P_WNDW + P_CHCK );
+ ObjcChange( OC_FORM, win, index, NORMAL, TRUE );
+ dp->cols = cert_display_width( &dp->cert_infos_n[dp->current] );
+ dp->rows = 8;
+ dp->scrollx = 0;
+ dp->scrolly = 0;
+
+ /* Send (!) redraw ( OC_MSG ) */
+ ObjcDrawParent( OC_FORM, FORM(win), VERIFY_BOX_DETAILS, 1, 7 | OC_MSG );
+ for( i = 0; i<dp->num_certs; i++) {
+ free( items[i] );
+ }
+}
+
+
+
+bool verify_ssl_form_do( const char * url, const struct ssl_cert_info * cert_infos_n ,
+ unsigned long num_certs )
+{
+ OBJECT *tree;
+ WINDOW * form;
+
+ bool bres = false;
+ bool cont = true;
+ int res = 0;
+ dp.cert_infos_n = (struct ssl_cert_info *)cert_infos_n;
+ dp.num_certs = num_certs;
+ dp.scrollx = 0;
+ dp.scrolly = 0;
+ dp.current = 0;
+ dp.cols = cert_display_width( &dp.cert_infos_n[dp.current] );
+ dp.rows = 8;
+ dp.tree = tree;
+
+ RsrcGaddr (h_gem_rsrc , R_TREE, VERIFY, &tree);
+ ObjcString( tree, VERIFY_LBL_HOST, (char*)url );
+ ObjcChange( OC_OBJC, tree, VERIFY_BT_ACCEPT, 0, 0 );
+ ObjcChange( OC_OBJC, tree, VERIFY_BT_REJECT, 0, 0 );
+ form = FormWindBegin( tree, (char*)"SSL Verify failed" );
+ EvntDataAdd( form, WM_REDRAW, cert_info_draw, (void*)&dp, EV_BOT );
+ /* this results in some extended objects which can not be freed: :( */
+ /* RsrcUserDraw( OC_FORM, tree, VERIFY_BOX_DETAILS, cert_info_draw,(void*)&dp ) ; */
+ ObjcAttachFormFunc( form, VERIFY_BT_NEXT_CERT, do_popup, &dp );
+ /*
+ ObjcAttachFormFunc( form, VERIFY_BT_NEXT_CERT, do_popup, &dp );
+ ObjcAttachFormFunc( form, VERIFY_BT_NEXT_CERT, do_popup, &dp );
+ */
+ while( cont ) {
+ res = FormWindDo( MU_MESAG );
+ cont = false;
+ switch( res ){
+ case VERIFY_BT_ACCEPT:
+ bres = true;
+ break;
+
+ case VERIFY_BT_NEXT_CERT:
+ /* select box clicked or dragged... */
+ cont = true;
+ break;
+
+ case VERIFY_BT_REJECT:
+ bres = false;
+ break;
+
+ case VERIFY_BT_SCROLL_D:
+ cont = true;
+ dp.scrolly += 1;
+ ObjcDrawParent( OC_FORM, form, VERIFY_BOX_DETAILS, 1, 7 | OC_MSG );
+ break;
+
+ case VERIFY_BT_SCROLL_U:
+ cont = true;
+ dp.scrolly -= 1;
+ ObjcDrawParent( OC_FORM, form, VERIFY_BOX_DETAILS, 1, 7 | OC_MSG );
+ break;
+
+ case VERIFY_BT_SCROLL_R:
+ LOG("scroll r!");
+ cont = true;
+ dp.scrollx += 1;
+ if( dp.scrollx > (dp.cols - (272 / 8 )) )
+ dp.scrollx -= 1;
+ ObjcDrawParent( OC_FORM, form, VERIFY_BOX_DETAILS, 1, 7 | OC_MSG);
+ break;
+
+ case VERIFY_BT_SCROLL_L:
+ cont = true;
+ dp.scrollx -= 1;
+ if( dp.scrollx < 0 )
+ dp.scrollx = 0;
+ ObjcDrawParent( OC_FORM, form, VERIFY_BOX_DETAILS, 1, 7 | OC_MSG );
+ break;
+
+ default:
+ break;
+ }
+ }
+ FormWindEnd( );
+ return( bres );
+}
diff --git a/frontends/atari/verify_ssl.h b/frontends/atari/verify_ssl.h
new file mode 100644
index 000000000..b69bc9cfb
--- /dev/null
+++ b/frontends/atari/verify_ssl.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2010 Ole Loots <ole@monochrom.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_VERIFY_SSL_H_INCLUDED
+#define NS_VERIFY_SSL_H_INCLUDED
+
+bool verify_ssl_form_do( const char * url, const struct ssl_cert_info * cert_infos_n , unsigned long num_certs );
+
+#endif
diff --git a/frontends/beos/Makefile b/frontends/beos/Makefile
new file mode 100644
index 000000000..8a79fc5ec
--- /dev/null
+++ b/frontends/beos/Makefile
@@ -0,0 +1,102 @@
+# ----------------------------------------------------------------------------
+# BeOS target setup
+# ----------------------------------------------------------------------------
+
+# Linker flags
+LDFLAGS += -L/boot/home/config/lib
+LDFLAGS += -L/boot/common/lib
+LDFLAGS += -lbe -ltranslation -ltracker -lcolumnlistview -lnetwork
+ifeq ($(CC_MAJOR),2)
+ LDFLAGS += -lstdc++.r4
+else
+ LDFLAGS += -lstdc++ -lsupc++
+endif
+
+COMMON_WARNFLAGS += -Wno-multichar
+
+# compiler flags
+CFLAGS += -std=c99 -Dnsbeos -D_BSD_SOURCE -D_POSIX_C_SOURCE -Drestrict="" -g
+CXXFLAGS += -Dnsbeos -D_BSD_SOURCE -D_POSIX_C_SOURCE -Drestrict="" -g
+
+BEOS_BERES := beres
+BEOS_RC := rc
+BEOS_XRES := xres
+BEOS_SETVER := setversion
+BEOS_MIMESET := mimeset
+
+VERSION_FULL := $(shell sed -n '/_version.*=.*"/{s/.*"\(.*\)".*/\1/;p;}' desktop/version.c)
+VERSION_MAJ := $(shell sed -n '/_major/{s/.* = \([0-9]*\).*/\1/;p;}' desktop/version.c)
+VERSION_MIN := $(shell sed -n '/_minor/{s/.* = \([0-9]*\).*/\1/;p;}' desktop/version.c)
+
+# ----------------------------------------------------------------------------
+# Source file setup
+# ----------------------------------------------------------------------------
+
+# sources purely for the BeOS build
+S_FRONTEND := about.cpp bitmap.cpp cookies.cpp download.cpp \
+ fetch_rsrc.cpp filetype.cpp font.cpp gui.cpp login.cpp \
+ gui_options.cpp plotters.cpp scaffolding.cpp search.cpp \
+ schedule.cpp throbber.cpp window.cpp
+
+# This is the final source build list
+# Note this is deliberately *not* expanded here as common and image
+# are not yet available
+SOURCES = $(S_COMMON) $(S_IMAGE) $(S_BROWSER) $(S_FRONTEND)
+EXETARGET := NetSurf
+
+# The filter and target for split messages
+MESSAGES_FILTER=beos
+MESSAGES_TARGET=$(FRONTEND_RESOURCES_DIR)
+
+# ----------------------------------------------------------------------------
+# Resources
+# ----------------------------------------------------------------------------
+
+RDEF_BEOS := res.rdef
+RDEF_BEOS := $(addprefix $(FRONTEND_SOURCE_DIR)/,$(RDEF_BEOS))
+
+RDEF_IMP_BEOS := res_import.rdef
+RDEF_IMP_BEOS := $(addprefix $(OBJROOT)/,$(subst /,_,$(RDEF_IMP_BEOS)))
+
+RDEP_BEOS := \
+ adblock.css beosdefault.css default.css internal.css quirks.css \
+ netsurf.png favicon.png ca-bundle.txt \
+ credits.html licence.html welcome.html maps.html SearchEngines
+
+RDEP_BEOS := $(addprefix $(FRONTEND_RESOURCES_DIR)/,$(RDEP_BEOS)) \
+ $(wildcard $(FRONTEND_RESOURCES_DIR)/icons/*.png) \
+ $(wildcard $(FRONTEND_RESOURCES_DIR)/??/*) \
+ $(wildcard $(FRONTEND_RESOURCES_DIR)/throbber/throbber*.png)
+
+RSRC_BEOS = $(addprefix $(OBJROOT)/,$(subst /,_,$(patsubst %.rdef,%.rsrc,$(RDEF_BEOS))))
+RESOURCES = $(RSRC_BEOS)
+
+$(RDEF_IMP_BEOS): $(RDEP_BEOS)
+ $(VQ)echo " GEN: $@"
+ $(Q)n=5000; for f in $^; do echo "resource($$n,\"$${f#beos/res/}\") #'data' import \"$${f#beos/}\";"; n=$$(($$n+1)); done > $@
+
+$(RSRC_BEOS): $(RDEF_BEOS) $(RDEF_IMP_BEOS)
+ $(VQ)echo " RC: $<"
+ $(Q)$(BEOS_RC) -I beos -o $@ $^
+
+
+# ----------------------------------------------------------------------------
+# Install target
+# ----------------------------------------------------------------------------
+
+install-beos:
+ mkdir -p $(DESTDIR)$(NETSURF_BEOS_BIN)
+ mkdir -p $(DESTDIR)$(NETSURF_BEOS_RESOURCES)
+ @copyattr -d $(EXETARGET) $(DESTDIR)$(NETSURF_BEOS_BIN)NetSurf
+ @cp -vRL $(FRONTEND_RESOURCES_DIR)/adblock.css $(DESTDIR)$(NETSURF_BEOS_RESOURCES)
+ @cp -vRL $(FRONTEND_RESOURCES_DIR)/ca-bundle.txt $(DESTDIR)$(NETSURF_BEOS_RESOURCES)
+ @cp -vRL $(FRONTEND_RESOURCES_DIR)/default.css $(DESTDIR)$(NETSURF_BEOS_RESOURCES)
+ @cp -vRL $(FRONTEND_RESOURCES_DIR)/beosdefault.css $(DESTDIR)$(NETSURF_BEOS_RESOURCES)
+ @cp -vRL $(FRONTEND_RESOURCES_DIR)/license $(DESTDIR)$(NETSURF_BEOS_RESOURCES)
+ @cp -vRL $(FRONTEND_RESOURCES_DIR)/SearchEngines $(DESTDIR)$(NETSURF_BEOS_RESOURCES)
+
+# ----------------------------------------------------------------------------
+# Package target
+# ----------------------------------------------------------------------------
+
+package-beos:
diff --git a/frontends/beos/Makefile.defaults b/frontends/beos/Makefile.defaults
new file mode 100644
index 000000000..7616a41a3
--- /dev/null
+++ b/frontends/beos/Makefile.defaults
@@ -0,0 +1,30 @@
+# ----------------------------------------------------------------------------
+# BeOS-specific options
+# ----------------------------------------------------------------------------
+
+# Where to install the netsurf binary
+NETSURF_BEOS_BIN := /boot/apps/netsurf/
+
+# TODO:HAIKU -- not sure if ~/.netsurf applies in beos
+# Where to search for NetSurf's resources after looking in ~/.netsurf and
+# $NETSURFRES. It must have a trailing /
+NETSURF_BEOS_RESOURCES := /boot/apps/netsurf/res/
+
+# Enable NetSurf's use of libsvgtiny for displaying SVGs
+# Valid options: YES, NO, AUTO
+NETSURF_USE_NSSVG := YES
+
+# Enable NetSurf's use of librosprite for displaying RISC OS Sprites
+# Valid options: YES, NO, AUTO
+NETSURF_USE_ROSPRITE := AUTO
+
+# Enable NetSurf's use of libharu for PDF export.
+# Valid options: YES, NO
+NETSURF_USE_HARU_PDF := NO
+
+# Force using glibc internal iconv implementation instead of external libiconv
+# Valid options: YES, NO
+NETSURF_USE_LIBICONV_PLUG := NO
+
+# Optimisation levels
+CFLAGS += -pipe -O2
diff --git a/frontends/beos/WindowStack.h b/frontends/beos/WindowStack.h
new file mode 100644
index 000000000..947b14360
--- /dev/null
+++ b/frontends/beos/WindowStack.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2010, Haiku, Inc. All rights reserved.
+ * Distributed under the terms of the MIT License.
+ */
+#ifndef WINDOW_STACK_H
+#define WINDOW_STACK_H
+
+
+#include <Window.h>
+
+
+class BWindowStack {
+public:
+ BWindowStack(BWindow* window);
+ ~BWindowStack();
+
+ status_t AddWindow(const BWindow* window);
+ status_t AddWindow(const BMessenger& window);
+ status_t AddWindowAt(const BWindow* window,
+ int32 position);
+ status_t AddWindowAt(const BMessenger& window,
+ int32 position);
+
+ status_t RemoveWindow(const BWindow* window);
+ status_t RemoveWindow(const BMessenger& window);
+ status_t RemoveWindowAt(int32 position,
+ BMessenger* window = NULL);
+
+ int32 CountWindows();
+
+ status_t WindowAt(int32 position,
+ BMessenger& messenger);
+ bool HasWindow(const BWindow* window);
+ bool HasWindow(const BMessenger& window);
+
+private:
+ status_t _AttachMessenger(const BMessenger& window);
+ status_t _ReadMessenger(BMessenger& window);
+ status_t _StartMessage(int32 what);
+
+ BPrivate::PortLink* fLink;
+};
+
+
+#endif
diff --git a/frontends/beos/about.cpp b/frontends/beos/about.cpp
new file mode 100644
index 000000000..89eb81829
--- /dev/null
+++ b/frontends/beos/about.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#define __STDBOOL_H__ 1
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+extern "C" {
+#include "desktop/version.h"
+#include "utils/log.h"
+#include "testament.h"
+#include "utils/useragent.h"
+#include "curl/curlver.h"
+#include "desktop/gui_clipboard.h"
+}
+#include "beos/about.h"
+#include "beos/scaffolding.h"
+#include "beos/window.h"
+
+#include <private/interface/AboutWindow.h>
+#include <Application.h>
+#include <Invoker.h>
+#include <String.h>
+
+
+/**
+ * Creates the about alert
+ */
+void nsbeos_about(struct gui_window *gui)
+{
+ BString text;
+ text << "Netsurf : " << user_agent_string() << "\n";
+ text << "Version : " << netsurf_version << "\n";
+ text << "Build ID : " << WT_REVID << "\n";
+ text << "Date : " << WT_COMPILEDATE << "\n";
+ text << "cURL : " << LIBCURL_VERSION << "\n";
+
+ BAboutWindow *alert = new BAboutWindow("About NetSurf", "application/x-vnd.NetSurf");
+ alert->AddExtraInfo(text);
+ alert->Show();
+ //TODO: i18n-ize
+}
diff --git a/frontends/beos/about.h b/frontends/beos/about.h
new file mode 100644
index 000000000..f80d33f01
--- /dev/null
+++ b/frontends/beos/about.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 __BEOS_ABOUT_H__
+#define __BEOS_ABOUT_H__
+
+void nsbeos_about(struct gui_window *gui);
+
+#endif /* __BEOS_ABOUT_H__ */
diff --git a/frontends/beos/beos_res.rsrc b/frontends/beos/beos_res.rsrc
new file mode 100644
index 000000000..d37e9e6ec
--- /dev/null
+++ b/frontends/beos/beos_res.rsrc
Binary files differ
diff --git a/frontends/beos/bitmap.cpp b/frontends/beos/bitmap.cpp
new file mode 100644
index 000000000..26b7a3957
--- /dev/null
+++ b/frontends/beos/bitmap.cpp
@@ -0,0 +1,556 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * BeOS implementation of generic bitmaps.
+ *
+ * This implements the interface given by image/bitmap.h using BBitmap.
+ */
+
+#define __STDBOOL_H__ 1
+#include <assert.h>
+#include <sys/param.h>
+#include <string.h>
+#include <Bitmap.h>
+#include <BitmapStream.h>
+#include <File.h>
+#include <GraphicsDefs.h>
+#include <TranslatorFormats.h>
+#include <TranslatorRoster.h>
+#include <View.h>
+#include <stdlib.h>
+
+extern "C" {
+#include "utils/log.h"
+#include "content/content.h"
+#include "content/urldb.h"
+#include "desktop/plotters.h"
+#include "desktop/browser.h"
+#include "image/bitmap.h"
+}
+
+#include "beos/bitmap.h"
+#include "beos/gui.h"
+#include "beos/scaffolding.h"
+#include "beos/plotters.h"
+
+
+struct bitmap {
+ BBitmap *primary;
+ BBitmap *shadow; // in NetSurf's ABGR order
+ BBitmap *pretile_x;
+ BBitmap *pretile_y;
+ BBitmap *pretile_xy;
+ bool opaque;
+};
+
+#define MIN_PRETILE_WIDTH 256
+#define MIN_PRETILE_HEIGHT 256
+
+#warning TODO: check rgba order
+#warning TODO: add correct locking (not strictly required)
+
+
+/**
+ * Convert to BeOS RGBA32_LITTLE (strictly BGRA) from NetSurf's favoured ABGR format.
+ *
+ * Copies the converted data elsewhere. Operation is rotate left 8 bits.
+ *
+ * \param src Source 32-bit pixels arranged in ABGR order.
+ * \param dst Output data in BGRA order.
+ * \param width Width of the bitmap
+ * \param height Height of the bitmap
+ * \param rowstride Number of bytes to skip after each row (this implementation
+ * requires this to be a multiple of 4.)
+ */
+static inline void nsbeos_rgba_to_bgra(void *src,
+ void *dst,
+ int width,
+ int height,
+ size_t rowstride)
+{
+ struct abgr { uint8 a, b, g, r; };
+ struct rgba { uint8 r, g, b ,a; };
+ struct bgra { uint8 b, g, r, a; };
+ struct rgba *from = (struct rgba *)src;
+ struct bgra *to = (struct bgra *)dst;
+
+ rowstride >>= 2;
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ to[x].b = from[x].b;
+ to[x].g = from[x].g;
+ to[x].r = from[x].r;
+ to[x].a = from[x].a;
+ /*
+ if (from[x].a == 0)
+ *(rgb_color *)&to[x] = B_TRANSPARENT_32_BIT;
+ */
+ }
+ from += rowstride;
+ to += rowstride;
+ }
+}
+
+
+/**
+ * Create a bitmap.
+ *
+ * \param width width of image in pixels
+ * \param height width of image in pixels
+ * \param state a flag word indicating the initial state
+ * \return an opaque struct bitmap, or NULL on memory exhaustion
+ */
+static void *bitmap_create(int width, int height, unsigned int state)
+{
+ struct bitmap *bmp = (struct bitmap *)malloc(sizeof(struct bitmap));
+ if (bmp == NULL)
+ return NULL;
+
+ int32 flags = 0;
+ if (state & BITMAP_CLEAR_MEMORY)
+ flags |= B_BITMAP_CLEAR_TO_WHITE;
+
+ BRect frame(0, 0, width - 1, height - 1);
+ //XXX: bytes per row ?
+ bmp->primary = new BBitmap(frame, flags, B_RGBA32);
+ bmp->shadow = new BBitmap(frame, flags, B_RGBA32);
+
+ bmp->pretile_x = bmp->pretile_y = bmp->pretile_xy = NULL;
+
+ bmp->opaque = (state & BITMAP_OPAQUE) != 0;
+
+ return bmp;
+}
+
+
+/**
+ * Sets whether a bitmap should be plotted opaque
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \param opaque whether the bitmap should be plotted opaque
+ */
+static void bitmap_set_opaque(void *vbitmap, bool opaque)
+{
+ struct bitmap *bitmap = (struct bitmap *)vbitmap;
+ assert(bitmap);
+ bitmap->opaque = opaque;
+}
+
+
+/**
+ * Tests whether a bitmap has an opaque alpha channel
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \return whether the bitmap is opaque
+ */
+static bool bitmap_test_opaque(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *)vbitmap;
+ assert(bitmap);
+ /* todo: test if bitmap is opaque */
+ return false;
+}
+
+
+/**
+ * Gets whether a bitmap should be plotted opaque
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ */
+static bool bitmap_get_opaque(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *)vbitmap;
+ assert(bitmap);
+ return bitmap->opaque;
+}
+
+
+/**
+ * Return a pointer to the pixel data in a bitmap.
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \return pointer to the pixel buffer
+ *
+ * The pixel data is packed as BITMAP_FORMAT, possibly with padding at the end
+ * of rows. The width of a row in bytes is given by bitmap_get_rowstride().
+ */
+
+static unsigned char *bitmap_get_buffer(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *)vbitmap;
+ assert(bitmap);
+ return (unsigned char *)(bitmap->shadow->Bits());
+}
+
+
+/**
+ * Find the width of a pixel row in bytes.
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \return width of a pixel row in the bitmap
+ */
+static size_t bitmap_get_rowstride(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *)vbitmap;
+ assert(bitmap);
+ return (bitmap->primary->BytesPerRow());
+}
+
+
+/**
+ * Find the bytes per pixels of a bitmap.
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \return bytes per pixels of the bitmap
+ */
+static size_t bitmap_get_bpp(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *)vbitmap;
+ assert(bitmap);
+ return 4;
+}
+
+
+/**
+ * Free pretiles of a bitmap.
+ *
+ * \param bitmap The bitmap to free the pretiles of.
+ */
+static void nsbeos_bitmap_free_pretiles(struct bitmap *bitmap)
+{
+#define FREE_TILE(XY) if (bitmap->pretile_##XY) delete (bitmap->pretile_##XY); bitmap->pretile_##XY = NULL
+ FREE_TILE(x);
+ FREE_TILE(y);
+ FREE_TILE(xy);
+#undef FREE_TILE
+}
+
+
+/**
+ * Free a bitmap.
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ */
+static void bitmap_destroy(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *)vbitmap;
+ assert(bitmap);
+ nsbeos_bitmap_free_pretiles(bitmap);
+ delete bitmap->primary;
+ delete bitmap->shadow;
+ free(bitmap);
+}
+
+
+/**
+ * Save a bitmap in the platform's native format.
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \param path pathname for file
+ * \param flags modify the behaviour of the save
+ * \return true on success, false on error and error reported
+ */
+static bool bitmap_save(void *vbitmap, const char *path, unsigned flags)
+{
+ struct bitmap *bitmap = (struct bitmap *)vbitmap;
+ BTranslatorRoster *roster = BTranslatorRoster::Default();
+ BBitmapStream stream(bitmap->primary);
+ BFile file(path, B_WRITE_ONLY | B_CREATE_FILE);
+ uint32 type = B_PNG_FORMAT;
+
+ if (file.InitCheck() < B_OK)
+ return false;
+
+ if (roster->Translate(&stream, NULL, NULL, &file, type) < B_OK)
+ return false;
+
+ return true;
+}
+
+
+/**
+ * The bitmap image has changed, so flush any persistant cache.
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ */
+void bitmap_modified(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *)vbitmap;
+ // convert the shadow (ABGR) to into the primary bitmap
+ nsbeos_rgba_to_bgra(bitmap->shadow->Bits(), bitmap->primary->Bits(),
+ bitmap->primary->Bounds().Width() + 1,
+ bitmap->primary->Bounds().Height() + 1,
+ bitmap->primary->BytesPerRow());
+ nsbeos_bitmap_free_pretiles(bitmap);
+}
+
+
+static int bitmap_get_width(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *)vbitmap;
+ return bitmap->primary->Bounds().Width() + 1;
+}
+
+
+static int bitmap_get_height(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *)vbitmap;
+ return bitmap->primary->Bounds().Height() + 1;
+}
+
+
+static BBitmap *
+nsbeos_bitmap_generate_pretile(BBitmap *primary, int repeat_x, int repeat_y)
+{
+ int width = primary->Bounds().Width() + 1;
+ int height = primary->Bounds().Height() + 1;
+ size_t primary_stride = primary->BytesPerRow();
+ BRect frame(0, 0, width * repeat_x - 1, height * repeat_y - 1);
+ BBitmap *result = new BBitmap(frame, 0, B_RGBA32);
+
+ char *target_buffer = (char *)result->Bits();
+ int x,y,row;
+ /* This algorithm won't work if the strides are not multiples */
+ assert((size_t)(result->BytesPerRow()) ==
+ (primary_stride * repeat_x));
+
+ if (repeat_x == 1 && repeat_y == 1) {
+ delete result;
+ // just return a copy
+ return new BBitmap(primary);
+ }
+
+ for (y = 0; y < repeat_y; ++y) {
+ char *primary_buffer = (char *)primary->Bits();
+ for (row = 0; row < height; ++row) {
+ for (x = 0; x < repeat_x; ++x) {
+ memcpy(target_buffer,
+ primary_buffer, primary_stride);
+ target_buffer += primary_stride;
+ }
+ primary_buffer += primary_stride;
+ }
+ }
+ return result;
+
+}
+
+
+/**
+ * The primary image associated with this bitmap object.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ */
+BBitmap *
+nsbeos_bitmap_get_primary(struct bitmap* bitmap)
+{
+ return bitmap->primary;
+}
+
+
+/**
+ * The X-pretiled image associated with this bitmap object.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ */
+BBitmap *
+nsbeos_bitmap_get_pretile_x(struct bitmap* bitmap)
+{
+ if (!bitmap->pretile_x) {
+ int width = bitmap->primary->Bounds().Width() + 1;
+ int xmult = (MIN_PRETILE_WIDTH + width - 1)/width;
+ LOG("Pretiling %p for X*%d", bitmap, xmult);
+ bitmap->pretile_x = nsbeos_bitmap_generate_pretile(bitmap->primary, xmult, 1);
+ }
+ return bitmap->pretile_x;
+
+}
+
+
+/**
+ * The Y-pretiled image associated with this bitmap object.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ */
+BBitmap *
+nsbeos_bitmap_get_pretile_y(struct bitmap* bitmap)
+{
+ if (!bitmap->pretile_y) {
+ int height = bitmap->primary->Bounds().Height() + 1;
+ int ymult = (MIN_PRETILE_HEIGHT + height - 1)/height;
+ LOG("Pretiling %p for Y*%d", bitmap, ymult);
+ bitmap->pretile_y = nsbeos_bitmap_generate_pretile(bitmap->primary, 1, ymult);
+ }
+ return bitmap->pretile_y;
+}
+
+
+/**
+ * The XY-pretiled image associated with this bitmap object.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ */
+BBitmap *
+nsbeos_bitmap_get_pretile_xy(struct bitmap* bitmap)
+{
+ if (!bitmap->pretile_xy) {
+ int width = bitmap->primary->Bounds().Width() + 1;
+ int height = bitmap->primary->Bounds().Height() + 1;
+ int xmult = (MIN_PRETILE_WIDTH + width - 1)/width;
+ int ymult = (MIN_PRETILE_HEIGHT + height - 1)/height;
+ LOG("Pretiling %p for X*%d Y*%d", bitmap, xmult, ymult);
+ bitmap->pretile_xy = nsbeos_bitmap_generate_pretile(bitmap->primary, xmult, ymult);
+ }
+ return bitmap->pretile_xy;
+}
+
+
+/**
+ * Create a thumbnail of a page.
+ *
+ * \param bitmap the bitmap to draw to
+ * \param content content structure to thumbnail
+ * \return true on success and bitmap updated else false
+ */
+static nserror bitmap_render(struct bitmap *bitmap, hlcache_handle *content)
+{
+ BBitmap *thumbnail;
+ BBitmap *small;
+ BBitmap *big;
+ BView *oldView;
+ BView *view;
+ BView *thumbView;
+ float width;
+ float height;
+ int big_width;
+ int big_height;
+ int depth;
+
+ struct redraw_context ctx;
+ ctx.interactive = false;
+ ctx.background_images = true;
+ ctx.plot = &nsbeos_plotters;
+
+ assert(content);
+ assert(bitmap);
+
+ thumbnail = nsbeos_bitmap_get_primary(bitmap);
+ width = thumbnail->Bounds().Width();
+ height = thumbnail->Bounds().Height();
+ depth = 32;
+
+ big_width = MIN(content_get_width(content), 1024);
+ big_height = (int)(((big_width * height) + (width / 2)) / width);
+
+ BRect contentRect(0, 0, big_width - 1, big_height - 1);
+ big = new BBitmap(contentRect, B_BITMAP_ACCEPTS_VIEWS, B_RGB32);
+
+ if (big->InitCheck() < B_OK) {
+ delete big;
+ return NSERROR_NOMEM;
+ }
+
+ small = new BBitmap(thumbnail->Bounds(),
+ B_BITMAP_ACCEPTS_VIEWS, B_RGB32);
+
+ if (small->InitCheck() < B_OK) {
+ delete small;
+ delete big;
+ return NSERROR_NOMEM;
+ }
+
+ //XXX: _lock ?
+ // backup the current gc
+ oldView = nsbeos_current_gc();
+
+ view = new BView(contentRect, "thumbnailer",
+ B_FOLLOW_NONE, B_WILL_DRAW);
+ big->AddChild(view);
+
+ thumbView = new BView(small->Bounds(), "thumbnail",
+ B_FOLLOW_NONE, B_WILL_DRAW);
+ small->AddChild(thumbView);
+
+ view->LockLooper();
+
+ /* impose our view on the content... */
+ nsbeos_current_gc_set(view);
+
+ /* render the content */
+ content_scaled_redraw(content, big_width, big_height, &ctx);
+
+ view->Sync();
+ view->UnlockLooper();
+
+ // restore the current gc
+ nsbeos_current_gc_set(oldView);
+
+
+ // now scale it down
+ //XXX: use Zeta's bilinear scaler ?
+ //#ifdef B_ZETA_VERSION
+ // err = ScaleBitmap(*shot, *scaledBmp);
+ //#else
+ thumbView->LockLooper();
+ thumbView->DrawBitmap(big, big->Bounds(), small->Bounds());
+ thumbView->Sync();
+ thumbView->UnlockLooper();
+
+ small->LockBits();
+ thumbnail->LockBits();
+
+ // copy it to the bitmap
+ memcpy(thumbnail->Bits(), small->Bits(), thumbnail->BitsLength());
+
+ thumbnail->UnlockBits();
+ small->UnlockBits();
+
+ bitmap_modified(bitmap);
+
+ // cleanup
+ small->RemoveChild(thumbView);
+ delete thumbView;
+ delete small;
+ big->RemoveChild(view);
+ delete view;
+ delete big;
+
+ return NSERROR_OK;
+}
+
+
+static struct gui_bitmap_table bitmap_table = {
+ /*.create =*/ bitmap_create,
+ /*.destroy =*/ bitmap_destroy,
+ /*.set_opaque =*/ bitmap_set_opaque,
+ /*.get_opaque =*/ bitmap_get_opaque,
+ /*.test_opaque =*/ bitmap_test_opaque,
+ /*.get_buffer =*/ bitmap_get_buffer,
+ /*.get_rowstride =*/ bitmap_get_rowstride,
+ /*.get_width =*/ bitmap_get_width,
+ /*.get_height =*/ bitmap_get_height,
+ /*.get_bpp =*/ bitmap_get_bpp,
+ /*.save =*/ bitmap_save,
+ /*.modified =*/ bitmap_modified,
+ /*.render =*/ bitmap_render,
+};
+
+struct gui_bitmap_table *beos_bitmap_table = &bitmap_table;
diff --git a/frontends/beos/bitmap.h b/frontends/beos/bitmap.h
new file mode 100644
index 000000000..bcf5f7b8d
--- /dev/null
+++ b/frontends/beos/bitmap.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_BEOS_BITMAP_H
+#define NS_BEOS_BITMAP_H
+
+#include <Bitmap.h>
+
+extern struct gui_bitmap_table *beos_bitmap_table;
+
+BBitmap *nsbeos_bitmap_get_primary(struct bitmap*);
+BBitmap *nsbeos_bitmap_get_pretile_x(struct bitmap*);
+BBitmap *nsbeos_bitmap_get_pretile_y(struct bitmap*);
+BBitmap *nsbeos_bitmap_get_pretile_xy(struct bitmap*);
+
+void bitmap_modified(void *vbitmap);
+
+#endif /* NS_BEOS_BITMAP_H */
diff --git a/frontends/beos/cookies.cpp b/frontends/beos/cookies.cpp
new file mode 100644
index 000000000..d1357fffa
--- /dev/null
+++ b/frontends/beos/cookies.cpp
@@ -0,0 +1,417 @@
+/*
+ * Copyright 2015 Adrián Arroyo Calle <adrian.arroyocalle@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#define __STDBOOL_H__ 1
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+extern "C" {
+#include "desktop/mouse.h"
+#include "utils/log.h"
+#include "desktop/cookie_manager.h"
+#include "desktop/plotters.h"
+#include "desktop/tree.h"
+#include "desktop/textinput.h"
+#include "content/urldb.h"
+}
+#include "beos/cookies.h"
+
+#include <Application.h>
+#include <InterfaceKit.h>
+#include <String.h>
+#include <Button.h>
+#include <Catalog.h>
+#include <private/interface/ColumnListView.h>
+#include <private/interface/ColumnTypes.h>
+#include <GroupLayoutBuilder.h>
+#include <NetworkCookieJar.h>
+#include <OutlineListView.h>
+#include <ScrollView.h>
+#include <StringView.h>
+
+#include <vector>
+
+static std::vector<struct cookie_data*> cookieJar;
+
+class CookieWindow : public BWindow {
+public:
+ CookieWindow(BRect frame);
+ virtual void MessageReceived(BMessage* message);
+ virtual void Show();
+ virtual bool QuitRequested();
+
+private:
+ void _BuildDomainList();
+ BStringItem* _AddDomain(BString domain, bool fake);
+ void _ShowCookiesForDomain(BString domain);
+ void _DeleteCookies();
+
+private:
+ BOutlineListView* fDomains;
+ BColumnListView* fCookies;
+ BStringView* fHeaderView;
+};
+
+enum {
+ COOKIE_IMPORT = 'cimp',
+ COOKIE_EXPORT = 'cexp',
+ COOKIE_DELETE = 'cdel',
+ COOKIE_REFRESH = 'rfsh',
+
+ DOMAIN_SELECTED = 'dmsl'
+};
+
+
+class CookieDateColumn: public BDateColumn
+{
+public:
+ CookieDateColumn(const char* title, float width)
+ :
+ BDateColumn(title, width, width / 2, width * 2)
+ {
+ }
+
+ void DrawField(BField* field, BRect rect, BView* parent) {
+ BDateField* dateField = (BDateField*)field;
+ if (dateField->UnixTime() == -1) {
+ DrawString("Session cookie", parent, rect);
+ } else {
+ BDateColumn::DrawField(field, rect, parent);
+ }
+ }
+};
+
+
+class CookieRow: public BRow
+{
+public:
+ CookieRow(BColumnListView* list, struct cookie_data& cookie)
+ :
+ BRow(),
+ fCookie(cookie)
+ {
+ list->AddRow(this);
+ SetField(new BStringField(cookie.name), 0);
+ SetField(new BStringField(cookie.path), 1);
+ time_t expiration = cookie.expires;
+ SetField(new BDateField(&expiration), 2);
+ SetField(new BStringField(cookie.value), 3);
+
+ BString flags;
+ if (cookie.secure)
+ flags = "https ";
+ if (cookie.http_only)
+ flags = "http ";
+
+ SetField(new BStringField(flags.String()), 4);
+ }
+
+public:
+ struct cookie_data fCookie;
+};
+
+
+class DomainItem: public BStringItem
+{
+public:
+ DomainItem(BString text, bool empty)
+ :
+ BStringItem(text),
+ fEmpty(empty)
+ {
+ }
+
+public:
+ bool fEmpty;
+};
+
+
+CookieWindow::CookieWindow(BRect frame)
+ :
+ BWindow(frame,"Cookie manager", B_TITLED_WINDOW,
+ B_NORMAL_WINDOW_FEEL,
+ B_AUTO_UPDATE_SIZE_LIMITS | B_ASYNCHRONOUS_CONTROLS)
+{
+ BGroupLayout* root = new BGroupLayout(B_HORIZONTAL, 0.0);
+ SetLayout(root);
+
+ fDomains = new BOutlineListView("domain list");
+ root->AddView(new BScrollView("scroll", fDomains, 0, false, true), 1);
+
+ fHeaderView = new BStringView("label","The cookie jar is empty!");
+ fCookies = new BColumnListView("cookie list", B_WILL_DRAW, B_FANCY_BORDER,
+ false);
+
+ float em = fCookies->StringWidth("M");
+ float flagsLength = fCookies->StringWidth("Mhttps hostOnly" B_UTF8_ELLIPSIS);
+
+ fCookies->AddColumn(new BStringColumn("Name",
+ 20 * em, 10 * em, 50 * em, 0), 0);
+ fCookies->AddColumn(new BStringColumn("Path",
+ 10 * em, 10 * em, 50 * em, 0), 1);
+ fCookies->AddColumn(new CookieDateColumn("Expiration",
+ fCookies->StringWidth("88/88/8888 88:88:88 AM")), 2);
+ fCookies->AddColumn(new BStringColumn("Value",
+ 20 * em, 10 * em, 50 * em, 0), 3);
+ fCookies->AddColumn(new BStringColumn("Flags",
+ flagsLength, flagsLength, flagsLength, 0), 4);
+
+ root->AddItem(BGroupLayoutBuilder(B_VERTICAL, B_USE_DEFAULT_SPACING)
+ .SetInsets(5, 5, 5, 5)
+ .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
+ .Add(fHeaderView)
+ .AddGlue()
+ .End()
+ .Add(fCookies)
+ .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
+ .SetInsets(5, 5, 5, 5)
+ .AddGlue()
+ .Add(new BButton("delete", "Delete",
+ new BMessage(COOKIE_DELETE))), 3);
+
+ fDomains->SetSelectionMessage(new BMessage(DOMAIN_SELECTED));
+}
+
+
+void
+CookieWindow::MessageReceived(BMessage* message)
+{
+ switch(message->what) {
+ case DOMAIN_SELECTED:
+ {
+ int32 index = message->FindInt32("index");
+ BStringItem* item = (BStringItem*)fDomains->ItemAt(index);
+ if (item != NULL) {
+ BString domain = item->Text();
+ _ShowCookiesForDomain(domain);
+ }
+ return;
+ }
+
+ case COOKIE_REFRESH:
+ _BuildDomainList();
+ return;
+
+ case COOKIE_DELETE:
+ _DeleteCookies();
+ return;
+ }
+ BWindow::MessageReceived(message);
+}
+
+
+void
+CookieWindow::Show()
+{
+ BWindow::Show();
+ if (IsHidden())
+ return;
+
+ PostMessage(COOKIE_REFRESH);
+}
+
+
+bool
+CookieWindow::QuitRequested()
+{
+ if (!IsHidden())
+ Hide();
+ cookieJar.clear();
+ return false;
+}
+
+
+void
+CookieWindow::_BuildDomainList()
+{
+ // Empty the domain list (TODO should we do this when hiding instead?)
+ for (int i = fDomains->FullListCountItems() - 1; i >= 1; i--) {
+ delete fDomains->FullListItemAt(i);
+ }
+ fDomains->MakeEmpty();
+
+ // BOutlineListView does not handle parent = NULL in many methods, so let's
+ // make sure everything always has a parent.
+ DomainItem* rootItem = new DomainItem("", true);
+ fDomains->AddItem(rootItem);
+
+ // Populate the domain list - TODO USE STL VECTOR
+
+
+ for(std::vector<struct cookie_data*>::iterator it = cookieJar.begin(); it != cookieJar.end(); ++it) {
+ _AddDomain((*it)->domain, false);
+ }
+
+ int i = 1;
+ while (i < fDomains->FullListCountItems())
+ {
+ DomainItem* item = (DomainItem*)fDomains->FullListItemAt(i);
+ // Detach items from the fake root
+ item->SetOutlineLevel(item->OutlineLevel() - 1);
+ i++;
+ }
+ fDomains->RemoveItem(rootItem);
+ delete rootItem;
+
+ i = 0;
+ int firstNotEmpty = i;
+ // Collapse empty items to keep the list short
+ while (i < fDomains->FullListCountItems())
+ {
+ DomainItem* item = (DomainItem*)fDomains->FullListItemAt(i);
+ if (item->fEmpty == true) {
+ if (fDomains->CountItemsUnder(item, true) == 1) {
+ // The item has no cookies, and only a single child. We can
+ // remove it and move its child one level up in the tree.
+
+ int count = fDomains->CountItemsUnder(item, false);
+ int index = fDomains->FullListIndexOf(item) + 1;
+ for (int j = 0; j < count; j++) {
+ BListItem* child = fDomains->FullListItemAt(index + j);
+ child->SetOutlineLevel(child->OutlineLevel() - 1);
+ }
+
+ fDomains->RemoveItem(item);
+ delete item;
+
+ // The moved child is at the same index the removed item was.
+ // We continue the loop without incrementing i to process it.
+ continue;
+ } else {
+ // The item has no cookies, but has multiple children. Mark it
+ // as disabled so it is not selectable.
+ item->SetEnabled(false);
+ if (i == firstNotEmpty)
+ firstNotEmpty++;
+ }
+ }
+
+ i++;
+ }
+
+ fDomains->Select(firstNotEmpty);
+}
+
+
+BStringItem*
+CookieWindow::_AddDomain(BString domain, bool fake)
+{
+ BStringItem* parent = NULL;
+ int firstDot = domain.FindFirst('.');
+ if (firstDot >= 0) {
+ BString parentDomain(domain);
+ parentDomain.Remove(0, firstDot + 1);
+ parent = _AddDomain(parentDomain, true);
+ } else {
+ parent = (BStringItem*)fDomains->FullListItemAt(0);
+ }
+
+ BListItem* existing;
+ int i = 0;
+ // check that we aren't already there
+ while ((existing = fDomains->ItemUnderAt(parent, true, i++)) != NULL) {
+ DomainItem* stringItem = (DomainItem*)existing;
+ if (stringItem->Text() == domain) {
+ if (fake == false)
+ stringItem->fEmpty = false;
+ return stringItem;
+ }
+ }
+
+ // Insert the new item, keeping the list alphabetically sorted
+ BStringItem* domainItem = new DomainItem(domain, fake);
+ domainItem->SetOutlineLevel(parent->OutlineLevel() + 1);
+ BStringItem* sibling = NULL;
+ int siblingCount = fDomains->CountItemsUnder(parent, true);
+ for (i = 0; i < siblingCount; i++) {
+ sibling = (BStringItem*)fDomains->ItemUnderAt(parent, true, i);
+ if (strcmp(sibling->Text(), domainItem->Text()) > 0) {
+ fDomains->AddItem(domainItem, fDomains->FullListIndexOf(sibling));
+ return domainItem;
+ }
+ }
+
+ if (sibling) {
+ // There were siblings, but all smaller than what we try to insert.
+ // Insert after the last one (and its subitems)
+ fDomains->AddItem(domainItem, fDomains->FullListIndexOf(sibling)
+ + fDomains->CountItemsUnder(sibling, false) + 1);
+ } else {
+ // There were no siblings, insert right after the parent
+ fDomains->AddItem(domainItem, fDomains->FullListIndexOf(parent) + 1);
+ }
+
+ return domainItem;
+}
+
+
+void
+CookieWindow::_ShowCookiesForDomain(BString domain)
+{
+ BString label;
+ label.SetToFormat("Cookies for %s", domain.String());
+ fHeaderView->SetText(label);
+
+ // Empty the cookie list
+ fCookies->Clear();
+
+ // Populate the domain list
+
+ for(std::vector<struct cookie_data*>::iterator it = cookieJar.begin(); it != cookieJar.end(); ++it) {
+ if((*it)->domain == domain) {
+ new CookieRow(fCookies,**it);
+ }
+ }
+}
+
+static bool nsbeos_cookie_parser(const struct cookie_data* data)
+{
+ cookieJar.push_back((struct cookie_data*)data);
+ return true;
+}
+
+void
+CookieWindow::_DeleteCookies()
+{
+ // TODO shall we handle multiple selection here?
+ CookieRow* row = (CookieRow*)fCookies->CurrentSelection();
+ if (row == NULL) {
+ // TODO see if a domain is selected in the domain list, and delete all
+ // cookies for that domain
+ return;
+ }
+
+ fCookies->RemoveRow(row);
+
+ urldb_delete_cookie(row->fCookie.domain, row->fCookie.path, row->fCookie.name);
+ cookieJar.clear();
+ urldb_iterate_cookies(&nsbeos_cookie_parser);
+
+ delete row;
+}
+
+/**
+ * Creates the Cookie Manager
+ */
+void nsbeos_cookies_init(void)
+{
+ CookieWindow* cookWin=new CookieWindow(BRect(100,100,700,500));
+ cookWin->Show();
+ cookWin->Activate();
+ urldb_iterate_cookies(&nsbeos_cookie_parser);
+}
diff --git a/frontends/beos/cookies.h b/frontends/beos/cookies.h
new file mode 100644
index 000000000..977ccd232
--- /dev/null
+++ b/frontends/beos/cookies.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2015 Adrián Arroyo Calle <adrian.arroyocalle@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 __BEOS_COOKIES_H__
+#define __BEOS_COOKIES_H__
+
+void nsbeos_cookies_init();
+
+#endif /* __BEOS_ABOUT_H__ */
diff --git a/frontends/beos/download.cpp b/frontends/beos/download.cpp
new file mode 100644
index 000000000..ea0271e60
--- /dev/null
+++ b/frontends/beos/download.cpp
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2012 Adrien Destugues <pulkomandy@pulkomandy.tk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#define __STDBOOL_H__ 1
+#include <stdbool.h>
+#include <sys/types.h>
+#include <stdlib.h>
+
+extern "C" {
+#include "desktop/download.h"
+#include "desktop/gui_download.h"
+#include "utils/utils.h"
+#include "utils/string.h"
+}
+#include "beos/download.h"
+
+#include <File.h>
+#include <FilePanel.h>
+#include <Locker.h>
+#include <Messenger.h>
+#include <StatusBar.h>
+#include <Window.h>
+
+class NSDownloadWindow: public BWindow
+{
+ public:
+ NSDownloadWindow(download_context* ctx);
+ ~NSDownloadWindow();
+
+ void MessageReceived(BMessage* message);
+
+ void Progress(int size);
+ void Failure(const char* error);
+ void Success();
+ private:
+ download_context* ctx;
+ BStatusBar* bar;
+ unsigned long progress;
+ bool success;
+};
+
+
+struct gui_download_window {
+ download_context* ctx;
+ NSDownloadWindow* window;
+
+ BLocker* storageLock;
+ BDataIO* storage;
+};
+
+
+NSDownloadWindow::NSDownloadWindow(download_context* ctx)
+ : BWindow(BRect(30, 30, 400, 200), "Downloads", B_TITLED_WINDOW,
+ B_NOT_RESIZABLE)
+ , ctx(ctx)
+ , progress(0)
+ , success(false)
+{
+ unsigned long dlsize = download_context_get_total_length(ctx);
+ char* buffer = human_friendly_bytesize(dlsize);
+
+ // Create the status bar
+ BRect rect = Bounds();
+ rect.InsetBy(3, 3);
+ bar = new BStatusBar(rect, "progress",
+ download_context_get_filename(ctx), buffer);
+ bar->SetMaxValue(dlsize);
+
+ // Create the backgroundview (just so that the area around the progress bar
+ // is B_PANEL_BACKGROUND_COLOR instead of white)
+ BView* back = new BView(Bounds(), "back", B_FOLLOW_ALL_SIDES, B_WILL_DRAW);
+ back->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
+
+ // Add the views to the window
+ back->AddChild(bar);
+ AddChild(back);
+
+ // Resize the window to leave a margin around the progress bar
+ BRect size = bar->Bounds();
+ ResizeTo(size.Width() + 6, size.Height() + 6);
+ Show();
+}
+
+
+NSDownloadWindow::~NSDownloadWindow()
+{
+ download_context_abort(ctx);
+ download_context_destroy(ctx);
+}
+
+
+void
+NSDownloadWindow::MessageReceived(BMessage* message)
+{
+ switch(message->what)
+ {
+ case B_SAVE_REQUESTED:
+ {
+ entry_ref directory;
+ const char* name;
+ struct gui_download_window* dw;
+ BFilePanel* source;
+
+ message->FindRef("directory", &directory);
+ message->FindString("name", &name);
+ message->FindPointer("dw", (void**)&dw);
+
+ BDirectory dir(&directory);
+ BFile* storage = new BFile(&dir, name,
+ B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
+ dw->storageLock->Lock();
+
+ BMallocIO* tempstore = dynamic_cast<BMallocIO*>(dw->storage);
+
+ storage->Write(tempstore->Buffer(), tempstore->BufferLength());
+ delete dw->storage;
+
+ if (success)
+ delete storage; // File is already finished downloading !
+ else
+ dw->storage = storage;
+ dw->storageLock->Unlock();
+
+ message->FindPointer("source", (void**)&source);
+ delete source;
+
+ break;
+ }
+ default:
+ BWindow::MessageReceived(message);
+ }
+}
+
+
+void
+NSDownloadWindow::Progress(int size)
+{
+ progress += size;
+
+ char* buffer = human_friendly_bytesize(progress);
+ strcat(buffer, "/");
+
+ bar->LockLooper();
+ bar->Update(size, NULL, buffer);
+ bar->Invalidate();
+ bar->UnlockLooper();
+}
+
+
+void
+NSDownloadWindow::Success()
+{
+ bar->LockLooper();
+ bar->SetBarColor(ui_color(B_SUCCESS_COLOR));
+ bar->UnlockLooper();
+
+ success = true;
+}
+
+
+void
+NSDownloadWindow::Failure(const char* error)
+{
+ bar->LockLooper();
+ bar->Update(0, NULL, error);
+ bar->SetBarColor(ui_color(B_FAILURE_COLOR));
+ bar->UnlockLooper();
+}
+
+
+static struct gui_download_window *gui_download_window_create(download_context *ctx,
+ struct gui_window *parent)
+{
+ struct gui_download_window *download = (struct gui_download_window*)malloc(sizeof *download);
+ if (download == NULL)
+ return NULL;
+
+ download->storageLock = new BLocker("storage_lock");
+ download->storage = new BMallocIO();
+ download->ctx = ctx;
+
+ download->window = new NSDownloadWindow(ctx);
+
+ // Also ask the user where to save the file
+ BMessage* msg = new BMessage(B_SAVE_REQUESTED);
+
+ BFilePanel* panel = new BFilePanel(B_SAVE_PANEL,
+ new BMessenger(download->window), NULL, 0, false);
+
+ panel->SetSaveText(download_context_get_filename(ctx));
+
+ msg->AddPointer("source", panel);
+ msg->AddPointer("dw", download);
+ panel->SetMessage(msg);
+
+ panel->Show();
+
+ return download;
+}
+
+
+static nserror gui_download_window_data(struct gui_download_window *dw,
+ const char *data, unsigned int size)
+{
+ dw->window->Progress(size);
+
+ dw->storageLock->Lock();
+ dw->storage->Write(data, size);
+ dw->storageLock->Unlock();
+
+ return NSERROR_OK;
+}
+
+
+static void gui_download_window_error(struct gui_download_window *dw,
+ const char *error_msg)
+{
+ dw->window->Failure(error_msg);
+
+ delete dw->storageLock;
+ delete dw->storage;
+}
+
+
+static void gui_download_window_done(struct gui_download_window *dw)
+{
+ dw->window->Success();
+
+ dw->storageLock->Lock();
+
+ // Only delete if the storage is already a file. Else, we must wait for the
+ // user to select something in the BFilePanel!
+ BFile* file = dynamic_cast<BFile*>(dw->storage);
+ delete file;
+ if (file)
+ delete dw->storageLock;
+ else
+ dw->storageLock->Unlock();
+}
+
+static struct gui_download_table download_table = {
+ gui_download_window_create,
+ gui_download_window_data,
+ gui_download_window_error,
+ gui_download_window_done,
+};
+
+struct gui_download_table *beos_download_table = &download_table;
+
diff --git a/frontends/beos/download.h b/frontends/beos/download.h
new file mode 100644
index 000000000..0ce387efc
--- /dev/null
+++ b/frontends/beos/download.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2012 Adrien Destugues <pulkomandy@pulkomandy.tk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+extern struct gui_download_table *beos_download_table;
diff --git a/frontends/beos/fetch_rsrc.cpp b/frontends/beos/fetch_rsrc.cpp
new file mode 100644
index 000000000..b771f7b2d
--- /dev/null
+++ b/frontends/beos/fetch_rsrc.cpp
@@ -0,0 +1,396 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ * Copyright 2008 Rob Kendrick <rjek@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/* rsrc: URL handling. */
+
+#define __STDBOOL_H__ 1
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <curl/curl.h> /* for URL unescaping functions */
+extern "C" {
+#include "utils/config.h"
+#include "content/fetch.h"
+#include "content/fetchers.h"
+#include "content/urldb.h"
+#include "utils/nsoption.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "utils/ring.h"
+#include "utils/base64.h"
+}
+#include "beos/fetch_rsrc.h"
+#include "beos/filetype.h"
+#include "beos/gui.h"
+
+#include <image.h>
+#include <Resources.h>
+#include <String.h>
+
+struct fetch_rsrc_context {
+ struct fetch *parent_fetch;
+ char *name;
+ char *url;
+ char *mimetype;
+ char *data;
+ size_t datalen;
+
+ bool aborted;
+ bool locked;
+
+ struct fetch_rsrc_context *r_next, *r_prev;
+};
+
+static struct fetch_rsrc_context *ring = NULL;
+
+BResources *gAppResources = NULL;
+
+static bool fetch_rsrc_initialise(lwc_string *scheme)
+{
+ LOG("fetch_rsrc_initialise called for %s", lwc_string_data(scheme));
+ return true;
+}
+
+static void fetch_rsrc_finalise(lwc_string *scheme)
+{
+ LOG("fetch_rsrc_finalise called for %s", lwc_string_data(scheme));
+}
+
+static bool fetch_rsrc_can_fetch(const nsurl *url)
+{
+ return true;
+}
+
+static void *fetch_rsrc_setup(struct fetch *parent_fetch, nsurl *url,
+ bool only_2xx, bool downgrade_tls, const char *post_urlenc,
+ const struct fetch_multipart_data *post_multipart,
+ const char **headers)
+{
+ struct fetch_rsrc_context *ctx;
+ ctx = (struct fetch_rsrc_context *)calloc(1, sizeof(*ctx));
+
+ if (ctx == NULL)
+ return NULL;
+
+ ctx->parent_fetch = parent_fetch;
+ /* TODO: keep as nsurl to avoid copy */
+ ctx->url = (char *)malloc(nsurl_length(url) + 1);
+
+ if (ctx->url == NULL) {
+ free(ctx);
+ return NULL;
+ }
+ memcpy(ctx->url, nsurl_access(url), nsurl_length(url) + 1);
+
+ RING_INSERT(ring, ctx);
+
+ return ctx;
+}
+
+static bool fetch_rsrc_start(void *ctx)
+{
+ return true;
+}
+
+static void fetch_rsrc_free(void *ctx)
+{
+ struct fetch_rsrc_context *c = (struct fetch_rsrc_context *)ctx;
+
+ free(c->name);
+ free(c->url);
+ free(c->data);
+ free(c->mimetype);
+ RING_REMOVE(ring, c);
+ free(ctx);
+}
+
+static void fetch_rsrc_abort(void *ctx)
+{
+ struct fetch_rsrc_context *c = (struct fetch_rsrc_context *)ctx;
+
+ /* To avoid the poll loop having to deal with the fetch context
+ * disappearing from under it, we simply flag the abort here.
+ * The poll loop itself will perform the appropriate cleanup.
+ */
+ c->aborted = true;
+}
+
+static void fetch_rsrc_send_callback(const fetch_msg *msg,
+ struct fetch_rsrc_context *c)
+{
+ c->locked = true;
+ fetch_send_callback(msg, c->parent_fetch);
+ c->locked = false;
+}
+
+static bool fetch_rsrc_process(struct fetch_rsrc_context *c)
+{
+ fetch_msg msg;
+ char *params;
+ char *at = NULL;
+ char *slash;
+ char *comma = NULL;
+ char *unescaped;
+ uint32 type = 'data'; // default for embeded files
+ int32 id = 0;
+
+ /* format of a rsrc: URL is:
+ * rsrc://[TYPE][@NUM]/name[,mime]
+ */
+
+ LOG("*** Processing %s", c->url);
+
+ if (strlen(c->url) < 7) {
+ /* 7 is the minimum possible length (rsrc://) */
+ msg.type = FETCH_ERROR;
+ msg.data.error = "Malformed rsrc: URL";
+ fetch_rsrc_send_callback(&msg, c);
+ return false;
+ }
+
+ /* skip the rsrc: part */
+ params = c->url + sizeof("rsrc://") - 1;
+
+ /* find the slash */
+ if ( (slash = strchr(params, '/')) == NULL) {
+ msg.type = FETCH_ERROR;
+ msg.data.error = "Malformed rsrc: URL";
+ fetch_rsrc_send_callback(&msg, c);
+ return false;
+ }
+
+ // doesn't exist in the filesystem but we should hit the internal types.
+ c->mimetype = strdup(fetch_filetype(slash));
+ c->name = strdup(slash + 1);
+
+ if (c->mimetype == NULL) {
+ msg.type = FETCH_ERROR;
+ msg.data.error =
+ "Unable to allocate memory for mimetype in rsrc: URL";
+ fetch_rsrc_send_callback(&msg, c);
+ return false;
+ }
+
+ if (params[0] != '/') {
+ uint8 c1, c2, c3, c4;
+ if (sscanf(params, "%c%c%c%c", &c1, &c2, &c3, &c4) > 3) {
+ type = c1 << 24 | c2 << 16 | c3 << 8 | c4;
+ LOG("fetch_rsrc: type:%4.4s\n", (char *)&type);
+ }
+ }
+
+ LOG("fetch_rsrc: 0x%08lx, %ld, '%s'\n", type, id, c->name);
+
+ bool found;
+ if (id)
+ found = gAppResources->HasResource(type, id);
+ else
+ found = gAppResources->HasResource(type, c->name);
+ if (!found) {
+ BString error("Cannot locate resource: ");
+ if (id)
+ error << id;
+ else
+ error << c->name;
+ msg.type = FETCH_ERROR;
+ msg.data.error = error.String();
+ fetch_rsrc_send_callback(&msg, c);
+ return false;
+ }
+
+ size_t len;
+ const void *data;
+ if (id)
+ data = gAppResources->LoadResource(type, id, &len);
+ else
+ data = gAppResources->LoadResource(type, c->name, &len);
+
+ if (!data) {
+ msg.type = FETCH_ERROR;
+ msg.data.error = "Cannot load rsrc: URL";
+ fetch_rsrc_send_callback(&msg, c);
+ return false;
+ }
+
+ c->datalen = len;
+ c->data = (char *)malloc(c->datalen);
+ if (c->data == NULL) {
+ msg.type = FETCH_ERROR;
+ msg.data.error = "Unable to allocate memory for rsrc: URL";
+ fetch_rsrc_send_callback(&msg, c);
+ return false;
+ }
+ memcpy(c->data, data, c->datalen);
+
+ return true;
+}
+
+static void fetch_rsrc_poll(lwc_string *scheme)
+{
+ fetch_msg msg;
+ struct fetch_rsrc_context *c, *next;
+
+ if (ring == NULL) return;
+
+ /* Iterate over ring, processing each pending fetch */
+ c = ring;
+ do {
+ /* Take a copy of the next pointer as we may destroy
+ * the ring item we're currently processing */
+ next = c->r_next;
+
+ /* Ignore fetches that have been flagged as locked.
+ * This allows safe re-entrant calls to this function.
+ * Re-entrancy can occur if, as a result of a callback,
+ * the interested party causes fetch_poll() to be called
+ * again.
+ */
+ if (c->locked == true) {
+ continue;
+ }
+
+ /* Only process non-aborted fetches */
+ if (!c->aborted && fetch_rsrc_process(c) == true) {
+ char header[64];
+
+ fetch_set_http_code(c->parent_fetch, 200);
+ LOG("setting rsrc: MIME type to %s, length to %zd", c->mimetype, c->datalen);
+ /* Any callback can result in the fetch being aborted.
+ * Therefore, we _must_ check for this after _every_
+ * call to fetch_rsrc_send_callback().
+ */
+ snprintf(header, sizeof header, "Content-Type: %s",
+ c->mimetype);
+ msg.type = FETCH_HEADER;
+ msg.data.header_or_data.buf = (const uint8_t *) header;
+ msg.data.header_or_data.len = strlen(header);
+ fetch_rsrc_send_callback(&msg, c);
+
+ snprintf(header, sizeof header, "Content-Length: %zd",
+ c->datalen);
+ msg.type = FETCH_HEADER;
+ msg.data.header_or_data.buf = (const uint8_t *) header;
+ msg.data.header_or_data.len = strlen(header);
+ fetch_rsrc_send_callback(&msg, c);
+
+ if (!c->aborted) {
+ msg.type = FETCH_DATA;
+ msg.data.header_or_data.buf = (const uint8_t *) c->data;
+ msg.data.header_or_data.len = c->datalen;
+ fetch_rsrc_send_callback(&msg, c);
+ }
+ if (!c->aborted) {
+ msg.type = FETCH_FINISHED;
+ fetch_rsrc_send_callback(&msg, c);
+ }
+ } else {
+ LOG("Processing of %s failed!", c->url);
+
+ /* Ensure that we're unlocked here. If we aren't,
+ * then fetch_rsrc_process() is broken.
+ */
+ assert(c->locked == false);
+ }
+
+ fetch_remove_from_queues(c->parent_fetch);
+ fetch_free(c->parent_fetch);
+
+ /* Advance to next ring entry, exiting if we've reached
+ * the start of the ring or the ring has become empty
+ */
+ } while ( (c = next) != ring && ring != NULL);
+}
+
+/* BAppFileInfo is supposed to find the app's resources for us,
+ * but this won't work if we ever want to be used as a replicant.
+ * This trick should work regardless,
+ */
+static int find_app_resources()
+{
+ char path[B_PATH_NAME_LENGTH];
+ if (nsbeos_find_app_path(path) < B_OK)
+ return B_ERROR;
+ //fprintf(stderr, "loading resources from '%s'\n", path);
+
+ BFile file(path, B_READ_ONLY);
+ if (file.InitCheck() < 0)
+ return file.InitCheck();
+ gAppResources = new BResources;
+ status_t err;
+ err = gAppResources->SetTo(&file);
+ if (err >= B_OK)
+ return B_OK;
+ delete gAppResources;
+ gAppResources = NULL;
+ return err;
+}
+
+BResources *get_app_resources()
+{
+ return gAppResources;
+}
+
+void fetch_rsrc_register(void)
+{
+ lwc_string *scheme;
+ int err;
+ nserror ret;
+
+ const struct fetcher_operation_table fetcher_ops_rsrc = {
+ fetch_rsrc_initialise,
+ fetch_rsrc_can_fetch,
+ fetch_rsrc_setup,
+ fetch_rsrc_start,
+ fetch_rsrc_abort,
+ fetch_rsrc_free,
+ fetch_rsrc_poll,
+ fetch_rsrc_finalise
+ };
+
+ err = find_app_resources();
+
+ if (err < B_OK) {
+ beos_warn_user("Resources", strerror(err));
+ return;
+ }
+
+ if (lwc_intern_string("rsrc", SLEN("rsrc"), &scheme) != lwc_error_ok) {
+ die("Failed to initialise the fetch module "
+ "(couldn't intern \"rsrc\").");
+ }
+
+ ret = fetcher_add(scheme, &fetcher_ops_rsrc);
+ if (ret != NSERROR_OK) {
+ die("unable to add rsrc fetcher.");
+ }
+}
+
+void fetch_rsrc_unregister(void)
+{
+ delete gAppResources;
+ gAppResources = NULL;
+}
diff --git a/frontends/beos/fetch_rsrc.h b/frontends/beos/fetch_rsrc.h
new file mode 100644
index 000000000..ce17670a4
--- /dev/null
+++ b/frontends/beos/fetch_rsrc.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ * Copyright 2008 Rob Kendrick <rjek@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * rsrc: URL method handler
+ */
+
+#ifndef NETSURF_BEOS_FETCH_DATA_H
+#define NETSURF_BEOS_FETCH_DATA_H
+
+void fetch_rsrc_register(void);
+void fetch_rsrc_unregister(void);
+
+class BResources;
+BResources *get_app_resources();
+
+#include "beos/res.h"
+
+#endif
diff --git a/frontends/beos/filetype.cpp b/frontends/beos/filetype.cpp
new file mode 100644
index 000000000..75a33240a
--- /dev/null
+++ b/frontends/beos/filetype.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#define __STDBOOL_H__ 1
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <Mime.h>
+#include <NodeInfo.h>
+#include <String.h>
+
+extern "C" {
+#include "content/fetch.h"
+#include "utils/log.h"
+#include "utils/hashtable.h"
+#include "utils/utils.h"
+}
+
+#include "beos/filetype.h"
+#include "beos/gui.h"
+
+static struct {
+ const char *type;
+ const char *ext1;
+ const char *ext2;
+} default_types[] = {
+ { "text/plain", "txt", NULL },
+ { "text/html", "htm", "html" },
+ { "text/css", "css", NULL },
+ { "image/gif", "gif", NULL },
+ { "image/jpeg", "jpg", "jpeg" },
+ { "image/png", "png", NULL },
+ { "image/jng", "jng", NULL },
+ { NULL, NULL, NULL }
+};
+
+void beos_fetch_filetype_init(void)
+{
+ BMimeType m;
+ status_t err;
+ int i;
+
+ // make sure we have basic mime types in the database
+ for (i = 0; default_types[i].type; i++) {
+ if (m.SetTo(default_types[i].type) < B_OK)
+ continue;
+ if (m.IsInstalled())
+ continue;
+ err = m.Install();
+ if (err < B_OK) {
+ beos_warn_user("Mime", strerror(err));
+ continue;
+ }
+ // the mime db doesn't know about it yet
+ BMessage extensions('exts');
+ if (default_types[i].ext1)
+ extensions.AddString("extensions", default_types[i].ext1);
+ if (default_types[i].ext2)
+ extensions.AddString("extensions", default_types[i].ext2);
+ err = m.SetFileExtensions(&extensions);
+ if (err < B_OK) {
+ beos_warn_user("Mime", strerror(err));
+ }
+ }
+}
+
+void beos_fetch_filetype_fin(void)
+{
+}
+
+const char *fetch_filetype(const char *unix_path)
+{
+ struct stat statbuf;
+ status_t err;
+ int i;
+ // NOT THREADSAFE
+ static char type[B_MIME_TYPE_LENGTH];
+
+ // override reading the mime type for known types
+ // avoids getting CSS files as text/x-source-code
+ // even though it's the mime sniffer rules that should be fixed.
+ BString ext(unix_path);
+ ext.Remove(0, ext.FindLast('.') + 1);
+ for (i = 0; default_types[i].type; i++) {
+ if (ext == default_types[i].ext1)
+ return default_types[i].type;
+ if (ext == default_types[i].ext2)
+ return default_types[i].type;
+ }
+
+ BEntry entry(unix_path, true);
+ BNode node(&entry);
+ err = node.InitCheck();
+ if (err < B_OK)
+ return "text/plain";
+
+ if (node.IsDirectory())
+ return "application/x-netsurf-directory";
+
+ BNodeInfo info(&node);
+ err = info.InitCheck();
+ if (err < B_OK)
+ return "test/plain";
+
+ err = info.GetType(type);
+ if (err < B_OK) {
+ // not there yet, sniff and retry
+ err = update_mime_info(unix_path, false, true, false);
+ if (err < B_OK)
+ return "text/plain";
+ err = info.GetType(type);
+ if (err < B_OK)
+ return "text/plain";
+ }
+
+ return type;
+}
diff --git a/frontends/beos/filetype.h b/frontends/beos/filetype.h
new file mode 100644
index 000000000..72252dbd0
--- /dev/null
+++ b/frontends/beos/filetype.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+void beos_fetch_filetype_init(void);
+void beos_fetch_filetype_fin(void);
+const char *fetch_filetype(const char *unix_path);
diff --git a/frontends/beos/font.cpp b/frontends/beos/font.cpp
new file mode 100644
index 000000000..003af52b5
--- /dev/null
+++ b/frontends/beos/font.cpp
@@ -0,0 +1,374 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Font handling (BeOS implementation).
+ * TODO: check for correctness, the code is taken from the GTK one.
+ * maybe use the current view instead of constructing a new BFont each time ?
+ */
+
+
+#define __STDBOOL_H__ 1
+#include <stdbool.h>
+#include <assert.h>
+#include <stdio.h>
+#include <Font.h>
+#include <String.h>
+#include <View.h>
+
+extern "C" {
+#include "utils/utils.h"
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "utils/nsurl.h"
+#include "desktop/gui_layout.h"
+}
+
+#include "beos/gui.h"
+#include "beos/font.h"
+#include "beos/plotters.h"
+
+
+/**
+ * Convert a font style to a PangoFontDescription.
+ *
+ * \param font Beos font object.
+ * \param fstyle style for this text
+ */
+void nsbeos_style_to_font(BFont &font, const struct plot_font_style *fstyle)
+{
+ float size;
+ uint16 face = 0;
+ const char *family;
+
+ switch (fstyle->family) {
+ case PLOT_FONT_FAMILY_SERIF:
+ family = nsoption_charp(font_serif);
+ break;
+ case PLOT_FONT_FAMILY_MONOSPACE:
+ family = nsoption_charp(font_mono);
+ break;
+ case PLOT_FONT_FAMILY_CURSIVE:
+ family = nsoption_charp(font_cursive);
+ break;
+ case PLOT_FONT_FAMILY_FANTASY:
+ family = nsoption_charp(font_fantasy);
+ break;
+ case PLOT_FONT_FAMILY_SANS_SERIF:
+ default:
+ family = nsoption_charp(font_sans);
+ break;
+ }
+
+ if ((fstyle->flags & FONTF_ITALIC)) {
+ face = B_ITALIC_FACE;
+ } else if ((fstyle->flags & FONTF_OBLIQUE)) {
+ face = B_ITALIC_FACE;
+ // XXX: no OBLIQUE flag ??
+ // maybe find "Oblique" style
+ // or use SetShear() ?
+ }
+
+#ifndef __HAIKU__XXX
+ if (fstyle->weight >= 600) {
+ face |= B_BOLD_FACE;
+ }
+#else
+ if (fstyle->weight >= 600) {
+ if (fstyle->weight >= 800)
+ face |= B_HEAVY_FACE;
+ else
+ face |= B_BOLD_FACE;
+ } else if (fstyle->weight <= 300) {
+ face |= B_LIGHT_FACE;
+ }
+#endif
+/*
+ case CSS_FONT_WEIGHT_100: weight = 100; break;
+ case CSS_FONT_WEIGHT_200: weight = 200; break;
+ case CSS_FONT_WEIGHT_300: weight = 300; break;
+ case CSS_FONT_WEIGHT_400: weight = 400; break;
+ case CSS_FONT_WEIGHT_500: weight = 500; break;
+ case CSS_FONT_WEIGHT_600: weight = 600; break;
+ case CSS_FONT_WEIGHT_700: weight = 700; break;
+ case CSS_FONT_WEIGHT_800: weight = 800; break;
+ case CSS_FONT_WEIGHT_900: weight = 900; break;
+*/
+
+ if (!face)
+ face = B_REGULAR_FACE;
+
+//fprintf(stderr, "nsbeos_style_to_font: %d, %d, %d -> '%s' %04x\n", style->font_family, style->font_style, style->font_weight, family, face);
+
+ if (family) {
+ font_family beos_family;
+
+ strncpy(beos_family, family, B_FONT_FAMILY_LENGTH);
+ // Ensure it's terminated
+ beos_family[B_FONT_FAMILY_LENGTH] = '\0';
+
+ font.SetFamilyAndFace(beos_family, face);
+ } else {
+ //XXX not used
+ font = be_plain_font;
+ font.SetFace(face);
+ }
+
+//fprintf(stderr, "nsbeos_style_to_font: value %f unit %d\n", style->font_size.value.length.value, style->font_size.value.length.unit);
+ size = fstyle->size / FONT_SIZE_SCALE;
+
+//fprintf(stderr, "nsbeos_style_to_font: %f %d\n", size, style->font_size.value.length.unit);
+
+ font.SetSize(size);
+}
+
+
+/**
+ * Measure the width of a string.
+ *
+ * \param fstyle style for this text
+ * \param string UTF-8 string to measure
+ * \param length length of string
+ * \param width updated to width of string[0..length)
+ * \return true on success, false on error and error reported
+ */
+static nserror beos_font_width(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int *width)
+{
+ //fprintf(stderr, "%s(, '%s', %d, )\n", __FUNCTION__, string, length);
+ BFont font;
+
+ if (length == 0) {
+ *width = 0;
+ return NSERROR_OK;
+ }
+
+ nsbeos_style_to_font(font, fstyle);
+ *width = (int)font.StringWidth(string, length);
+
+ return NSERROR_OK;
+}
+
+
+static int utf8_char_len(const char *c)
+{
+ uint8 *p = (uint8 *)c;
+ uint8 m = 0xE0;
+ uint8 v = 0xC0;
+ int i;
+
+ if (!*p)
+ return 0;
+ if ((*p & 0x80) == 0)
+ return 1;
+ if ((*p & 0xC0) == 0x80)
+ return 1; // actually one of the remaining bytes...
+ for (i = 2; i < 5; i++) {
+ if ((*p & m) == v)
+ return i;
+ v = (v >> 1) | 0x80;
+ m = (m >> 1) | 0x80;
+ }
+ return i;
+}
+
+
+/**
+ * Find the position in a string where an x coordinate falls.
+ *
+ * \param fstyle style for this text
+ * \param string UTF-8 string to measure
+ * \param length length of string
+ * \param x x coordinate to search for
+ * \param char_offset updated to offset in string of actual_x, [0..length]
+ * \param actual_x updated to x coordinate of character closest to x
+ * \return true on success, false on error and error reported
+ */
+static nserror beos_font_position(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x)
+{
+ //LOG("(, '%s', %d, %d, , )", string, length, x);
+ //fprintf(stderr, "%s(, '%s', %d, %d, , )\n", __FUNCTION__, string, length, x);
+ int index;
+ BFont font;
+
+ nsbeos_style_to_font(font, fstyle);
+ BString str(string);
+ int32 len = str.CountChars();
+ float escapements[len];
+ float esc = 0.0;
+ float current = 0.0;
+ int i;
+
+ index = 0;
+ font.GetEscapements(string, len, escapements);
+ // slow but it should work
+ for (i = 0; string[index] && i < len; i++) {
+ esc += escapements[i];
+ current = font.Size() * esc;
+ index += utf8_char_len(&string[index]);
+ // is current char already too far away?
+ if (x < current)
+ break;
+ }
+ *actual_x = (int)current;
+ *char_offset = i; //index;
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Find where to split a string to make it fit a width.
+ *
+ * \param fstyle style for this text
+ * \param string UTF-8 string to measure
+ * \param length length of string, in bytes
+ * \param x width available
+ * \param char_offset updated to offset in string of actual_x, [1..length]
+ * \param actual_x updated to x coordinate of character closest to x
+ * \return true on success, false on error and error reported
+ *
+ * On exit, char_offset indicates first character after split point.
+ *
+ * Note: char_offset of 0 should never be returned.
+ *
+ * Returns:
+ * char_offset giving split point closest to x, where actual_x <= x
+ * else
+ * char_offset giving split point closest to x, where actual_x > x
+ *
+ * Returning char_offset == length means no split possible
+ */
+static nserror beos_font_split(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x)
+{
+ //fprintf(stderr, "%s(, '%s', %d, %d, , )\n", __FUNCTION__, string, length, x);
+ //LOG("(, '%s', %d, %d, , )", string, length, x);
+ int index = 0;
+ BFont font;
+
+ nsbeos_style_to_font(font, fstyle);
+ BString str(string);
+ int32 len = str.CountChars();
+ float escapements[len];
+ float esc = 0.0;
+ float current = 0.0;
+ float last_x = 0.0;
+ int i;
+ int last_space = 0;
+
+ font.GetEscapements(string, len, escapements);
+ // very slow but it should work
+ for (i = 0; string[index] && i < len; i++) {
+ if (string[index] == ' ') {
+ last_x = current;
+ last_space = index;
+ }
+ if (x < current && last_space != 0) {
+ *actual_x = (int)last_x;
+ *char_offset = last_space;
+ return NSERROR_OK;;
+ }
+ esc += escapements[i];
+ current = font.Size() * esc;
+ index += utf8_char_len(&string[index]);
+ }
+ *actual_x = MIN(*actual_x, (int)current);
+ *char_offset = index;
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Render a string.
+ *
+ * \param fstyle style for this text
+ * \param string UTF-8 string to measure
+ * \param length length of string
+ * \param x x coordinate
+ * \param y y coordinate
+ * \return true on success, false on error and error reported
+ */
+
+bool nsfont_paint(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int x, int y)
+{
+ //fprintf(stderr, "%s(, '%s', %d, %d, %d, )\n", __FUNCTION__, string, length, x, y);
+ //CALLED();
+ BFont font;
+ rgb_color oldbg;
+ rgb_color background;
+ rgb_color foreground;
+ BView *view;
+ float size;
+
+ if (length == 0)
+ return true;
+
+ nsbeos_style_to_font(font, fstyle);
+ background = nsbeos_rgb_colour(fstyle->background);
+ foreground = nsbeos_rgb_colour(fstyle->foreground);
+
+ view = nsbeos_current_gc/*_lock*/();
+ if (view == NULL) {
+ beos_warn_user("No GC", 0);
+ return false;
+ }
+
+ oldbg = view->LowColor();
+ drawing_mode oldmode = view->DrawingMode();
+ view->SetLowColor(B_TRANSPARENT_32_BIT);
+
+ //view->SetScale() XXX
+
+//printf("nsfont_paint: Size: %f\n", font.Size());
+ size = (float)font.Size();
+#warning XXX use scale
+
+ view->SetFont(&font);
+ view->SetHighColor(foreground);
+ view->SetDrawingMode(B_OP_OVER);
+
+ BString line(string, length);
+
+ BPoint where(x, y + 1);
+ view->DrawString(line.String(), where);
+
+ view->SetDrawingMode(oldmode);
+ if (memcmp(&oldbg, &background, sizeof(rgb_color)))
+ view->SetLowColor(oldbg);
+
+ //nsbeos_current_gc_unlock();
+
+ return true;
+}
+
+
+static struct gui_layout_table layout_table = {
+ /*.width = */beos_font_width,
+ /*.position = */beos_font_position,
+ /*.split = */beos_font_split
+};
+
+struct gui_layout_table *beos_layout_table = &layout_table;
diff --git a/frontends/beos/font.h b/frontends/beos/font.h
new file mode 100644
index 000000000..63909efcf
--- /dev/null
+++ b/frontends/beos/font.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * Beos font layout handling interface.
+ */
+
+#ifndef NS_BEOS_FONT_H
+#define NS_BEOS_FONT_H
+
+#include "desktop/plot_style.h"
+
+bool nsfont_paint(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int x, int y);
+
+void nsbeos_style_to_font(BFont &font, const struct plot_font_style *fstyle);
+
+extern struct gui_layout_table *beos_layout_table;
+
+#endif
diff --git a/frontends/beos/gui.cpp b/frontends/beos/gui.cpp
new file mode 100644
index 000000000..2e0aa4cc5
--- /dev/null
+++ b/frontends/beos/gui.cpp
@@ -0,0 +1,1109 @@
+/*
+ * Copyright 2015 Adrián Arroyo Calle <adrian.arroyocalle@gmail.com>
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#define __STDBOOL_H__ 1
+#include <assert.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <curl/curl.h>
+
+#include <Alert.h>
+#include <Application.h>
+#include <BeBuild.h>
+#include <FindDirectory.h>
+#include <Mime.h>
+#include <Path.h>
+#include <PathFinder.h>
+#include <Resources.h>
+#include <Roster.h>
+#include <Screen.h>
+#include <String.h>
+
+extern "C" {
+
+#include "utils/nsoption.h"
+#include "utils/filename.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/url.h"
+#include "utils/corestrings.h"
+#include "utils/utf8.h"
+#include "utils/utils.h"
+
+#include "content/content.h"
+#include "content/content_protected.h"
+#include "content/fetch.h"
+#include "content/fetchers.h"
+#include "content/fetchers/resource.h"
+#include "content/hlcache.h"
+#include "content/urldb.h"
+#include "desktop/browser.h"
+#include "desktop/gui_misc.h"
+#include "desktop/gui_clipboard.h"
+#include "desktop/gui_search.h"
+#include "desktop/gui_fetch.h"
+#include "desktop/netsurf.h"
+
+}
+
+#include "beos/gui.h"
+#include "beos/gui_options.h"
+//#include "beos/completion.h"
+#include "beos/window.h"
+#include "beos/throbber.h"
+#include "beos/filetype.h"
+#include "beos/download.h"
+#include "beos/schedule.h"
+#include "beos/fetch_rsrc.h"
+#include "beos/scaffolding.h"
+#include "beos/bitmap.h"
+#include "beos/font.h"
+
+//TODO: use resources
+// enable using resources instead of files
+#define USE_RESOURCES 1
+
+bool nsbeos_done = false;
+
+bool replicated = false; /**< if we are running as a replicant */
+
+char *options_file_location;
+char *glade_file_location;
+
+struct gui_window *search_current_window = 0;
+
+BWindow *wndAbout;
+BWindow *wndWarning;
+//GladeXML *gladeWindows;
+BWindow *wndTooltip;
+//beosLabel *labelTooltip;
+BFilePanel *wndOpenFile;
+
+static thread_id sBAppThreadID;
+
+static BMessage *gFirstRefsReceived = NULL;
+
+static int sEventPipe[2];
+
+// #pragma mark - class NSBrowserFrameView
+
+
+/* exported function defined in beos/gui.h */
+nserror beos_warn_user(const char *warning, const char *detail)
+{
+ LOG("warn_user: %s (%s)", warning, detail);
+ BAlert *alert;
+ BString text(warning);
+ if (detail)
+ text << ":\n" << detail;
+
+ alert = new BAlert("NetSurf Warning", text.String(), "Debug", "Ok",
+ NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
+ if (alert->Go() < 1) {
+ debugger("warn_user");
+ }
+
+ return NSERROR_OK;
+}
+
+NSBrowserApplication::NSBrowserApplication()
+ : BApplication("application/x-vnd.NetSurf")
+{
+}
+
+
+NSBrowserApplication::~NSBrowserApplication()
+{
+}
+
+
+void
+NSBrowserApplication::MessageReceived(BMessage *message)
+{
+ switch (message->what) {
+ case B_REFS_RECEIVED:
+ case B_UI_SETTINGS_CHANGED:
+ // messages for top-level
+ // we'll just send them to the first window
+ case 'back':
+ case 'forw':
+ case 'stop':
+ case 'relo':
+ case 'home':
+ case 'urlc':
+ case 'urle':
+ case 'sear':
+ case 'menu':
+ // NetPositive messages
+ case B_NETPOSITIVE_OPEN_URL:
+ case B_NETPOSITIVE_BACK:
+ case B_NETPOSITIVE_FORWARD:
+ case B_NETPOSITIVE_HOME:
+ case B_NETPOSITIVE_RELOAD:
+ case B_NETPOSITIVE_STOP:
+ case B_NETPOSITIVE_DOWN:
+ case B_NETPOSITIVE_UP:
+ //DetachCurrentMessage();
+ //nsbeos_pipe_message(message, this, fGuiWindow);
+ break;
+ default:
+ BApplication::MessageReceived(message);
+ }
+}
+
+
+void
+NSBrowserApplication::ArgvReceived(int32 argc, char **argv)
+{
+ NSBrowserWindow *win = nsbeos_find_last_window();
+ if (!win) {
+ return;
+ }
+ win->Unlock();
+ BMessage *message = DetachCurrentMessage();
+ nsbeos_pipe_message_top(message, win, win->Scaffolding());
+}
+
+
+void
+NSBrowserApplication::RefsReceived(BMessage *message)
+{
+ DetachCurrentMessage();
+ NSBrowserWindow *win = nsbeos_find_last_window();
+ if (!win) {
+ gFirstRefsReceived = message;
+ return;
+ }
+ win->Unlock();
+ nsbeos_pipe_message_top(message, win, win->Scaffolding());
+}
+
+
+void
+NSBrowserApplication::AboutRequested()
+{
+ nsbeos_pipe_message(new BMessage(B_ABOUT_REQUESTED), NULL, NULL);
+}
+
+
+bool
+NSBrowserApplication::QuitRequested()
+{
+ // let it notice it
+ nsbeos_pipe_message(new BMessage(B_QUIT_REQUESTED), NULL, NULL);
+ // we'll let the main thread Quit() ourselves when it's done.
+ return false;
+}
+
+
+// #pragma mark - implementation
+
+
+
+/* realpath fallback on R5 */
+#if !defined(__HAIKU__) && !defined(B_BEOS_VERSION_DANO)
+extern "C" char *realpath(const char *f, char *buf);
+char *realpath(const char *f, char *buf)
+{
+ BPath path(f, NULL, true);
+ if (path.InitCheck() < 0) {
+ strncpy(buf, f, MAXPATHLEN);
+ return NULL;
+ }
+ //printf("RP: '%s'\n", path.Path());
+ strncpy(buf, path.Path(), MAXPATHLEN);
+ return buf;
+}
+#endif
+
+/* finds the NetSurf binary image ID and path
+ *
+ */
+image_id nsbeos_find_app_path(char *path)
+{
+ image_info info;
+ int32 cookie = 0;
+ while (get_next_image_info(0, &cookie, &info) == B_OK) {
+//fprintf(stderr, "%p <> %p, %p\n", (char *)&find_app_resources, (char *)info.text, (char *)info.text + info.text_size);
+ if (((char *)&nsbeos_find_app_path >= (char *)info.text)
+ && ((char *)&nsbeos_find_app_path < (char *)info.text + info.text_size)) {
+//fprintf(stderr, "match\n");
+ if (path) {
+ memset(path, 0, B_PATH_NAME_LENGTH);
+ strncpy(path, info.name, B_PATH_NAME_LENGTH-1);
+ }
+ return info.id;
+ }
+ }
+ return B_ERROR;
+}
+
+/**
+ * Locate a shared resource file by searching known places in order.
+ *
+ * Search order is: ~/config/settings/NetSurf/, ~/.netsurf/, $NETSURFRES/
+ * (where NETSURFRES is an environment variable), and finally the path
+ * specified by the macro at the top of this file.
+ *
+ * \param buf buffer to write to. must be at least PATH_MAX chars
+ * \param filename file to look for
+ * \param def default to return if file not found
+ * \return path to resource.
+ */
+char *find_resource(char *buf, const char *filename, const char *def)
+{
+ const char *cdir = NULL;
+ status_t err;
+ BPath path;
+ char t[PATH_MAX];
+
+ err = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
+ path.Append("NetSurf");
+ if (err >= B_OK)
+ cdir = path.Path();
+ if (cdir != NULL) {
+ strcpy(t, cdir);
+ strcat(t, "/");
+ strcat(t, filename);
+ realpath(t, buf);
+ if (access(buf, R_OK) == 0)
+ return buf;
+ }
+
+ cdir = getenv("HOME");
+ if (cdir != NULL) {
+ strcpy(t, cdir);
+ strcat(t, "/.netsurf/");
+ strcat(t, filename);
+ realpath(t, buf);
+ if (access(buf, R_OK) == 0)
+ return buf;
+ }
+
+ cdir = getenv("NETSURFRES");
+
+ if (cdir != NULL) {
+ realpath(cdir, buf);
+ strcat(buf, "/");
+ strcat(buf, filename);
+ if (access(buf, R_OK) == 0)
+ return buf;
+ }
+
+
+ BPathFinder f((void*)find_resource);
+
+ BPath p;
+ if (f.FindPath(B_FIND_PATH_APPS_DIRECTORY, "netsurf/res", p) == B_OK) {
+ strcpy(t, p.Path());
+ strcat(t, filename);
+ realpath(t, buf);
+ if (access(buf, R_OK) == 0)
+ return buf;
+ }
+
+ if (def[0] == '%') {
+ snprintf(t, PATH_MAX, "%s%s", path.Path(), def + 1);
+ if (realpath(t, buf) == NULL) {
+ strcpy(buf, t);
+ }
+ } else if (def[0] == '~') {
+ snprintf(t, PATH_MAX, "%s%s", getenv("HOME"), def + 1);
+ if (realpath(t, buf) == NULL) {
+ strcpy(buf, t);
+ }
+ } else {
+ if (realpath(def, buf) == NULL) {
+ strcpy(buf, def);
+ }
+ }
+
+ return buf;
+}
+
+/**
+ * Check that ~/.netsurf/ exists, and if it doesn't, create it.
+ */
+static void check_homedir(void)
+{
+ status_t err;
+
+ BPath path;
+ err = find_directory(B_USER_SETTINGS_DIRECTORY, &path, true);
+
+ if (err < B_OK) {
+ /* we really can't continue without a home directory. */
+ LOG("Can't find user settings directory - nowhere to store state!");
+ die("NetSurf needs to find the user settings directory in order to run.\n");
+ }
+
+ path.Append("NetSurf");
+ err = create_directory(path.Path(), 0644);
+ if (err < B_OK) {
+ LOG("Unable to create %s", path.Path());
+ die("NetSurf could not create its settings directory.\n");
+ }
+}
+
+static int32 bapp_thread(void *arg)
+{
+ be_app->Lock();
+ be_app->Run();
+ return 0;
+}
+
+static nsurl *gui_get_resource_url(const char *path)
+{
+ nsurl *url = NULL;
+ BString u("rsrc:///");
+
+ /* default.css -> beosdefault.css */
+ if (strcmp(path, "default.css") == 0)
+ path = "beosdefault.css";
+
+ /* favicon.ico -> favicon.png */
+ if (strcmp(path, "favicon.ico") == 0)
+ path = "favicon.png";
+
+ u << path;
+ LOG("(%s) -> '%s'\n", path, u.String());
+ nsurl_create(u.String(), &url);
+ return url;
+}
+
+
+
+#if !defined(__HAIKU__) && !defined(B_BEOS_VERSION_DANO)
+/* more ui_colors, R5 only had a few defined... */
+#define B_PANEL_TEXT_COLOR ((color_which)10)
+#define B_DOCUMENT_BACKGROUND_COLOR ((color_which)11)
+#define B_DOCUMENT_TEXT_COLOR ((color_which)12)
+#define B_CONTROL_BACKGROUND_COLOR ((color_which)13)
+#define B_CONTROL_TEXT_COLOR ((color_which)14)
+#define B_CONTROL_BORDER_COLOR ((color_which)15)
+#define B_CONTROL_HIGHLIGHT_COLOR ((color_which)16)
+#define B_NAVIGATION_BASE_COLOR ((color_which)4)
+#define B_NAVIGATION_PULSE_COLOR ((color_which)17)
+#define B_SHINE_COLOR ((color_which)18)
+#define B_SHADOW_COLOR ((color_which)19)
+#define B_MENU_SELECTED_BORDER_COLOR ((color_which)9)
+#define B_TOOL_TIP_BACKGROUND_COLOR ((color_which)20)
+#define B_TOOL_TIP_TEXT_COLOR ((color_which)21)
+#define B_SUCCESS_COLOR ((color_which)100)
+#define B_FAILURE_COLOR ((color_which)101)
+#define B_MENU_SELECTED_BACKGROUND_COLOR B_MENU_SELECTION_BACKGROUND_COLOR
+#define B_RANDOM_COLOR ((color_which)0x80000000)
+#define B_MICHELANGELO_FAVORITE_COLOR ((color_which)0x80000001)
+#define B_DSANDLER_FAVORITE_SKY_COLOR ((color_which)0x80000002)
+#define B_DSANDLER_FAVORITE_INK_COLOR ((color_which)0x80000003)
+#define B_DSANDLER_FAVORITE_SHOES_COLOR ((color_which)0x80000004)
+#define B_DAVE_BROWN_FAVORITE_COLOR ((color_which)0x80000005)
+#endif
+#if defined(B_BEOS_VERSION_DANO)
+#define B_TOOL_TIP_BACKGROUND_COLOR B_TOOLTIP_BACKGROUND_COLOR
+#define B_TOOL_TIP_TEXT_COLOR B_TOOLTIP_TEXT_COLOR
+#define
+#endif
+#define NOCOL ((color_which)0)
+
+/**
+ * set option from pen
+ */
+static nserror
+set_colour_from_ui(struct nsoption_s *opts,
+ color_which ui,
+ enum nsoption_e option,
+ colour def_colour)
+{
+ if (ui != NOCOL) {
+ rgb_color c;
+ if (ui == B_DESKTOP_COLOR) {
+ BScreen s;
+ c = s.DesktopColor();
+ } else {
+ c = ui_color(ui);
+ }
+
+ def_colour = ((((uint32_t)c.blue << 16) & 0xff0000) |
+ ((c.green << 8) & 0x00ff00) |
+ ((c.red) & 0x0000ff));
+ }
+
+ opts[option].value.c = def_colour;
+
+ return NSERROR_OK;
+}
+
+/**
+ * Set option defaults for framebuffer frontend
+ *
+ * @param defaults The option table to update.
+ * @return error status.
+ */
+static nserror set_defaults(struct nsoption_s *defaults)
+{
+ /* set system colours for beos ui */
+ set_colour_from_ui(defaults, NOCOL, NSOPTION_sys_colour_ActiveBorder, 0x00000000);
+ set_colour_from_ui(defaults, B_WINDOW_TAB_COLOR, NSOPTION_sys_colour_ActiveCaption, 0x00dddddd);
+ set_colour_from_ui(defaults, B_PANEL_BACKGROUND_COLOR, NSOPTION_sys_colour_AppWorkspace, 0x00eeeeee);
+ set_colour_from_ui(defaults, B_DESKTOP_COLOR, NSOPTION_sys_colour_Background, 0x00aa0000);
+ set_colour_from_ui(defaults, B_CONTROL_BACKGROUND_COLOR, NSOPTION_sys_colour_ButtonFace, 0x00aaaaaa);
+ set_colour_from_ui(defaults, B_CONTROL_HIGHLIGHT_COLOR, NSOPTION_sys_colour_ButtonHighlight, 0x00cccccc);
+ set_colour_from_ui(defaults, NOCOL, NSOPTION_sys_colour_ButtonShadow, 0x00bbbbbb);
+ set_colour_from_ui(defaults, B_CONTROL_TEXT_COLOR, NSOPTION_sys_colour_ButtonText, 0x00000000);
+ set_colour_from_ui(defaults, NOCOL, NSOPTION_sys_colour_CaptionText, 0x00000000);
+ set_colour_from_ui(defaults, NOCOL, NSOPTION_sys_colour_GrayText, 0x00777777);
+ set_colour_from_ui(defaults, NOCOL, NSOPTION_sys_colour_Highlight, 0x00ee0000);
+ set_colour_from_ui(defaults, NOCOL, NSOPTION_sys_colour_HighlightText, 0x00000000);
+ set_colour_from_ui(defaults, NOCOL, NSOPTION_sys_colour_InactiveBorder, 0x00000000);
+ set_colour_from_ui(defaults, NOCOL, NSOPTION_sys_colour_InactiveCaption, 0x00ffffff);
+ set_colour_from_ui(defaults, NOCOL, NSOPTION_sys_colour_InactiveCaptionText, 0x00cccccc);
+ set_colour_from_ui(defaults, B_TOOL_TIP_BACKGROUND_COLOR, NSOPTION_sys_colour_InfoBackground, 0x00aaaaaa);
+ set_colour_from_ui(defaults, B_TOOL_TIP_TEXT_COLOR, NSOPTION_sys_colour_InfoText, 0x00000000);
+ set_colour_from_ui(defaults, B_MENU_BACKGROUND_COLOR, NSOPTION_sys_colour_Menu, 0x00aaaaaa);
+ set_colour_from_ui(defaults, B_MENU_ITEM_TEXT_COLOR, NSOPTION_sys_colour_MenuText, 0x00000000);
+ set_colour_from_ui(defaults, NOCOL, NSOPTION_sys_colour_Scrollbar, 0x00aaaaaa);
+ set_colour_from_ui(defaults, NOCOL, NSOPTION_sys_colour_ThreeDDarkShadow, 0x00555555);
+ set_colour_from_ui(defaults, NOCOL, NSOPTION_sys_colour_ThreeDFace, 0x00dddddd);
+ set_colour_from_ui(defaults, NOCOL, NSOPTION_sys_colour_ThreeDHighlight, 0x00aaaaaa);
+ set_colour_from_ui(defaults, NOCOL, NSOPTION_sys_colour_ThreeDLightShadow, 0x00999999);
+ set_colour_from_ui(defaults, NOCOL, NSOPTION_sys_colour_ThreeDShadow, 0x00777777);
+ set_colour_from_ui(defaults, B_DOCUMENT_BACKGROUND_COLOR, NSOPTION_sys_colour_Window, 0x00aaaaaa);
+ set_colour_from_ui(defaults, NOCOL, NSOPTION_sys_colour_WindowFrame, 0x00000000);
+ set_colour_from_ui(defaults, B_DOCUMENT_TEXT_COLOR, NSOPTION_sys_colour_WindowText, 0x00000000);
+
+ return NSERROR_OK;
+}
+
+void nsbeos_update_system_ui_colors(void)
+{
+ set_defaults(nsoptions);
+}
+
+/**
+ * Ensures output logging stream is correctly configured
+ */
+static bool nslog_stream_configure(FILE *fptr)
+{
+ /* set log stream to be non-buffering */
+ setbuf(fptr, NULL);
+
+ return true;
+}
+
+static BPath get_messages_path()
+{
+ BPathFinder f((void*)get_messages_path);
+
+ BPath p;
+ f.FindPath(B_FIND_PATH_APPS_DIRECTORY, "netsurf/res", p);
+ // TODO: use Haiku's BLocale stuff
+ BString lang(getenv("LC_MESSAGES"));
+ lang.Truncate(2);
+ BDirectory d(p.Path());
+ if (!d.Contains(lang.String(), B_DIRECTORY_NODE))
+ lang = "en";
+ p.Append(lang.String());
+ p.Append("Messages");
+ return p;
+}
+
+
+static void gui_init(int argc, char** argv)
+{
+ const char *addr;
+ nsurl *url;
+ nserror error;
+ char buf[PATH_MAX];
+
+ if (pipe(sEventPipe) < 0)
+ return;
+ if (!replicated) {
+ sBAppThreadID = spawn_thread(bapp_thread, "BApplication(NetSurf)", B_NORMAL_PRIORITY, (void *)find_thread(NULL));
+ if (sBAppThreadID < B_OK)
+ return; /* #### handle errors */
+ if (resume_thread(sBAppThreadID) < B_OK)
+ return;
+ }
+
+ nsbeos_update_system_ui_colors();
+
+ fetch_rsrc_register();
+
+ check_homedir();
+
+ // make sure the cache dir exists
+ create_directory(TEMP_FILENAME_PREFIX, 0700);
+
+ //nsbeos_completion_init();
+
+
+ /* This is an ugly hack to just get the new-style throbber going.
+ * It, along with the PNG throbber loader, need making more generic.
+ */
+ {
+#define STROF(n) #n
+#define FIND_THROB(n) filenames[(n)] = \
+ "throbber/throbber" STROF(n) ".png";
+ const char *filenames[9];
+ FIND_THROB(0);
+ FIND_THROB(1);
+ FIND_THROB(2);
+ FIND_THROB(3);
+ FIND_THROB(4);
+ FIND_THROB(5);
+ FIND_THROB(6);
+ FIND_THROB(7);
+ FIND_THROB(8);
+ nsbeos_throbber_initialise_from_png(9,
+ filenames[0], filenames[1], filenames[2], filenames[3],
+ filenames[4], filenames[5], filenames[6], filenames[7],
+ filenames[8]);
+#undef FIND_THROB
+#undef STROF
+ }
+
+ if (nsbeos_throbber == NULL)
+ die("Unable to load throbber image.\n");
+
+ find_resource(buf, "Choices", "%/Choices");
+ LOG("Using '%s' as Preferences file", buf);
+ options_file_location = strdup(buf);
+ nsoption_read(buf, NULL);
+
+
+ /* check what the font settings are, setting them to a default font
+ * if they're not set - stops Pango whinging
+ */
+#define SETFONTDEFAULT(OPTION,y) if (nsoption_charp(OPTION) == NULL) nsoption_set_charp(OPTION, strdup((y)))
+
+ //XXX: use be_plain_font & friends, when we can check if font is serif or not.
+/*
+ font_family family;
+ font_style style;
+ be_plain_font->GetFamilyAndStyle(&family, &style);
+ SETFONTDEFAULT(font_sans, family);
+ SETFONTDEFAULT(font_serif, family);
+ SETFONTDEFAULT(font_mono, family);
+ SETFONTDEFAULT(font_cursive, family);
+ SETFONTDEFAULT(font_fantasy, family);
+*/
+#ifdef __HAIKU__
+ SETFONTDEFAULT(font_sans, "DejaVu Sans");
+ SETFONTDEFAULT(font_serif, "DejaVu Serif");
+ SETFONTDEFAULT(font_mono, "DejaVu Mono");
+ SETFONTDEFAULT(font_cursive, "DejaVu Sans");
+ SETFONTDEFAULT(font_fantasy, "DejaVu Sans");
+#else
+ SETFONTDEFAULT(font_sans, "Bitstream Vera Sans");
+ SETFONTDEFAULT(font_serif, "Bitstream Vera Serif");
+ SETFONTDEFAULT(font_mono, "Bitstream Vera Sans Mono");
+ SETFONTDEFAULT(font_cursive, "Bitstream Vera Serif");
+ SETFONTDEFAULT(font_fantasy, "Bitstream Vera Serif");
+#endif
+
+ nsbeos_options_init();
+
+ /* We don't yet have an implementation of "select" form elements (they should use a popup menu)
+ * So we use the cross-platform code instead. */
+ nsoption_set_bool(core_select_menu, true);
+
+ if (nsoption_charp(cookie_file) == NULL) {
+ find_resource(buf, "Cookies", "%/Cookies");
+ LOG("Using '%s' as Cookies file", buf);
+ nsoption_set_charp(cookie_file, strdup(buf));
+ }
+ if (nsoption_charp(cookie_jar) == NULL) {
+ find_resource(buf, "Cookies", "%/Cookies");
+ LOG("Using '%s' as Cookie Jar file", buf);
+ nsoption_set_charp(cookie_jar, strdup(buf));
+ }
+ if ((nsoption_charp(cookie_file) == NULL) ||
+ (nsoption_charp(cookie_jar) == NULL))
+ die("Failed initialising cookie options");
+
+ if (nsoption_charp(url_file) == NULL) {
+ find_resource(buf, "URLs", "%/URLs");
+ LOG("Using '%s' as URL file", buf);
+ nsoption_set_charp(url_file, strdup(buf));
+ }
+
+ if (nsoption_charp(ca_path) == NULL) {
+ find_resource(buf, "certs", "/etc/ssl/certs");
+ LOG("Using '%s' as certificate path", buf);
+ nsoption_set_charp(ca_path, strdup(buf));
+ }
+
+ //find_resource(buf, "mime.types", "/etc/mime.types");
+ beos_fetch_filetype_init();
+
+ urldb_load(nsoption_charp(url_file));
+ urldb_load_cookies(nsoption_charp(cookie_file));
+
+ //nsbeos_download_initialise();
+
+ if (!replicated)
+ be_app->Unlock();
+
+ if (argc > 1) {
+ addr = argv[1];
+ } else if (nsoption_charp(homepage_url) != NULL) {
+ addr = nsoption_charp(homepage_url);
+ } else {
+ addr = NETSURF_HOMEPAGE;
+ }
+
+ /* create an initial browser window */
+ error = nsurl_create(addr, &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(
+ BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ beos_warn_user(messages_get_errorcode(error), 0);
+ }
+
+ if (gFirstRefsReceived) {
+ // resend the refs we got before having a window to send them to
+ be_app_messenger.SendMessage(gFirstRefsReceived);
+ delete gFirstRefsReceived;
+ gFirstRefsReceived = NULL;
+ }
+
+}
+
+
+
+
+void nsbeos_pipe_message(BMessage *message, BView *_this, struct gui_window *gui)
+{
+ if (message == NULL) {
+ fprintf(stderr, "%s(NULL)!\n", __FUNCTION__);
+ return;
+ }
+ if (_this)
+ message->AddPointer("View", _this);
+ if (gui)
+ message->AddPointer("gui_window", gui);
+ write(sEventPipe[1], &message, sizeof(void *));
+}
+
+
+void nsbeos_pipe_message_top(BMessage *message, BWindow *_this, struct beos_scaffolding *scaffold)
+{
+ if (message == NULL) {
+ fprintf(stderr, "%s(NULL)!\n", __FUNCTION__);
+ return;
+ }
+ if (_this)
+ message->AddPointer("Window", _this);
+ if (scaffold)
+ message->AddPointer("scaffolding", scaffold);
+ write(sEventPipe[1], &message, sizeof(void *));
+}
+
+
+void nsbeos_gui_poll(void)
+{
+ fd_set read_fd_set, write_fd_set, exc_fd_set;
+ int max_fd;
+ struct timeval timeout;
+ unsigned int fd_count = 0;
+ bigtime_t next_schedule = 0;
+
+ /* get any active fetcher fd */
+ fetcher_fdset(&read_fd_set, &write_fd_set, &exc_fd_set, &max_fd);
+
+ /* run the scheduler */
+ schedule_run();
+
+ // our own event pipe
+ FD_SET(sEventPipe[0], &read_fd_set);
+
+ // max of all the fds in the set, plus one for select()
+ max_fd = MAX(max_fd, sEventPipe[0]) + 1;
+
+ // compute schedule timeout
+ if (earliest_callback_timeout != B_INFINITE_TIMEOUT) {
+ next_schedule = earliest_callback_timeout - system_time();
+ } else {
+ next_schedule = earliest_callback_timeout;
+ }
+
+ // we're quite late already...
+ if (next_schedule < 0)
+ next_schedule = 0;
+
+ timeout.tv_sec = (long)(next_schedule / 1000000LL);
+ timeout.tv_usec = (long)(next_schedule % 1000000LL);
+
+ //LOG("gui_poll: select(%d, ..., %Ldus", max_fd, next_schedule);
+ fd_count = select(max_fd, &read_fd_set, &write_fd_set, &exc_fd_set,
+ &timeout);
+ //LOG("select: %d\n", fd_count);
+
+ if (fd_count > 0 && FD_ISSET(sEventPipe[0], &read_fd_set)) {
+ BMessage *message;
+ int len = read(sEventPipe[0], &message, sizeof(void *));
+ //LOG("gui_poll: BMessage ? %d read", len);
+ if (len == sizeof(void *)) {
+ //LOG("gui_poll: BMessage.what %-4.4s\n", &(message->what));
+ nsbeos_dispatch_event(message);
+ }
+ }
+}
+
+
+static void gui_quit(void)
+{
+ urldb_save_cookies(nsoption_charp(cookie_jar));
+ urldb_save(nsoption_charp(url_file));
+ //options_save_tree(hotlist,nsoption_charp(hotlist_file),messages_get("TreeHotlist"));
+
+ free(nsoption_charp(cookie_file));
+ free(nsoption_charp(cookie_jar));
+ beos_fetch_filetype_fin();
+ fetch_rsrc_unregister();
+}
+
+static char *url_to_path(const char *url)
+{
+ char *url_path = curl_unescape(url, 0);
+ char *path;
+
+ /* return the absolute path including leading / */
+ path = strdup(url_path + (FILE_SCHEME_PREFIX_LEN - 1));
+ curl_free(url_path);
+
+ return path;
+}
+
+/**
+ * Send the source of a content to a text editor.
+ */
+
+void nsbeos_gui_view_source(struct hlcache_handle *content)
+{
+ char *temp_name;
+ bool done = false;
+ BPath path;
+ status_t err;
+ size_t size;
+ const char *source = content_get_source_data(content, &size);
+
+ if (!content || !source) {
+ beos_warn_user("MiscError", "No document source");
+ return;
+ }
+
+ /* try to load local files directly. */
+ temp_name = url_to_path(nsurl_access(hlcache_handle_get_url(content)));
+ if (temp_name) {
+ path.SetTo(temp_name);
+ BEntry entry;
+ if (entry.SetTo(path.Path()) >= B_OK
+ && entry.Exists() && entry.IsFile())
+ done = true;
+ }
+ if (!done) {
+ /* We cannot release the requested filename until after it
+ * has finished being used. As we can't easily find out when
+ * this is, we simply don't bother releasing it and simply
+ * allow it to be re-used next time NetSurf is started. The
+ * memory overhead from doing this is under 1 byte per
+ * filename. */
+ const char *filename = filename_request();
+ if (!filename) {
+ beos_warn_user("NoMemory", 0);
+ return;
+ }
+ path.SetTo(TEMP_FILENAME_PREFIX);
+ path.Append(filename);
+ BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE);
+ err = file.InitCheck();
+ if (err < B_OK) {
+ beos_warn_user("IOError", strerror(err));
+ return;
+ }
+ err = file.Write(source, size);
+ if (err < B_OK) {
+ beos_warn_user("IOError", strerror(err));
+ return;
+ }
+ lwc_string *mime = content_get_mime_type(content);
+ if (mime) {
+ file.WriteAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0LL,
+ lwc_string_data(mime), lwc_string_length(mime) + 1);
+ lwc_string_unref(mime);
+ }
+
+ }
+
+ entry_ref ref;
+ if (get_ref_for_path(path.Path(), &ref) < B_OK)
+ return;
+
+ BMessage m(B_REFS_RECEIVED);
+ m.AddRef("refs", &ref);
+
+
+ // apps to try
+ const char *editorSigs[] = {
+ "application/x-vnd.beunited.pe",
+ "application/x-vnd.XEmacs",
+ "application/x-vnd.Haiku-StyledEdit",
+ "application/x-vnd.Be-STEE",
+ "application/x-vnd.yT-STEE",
+ NULL
+ };
+ int i;
+ for (i = 0; editorSigs[i]; i++) {
+ team_id team = -1;
+ {
+ BMessenger msgr(editorSigs[i], team);
+ if (msgr.SendMessage(&m) >= B_OK)
+ break;
+ }
+
+ err = be_roster->Launch(editorSigs[i], (BMessage *)&m, &team);
+ if (err >= B_OK)
+ break;
+ }
+}
+
+/**
+ * Broadcast an URL that we can't handle.
+ */
+
+static nserror gui_launch_url(struct nsurl *url)
+{
+ status_t status;
+ // try to open it as an URI
+ BString mimeType = "application/x-vnd.Be.URL.";
+ BString arg(nsurl_access(url));
+
+ mimeType.Append(arg, arg.FindFirst(":"));
+
+ // special case, text/x-email is used traditionally
+ // use it instead
+ if (arg.IFindFirst("mailto:") == 0)
+ mimeType = "text/x-email";
+
+ // the protocol should be alphanum
+ // we just check if it's registered
+ // if not there is likely no supporting app anyway
+ if (!BMimeType::IsValid(mimeType.String()))
+ return NSERROR_NO_FETCH_HANDLER;
+ char *args[2] = { (char *)nsurl_access(url), NULL };
+ status = be_roster->Launch(mimeType.String(), 1, args);
+ if (status < B_OK)
+ beos_warn_user("Cannot launch url", strerror(status));
+ return NSERROR_OK;
+}
+
+
+
+void die(const char * const error)
+{
+ fprintf(stderr, "%s", error);
+ BAlert *alert;
+ BString text("Cannot continue:\n");
+ text << error;
+
+ alert = new BAlert("NetSurf Error", text.String(), "Debug", "Ok", NULL,
+ B_WIDTH_AS_USUAL, B_STOP_ALERT);
+ if (alert->Go() < 1)
+ debugger("die");
+
+ exit(EXIT_FAILURE);
+}
+
+
+static struct gui_fetch_table beos_fetch_table = {
+ fetch_filetype,
+ gui_get_resource_url,
+ NULL, // ???
+ NULL, // release_resource_data
+ NULL, // fetch_mimetype
+};
+
+static struct gui_misc_table beos_misc_table = {
+ beos_schedule,
+ beos_warn_user,
+ gui_quit,
+ gui_launch_url,
+ NULL, //cert_verify
+ gui_401login_open,
+ NULL, // pdf_password (if we have Haru support)
+};
+
+
+/** Normal entry point from OS */
+int main(int argc, char** argv)
+{
+ nserror ret;
+ BPath options;
+ struct netsurf_table beos_table = {
+ &beos_misc_table,
+ beos_window_table,
+ beos_download_table,
+ beos_clipboard_table,
+ &beos_fetch_table,
+ NULL, /* use POSIX file */
+ NULL, /* default utf8 */
+ NULL, /* default search */
+ NULL, /* default web search */
+ NULL, /* default low level cache persistant storage */
+ beos_bitmap_table,
+ beos_layout_table
+ };
+
+ ret = netsurf_register(&beos_table);
+ if (ret != NSERROR_OK) {
+ die("NetSurf operation table failed registration");
+ }
+
+ if (find_directory(B_USER_SETTINGS_DIRECTORY, &options, true) == B_OK) {
+ options.Append("x-vnd.NetSurf");
+ }
+
+ if (!replicated) {
+ // create the Application object before trying to use messages
+ // so we can open an alert in case of error.
+ new NSBrowserApplication;
+ }
+
+ /* initialise logging. Not fatal if it fails but not much we
+ * can do about it either.
+ */
+ nslog_init(nslog_stream_configure, &argc, argv);
+
+ /* user options setup */
+ ret = nsoption_init(set_defaults, &nsoptions, &nsoptions_default);
+ if (ret != NSERROR_OK) {
+ die("Options failed to initialise");
+ }
+ nsoption_read(options.Path(), NULL);
+ nsoption_commandline(&argc, argv, NULL);
+
+ /* common initialisation */
+ BResources resources;
+ resources.SetToImage((const void*)main);
+ size_t size = 0;
+
+ char path[12];
+ sprintf(path,"%.2s/Messages", getenv("LC_MESSAGES"));
+ fprintf(stderr, "Loading messages from resource %s\n", path);
+
+ const uint8_t* res = (const uint8_t*)resources.LoadResource('data', path, &size);
+ if (size > 0 && res != NULL) {
+ ret = messages_add_from_inline(res, size);
+ } else {
+ BPath messages = get_messages_path();
+ ret = messages_add_from_file(messages.Path());
+ }
+
+ ret = netsurf_init(NULL);
+ if (ret != NSERROR_OK) {
+ die("NetSurf failed to initialise");
+ }
+
+ gui_init(argc, argv);
+
+ while (!nsbeos_done) {
+ nsbeos_gui_poll();
+ }
+
+ netsurf_exit();
+
+ return 0;
+}
+
+/** called when replicated from NSBaseView::Instantiate() */
+int gui_init_replicant(int argc, char** argv)
+{
+ nserror ret;
+ BPath options;
+ struct netsurf_table beos_table = {
+ &beos_misc_table,
+ beos_window_table,
+ beos_download_table,
+ beos_clipboard_table,
+ &beos_fetch_table,
+ NULL, /* use POSIX file */
+ NULL, /* default utf8 */
+ NULL, /* default search */
+ NULL, /* default web search */
+ NULL, /* default low level cache persistant storage */
+ beos_bitmap_table,
+ beos_layout_table
+ };
+
+ ret = netsurf_register(&beos_table);
+ if (ret != NSERROR_OK) {
+ die("NetSurf operation table failed registration");
+ }
+
+ if (find_directory(B_USER_SETTINGS_DIRECTORY, &options, true) == B_OK) {
+ options.Append("x-vnd.NetSurf");
+ }
+
+ /* initialise logging. Not fatal if it fails but not much we
+ * can do about it either.
+ */
+ nslog_init(nslog_stream_configure, &argc, argv);
+
+ // FIXME: use options as readonly for replicants
+ /* user options setup */
+ ret = nsoption_init(set_defaults, &nsoptions, &nsoptions_default);
+ if (ret != NSERROR_OK) {
+ // FIXME: must not die when in replicant!
+ die("Options failed to initialise");
+ }
+ nsoption_read(options.Path(), NULL);
+ nsoption_commandline(&argc, argv, NULL);
+
+ /* common initialisation */
+ BPath messages = get_messages_path();
+ ret = messages_add_from_file(messages.Path());
+
+ ret = netsurf_init(NULL);
+ if (ret != NSERROR_OK) {
+ // FIXME: must not die when in replicant!
+ die("NetSurf failed to initialise");
+ }
+
+ gui_init(argc, argv);
+
+ return 0;
+}
diff --git a/frontends/beos/gui.h b/frontends/beos/gui.h
new file mode 100644
index 000000000..774820baa
--- /dev/null
+++ b/frontends/beos/gui.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2015 Adrián Arroyo Calle <adrian.arroyocalle@gmail.com>
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include <Application.h>
+#include <FilePanel.h>
+#include <Window.h>
+#include <BeBuild.h>
+
+#ifndef B_BEOS_VERSION_DANO
+#define B_UI_SETTINGS_CHANGED '_UIC'
+#endif
+
+#define CALLED() fprintf(stderr, "%s()\n", __FUNCTION__);
+
+extern bool nsbeos_done;
+
+extern bool replicated;
+int gui_init_replicant(int argc, char** argv);
+
+extern "C" void gui_401login_open(nsurl *url, const char *realm,
+ nserror (*cb)(bool proceed, void *pw), void *cbpw);
+
+extern "C" void nsbeos_gui_poll(void);
+
+extern char *options_file_location;
+
+class NSBrowserApplication : public BApplication {
+public:
+ NSBrowserApplication();
+virtual ~NSBrowserApplication();
+
+virtual void MessageReceived(BMessage *message);
+virtual void RefsReceived(BMessage *message);
+virtual void ArgvReceived(int32 argc, char **argv);
+
+virtual void AboutRequested();
+virtual bool QuitRequested();
+};
+
+extern BWindow *wndAbout;
+
+extern BWindow *wndTooltip;
+
+extern BFilePanel *wndOpenFile;
+
+void nsbeos_pipe_message(BMessage *message, BView *_this, struct gui_window *gui);
+void nsbeos_pipe_message_top(BMessage *message, BWindow *_this, struct beos_scaffolding *scaffold);
+
+void nsbeos_gui_view_source(struct hlcache_handle *content);
+image_id nsbeos_find_app_path(char *path);
+char *find_resource(char *buf, const char *filename, const char *def);
+
+void nsbeos_update_system_ui_colors(void);
+
+/**
+ * Cause an abnormal program termination.
+ *
+ * \note This never returns and is intended to terminate without any cleanup.
+ *
+ * \param error The message to display to the user.
+ */
+void die(const char * const error) __attribute__ ((noreturn));
+
+/**
+ * Display a warning for a serious problem (eg memory exhaustion).
+ *
+ * \param warning message key for warning message
+ * \param detail additional message, or 0
+ */
+nserror beos_warn_user(const char *warning, const char *detail);
diff --git a/frontends/beos/gui_options.cpp b/frontends/beos/gui_options.cpp
new file mode 100644
index 000000000..035c62941
--- /dev/null
+++ b/frontends/beos/gui_options.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#define __STDBOOL_H__ 1
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+extern "C" {
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "utils/nsurl.h"
+}
+#include "beos/gui.h"
+#include "beos/scaffolding.h"
+#include "beos/gui_options.h"
+
+#include <View.h>
+#include <Window.h>
+
+BWindow *wndPreferences;
+
+void nsbeos_options_init(void) {
+ /* set the widgets to reflect the current options */
+ nsbeos_options_load();
+}
+
+void nsbeos_options_load(void) {
+#warning WRITEME
+}
+
+
+void nsbeos_options_save(void) {
+#warning WRITEME
+}
diff --git a/frontends/beos/gui_options.h b/frontends/beos/gui_options.h
new file mode 100644
index 000000000..e5c91a797
--- /dev/null
+++ b/frontends/beos/gui_options.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_BEOS_OPTIONS_H
+#define NETSURF_BEOS_OPTIONS_H
+
+#include <Window.h>
+
+extern BWindow *wndPreferences;
+
+void nsbeos_options_init(void); /** Init options and load window */
+void nsbeos_options_load(void); /** Load current options into window */
+void nsbeos_options_save(void); /** Save options from window */
+
+#endif
diff --git a/frontends/beos/login.cpp b/frontends/beos/login.cpp
new file mode 100644
index 000000000..7689c9d35
--- /dev/null
+++ b/frontends/beos/login.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#define __STDBOOL_H__ 1
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <Alert.h>
+#include <String.h>
+#include <TextControl.h>
+#include <View.h>
+#include <Window.h>
+extern "C" {
+#include "utils/log.h"
+#include "content/content.h"
+#include "content/urldb.h"
+#include "desktop/browser.h"
+#include "utils/messages.h"
+#include "utils/url.h"
+#include "utils/utils.h"
+#include "desktop/gui_clipboard.h"
+}
+#include "beos/gui.h"
+#include "beos/scaffolding.h"
+#include "beos/window.h"
+
+class LoginAlert : public BAlert {
+public:
+ LoginAlert(nserror (*callback)(bool proceed, void *pw),
+ void *callbaclpw,
+ nsurl *url,
+ const char *host,
+ const char *realm,
+ const char *text);
+ virtual ~LoginAlert();
+ void MessageReceived(BMessage *message);
+
+private:
+ nsurl* fUrl; /**< URL being fetched */
+ BString fHost; /**< Host for user display */
+ BString fRealm; /**< Authentication realm */
+ nserror (*fCallback)(bool proceed, void *pw);
+ void *fCallbackPw;
+
+ BTextControl *fUserControl;
+ BTextControl *fPassControl;
+};
+
+static void create_login_window(nsurl *host,
+ lwc_string *realm, const char *fetchurl,
+ nserror (*cb)(bool proceed, void *pw), void *cbpw);
+
+
+#define TC_H 25
+#define TC_MARGIN 10
+
+LoginAlert::LoginAlert(nserror (*callback)(bool proceed, void *pw),
+ void *callbackpw,
+ nsurl *url,
+ const char *host,
+ const char *realm,
+ const char *text)
+ : BAlert("Login", text, "Cancel", "Ok", NULL,
+ B_WIDTH_AS_USUAL, B_WARNING_ALERT)
+{
+ fCallback = callback;
+ fCallbackPw = callbackpw;
+ fUrl = url;
+ fHost = host;
+ fRealm = realm;
+
+ SetFeel(B_MODAL_SUBSET_WINDOW_FEEL);
+ /*
+ // XXX: can't do that anymore
+ nsbeos_scaffolding *s = nsbeos_get_scaffold(bw->window);
+ if (s) {
+ NSBrowserWindow *w = nsbeos_get_bwindow_for_scaffolding(s);
+ if (w)
+ AddToSubset(w);
+ }*/
+
+ // make space for controls
+ ResizeBy(0, 2 * TC_H);
+ MoveTo(AlertPosition(Frame().Width() + 1,
+ Frame().Height() + 1));
+
+
+ BTextView *tv = TextView();
+ BRect r(TC_MARGIN, tv->Bounds().bottom - 2 * TC_H,
+ tv->Bounds().right - TC_MARGIN, tv->Bounds().bottom - TC_H);
+
+ fUserControl = new BTextControl(r, "user", "Username", "",
+ new BMessage(), B_FOLLOW_BOTTOM | B_FOLLOW_RIGHT);
+ fUserControl->SetDivider(60);
+ tv->AddChild(fUserControl);
+
+ r.OffsetBySelf(0, TC_H);
+
+ fPassControl = new BTextControl(r, "pass", "Password", "",
+ new BMessage(), B_FOLLOW_BOTTOM | B_FOLLOW_RIGHT);
+ fPassControl->TextView()->HideTyping(true);
+ fPassControl->SetDivider(60);
+ tv->AddChild(fPassControl);
+
+ SetShortcut(0, B_ESCAPE);
+}
+
+LoginAlert::~LoginAlert()
+{
+}
+
+void
+LoginAlert::MessageReceived(BMessage *message)
+{
+ switch (message->what) {
+ case 'ALTB':
+ {
+ int32 which;
+ if (message->FindInt32("which", &which) < B_OK)
+ break;
+ // not 'Ok'
+ if (which != 1)
+ break;
+ BMessage *m = new BMessage(*message);
+ m->what = 'nsLO';
+ m->AddPointer("URL", fUrl);
+ m->AddString("Host", fHost.String());
+ m->AddString("Realm", fRealm.String());
+ m->AddPointer("callback", (void *)fCallback);
+ m->AddPointer("callback_pw", (void *)fCallbackPw);
+ m->AddString("User", fUserControl->Text());
+ m->AddString("Pass", fPassControl->Text());
+ BString auth(fUserControl->Text());
+ auth << ":" << fPassControl->Text();
+ m->AddString("Auth", auth.String());
+
+ // notify the main thread
+ // the event dispatcher will handle it
+ nsbeos_pipe_message(m, NULL, NULL);
+ }
+ break;
+ default:
+ break;
+ }
+ BAlert::MessageReceived(message);
+}
+
+
+extern "C" void gui_401login_open(nsurl *url, const char *realm,
+ nserror (*cb)(bool proceed, void *pw), void *cbpw)
+{
+ lwc_string *host;
+
+ host = nsurl_get_component(url, NSURL_HOST);
+
+ create_login_window(url, host, realm, cb, cbpw);
+
+ free(host);
+}
+
+//void create_login_window(struct browser_window *bw, const char *host,
+// const char *realm, const char *fetchurl)
+static void create_login_window(nsurl *url, lwc_string *host,
+ const char *realm, nserror (*cb)(bool proceed, void *pw),
+ void *cbpw)
+{
+ BString r("Secure Area");
+ if (realm)
+ r = realm;
+ BString text(/*messages_get(*/"Please login\n");
+ text << "Realm: " << r << "\n";
+ text << "Host: " << host << "\n";
+ //text << "\n";
+
+ LoginAlert *a = new LoginAlert(cb, cbpw, url, lwc_string_data(host),
+ r.String(), text.String());
+ // asynchronously
+ a->Go(NULL);
+
+}
diff --git a/frontends/beos/options.h b/frontends/beos/options.h
new file mode 100644
index 000000000..40d23a3bc
--- /dev/null
+++ b/frontends/beos/options.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_BEOS_OPTIONS_H_
+#define _NETSURF_BEOS_OPTIONS_H_
+
+/* currently nothing here */
+
+#endif
+
+NSOPTION_BOOL(render_resample, false)
+NSOPTION_STRING(url_file, NULL)
+
diff --git a/frontends/beos/plotters.cpp b/frontends/beos/plotters.cpp
new file mode 100644
index 000000000..3fd786ecd
--- /dev/null
+++ b/frontends/beos/plotters.cpp
@@ -0,0 +1,659 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Target independent plotting (BeOS/Haiku implementation).
+ */
+
+#define __STDBOOL_H__ 1
+#include <math.h>
+#include <BeBuild.h>
+#include <Bitmap.h>
+#include <GraphicsDefs.h>
+#include <Region.h>
+#include <View.h>
+#include <Shape.h>
+extern "C" {
+#include "desktop/plotters.h"
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "utils/nsoption.h"
+#include "utils/nsurl.h"
+}
+#include "beos/font.h"
+#include "beos/gui.h"
+#include "beos/plotters.h"
+#include "beos/bitmap.h"
+
+#warning MAKE ME static
+/*static*/ BView *current_view;
+
+/*
+ * NOTE: BeOS rects differ from NetSurf ones:
+ * the right-bottom pixel is actually part of the BRect!
+ */
+
+static bool nsbeos_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style);
+static bool nsbeos_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *style);
+static bool nsbeos_plot_polygon(const int *p, unsigned int n, const plot_style_t *style);
+static bool nsbeos_plot_path(const float *p, unsigned int n, colour fill, float width,
+ colour c, const float transform[6]);
+static bool nsbeos_plot_clip(const struct rect *ns_clip);
+static bool nsbeos_plot_text(int x, int y, const char *text, size_t length,
+ const plot_font_style_t *fstyle);
+static bool nsbeos_plot_disc(int x, int y, int radius, const plot_style_t *style);
+static bool nsbeos_plot_arc(int x, int y, int radius, int angle1, int angle2,
+ const plot_style_t *style);
+static bool nsbeos_plot_bitmap(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg,
+ bitmap_flags_t flags);
+
+
+#warning make patterns nicer
+static const pattern kDottedPattern = { 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa };
+static const pattern kDashedPattern = { 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0x33, 0x33 };
+
+static const rgb_color kBlackColor = { 0, 0, 0, 255 };
+
+struct plotter_table plot;
+
+const struct plotter_table nsbeos_plotters = {
+ nsbeos_plot_clip,
+ nsbeos_plot_arc,
+ nsbeos_plot_disc,
+ nsbeos_plot_line,
+ nsbeos_plot_rectangle,
+ nsbeos_plot_polygon,
+ nsbeos_plot_path,
+ nsbeos_plot_bitmap,
+ nsbeos_plot_text,
+ NULL, // Group Start
+ NULL, // Group End
+ NULL, // Flush
+ true // option_knockout
+};
+
+
+// #pragma mark - implementation
+
+
+BView *nsbeos_current_gc(void)
+{
+ return current_view;
+}
+
+BView *nsbeos_current_gc_lock(void)
+{
+ BView *view = current_view;
+ if (view && view->LockLooper())
+ return view;
+ return NULL;
+}
+
+void nsbeos_current_gc_unlock(void)
+{
+ if (current_view)
+ current_view->UnlockLooper();
+}
+
+void nsbeos_current_gc_set(BView *view)
+{
+ // XXX: (un)lock previous ?
+ current_view = view;
+}
+
+bool nsbeos_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ if (style->fill_type != PLOT_OP_TYPE_NONE) {
+ BView *view;
+
+ view = nsbeos_current_gc/*_lock*/();
+ if (view == NULL) {
+ beos_warn_user("No GC", 0);
+ return false;
+ }
+
+ nsbeos_set_colour(style->fill_colour);
+
+ BRect rect(x0, y0, x1 - 1, y1 - 1);
+ view->FillRect(rect);
+
+ //nsbeos_current_gc_unlock();
+
+ }
+
+ if (style->stroke_type != PLOT_OP_TYPE_NONE) {
+ pattern pat;
+ BView *view;
+
+ switch (style->stroke_type) {
+ case PLOT_OP_TYPE_SOLID: /**< Solid colour */
+ default:
+ pat = B_SOLID_HIGH;
+ break;
+
+ case PLOT_OP_TYPE_DOT: /**< Doted plot */
+ pat = kDottedPattern;
+ break;
+
+ case PLOT_OP_TYPE_DASH: /**< dashed plot */
+ pat = kDashedPattern;
+ break;
+ }
+
+ view = nsbeos_current_gc/*_lock*/();
+ if (view == NULL) {
+ beos_warn_user("No GC", 0);
+ return false;
+ }
+
+ nsbeos_set_colour(style->stroke_colour);
+
+ float pensize = view->PenSize();
+ view->SetPenSize(style->stroke_width);
+
+ BRect rect(x0, y0, x1, y1);
+ view->StrokeRect(rect, pat);
+
+ view->SetPenSize(pensize);
+
+ //nsbeos_current_gc_unlock();
+
+ }
+
+ return true;
+}
+
+
+
+bool nsbeos_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ pattern pat;
+ BView *view;
+
+ switch (style->stroke_type) {
+ case PLOT_OP_TYPE_SOLID: /**< Solid colour */
+ default:
+ pat = B_SOLID_HIGH;
+ break;
+
+ case PLOT_OP_TYPE_DOT: /**< Doted plot */
+ pat = kDottedPattern;
+ break;
+
+ case PLOT_OP_TYPE_DASH: /**< dashed plot */
+ pat = kDashedPattern;
+ break;
+ }
+
+ view = nsbeos_current_gc/*_lock*/();
+ if (view == NULL) {
+ beos_warn_user("No GC", 0);
+ return false;
+ }
+
+ nsbeos_set_colour(style->stroke_colour);
+
+ float pensize = view->PenSize();
+ view->SetPenSize(style->stroke_width);
+
+ BPoint start(x0, y0);
+ BPoint end(x1, y1);
+ view->StrokeLine(start, end, pat);
+
+ view->SetPenSize(pensize);
+
+ //nsbeos_current_gc_unlock();
+
+ return true;
+}
+
+
+bool nsbeos_plot_polygon(const int *p, unsigned int n, const plot_style_t *style)
+{
+ unsigned int i;
+ BView *view;
+
+ view = nsbeos_current_gc/*_lock*/();
+ if (view == NULL) {
+ beos_warn_user("No GC", 0);
+ return false;
+ }
+
+ nsbeos_set_colour(style->fill_colour);
+
+ BPoint points[n];
+
+ for (i = 0; i < n; i++) {
+ points[i] = BPoint(p[2 * i] - 0.5, p[2 * i + 1] - 0.5);
+ }
+
+ if (style->fill_colour == NS_TRANSPARENT)
+ view->StrokePolygon(points, (int32)n);
+ else
+ view->FillPolygon(points, (int32)n);
+
+ return true;
+}
+
+
+
+
+bool nsbeos_plot_clip(const struct rect *ns_clip)
+{
+ BView *view;
+ //fprintf(stderr, "%s(%d, %d, %d, %d)\n", __FUNCTION__, clip_x0, clip_y0, clip_x1, clip_y1);
+
+ view = nsbeos_current_gc/*_lock*/();
+ if (view == NULL) {
+ beos_warn_user("No GC", 0);
+ return false;
+ }
+
+ BRect rect(ns_clip->x0, ns_clip->y0, ns_clip->x1 - 1,
+ ns_clip->y1 - 1);
+ BRegion clip(rect);
+ view->ConstrainClippingRegion(NULL);
+ if (view->Bounds() != rect)
+ view->ConstrainClippingRegion(&clip);
+
+
+ //nsbeos_current_gc_unlock();
+
+ return true;
+}
+
+
+bool nsbeos_plot_text(int x, int y, const char *text, size_t length,
+ const plot_font_style_t *fstyle)
+{
+ return nsfont_paint(fstyle, text, length, x, y);
+}
+
+
+bool nsbeos_plot_disc(int x, int y, int radius, const plot_style_t *style)
+{
+ BView *view;
+
+ view = nsbeos_current_gc/*_lock*/();
+ if (view == NULL) {
+ beos_warn_user("No GC", 0);
+ return false;
+ }
+
+ nsbeos_set_colour(style->fill_colour);
+
+ BPoint center(x, y);
+ if (style->fill_type != PLOT_OP_TYPE_NONE)
+ view->FillEllipse(center, radius, radius);
+ else
+ view->StrokeEllipse(center, radius, radius);
+
+ //nsbeos_current_gc_unlock();
+
+ return true;
+}
+
+bool nsbeos_plot_arc(int x, int y, int radius, int angle1, int angle2, const plot_style_t *style)
+{
+ BView *view;
+
+ view = nsbeos_current_gc/*_lock*/();
+ if (view == NULL) {
+ beos_warn_user("No GC", 0);
+ return false;
+ }
+
+ nsbeos_set_colour(style->fill_colour);
+
+ BPoint center(x, y);
+ float angle = angle1; // in degree
+ float span = angle2 - angle1; // in degree
+ view->StrokeArc(center, radius, radius, angle, span);
+
+ //nsbeos_current_gc_unlock();
+
+ return true;
+}
+
+static bool nsbeos_plot_bbitmap(int x, int y, int width, int height,
+ BBitmap *b, colour bg)
+{
+ /* XXX: This currently ignores the background colour supplied.
+ * Does this matter?
+ */
+
+ if (width == 0 || height == 0)
+ return true;
+
+ BView *view;
+
+ view = nsbeos_current_gc/*_lock*/();
+ if (view == NULL) {
+ beos_warn_user("No GC", 0);
+ return false;
+ }
+
+ drawing_mode oldmode = view->DrawingMode();
+ source_alpha alpha;
+ alpha_function func;
+ view->GetBlendingMode(&alpha, &func);
+ //view->SetDrawingMode(B_OP_OVER);
+ view->SetDrawingMode(B_OP_ALPHA);
+ view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
+
+ // XXX DrawBitmap() resamples if rect doesn't match,
+ // but doesn't do any filtering
+ // XXX: use Zeta API if available ?
+
+ BRect rect(x, y, x + width - 1, y + height - 1);
+ /*
+ rgb_color old = view->LowColor();
+ if (bg != NS_TRANSPARENT) {
+ view->SetLowColor(nsbeos_rgb_colour(bg));
+ view->FillRect(rect, B_SOLID_LOW);
+ }
+ */
+ view->DrawBitmap(b, rect);
+ // maybe not needed?
+ //view->SetLowColor(old);
+ view->SetBlendingMode(alpha, func);
+ view->SetDrawingMode(oldmode);
+
+ //nsbeos_current_gc_unlock();
+
+ return true;
+}
+
+
+bool nsbeos_plot_bitmap(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg,
+ bitmap_flags_t flags)
+{
+ int doneheight = 0, donewidth = 0;
+ BBitmap *primary;
+ BBitmap *pretiled;
+ bool repeat_x = (flags & BITMAPF_REPEAT_X);
+ bool repeat_y = (flags & BITMAPF_REPEAT_Y);
+
+ if (!(repeat_x || repeat_y)) {
+ /* Not repeating at all, so just plot it */
+ primary = nsbeos_bitmap_get_primary(bitmap);
+ return nsbeos_plot_bbitmap(x, y, width, height, primary, bg);
+ }
+
+ if (repeat_x && !repeat_y)
+ pretiled = nsbeos_bitmap_get_pretile_x(bitmap);
+ if (repeat_x && repeat_y)
+ pretiled = nsbeos_bitmap_get_pretile_xy(bitmap);
+ if (!repeat_x && repeat_y)
+ pretiled = nsbeos_bitmap_get_pretile_y(bitmap);
+ primary = nsbeos_bitmap_get_primary(bitmap);
+
+ /* use the primary and pretiled widths to scale the w/h provided */
+ width *= pretiled->Bounds().Width() + 1;
+ width /= primary->Bounds().Width() + 1;
+ height *= pretiled->Bounds().Height() + 1;
+ height /= primary->Bounds().Height() + 1;
+
+ BView *view;
+
+ view = nsbeos_current_gc/*_lock*/();
+ if (view == NULL) {
+ beos_warn_user("No GC", 0);
+ return false;
+ }
+
+ // XXX: do we really need to use clipping reg ?
+ // I guess it's faster to not draw clipped out stuff...
+
+ BRect cliprect;
+ BRegion clipreg;
+ view->GetClippingRegion(&clipreg);
+ cliprect = clipreg.Frame();
+
+ //XXX: FIXME
+
+ if (y > cliprect.top)
+ doneheight = ((int)cliprect.top - height) + ((y - (int)cliprect.top) % height);
+ else
+ doneheight = y;
+
+ while (doneheight < ((int)cliprect.bottom)) {
+ if (x > cliprect.left)
+ donewidth = ((int)cliprect.left - width) + ((x - (int)cliprect.left) % width);
+ else
+ donewidth = x;
+ while (donewidth < (cliprect.right)) {
+ nsbeos_plot_bbitmap(donewidth, doneheight,
+ width, height, pretiled, bg);
+ donewidth += width;
+ if (!repeat_x) break;
+ }
+ doneheight += height;
+ if (!repeat_y) break;
+ }
+
+#warning WRITEME
+ return true;
+}
+
+static BPoint transform_pt(float x, float y, const float transform[6])
+{
+#warning XXX: verify
+ //return BPoint(x, y);
+ BPoint pt;
+ pt.x = x * transform[0] + y * transform[1] + transform[4];
+ pt.y = x * transform[2] + y * transform[3] + transform[5];
+ /*
+ printf("TR: {%f, %f} { %f, %f, %f, %f, %f, %f} = { %f, %f }\n",
+ x, y,
+ transform[0], transform[1], transform[2],
+ transform[3], transform[4], transform[5],
+ pt.x, pt.y);
+ */
+ return pt;
+}
+
+bool nsbeos_plot_path(const float *p, unsigned int n, colour fill, float width,
+ colour c, const float transform[6])
+{
+ unsigned int i;
+
+ if (n == 0)
+ return true;
+
+ if (p[0] != PLOTTER_PATH_MOVE) {
+ LOG("path doesn't start with a move");
+ return false;
+ }
+
+ BShape shape;
+
+ for (i = 0; i < n; ) {
+ if (p[i] == PLOTTER_PATH_MOVE) {
+ BPoint pt(transform_pt(p[i + 1], p[i + 2], transform));
+ shape.MoveTo(pt);
+ i += 3;
+ } else if (p[i] == PLOTTER_PATH_CLOSE) {
+ shape.Close();
+ i++;
+ } else if (p[i] == PLOTTER_PATH_LINE) {
+ BPoint pt(transform_pt(p[i + 1], p[i + 2], transform));
+ shape.LineTo(pt);
+ i += 3;
+ } else if (p[i] == PLOTTER_PATH_BEZIER) {
+ BPoint pt[3] = {
+ transform_pt(p[i + 1], p[i + 2], transform),
+ transform_pt(p[i + 3], p[i + 4], transform),
+ transform_pt(p[i + 5], p[i + 6], transform)
+ };
+ shape.BezierTo(pt);
+ i += 7;
+ } else {
+ LOG("bad path command %f", p[i]);
+ return false;
+ }
+ }
+ shape.Close();
+
+ BView *view;
+
+ view = nsbeos_current_gc/*_lock*/();
+ if (view == NULL)
+ return false;
+
+ rgb_color old_high = view->HighColor();
+ float old_pen = view->PenSize();
+ view->SetPenSize(width);
+ view->MovePenTo(0, 0);
+ if (fill != NS_TRANSPARENT) {
+ view->SetHighColor(nsbeos_rgb_colour(fill));
+ view->FillShape(&shape);
+ }
+ if (c != NS_TRANSPARENT) {
+ view->SetHighColor(nsbeos_rgb_colour(c));
+ view->StrokeShape(&shape);
+ }
+ // restore
+ view->SetPenSize(old_pen);
+ view->SetHighColor(old_high);
+
+ //nsbeos_current_gc_unlock();
+
+ return true;
+}
+
+rgb_color nsbeos_rgb_colour(colour c)
+{
+ rgb_color color;
+ if (c == NS_TRANSPARENT)
+ return B_TRANSPARENT_32_BIT;
+ color.red = c & 0x0000ff;
+ color.green = (c & 0x00ff00) >> 8;
+ color.blue = (c & 0xff0000) >> 16;
+ return color;
+}
+
+void nsbeos_set_colour(colour c)
+{
+ rgb_color color = nsbeos_rgb_colour(c);
+ BView *view = nsbeos_current_gc();
+ view->SetHighColor(color);
+}
+
+/** Plot a caret. It is assumed that the plotters have been set up. */
+void nsbeos_plot_caret(int x, int y, int h)
+{
+ BView *view;
+
+ view = nsbeos_current_gc/*_lock*/();
+ if (view == NULL)
+ /* TODO: report an error here */
+ return;
+
+ BPoint start(x, y);
+ BPoint end(x, y + h - 1);
+#if defined(__HAIKU__) || defined(B_BEOS_VERSION_DANO)
+ view->SetHighColor(ui_color(B_DOCUMENT_TEXT_COLOR));
+#else
+ view->SetHighColor(kBlackColor);
+#endif
+ view->StrokeLine(start, end);
+
+ //nsbeos_current_gc_unlock();
+
+}
+
+#ifdef TEST_PLOTTERS
+//
+static void test_plotters(void)
+{
+ int x0, y0;
+ int x1, y1;
+ struct rect r;
+
+ x0 = 5;
+ y0 = 5;
+ x1 = 35;
+ y1 = 6;
+
+ plot.line(x0, y0, x1, y1, 1, 0x0000ff00, false, false);
+ y0+=2; y1+=2;
+ plot.line(x0, y0, x1, y1, 1, 0x0000ff00, true, false);
+ y0+=2; y1+=2;
+ plot.line(x0, y0, x1, y1, 1, 0x0000ff00, false, true);
+ y0+=2; y1+=2;
+ plot.line(x0, y0, x1, y1, 1, 0x0000ff00, true, true);
+ y0+=10; y1+=20;
+
+ plot.fill(x0, y0, x1, y1, 0x00ff0000);
+ plot.rectangle(x0+10, y0+10, x1-x0+1, y1-y0+1, 2, 0x00ffff00, true, false);
+ y0+=30; y1+=30;
+
+ r.x0 = x0 + 2;
+ r.y0 = y0 + 2;
+ r.x1 = x1 - 2;
+ r.y1 = y1 - 2;
+ plot.clip(&r);
+
+ plot.fill(x0, y0, x1, y1, 0x00000000);
+ plot.disc(x1, y1, 8, 0x000000ff, false);
+
+ r.x0 = 0;
+ r.y0 = 0;
+ r.x1 = 300;
+ r.y1 = 300;
+ plot.clip(&r);
+
+ y0+=30; y1+=30;
+
+}
+
+#include <Application.h>
+#include <View.h>
+#include <Window.h>
+class PTView : public BView {
+public:
+ PTView(BRect frame) : BView(frame, "view", B_FOLLOW_NONE, B_WILL_DRAW) {};
+ virtual ~PTView() {};
+ virtual void Draw(BRect update)
+ {
+ test_plotters();
+ };
+
+};
+
+extern "C" void test_plotters_main(void);
+void test_plotters_main(void)
+{
+ BApplication app("application/x-vnd.NetSurf");
+ memcpy(&plot, &nsbeos_plotters, sizeof(plot));
+ BRect frame(0,0,300,300);
+ PTView *view = new PTView(frame);
+ frame.OffsetBySelf(100,100);
+ BWindow *win = new BWindow(frame, "NetSurfPlotterTest", B_TITLED_WINDOW, B_QUIT_ON_WINDOW_CLOSE);
+ win->AddChild(view);
+ nsbeos_current_gc_set(view);
+ win->Show();
+ app.Run();
+}
+#endif /* TEST_PLOTTERS */
+
diff --git a/frontends/beos/plotters.h b/frontends/beos/plotters.h
new file mode 100644
index 000000000..523f3c7c6
--- /dev/null
+++ b/frontends/beos/plotters.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Target independent plotting (BeOS interface).
+ */
+
+#ifndef NETSURF_BEOS_PLOTTERS_H
+#define NETSURF_BEOS_PLOTTERS_H 1
+
+extern "C" {
+
+struct plotter_table;
+
+extern const struct plotter_table nsbeos_plotters;
+
+}
+
+#include <View.h>
+
+extern BView *current_view;
+
+extern BView *nsbeos_current_gc(void);
+extern BView *nsbeos_current_gc_lock(void);
+extern void nsbeos_current_gc_unlock(void);
+extern void nsbeos_current_gc_set(BView *view);
+
+rgb_color nsbeos_rgb_colour(colour c);
+void nsbeos_set_colour(colour c);
+void nsbeos_plot_caret(int x, int y, int h);
+
+#endif /* NETSURF_GTK_PLOTTERS_H */
diff --git a/frontends/beos/res.h b/frontends/beos/res.h
new file mode 100644
index 000000000..278ffb545
--- /dev/null
+++ b/frontends/beos/res.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
diff --git a/frontends/beos/res.rdef b/frontends/beos/res.rdef
new file mode 100644
index 000000000..07012356c
--- /dev/null
+++ b/frontends/beos/res.rdef
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "res.h"
+
+/* files exported as resource: via rsrc:// mapping are added by the makefile */
+
+/* application flags (multiple launch) */
+resource(1, "BEOS:APP_FLAGS") (#'APPF') $"01000000";
+
+/* application MIME signature */
+resource(1, "BEOS:APP_SIG") (#'MIMS') "application/x-vnd.NetSurf";
+
+/* list of supported MIME types */
+resource(1, "BEOS:FILE_TYPES") message {
+ "types" = "text/html",
+ "types" = "image/gif",
+ "types" = "image/jpeg",
+ "types" = "application/x-vnd.Be-bookmark",
+ "types" = "text",
+ "types" = "application/x-vnd.Be-doc_bookmark",
+ "types" = "application/x-vnd.Be.URL.file",
+ "types" = "application/x-vnd.Be.URL.ftp",
+ "types" = "application/x-vnd.Be.URL.http",
+ "types" = "application/x-vnd.Be.URL.https"
+};
+
+/* BeOS large (32x32) icon */
+resource(101, "BEOS:L:STD_ICON") #'ICON' array {
+ $"FFFFFFFFFFFFFFFFFFFFFF0E0A00D600D6000AAF0EFFFFFFFFFFFFFFFFFFFFFF"
+ $"FFFFFFFFFFFFFFFF0E0A000001DED5D5D5DE020000090EFFFFFFFFFFFFFFFFFF"
+ $"FFFFFFFFFFFFFF0B0001B58D6666666C6C6C6C93B50200090EFFFFFFFFFFFFFF"
+ $"FFFFFFFFFF0E0000016D66666666666C6C6C6C6C6C6CB40104AFFFFFFFFFFFFF"
+ $"FFFFFFFF0E0001B503464666666666666C6C6C6C6C6C6C9303000DFFFFFFFFFF"
+ $"FFFFFF0E000267AEB5464666666666666C6C6C6C66666C7292DE000DFFFFFFFF"
+ $"FFFFFF070167464666464646666666666C666C6C1F1F66664092DE000EFFFFFF"
+ $"FFFF0B008E46464646464646666666666C403F1E3F3F3F3F6692930100FFFFFF"
+ $"FFFF00074646668D46464666666666666C663F3F3F3F3F3F6672927E000EFFFF"
+ $"FF0B006D6646670246464666666666666C663F3F3F3F3F3F3F1E6C923D00FFFF"
+ $"FF00D56666668E044646666666666666403F3F3F3F3F3F3F1F6692927E000EFF"
+ $"0E008E666666678E666666666666666C6C661E3F3F3F3F3F1D92929293000BFF"
+ $"0B016C66666666666666666666666C6C6C6C403F3F3F3F3F3F6C929292DE00FF"
+ $"08036C666666666666666666666C6C6C6C6C1E1D403F1E6C666C929292DD00FF"
+ $"00D56C6C66668D06666666666C6C6C6C6C6C6C6C6C1D667292929292927E03FF"
+ $"00D56C6C6C6C8D02666C6C6C6C6C6C6C6C6C6C6C6C6C729292929292927E03FF"
+ $"00D56C6C6C6C6CDE6C6C6C6C6C6C6C6C6C6C6C6C7272729292929292927E03FF"
+ $"00DE6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C7272729292929292925F0103FF"
+ $"09026C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C72727292929292929293010100FF"
+ $"0B008D6C6C6C6C8DD56C6C6C6C6C6C6C6C72727272929292929292927E3D29FF"
+ $"0E00B46C6C6C6C6C01936C6C6C6C6C6C72727292929292927E7E92925F000BFF"
+ $"FF00DE6C6C6C6C6CB5B46C6C6C6C727272729292929292DE297E9292DD020EFF"
+ $"FF0C0002B4726C6C6C6C7272727272727292B4DD7F929293929292920100FFFF"
+ $"FFFF00007F7272B4DDDD927293DDDEB49292DEDDB3929292929292DD000EFFFF"
+ $"FFFFAF00B4727293B47FDEB4937E7E93929292929292929292925F0009FFFFFF"
+ $"FFFFFF0A00B4929292929301939292929292929292929292925F0100FFFFFFFF"
+ $"FFFFFFFF0700B4929292927E9392929292929292929292925F01000EFFFFFFFF"
+ $"FFFFFFFFFF0700DD9292929292929292929292929292927E01000EFFFFFFFFFF"
+ $"FFFFFFFFFFFF0A003D7E9292927E7E9292929292925FDE00000EFFFFFFFFFFFF"
+ $"FFFFFFFFFFFFFF0D00003D7F5F5F017E92925F7EDE000300FFFFFFFFFFFFFFFF"
+ $"FFFFFFFFFFFFFFFFFF0D00030000000001010000000C0EFFFFFFFFFFFFFFFFFF"
+ $"FFFFFFFFFFFFFFFFFFFFFFFF0E0B000A0A0A0D0EFFFFFFFFFFFFFFFFFFFFFFFF"
+};
+
+/* BeOS small (16x16) icon */
+resource(101, "BEOS:M:STD_ICON") #'MICN' array {
+ $"FFFFFFFF0E0900020205000DFFFFFFFF"
+ $"FFFFFF09038E66666C6C93DD000EFFFF"
+ $"FFFF00B5AE4666666C6C666CB4050EFF"
+ $"FF098E464646666666401D4066B400FF"
+ $"0E05668E664666666C1E3F3F1D6CDDAF"
+ $"098E668E66666666661F3F3F1E729300"
+ $"006C66666666666C6C401E1E40729204"
+ $"026C6CB46C666C6C6C6C6C66929292DE"
+ $"026C6C8D6C6C6C6C6C6C72729292923D"
+ $"006C6C6C8D6C6C6C6C72729292927E04"
+ $"09946C6CB56C6C6C72729292DD935F00"
+ $"0E009492B49293B492B47E929292DE0D"
+ $"FF0A7F92937F939392929292927E00FF"
+ $"FFFF08DD92929292929292927E00FFFF"
+ $"FFFFFF0B017E93DD92927EDE09FFFFFF"
+ $"FFFFFFFF0E0B000500000A0EFFFFFFFF"
+};
+
+/* Haiku vector icon */
+resource(101, "BEOS:ICON") #'VICN' array {
+ $"6E6369660A02010203AE259A400198BFFA65AE20E548AF9547F2037F30A0FFFF"
+ $"00E8F1FDFFF131A0FFFB02000602B83B85BC57333C5733B83B8549EAE1481199"
+ $"00359FFFFFFFFFFF05FF05010400600500020002033D9F21B889EB3A4CEE3F48"
+ $"204AB4B1440D2D10339DFF51369381810DB3339DFF68020002013E7E6E26628C"
+ $"A83C7540569A4565A3CA24492BEEF3F70001EEF3F731017D828B95160204BFE6"
+ $"B413C670B413B95DB413B411BF78B411B92DB411C5C3BFE6CADDB95CCADDC66F"
+ $"CADDCBBCBF78CBBCC5C4CBBCB92E0204BC63B4EAC0E922B7DDB608B570BBF0B4"
+ $"C0B92EB61EBEB2BEDDBEE8BA56C007C363BDCBC5D0B7E2C67FBAA5C522B52002"
+ $"04BFDEB448C60DB448B9ADB448B4ADBF9AB4ADB955B4ADC5DABFDECAEDB9ABCA"
+ $"EDC60BCAEDCB0FBF9ACB0FC5DCCB0FB9570204BFDFB4D8C5BFB4D8B9FEB4D8B5"
+ $"3DBF9AB53DB9A5B53DC58ABFDFCA5CB9FCCA5CC5BDCA5CCA81BF9ACA81C58BCA"
+ $"81B9A70606BA0BBF11CB5CC71BCAFECB92C8F7CA15CAF2CCF9C666CA36C48EC4"
+ $"A7CA76C78DC943C2BECB40BF11CB5D0A04BB16B6B2BA9C2EB9CFB86BBA5AB65D"
+ $"0A04BA10BC6AB954BC62B96CBADFBA3EBAE70A04BA21BEB5BA5AC047B98DC071"
+ $"B93ABEF30A04BAFDC28BBB88C3E9BAD5C43EBA59C2CD0A04BD84C730BCE154BC"
+ $"1CC679BCAAC5F90A04BEF3C932C015CA3BBF51CAA0BE4FC9AB0A04B812C4E6B8"
+ $"1CC5AEB693C46DB6DEC3F70A04BA6AC535BBD4C545BBCDC617BAACC6020A04BE"
+ $"1EC565BF9AC51CBFB2C5E6BE5FC6340A04C1DCC4D3C348C459C381C524C244C5"
+ $"8D0A04C6D0C2EDC721C39FC5E5C45FC595C3AA0A04C8E6C175C9A042CA54C102"
+ $"C962C20E0611AAAAAAAE02C14FB955C2EAB96FC38BB7E6C47FB928C628B8C3C6"
+ $"04BA40C793BB18C64ABC15C6B4BDE0C50CBDCAC479BF41C372BE11C17FBE79C1"
+ $"FEBCB2C207BCB3C1F5BCB2C047BC1CC1A7BAEBC14EB9540205C03CC10EC030C0"
+ $"EFC0B9C250BFCBC606BE8FC591C21CC6E2C5DBC385C74DC4AAC5AAC35EC643BC"
+ $"27C7D2BF42C4B3B90CC27CB994C3B1BA28C084B8A30605EF03B6CFBA0EB779B7"
+ $"92B672BB6AB633BD29B598BCA2B7B3BE77BD4BBD3EC58CB654C86EB88FC42DB5"
+ $"45BC71B55CBF70B47FB920B65000000204CB77C91ACB76C797CB79CAACC293CB"
+ $"88C456CB8AC0CCCB86BAB3C975BAACCAF2BABAC7F6C286C704C0CCC704C44DC7"
+ $"04250A090115023FFFDCB41DBB3451734030CDBF6EE7C3CF400A000100023F88"
+ $"1AB4F02F34DE2C3F9E484326CF4422000A060112023CDC96413114C145263E69"
+ $"DE4C32CFCB0EE00A0301031A4002C300000000000040012FBD3E023BCFC439BF"
+ $"01178100040A0301031A4002C300000000000040012FBD3E023BCFC42F390117"
+ $"8200040A0301031A4002C300000000000040012FBD3E023BCFC41A2F01178200"
+ $"040A050105023EF4D13E9DFDBE66B43EB65E491E02C6E7CA0A0501051A3EE861"
+ $"3EABC8BE735C3EAAF54929C0C6EFBE1A3901178100040A050106023F4B963E44"
+ $"75BE44753F4B9648D9CDC72B1A0A0501061A3F4B963E4475BE44753F4B9648D9"
+ $"CDC72B1A1A3901178100040A0501061A3F4B963E4475BE44753F4B9648D9CDC7"
+ $"2B1A001901178300040A050107023F4B963E4475BE44753F4B9648D9CDC72B1A"
+ $"0A0501071A3F4B963E4475BE44753F4B9648D9CDC72B1A1A3901178100040A05"
+ $"0108023F4B963E4475BE44753F4B9648D9CDC72B1A0A0501081A3F4B963E4475"
+ $"BE44753F4B9648D9CDC72B1A1A3901178100040A0501081A3F4B963E4475BE44"
+ $"753F4B9648D9CDC72B1A001901178300040A050109023F70F53E0A00BE0A003F"
+ $"70F5485F8CC6CD790A0501091A3F70F53E0A00BE0A003F70F5485F8CC6CD791A"
+ $"3901178100040A05010A023F4B963E4475BD81043E8BD247F0BEC26F710A0501"
+ $"0A1A3F4B963E4475BD81043E8BD247F0BEC26F711A3901178100040A05010A1A"
+ $"3F4B963E4475BD81043E8BD247F0BEC26F71001901178300040A05010B023F7B"
+ $"673D4154BE6C8B3F3385495313C5FE290A05010B1A3F7B673D4154BE6C8B3F33"
+ $"85495313C5FE291A3901178100040A05010C023F70093E0B8EBE0B8E3F700948"
+ $"A7CCC721CF0A05010C1A3F70093E0B8EBE0B8E3F700948A7CCC721CF1A390117"
+ $"8100040A05010D023F70093E0B8EBE0B8E3F700948A7CCC721CF0A05010D1A3F"
+ $"70093E0B8EBE0B8E3F700948A7CCC721CF1A3901178100040A05010D1A3F7009"
+ $"3E0B8EBE0B8E3F700948A7CCC721CF001901178300040A05010E023F70093E0B"
+ $"8EBE0B8E3F700948A7CCC721CF0A05010E1A3F70093E0B8EBE0B8E3F700948A7"
+ $"CCC721CF1A3901178100040A05010F023FB1173D145ABD145A3FB117474282C6"
+ $"7CF80A05010F1A3FB1173D145ABD145A3FB117474282C67CF81A390117810004"
+ $"0A05010F1A3FB1173D145ABD145A3FB117474282C67CF8001901178300040A05"
+ $"0110023F68373CD78FBD05193F9ECF48132CC5E0A80A0501101A3F68373CD78F"
+ $"BD05193F9ECF48132CC5E0A81A3901178100040A020111000A0301031A4002C3"
+ $"00000000000040012FBD3E023BCFC400190117850004"
+};
+
+/* ZETA SVG icon descriptor */
+resource(101, "BEOS:D:STD_ICON") (#'iICO') $"4944585A055300";
+
+/* ZETA SVG compressed icon */
+resource(101, "BEOS:V:STD_ICON") #'zICO' array {
+ $"7A10000078DAED57DD6EDB3618BDEF5310CA4D03C40C7F3E8A6466B7C002AC37"
+ $"2C066CCD03B8B1EC6853A44052B3644FBFF351769C7601D68B41BB5814C03AA2"
+ $"8EA8C3F3FD5059BE7FB86DC47DD50F75D7AE0A2D5521AAF6BADBD4ED6E555C7D"
+ $"FA69110A318CEB76B36EBAB65A158FD550BC7FF76639DCEFC41FF566BC591525"
+ $"15E2A6AA7737E384AFBBA6EB17753B56FD5DD7ACC73C7353B7D5BAFFE5C38FC5"
+ $"BB3762B9C38F58DEADC71B4CFED860DE6DDD341727DB7C14B8391D9B55F131C8"
+ $"6085355299CB0C8D92C10BEDF3AFCE231137277C494AEAC3384569F49E3FE169"
+ $"9A099365E6C4772633F33C195F4EF34FE3F9B513FD28E6CFA3CAB15FB7C3B6EB"
+ $"6F57C5ED7AECEB87B75A928E670A7F135AE08D21B8B385B6D2913FE567CF5FF6"
+ $"A085CD3F60A4EF7EAF0E861CAE17D9F10B92D6C5F26990ADFDADABDB8BCFD57D"
+ $"D5FC9FCCEBD79B7ADD7CE053D58EA2C68A77FB0BE4F1015EB5F538AC8A2F43D5"
+ $"FF7AB7BEAE7E6EAF860A69FAB02AC0BA7ECCA77ECADDC3339FFEA64A49174C56"
+ $"B598A00ED2C4F20C0E292277CA790D49C3D8DD896EBB1DAA31CFCBD78B5C11AB"
+ $"E2E473D83AA4F7F90B54FD0D55A9E80FD4E5F9D70B7D3971BEF4CDDB93A7E59F"
+ $"BE16D15311A97C7C53445A3AEBC26B117D6717262309EB0CD287449A8518278D"
+ $"4F445295C260529BB0923208A36524E638811229CB64BD8C41E828A34DB69486"
+ $"D83A054CB2746C0EE964B5B49E39A01B5912CFA875821F1E9864C03846A0A094"
+ $"44CCB1D92D6F9275321856468E5FE5BCE0C980A3D450869743E551FD7FD8B7D1"
+ $"29BCF9BEBEFD6AF73F25E954D45FBBC68BC6B2E09449C02A5B02CD30C069C604"
+ $"1CA42B858619E9197D5E9921DB89CA8E90C39185DFBA942E66CC30F26D45E919"
+ $"7566891C6E477BBB900CCAB18DE84EC0F6492E3248A70379EE5813C494B23489"
+ $"1B2C9A629441735029705325CBC24088313D23CF2A1281837B68DCCA2794150A"
+ $"8D9C8C09D54668FFA50CC05A9A4C0958C7913EAF4C98640405CE438E64291CFA"
+ $"0970902AF2166493C9658F7DCB43F2913E6FC8D1080D875621FF48EA2828B71E"
+ $"6DB3366E64893981C7C384F7FC79757AE9598ECAD1B591B1A58CF32E1EFCBE23"
+ $"01E7AADAB3E78DB9934AF37B89C3CC9969789730874F0DC721577EAFF7C89E55"
+ $"A4CD5F3F882605DE78BCE668A2E3A09235ED2D06C7E5E847979EF1E7D589E70D"
+ $"37206C8A6C12FACEB4115A2E20BE5BF206E9F3D75EC8F8C09F5527BA0F3666B8"
+ $"A774227C5F46EEE6E8F2FC3D4A025F088025F726C8D7907FA4FF2B3297E7F8C7"
+ $"7F793EDCE3F417BB10DA0B"
+};
+
+/* toolbar icons from Zumi <http://zumi.xoom.it/myhaiku>
+ * (generated by Icon-O-Matic from the HFIV source file,
+ * can't be automated)
+ */
+
+resource(102, "forward_button") #'VICN' array {
+ $"6E6369660304006603005900020006020000003C6000C000000000004C000048"
+ $"A0000080FF80FF00B300010A0748353448343E223E222C342C3422030A000100"
+ $"30222201178322040A0101001001178322040A02010000"
+};
+
+resource(103, "back_button") #'VICN' array {
+ $"6E6369660304006603005900020006020000003C6000C000000000004C000048"
+ $"A0000080FF80FF00B300010A0722353622362C482C483E363E3648030A000100"
+ $"30222201178322040A0101001001178322040A02010000"
+};
+
+resource(104, "stop_button") #'VICN' array {
+ $"6E6369660304006603800000020006020000003C6000C000000000004C000048"
+ $"A00000FFABABFFD900000208022A40402A02043525BEE325B7D825253525B7D8"
+ $"25BEE33545B7D845BEE345453545BEE345B7D8030A0002000130222201178900"
+ $"040A010200011001178900040A02020100100117850004"
+};
+
+resource(105, "reload_button") #'VICN' array {
+ $"6E6369660404006603004080020006020000003A0000C000000000004C000046"
+ $"7FFF00ABD5FFFF006CD9020006020000003A0000C000000000004C0000467FFF"
+ $"FFAAD4FF00006CD9010606C60F482232383D2D3D2D3826222A2B2329224327BC"
+ $"B7B25A4327060A00010030222201178322040A0101001001178322040A020100"
+ $"000A00010012C00000000000000000C000004AC0004AC00001178422040A0101"
+ $"0012C00000000000000000C000004AA0004AA00001178422040A03010002C000"
+ $"00000000000000C000004AA0004AA000"
+};
+
+resource(106, "home_button") #'VICN' array {
+ $"6E6369660804006603800000020006020000003A8000C000000000004C000047"
+ $"000000FFABABFFD900000554020016020000003AC000C000000000004BE00048"
+ $"A00000FFFFE50300590002000602000000370000C000000000004C00004A5000"
+ $"0080FF80FF00B20003806040040A064836483035222230223635280A04484848"
+ $"42224222480A0542404234352A283428400A042C342C4032403234080A030102"
+ $"1001178400040A040102000A0101001001178402040A020100000A0501011001"
+ $"178402040A060101000A070103000A0701030240AAAA0000000000003E000045"
+ $"0000468000"
+};
+
diff --git a/frontends/beos/res/SearchEngines b/frontends/beos/res/SearchEngines
new file mode 120000
index 000000000..df5252e07
--- /dev/null
+++ b/frontends/beos/res/SearchEngines
@@ -0,0 +1 @@
+../../resources/SearchEngines \ No newline at end of file
diff --git a/frontends/beos/res/adblock.css b/frontends/beos/res/adblock.css
new file mode 120000
index 000000000..e3811f62b
--- /dev/null
+++ b/frontends/beos/res/adblock.css
@@ -0,0 +1 @@
+../../!NetSurf/Resources/AdBlock,f79 \ No newline at end of file
diff --git a/frontends/beos/res/beosdefault.css b/frontends/beos/res/beosdefault.css
new file mode 100644
index 000000000..9b457d448
--- /dev/null
+++ b/frontends/beos/res/beosdefault.css
@@ -0,0 +1,22 @@
+/*
+ * This file is part of NetSurf, http://netsurf-browser.org/
+ */
+
+/* Load base stylesheet. */
+
+/*@import "default.css";*/
+@import "rsrc:///default.css";
+
+/* Apply GTK specific rules. */
+
+input { font-size: 95%; border: medium inset #ddd; }
+input[type=button], input[type=reset], input[type=submit], button {
+ background-color: #ddd; border: medium outset #ddd; }
+input[type=checkbox], input[type=radio] { font-size: 105%; }
+input[type=file] { background-color: #ddd; border: medium inset #ddd; }
+
+select { background-color: #ddd; border: medium inset #ddd; font-size: 95%; }
+select:after { border-left:4px ridge #ddd; }
+
+textarea { font-size: 95%; border: medium inset #ddd; }
+
diff --git a/frontends/beos/res/ca-bundle.txt b/frontends/beos/res/ca-bundle.txt
new file mode 120000
index 000000000..ad2dd6b55
--- /dev/null
+++ b/frontends/beos/res/ca-bundle.txt
@@ -0,0 +1 @@
+../../!NetSurf/Resources/ca-bundle \ No newline at end of file
diff --git a/frontends/beos/res/credits.html b/frontends/beos/res/credits.html
new file mode 120000
index 000000000..ca85d3d27
--- /dev/null
+++ b/frontends/beos/res/credits.html
@@ -0,0 +1 @@
+en/credits.html \ No newline at end of file
diff --git a/frontends/beos/res/de/welcome.html b/frontends/beos/res/de/welcome.html
new file mode 120000
index 000000000..b2ddfc796
--- /dev/null
+++ b/frontends/beos/res/de/welcome.html
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/de/welcome.html,faf \ No newline at end of file
diff --git a/frontends/beos/res/default.css b/frontends/beos/res/default.css
new file mode 120000
index 000000000..6d2d4da5b
--- /dev/null
+++ b/frontends/beos/res/default.css
@@ -0,0 +1 @@
+../../!NetSurf/Resources/CSS,f79 \ No newline at end of file
diff --git a/frontends/beos/res/en/credits.html b/frontends/beos/res/en/credits.html
new file mode 120000
index 000000000..1ba17392b
--- /dev/null
+++ b/frontends/beos/res/en/credits.html
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/en/credits.html,faf \ No newline at end of file
diff --git a/frontends/beos/res/en/licence.html b/frontends/beos/res/en/licence.html
new file mode 120000
index 000000000..147dd6db2
--- /dev/null
+++ b/frontends/beos/res/en/licence.html
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/en/licence.html,faf \ No newline at end of file
diff --git a/frontends/beos/res/en/maps.html b/frontends/beos/res/en/maps.html
new file mode 120000
index 000000000..bb1eedd5a
--- /dev/null
+++ b/frontends/beos/res/en/maps.html
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/en/maps.html,faf \ No newline at end of file
diff --git a/frontends/beos/res/en/welcome.html b/frontends/beos/res/en/welcome.html
new file mode 120000
index 000000000..28362130a
--- /dev/null
+++ b/frontends/beos/res/en/welcome.html
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/en/welcome.html,faf \ No newline at end of file
diff --git a/frontends/beos/res/favicon.png b/frontends/beos/res/favicon.png
new file mode 120000
index 000000000..d968c3827
--- /dev/null
+++ b/frontends/beos/res/favicon.png
@@ -0,0 +1 @@
+../../gtk/res/favicon.png \ No newline at end of file
diff --git a/frontends/beos/res/icons b/frontends/beos/res/icons
new file mode 120000
index 000000000..4a0ebabc6
--- /dev/null
+++ b/frontends/beos/res/icons
@@ -0,0 +1 @@
+../../!NetSurf/Resources/Icons \ No newline at end of file
diff --git a/frontends/beos/res/internal.css b/frontends/beos/res/internal.css
new file mode 120000
index 000000000..e777d8ae1
--- /dev/null
+++ b/frontends/beos/res/internal.css
@@ -0,0 +1 @@
+../../!NetSurf/Resources/internal.css,f79 \ No newline at end of file
diff --git a/frontends/beos/res/it/credits.html b/frontends/beos/res/it/credits.html
new file mode 120000
index 000000000..6e1e15ed5
--- /dev/null
+++ b/frontends/beos/res/it/credits.html
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/it/credits.html,faf \ No newline at end of file
diff --git a/frontends/beos/res/it/licence.html b/frontends/beos/res/it/licence.html
new file mode 120000
index 000000000..3a7c056b6
--- /dev/null
+++ b/frontends/beos/res/it/licence.html
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/it/licence.html,faf \ No newline at end of file
diff --git a/frontends/beos/res/it/welcome.html b/frontends/beos/res/it/welcome.html
new file mode 120000
index 000000000..dea1e839c
--- /dev/null
+++ b/frontends/beos/res/it/welcome.html
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/it/welcome.html,faf \ No newline at end of file
diff --git a/frontends/beos/res/ja/welcome.html b/frontends/beos/res/ja/welcome.html
new file mode 120000
index 000000000..827796f02
--- /dev/null
+++ b/frontends/beos/res/ja/welcome.html
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/ja/welcome.html,faf \ No newline at end of file
diff --git a/frontends/beos/res/licence.html b/frontends/beos/res/licence.html
new file mode 120000
index 000000000..86f8c54bf
--- /dev/null
+++ b/frontends/beos/res/licence.html
@@ -0,0 +1 @@
+en/licence.html \ No newline at end of file
diff --git a/frontends/beos/res/license b/frontends/beos/res/license
new file mode 100644
index 000000000..6c2a58f90
--- /dev/null
+++ b/frontends/beos/res/license
@@ -0,0 +1,332 @@
+The source code, documentation, translatable messages files and UI
+definitions contained within NetSurf are licensed under the following terms:
+
+ NetSurf 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; version 2 of the License.
+
+ In addition, as a special exception, permission is granted to link the
+ code of this release of NetSurf with the OpenSSL project's "OpenSSL"
+ library (or with modified versions of it that use the same licence as
+ the "OpenSSL" library), and distribute the linked executables. You must
+ obey the GNU General Public License version 2 in all respects for all of
+ the code used other than "OpenSSL". If you modify the code, you may
+ extend this exception to your version of the code, but you are not
+ obligated to do so. If you do not wish to do so, delete this exception
+ statement from your version.
+
+All visual artwork contained within NetSurf is licensed under the terms of
+the MIT License.
+
+The full text of the MIT and GPL licenses are provided in Annex A and
+Annex B of this document.
+
+
+Annex A: The MIT License
+------------------------
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
+Annex B: The GNU General Public License
+---------------------------------------
+
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software
+ interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the
+Free Software Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS
+NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE
+LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS
+AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF
+ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OFMERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU
+ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY
+MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE
+TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
+BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISE
+OF THE POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
diff --git a/frontends/beos/res/maps.html b/frontends/beos/res/maps.html
new file mode 120000
index 000000000..a32f725fb
--- /dev/null
+++ b/frontends/beos/res/maps.html
@@ -0,0 +1 @@
+en/maps.html \ No newline at end of file
diff --git a/frontends/beos/res/netsurf.png b/frontends/beos/res/netsurf.png
new file mode 120000
index 000000000..0fbf42709
--- /dev/null
+++ b/frontends/beos/res/netsurf.png
@@ -0,0 +1 @@
+../../!NetSurf/Resources/netsurf.png,b60 \ No newline at end of file
diff --git a/frontends/beos/res/quirks.css b/frontends/beos/res/quirks.css
new file mode 120000
index 000000000..d9fb80334
--- /dev/null
+++ b/frontends/beos/res/quirks.css
@@ -0,0 +1 @@
+../../!NetSurf/Resources/Quirks,f79 \ No newline at end of file
diff --git a/frontends/beos/res/throbber/throbber0.png b/frontends/beos/res/throbber/throbber0.png
new file mode 100644
index 000000000..7c79be47b
--- /dev/null
+++ b/frontends/beos/res/throbber/throbber0.png
Binary files differ
diff --git a/frontends/beos/res/throbber/throbber1.png b/frontends/beos/res/throbber/throbber1.png
new file mode 100644
index 000000000..b5b83a465
--- /dev/null
+++ b/frontends/beos/res/throbber/throbber1.png
Binary files differ
diff --git a/frontends/beos/res/throbber/throbber2.png b/frontends/beos/res/throbber/throbber2.png
new file mode 100644
index 000000000..dc1019dbd
--- /dev/null
+++ b/frontends/beos/res/throbber/throbber2.png
Binary files differ
diff --git a/frontends/beos/res/throbber/throbber3.png b/frontends/beos/res/throbber/throbber3.png
new file mode 100644
index 000000000..5d458ac0b
--- /dev/null
+++ b/frontends/beos/res/throbber/throbber3.png
Binary files differ
diff --git a/frontends/beos/res/throbber/throbber4.png b/frontends/beos/res/throbber/throbber4.png
new file mode 100644
index 000000000..4940aba02
--- /dev/null
+++ b/frontends/beos/res/throbber/throbber4.png
Binary files differ
diff --git a/frontends/beos/res/throbber/throbber5.png b/frontends/beos/res/throbber/throbber5.png
new file mode 100644
index 000000000..4c70ba64b
--- /dev/null
+++ b/frontends/beos/res/throbber/throbber5.png
Binary files differ
diff --git a/frontends/beos/res/throbber/throbber6.png b/frontends/beos/res/throbber/throbber6.png
new file mode 100644
index 000000000..3242b06b4
--- /dev/null
+++ b/frontends/beos/res/throbber/throbber6.png
Binary files differ
diff --git a/frontends/beos/res/throbber/throbber7.png b/frontends/beos/res/throbber/throbber7.png
new file mode 100644
index 000000000..c4605101f
--- /dev/null
+++ b/frontends/beos/res/throbber/throbber7.png
Binary files differ
diff --git a/frontends/beos/res/throbber/throbber8.png b/frontends/beos/res/throbber/throbber8.png
new file mode 100644
index 000000000..68f451e5b
--- /dev/null
+++ b/frontends/beos/res/throbber/throbber8.png
Binary files differ
diff --git a/frontends/beos/res/welcome.html b/frontends/beos/res/welcome.html
new file mode 120000
index 000000000..1abdc5e8a
--- /dev/null
+++ b/frontends/beos/res/welcome.html
@@ -0,0 +1 @@
+en/welcome.html \ No newline at end of file
diff --git a/frontends/beos/scaffolding.cpp b/frontends/beos/scaffolding.cpp
new file mode 100644
index 000000000..630e059f2
--- /dev/null
+++ b/frontends/beos/scaffolding.cpp
@@ -0,0 +1,2332 @@
+/*
+ * Copyright 2015 Adrián Arroyo Calle <adrian.arroyocalle@gmail.com>
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#define __STDBOOL_H__ 1
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <BeBuild.h>
+#include <Bitmap.h>
+#include <Box.h>
+#include <Button.h>
+#include <Dragger.h>
+#include <Menu.h>
+#include <MenuBar.h>
+#include <MenuItem.h>
+#include <Node.h>
+#include <Path.h>
+#include <PopUpMenu.h>
+#include <Resources.h>
+#include <Roster.h>
+#include <Screen.h>
+#include <ScrollView.h>
+#include <String.h>
+#include <StringView.h>
+#include <TextControl.h>
+#include <View.h>
+#include <Window.h>
+
+#if defined(__HAIKU__)
+#include <IconUtils.h>
+#include "WindowStack.h"
+#endif
+
+#include <fs_attr.h>
+extern "C" {
+#include "content/content.h"
+#include "desktop/browser_history.h"
+#include "desktop/browser.h"
+#include "desktop/netsurf.h"
+#include "desktop/version.h"
+#include "desktop/searchweb.h"
+#include "desktop/search.h"
+#include "desktop/plotters.h"
+#include "utils/nsoption.h"
+#include "desktop/textinput.h"
+#include "render/form.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "utils/log.h"
+#include "utils/nsurl.h"
+#include "desktop/gui_clipboard.h"
+}
+#include "beos/about.h"
+#include "beos/bitmap.h"
+#include "beos/gui.h"
+#include "beos/plotters.h"
+#include "beos/scaffolding.h"
+#include "beos/gui_options.h"
+//#include "beos/completion.h"
+#include "beos/throbber.h"
+#include "beos/window.h"
+#include "beos/schedule.h"
+//#include "beos/download.h"
+#include "beos/cookies.h"
+
+#define TOOLBAR_HEIGHT 32
+#define DRAGGER_WIDTH 8
+
+struct beos_history_window;
+
+class NSIconTextControl;
+class NSBrowserWindow;
+class NSThrobber;
+
+struct beos_scaffolding {
+ NSBrowserWindow *window; // top-level container object
+
+ // top-level view, contains toolbar & top-level browser view
+ NSBaseView *top_view;
+
+ BMenuBar *menu_bar;
+
+ BPopUpMenu *popup_menu;
+
+#ifdef ENABLE_DRAGGER
+ BDragger *dragger;
+#endif
+
+ BView *tool_bar;
+
+ BControl *back_button;
+ BControl *forward_button;
+ BControl *stop_button;
+ BControl *reload_button;
+ BControl *home_button;
+
+ NSIconTextControl *url_bar;
+ BTextControl *search_bar;
+ //BMenuField *url_bar_completion;
+
+ NSThrobber *throbber;
+
+ BStringView *status_bar;
+
+ BScrollView *scroll_view;
+
+ struct beos_history_window *history_window;
+
+ int throb_frame;
+ struct gui_window *top_level;
+ int being_destroyed;
+
+ bool fullscreen;
+};
+
+struct beos_history_window {
+ struct beos_scaffolding *g;
+ BWindow *window;
+
+};
+
+struct menu_events {
+ const char *widget;
+};
+
+// passed to the replicant main thread
+struct replicant_thread_info {
+ char app[B_PATH_NAME_LENGTH];
+ BString url;
+ char *args[3];
+};
+
+
+static int open_windows = 0; /**< current number of open browsers */
+static NSBaseView *replicant_view = NULL; /**< if not NULL, the replicant View we are running NetSurf for */
+static sem_id replicant_done_sem = -1;
+static thread_id replicant_thread = -1;
+
+static void nsbeos_window_update_back_forward(struct beos_scaffolding *);
+static void nsbeos_throb(void *);
+static int32 nsbeos_replicant_main_thread(void *_arg);
+
+// in beos_gui.cpp
+extern int main(int argc, char** argv);
+
+// in fetch_rsrc.cpp
+extern BResources *gAppResources;
+
+// #pragma mark - class NSIconTextControl
+
+#define ICON_WIDTH 16
+
+class NSIconTextControl : public BTextControl {
+public:
+ NSIconTextControl(BRect frame, const char* name,
+ const char* label, const char* initialText,
+ BMessage* message,
+ uint32 resizeMode
+ = B_FOLLOW_LEFT | B_FOLLOW_TOP,
+ uint32 flags
+ = B_WILL_DRAW | B_NAVIGABLE | B_DRAW_ON_CHILDREN);
+virtual ~NSIconTextControl();
+
+virtual void FrameResized(float newWidth, float newHeight);
+virtual void Draw(BRect updateRect);
+virtual void DrawAfterChildren(BRect updateRect);
+virtual void AttachedToWindow();
+
+void SetBitmap(const BBitmap *bitmap);
+void FixupTextRect();
+
+private:
+ BPoint fIconOffset;
+ BRect fIconFrame;
+ const BBitmap *fIconBitmap;
+};
+
+NSIconTextControl::NSIconTextControl(BRect frame, const char* name,
+ const char* label, const char* initialText,
+ BMessage* message,
+ uint32 resizeMode,
+ uint32 flags)
+ : BTextControl(frame, name, label, initialText, message, resizeMode, flags),
+ fIconOffset(0,0),
+ fIconBitmap(NULL)
+{
+ BRect r(Bounds());
+ fIconFrame = r;
+ fIconFrame.right = fIconFrame.left + ICON_WIDTH - 1;
+ fIconFrame.bottom = fIconFrame.top + ICON_WIDTH - 1;
+ fIconFrame.OffsetBy((int32)((r.IntegerHeight() - ICON_WIDTH + 3) / 2),
+ (int32)((r.IntegerHeight() - ICON_WIDTH + 1) / 2));
+ FixupTextRect();
+}
+
+
+NSIconTextControl::~NSIconTextControl()
+{
+ delete fIconBitmap;
+}
+
+
+void
+NSIconTextControl::FrameResized(float newWidth, float newHeight)
+{
+ BTextControl::FrameResized(newWidth, newHeight);
+ FixupTextRect();
+}
+
+
+void
+NSIconTextControl::Draw(BRect updateRect)
+{
+ FixupTextRect();
+ BTextControl::Draw(updateRect);
+}
+
+
+void
+NSIconTextControl::DrawAfterChildren(BRect updateRect)
+{
+ BTextControl::DrawAfterChildren(updateRect);
+
+ PushState();
+
+ SetDrawingMode(B_OP_ALPHA);
+ DrawBitmap(fIconBitmap, fIconFrame);
+
+ //XXX: is this needed?
+ PopState();
+}
+
+
+void
+NSIconTextControl::AttachedToWindow()
+{
+ BTextControl::AttachedToWindow();
+ FixupTextRect();
+}
+
+
+void
+NSIconTextControl::SetBitmap(const BBitmap *bitmap)
+{
+ delete fIconBitmap;
+ fIconBitmap = NULL;
+
+ // keep a copy
+ if (bitmap)
+ fIconBitmap = new BBitmap(bitmap);
+ // invalidate just the icon area
+ Invalidate(fIconFrame);
+}
+
+
+void
+NSIconTextControl::FixupTextRect()
+{
+ // FIXME: this flickers on resize, quite ugly
+ BRect r(TextView()->TextRect());
+
+ // don't fix the fix
+ if (r.left > ICON_WIDTH)
+ return;
+
+ r.left += r.bottom - r.top;
+ TextView()->SetTextRect(r);
+}
+
+
+#undef ICON_WIDTH
+
+// #pragma mark - class NSResizeKnob
+
+class NSResizeKnob : public BView {
+public:
+ NSResizeKnob(BRect frame, BView *target);
+virtual ~NSResizeKnob();
+
+virtual void MouseDown(BPoint where);
+virtual void MouseUp(BPoint where);
+virtual void MouseMoved(BPoint where, uint32 code,
+ const BMessage* dragMessage);
+
+virtual void Draw(BRect updateRect);
+
+void SetBitmap(const BBitmap *bitmap);
+
+private:
+ const BBitmap *fBitmap;
+ BView *fTarget;
+ BPoint fOffset;
+};
+
+NSResizeKnob::NSResizeKnob(BRect frame, BView *target)
+ : BView(frame, "NSResizeKnob", B_FOLLOW_BOTTOM | B_FOLLOW_RIGHT, B_WILL_DRAW),
+ fBitmap(NULL),
+ fTarget(target),
+ fOffset(-1, -1)
+{
+ SetViewColor(0, 255, 0);
+}
+
+
+NSResizeKnob::~NSResizeKnob()
+{
+}
+
+
+void
+NSResizeKnob::MouseDown(BPoint where)
+{
+ SetMouseEventMask(B_POINTER_EVENTS,
+ B_NO_POINTER_HISTORY | B_LOCK_WINDOW_FOCUS);
+ fOffset = where;
+}
+
+
+void
+NSResizeKnob::MouseUp(BPoint where)
+{
+ fOffset.Set(-1, -1);
+}
+
+
+void
+NSResizeKnob::MouseMoved(BPoint where, uint32 code,
+ const BMessage* dragMessage)
+{
+ if (fOffset.x >= 0) {
+ fTarget->ResizeBy(where.x - fOffset.x, where.y - fOffset.y);
+ }
+}
+
+
+void
+NSResizeKnob::Draw(BRect updateRect)
+{
+ if (!fBitmap)
+ return;
+ DrawBitmap(fBitmap);
+}
+
+
+void
+NSResizeKnob::SetBitmap(const BBitmap *bitmap)
+{
+ fBitmap = bitmap;
+ Invalidate();
+}
+
+
+// #pragma mark - class NSThrobber
+
+class NSThrobber : public BView {
+public:
+ NSThrobber(BRect frame);
+virtual ~NSThrobber();
+
+virtual void MessageReceived(BMessage *message);
+virtual void Draw(BRect updateRect);
+void SetBitmap(const BBitmap *bitmap);
+
+private:
+ const BBitmap *fBitmap;
+};
+
+NSThrobber::NSThrobber(BRect frame)
+ : BView(frame, "NSThrobber", B_FOLLOW_TOP | B_FOLLOW_RIGHT, B_WILL_DRAW),
+ fBitmap(NULL)
+{
+}
+
+
+NSThrobber::~NSThrobber()
+{
+}
+
+
+void
+NSThrobber::MessageReceived(BMessage *message)
+{
+ BView::MessageReceived(message);
+}
+
+
+void
+NSThrobber::Draw(BRect updateRect)
+{
+ if (!fBitmap)
+ return;
+ DrawBitmap(fBitmap);
+}
+
+
+void
+NSThrobber::SetBitmap(const BBitmap *bitmap)
+{
+ fBitmap = bitmap;
+ Invalidate();
+}
+
+
+// #pragma mark - class NSBaseView
+
+
+NSBaseView::NSBaseView(BRect frame)
+ : BView(frame, "NetSurf", B_FOLLOW_ALL_SIDES,
+ 0 /*B_WILL_DRAW | B_NAVIGABLE | B_FRAME_EVENTS*/ /*| B_SUBPIXEL_PRECISE*/),
+ fScaffolding(NULL)
+{
+}
+
+NSBaseView::NSBaseView(BMessage *archive)
+ : BView(archive),
+ fScaffolding(NULL)
+{
+}
+
+
+NSBaseView::~NSBaseView()
+{
+ //beos_warn_user("~NSBaseView()", NULL);
+ if (replicated) {
+ BMessage *message = new BMessage(B_QUIT_REQUESTED);
+ nsbeos_pipe_message_top(message, NULL, fScaffolding);
+ while (acquire_sem(replicant_done_sem) == EINTR);
+ //debugger("plop");
+ status_t status = -1;
+ wait_for_thread(replicant_thread, &status);
+ }
+}
+
+
+void
+NSBaseView::MessageReceived(BMessage *message)
+{
+ switch (message->what) {
+ case B_SIMPLE_DATA:
+ case B_ABOUT_REQUESTED:
+ case B_ARGV_RECEIVED:
+ case B_REFS_RECEIVED:
+ case B_COPY:
+ case B_CUT:
+ case B_PASTE:
+ case B_SELECT_ALL:
+ //case B_MOUSE_WHEEL_CHANGED:
+ case B_UI_SETTINGS_CHANGED:
+ // NetPositive messages
+ case B_NETPOSITIVE_OPEN_URL:
+ case B_NETPOSITIVE_BACK:
+ case B_NETPOSITIVE_FORWARD:
+ case B_NETPOSITIVE_HOME:
+ case B_NETPOSITIVE_RELOAD:
+ case B_NETPOSITIVE_STOP:
+ case B_NETPOSITIVE_DOWN:
+ case B_NETPOSITIVE_UP:
+ // messages for top-level
+ case 'back':
+ case 'forw':
+ case 'stop':
+ case 'relo':
+ case 'home':
+ case 'urlc':
+ case 'urle':
+ case 'sear':
+ case 'menu':
+ case NO_ACTION:
+ case HELP_OPEN_CONTENTS:
+ case HELP_OPEN_GUIDE:
+ case HELP_OPEN_INFORMATION:
+ case HELP_OPEN_ABOUT:
+ case HELP_OPEN_LICENCE:
+ case HELP_LAUNCH_INTERACTIVE:
+ case HISTORY_SHOW_LOCAL:
+ case HISTORY_SHOW_GLOBAL:
+ case HOTLIST_ADD_URL:
+ case HOTLIST_SHOW:
+ case COOKIES_SHOW:
+ case COOKIES_DELETE:
+ case BROWSER_PAGE:
+ case BROWSER_PAGE_INFO:
+ case BROWSER_PRINT:
+ case BROWSER_NEW_WINDOW:
+ case BROWSER_VIEW_SOURCE:
+ case BROWSER_OBJECT:
+ case BROWSER_OBJECT_INFO:
+ case BROWSER_OBJECT_RELOAD:
+ case BROWSER_OBJECT_SAVE:
+ case BROWSER_OBJECT_EXPORT_SPRITE:
+ case BROWSER_OBJECT_SAVE_URL_URI:
+ case BROWSER_OBJECT_SAVE_URL_URL:
+ case BROWSER_OBJECT_SAVE_URL_TEXT:
+ case BROWSER_SAVE:
+ case BROWSER_SAVE_COMPLETE:
+ case BROWSER_EXPORT_DRAW:
+ case BROWSER_EXPORT_TEXT:
+ case BROWSER_SAVE_URL_URI:
+ case BROWSER_SAVE_URL_URL:
+ case BROWSER_SAVE_URL_TEXT:
+ case HOTLIST_EXPORT:
+ case HISTORY_EXPORT:
+ case BROWSER_NAVIGATE_HOME:
+ case BROWSER_NAVIGATE_BACK:
+ case BROWSER_NAVIGATE_FORWARD:
+ case BROWSER_NAVIGATE_UP:
+ case BROWSER_NAVIGATE_RELOAD:
+ case BROWSER_NAVIGATE_RELOAD_ALL:
+ case BROWSER_NAVIGATE_STOP:
+ case BROWSER_NAVIGATE_URL:
+ case BROWSER_SCALE_VIEW:
+ case BROWSER_FIND_TEXT:
+ case BROWSER_IMAGES_FOREGROUND:
+ case BROWSER_IMAGES_BACKGROUND:
+ case BROWSER_BUFFER_ANIMS:
+ case BROWSER_BUFFER_ALL:
+ case BROWSER_SAVE_VIEW:
+ case BROWSER_WINDOW_DEFAULT:
+ case BROWSER_WINDOW_STAGGER:
+ case BROWSER_WINDOW_COPY:
+ case BROWSER_WINDOW_RESET:
+ case TREE_NEW_FOLDER:
+ case TREE_NEW_LINK:
+ case TREE_EXPAND_ALL:
+ case TREE_EXPAND_FOLDERS:
+ case TREE_EXPAND_LINKS:
+ case TREE_COLLAPSE_ALL:
+ case TREE_COLLAPSE_FOLDERS:
+ case TREE_COLLAPSE_LINKS:
+ case TREE_SELECTION:
+ case TREE_SELECTION_EDIT:
+ case TREE_SELECTION_LAUNCH:
+ case TREE_SELECTION_DELETE:
+ case TREE_SELECT_ALL:
+ case TREE_CLEAR_SELECTION:
+ case TOOLBAR_BUTTONS:
+ case TOOLBAR_ADDRESS_BAR:
+ case TOOLBAR_THROBBER:
+ case TOOLBAR_EDIT:
+ case CHOICES_SHOW:
+ case APPLICATION_QUIT:
+ if (Window())
+ Window()->DetachCurrentMessage();
+ nsbeos_pipe_message_top(message, NULL, fScaffolding);
+ break;
+ default:
+ //message->PrintToStream();
+ BView::MessageReceived(message);
+ }
+}
+
+
+status_t
+NSBaseView::Archive(BMessage *archive, bool deep) const
+{
+ // force archiving only the base view
+ deep = false;
+ status_t err;
+ err = BView::Archive(archive, deep);
+ if (err < B_OK)
+ return err;
+ // add our own fields
+ // we try to reuse the same fields as NetPositive
+ archive->AddString("add_on", "application/x-vnd.NetSurf");
+ //archive->AddInt32("version", 2);
+ archive->AddString("url", fScaffolding->url_bar->Text());
+ archive->AddBool("openAsText", false);
+ archive->AddInt32("encoding", 258);
+ return err;
+}
+
+
+BArchivable *
+NSBaseView::Instantiate(BMessage *archive)
+{
+ if (!validate_instantiation(archive, "NSBaseView"))
+ return NULL;
+ const char *url;
+ if (archive->FindString("url", &url) < B_OK
+ || url == NULL || strlen(url) == 0) {
+ url = "about:";
+ }
+
+ struct replicant_thread_info *info = new replicant_thread_info;
+ info->url = BString(url);
+ if (nsbeos_find_app_path(info->app) < B_OK)
+ return NULL;
+ info->args[0] = info->app;
+ info->args[1] = (char *)info->url.String();
+ info->args[2] = NULL;
+ NSBaseView *view = new NSBaseView(archive);
+ replicant_view = view;
+ replicated = true;
+
+ //TODO:FIXME: fix replicants
+ // do as much as possible in this thread to avoid deadlocks
+
+ gui_init_replicant(2, info->args);
+
+ replicant_done_sem = create_sem(0, "NS Replicant created");
+ replicant_thread = spawn_thread(nsbeos_replicant_main_thread,
+ "NetSurf Main Thread", B_NORMAL_PRIORITY, info);
+ if (replicant_thread < B_OK) {
+ delete_sem(replicant_done_sem);
+ delete info;
+ delete view;
+ return NULL;
+ }
+ resume_thread(replicant_thread);
+ //XXX: deadlocks BeHappy
+ //while (acquire_sem(replicant_done_sem) == EINTR);
+
+ return view;
+}
+
+
+void
+NSBaseView::SetScaffolding(struct beos_scaffolding *scaf)
+{
+ fScaffolding = scaf;
+}
+
+
+// AttachedToWindow() is not enough to get the dragger and status bar
+// stick to the panel color
+void
+NSBaseView::AllAttached()
+{
+ BView::AllAttached();
+
+ struct beos_scaffolding *g = fScaffolding;
+ if (!g)
+ return;
+ // set targets to the topmost ns view
+ g->back_button->SetTarget(this);
+ g->forward_button->SetTarget(this);
+ g->stop_button->SetTarget(this);
+ g->reload_button->SetTarget(this);
+ g->home_button->SetTarget(this);
+
+ rgb_color c = ui_color(B_PANEL_BACKGROUND_COLOR);
+ SetViewColor(c);
+
+ g->tool_bar->SetViewColor(c);
+ g->back_button->SetViewColor(c);
+ g->back_button->SetLowColor(c);
+ g->forward_button->SetViewColor(c);
+ g->forward_button->SetLowColor(c);
+ g->stop_button->SetViewColor(c);
+ g->stop_button->SetLowColor(c);
+ g->reload_button->SetViewColor(c);
+ g->reload_button->SetLowColor(c);
+ g->home_button->SetViewColor(c);
+ g->home_button->SetLowColor(c);
+ g->url_bar->SetViewColor(c);
+ g->search_bar->SetViewColor(c);
+ g->throbber->SetViewColor(c);
+ g->scroll_view->SetViewColor(c);
+
+#ifdef ENABLE_DRAGGER
+ g->dragger->SetViewColor(c);
+#endif
+
+ g->status_bar->SetViewColor(c);
+ g->status_bar->SetLowColor(c);
+#if defined(__HAIKU__) || defined(B_DANO_VERSION)
+ g->status_bar->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
+#endif
+}
+
+
+// #pragma mark - class NSBrowserWindow
+
+
+NSBrowserWindow::NSBrowserWindow(BRect frame, struct beos_scaffolding *scaf)
+ : BWindow(frame, "NetSurf", B_DOCUMENT_WINDOW, 0),
+ fScaffolding(scaf)
+{
+}
+
+
+NSBrowserWindow::~NSBrowserWindow()
+{
+ if(activeWindow == this)
+ activeWindow = NULL;
+}
+
+
+void
+NSBrowserWindow::DispatchMessage(BMessage *message, BHandler *handler)
+{
+ BMessage *msg;
+ switch (message->what) {
+ case B_UI_SETTINGS_CHANGED:
+ msg = new BMessage(*message);
+ nsbeos_pipe_message_top(msg, this, fScaffolding);
+ break;
+ }
+ BWindow::DispatchMessage(message, handler);
+}
+
+
+void
+NSBrowserWindow::MessageReceived(BMessage *message)
+{
+ switch (message->what) {
+ case B_ARGV_RECEIVED:
+ case B_REFS_RECEIVED:
+ case B_UI_SETTINGS_CHANGED:
+ DetachCurrentMessage();
+ nsbeos_pipe_message_top(message, this, fScaffolding);
+ break;
+ default:
+ BWindow::MessageReceived(message);
+ }
+}
+
+bool
+NSBrowserWindow::QuitRequested(void)
+{
+ BWindow::QuitRequested();
+ BMessage *message = DetachCurrentMessage();
+ // BApplication::Quit() calls us directly...
+ if (message == NULL)
+ message = new BMessage(B_QUIT_REQUESTED);
+ nsbeos_pipe_message_top(message, this, fScaffolding);
+ return false; // we will Quit() ourselves from the main thread
+}
+
+
+void
+NSBrowserWindow::WindowActivated(bool active)
+{
+ if(active)
+ activeWindow = this;
+ else if(activeWindow == this)
+ activeWindow = NULL;
+}
+
+
+// #pragma mark - implementation
+
+int32 nsbeos_replicant_main_thread(void *_arg)
+{
+ struct replicant_thread_info *info = (struct replicant_thread_info *)_arg;
+ int32 ret = 0;
+
+ while (!nsbeos_done) {
+ nsbeos_gui_poll();
+ }
+
+ netsurf_exit();
+ delete info;
+ delete_sem(replicant_done_sem);
+ return ret;
+}
+
+
+/* event handlers and support functions for them */
+
+static void nsbeos_window_destroy_event(NSBrowserWindow *window, nsbeos_scaffolding *g, BMessage *event)
+{
+ LOG("Being Destroyed = %d", g->being_destroyed);
+
+ if (--open_windows == 0)
+ nsbeos_done = true;
+
+ if (window) {
+ window->Lock();
+ window->Quit();
+ }
+
+ if (!g->being_destroyed) {
+ g->being_destroyed = 1;
+ nsbeos_window_destroy_browser(g->top_level);
+ }
+}
+
+
+static void nsbeos_scaffolding_update_colors(nsbeos_scaffolding *g)
+{
+ if (!g->top_view->LockLooper())
+ return;
+ rgb_color c = ui_color(B_PANEL_BACKGROUND_COLOR);
+ g->top_view->SetViewColor(c);
+
+ g->tool_bar->SetViewColor(c);
+ g->back_button->SetViewColor(c);
+ g->forward_button->SetViewColor(c);
+ g->stop_button->SetViewColor(c);
+ g->reload_button->SetViewColor(c);
+ g->home_button->SetViewColor(c);
+ g->url_bar->SetViewColor(c);
+ g->search_bar->SetViewColor(c);
+ g->throbber->SetViewColor(c);
+ g->scroll_view->SetViewColor(c);
+
+#ifdef ENABLE_DRAGGER
+ g->dragger->SetViewColor(c);
+#endif
+
+ g->status_bar->SetViewColor(c);
+ g->status_bar->SetLowColor(c);
+#if defined(__HAIKU__) || defined(B_DANO_VERSION)
+ g->status_bar->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
+#endif
+ g->top_view->UnlockLooper();
+}
+
+
+/*static*/ BWindow*
+NSBrowserWindow::activeWindow = NULL;
+
+
+void nsbeos_scaffolding_dispatch_event(nsbeos_scaffolding *scaffold, BMessage *message)
+{
+ struct browser_window *bw;
+ bw = nsbeos_get_browser_for_gui(scaffold->top_level);
+ bool reloadAll = false;
+
+ LOG("nsbeos_scaffolding_dispatch_event() what = 0x%08lx", message->what);
+ switch (message->what) {
+ case B_QUIT_REQUESTED:
+ nsbeos_scaffolding_destroy(scaffold);
+ break;
+ case B_ABOUT_REQUESTED:
+ {
+ nsbeos_about(scaffold->top_level);
+ break;
+ }
+ case B_NETPOSITIVE_DOWN:
+ //XXX WRITEME
+ break;
+ case B_SIMPLE_DATA:
+ {
+ if (!message->HasRef("refs")) {
+ // XXX handle DnD
+ break;
+ }
+ // FALL THROUGH
+ // handle refs
+ }
+ case B_REFS_RECEIVED:
+ {
+ int32 i;
+ entry_ref ref;
+
+ for (i = 0; message->FindRef("refs", i, &ref) >= B_OK; i++) {
+ BString url("file://");
+ BPath path(&ref);
+ if (path.InitCheck() < B_OK)
+ break;
+
+ BNode node(path.Path());
+ if (node.InitCheck() < B_OK)
+ break;
+ if (node.IsSymLink()) {
+ // dereference the symlink
+ BEntry entry(path.Path(), true);
+ if (entry.InitCheck() < B_OK)
+ break;
+ if (entry.GetPath(&path) < B_OK)
+ break;
+ if (node.SetTo(path.Path()) < B_OK)
+ break;
+ }
+
+ attr_info ai;
+ if (node.GetAttrInfo("META:url", &ai) >= B_OK) {
+ char data[(size_t)ai.size + 1];
+ memset(data, 0, (size_t)ai.size + 1);
+ if (node.ReadAttr("META:url", B_STRING_TYPE, 0LL, data, (size_t)ai.size) < 4)
+ break;
+ url = data;
+ } else
+ url << path.Path();
+
+ nsurl *nsurl;
+ nserror error;
+
+ error = nsurl_create(url.String(), &nsurl);
+ if (error == NSERROR_OK) {
+ if (/*message->WasDropped() &&*/ i == 0) {
+ browser_window_navigate(bw, nsurl, NULL,
+ (browser_window_nav_flags)
+ (BW_NAVIGATE_HISTORY),
+ NULL, NULL, NULL);
+ } else {
+ error = browser_window_create(BW_CREATE_CLONE,
+ nsurl,
+ NULL,
+ bw,
+ NULL);
+ }
+ nsurl_unref(nsurl);
+ }
+ if (error != NSERROR_OK) {
+ beos_warn_user(messages_get_errorcode(error), 0);
+ }
+ }
+ break;
+ }
+ case B_ARGV_RECEIVED:
+ {
+ int32 i;
+ BString urltxt;
+ nsurl *url;
+ nserror error;
+
+ for (i = 1; message->FindString("argv", i, &urltxt) >= B_OK; i++) {
+ error = nsurl_create(urltxt.String(), &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_CLONE,
+ url,
+ NULL,
+ bw,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ beos_warn_user(messages_get_errorcode(error), 0);
+ }
+ }
+ break;
+ }
+ case B_UI_SETTINGS_CHANGED:
+ nsbeos_update_system_ui_colors();
+ nsbeos_scaffolding_update_colors(scaffold);
+ break;
+ case B_NETPOSITIVE_OPEN_URL:
+ {
+ BString url;
+ if (message->FindString("be:url", &url) < B_OK)
+ break;
+
+ nsurl *nsurl;
+ nserror error;
+
+ error = nsurl_create(url.String(), &nsurl);
+ if (error != NSERROR_OK) {
+ beos_warn_user(messages_get_errorcode(error), 0);
+ } else {
+ browser_window_navigate(bw,
+ nsurl,
+ NULL,
+ (browser_window_nav_flags)(BW_NAVIGATE_HISTORY | BW_NAVIGATE_UNVERIFIABLE),
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(nsurl);
+ }
+ break;
+ }
+ case B_COPY:
+ browser_window_key_press(bw, NS_KEY_COPY_SELECTION);
+ break;
+ case B_CUT:
+ browser_window_key_press(bw, NS_KEY_CUT_SELECTION);
+ break;
+ case B_PASTE:
+ browser_window_key_press(bw, NS_KEY_PASTE);
+ break;
+ case B_SELECT_ALL:
+ LOG("Selecting all text");
+ browser_window_key_press(bw, NS_KEY_SELECT_ALL);
+ break;
+ case B_NETPOSITIVE_BACK:
+ case BROWSER_NAVIGATE_BACK:
+ case 'back':
+ if (!browser_window_history_back_available(bw))
+ break;
+ browser_window_history_back(bw, false);
+ nsbeos_window_update_back_forward(scaffold);
+ break;
+ case B_NETPOSITIVE_FORWARD:
+ case BROWSER_NAVIGATE_FORWARD:
+ case 'forw':
+ if (!browser_window_history_forward_available(bw))
+ break;
+ browser_window_history_forward(bw, false);
+ nsbeos_window_update_back_forward(scaffold);
+ break;
+ case B_NETPOSITIVE_STOP:
+ case BROWSER_NAVIGATE_STOP:
+ case 'stop':
+ browser_window_stop(bw);
+ break;
+ case B_NETPOSITIVE_RELOAD:
+ case BROWSER_NAVIGATE_RELOAD_ALL:
+ case 'relo':
+ reloadAll = true;
+ // FALLTHRU
+ case BROWSER_NAVIGATE_RELOAD:
+ browser_window_reload(bw, reloadAll);
+ break;
+ case B_NETPOSITIVE_HOME:
+ case BROWSER_NAVIGATE_HOME:
+ case 'home':
+ {
+ nsurl *url;
+ nserror error;
+
+ static const char *addr = NETSURF_HOMEPAGE;
+
+ if (nsoption_charp(homepage_url) != NULL) {
+ addr = nsoption_charp(homepage_url);
+ }
+
+ error = nsurl_create(addr, &url);
+ if (error != NSERROR_OK) {
+ beos_warn_user(messages_get_errorcode(error), 0);
+ } else {
+ browser_window_navigate(bw,
+ url,
+ NULL,
+ (browser_window_nav_flags)(BW_NAVIGATE_HISTORY),
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ break;
+ }
+ case 'urle':
+ {
+ nsurl *url;
+ nserror error;
+ BString text;
+
+ if (!scaffold->url_bar->LockLooper())
+ break;
+
+ text = scaffold->url_bar->Text();
+ scaffold->scroll_view->Target()->MakeFocus();
+ scaffold->url_bar->UnlockLooper();
+
+ error = nsurl_create(text.String(), &url);
+ if (error != NSERROR_OK) {
+ beos_warn_user(messages_get_errorcode(error), 0);
+ } else {
+ browser_window_navigate(bw,
+ url,
+ NULL,
+ (browser_window_nav_flags)(BW_NAVIGATE_HISTORY),
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ break;
+ }
+ case 'urlc':
+ {
+ BString text;
+ if (!scaffold->url_bar->LockLooper())
+ break;
+ text = scaffold->url_bar->Text();
+ scaffold->url_bar->UnlockLooper();
+ //nsbeos_completion_update(text.String());
+ break;
+ }
+ case 'sear':
+ {
+ nserror ret;
+ nsurl* url;
+ BString text;
+ if (!scaffold->search_bar->LockLooper())
+ break;
+ text = scaffold->search_bar->Text();
+ scaffold->search_bar->UnlockLooper();
+
+ char t[PATH_MAX];
+ find_resource(t,"SearchEngines","./beos/res/SearchEngines");
+
+ search_web_init(&t[0]);
+
+ ret = search_web_omni(text.String(),SEARCH_WEB_OMNI_SEARCHONLY
+ ,&url);
+ if (ret == NSERROR_OK) {
+ ret = browser_window_create(
+ (browser_window_create_flags)(BW_CREATE_HISTORY | BW_CREATE_TAB),
+ url,
+ NULL,
+ bw,
+ NULL);
+ nsurl_unref(url);
+ }
+
+ if (ret != NSERROR_OK) {
+ beos_warn_user(messages_get_errorcode(ret), 0);
+ }
+
+ search_web_finalise();
+
+ break;
+ }
+/*
+ case 'menu':
+ {
+ menu_action action;
+ if (message->FindInt32("action", (int32 *)&action) < B_OK)
+ break;
+ switch (action) {
+ case NO_ACTION:
+ case HELP_OPEN_CONTENTS:
+ case HELP_OPEN_GUIDE:
+ case HELP_OPEN_INFORMATION:
+ case HELP_OPEN_ABOUT:
+ case HELP_LAUNCH_INTERACTIVE:
+
+ break;
+ }
+#warning XXX
+ break;
+ }
+*/
+ case NO_ACTION:
+ break;
+ case HELP_OPEN_CONTENTS:
+ break;
+ case HELP_OPEN_GUIDE:
+ break;
+ case HELP_OPEN_INFORMATION:
+ break;
+ case HELP_OPEN_ABOUT:
+ {
+ const char *goto_url = "about:credits";
+ nserror nserr;
+ nsurl *url;
+ nserr = nsurl_create(goto_url, &url);
+ if (nserr == NSERROR_OK) {
+ nserr = browser_window_navigate(bw,
+ url, NULL,
+ (browser_window_nav_flags)(BW_NAVIGATE_HISTORY),
+ NULL, NULL, NULL);
+ nsurl_unref(url);
+ }
+ if (nserr != NSERROR_OK) {
+ beos_warn_user(messages_get_errorcode(nserr), 0);
+ }
+ }
+ break;
+ case HELP_OPEN_LICENCE:
+ {
+ const char *goto_url = "about:licence";
+ nserror nserr;
+ nsurl *url;
+ nserr = nsurl_create(goto_url, &url);
+ if (nserr == NSERROR_OK) {
+ nserr = browser_window_navigate(bw,
+ url, NULL,
+ (browser_window_nav_flags)(BW_NAVIGATE_HISTORY),
+ NULL, NULL, NULL);
+ nsurl_unref(url);
+ }
+ if (nserr != NSERROR_OK) {
+ beos_warn_user(messages_get_errorcode(nserr), 0);
+ }
+ }
+ break;
+ case HELP_LAUNCH_INTERACTIVE:
+ break;
+ case HISTORY_SHOW_LOCAL:
+ break;
+ case HISTORY_SHOW_GLOBAL:
+ break;
+ case HOTLIST_ADD_URL:
+ break;
+ case HOTLIST_SHOW:
+ break;
+ case COOKIES_SHOW:
+ {
+ nsbeos_cookies_init();
+ break;
+ }
+ case COOKIES_DELETE:
+ {
+ nsbeos_cookies_init();
+ break;
+ }
+ case BROWSER_PAGE:
+ break;
+ case BROWSER_PAGE_INFO:
+ break;
+ case BROWSER_PRINT:
+ break;
+ case BROWSER_NEW_WINDOW:
+ {
+ BString text;
+ nsurl *url;
+ nserror error;
+
+ if (!scaffold->url_bar->LockLooper())
+ break;
+ text = scaffold->url_bar->Text();
+ scaffold->url_bar->UnlockLooper();
+
+ NSBrowserWindow::activeWindow = scaffold->window;
+
+ error = nsurl_create(text.String(), &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_CLONE,
+ url,
+ NULL,
+ bw,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ beos_warn_user(messages_get_errorcode(error), 0);
+ }
+ break;
+ }
+ case BROWSER_VIEW_SOURCE:
+ {
+ if (!bw || browser_window_has_content(bw) == false)
+ break;
+ nsbeos_gui_view_source(browser_window_get_content(bw));
+ break;
+ }
+ case BROWSER_OBJECT:
+ break;
+ case BROWSER_OBJECT_INFO:
+ break;
+ case BROWSER_OBJECT_RELOAD:
+ break;
+ case BROWSER_OBJECT_SAVE:
+ break;
+ case BROWSER_OBJECT_EXPORT_SPRITE:
+ break;
+ case BROWSER_OBJECT_SAVE_URL_URI:
+ break;
+ case BROWSER_OBJECT_SAVE_URL_URL:
+ break;
+ case BROWSER_OBJECT_SAVE_URL_TEXT:
+ break;
+ case BROWSER_SAVE:
+ break;
+ case BROWSER_SAVE_COMPLETE:
+ break;
+ case BROWSER_EXPORT_DRAW:
+ break;
+ case BROWSER_EXPORT_TEXT:
+ break;
+ case BROWSER_SAVE_URL_URI:
+ break;
+ case BROWSER_SAVE_URL_URL:
+ break;
+ case BROWSER_SAVE_URL_TEXT:
+ break;
+ case HOTLIST_EXPORT:
+ break;
+ case HISTORY_EXPORT:
+ break;
+ case B_NETPOSITIVE_UP:
+ case BROWSER_NAVIGATE_UP:
+ break;
+ case BROWSER_NAVIGATE_URL:
+ if (!scaffold->url_bar->LockLooper())
+ break;
+ scaffold->url_bar->MakeFocus();
+ scaffold->url_bar->UnlockLooper();
+ break;
+ case BROWSER_SCALE_VIEW:
+ break;
+ case BROWSER_FIND_TEXT:
+ break;
+ case BROWSER_IMAGES_FOREGROUND:
+ break;
+ case BROWSER_IMAGES_BACKGROUND:
+ break;
+ case BROWSER_BUFFER_ANIMS:
+ break;
+ case BROWSER_BUFFER_ALL:
+ break;
+ case BROWSER_SAVE_VIEW:
+ break;
+ case BROWSER_WINDOW_DEFAULT:
+ break;
+ case BROWSER_WINDOW_STAGGER:
+ break;
+ case BROWSER_WINDOW_COPY:
+ break;
+ case BROWSER_WINDOW_RESET:
+ break;
+ case TREE_NEW_FOLDER:
+ case TREE_NEW_LINK:
+ case TREE_EXPAND_ALL:
+ case TREE_EXPAND_FOLDERS:
+ case TREE_EXPAND_LINKS:
+ case TREE_COLLAPSE_ALL:
+ case TREE_COLLAPSE_FOLDERS:
+ case TREE_COLLAPSE_LINKS:
+ case TREE_SELECTION:
+ case TREE_SELECTION_EDIT:
+ case TREE_SELECTION_LAUNCH:
+ case TREE_SELECTION_DELETE:
+ case TREE_SELECT_ALL:
+ case TREE_CLEAR_SELECTION:
+ break;
+ case TOOLBAR_BUTTONS:
+ break;
+ case TOOLBAR_ADDRESS_BAR:
+ break;
+ case TOOLBAR_THROBBER:
+ break;
+ case TOOLBAR_EDIT:
+ break;
+ case CHOICES_SHOW:
+ break;
+ case APPLICATION_QUIT:
+ nsbeos_done = true;
+ break;
+ default:
+ break;
+ }
+}
+
+void nsbeos_scaffolding_destroy(nsbeos_scaffolding *scaffold)
+{
+ LOG("Being Destroyed = %d", scaffold->being_destroyed);
+ if (scaffold->being_destroyed) return;
+ scaffold->being_destroyed = 1;
+ nsbeos_window_destroy_event(scaffold->window, scaffold, NULL);
+}
+
+
+void nsbeos_window_update_back_forward(struct beos_scaffolding *g)
+{
+ struct browser_window *bw = nsbeos_get_browser_for_gui(g->top_level);
+
+ if (!g->top_view->LockLooper())
+ return;
+
+ g->back_button->SetEnabled(browser_window_history_back_available(bw));
+ g->forward_button->SetEnabled(browser_window_history_forward_available(bw));
+
+ g->top_view->UnlockLooper();
+
+}
+
+void nsbeos_throb(void *p)
+{
+ struct beos_scaffolding *g = (struct beos_scaffolding *)p;
+
+ if (g->throb_frame >= (nsbeos_throbber->nframes - 1))
+ g->throb_frame = 1;
+ else
+ g->throb_frame++;
+
+ if (!g->top_view->LockLooper())
+ return;
+
+ g->throbber->SetBitmap(nsbeos_throbber->framedata[g->throb_frame]);
+ g->throbber->Invalidate();
+
+ g->top_view->UnlockLooper();
+
+ beos_schedule(100, nsbeos_throb, p);
+
+}
+
+
+NSBrowserWindow *nsbeos_find_last_window(void)
+{
+ int32 i;
+ if (!be_app || !be_app->Lock())
+ return NULL;
+ for (i = be_app->CountWindows() - 1; i >= 0; i--) {
+ if (be_app->WindowAt(i) == NULL)
+ continue;
+ NSBrowserWindow *win;
+ win = dynamic_cast<NSBrowserWindow *>(be_app->WindowAt(i));
+ if (win) {
+ win->Lock();
+ be_app->Unlock();
+ return win;
+ }
+ }
+ be_app->Unlock();
+ return NULL;
+}
+
+NSBrowserWindow *nsbeos_get_bwindow_for_scaffolding(nsbeos_scaffolding *scaffold)
+{
+ return scaffold->window;
+}
+
+NSBaseView *nsbeos_get_baseview_for_scaffolding(nsbeos_scaffolding *scaffold)
+{
+ return scaffold->top_view;
+}
+
+static void recursively_set_menu_items_target(BMenu *menu, BHandler *handler)
+{
+ menu->SetTargetForItems(handler);
+ for (int i = 0; menu->ItemAt(i); i++) {
+ if (!menu->SubmenuAt(i))
+ continue;
+ recursively_set_menu_items_target(menu->SubmenuAt(i), handler);
+ }
+}
+
+void nsbeos_attach_toplevel_view(nsbeos_scaffolding *g, BView *view)
+{
+ LOG("Attaching view to scaffolding %p", g);
+
+ // this is a replicant,... and it went bad
+ if (!g->window) {
+ if (g->top_view->Looper() && !g->top_view->LockLooper())
+ return;
+ }
+
+ BRect rect(g->top_view->Bounds());
+ rect.top += TOOLBAR_HEIGHT;
+ rect.right -= B_V_SCROLL_BAR_WIDTH;
+ rect.bottom -= B_H_SCROLL_BAR_HEIGHT;
+
+ view->ResizeTo(rect.Width() /*+ 1*/, rect.Height() /*+ 1*/);
+ view->MoveTo(rect.LeftTop());
+
+
+ g->scroll_view = new BScrollView("NetSurfScrollView", view,
+ B_FOLLOW_ALL, 0, true, true, B_NO_BORDER);
+
+ g->top_view->AddChild(g->scroll_view);
+
+ // for replicants, add a NSResizeKnob to allow resizing
+ if (!g->window) {
+ BRect frame = g->scroll_view->Bounds();
+ frame.left = frame.right - B_V_SCROLL_BAR_WIDTH;
+ frame.top = frame.bottom - B_H_SCROLL_BAR_HEIGHT;
+ NSResizeKnob *knob = new NSResizeKnob(frame, g->top_view);
+ //TODO: set bitmap
+ g->scroll_view->AddChild(knob);
+ }
+
+ view->MakeFocus();
+
+ // resize the horiz scrollbar to make room for the status bar and add it.
+
+ BScrollBar *sb = g->scroll_view->ScrollBar(B_HORIZONTAL);
+ rect = sb->Frame();
+ float divider = rect.Width() + 1;
+ //divider /= 2;
+ divider *= 67.0/100; // 67%
+
+ sb->ResizeBy(-divider, 0);
+ sb->MoveBy(divider, 0);
+
+ rect.right = rect.left + divider - 1;
+
+ /*
+ BBox *statusBarBox = new BBox(rect, "StatusBarBox",
+ B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM,
+ B_WILL_DRAW | B_FRAME_EVENTS,
+ B_RAISED_BORDER);
+ */
+
+ g->status_bar->MoveTo(rect.LeftTop());
+ g->status_bar->ResizeTo(rect.Width() + 1, rect.Height() + 1);
+ g->scroll_view->AddChild(g->status_bar);
+ g->status_bar->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
+ g->status_bar->SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)) ;
+#if defined(__HAIKU__) || defined(B_DANO_VERSION)
+ g->status_bar->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
+#endif
+
+
+
+ // set targets to the topmost ns view,
+ // we might not have a window later (replicant ?)
+ // this won't work for replicants, since the base view isn't attached yet
+ // we'll redo this in NSBaseView::AllAttached
+ g->back_button->SetTarget(view);
+ g->forward_button->SetTarget(view);
+ g->stop_button->SetTarget(view);
+ g->reload_button->SetTarget(view);
+ g->home_button->SetTarget(view);
+
+ g->url_bar->SetTarget(view);
+ g->search_bar->SetTarget(view);
+
+ nsbeos_scaffolding_update_colors(g);
+
+ if (g->window) {
+ recursively_set_menu_items_target(g->menu_bar, view);
+
+ // add toolbar shortcuts
+ BMessage *message;
+
+ message = new BMessage('back');
+ message->AddPointer("scaffolding", g);
+ g->window->AddShortcut(B_LEFT_ARROW, 0, message, view);
+
+ message = new BMessage('forw');
+ message->AddPointer("scaffolding", g);
+ g->window->AddShortcut(B_RIGHT_ARROW, 0, message, view);
+
+ message = new BMessage('stop');
+ message->AddPointer("scaffolding", g);
+ g->window->AddShortcut('S', 0, message, view);
+
+ message = new BMessage('relo');
+ message->AddPointer("scaffolding", g);
+ g->window->AddShortcut('R', 0, message, view);
+
+ message = new BMessage('home');
+ message->AddPointer("scaffolding", g);
+ g->window->AddShortcut('H', 0, message, view);
+
+
+#if defined(__HAIKU__)
+ // Make sure the window is layouted and answering to events, but do not
+ // show it before it is actually resized
+ g->window->Hide();
+ g->window->Show();
+
+ if(NSBrowserWindow::activeWindow) {
+ BWindowStack stack(NSBrowserWindow::activeWindow);
+ stack.AddWindow(g->window);
+ }
+#endif
+ g->window->Show();
+
+ } else {
+ if (g->top_view->Looper())
+ g->top_view->UnlockLooper();
+ }
+
+
+}
+
+static BMenuItem *make_menu_item(const char *name, BMessage *message, bool enabled=false)
+{
+ BMenuItem *item;
+ BString label(messages_get(name));
+ BString accel;
+ uint32 mods = 0;
+ char key = 0;
+ // try to understand accelerators
+ int32 start = label.IFindLast(" ");
+ if (start > 0 && (label.Length() - start > 1)
+ && (label.Length() - start < 7)
+ && (label[start + 1] == 'F'
+ || !strcmp(label.String() + start + 1, "PRINT")
+ || label[start + 1] == '\xe2'
+ || label[start + 1] == '^')) {
+
+ label.MoveInto(accel, start + 1, label.Length());
+ // strip the trailing spaces
+ while (label[label.Length() - 1] == ' ')
+ label.Truncate(label.Length() - 1);
+
+ if (accel.FindFirst("\xe2\x87\x91") > -1) {
+ accel.RemoveFirst("\xe2\x87\x91");
+ mods |= B_SHIFT_KEY;
+ }
+ if (accel.FindFirst("^") > -1) {
+ accel.RemoveFirst("^");
+ mods |= B_CONTROL_KEY; // ALT!!!
+ }
+ if (accel.FindFirst("PRINT") > -1) {
+ accel.RemoveFirst("PRINT");
+ //mods |= ; // ALT!!!
+ key = B_PRINT_KEY;
+ }
+ if (accel.Length() > 1 && accel[0] == 'F') { // Function key
+ int num;
+ if (sscanf(accel.String(), "F%d", &num) > 0) {
+ //
+ }
+ } else if (accel.Length() > 0) {
+ key = accel[0];
+ }
+ //printf("MENU: detected accel '%s' mods 0x%08lx, key %d\n", accel.String(), mods, key);
+ }
+
+ // turn ... into ellipsis
+ label.ReplaceAll("...", B_UTF8_ELLIPSIS);
+
+ item = new BMenuItem(label.String(), message, key, mods);
+
+ item->SetEnabled(enabled);
+
+ return item;
+}
+
+
+class BBitmapButton: public BButton
+{
+ public:
+ BBitmapButton(BRect rect, const char* name, const char* label,
+ BMessage* message);
+ ~BBitmapButton();
+
+ void Draw(BRect updateRect);
+ void SetBitmap(const char* attrName);
+ private:
+ BBitmap* fBitmap;
+ BBitmap* fDisabledBitmap;
+};
+
+
+BBitmapButton::BBitmapButton(BRect rect, const char* name, const char* label,
+ BMessage* message)
+ : BButton(rect, name, label, message)
+{
+ SetBitmap(name);
+}
+
+
+BBitmapButton::~BBitmapButton()
+{
+ delete fBitmap;
+ delete fDisabledBitmap;
+}
+
+
+void BBitmapButton::Draw(BRect updateRect)
+{
+ if(fBitmap == NULL) {
+ BButton::Draw(updateRect);
+ return;
+ }
+
+ SetDrawingMode(B_OP_COPY);
+ FillRect(updateRect, B_SOLID_LOW);
+ rgb_color color = LowColor();
+
+ SetDrawingMode(B_OP_ALPHA);
+ if(IsEnabled()) {
+ if(Value() != 0) {
+ // button is clicked
+ DrawBitmap(fBitmap, BPoint(1, 1));
+ } else {
+ // button is released
+ DrawBitmap(fBitmap, BPoint(0, 0));
+ }
+ } else
+ DrawBitmap(fDisabledBitmap, BPoint(0, 0));
+}
+
+
+void BBitmapButton::SetBitmap(const char* attrname)
+{
+#ifdef __HAIKU__
+ size_t size = 0;
+ const void* data = gAppResources->LoadResource('VICN', attrname, &size);
+
+ if (!data) {
+ printf("CANT LOAD RESOURCE %s\n", attrname);
+ return;
+ }
+
+ fBitmap = new BBitmap(BRect(0, 0, 32, 32), B_RGB32);
+ status_t status = BIconUtils::GetVectorIcon((const uint8*)data, size, fBitmap);
+
+ if(status != B_OK) {
+ fprintf(stderr, "%s > oops %s\n", attrname, strerror(status));
+ delete fBitmap;
+ fBitmap = NULL;
+ }
+
+ fDisabledBitmap = new BBitmap(fBitmap);
+ rgb_color* pixel = (rgb_color*)fDisabledBitmap->Bits();
+ for(int i = 0; i < fDisabledBitmap->BitsLength()/4; i++)
+ {
+ *pixel = tint_color(*pixel, B_DISABLED_MARK_TINT);
+ pixel++;
+ }
+#else
+ // No vector icon support on BeOS. We could try to load a bitmap one
+ fBitmap = NULL;
+ fDisabledBitmap = NULL;
+#endif
+}
+
+
+nsbeos_scaffolding *nsbeos_new_scaffolding(struct gui_window *toplevel)
+{
+ struct beos_scaffolding *g = (struct beos_scaffolding *)malloc(sizeof(*g));
+
+ LOG("Constructing a scaffold of %p for gui_window %p", g, toplevel);
+
+ g->top_level = toplevel;
+ g->being_destroyed = 0;
+ g->fullscreen = false;
+
+ open_windows++;
+
+ BMessage *message;
+ BRect rect;
+
+ g->window = NULL;
+ g->menu_bar = NULL;
+
+ if (replicated && !replicant_view) {
+ beos_warn_user("Error: No subwindow allowed when replicated.", NULL);
+ return NULL;
+ }
+
+
+ if (!replicant_view) {
+ BRect frame(0, 0, 600-1, 500-1);
+ if (nsoption_int(window_width) > 0) {
+ frame.Set(0, 0, nsoption_int(window_width) - 1, nsoption_int(window_height) - 1);
+ frame.OffsetToSelf(nsoption_int(window_x), nsoption_int(window_y));
+ } else {
+ BPoint pos(50, 50);
+ // XXX: use last BApplication::WindowAt()'s dynamic_cast<NSBrowserWindow *> Frame()
+ NSBrowserWindow *win = nsbeos_find_last_window();
+ if (win) {
+ pos = win->Frame().LeftTop();
+ win->UnlockLooper();
+ }
+ pos += BPoint(20, 20);
+ BScreen screen;
+ BRect screenFrame(screen.Frame());
+ if (pos.y + frame.Height() >= screenFrame.Height()) {
+ pos.y = 50;
+ pos.x += 50;
+ }
+ if (pos.x + frame.Width() >= screenFrame.Width()) {
+ pos.x = 50;
+ pos.y = 50;
+ }
+ frame.OffsetToSelf(pos);
+ }
+
+ g->window = new NSBrowserWindow(frame, g);
+
+ rect = frame.OffsetToCopy(0,0);
+ rect.bottom = rect.top + 20;
+
+ // build menus
+ g->menu_bar = new BMenuBar(rect, "menu_bar");
+ g->window->AddChild(g->menu_bar);
+
+ BMenu *menu;
+ BMenuItem *item;
+
+ // App menu
+ //XXX: use icon item ?
+
+ menu = new BMenu(messages_get("NetSurf"));
+ g->menu_bar->AddItem(menu);
+
+ message = new BMessage(B_ABOUT_REQUESTED);
+ item = make_menu_item("Info", message, true);
+ menu->AddItem(item);
+
+#if 0
+ message = new BMessage(NO_ACTION);
+ item = make_menu_item("AppHelp", message);
+ menu->AddItem(item);
+
+ submenu = new BMenu(messages_get("Open"));
+ menu->AddItem(submenu);
+
+ message = new BMessage(NO_ACTION);
+ item = make_menu_item("OpenURL", message);
+ submenu->AddItem(item);
+
+ message = new BMessage(CHOICES_SHOW);
+ item = make_menu_item("Choices", message);
+ menu->AddItem(item);
+#endif
+
+ message = new BMessage(APPLICATION_QUIT);
+ item = make_menu_item("Quit", message, true);
+ menu->AddItem(item);
+
+ // Page menu
+
+ menu = new BMenu(messages_get("Page"));
+ g->menu_bar->AddItem(menu);
+
+#if 0
+ message = new BMessage(BROWSER_PAGE_INFO);
+ item = make_menu_item("PageInfo", message);
+ menu->AddItem(item);
+
+ message = new BMessage(BROWSER_SAVE);
+ item = make_menu_item("SaveAsNS", message);
+ menu->AddItem(item);
+
+ message = new BMessage(BROWSER_SAVE_COMPLETE);
+ item = make_menu_item("SaveCompNS", message);
+ menu->AddItem(item);
+
+ submenu = new BMenu(messages_get("Export"));
+ menu->AddItem(submenu);
+
+ /*
+ message = new BMessage(BROWSER_EXPORT_DRAW);
+ item = make_menu_item("Draw", message);
+ submenu->AddItem(item);
+ */
+
+ message = new BMessage(BROWSER_EXPORT_TEXT);
+ item = make_menu_item("LinkText", message);
+ submenu->AddItem(item);
+
+
+ submenu = new BMenu(messages_get("SaveURL"));
+ menu->AddItem(submenu);
+
+ //XXX
+ message = new BMessage(BROWSER_OBJECT_SAVE_URL_URL);
+ item = make_menu_item("URL", message);
+ submenu->AddItem(item);
+
+
+ message = new BMessage(BROWSER_PRINT);
+ item = make_menu_item("PrintNS", message);
+ menu->AddItem(item);
+#endif
+
+ message = new BMessage(BROWSER_NEW_WINDOW);
+ item = make_menu_item("NewWindowNS", message, true);
+ menu->AddItem(item);
+
+ message = new BMessage(BROWSER_VIEW_SOURCE);
+ item = make_menu_item("ViewSrc", message, true);
+ menu->AddItem(item);
+
+#if 0 // FIXME This is supposed to be a popup menu!
+ // Object menu
+
+ menu = new BMenu(messages_get("Object"));
+ g->menu_bar->AddItem(menu);
+
+ message = new BMessage(BROWSER_OBJECT_INFO);
+ item = make_menu_item("ObjInfo", message);
+ menu->AddItem(item);
+
+ message = new BMessage(BROWSER_OBJECT_SAVE);
+ item = make_menu_item("ObjSave", message);
+ menu->AddItem(item);
+ // XXX: submenu: Sprite ?
+
+ message = new BMessage(BROWSER_OBJECT_RELOAD);
+ item = make_menu_item("ObjReload", message);
+ menu->AddItem(item);
+#endif
+
+ // Navigate menu
+
+ menu = new BMenu(messages_get("Navigate"));
+ g->menu_bar->AddItem(menu);
+
+ message = new BMessage(BROWSER_NAVIGATE_HOME);
+ item = make_menu_item("Home", message, true);
+ menu->AddItem(item);
+
+ message = new BMessage(BROWSER_NAVIGATE_BACK);
+ item = make_menu_item("Back", message, true);
+ menu->AddItem(item);
+
+ message = new BMessage(BROWSER_NAVIGATE_FORWARD);
+ item = make_menu_item("Forward", message, true);
+ menu->AddItem(item);
+
+ message = new BMessage(BROWSER_NAVIGATE_UP);
+ item = make_menu_item("UpLevel", message);
+ menu->AddItem(item);
+
+ message = new BMessage(BROWSER_NAVIGATE_RELOAD);
+ item = make_menu_item("Reload", message, true);
+ menu->AddItem(item);
+
+ message = new BMessage(BROWSER_NAVIGATE_STOP);
+ item = make_menu_item("Stop", message, true);
+ menu->AddItem(item);
+
+#if 0
+ // View menu
+
+ menu = new BMenu(messages_get("View"));
+ g->menu_bar->AddItem(menu);
+
+ message = new BMessage(BROWSER_SCALE_VIEW);
+ item = make_menu_item("ScaleView", message);
+ menu->AddItem(item);
+
+ submenu = new BMenu(messages_get("Images"));
+ menu->AddItem(submenu);
+
+ message = new BMessage(BROWSER_IMAGES_FOREGROUND);
+ item = make_menu_item("ForeImg", message);
+ submenu->AddItem(item);
+
+ message = new BMessage(BROWSER_IMAGES_BACKGROUND);
+ item = make_menu_item("BackImg", message);
+ submenu->AddItem(item);
+
+
+ submenu = new BMenu(messages_get("Toolbars"));
+ menu->AddItem(submenu);
+ submenu->SetEnabled(false);
+
+ message = new BMessage(NO_ACTION);
+ item = make_menu_item("ToolButtons", message);
+ submenu->AddItem(item);
+
+ message = new BMessage(NO_ACTION);
+ item = make_menu_item("ToolAddress", message);
+ submenu->AddItem(item);
+
+ message = new BMessage(NO_ACTION);
+ item = make_menu_item("ToolThrob", message);
+ submenu->AddItem(item);
+
+ message = new BMessage(NO_ACTION);
+ item = make_menu_item("ToolStatus", message);
+ submenu->AddItem(item);
+
+
+ submenu = new BMenu(messages_get("Render"));
+ menu->AddItem(submenu);
+
+ message = new BMessage(BROWSER_BUFFER_ANIMS);
+ item = make_menu_item("RenderAnims", message);
+ submenu->AddItem(item);
+
+ message = new BMessage(BROWSER_BUFFER_ALL);
+ item = make_menu_item("RenderAll", message);
+ submenu->AddItem(item);
+
+
+ message = new BMessage(NO_ACTION);
+ item = make_menu_item("OptDefault", message);
+ menu->AddItem(item);
+#endif
+
+ // Utilities menu
+
+ menu = new BMenu(messages_get("Utilities"));
+ g->menu_bar->AddItem(menu);
+
+#if 0
+ submenu = new BMenu(messages_get("Hotlist"));
+ menu->AddItem(submenu);
+
+ message = new BMessage(HOTLIST_ADD_URL);
+ item = make_menu_item("HotlistAdd", message);
+ submenu->AddItem(item);
+
+ message = new BMessage(HOTLIST_SHOW);
+ item = make_menu_item("HotlistShowNS", message);
+ submenu->AddItem(item);
+
+
+ submenu = new BMenu(messages_get("History"));
+ menu->AddItem(submenu);
+
+ message = new BMessage(HISTORY_SHOW_LOCAL);
+ item = make_menu_item("HistLocal", message);
+ submenu->AddItem(item);
+
+ message = new BMessage(HISTORY_SHOW_GLOBAL);
+ item = make_menu_item("HistGlobal", message);
+ submenu->AddItem(item);
+#endif
+
+ message = new BMessage(COOKIES_SHOW);
+ item = make_menu_item("Cookie manager", message, true);
+ menu->AddItem(item);
+
+#if 0
+ message = new BMessage(BROWSER_FIND_TEXT);
+ item = make_menu_item("FindText", message);
+ menu->AddItem(item);
+
+ submenu = new BMenu(messages_get("Window"));
+ menu->AddItem(submenu);
+
+ message = new BMessage(BROWSER_WINDOW_DEFAULT);
+ item = make_menu_item("WindowSave", message);
+ submenu->AddItem(item);
+
+ message = new BMessage(BROWSER_WINDOW_STAGGER);
+ item = make_menu_item("WindowStagr", message);
+ submenu->AddItem(item);
+
+ message = new BMessage(BROWSER_WINDOW_COPY);
+ item = make_menu_item("WindowSize", message);
+ submenu->AddItem(item);
+
+ message = new BMessage(BROWSER_WINDOW_RESET);
+ item = make_menu_item("WindowReset", message);
+ submenu->AddItem(item);
+#endif
+
+
+ // Help menu
+
+ menu = new BMenu(messages_get("Help"));
+ g->menu_bar->AddItem(menu);
+
+#if 0
+ message = new BMessage(HELP_OPEN_CONTENTS);
+ item = make_menu_item("HelpContent", message);
+ menu->AddItem(item);
+
+ message = new BMessage(HELP_OPEN_GUIDE);
+ item = make_menu_item("HelpGuide", message);
+ menu->AddItem(item);
+
+ message = new BMessage(HELP_OPEN_INFORMATION);
+ item = make_menu_item("HelpInfo", message);
+ menu->AddItem(item);
+#endif
+
+ message = new BMessage(HELP_OPEN_ABOUT);
+ item = make_menu_item("HelpCredits", message, true);
+ menu->AddItem(item);
+
+ message = new BMessage(HELP_OPEN_LICENCE);
+ item = make_menu_item("HelpLicence", message, true);
+ menu->AddItem(item);
+
+#if 0
+ message = new BMessage(HELP_LAUNCH_INTERACTIVE);
+ item = make_menu_item("HelpInter", message);
+ menu->AddItem(item);
+#endif
+
+ // the base view that receives the toolbar, statusbar and top-level view.
+ rect = frame.OffsetToCopy(0,0);
+ rect.top = g->menu_bar->Bounds().Height() + 1;
+ //rect.top = 20 + 1; // XXX
+ //rect.bottom -= B_H_SCROLL_BAR_HEIGHT;
+ g->top_view = new NSBaseView(rect);
+ // add the top view to the window
+ g->window->AddChild(g->top_view);
+ } else { // replicant_view
+ // the base view has already been created with the archive constructor
+ g->top_view = replicant_view;
+ }
+ g->top_view->SetScaffolding(g);
+
+ // build popup menu
+ g->popup_menu = new BPopUpMenu("");
+
+
+#ifdef ENABLE_DRAGGER
+ // the dragger to allow replicating us
+ // XXX: try to stuff it in the status bar at the bottom
+ // (BDragger *must* be a parent, sibiling or direct child of NSBaseView!)
+ rect = g->top_view->Bounds();
+ rect.bottom = rect.top + TOOLBAR_HEIGHT - 1;
+ rect.left = rect.right - DRAGGER_WIDTH + 1;
+ g->dragger = new BDragger(rect, g->top_view,
+ B_FOLLOW_RIGHT | B_FOLLOW_TOP, B_WILL_DRAW);
+ g->top_view->AddChild(g->dragger);
+ g->dragger->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
+ g->dragger->SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)) ;
+#endif
+
+ // tool_bar
+ // the toolbar is also the dragger for now
+ // XXX: try to stuff it in the status bar at the bottom
+ // (BDragger *must* be a parent, sibiling or direct child of NSBaseView!)
+ // XXX: B_FULL_UPDATE_ON_RESIZE avoids leaving bits on resize,
+ // but causes flicker
+ rect = g->top_view->Bounds();
+ rect.bottom = rect.top + TOOLBAR_HEIGHT - 1;
+#ifdef ENABLE_DRAGGER
+ rect.right = rect.right - DRAGGER_WIDTH;
+#else
+ rect.right = rect.right + 1;
+#endif
+ g->tool_bar = new BBox(rect, "Toolbar",
+ B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP, B_WILL_DRAW | B_FRAME_EVENTS
+ | B_FULL_UPDATE_ON_RESIZE | B_NAVIGABLE_JUMP, B_PLAIN_BORDER);
+ g->top_view->AddChild(g->tool_bar);
+ g->tool_bar->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
+ g->tool_bar->SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)) ;
+
+ // buttons
+ rect = g->tool_bar->Bounds();
+ rect.right = TOOLBAR_HEIGHT;
+ rect.InsetBySelf(5, 5);
+ rect.OffsetBySelf(0, -1);
+ int nButtons = 0;
+
+ message = new BMessage('back');
+ message->AddPointer("scaffolding", g);
+ g->back_button = new BBitmapButton(rect, "back_button", "<", message);
+ g->tool_bar->AddChild(g->back_button);
+ nButtons++;
+
+ rect.OffsetBySelf(TOOLBAR_HEIGHT, 0);
+ message = new BMessage('forw');
+ message->AddPointer("scaffolding", g);
+ g->forward_button = new BBitmapButton(rect, "forward_button", ">", message);
+ g->tool_bar->AddChild(g->forward_button);
+ nButtons++;
+
+ rect.OffsetBySelf(TOOLBAR_HEIGHT, 0);
+ message = new BMessage('stop');
+ message->AddPointer("scaffolding", g);
+ g->stop_button = new BBitmapButton(rect, "stop_button", "S", message);
+ g->tool_bar->AddChild(g->stop_button);
+ nButtons++;
+
+ rect.OffsetBySelf(TOOLBAR_HEIGHT, 0);
+ message = new BMessage('relo');
+ message->AddPointer("scaffolding", g);
+ g->reload_button = new BBitmapButton(rect, "reload_button", "R", message);
+ g->tool_bar->AddChild(g->reload_button);
+ nButtons++;
+
+ rect.OffsetBySelf(TOOLBAR_HEIGHT, 0);
+ message = new BMessage('home');
+ message->AddPointer("scaffolding", g);
+ g->home_button = new BBitmapButton(rect, "home_button", "H", message);
+ g->tool_bar->AddChild(g->home_button);
+ nButtons++;
+
+
+ // url bar
+ rect = g->tool_bar->Bounds();
+ rect.left += TOOLBAR_HEIGHT * nButtons;
+ rect.right -= TOOLBAR_HEIGHT * 1 + 100;
+ rect.InsetBySelf(5, 5);
+ message = new BMessage('urle');
+ message->AddPointer("scaffolding", g);
+ g->url_bar = new NSIconTextControl(rect, "url_bar", "", "", message,
+ B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP);
+ g->url_bar->SetDivider(0);
+ rect = g->url_bar->TextView()->TextRect();
+ rect.left += 16;
+ g->url_bar->TextView()->SetTextRect(rect);
+ g->tool_bar->AddChild(g->url_bar);
+
+ // search bar
+
+ rect = g->tool_bar->Bounds();
+ rect.left = g->url_bar->Frame().right;
+ rect.right -= TOOLBAR_HEIGHT * 1;
+ rect.InsetBy(5,5);
+ message = new BMessage('sear');
+ message->AddPointer("scaffolding", g);
+ g->search_bar = new BTextControl(rect, "search_bar", "",
+ "Search" B_UTF8_ELLIPSIS, message, B_FOLLOW_RIGHT | B_FOLLOW_TOP);
+ g->search_bar->SetDivider(0);
+ g->tool_bar->AddChild(g->search_bar);
+
+ // throbber
+ rect.Set(0, 0, 24, 24);
+ rect.OffsetTo(g->tool_bar->Bounds().right - 24 - (TOOLBAR_HEIGHT - 24) / 2,
+ (TOOLBAR_HEIGHT - 24) / 2);
+ g->throbber = new NSThrobber(rect);
+ g->tool_bar->AddChild(g->throbber);
+ g->throbber->SetViewColor(g->tool_bar->ViewColor());
+ g->throbber->SetLowColor(g->tool_bar->ViewColor());
+ g->throbber->SetDrawingMode(B_OP_ALPHA);
+ g->throbber->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
+ /* set up the throbber. */
+ g->throbber->SetBitmap(nsbeos_throbber->framedata[0]);
+ g->throb_frame = 0;
+
+
+ // the status bar at the bottom
+ BString status("NetSurf");
+ status << " " << netsurf_version;
+ g->status_bar = new BStringView(BRect(0,0,-1,-1), "StatusBar",
+ status.String(), B_FOLLOW_LEFT/*_RIGHT*/ | B_FOLLOW_BOTTOM);
+
+ // will be added to the scrollview when adding the top view.
+
+ // notify the thread creating the replicant that we're done
+ if (replicant_view)
+ release_sem(replicant_done_sem);
+
+ replicant_view = NULL;
+
+ return g;
+}
+
+void gui_window_set_title(struct gui_window *_g, const char *title)
+{
+ struct beos_scaffolding *g = nsbeos_get_scaffold(_g);
+ if (g->top_level != _g) return;
+
+ // if we're a replicant, discard
+ if (!g->window)
+ return;
+
+ BString nt(title);
+ if (nt.Length())
+ nt << " - ";
+ nt << "NetSurf";
+
+ if (!g->top_view->LockLooper())
+ return;
+
+ g->window->SetTitle(nt.String());
+
+ g->top_view->UnlockLooper();
+}
+
+void gui_window_set_status(struct gui_window *_g, const char *text)
+{
+ struct beos_scaffolding *g = nsbeos_get_scaffold(_g);
+ assert(g);
+ assert(g->status_bar);
+
+ if (!g->top_view->LockLooper())
+ return;
+
+ if (text == NULL || text[0] == '\0')
+ {
+ BString status("NetSurf");
+ status << " " << netsurf_version;
+ g->status_bar->SetText(status.String());
+ }
+ else
+ {
+ g->status_bar->SetText(text);
+ }
+ g->top_view->UnlockLooper();
+}
+
+nserror gui_window_set_url(struct gui_window *gw, nsurl *url)
+{
+ struct beos_scaffolding *g;
+
+ g = nsbeos_get_scaffold(gw);
+ if (g->top_level != gw)
+ return NSERROR_OK;
+
+ assert(g->status_bar);
+
+ if (g->top_view->LockLooper()) {
+ g->url_bar->SetText(nsurl_access(url));
+
+ g->top_view->UnlockLooper();
+ }
+
+ return NSERROR_OK;
+}
+
+void gui_window_start_throbber(struct gui_window* _g)
+{
+ struct beos_scaffolding *g = nsbeos_get_scaffold(_g);
+
+ if (!g->top_view->LockLooper())
+ return;
+
+ g->stop_button->SetEnabled(true);
+ g->reload_button->SetEnabled(false);
+
+ g->top_view->UnlockLooper();
+
+ nsbeos_window_update_back_forward(g);
+
+ beos_schedule(100, nsbeos_throb, g);
+}
+
+void gui_window_stop_throbber(struct gui_window* _g)
+{
+ struct beos_scaffolding *g = nsbeos_get_scaffold(_g);
+
+ nsbeos_window_update_back_forward(g);
+
+ beos_schedule(-1, nsbeos_throb, g);
+
+ if (!g->top_view->LockLooper())
+ return;
+
+ g->stop_button->SetEnabled(false);
+ g->reload_button->SetEnabled(true);
+
+ g->throbber->SetBitmap(nsbeos_throbber->framedata[0]);
+ g->throbber->Invalidate();
+
+ g->top_view->UnlockLooper();
+}
+
+/**
+ * add retrieved favicon to the gui
+ */
+void gui_window_set_icon(struct gui_window *_g, hlcache_handle *icon)
+{
+ BBitmap *bitmap = NULL;
+ struct bitmap *bmp_icon;
+
+ bmp_icon = (icon != NULL) ? content_get_bitmap(icon) : NULL;
+
+ if (bmp_icon) {
+ bitmap = nsbeos_bitmap_get_primary(bmp_icon);
+ }
+
+ struct beos_scaffolding *g = nsbeos_get_scaffold(_g);
+
+ if (!g->top_view->LockLooper())
+ return;
+
+ g->url_bar->SetBitmap(bitmap);
+
+ g->top_view->UnlockLooper();
+}
+
+
+void nsbeos_scaffolding_popup_menu(nsbeos_scaffolding *g, BPoint where)
+{
+ g->popup_menu->Go(where);
+}
+
diff --git a/frontends/beos/scaffolding.h b/frontends/beos/scaffolding.h
new file mode 100644
index 000000000..3fdca57f6
--- /dev/null
+++ b/frontends/beos/scaffolding.h
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_BEOS_SCAFFOLDING_H
+#define NETSURF_BEOS_SCAFFOLDING_H 1
+
+#include <View.h>
+#include <Window.h>
+#include <NetPositive.h>
+
+extern "C" {
+struct hlcache_handle;
+struct nsurl;
+}
+
+typedef struct beos_scaffolding nsbeos_scaffolding;
+
+class NSBaseView : public BView {
+public:
+ NSBaseView(BRect frame);
+ NSBaseView(BMessage *archive);
+virtual ~NSBaseView();
+
+virtual void MessageReceived(BMessage *message);
+//virtual void Draw(BRect updateRect);
+
+//virtual void FrameMoved(BPoint new_location);
+//virtual void FrameResized(float new_width, float new_height);
+
+virtual void AllAttached(void);
+
+virtual status_t Archive(BMessage *archive, bool deep=true) const;
+static BArchivable *Instantiate(BMessage *archive);
+
+void SetScaffolding(struct beos_scaffolding *scaf);
+private:
+ struct beos_scaffolding *fScaffolding;
+};
+
+class NSBrowserWindow : public BWindow {
+public:
+ NSBrowserWindow(BRect frame, struct beos_scaffolding *scaf);
+virtual ~NSBrowserWindow();
+
+virtual void DispatchMessage(BMessage *message, BHandler *handler);
+virtual void MessageReceived(BMessage *message);
+virtual bool QuitRequested(void);
+void WindowActivated(bool active);
+
+struct beos_scaffolding *Scaffolding() const { return fScaffolding; };
+
+static BWindow* activeWindow;
+private:
+ struct beos_scaffolding *fScaffolding;
+
+
+};
+
+
+// XXX: clean up
+typedef enum {
+
+ /* no/unknown actions */
+ NO_ACTION = 'nsMA',
+
+ /* help actions */
+ HELP_OPEN_CONTENTS,
+ HELP_OPEN_GUIDE,
+ HELP_OPEN_INFORMATION,
+ HELP_OPEN_ABOUT,
+ HELP_OPEN_LICENCE,
+ HELP_LAUNCH_INTERACTIVE,
+
+ /* history actions */
+ HISTORY_SHOW_LOCAL,
+ HISTORY_SHOW_GLOBAL,
+
+ /* hotlist actions */
+ HOTLIST_ADD_URL,
+ HOTLIST_SHOW,
+
+ /* cookie actions */
+ COOKIES_SHOW,
+ COOKIES_DELETE,
+
+ /* page actions */
+ BROWSER_PAGE,
+ BROWSER_PAGE_INFO,
+ BROWSER_PRINT,
+ BROWSER_NEW_WINDOW,
+ BROWSER_VIEW_SOURCE,
+
+ /* object actions */
+ BROWSER_OBJECT,
+ BROWSER_OBJECT_INFO,
+ BROWSER_OBJECT_RELOAD,
+
+ /* save actions */
+ BROWSER_OBJECT_SAVE,
+ BROWSER_OBJECT_EXPORT_SPRITE,
+ BROWSER_OBJECT_SAVE_URL_URI,
+ BROWSER_OBJECT_SAVE_URL_URL,
+ BROWSER_OBJECT_SAVE_URL_TEXT,
+ BROWSER_SAVE,
+ BROWSER_SAVE_COMPLETE,
+ BROWSER_EXPORT_DRAW,
+ BROWSER_EXPORT_TEXT,
+ BROWSER_SAVE_URL_URI,
+ BROWSER_SAVE_URL_URL,
+ BROWSER_SAVE_URL_TEXT,
+ HOTLIST_EXPORT,
+ HISTORY_EXPORT,
+
+ /* navigation actions */
+ BROWSER_NAVIGATE_HOME,
+ BROWSER_NAVIGATE_BACK,
+ BROWSER_NAVIGATE_FORWARD,
+ BROWSER_NAVIGATE_UP,
+ BROWSER_NAVIGATE_RELOAD,
+ BROWSER_NAVIGATE_RELOAD_ALL,
+ BROWSER_NAVIGATE_STOP,
+ BROWSER_NAVIGATE_URL,
+
+ /* browser window/display actions */
+ BROWSER_SCALE_VIEW,
+ BROWSER_FIND_TEXT,
+ BROWSER_IMAGES_FOREGROUND,
+ BROWSER_IMAGES_BACKGROUND,
+ BROWSER_BUFFER_ANIMS,
+ BROWSER_BUFFER_ALL,
+ BROWSER_SAVE_VIEW,
+ BROWSER_WINDOW_DEFAULT,
+ BROWSER_WINDOW_STAGGER,
+ BROWSER_WINDOW_COPY,
+ BROWSER_WINDOW_RESET,
+
+ /* tree actions */
+ TREE_NEW_FOLDER,
+ TREE_NEW_LINK,
+ TREE_EXPAND_ALL,
+ TREE_EXPAND_FOLDERS,
+ TREE_EXPAND_LINKS,
+ TREE_COLLAPSE_ALL,
+ TREE_COLLAPSE_FOLDERS,
+ TREE_COLLAPSE_LINKS,
+ TREE_SELECTION,
+ TREE_SELECTION_EDIT,
+ TREE_SELECTION_LAUNCH,
+ TREE_SELECTION_DELETE,
+ TREE_SELECT_ALL,
+ TREE_CLEAR_SELECTION,
+
+ /* toolbar actions */
+ TOOLBAR_BUTTONS,
+ TOOLBAR_ADDRESS_BAR,
+ TOOLBAR_THROBBER,
+ TOOLBAR_EDIT,
+
+ /* misc actions */
+ CHOICES_SHOW,
+ APPLICATION_QUIT,
+} menu_action;
+
+
+NSBrowserWindow *nsbeos_find_last_window(void);
+
+NSBrowserWindow *nsbeos_get_bwindow_for_scaffolding(nsbeos_scaffolding *scaffold);
+
+NSBaseView *nsbeos_get_baseview_for_scaffolding(nsbeos_scaffolding *scaffold);
+
+nsbeos_scaffolding *nsbeos_new_scaffolding(struct gui_window *toplevel);
+
+bool nsbeos_scaffolding_is_busy(nsbeos_scaffolding *scaffold);
+
+void nsbeos_attach_toplevel_view(nsbeos_scaffolding *g, BView *view);
+
+
+void nsbeos_scaffolding_dispatch_event(nsbeos_scaffolding *scaffold, BMessage *message);
+
+void nsbeos_scaffolding_destroy(nsbeos_scaffolding *scaffold);
+
+//void nsbeos_window_destroy_event(NSBrowserWindow *window, nsbeos_scaffolding *g, BMessage *event);
+
+
+void nsbeos_scaffolding_popup_menu(nsbeos_scaffolding *g, BPoint where);
+
+void gui_window_set_title(struct gui_window *_g, const char *title);
+nserror gui_window_set_url(struct gui_window *_g, struct nsurl *url);
+void gui_window_set_icon(struct gui_window *_g, struct hlcache_handle *icon);
+void gui_window_set_status(struct gui_window *_g, const char *text);
+void gui_window_start_throbber(struct gui_window* _g);
+void gui_window_stop_throbber(struct gui_window* _g);
+
+#endif /* NETSURF_BEOS_SCAFFOLDING_H */
diff --git a/frontends/beos/schedule.cpp b/frontends/beos/schedule.cpp
new file mode 100644
index 000000000..a9da53501
--- /dev/null
+++ b/frontends/beos/schedule.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#define __STDBOOL_H__ 1
+#include <stdlib.h>
+#include <stdbool.h>
+#include <OS.h>
+#include <List.h>
+
+extern "C" {
+#include "utils/errors.h"
+#include "beos/schedule.h"
+#include "content/content.h"
+#include "desktop/browser.h"
+
+#ifdef DEBUG_BEOS_SCHEDULE
+#include "utils/log.h"
+#else
+#define LOG(x...)
+#endif
+}
+
+/** Killable callback closure embodiment. */
+typedef struct {
+ void (*callback)(void *); /**< The callback function. */
+ void *context; /**< The context for the callback. */
+ bool callback_killed; /**< Whether or not this was killed. */
+ bool callback_fired; /**< Whether or not this has fired yet. */
+ bigtime_t timeout;
+} _nsbeos_callback_t;
+
+/** List of all callbacks. */
+static BList *callbacks = NULL;
+
+/** earliest deadline. It's used for select() in gui_poll() */
+bigtime_t earliest_callback_timeout = B_INFINITE_TIMEOUT;
+
+
+static bool
+nsbeos_schedule_kill_callback(void *_target, void *_match)
+{
+ _nsbeos_callback_t *target = (_nsbeos_callback_t *)_target;
+ _nsbeos_callback_t *match = (_nsbeos_callback_t *)_match;
+ if ((target->callback == match->callback) &&
+ (target->context == match->context)) {
+ LOG("Found match for %p(%p), killing.", target->callback, target->context);
+ target->callback = NULL;
+ target->context = NULL;
+ target->callback_killed = true;
+ }
+ return false;
+}
+
+static void
+schedule_remove(void (*callback)(void *p), void *p)
+{
+ LOG("schedule_remove() for %p(%p)", cb->callback, cb->context);
+ if (callbacks == NULL)
+ return;
+ _nsbeos_callback_t cb_match;
+ cb_match.callback = callback;
+ cb_match.context = p;
+
+ callbacks->DoForEach(nsbeos_schedule_kill_callback, &cb_match);
+}
+
+nserror beos_schedule(int t, void (*callback)(void *p), void *p)
+{
+ LOG("t:%d cb:%p p:%p", t, cb->callback, cb->context);
+
+ if (callbacks == NULL) {
+ callbacks = new BList;
+ }
+
+ /* Kill any pending schedule of this kind. */
+ schedule_remove(callback, p);
+
+ if (t < 0) {
+ return NSERROR_OK;
+ }
+
+ bigtime_t timeout = system_time() + t * 1000LL;
+ _nsbeos_callback_t *cb = (_nsbeos_callback_t *)malloc(sizeof(_nsbeos_callback_t));
+ cb->callback = callback;
+ cb->context = p;
+ cb->callback_killed = cb->callback_fired = false;
+ cb->timeout = timeout;
+ if (earliest_callback_timeout > timeout) {
+ earliest_callback_timeout = timeout;
+ }
+ callbacks->AddItem(cb);
+
+ return NSERROR_OK;
+}
+
+bool
+schedule_run(void)
+{
+ LOG("schedule_run()");
+
+ earliest_callback_timeout = B_INFINITE_TIMEOUT;
+ if (callbacks == NULL)
+ return false; /* Nothing to do */
+
+ bigtime_t now = system_time();
+ int32 i;
+
+ LOG("Checking %ld callbacks to for deadline.", this_run->CountItems());
+
+ /* Run all the callbacks which made it this far. */
+ for (i = 0; i < callbacks->CountItems(); ) {
+ _nsbeos_callback_t *cb = (_nsbeos_callback_t *)(callbacks->ItemAt(i));
+ if (cb->timeout > now) {
+ // update next deadline
+ if (earliest_callback_timeout > cb->timeout)
+ earliest_callback_timeout = cb->timeout;
+ i++;
+ continue;
+ }
+ LOG("Running callbacks %p(%p).", cb->callback, cb->context);
+ if (!cb->callback_killed)
+ cb->callback(cb->context);
+ callbacks->RemoveItem(cb);
+ free(cb);
+ }
+ return true;
+}
diff --git a/frontends/beos/schedule.h b/frontends/beos/schedule.h
new file mode 100644
index 000000000..18f1efd4d
--- /dev/null
+++ b/frontends/beos/schedule.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_BEOS_CALLBACK_H
+#define NETSURF_BEOS_CALLBACK_H 1
+
+extern bigtime_t earliest_callback_timeout;
+
+extern "C" nserror beos_schedule(int t, void (*callback)(void *p), void *p);
+
+extern "C" bool schedule_run(void);
+
+
+#endif /* NETSURF_BEOS_CALLBACK_H */
diff --git a/frontends/beos/search.cpp b/frontends/beos/search.cpp
new file mode 100644
index 000000000..97e3d6d5b
--- /dev/null
+++ b/frontends/beos/search.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#define __STDBOOL_H__ 1
+//#include <stdbool.h>
+#include <string.h>
+
+extern "C" {
+#include "utils/log.h"
+}
+/* callback functions for search implementation */
+static void gui_search_set_status(bool found, void *p);
+static void gui_search_set_hourglass(bool active, void *p);
+static void gui_search_add_recent(const char *string, void *p);
+static void gui_search_set_forward_state(bool active, void *p);
+static void gui_search_set_back_state(bool active, void *p);
+
+/**
+ * Change the displayed search status.
+ * \param found search pattern matched in text
+ * \param p the pointer sent to search_verify_new() / search_create_context()
+ */
+void gui_search_set_status(bool found, void *p)
+{
+}
+
+/**
+ * display hourglass while searching
+ * \param active start/stop indicator
+ * \param p the pointer sent to search_verify_new() / search_create_context()
+ */
+void gui_search_set_hourglass(bool active, void *p)
+{
+}
+
+/**
+ * add search string to recent searches list
+ * \param string search pattern
+ * \param p the pointer sent to search_verify_new() / search_create_context()
+ */
+void gui_search_add_recent(const char *string, void *p)
+{
+}
+
+/**
+ * activate search forwards button in gui
+ * \param active activate/inactivate
+ * \param p the pointer sent to search_verify_new() / search_create_context()
+ */
+void gui_search_set_forward_state(bool active, void *p)
+{
+}
+
+/**
+ * activate search forwards button in gui
+ * \param active activate/inactivate
+ * \param p the pointer sent to search_verify_new() / search_create_context()
+ */
+void gui_search_set_back_state(bool active, void *p)
+{
+}
diff --git a/frontends/beos/throbber.cpp b/frontends/beos/throbber.cpp
new file mode 100644
index 000000000..fe40b3edc
--- /dev/null
+++ b/frontends/beos/throbber.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#define __STDBOOL_H__ 1
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+extern "C" {
+#include "utils/log.h"
+}
+#include "beos/throbber.h"
+#include "beos/bitmap.h"
+#include "beos/fetch_rsrc.h"
+
+#include <File.h>
+#include <Resources.h>
+#include <TranslationUtils.h>
+
+struct nsbeos_throbber *nsbeos_throbber = NULL;
+
+/**
+ * Creates the throbber using a PNG for each frame. The number of frames must
+ * be at least two. The first frame is the inactive frame, others are the
+ * active frames.
+ *
+ * \param frames The number of frames. Must be at least two.
+ * \param ... Filenames of PNGs containing frames.
+ * \return true on success.
+ */
+bool nsbeos_throbber_initialise_from_png(const int frames, ...)
+{
+ va_list filenames;
+ struct nsbeos_throbber *throb; /**< structure we generate */
+ bool errors_when_loading = false; /**< true if a frame failed */
+
+ if (frames < 2) {
+ /* we need at least two frames - one for idle, one for active */
+ LOG("Insufficent number of frames in throbber animation!");
+ LOG("(called with %d frames, where 2 is a minimum.)", frames);
+ return false;
+ }
+
+ BResources *res = get_app_resources();
+ if (res == NULL) {
+ LOG("Can't find resources for throbber!");
+ return false;
+ }
+
+ throb = (struct nsbeos_throbber *)malloc(sizeof(throb));
+ throb->nframes = frames;
+ throb->framedata = (BBitmap **)malloc(sizeof(BBitmap *) * throb->nframes);
+
+ va_start(filenames, frames);
+
+ for (int i = 0; i < frames; i++) {
+ const char *fn = va_arg(filenames, const char *);
+ const void *data;
+ size_t size;
+ data = res->LoadResource('data', fn, &size);
+ throb->framedata[i] = NULL;
+ if (!data) {
+ LOG("Error when loading resource %s", fn);
+ errors_when_loading = true;
+ continue;
+ }
+ BMemoryIO mem(data, size);
+ throb->framedata[i] = BTranslationUtils::GetBitmap(&mem);
+ if (throb->framedata[i] == NULL) {
+ LOG("Error when loading %s: GetBitmap() returned NULL", fn);
+ errors_when_loading = true;
+ }
+ }
+
+ va_end(filenames);
+
+ if (errors_when_loading == true) {
+ for (int i = 0; i < frames; i++) {
+ delete throb->framedata[i];
+ }
+
+ free(throb->framedata);
+ free(throb);
+
+ return false;
+ }
+
+ nsbeos_throbber = throb;
+
+ return true;
+}
+
+void nsbeos_throbber_finalise(void)
+{
+ int i;
+
+ for (i = 0; i < nsbeos_throbber->nframes; i++)
+ delete nsbeos_throbber->framedata[i];
+
+ free(nsbeos_throbber->framedata);
+ free(nsbeos_throbber);
+
+ nsbeos_throbber = NULL;
+}
diff --git a/frontends/beos/throbber.h b/frontends/beos/throbber.h
new file mode 100644
index 000000000..670c60a06
--- /dev/null
+++ b/frontends/beos/throbber.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 __BEOS_THROBBER_H__
+#define __BEOS_THROBBER_H__
+
+#include <Bitmap.h>
+
+struct nsbeos_throbber
+{
+ int nframes; /**< Number of frames in the throbber */
+ BBitmap **framedata;
+};
+
+extern struct nsbeos_throbber *nsbeos_throbber;
+
+bool nsbeos_throbber_initialise_from_gif(const char *fn);
+bool nsbeos_throbber_initialise_from_png(const int frames, ...);
+void nsbeos_throbber_finalise(void);
+
+#endif /* __BEOS_THROBBER_H__ */
diff --git a/frontends/beos/window.cpp b/frontends/beos/window.cpp
new file mode 100644
index 000000000..62624f077
--- /dev/null
+++ b/frontends/beos/window.cpp
@@ -0,0 +1,1382 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ * Copyright 2006 Daniel Silverstone <dsilvers@digital-scurf.org>
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#define __STDBOOL_H__ 1
+#include <stdlib.h>
+#include <assert.h>
+
+extern "C" {
+#include "utils/nsoption.h"
+#include "utils/log.h"
+#include "utils/utf8.h"
+#include "utils/utils.h"
+#include "content/content.h"
+#include "content/urldb.h"
+#include "desktop/browser.h"
+#include "desktop/mouse.h"
+#include "desktop/textinput.h"
+#include "desktop/plotters.h"
+#include "desktop/gui_window.h"
+#include "desktop/gui_clipboard.h"
+}
+
+#include "beos/about.h"
+#include "beos/window.h"
+#include "beos/font.h"
+#include "beos/gui.h"
+#include "beos/scaffolding.h"
+#include "beos/plotters.h"
+
+#include <AppDefs.h>
+#include <BeBuild.h>
+#include <Clipboard.h>
+#include <Cursor.h>
+#include <InterfaceDefs.h>
+#include <Message.h>
+#include <ScrollBar.h>
+#include <String.h>
+#include <String.h>
+#include <TextView.h>
+#include <View.h>
+#include <Window.h>
+
+class NSBrowserFrameView;
+
+struct gui_window {
+ /* All gui_window objects have an ultimate scaffold */
+ nsbeos_scaffolding *scaffold;
+ bool toplevel;
+ /* A gui_window is the rendering of a browser_window */
+ struct browser_window *bw;
+
+ struct {
+ int pressed_x;
+ int pressed_y;
+ int state; /* browser_mouse_state */
+ } mouse;
+
+ /* These are the storage for the rendering */
+ int caretx, carety, careth;
+ gui_pointer_shape current_pointer;
+ int last_x, last_y;
+
+ NSBrowserFrameView *view;
+
+ // some cached events to speed up things
+ // those are the last queued event of their kind,
+ // we can safely drop others and avoid wasting cpu.
+ // number of pending resizes
+ int32 pending_resizes;
+ // accumulated rects of pending redraws
+ //volatile BMessage *lastRedraw;
+ // UNUSED YET
+ BRect pendingRedraw;
+
+ /* Keep gui_windows in a list for cleanup later */
+ struct gui_window *next, *prev;
+
+ float scale;
+};
+
+
+
+static const rgb_color kWhiteColor = {255, 255, 255, 255};
+
+static struct gui_window *window_list = 0; /**< first entry in win list*/
+
+static BString current_selection;
+static BList current_selection_textruns;
+
+/* Methods which apply only to a gui_window */
+static void nsbeos_window_expose_event(BView *view, gui_window *g, BMessage *message);
+static void nsbeos_window_keypress_event(BView *view, gui_window *g, BMessage *event);
+static void nsbeos_window_resize_event(BView *view, gui_window *g, BMessage *event);
+static void nsbeos_window_moved_event(BView *view, gui_window *g, BMessage *event);
+/* Other useful bits */
+static void nsbeos_redraw_caret(struct gui_window *g);
+
+
+// #pragma mark - class NSBrowserFrameView
+
+
+NSBrowserFrameView::NSBrowserFrameView(BRect frame, struct gui_window *gui)
+ : BView(frame, "NSBrowserFrameView", B_FOLLOW_ALL_SIDES,
+ B_WILL_DRAW | B_NAVIGABLE | B_FRAME_EVENTS ),
+ fGuiWindow(gui)
+{
+}
+
+
+NSBrowserFrameView::~NSBrowserFrameView()
+{
+}
+
+
+void
+NSBrowserFrameView::MessageReceived(BMessage *message)
+{
+ switch (message->what) {
+ case B_SIMPLE_DATA:
+ case B_ARGV_RECEIVED:
+ case B_REFS_RECEIVED:
+ case B_COPY:
+ case B_CUT:
+ case B_PASTE:
+ case B_SELECT_ALL:
+ //case B_MOUSE_WHEEL_CHANGED:
+ case B_UI_SETTINGS_CHANGED:
+ // NetPositive messages
+ case B_NETPOSITIVE_OPEN_URL:
+ case B_NETPOSITIVE_BACK:
+ case B_NETPOSITIVE_FORWARD:
+ case B_NETPOSITIVE_HOME:
+ case B_NETPOSITIVE_RELOAD:
+ case B_NETPOSITIVE_STOP:
+ case B_NETPOSITIVE_DOWN:
+ case B_NETPOSITIVE_UP:
+ // messages for top-level
+ case 'back':
+ case 'forw':
+ case 'stop':
+ case 'relo':
+ case 'home':
+ case 'urlc':
+ case 'urle':
+ case 'sear':
+ case 'menu':
+ case NO_ACTION:
+ case HELP_OPEN_CONTENTS:
+ case HELP_OPEN_GUIDE:
+ case HELP_OPEN_INFORMATION:
+ case HELP_OPEN_ABOUT:
+ case HELP_LAUNCH_INTERACTIVE:
+ case HISTORY_SHOW_LOCAL:
+ case HISTORY_SHOW_GLOBAL:
+ case HOTLIST_ADD_URL:
+ case HOTLIST_SHOW:
+ case COOKIES_SHOW:
+ case COOKIES_DELETE:
+ case BROWSER_PAGE:
+ case BROWSER_PAGE_INFO:
+ case BROWSER_PRINT:
+ case BROWSER_NEW_WINDOW:
+ case BROWSER_VIEW_SOURCE:
+ case BROWSER_OBJECT:
+ case BROWSER_OBJECT_INFO:
+ case BROWSER_OBJECT_RELOAD:
+ case BROWSER_OBJECT_SAVE:
+ case BROWSER_OBJECT_EXPORT_SPRITE:
+ case BROWSER_OBJECT_SAVE_URL_URI:
+ case BROWSER_OBJECT_SAVE_URL_URL:
+ case BROWSER_OBJECT_SAVE_URL_TEXT:
+ case BROWSER_SAVE:
+ case BROWSER_SAVE_COMPLETE:
+ case BROWSER_EXPORT_DRAW:
+ case BROWSER_EXPORT_TEXT:
+ case BROWSER_SAVE_URL_URI:
+ case BROWSER_SAVE_URL_URL:
+ case BROWSER_SAVE_URL_TEXT:
+ case HOTLIST_EXPORT:
+ case HISTORY_EXPORT:
+ case BROWSER_NAVIGATE_HOME:
+ case BROWSER_NAVIGATE_BACK:
+ case BROWSER_NAVIGATE_FORWARD:
+ case BROWSER_NAVIGATE_UP:
+ case BROWSER_NAVIGATE_RELOAD:
+ case BROWSER_NAVIGATE_RELOAD_ALL:
+ case BROWSER_NAVIGATE_STOP:
+ case BROWSER_NAVIGATE_URL:
+ case BROWSER_SCALE_VIEW:
+ case BROWSER_FIND_TEXT:
+ case BROWSER_IMAGES_FOREGROUND:
+ case BROWSER_IMAGES_BACKGROUND:
+ case BROWSER_BUFFER_ANIMS:
+ case BROWSER_BUFFER_ALL:
+ case BROWSER_SAVE_VIEW:
+ case BROWSER_WINDOW_DEFAULT:
+ case BROWSER_WINDOW_STAGGER:
+ case BROWSER_WINDOW_COPY:
+ case BROWSER_WINDOW_RESET:
+ case TREE_NEW_FOLDER:
+ case TREE_NEW_LINK:
+ case TREE_EXPAND_ALL:
+ case TREE_EXPAND_FOLDERS:
+ case TREE_EXPAND_LINKS:
+ case TREE_COLLAPSE_ALL:
+ case TREE_COLLAPSE_FOLDERS:
+ case TREE_COLLAPSE_LINKS:
+ case TREE_SELECTION:
+ case TREE_SELECTION_EDIT:
+ case TREE_SELECTION_LAUNCH:
+ case TREE_SELECTION_DELETE:
+ case TREE_SELECT_ALL:
+ case TREE_CLEAR_SELECTION:
+ case TOOLBAR_BUTTONS:
+ case TOOLBAR_ADDRESS_BAR:
+ case TOOLBAR_THROBBER:
+ case TOOLBAR_EDIT:
+ case CHOICES_SHOW:
+ case APPLICATION_QUIT:
+ Window()->DetachCurrentMessage();
+ nsbeos_pipe_message_top(message, NULL, fGuiWindow->scaffold);
+ break;
+ default:
+ //message->PrintToStream();
+ BView::MessageReceived(message);
+ }
+}
+
+
+void
+NSBrowserFrameView::Draw(BRect updateRect)
+{
+ BMessage *message = NULL;
+ //message = Window()->DetachCurrentMessage();
+ // might be called directly...
+ if (message == NULL)
+ message = new BMessage(_UPDATE_);
+ message->AddRect("rect", updateRect);
+ nsbeos_pipe_message(message, this, fGuiWindow);
+}
+
+
+
+void
+NSBrowserFrameView::FrameResized(float new_width, float new_height)
+{
+ BMessage *message = Window()->DetachCurrentMessage();
+ // discard any other pending resize,
+ // so we don't end up processing them all, the last one matters.
+ atomic_add(&fGuiWindow->pending_resizes, 1);
+ nsbeos_pipe_message(message, this, fGuiWindow);
+ BView::FrameResized(new_width, new_height);
+}
+
+
+void
+NSBrowserFrameView::KeyDown(const char *bytes, int32 numBytes)
+{
+ BMessage *message = Window()->DetachCurrentMessage();
+ nsbeos_pipe_message(message, this, fGuiWindow);
+}
+
+
+void
+NSBrowserFrameView::MouseDown(BPoint where)
+{
+ BMessage *message = Window()->DetachCurrentMessage();
+ BPoint screenWhere;
+ if (message->FindPoint("screen_where", &screenWhere) < B_OK) {
+ screenWhere = ConvertToScreen(where);
+ message->AddPoint("screen_where", screenWhere);
+ }
+ nsbeos_pipe_message(message, this, fGuiWindow);
+}
+
+
+void
+NSBrowserFrameView::MouseUp(BPoint where)
+{
+ //BMessage *message = Window()->DetachCurrentMessage();
+ //nsbeos_pipe_message(message, this, fGuiWindow);
+ BMessage *message = Window()->DetachCurrentMessage();
+ BPoint screenWhere;
+ if (message->FindPoint("screen_where", &screenWhere) < B_OK) {
+ screenWhere = ConvertToScreen(where);
+ message->AddPoint("screen_where", screenWhere);
+ }
+ nsbeos_pipe_message(message, this, fGuiWindow);
+}
+
+
+void
+NSBrowserFrameView::MouseMoved(BPoint where, uint32 transit, const BMessage *msg)
+{
+ if (transit != B_INSIDE_VIEW) {
+ BView::MouseMoved(where, transit, msg);
+ return;
+ }
+ BMessage *message = Window()->DetachCurrentMessage();
+ nsbeos_pipe_message(message, this, fGuiWindow);
+}
+
+
+// #pragma mark - gui_window
+
+struct browser_window *nsbeos_get_browser_window(struct gui_window *g)
+{
+ return g->bw;
+}
+
+nsbeos_scaffolding *nsbeos_get_scaffold(struct gui_window *g)
+{
+ return g->scaffold;
+}
+
+struct browser_window *nsbeos_get_browser_for_gui(struct gui_window *g)
+{
+ return g->bw;
+}
+
+float nsbeos_get_scale_for_gui(struct gui_window *g)
+{
+ return g->scale;
+}
+
+/* Create a gui_window */
+static struct gui_window *gui_window_create(struct browser_window *bw,
+ struct gui_window *existing,
+ gui_window_create_flags flags)
+{
+ struct gui_window *g; /**< what we're creating to return */
+
+ g = (struct gui_window *)malloc(sizeof(*g));
+ if (!g) {
+ beos_warn_user("NoMemory", 0);
+ return 0;
+ }
+
+ LOG("Creating gui window %p for browser window %p", g, bw);
+
+ g->bw = bw;
+ g->mouse.state = 0;
+ g->current_pointer = GUI_POINTER_DEFAULT;
+ g->scale = browser_window_get_scale(bw);
+
+ g->careth = 0;
+ g->pending_resizes = 0;
+
+ /* Attach ourselves to the list (push_top) */
+ if (window_list)
+ window_list->prev = g;
+ g->next = window_list;
+ g->prev = NULL;
+ window_list = g;
+
+ /* Now construct and attach a scaffold */
+ g->scaffold = nsbeos_new_scaffolding(g);
+ if (!g->scaffold)
+ return NULL;
+
+ /* Construct our primary elements */
+ BRect frame(0,0,-1,-1); // will be resized later
+ g->view = new NSBrowserFrameView(frame, g);
+ /* set the default background colour of the drawing area to white. */
+ //g->view->SetViewColor(kWhiteColor);
+ /* NOOO! Since we defer drawing (DetachCurrent()), the white flickers,
+ * besides sometimes text was drawn twice, making it ugly.
+ * Instead we set to transparent here, and implement plot_clg() to
+ * do it just before the rest. This almost removes the flicker. */
+ g->view->SetViewColor(B_TRANSPARENT_COLOR);
+ g->view->SetLowColor(kWhiteColor);
+
+#ifdef B_BEOS_VERSION_DANO
+ /* enable double-buffering on the content view */
+/*
+ XXX: doesn't really work
+ g->view->SetDoubleBuffering(B_UPDATE_INVALIDATED
+ | B_UPDATE_SCROLLED
+ //| B_UPDATE_RESIZED
+ | B_UPDATE_EXPOSED);
+*/
+#endif
+
+
+ g->toplevel = true;
+
+ /* Attach our viewport into the scaffold */
+ nsbeos_attach_toplevel_view(g->scaffold, g->view);
+
+
+ return g;
+}
+
+/* exported interface documented in beos/window.h */
+void nsbeos_dispatch_event(BMessage *message)
+{
+ struct gui_window *gui = NULL;
+ NSBrowserFrameView *view = NULL;
+ struct beos_scaffolding *scaffold = NULL;
+ NSBrowserWindow *window = NULL;
+
+ //message->PrintToStream();
+ if (message->FindPointer("View", (void **)&view) < B_OK)
+ view = NULL;
+ if (message->FindPointer("gui_window", (void **)&gui) < B_OK)
+ gui = NULL;
+ if (message->FindPointer("Window", (void **)&window) < B_OK)
+ window = NULL;
+ if (message->FindPointer("scaffolding", (void **)&scaffold) < B_OK)
+ scaffold = NULL;
+
+ struct gui_window *z;
+ for (z = window_list; z && gui && z != gui; z = z->next)
+ continue;
+
+ struct gui_window *y;
+ for (y = window_list; y && scaffold && y->scaffold != scaffold; y = y->next)
+ continue;
+
+ if (gui && gui != z) {
+ LOG("discarding event for destroyed gui_window");
+ delete message;
+ return;
+ }
+ if (scaffold && (!y || scaffold != y->scaffold)) {
+ LOG("discarding event for destroyed scaffolding");
+ delete message;
+ return;
+ }
+
+ // messages for top-level
+ if (scaffold) {
+ LOG("dispatching to top-level");
+ nsbeos_scaffolding_dispatch_event(scaffold, message);
+ delete message;
+ return;
+ }
+
+ //LOG("processing message");
+ switch (message->what) {
+ case B_QUIT_REQUESTED:
+ // from the BApplication
+ nsbeos_done = true;
+ break;
+ case B_ABOUT_REQUESTED:
+ {
+ if (gui == NULL)
+ gui = window_list;
+ nsbeos_about(gui);
+ break;
+ }
+ case _UPDATE_:
+ if (gui && view)
+ nsbeos_window_expose_event(view, gui, message);
+ break;
+ case B_MOUSE_MOVED:
+ {
+ if (gui == NULL)
+ break;
+
+ BPoint where;
+ int32 mods;
+ // where refers to Window coords !?
+ // check be:view_where first
+ if (message->FindPoint("be:view_where", &where) < B_OK) {
+ if (message->FindPoint("where", &where) < B_OK)
+ break;
+ }
+ if (message->FindInt32("modifiers", &mods) < B_OK)
+ mods = 0;
+
+
+ if (gui->mouse.state & BROWSER_MOUSE_PRESS_1) {
+ /* Start button 1 drag */
+ browser_window_mouse_click(gui->bw, BROWSER_MOUSE_DRAG_1,
+ gui->mouse.pressed_x, gui->mouse.pressed_y);
+ /* Replace PRESS with HOLDING and declare drag in progress */
+ gui->mouse.state ^= (BROWSER_MOUSE_PRESS_1 |
+ BROWSER_MOUSE_HOLDING_1);
+ gui->mouse.state |= BROWSER_MOUSE_DRAG_ON;
+ } else if (gui->mouse.state & BROWSER_MOUSE_PRESS_2) {
+ /* Start button 2 drag */
+ browser_window_mouse_click(gui->bw, BROWSER_MOUSE_DRAG_2,
+ gui->mouse.pressed_x, gui->mouse.pressed_y);
+ /* Replace PRESS with HOLDING and declare drag in progress */
+ gui->mouse.state ^= (BROWSER_MOUSE_PRESS_2 |
+ BROWSER_MOUSE_HOLDING_2);
+ gui->mouse.state |= BROWSER_MOUSE_DRAG_ON;
+ }
+
+ bool shift = mods & B_SHIFT_KEY;
+ bool ctrl = mods & B_CONTROL_KEY;
+
+ /* Handle modifiers being removed */
+ if (gui->mouse.state & BROWSER_MOUSE_MOD_1 && !shift)
+ gui->mouse.state ^= BROWSER_MOUSE_MOD_1;
+ if (gui->mouse.state & BROWSER_MOUSE_MOD_2 && !ctrl)
+ gui->mouse.state ^= BROWSER_MOUSE_MOD_2;
+
+ browser_window_mouse_track(gui->bw, (browser_mouse_state)gui->mouse.state,
+ (int)(where.x / gui->scale),
+ (int)(where.y / gui->scale));
+
+ gui->last_x = (int)where.x;
+ gui->last_y = (int)where.y;
+ break;
+ }
+ case B_MOUSE_DOWN:
+ {
+ if (gui == NULL)
+ break;
+
+ BPoint where;
+ int32 buttons;
+ int32 mods;
+ BPoint screenWhere;
+ if (message->FindPoint("be:view_where", &where) < B_OK) {
+ if (message->FindPoint("where", &where) < B_OK)
+ break;
+ }
+ if (message->FindInt32("buttons", &buttons) < B_OK)
+ break;
+ if (message->FindPoint("screen_where", &screenWhere) < B_OK)
+ break;
+ if (message->FindInt32("modifiers", &mods) < B_OK)
+ mods = 0;
+
+ if (buttons & B_SECONDARY_MOUSE_BUTTON) {
+ /* 2 == right button on BeOS */
+
+ nsbeos_scaffolding_popup_menu(gui->scaffold, screenWhere);
+ break;
+ }
+
+ gui->mouse.state = BROWSER_MOUSE_PRESS_1;
+
+ if (buttons & B_TERTIARY_MOUSE_BUTTON) /* 3 == middle button on BeOS */
+ gui->mouse.state = BROWSER_MOUSE_PRESS_2;
+
+ if (mods & B_SHIFT_KEY)
+ gui->mouse.state |= BROWSER_MOUSE_MOD_1;
+ if (mods & B_CONTROL_KEY)
+ gui->mouse.state |= BROWSER_MOUSE_MOD_2;
+
+ gui->mouse.pressed_x = where.x / gui->scale;
+ gui->mouse.pressed_y = where.y / gui->scale;
+
+ // make sure the view is in focus
+ if (view && view->LockLooper()) {
+ if (!view->IsFocus())
+ view->MakeFocus();
+ view->UnlockLooper();
+ }
+
+ browser_window_mouse_click(gui->bw,
+ (browser_mouse_state)gui->mouse.state,
+ gui->mouse.pressed_x, gui->mouse.pressed_y);
+
+ break;
+ }
+ case B_MOUSE_UP:
+ {
+ if (gui == NULL)
+ break;
+
+ BPoint where;
+ int32 buttons;
+ int32 mods;
+ BPoint screenWhere;
+ if (message->FindPoint("be:view_where", &where) < B_OK) {
+ if (message->FindPoint("where", &where) < B_OK)
+ break;
+ }
+ if (message->FindInt32("buttons", &buttons) < B_OK)
+ break;
+ if (message->FindPoint("screen_where", &screenWhere) < B_OK)
+ break;
+ if (message->FindInt32("modifiers", &mods) < B_OK)
+ mods = 0;
+
+ /* If the mouse state is PRESS then we are waiting for a release to emit
+ * a click event, otherwise just reset the state to nothing*/
+ if (gui->mouse.state & BROWSER_MOUSE_PRESS_1)
+ gui->mouse.state ^= (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_CLICK_1);
+ else if (gui->mouse.state & BROWSER_MOUSE_PRESS_2)
+ gui->mouse.state ^= (BROWSER_MOUSE_PRESS_2 | BROWSER_MOUSE_CLICK_2);
+
+ bool shift = mods & B_SHIFT_KEY;
+ bool ctrl = mods & B_CONTROL_KEY;
+
+ /* Handle modifiers being removed */
+ if (gui->mouse.state & BROWSER_MOUSE_MOD_1 && !shift)
+ gui->mouse.state ^= BROWSER_MOUSE_MOD_1;
+ if (gui->mouse.state & BROWSER_MOUSE_MOD_2 && !ctrl)
+ gui->mouse.state ^= BROWSER_MOUSE_MOD_2;
+
+ /*
+ if (view && view->LockLooper()) {
+ view->MakeFocus();
+ view->UnlockLooper();
+ }
+ */
+
+ if (gui->mouse.state & (BROWSER_MOUSE_CLICK_1|BROWSER_MOUSE_CLICK_2))
+ browser_window_mouse_click(gui->bw,
+ (browser_mouse_state)gui->mouse.state,
+ where.x / gui->scale,
+ where.y / gui->scale);
+ else
+ browser_window_mouse_track(gui->bw, (browser_mouse_state)0,
+ where.x, where.y);
+
+ gui->mouse.state = 0;
+
+ break;
+ }
+ case B_KEY_DOWN:
+ if (gui && view)
+ nsbeos_window_keypress_event(view, gui, message);
+ break;
+ case B_VIEW_RESIZED:
+ if (gui && view)
+ nsbeos_window_resize_event(view, gui, message);
+ break;
+ case B_VIEW_MOVED:
+ if (gui && view)
+ nsbeos_window_moved_event(view, gui, message);
+ break;
+ case B_MOUSE_WHEEL_CHANGED:
+ break;
+ case B_UI_SETTINGS_CHANGED:
+ nsbeos_update_system_ui_colors();
+ break;
+ case 'nsLO': // login
+ {
+ nsurl* url;
+ BString realm;
+ BString auth;
+ void* cbpw;
+ nserror (*cb)(bool proceed, void* pw);
+
+ if (message->FindPointer("URL", (void**)&url) < B_OK)
+ break;
+ if (message->FindString("Realm", &realm) < B_OK)
+ break;
+ if (message->FindString("Auth", &auth) < B_OK)
+ break;
+ if (message->FindPointer("callback", (void**)&cb) < B_OK)
+ break;
+ if (message->FindPointer("callback_pw", (void**)&cbpw) < B_OK)
+ break;
+ //printf("login to '%s' with '%s'\n", url.String(), auth.String());
+ urldb_set_auth_details(url, realm.String(), auth.String());
+ cb(true, cbpw);
+ break;
+ }
+ default:
+ break;
+ }
+ delete message;
+}
+
+void nsbeos_window_expose_event(BView *view, gui_window *g, BMessage *message)
+{
+ BRect updateRect;
+ float scale = g->scale;
+ struct rect clip;
+
+ struct redraw_context ctx = { true, true, &nsbeos_plotters };
+
+ assert(g);
+ assert(g->bw);
+
+ struct gui_window *z;
+ for (z = window_list; z && z != g; z = z->next)
+ continue;
+ assert(z);
+ assert(g->view == view);
+
+ // we'll be resizing = reflowing = redrawing everything anyway...
+ if (g->pending_resizes > 1)
+ return;
+
+ if (message->FindRect("rect", &updateRect) < B_OK)
+ return;
+
+ if (browser_window_has_content(g->bw) == false)
+ return;
+
+ if (!view->LockLooper())
+ return;
+ nsbeos_current_gc_set(view);
+
+ if (view->Window())
+ view->Window()->BeginViewTransaction();
+
+ clip.x0 = (int)updateRect.left;
+ clip.y0 = (int)updateRect.top;
+ clip.x1 = (int)updateRect.right + 1;
+ clip.y1 = (int)updateRect.bottom + 1;
+
+ browser_window_redraw(g->bw, 0, 0, &clip, &ctx);
+
+ if (g->careth != 0)
+ nsbeos_plot_caret(g->caretx, g->carety, g->careth);
+
+ if (view->Window())
+ view->Window()->EndViewTransaction();
+
+ // reset clipping just in case
+ view->ConstrainClippingRegion(NULL);
+ nsbeos_current_gc_set(NULL);
+ view->UnlockLooper();
+}
+
+void nsbeos_window_keypress_event(BView *view, gui_window *g, BMessage *event)
+{
+ const char *bytes;
+ char buff[6];
+ int numbytes = 0;
+ uint32 mods;
+ uint32 key;
+ uint32 raw_char;
+ uint32_t nskey;
+ int i;
+
+ if (event->FindInt32("modifiers", (int32 *)&mods) < B_OK)
+ mods = modifiers();
+ if (event->FindInt32("key", (int32 *)&key) < B_OK)
+ key = 0;
+ if (event->FindInt32("raw_char", (int32 *)&raw_char) < B_OK)
+ raw_char = 0;
+ /* check for byte[] first, because C-space gives bytes="" (and byte[0] = '\0') */
+ for (i = 0; i < 5; i++) {
+ buff[i] = '\0';
+ if (event->FindInt8("byte", i, (int8 *)&buff[i]) < B_OK)
+ break;
+ }
+
+ if (i) {
+ bytes = buff;
+ numbytes = i;
+ } else if (event->FindString("bytes", &bytes) < B_OK)
+ bytes = "";
+
+ if (!numbytes)
+ numbytes = strlen(bytes);
+
+ LOG("mods 0x%08lx key %ld raw %ld byte[0] %d", mods, key, raw_char, buff[0]);
+
+ char byte;
+ if (numbytes == 1) {
+ byte = bytes[0];
+ if (mods & B_CONTROL_KEY)
+ byte = (char)raw_char;
+ if (byte >= '!' && byte <= '~')
+ nskey = (uint32_t)byte;
+ else {
+ switch (byte) {
+ case B_BACKSPACE: nskey = NS_KEY_DELETE_LEFT; break;
+ case B_TAB: nskey = NS_KEY_TAB; break;
+ /*case XK_Linefeed: return QKlinefeed;*/
+ case B_ENTER: nskey = (uint32_t)10; break;
+ case B_ESCAPE: nskey = (uint32_t)'\033'; break;
+ case B_SPACE: nskey = (uint32_t)' '; break;
+ case B_DELETE: nskey = NS_KEY_DELETE_RIGHT; break;
+ /*
+ case B_INSERT: nskey = NS_KEYSYM("insert"); break;
+ */
+ case B_HOME: nskey = NS_KEY_LINE_START; break; // XXX ?
+ case B_END: nskey = NS_KEY_LINE_END; break; // XXX ?
+ case B_PAGE_UP: nskey = NS_KEY_PAGE_UP; break;
+ case B_PAGE_DOWN: nskey = NS_KEY_PAGE_DOWN; break;
+ case B_LEFT_ARROW: nskey = NS_KEY_LEFT; break;
+ case B_RIGHT_ARROW: nskey = NS_KEY_RIGHT; break;
+ case B_UP_ARROW: nskey = NS_KEY_UP; break;
+ case B_DOWN_ARROW: nskey = NS_KEY_DOWN; break;
+ /*
+ case B_FUNCTION_KEY:
+ switch (scancode) {
+ case B_F1_KEY: nskey = KEYSYM("f1"); break;
+ case B_F2_KEY: nskey = KEYSYM("f2"); break;
+ case B_F3_KEY: nskey = KEYSYM("f3"); break;
+ case B_F4_KEY: nskey = KEYSYM("f4"); break;
+ case B_F5_KEY: nskey = KEYSYM("f5"); break;
+ case B_F6_KEY: nskey = KEYSYM("f6"); break;
+ case B_F7_KEY: nskey = KEYSYM("f7"); break;
+ case B_F8_KEY: nskey = KEYSYM("f8"); break;
+ case B_F9_KEY: nskey = KEYSYM("f9"); break;
+ case B_F10_KEY: nskey = KEYSYM("f10"); break;
+ case B_F11_KEY: nskey = KEYSYM("f11"); break;
+ case B_F12_KEY: nskey = KEYSYM("f12"); break;
+ case B_PRINT_KEY: nskey = KEYSYM("print"); break;
+ case B_SCROLL_KEY: nskey = KEYSYM("scroll-lock"); break;
+ case B_PAUSE_KEY: nskey = KEYSYM("pause"); break;
+ }
+ */
+ case 0:
+ nskey = (uint32_t)0;
+ default:
+ nskey = (uint32_t)raw_char;
+ /*if (simple_p)
+ nskey = (uint32_t)0;*/
+ break;
+ }
+ }
+ } else {
+ nskey = utf8_to_ucs4(bytes, numbytes);
+ }
+
+ if(browser_window_key_press(g->bw, nskey))
+ return;
+
+ // Remaining events are for scrolling the page around
+ float hdelta = 0.0f, vdelta = 0.0f;
+ g->view->LockLooper();
+ BRect size = g->view->Bounds();
+ switch (byte) {
+ case B_HOME:
+ g->view->ScrollTo(0.0f, 0.0f);
+ break;
+ case B_END:
+ {
+ // TODO
+ break;
+ }
+ case B_PAGE_UP:
+ vdelta = -size.Height();
+ break;
+ case B_PAGE_DOWN:
+ vdelta = size.Height();
+ break;
+ case B_LEFT_ARROW:
+ hdelta = -10;
+ break;
+ case B_RIGHT_ARROW:
+ hdelta = 10;
+ break;
+ case B_UP_ARROW:
+ vdelta = -10;
+ break;
+ case B_DOWN_ARROW:
+ vdelta = 10;
+ break;
+ }
+
+ g->view->ScrollBy(hdelta, vdelta);
+ g->view->UnlockLooper();
+}
+
+
+void nsbeos_window_resize_event(BView *view, gui_window *g, BMessage *event)
+{
+ //CALLED();
+ int32 width;
+ int32 height;
+
+ // drop this event if we have at least 2 resize pending
+ if (atomic_add(&g->pending_resizes, -1) > 1)
+ return;
+
+ if (event->FindInt32("width", &width) < B_OK)
+ width = -1;
+ if (event->FindInt32("height", &height) < B_OK)
+ height = -1;
+ width++;
+ height++;
+
+ browser_window_schedule_reformat(g->bw);
+
+ return;
+}
+
+
+void nsbeos_window_moved_event(BView *view, gui_window *g, BMessage *event)
+{
+ //CALLED();
+
+#warning XXX: Invalidate ?
+ if (!view || !view->LockLooper())
+ return;
+ //view->Invalidate(view->Bounds());
+ view->UnlockLooper();
+
+ return;
+}
+
+
+void nsbeos_reflow_all_windows(void)
+{
+ for (struct gui_window *g = window_list; g; g = g->next) {
+ browser_window_schedule_reformat(g->bw);
+ }
+}
+
+
+
+/**
+ * callback from core to reformat a window.
+ */
+static void beos_window_reformat(struct gui_window *g)
+{
+ if (g == NULL) {
+ return;
+ }
+
+ NSBrowserFrameView *view = g->view;
+ if (view && view->LockLooper()) {
+ BRect bounds = view->Bounds();
+ view->UnlockLooper();
+#warning XXX why - 1 & - 2 !???
+ browser_window_reformat(g->bw,
+ false,
+ bounds.Width() + 1 /* - 2*/,
+ bounds.Height() + 1);
+ }
+}
+
+void nsbeos_window_destroy_browser(struct gui_window *g)
+{
+ browser_window_destroy(g->bw);
+}
+
+static void gui_window_destroy(struct gui_window *g)
+{
+ if (!g)
+ return;
+
+ if (g->prev)
+ g->prev->next = g->next;
+ else
+ window_list = g->next;
+
+ if (g->next)
+ g->next->prev = g->prev;
+
+
+ LOG("Destroying gui_window %p", g);
+ assert(g != NULL);
+ assert(g->bw != NULL);
+ LOG(" Scaffolding: %p", g->scaffold);
+
+ if (g->view == NULL)
+ return;
+ if (!g->view->LockLooper())
+ return;
+
+ BLooper *looper = g->view->Looper();
+ /* If we're a top-level gui_window, destroy our scaffold */
+ if (g->toplevel) {
+ g->view->RemoveSelf();
+ delete g->view;
+ nsbeos_scaffolding_destroy(g->scaffold);
+ } else {
+ g->view->RemoveSelf();
+ delete g->view;
+ looper->Unlock();
+ }
+ //XXX
+ //looper->Unlock();
+
+
+ free(g);
+
+}
+
+void nsbeos_redraw_caret(struct gui_window *g)
+{
+ if (g->careth == 0)
+ return;
+
+ if (g->view == NULL)
+ return;
+ if (!g->view->LockLooper())
+ return;
+
+ nsbeos_current_gc_set(g->view);
+
+ g->view->Invalidate(BRect(g->caretx, g->carety,
+ g->caretx, g->carety + g->careth));
+
+ nsbeos_current_gc_set(NULL);
+ g->view->UnlockLooper();
+}
+
+static void gui_window_redraw_window(struct gui_window *g)
+{
+ if (g->view == NULL)
+ return;
+ if (!g->view->LockLooper())
+ return;
+
+ nsbeos_current_gc_set(g->view);
+
+ g->view->Invalidate();
+
+ nsbeos_current_gc_set(NULL);
+ g->view->UnlockLooper();
+}
+
+static void gui_window_update_box(struct gui_window *g, const struct rect *rect)
+{
+ if (browser_window_has_content(g->bw) == false)
+ return;
+
+ if (g->view == NULL)
+ return;
+ if (!g->view->LockLooper())
+ return;
+
+ nsbeos_current_gc_set(g->view);
+
+//XXX +1 ??
+ g->view->Invalidate(BRect(rect->x0, rect->y0,
+ rect->x1 - 1, rect->y1 - 1));
+
+ nsbeos_current_gc_set(NULL);
+ g->view->UnlockLooper();
+}
+
+static bool gui_window_get_scroll(struct gui_window *g, int *sx, int *sy)
+{
+ //CALLED();
+ if (g->view == NULL)
+ return false;
+ if (!g->view->LockLooper())
+ return false;
+
+#warning XXX: report to view frame ?
+ if (g->view->ScrollBar(B_HORIZONTAL))
+ *sx = (int)g->view->ScrollBar(B_HORIZONTAL)->Value();
+ if (g->view->ScrollBar(B_VERTICAL))
+ *sy = (int)g->view->ScrollBar(B_VERTICAL)->Value();
+
+ g->view->UnlockLooper();
+ return true;
+}
+
+static void gui_window_set_scroll(struct gui_window *g, int sx, int sy)
+{
+ //CALLED();
+ if (g->view == NULL)
+ return;
+ if (!g->view->LockLooper())
+ return;
+
+#warning XXX: report to view frame ?
+ if (g->view->ScrollBar(B_HORIZONTAL))
+ g->view->ScrollBar(B_HORIZONTAL)->SetValue(sx);
+ if (g->view->ScrollBar(B_VERTICAL))
+ g->view->ScrollBar(B_VERTICAL)->SetValue(sy);
+
+ g->view->UnlockLooper();
+}
+
+
+static void gui_window_update_extent(struct gui_window *g)
+{
+ nserror err;
+ //CALLED();
+ if (browser_window_has_content(g->bw) == false)
+ return;
+
+ if (g->view == NULL)
+ return;
+ if (!g->view->LockLooper())
+ return;
+
+ int x_max, y_max;
+
+ err = browser_window_get_extents(g->bw, true, &x_max, &y_max);
+ if (err != NSERROR_OK)
+ return;
+
+ float x_prop = g->view->Bounds().Width() / x_max;
+ float y_prop = g->view->Bounds().Height() / y_max;
+ x_max -= g->view->Bounds().Width() + 1;
+ y_max -= g->view->Bounds().Height() + 1;
+
+ LOG("x_max = %d y_max = %d x_prop = %f y_prop = %f\n",
+ x_max, y_max, x_prop, y_prop);
+
+ if (g->view->ScrollBar(B_HORIZONTAL)) {
+ g->view->ScrollBar(B_HORIZONTAL)->SetRange(0, x_max);
+ g->view->ScrollBar(B_HORIZONTAL)->SetProportion(x_prop);
+ g->view->ScrollBar(B_HORIZONTAL)->SetSteps(10, 50);
+ }
+ if (g->view->ScrollBar(B_VERTICAL)) {
+ g->view->ScrollBar(B_VERTICAL)->SetRange(0, y_max);
+ g->view->ScrollBar(B_VERTICAL)->SetProportion(y_prop);
+ g->view->ScrollBar(B_VERTICAL)->SetSteps(10, 50);
+ }
+
+
+ g->view->UnlockLooper();
+}
+
+/* some cursors like those in Firefox */
+// XXX: move to separate file or resource ?
+
+const uint8 kLinkCursorBits[] = {
+ 16, /* cursor size */
+ 1, /* bits per pixel */
+ 2, /* vertical hot spot */
+ 2, /* horizontal hot spot */
+
+ /* data */
+ 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x24, 0x00, 0x24, 0x00, 0x13, 0xe0, 0x12, 0x5c, 0x09, 0x2a,
+ 0x08, 0x01, 0x3c, 0x21, 0x4c, 0x71, 0x42, 0x71, 0x30, 0xf9, 0x0c, 0xf9, 0x02, 0x02, 0x01, 0x00,
+
+ /* mask */
+ 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x1f, 0xe0, 0x1f, 0xfc, 0x0f, 0xfe,
+ 0x0f, 0xff, 0x3f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x3f, 0xff, 0x0f, 0xff, 0x03, 0xfc, 0x01, 0xe0
+};
+
+const uint8 kWatchCursorBits[] = {
+ 16, /* cursor size */
+ 1, /* bits per pixel */
+ 0, /* vertical hot spot */
+ 1, /* horizontal hot spot */
+
+ /* data */
+ 0x70, 0x00, 0x48, 0x00, 0x48, 0x00, 0x27, 0xc0, 0x24, 0xb8, 0x12, 0x54, 0x10, 0x02, 0x78, 0x02,
+ 0x98, 0x02, 0x84, 0x02, 0x60, 0x3a, 0x18, 0x46, 0x04, 0x8a, 0x02, 0x92, 0x01, 0x82, 0x00, 0x45,
+
+ /* mask */
+ 0x70, 0x00, 0x78, 0x00, 0x78, 0x00, 0x3f, 0xc0, 0x3f, 0xf8, 0x1f, 0xfc, 0x1f, 0xfe, 0x7f, 0xfe,
+ 0xff, 0xfe, 0xff, 0xfe, 0x7f, 0xfe, 0x1f, 0xfe, 0x07, 0xfe, 0x03, 0xfe, 0x01, 0xfe, 0x00, 0x7f
+};
+
+const uint8 kWatch2CursorBits[] = {
+ 16, /* cursor size */
+ 1, /* bits per pixel */
+ 0, /* vertical hot spot */
+ 1, /* horizontal hot spot */
+
+ /* data */
+ 0x70, 0x00, 0x48, 0x00, 0x48, 0x00, 0x27, 0xc0, 0x24, 0xb8, 0x12, 0x54, 0x10, 0x02, 0x78, 0x02,
+ 0x98, 0x02, 0x84, 0x02, 0x60, 0x3a, 0x18, 0x46, 0x04, 0xa2, 0x02, 0x92, 0x01, 0xa2, 0x00, 0x45,
+
+ /* mask */
+ 0x70, 0x00, 0x78, 0x00, 0x78, 0x00, 0x3f, 0xc0, 0x3f, 0xf8, 0x1f, 0xfc, 0x1f, 0xfe, 0x7f, 0xfe,
+ 0xff, 0xfe, 0xff, 0xfe, 0x7f, 0xfe, 0x1f, 0xfe, 0x07, 0xfe, 0x03, 0xfe, 0x01, 0xfe, 0x00, 0x7f
+};
+
+
+static void gui_window_set_pointer(struct gui_window *g, gui_pointer_shape shape)
+{
+ BCursor *cursor = NULL;
+ bool allocated = false;
+
+ if (g->current_pointer == shape)
+ return;
+
+ g->current_pointer = shape;
+
+ switch (shape) {
+ case GUI_POINTER_POINT:
+ cursor = new BCursor(kLinkCursorBits);
+ allocated = true;
+ break;
+ case GUI_POINTER_CARET:
+ cursor = (BCursor *)B_CURSOR_I_BEAM;
+ break;
+ case GUI_POINTER_WAIT:
+ cursor = new BCursor(kWatchCursorBits);
+ allocated = true;
+ break;
+ case GUI_POINTER_PROGRESS:
+ cursor = new BCursor(kWatch2CursorBits);
+ allocated = true;
+ break;
+ default:
+ cursor = (BCursor *)B_CURSOR_SYSTEM_DEFAULT;
+ allocated = false;
+ }
+
+ if (g->view && g->view->LockLooper()) {
+ g->view->SetViewCursor(cursor);
+ g->view->UnlockLooper();
+ }
+
+ if (allocated)
+ delete cursor;
+}
+
+static void gui_window_place_caret(struct gui_window *g, int x, int y, int height,
+ const struct rect *clip)
+{
+ //CALLED();
+ if (g->view == NULL)
+ return;
+ if (!g->view->LockLooper())
+ return;
+
+ nsbeos_redraw_caret(g);
+
+ g->caretx = x;
+ g->carety = y + 1;
+ g->careth = height - 2;
+
+ nsbeos_redraw_caret(g);
+ g->view->MakeFocus();
+
+ g->view->UnlockLooper();
+}
+
+static void gui_window_remove_caret(struct gui_window *g)
+{
+ int oh = g->careth;
+
+ if (oh == 0)
+ return;
+
+ g->careth = 0;
+
+ if (g->view == NULL)
+ return;
+ if (!g->view->LockLooper())
+ return;
+
+ nsbeos_current_gc_set(g->view);
+
+ g->view->Invalidate(BRect(g->caretx, g->carety, g->caretx, g->carety + oh));
+
+ nsbeos_current_gc_set(NULL);
+ g->view->UnlockLooper();
+}
+
+static void gui_window_new_content(struct gui_window *g)
+{
+ if (!g->toplevel)
+ return;
+
+ if (g->view == NULL)
+ return;
+ if (!g->view->LockLooper())
+ return;
+
+ // scroll back to top
+ g->view->ScrollTo(0,0);
+
+ g->view->UnlockLooper();
+}
+
+static void gui_start_selection(struct gui_window *g)
+{
+ if (!g->view->LockLooper())
+ return;
+
+ g->view->MakeFocus();
+
+ g->view->UnlockLooper();
+}
+
+static void gui_get_clipboard(char **buffer, size_t *length)
+{
+ BMessage *clip;
+ *length = 0;
+ *buffer = NULL;
+
+ if (be_clipboard->Lock()) {
+ clip = be_clipboard->Data();
+ if (clip) {
+ const char *text;
+ ssize_t textlen;
+ if (clip->FindData("text/plain", B_MIME_TYPE,
+ (const void **)&text, &textlen) >= B_OK) {
+ *buffer = (char *)malloc(textlen);
+ *length = textlen;
+ memcpy(*buffer, text, textlen);
+ }
+ }
+ be_clipboard->Unlock();
+ }
+}
+
+static void gui_set_clipboard(const char *buffer, size_t length,
+ nsclipboard_styles styles[], int n_styles)
+{
+ BMessage *clip;
+
+ if (be_clipboard->Lock()) {
+ be_clipboard->Clear();
+ clip = be_clipboard->Data();
+ if (clip) {
+ clip->AddData("text/plain", B_MIME_TYPE, buffer, length);
+
+ int arraySize = sizeof(text_run_array) +
+ n_styles * sizeof(text_run);
+ text_run_array *array = (text_run_array *)malloc(arraySize);
+ array->count = n_styles;
+ for (int i = 0; i < n_styles; i++) {
+ BFont font;
+ nsbeos_style_to_font(font, &styles[i].style);
+ array->runs[i].offset = styles[i].start;
+ array->runs[i].font = font;
+ array->runs[i].color =
+ nsbeos_rgb_colour(styles[i].style.foreground);
+ }
+ clip->AddData("application/x-vnd.Be-text_run_array", B_MIME_TYPE,
+ array, arraySize);
+ free(array);
+ be_clipboard->Commit();
+ }
+ be_clipboard->Unlock();
+ }
+}
+
+static struct gui_clipboard_table clipboard_table = {
+ gui_get_clipboard,
+ gui_set_clipboard,
+};
+
+struct gui_clipboard_table *beos_clipboard_table = &clipboard_table;
+
+static void gui_window_get_dimensions(struct gui_window *g, int *width, int *height,
+ bool scaled)
+{
+ if (g->view && g->view->LockLooper()) {
+ *width = g->view->Bounds().Width() + 1;
+ *height = g->view->Bounds().Height() + 1;
+ g->view->UnlockLooper();
+ }
+
+ if (scaled) {
+ *width /= g->scale;
+ *height /= g->scale;
+ }
+}
+
+static struct gui_window_table window_table = {
+ gui_window_create,
+ gui_window_destroy,
+ gui_window_redraw_window,
+ gui_window_update_box,
+ gui_window_get_scroll,
+ gui_window_set_scroll,
+ gui_window_get_dimensions,
+ gui_window_update_extent,
+ beos_window_reformat,
+
+ /* from scaffold */
+ gui_window_set_title,
+ gui_window_set_url,
+ gui_window_set_icon,
+ gui_window_set_status,
+ gui_window_set_pointer,
+ gui_window_place_caret,
+ gui_window_remove_caret,
+ gui_window_start_throbber,
+ gui_window_stop_throbber,
+ NULL, //drag_start
+ NULL, //save_link
+ NULL, //scroll_visible
+ NULL, //scroll_start
+ gui_window_new_content,
+ NULL, //create_form_select_menu
+ NULL, //file_gadget_open
+ NULL, //drag_save_object
+ NULL, //drag_save_selection
+ gui_start_selection
+};
+
+struct gui_window_table *beos_window_table = &window_table;
diff --git a/frontends/beos/window.h b/frontends/beos/window.h
new file mode 100644
index 000000000..928acca22
--- /dev/null
+++ b/frontends/beos/window.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_BEOS_WINDOW_H
+#define NETSURF_BEOS_WINDOW_H 1
+
+#include <View.h>
+#include <Window.h>
+#include <NetPositive.h>
+
+extern struct gui_window_table *beos_window_table;
+extern struct gui_clipboard_table *beos_clipboard_table;
+
+struct gui_window;
+struct browser_window;
+struct beos_scaffolding;
+
+class NSBrowserFrameView : public BView {
+public:
+ NSBrowserFrameView(BRect frame, struct gui_window *gui);
+ virtual ~NSBrowserFrameView();
+
+ virtual void MessageReceived(BMessage *message);
+ virtual void Draw(BRect updateRect);
+
+ //virtual void FrameMoved(BPoint new_location);
+ virtual void FrameResized(float new_width, float new_height);
+
+ virtual void KeyDown(const char *bytes, int32 numBytes);
+ virtual void MouseDown(BPoint where);
+ virtual void MouseUp(BPoint where);
+ virtual void MouseMoved(BPoint where, uint32 transit, const BMessage *msg);
+
+private:
+ struct gui_window *fGuiWindow;
+};
+
+/**
+ * Process beos messages into browser operations.
+ *
+ * \param message The beos message to process.
+ */
+void nsbeos_dispatch_event(BMessage *message);
+
+/**
+ * Cause all windows to be reflowed.
+ */
+void nsbeos_reflow_all_windows(void);
+
+/**
+ * Get containing scaffold of a beos gui window
+ *
+ * \param g gui window to find scaffold of.
+ * \return The containing scaffold.
+ */
+struct beos_scaffolding *nsbeos_get_scaffold(struct gui_window *g);
+
+struct browser_window *nsbeos_get_browser_for_gui(struct gui_window *g);
+
+float nsbeos_get_scale_for_gui(struct gui_window *g);
+
+int nsbeos_gui_window_update_targets(struct gui_window *g);
+
+void nsbeos_window_destroy_browser(struct gui_window *g);
+
+struct browser_window *nsbeos_get_browser_window(struct gui_window *g);
+
+#endif /* NETSURF_BEOS_WINDOW_H */
diff --git a/frontends/cocoa/ArrowBox.h b/frontends/cocoa/ArrowBox.h
new file mode 100644
index 000000000..c49fcb6e7
--- /dev/null
+++ b/frontends/cocoa/ArrowBox.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 1011 Sven Weidauer
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+
+#import <Cocoa/Cocoa.h>
+
+typedef enum {
+ ArrowNone,
+ ArrowTopEdge,
+ ArrowBottomEdge,
+ ArrowLeftEdge,
+ ArrowRightEdge
+} ArrowEdge;
+
+
+@interface ArrowBox : NSView {
+ CGFloat arrowPosition;
+ CGFloat arrowSize;
+ ArrowEdge arrowEdge;
+ CGFloat cornerRadius;
+ BOOL updateShadow;
+}
+
+@property (readwrite, assign, nonatomic) CGFloat arrowPosition;
+@property (readwrite, assign, nonatomic) CGFloat arrowSize;
+@property (readwrite, assign, nonatomic) ArrowEdge arrowEdge;
+@property (readwrite, assign, nonatomic) CGFloat cornerRadius;
+
+@end
diff --git a/frontends/cocoa/ArrowBox.m b/frontends/cocoa/ArrowBox.m
new file mode 100644
index 000000000..6605fcca9
--- /dev/null
+++ b/frontends/cocoa/ArrowBox.m
@@ -0,0 +1,163 @@
+/* Copyright 2011 Sven Weidauer
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import "ArrowBox.h"
+
+@implementation ArrowBox
+
+@synthesize arrowPosition;
+@synthesize arrowSize;
+@synthesize arrowEdge;
+@synthesize cornerRadius;
+
+- (void) setArrowEdge: (ArrowEdge)newEdge
+{
+ if (arrowEdge == newEdge) {
+ return;
+ }
+
+ arrowEdge = newEdge;
+
+ [self setNeedsDisplay: YES];
+ updateShadow = YES;
+}
+
+
+- (void) setArrowSize: (CGFloat)newSize
+{
+ arrowSize = newSize;
+ [self setNeedsDisplay: YES];
+ updateShadow = YES;
+}
+
+
+- (void) setCornerRadius: (CGFloat)newRadius
+{
+ cornerRadius = newRadius;
+ [self setNeedsDisplay: YES];
+ updateShadow = YES;
+}
+
+- (void) setArrowPosition: (CGFloat)newPosition
+{
+ arrowPosition = newPosition;
+
+ [self setNeedsDisplay: YES];
+ updateShadow = YES;
+}
+
+
+- (id)initWithFrame:(NSRect)frame
+{
+ self = [super initWithFrame:frame];
+ if (self) {
+ arrowPosition = 50;
+ cornerRadius = 10;
+ arrowSize = 15;
+ }
+ return self;
+}
+
+- (void)drawRect:(NSRect)dirtyRect
+{
+ [[NSColor clearColor] set];
+ [NSBezierPath fillRect: dirtyRect];
+
+ NSBezierPath *path = [NSBezierPath bezierPath];
+
+ NSRect bounds = [self convertRectToBase: NSInsetRect( [self bounds], 2, 2 )];
+ bounds.origin.x = floor( bounds.origin.x );
+ bounds.origin.y = floor( bounds.origin.y );
+ bounds.size.width = floor( bounds.size.width );
+ bounds.size.height = floor( bounds.size.height );
+ bounds = [self convertRectFromBase: bounds];
+
+ const CGFloat right = bounds.size.width - arrowSize;
+ const CGFloat top = bounds.size.height - arrowSize;
+ const CGFloat left = arrowSize;
+ const CGFloat bottom = arrowSize;
+
+ [path setLineJoinStyle:NSRoundLineJoinStyle];
+
+ [path moveToPoint: NSMakePoint( right - cornerRadius, top )];
+
+ if (arrowEdge == ArrowTopEdge) {
+ [path lineToPoint: NSMakePoint( arrowPosition + arrowSize, top )];
+ [path lineToPoint: NSMakePoint( arrowPosition, top + arrowSize )];
+ [path lineToPoint: NSMakePoint( arrowPosition - arrowSize, top )];
+ }
+
+ [path appendBezierPathWithArcFromPoint: NSMakePoint( left, top )
+ toPoint: NSMakePoint( left, top - cornerRadius )
+ radius: cornerRadius];
+
+ if (arrowEdge == ArrowLeftEdge) {
+ [path lineToPoint: NSMakePoint( left, bottom + arrowPosition + arrowSize )];
+ [path lineToPoint: NSMakePoint( left - arrowSize, bottom + arrowPosition )];
+ [path lineToPoint: NSMakePoint( left, bottom + arrowPosition - arrowSize )];
+ }
+
+ [path appendBezierPathWithArcFromPoint: NSMakePoint( left, bottom )
+ toPoint: NSMakePoint( left + cornerRadius, bottom )
+ radius: cornerRadius];
+
+ if (arrowEdge == ArrowBottomEdge) {
+ [path lineToPoint: NSMakePoint( arrowPosition - arrowSize, bottom )];
+ [path lineToPoint: NSMakePoint( arrowPosition, bottom - arrowSize )];
+ [path lineToPoint: NSMakePoint( arrowPosition + arrowSize, bottom )];
+ }
+
+ [path appendBezierPathWithArcFromPoint: NSMakePoint( right, bottom )
+ toPoint: NSMakePoint( right, bottom + cornerRadius )
+ radius: cornerRadius];
+
+ if (arrowEdge == ArrowRightEdge) {
+ [path lineToPoint: NSMakePoint( right, bottom + arrowPosition - arrowSize )];
+ [path lineToPoint: NSMakePoint( right + arrowSize, bottom + arrowPosition )];
+ [path lineToPoint: NSMakePoint( right, bottom + arrowPosition + arrowSize )];
+ }
+
+ [path appendBezierPathWithArcFromPoint: NSMakePoint( right, top )
+ toPoint: NSMakePoint( right - cornerRadius, top )
+ radius: cornerRadius];
+ [path closePath];
+
+ [[NSColor colorWithDeviceWhite: 1.0 alpha: 0.4] set];
+ [[NSColor colorWithDeviceWhite: 0.0 alpha: 0.75] setFill];
+
+ NSAffineTransform *transform = [NSAffineTransform transform];
+ [transform translateXBy: bounds.origin.x yBy: bounds.origin.y];
+ [transform concat];
+
+ [path setLineWidth: 2.0];
+ [path fill];
+ [path stroke];
+
+ if (updateShadow) {
+ [[self window] invalidateShadow];
+ [[self window] update];
+ updateShadow = NO;
+ }
+}
+
+@end
diff --git a/frontends/cocoa/ArrowWindow.h b/frontends/cocoa/ArrowWindow.h
new file mode 100644
index 000000000..79f422f3e
--- /dev/null
+++ b/frontends/cocoa/ArrowWindow.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 1011 Sven Weidauer
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+
+#import <Cocoa/Cocoa.h>
+
+#import "ArrowBox.h"
+
+@interface ArrowWindow : NSWindow {
+ ArrowBox *box;
+ NSView *content;
+ BOOL acceptsKey;
+ NSWindow *attachedWindow;
+}
+
+@property (readwrite, assign, nonatomic) BOOL acceptsKey;
+
+@property (readwrite, assign, nonatomic) CGFloat arrowPosition;
+@property (readwrite, assign, nonatomic) CGFloat arrowSize;
+@property (readwrite, assign, nonatomic) ArrowEdge arrowEdge;
+@property (readwrite, assign, nonatomic) CGFloat cornerRadius;
+
+- (void) moveToPoint: (NSPoint) screenPoint;
+- (void) attachToView: (NSView *) view;
+- (void) detach;
+
+@end
diff --git a/frontends/cocoa/ArrowWindow.m b/frontends/cocoa/ArrowWindow.m
new file mode 100644
index 000000000..8edc32e9f
--- /dev/null
+++ b/frontends/cocoa/ArrowWindow.m
@@ -0,0 +1,239 @@
+/* Copyright 2011 Sven Weidauer
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import "ArrowWindow.h"
+#import "ArrowBox.h"
+
+@implementation ArrowWindow
+
+@synthesize acceptsKey;
+
+- (id) initWithContentRect: (NSRect)contentRect styleMask: (NSUInteger)aStyle backing: (NSBackingStoreType)bufferingType defer: (BOOL)flag
+{
+ if ((self = [super initWithContentRect: contentRect styleMask: NSBorderlessWindowMask backing: bufferingType defer: flag]) == nil) {
+ return nil;
+ }
+
+ [self setBackgroundColor: [NSColor clearColor]];
+ [self setOpaque: NO];
+ [self setHasShadow: YES];
+
+ return self;
+}
+
+- (void) setContentView: (NSView *)aView
+{
+ if (aView == content) return;
+
+ [content removeFromSuperview];
+ content = aView;
+
+ if (content == nil) return;
+
+ if (box == nil) {
+ box = [[ArrowBox alloc] initWithFrame: NSZeroRect];
+ [box setArrowEdge: ArrowTopEdge];
+ [super setContentView: box];
+ [box release];
+ }
+
+ [box addSubview: content];
+
+ NSRect frame = [self contentRectForFrameRect: [self frame]];
+ frame.origin = [self convertScreenToBase: frame.origin];
+ frame = [box convertRect: frame fromView: nil];
+
+ [content setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
+ [content setFrame: frame];
+}
+
+- (void) setContentSize: (NSSize)aSize
+{
+ NSRect frame = [content frame];
+ frame.size = aSize;
+
+ frame = [box convertRect: frame toView: nil];
+ frame.origin = [self convertBaseToScreen: frame.origin];
+ frame = [self frameRectForContentRect: frame];
+
+ [self setFrame: frame display: YES];
+}
+
+static const CGFloat padding = 0;
+
+- (NSRect) contentRectForFrameRect: (NSRect)frameRect
+{
+ const CGFloat arrowSize = [box arrowSize];
+ const CGFloat offset = 2 * (padding + arrowSize );
+
+ return NSInsetRect( frameRect, offset, offset );
+}
+
+- (NSRect) frameRectForContentRect: (NSRect)contentRect
+{
+ const CGFloat arrowSize = [box arrowSize];
+ const CGFloat offset = -2 * (padding + arrowSize );
+
+ return NSInsetRect( contentRect, offset, offset );
+}
+
++ (NSRect) frameRectForContentRect: (NSRect)cRect styleMask: (NSUInteger)aStyle
+{
+ const CGFloat DefaultArrowSize = 15;
+ const CGFloat offset = -2 * (padding + DefaultArrowSize);
+
+ return NSInsetRect( cRect, offset, offset );
+}
+
+- (BOOL) canBecomeKeyWindow
+{
+ return acceptsKey;
+}
+
+- (void) moveToPoint: (NSPoint) screenPoint
+{
+ switch ([box arrowEdge]) {
+ case ArrowNone:
+ screenPoint.x -= [box arrowSize];
+ screenPoint.y += [box arrowSize];
+ break;
+
+ case ArrowTopEdge:
+ screenPoint.x -= [box arrowPosition];
+ break;
+
+ case ArrowBottomEdge:
+ screenPoint.x -= [box arrowPosition];
+ screenPoint.y += NSHeight( [self frame] );
+ break;
+
+ case ArrowLeftEdge:
+ screenPoint.y += NSHeight( [self frame] ) - [box arrowPosition] - [box arrowSize];
+ break;
+
+ case ArrowRightEdge:
+ screenPoint.x -= NSWidth( [self frame] );
+ screenPoint.y += NSHeight( [self frame] ) - [box arrowPosition] - [box arrowSize];
+ break;
+ }
+
+ [self setFrameTopLeftPoint: screenPoint];
+}
+
+static NSRect ScreenRectForView( NSView *view )
+{
+ NSRect viewRect = [view bounds]; // in View coordinate system
+ viewRect = [view convertRect: viewRect toView: nil]; // translate to window coordinates
+ viewRect.origin = [[view window] convertBaseToScreen: viewRect.origin]; // translate to screen coordinates
+ return viewRect;
+}
+
+- (void) attachToView: (NSView *) view
+{
+ if (nil != attachedWindow) [self detach];
+
+ NSRect viewRect = ScreenRectForView( view );
+ NSPoint arrowPoint;
+
+ switch ([box arrowEdge]) {
+ case ArrowLeftEdge:
+ arrowPoint = NSMakePoint( NSMaxX( viewRect ),
+ NSMidY( viewRect ) );
+ break;
+
+ case ArrowBottomEdge:
+ arrowPoint = NSMakePoint( NSMidX( viewRect ),
+ NSMaxY( viewRect ) );
+ break;
+
+ case ArrowRightEdge:
+ arrowPoint = NSMakePoint( NSMinX( viewRect ),
+ NSMidY( viewRect ) );
+ break;
+
+ case ArrowNone:
+ case ArrowTopEdge:
+ default:
+ arrowPoint = NSMakePoint( NSMidX( viewRect ),
+ NSMinY( viewRect ) );
+ break;
+
+ }
+ attachedWindow = [view window];
+ [self moveToPoint: arrowPoint];
+ [attachedWindow addChildWindow: self ordered: NSWindowAbove];
+}
+
+- (void) detach
+{
+ [attachedWindow removeChildWindow: self];
+ [self close];
+ attachedWindow = nil;
+}
+
+//MARK: -
+//MARK: Properties
+
+- (void) setArrowPosition: (CGFloat) newPosition
+{
+ [box setArrowPosition: newPosition];
+}
+
+- (CGFloat) arrowPosition
+{
+ return [box arrowPosition];
+}
+
+- (void) setArrowSize: (CGFloat)newSize
+{
+ NSRect contentRect = [self contentRectForFrameRect: [self frame]];
+ [box setArrowSize: newSize];
+ [self setFrame: [self frameRectForContentRect: contentRect] display: [self isVisible]];
+}
+
+- (CGFloat) arrowSize
+{
+ return [box arrowSize];
+}
+
+- (void) setArrowEdge: (ArrowEdge) newEdge
+{
+ [box setArrowEdge: newEdge];
+}
+
+- (ArrowEdge) arrowEdge
+{
+ return [box arrowEdge];
+}
+
+- (void) setCornerRadius: (CGFloat)newRadius
+{
+ [box setCornerRadius: newRadius];
+}
+
+- (CGFloat) cornerRadius
+{
+ return [box cornerRadius];
+}
+
+@end
diff --git a/frontends/cocoa/BlackScroller.h b/frontends/cocoa/BlackScroller.h
new file mode 100644
index 000000000..34110b035
--- /dev/null
+++ b/frontends/cocoa/BlackScroller.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 1011 Sven Weidauer
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+
+#import <Cocoa/Cocoa.h>
+
+
+@interface BlackScroller : NSScroller {
+ BOOL drawTrack;
+ NSTrackingRectTag tag;
+}
+
+@property (readonly, getter=isHorizontal) BOOL horizontal;
+
+@end
diff --git a/frontends/cocoa/BlackScroller.m b/frontends/cocoa/BlackScroller.m
new file mode 100644
index 000000000..2ee739f3a
--- /dev/null
+++ b/frontends/cocoa/BlackScroller.m
@@ -0,0 +1,154 @@
+/* Copyright (c) 1011 Sven Weidauer
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import "BlackScroller.h"
+
+@implementation BlackScroller
+
+- (void) setFrame: (NSRect)frameRect
+{
+ [super setFrame: frameRect];
+ if (tag != 0) [self removeTrackingRect: tag];
+ tag = [self addTrackingRect: [self bounds] owner: self userData: NULL assumeInside: NO];
+}
+
+- (void) drawRect: (NSRect)dirtyRect
+{
+ [[NSColor clearColor] set];
+ [NSBezierPath fillRect: dirtyRect];
+
+ if (drawTrack) [self drawKnobSlotInRect: [self rectForPart: NSScrollerKnobSlot]
+ highlight: NO];
+ [self drawKnob];
+}
+
+- (void) drawKnobSlotInRect: (NSRect)slotRect highlight: (BOOL)flag
+{
+ slotRect = NSInsetRect( slotRect, 2, 2 );
+ slotRect = [self convertRectToBase: slotRect];
+ slotRect.origin.x = floor( slotRect.origin.x ) + 0.5;
+ slotRect.origin.y = floor( slotRect.origin.y ) + 0.5;
+ slotRect.size.width = floor( slotRect.size.width );
+ slotRect.size.height = floor( slotRect.size.height );
+ slotRect = [self convertRectFromBase: slotRect];
+
+ NSGradient *gradient = [[[NSGradient alloc] initWithColorsAndLocations:
+ [NSColor clearColor], 0.0,
+ [NSColor clearColor], 0.4,
+ [NSColor whiteColor], 1.0,
+ nil] autorelease];
+ [[NSColor whiteColor] set];
+ const float radius = 0.5 * ([self isHorizontal] ? NSHeight( slotRect ) : NSWidth( slotRect ));
+ NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect: slotRect
+ xRadius: radius
+ yRadius: radius];
+ [gradient drawInBezierPath: path angle: [self isHorizontal] ? 90 : 0];
+
+ [path stroke];
+}
+
+
+- (NSUsableScrollerParts) usableParts
+{
+ return NSScrollerKnob | NSScrollerKnobSlot;
+}
+
+- (void) drawKnob
+{
+ NSRect rect = NSInsetRect( [self rectForPart: NSScrollerKnob], 2, 2 );
+
+ rect = [self convertRectToBase: rect];
+ rect.origin.x = floor( rect.origin.x ) + 0.5;
+ rect.origin.y = floor( rect.origin.y ) + 0.5;
+ rect.size.width = floor( rect.size.width );
+ rect.size.height = floor( rect.size.height );
+ rect = [self convertRectFromBase: rect];
+
+ [[NSColor colorWithDeviceWhite: 1.0 alpha: drawTrack ? 1.0 : 0.6] set];
+
+ const float radius = 0.5 * ([self isHorizontal] ? NSHeight( rect ) : NSWidth( rect ));
+ NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect: rect
+ xRadius: radius
+ yRadius: radius];
+ [path fill];
+ [path stroke];
+}
+
+- (NSRect) rectForPart: (NSScrollerPart)partCode
+{
+ const bool horizontal = [self isHorizontal];
+
+ NSRect rect = horizontal ? NSInsetRect( [self bounds], 4, 0 ) : NSInsetRect( [self bounds], 0, 4 );
+
+ switch (partCode) {
+ case NSScrollerKnobSlot:
+ return rect;
+
+ case NSScrollerKnob: {
+ const CGFloat len = horizontal ? NSWidth( rect ) : NSHeight( rect );
+ CGFloat knobLen = [self knobProportion] * len;
+ const CGFloat minKnobLen = horizontal ? NSHeight( rect ) : NSWidth( rect );
+ if (knobLen < minKnobLen) knobLen = minKnobLen;
+
+ const CGFloat start = [self doubleValue] * (len - knobLen);
+
+ if (horizontal) {
+ rect.origin.x += start;
+ rect.size.width = knobLen;
+ } else {
+ rect.origin.y += start;
+ rect.size.height = knobLen;
+ }
+
+ return rect;
+ }
+
+ default:
+ return [super rectForPart: partCode];
+ }
+}
+
+- (BOOL) isOpaque
+{
+ return NO;
+}
+
+- (BOOL) isHorizontal
+{
+ NSRect bounds = [self bounds];
+ return NSWidth( bounds ) > NSHeight( bounds );
+}
+
+- (void) mouseEntered: (NSEvent *)theEvent
+{
+ drawTrack = YES;
+ [self setNeedsDisplay: YES];
+}
+
+- (void) mouseExited: (NSEvent *)theEvent
+{
+ drawTrack = NO;
+ [self setNeedsDisplay: YES];
+}
+
+@end
diff --git a/frontends/cocoa/BookmarksController.h b/frontends/cocoa/BookmarksController.h
new file mode 100644
index 000000000..aa71a1ac0
--- /dev/null
+++ b/frontends/cocoa/BookmarksController.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@class Tree;
+@class TreeView;
+
+@interface BookmarksController : NSWindowController {
+ Tree *tree;
+ TreeView *view;
+ NSMapTable *nodeForMenu;
+ NSMenu *defaultMenu;
+}
+
+@property (readwrite, assign, nonatomic) IBOutlet NSMenu *defaultMenu;
+@property (readwrite, assign, nonatomic) IBOutlet TreeView *view;
+
+- (IBAction) openBookmarkURL: (id) sender;
+- (IBAction) addBookmark: (id) sender;
+
+- (IBAction) editSelected: (id) sender;
+- (IBAction) deleteSelected: (id) sender;
+- (IBAction) addFolder: (id) sender;
+
+@end
diff --git a/frontends/cocoa/BookmarksController.m b/frontends/cocoa/BookmarksController.m
new file mode 100644
index 000000000..0bd51b273
--- /dev/null
+++ b/frontends/cocoa/BookmarksController.m
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import "utils/messages.h"
+#import "utils/utils.h"
+#import "desktop/browser.h"
+#import "desktop/hotlist.h"
+#import "desktop/tree.h"
+#import "content/hlcache.h"
+
+#import "cocoa/BookmarksController.h"
+#import "cocoa/Tree.h"
+#import "cocoa/TreeView.h"
+#import "cocoa/NetsurfApp.h"
+#import "cocoa/BrowserViewController.h"
+#import "cocoa/gui.h"
+
+
+@interface BookmarksController ()
+- (void) noteAppWillTerminate: (NSNotification *) note;
+- (void) save;
+@end
+
+@implementation BookmarksController
+
+@synthesize defaultMenu;
+@synthesize view;
+
+static const char *cocoa_hotlist_path( void )
+{
+ NSString *path = [[NSUserDefaults standardUserDefaults]
+ stringForKey: kHotlistFileOption];
+ return [path UTF8String];
+}
+
+- (id)init
+{
+ if ((self = [super initWithWindowNibName: @"BookmarksWindow"]) == nil) {
+ return nil;
+ }
+ tree_hotlist_path = cocoa_hotlist_path();
+ tree = [[Tree alloc] initWithFlags: TREE_HOTLIST];
+ nodeForMenu = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
+ NSNonOwnedPointerMapValueCallBacks,
+ 0);
+
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(noteAppWillTerminate:)
+ name:NSApplicationWillTerminateNotification
+ object:NSApp];
+
+ return self;
+}
+
+- (void) noteAppWillTerminate: (NSNotification *)note
+{
+ [self save];
+}
+
+- (void) save
+{
+ hotlist_export( cocoa_hotlist_path(), NULL );
+}
+
+- (void) dealloc
+{
+ [self setView: nil];
+ NSFreeMapTable( nodeForMenu );
+ [tree release];
+
+ [[NSNotificationCenter defaultCenter] removeObserver: self];
+
+ [super dealloc];
+}
+
+- (void) menuNeedsUpdate: (NSMenu *)menu
+{
+#if 0
+ for (NSMenuItem *item in [menu itemArray]) {
+ if ([item hasSubmenu]) NSMapRemove( nodeForMenu, [item submenu] );
+ [menu removeItem: item];
+ }
+
+ bool hasSeparator = true;
+ struct node *node = (struct node *)NSMapGet( nodeForMenu, menu );
+ if (node == NULL) {
+ for (NSMenuItem *item in [defaultMenu itemArray]) {
+ [menu addItem: [[item copy] autorelease]];
+ }
+ hasSeparator = false;
+ }
+
+ for (struct node *child = tree_node_get_child( node );
+ child != NULL;
+ child = tree_node_get_next( child )) {
+
+ if (tree_node_is_deleted( child )) continue;
+
+ if (!hasSeparator) {
+ [menu addItem: [NSMenuItem separatorItem]];
+ hasSeparator = true;
+ }
+
+ NSString *title = [NSString stringWithUTF8String: tree_url_node_get_title( child )];
+
+ NSMenuItem *item = [menu addItemWithTitle: title action: NULL keyEquivalent: @""];
+ if (tree_node_is_folder( child )) {
+ NSMenu *subMenu = [[[NSMenu alloc] initWithTitle: title] autorelease];
+ NSMapInsert( nodeForMenu, subMenu, child );
+ [subMenu setDelegate: self];
+ [menu setSubmenu: subMenu forItem: item];
+ } else {
+ [item setRepresentedObject: [NSString stringWithUTF8String: tree_url_node_get_url( child )]];
+ [item setTarget: self];
+ [item setAction: @selector( openBookmarkURL: )];
+ }
+ }
+#endif
+}
+
+- (IBAction) openBookmarkURL: (id)sender
+{
+ const char *urltxt = [[sender representedObject] UTF8String];
+ NSParameterAssert( urltxt != NULL );
+
+ nsurl *url;
+ nserror error;
+
+ error = nsurl_create(urltxt, &url);
+ if (error == NSERROR_OK) {
+ BrowserViewController *tab = [(NetSurfApp *)NSApp frontTab];
+ if (tab != nil) {
+ error = browser_window_navigate([tab browser],
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ } else {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ }
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ }
+}
+
+- (IBAction) addBookmark: (id)sender
+{
+ struct browser_window *bw = [[(NetSurfApp *)NSApp frontTab] browser];
+ if (bw != NULL) {
+ hotlist_add_url(browser_window_get_url(bw));
+ }
+}
+
+- (BOOL) validateUserInterfaceItem: (id)item
+{
+ SEL action = [item action];
+
+ if (action == @selector( addBookmark: )) {
+ return [(NetSurfApp *)NSApp frontTab] != nil;
+ }
+
+ return YES;
+}
+
+- (void) windowDidLoad
+{
+ hotlist_expand(false);
+ hotlist_contract(true);
+
+ [view setTree: tree];
+}
+
+
++ (void) initialize
+{
+ [[NSUserDefaults standardUserDefaults]
+ registerDefaults:
+ [NSDictionary
+ dictionaryWithObjectsAndKeys:cocoa_get_user_path( @"Bookmarks.html" ),
+ kHotlistFileOption,
+ nil]];
+}
+
+- (IBAction) editSelected: (id)sender
+{
+ hotlist_edit_selection();
+}
+
+- (IBAction) deleteSelected: (id)sender
+{
+ hotlist_keypress(NS_KEY_DELETE_LEFT);
+}
+
+- (IBAction) addFolder: (id)sender
+{
+ hotlist_add_folder(NULL, false, 0);
+}
+
+@end
diff --git a/frontends/cocoa/BrowserView.h b/frontends/cocoa/BrowserView.h
new file mode 100644
index 000000000..c626c8c5b
--- /dev/null
+++ b/frontends/cocoa/BrowserView.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#import "cocoa/ScrollableView.h"
+
+@class LocalHistoryController;
+
+@interface BrowserView : ScrollableView <NSTextInput> {
+ struct browser_window *browser;
+
+ NSPoint caretPoint;
+ CGFloat caretHeight;
+ BOOL caretVisible;
+ BOOL hasCaret;
+ NSTimer *caretTimer;
+
+ BOOL isDragging;
+ NSPoint dragStart;
+
+ BOOL historyVisible;
+ LocalHistoryController *history;
+
+ NSString *markedText;
+}
+
+@property (readwrite, assign, nonatomic) struct browser_window *browser;
+@property (readwrite, retain, nonatomic) NSTimer *caretTimer;
+@property (readwrite, assign, nonatomic, getter=isHistoryVisible) BOOL historyVisible;
+
+- (void) removeCaret;
+- (void) addCaretAt: (NSPoint) point height: (CGFloat) height;
+
+- (void) reformat;
+- (void) updateHistory;
+
+@end
diff --git a/frontends/cocoa/BrowserView.m b/frontends/cocoa/BrowserView.m
new file mode 100644
index 000000000..fc50dbc64
--- /dev/null
+++ b/frontends/cocoa/BrowserView.m
@@ -0,0 +1,757 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import "utils/nsoption.h"
+#import "utils/messages.h"
+#import "desktop/browser.h"
+#import "desktop/plotters.h"
+#import "desktop/textinput.h"
+#import "content/hlcache.h"
+
+#import "cocoa/gui.h"
+#import "cocoa/BrowserView.h"
+#import "cocoa/HistoryView.h"
+#import "cocoa/font.h"
+#import "cocoa/plotter.h"
+#import "cocoa/LocalHistoryController.h"
+#import "cocoa/BrowserWindowController.h"
+
+
+@interface BrowserView ()
+
+@property (readwrite, copy, nonatomic) NSString *markedText;
+
+- (void) scrollHorizontal: (CGFloat) amount;
+- (void) scrollVertical: (CGFloat) amount;
+- (CGFloat) pageScroll;
+
+- (void) reformat;
+
+- (void) popUpContextMenuForEvent: (NSEvent *) event;
+
+- (IBAction) cmOpenURLInTab: (id) sender;
+- (IBAction) cmOpenURLInWindow: (id) sender;
+- (IBAction) cmDownloadURL: (id) sender;
+
+- (IBAction) cmLinkCopy: (id) sender;
+- (IBAction) cmImageCopy: (id) sender;
+
+@end
+
+@implementation BrowserView
+
+@synthesize browser;
+@synthesize caretTimer;
+@synthesize markedText;
+
+static const CGFloat CaretWidth = 1.0;
+static const NSTimeInterval CaretBlinkTime = 0.8;
+
+- initWithFrame: (NSRect) frame
+{
+ if ((self = [super initWithFrame: frame]) == nil) {
+ return nil;
+ }
+
+ [self registerForDraggedTypes: [NSArray arrayWithObjects: NSURLPboardType, @"public.url", nil]];
+
+ return self;
+}
+
+- (void) dealloc
+{
+ [self setCaretTimer: nil];
+ [self setMarkedText: nil];
+ [history release];
+
+ [super dealloc];
+}
+
+- (void) setCaretTimer: (NSTimer *)newTimer
+{
+ if (newTimer != caretTimer) {
+ [caretTimer invalidate];
+ [caretTimer release];
+ caretTimer = [newTimer retain];
+ }
+}
+
+- (void) updateHistory
+{
+ [history redraw];
+}
+
+static inline NSRect cocoa_get_caret_rect( BrowserView *view )
+{
+ float bscale = browser_window_get_scale(view->browser);
+
+ NSRect caretRect = {
+ .origin = NSMakePoint( view->caretPoint.x * bscale, view->caretPoint.y * bscale ),
+ .size = NSMakeSize( CaretWidth, view->caretHeight * bscale )
+ };
+
+ return caretRect;
+}
+
+- (void) removeCaret
+{
+ hasCaret = NO;
+ [self setNeedsDisplayInRect: cocoa_get_caret_rect( self )];
+
+ [self setCaretTimer: nil];
+}
+
+- (void) addCaretAt: (NSPoint) point height: (CGFloat) height
+{
+ if (hasCaret) {
+ [self setNeedsDisplayInRect: cocoa_get_caret_rect( self )];
+ }
+
+ caretPoint = point;
+ caretHeight = height;
+ hasCaret = YES;
+ caretVisible = YES;
+
+ if (nil == caretTimer) {
+ [self setCaretTimer: [NSTimer scheduledTimerWithTimeInterval: CaretBlinkTime target: self selector: @selector(caretBlink:) userInfo: nil repeats: YES]];
+ } else {
+ [caretTimer setFireDate: [NSDate dateWithTimeIntervalSinceNow: CaretBlinkTime]];
+ }
+
+ [self setNeedsDisplayInRect: cocoa_get_caret_rect( self )];
+}
+
+
+- (void) caretBlink: (NSTimer *)timer
+{
+ if (hasCaret) {
+ caretVisible = !caretVisible;
+ [self setNeedsDisplayInRect: cocoa_get_caret_rect( self )];
+ }
+}
+
+- (void)drawRect:(NSRect)dirtyRect
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &cocoa_plotters
+ };
+
+ const NSRect *rects = NULL;
+ NSInteger count = 0;
+ [self getRectsBeingDrawn: &rects count: &count];
+
+ for (NSInteger i = 0; i < count; i++) {
+ const struct rect clip = {
+ .x0 = cocoa_pt_to_px( NSMinX( rects[i] ) ),
+ .y0 = cocoa_pt_to_px( NSMinY( rects[i] ) ),
+ .x1 = cocoa_pt_to_px( NSMaxX( rects[i] ) ),
+ .y1 = cocoa_pt_to_px( NSMaxY( rects[i] ) )
+ };
+
+ browser_window_redraw(browser, 0, 0, &clip, &ctx);
+ }
+
+ NSRect caretRect = cocoa_get_caret_rect( self );
+ if (hasCaret && caretVisible && [self needsToDrawRect: caretRect]) {
+ [[NSColor blackColor] set];
+ [NSBezierPath fillRect: caretRect];
+ }
+
+ [pool release];
+}
+
+- (BOOL) isFlipped
+{
+ return YES;
+}
+
+- (void) viewDidMoveToWindow
+{
+ NSTrackingArea *area = [[NSTrackingArea alloc] initWithRect: [self visibleRect]
+ options: NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect
+ owner: self
+ userInfo: nil];
+ [self addTrackingArea: area];
+ [area release];
+}
+
+static browser_mouse_state cocoa_mouse_flags_for_event( NSEvent *evt )
+{
+ browser_mouse_state result = 0;
+ NSUInteger flags = [evt modifierFlags];
+
+ if (flags & NSShiftKeyMask) result |= BROWSER_MOUSE_MOD_1;
+ if (flags & NSAlternateKeyMask) result |= BROWSER_MOUSE_MOD_2;
+
+ return result;
+}
+
+- (NSPoint) convertMousePoint: (NSEvent *)event
+{
+ NSPoint location = [self convertPoint: [event locationInWindow] fromView: nil];
+ float bscale = browser_window_get_scale(browser);
+
+ location.x /= bscale;
+ location.y /= bscale;
+
+ location.x = cocoa_pt_to_px( location.x );
+ location.y = cocoa_pt_to_px( location.y );
+ return location;
+}
+
+- (void) mouseDown: (NSEvent *)theEvent
+{
+ if ([theEvent modifierFlags] & NSControlKeyMask) {
+ [self popUpContextMenuForEvent: theEvent];
+ return;
+ }
+
+ dragStart = [self convertMousePoint: theEvent];
+
+ browser_window_mouse_click(browser,
+ BROWSER_MOUSE_PRESS_1 | cocoa_mouse_flags_for_event( theEvent ),
+ dragStart.x,
+ dragStart.y );
+}
+
+- (void) rightMouseDown: (NSEvent *)theEvent
+{
+ [self popUpContextMenuForEvent: theEvent];
+}
+
+- (void) mouseUp: (NSEvent *)theEvent
+{
+ if (historyVisible) {
+ [self setHistoryVisible: NO];
+ return;
+ }
+
+ NSPoint location = [self convertMousePoint: theEvent];
+
+ browser_mouse_state modifierFlags = cocoa_mouse_flags_for_event( theEvent );
+
+ if (isDragging) {
+ isDragging = NO;
+ browser_window_mouse_track( browser, (browser_mouse_state)0, location.x, location.y );
+ } else {
+ modifierFlags |= BROWSER_MOUSE_CLICK_1;
+ if ([theEvent clickCount] == 2) modifierFlags |= BROWSER_MOUSE_DOUBLE_CLICK;
+ browser_window_mouse_click( browser, modifierFlags, location.x, location.y );
+ }
+}
+
+#define squared(x) ((x)*(x))
+#define MinDragDistance (5.0)
+
+- (void) mouseDragged: (NSEvent *)theEvent
+{
+ NSPoint location = [self convertMousePoint: theEvent];
+ browser_mouse_state modifierFlags = cocoa_mouse_flags_for_event( theEvent );
+
+ if (!isDragging) {
+ const CGFloat distance = squared( dragStart.x - location.x ) + squared( dragStart.y - location.y );
+
+ if (distance >= squared( MinDragDistance)) {
+ isDragging = YES;
+ browser_window_mouse_click(browser,
+ BROWSER_MOUSE_DRAG_1 | modifierFlags,
+ dragStart.x,
+ dragStart.y);
+ }
+ }
+
+ if (isDragging) {
+ browser_window_mouse_track(browser,
+ BROWSER_MOUSE_HOLDING_1 | BROWSER_MOUSE_DRAG_ON | modifierFlags,
+ location.x,
+ location.y );
+ }
+}
+
+- (void) mouseMoved: (NSEvent *)theEvent
+{
+ if (historyVisible) return;
+
+ NSPoint location = [self convertMousePoint: theEvent];
+
+ browser_window_mouse_track(browser,
+ cocoa_mouse_flags_for_event(theEvent),
+ location.x,
+ location.y);
+}
+
+- (void) mouseExited: (NSEvent *) theEvent
+{
+ [[NSCursor arrowCursor] set];
+}
+
+- (void) keyDown: (NSEvent *)theEvent
+{
+ if (!historyVisible) {
+ [self interpretKeyEvents: [NSArray arrayWithObject: theEvent]];
+ } else {
+ [history keyDown: theEvent];
+ }
+}
+
+- (void) insertText: (id)string
+{
+ for (NSUInteger i = 0, length = [string length]; i < length; i++) {
+ unichar ch = [string characterAtIndex: i];
+ if (!browser_window_key_press( browser, ch )) {
+ if (ch == ' ') [self scrollPageDown: self];
+ break;
+ }
+ }
+ [self setMarkedText: nil];
+}
+
+- (void) moveLeft: (id)sender
+{
+ if (browser_window_key_press( browser, NS_KEY_LEFT )) return;
+ [self scrollHorizontal: -[[self enclosingScrollView] horizontalLineScroll]];
+}
+
+- (void) moveRight: (id)sender
+{
+ if (browser_window_key_press( browser, NS_KEY_RIGHT )) return;
+ [self scrollHorizontal: [[self enclosingScrollView] horizontalLineScroll]];
+}
+
+- (void) moveUp: (id)sender
+{
+ if (browser_window_key_press( browser, NS_KEY_UP )) return;
+ [self scrollVertical: -[[self enclosingScrollView] lineScroll]];
+}
+
+- (void) moveDown: (id)sender
+{
+ if (browser_window_key_press( browser, NS_KEY_DOWN )) return;
+ [self scrollVertical: [[self enclosingScrollView] lineScroll]];
+}
+
+- (void) deleteBackward: (id)sender
+{
+ if (!browser_window_key_press( browser, NS_KEY_DELETE_LEFT )) {
+ [NSApp sendAction: @selector( goBack: ) to: nil from: self];
+ }
+}
+
+- (void) deleteForward: (id)sender
+{
+ browser_window_key_press( browser, NS_KEY_DELETE_RIGHT );
+}
+
+- (void) cancelOperation: (id)sender
+{
+ browser_window_key_press( browser, NS_KEY_ESCAPE );
+}
+
+- (void) scrollPageUp: (id)sender
+{
+ if (browser_window_key_press( browser, NS_KEY_PAGE_UP )) {
+ return;
+ }
+ [self scrollVertical: -[self pageScroll]];
+}
+
+- (void) scrollPageDown: (id)sender
+{
+ if (browser_window_key_press( browser, NS_KEY_PAGE_DOWN )) {
+ return;
+ }
+ [self scrollVertical: [self pageScroll]];
+}
+
+- (void) insertTab: (id)sender
+{
+ browser_window_key_press( browser, NS_KEY_TAB );
+}
+
+- (void) insertBacktab: (id)sender
+{
+ browser_window_key_press( browser, NS_KEY_SHIFT_TAB );
+}
+
+- (void) moveToBeginningOfLine: (id)sender
+{
+ browser_window_key_press( browser, NS_KEY_LINE_START );
+}
+
+- (void) moveToEndOfLine: (id)sender
+{
+ browser_window_key_press( browser, NS_KEY_LINE_END );
+}
+
+- (void) moveToBeginningOfDocument: (id)sender
+{
+ if (browser_window_key_press( browser, NS_KEY_TEXT_START )) return;
+}
+
+- (void) scrollToBeginningOfDocument: (id) sender
+{
+ NSPoint origin = [self visibleRect].origin;
+ origin.y = 0;
+ [self scrollPoint: origin];
+}
+
+- (void) moveToEndOfDocument: (id)sender
+{
+ browser_window_key_press( browser, NS_KEY_TEXT_END );
+}
+
+- (void) scrollToEndOfDocument: (id) sender
+{
+ NSPoint origin = [self visibleRect].origin;
+ origin.y = NSHeight( [self frame] );
+ [self scrollPoint: origin];
+}
+
+- (void) insertNewline: (id)sender
+{
+ browser_window_key_press( browser, NS_KEY_NL );
+}
+
+- (void) selectAll: (id)sender
+{
+ browser_window_key_press( browser, NS_KEY_SELECT_ALL );
+}
+
+- (void) copy: (id)sender
+{
+ browser_window_key_press( browser, NS_KEY_COPY_SELECTION );
+}
+
+- (void) cut: (id)sender
+{
+ browser_window_key_press( browser, NS_KEY_CUT_SELECTION );
+}
+
+- (void) paste: (id)sender
+{
+ browser_window_key_press( browser, NS_KEY_PASTE );
+}
+
+- (BOOL) acceptsFirstResponder
+{
+ return YES;
+}
+
+- (void) adjustFrame
+{
+ browser_window_schedule_reformat(browser);
+
+ [super adjustFrame];
+}
+
+- (BOOL) isHistoryVisible
+{
+ return historyVisible;
+}
+
+- (void) setHistoryVisible: (BOOL)newVisible
+{
+ if (newVisible == historyVisible) return;
+ historyVisible = newVisible;
+
+ if (historyVisible) {
+ if (nil == history) {
+ history = [[LocalHistoryController alloc] initWithBrowser: self];
+ }
+ [history attachToView: [(BrowserWindowController *)[[self window] windowController] historyButton]];
+ } else {
+ [history detach];
+ }
+}
+
+- (void) scrollHorizontal: (CGFloat) amount
+{
+ NSPoint currentPoint = [self visibleRect].origin;
+ currentPoint.x += amount;
+ [self scrollPoint: currentPoint];
+}
+
+- (void) scrollVertical: (CGFloat) amount
+{
+ NSPoint currentPoint = [self visibleRect].origin;
+ currentPoint.y += amount;
+ [self scrollPoint: currentPoint];
+}
+
+- (CGFloat) pageScroll
+{
+ return NSHeight( [[self superview] frame] ) - [[self enclosingScrollView] pageScroll];
+}
+
+- (void) reformat
+{
+ NSRect size = [[self superview] frame];
+ browser_window_reformat(browser,
+ false,
+ cocoa_pt_to_px( NSWidth( size ) ),
+ cocoa_pt_to_px( NSHeight( size ) ) );
+}
+
+- (void) popUpContextMenuForEvent: (NSEvent *) event
+{
+ NSMenu *popupMenu = [[NSMenu alloc] initWithTitle: @""];
+ NSPoint point = [self convertMousePoint: event];
+
+ struct browser_window_features cont;
+
+ browser_window_get_features(browser, point.x, point.y, &cont);
+
+ if (cont.object != NULL) {
+ NSString *imageURL = [NSString stringWithUTF8String: nsurl_access(hlcache_handle_get_url( cont.object ))];
+
+ [[popupMenu addItemWithTitle: NSLocalizedString( @"Open image in new tab", @"Context menu" )
+ action: @selector(cmOpenURLInTab:)
+ keyEquivalent: @""] setRepresentedObject: imageURL];
+ [[popupMenu addItemWithTitle: NSLocalizedString( @"Open image in new window", @"Context menu" )
+ action: @selector(cmOpenURLInWindow:)
+ keyEquivalent: @""] setRepresentedObject: imageURL];
+ [[popupMenu addItemWithTitle: NSLocalizedString( @"Save image as", @"Context menu" )
+ action: @selector(cmDownloadURL:)
+ keyEquivalent: @""] setRepresentedObject: imageURL];
+ [[popupMenu addItemWithTitle: NSLocalizedString( @"Copy image", @"Context menu" )
+ action: @selector(cmImageCopy:)
+ keyEquivalent: @""] setRepresentedObject: (id)content_get_bitmap( cont.object )];
+
+ [popupMenu addItem: [NSMenuItem separatorItem]];
+ }
+
+ if (cont.link != NULL) {
+ NSString *target = [NSString stringWithUTF8String: nsurl_access(cont.link)];
+
+ [[popupMenu addItemWithTitle: NSLocalizedString( @"Open link in new tab", @"Context menu" )
+ action: @selector(cmOpenURLInTab:)
+ keyEquivalent: @""] setRepresentedObject: target];
+ [[popupMenu addItemWithTitle: NSLocalizedString( @"Open link in new window", @"Context menu" )
+ action: @selector(cmOpenURLInWindow:)
+ keyEquivalent: @""] setRepresentedObject: target];
+ [[popupMenu addItemWithTitle: NSLocalizedString( @"Save link target", @"Context menu" )
+ action: @selector(cmDownloadURL:)
+ keyEquivalent: @""] setRepresentedObject: target];
+ [[popupMenu addItemWithTitle: NSLocalizedString( @"Copy link", @"Context menu" )
+ action: @selector(cmLinkCopy:)
+ keyEquivalent: @""] setRepresentedObject: target];
+
+ [popupMenu addItem: [NSMenuItem separatorItem]];
+ }
+
+ [popupMenu addItemWithTitle: NSLocalizedString( @"Back", @"Context menu" )
+ action: @selector(goBack:) keyEquivalent: @""];
+ [popupMenu addItemWithTitle: NSLocalizedString( @"Reload", @"Context menu" )
+ action: @selector(reloadPage:) keyEquivalent: @""];
+ [popupMenu addItemWithTitle: NSLocalizedString( @"Forward", @"Context menu" )
+ action: @selector(goForward:) keyEquivalent: @""];
+ [popupMenu addItemWithTitle: NSLocalizedString( @"View Source", @"Context menu" )
+ action: @selector(viewSource:) keyEquivalent: @""];
+
+ [NSMenu popUpContextMenu: popupMenu withEvent: event forView: self];
+
+ [popupMenu release];
+}
+
+- (IBAction) cmOpenURLInTab: (id)sender
+{
+ nsurl *url;
+ nserror error;
+
+ error = nsurl_create([[sender representedObject] UTF8String], &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY |
+ BW_CREATE_TAB |
+ BW_CREATE_CLONE,
+ url,
+ NULL,
+ browser,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ }
+}
+
+- (IBAction) cmOpenURLInWindow: (id)sender
+{
+ nsurl *url;
+ nserror error;
+
+ error = nsurl_create([[sender representedObject] UTF8String], &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY |
+ BW_CREATE_CLONE,
+ url,
+ NULL,
+ browser,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ }
+}
+
+- (IBAction) cmDownloadURL: (id)sender
+{
+ nsurl *url;
+
+ if (nsurl_create([[sender representedObject] UTF8String], &url) == NSERROR_OK) {
+ browser_window_navigate(browser,
+ url,
+ NULL,
+ BW_NAVIGATE_DOWNLOAD,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+}
+
+- (IBAction) cmImageCopy: (id)sender
+{
+ NSPasteboard *pb = [NSPasteboard generalPasteboard];
+ [pb declareTypes: [NSArray arrayWithObject: NSTIFFPboardType] owner: nil];
+ [pb setData: [[sender representedObject] TIFFRepresentation] forType: NSTIFFPboardType];
+}
+
+- (IBAction) cmLinkCopy: (id)sender
+{
+ NSPasteboard *pb = [NSPasteboard generalPasteboard];
+ [pb declareTypes: [NSArray arrayWithObject: NSStringPboardType] owner: nil];
+ [pb setString: [sender representedObject] forType: NSStringPboardType];
+}
+
+
+// MARK: -
+// MARK: Accepting dragged URLs
+
+- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
+{
+ if ((NSDragOperationCopy | NSDragOperationGeneric) & [sender draggingSourceOperationMask]) {
+ return NSDragOperationCopy;
+ }
+
+ return NSDragOperationNone;
+}
+
+- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
+{
+ return YES;
+}
+
+- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
+{
+ nsurl *url;
+ nserror error;
+
+ NSPasteboard *pb = [sender draggingPasteboard];
+
+ NSString *type = [pb availableTypeFromArray:[NSArray arrayWithObjects: @"public.url", NSURLPboardType, nil]];
+
+ NSString *urlstr = nil;
+
+ if ([type isEqualToString: NSURLPboardType]) {
+ urlstr = [[NSURL URLFromPasteboard: pb] absoluteString];
+ } else {
+ urlstr = [pb stringForType: type];
+ }
+
+ error = nsurl_create([urlstr UTF8String], &url);
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ } else {
+ browser_window_navigate(browser,
+ url,
+ NULL,
+ BW_NAVIGATE_DOWNLOAD,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+
+ return YES;
+}
+
+// MARK: -
+// MARK: NSTextInput protocol implementation
+
+- (void) setMarkedText: (id) aString selectedRange: (NSRange) selRange
+{
+ [markedText release];
+ markedText = [aString isEqualToString: @""] ? nil : [aString copy];
+}
+
+- (void) unmarkText
+{
+ [self setMarkedText: nil];
+}
+
+- (BOOL) hasMarkedText
+{
+ return markedText != nil;
+}
+
+- (NSInteger) conversationIdentifier
+{
+ return (NSInteger)self;
+}
+
+- (NSAttributedString *) attributedSubstringFromRange: (NSRange) theRange
+{
+ return [[[NSAttributedString alloc] initWithString: @""] autorelease];
+}
+
+- (NSRange) markedRange
+{
+ return NSMakeRange( NSNotFound, 0 );
+}
+
+- (NSRange) selectedRange
+{
+ return NSMakeRange( NSNotFound, 0 );
+}
+
+- (NSRect) firstRectForCharacterRange: (NSRange) theRange
+{
+ return NSZeroRect;
+}
+
+- (NSUInteger) characterIndexForPoint: (NSPoint) thePoint
+{
+ return 0;
+}
+
+- (NSArray *) validAttributesForMarkedText
+{
+ return [NSArray array];
+}
+
+- (void) doCommandBySelector: (SEL) sel;
+{
+ [super doCommandBySelector: sel];
+}
+
+@end
diff --git a/frontends/cocoa/BrowserViewController.h b/frontends/cocoa/BrowserViewController.h
new file mode 100644
index 000000000..6b4c3e79c
--- /dev/null
+++ b/frontends/cocoa/BrowserViewController.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+
+#import <Cocoa/Cocoa.h>
+
+struct browser_window;
+@class BrowserView;
+@class BrowserWindowController;
+
+@interface BrowserViewController : NSViewController {
+ struct browser_window *browser;
+ NSString *url;
+ BrowserView *browserView;
+ BrowserWindowController *windowController;
+ NSString *title;
+ NSString *status;
+ BOOL isProcessing;
+ NSImage *favicon;
+ BOOL canGoBack;
+ BOOL canGoForward;
+}
+
+@property (readwrite, assign, nonatomic) struct browser_window *browser;
+@property (readwrite, copy, nonatomic) NSString *url;
+@property (readwrite, assign, nonatomic) IBOutlet BrowserView *browserView;
+@property (readwrite, retain, nonatomic) BrowserWindowController *windowController;
+@property (readwrite, copy, nonatomic) NSString *title;
+@property (readwrite, copy, nonatomic) NSString *status;
+@property (readwrite, assign, nonatomic) BOOL isProcessing;
+@property (readwrite, copy, nonatomic) NSImage *favicon;
+@property (readwrite, assign, nonatomic) BOOL canGoBack;
+@property (readwrite, assign, nonatomic) BOOL canGoForward;
+
+- (id) initWithBrowser: (struct browser_window *) bw;
+
+- (void) contentUpdated;
+- (void) updateBackForward;
+
+- (IBAction) navigate: (id) sender;
+
+- (IBAction) backForwardSelected: (id) sender;
+
+- (IBAction) goHome: (id) sender;
+
+- (IBAction) goBack: (id) sender;
+- (IBAction) goForward: (id) sender;
+- (IBAction) reloadPage: (id) sender;
+- (IBAction) stopLoading: (id) sender;
+
+- (IBAction) zoomIn: (id) sender;
+- (IBAction) zoomOut: (id) sender;
+- (IBAction) zoomOriginal: (id) sender;
+
+- (IBAction) viewSource: (id) sender;
+
+- (void) buildBackMenu: (NSMenu *)menu;
+- (void) buildForwardMenu: (NSMenu *)menu;
+
+@end
diff --git a/frontends/cocoa/BrowserViewController.m b/frontends/cocoa/BrowserViewController.m
new file mode 100644
index 000000000..5fa8b642d
--- /dev/null
+++ b/frontends/cocoa/BrowserViewController.m
@@ -0,0 +1,378 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import "utils/nsoption.h"
+#import "utils/corestrings.h"
+#import "utils/filename.h"
+#import "utils/file.h"
+#import "utils/messages.h"
+#import "utils/url.h"
+#import "desktop/browser_history.h"
+#import "desktop/browser.h"
+#import "desktop/textinput.h"
+#import "content/hlcache.h"
+
+#import "cocoa/gui.h"
+#import "cocoa/BrowserViewController.h"
+#import "cocoa/BrowserView.h"
+#import "cocoa/BrowserWindowController.h"
+#import "cocoa/fetch.h"
+
+
+
+@implementation BrowserViewController
+
+@synthesize browser;
+@synthesize url;
+@synthesize browserView;
+@synthesize windowController;
+@synthesize title;
+@synthesize status;
+@synthesize isProcessing;
+@synthesize favicon;
+@synthesize canGoBack;
+@synthesize canGoForward;
+
+- (void) dealloc
+{
+ [self setUrl: nil];
+ [self setBrowserView: nil];
+ [self setWindowController: nil];
+ [self setTitle: nil];
+ [self setStatus: nil];
+ [self setFavicon: nil];
+
+ [super dealloc];
+}
+
+- initWithBrowser: (struct browser_window *) bw
+{
+ if ((self = [super initWithNibName: @"Browser" bundle: nil]) == nil) {
+ return nil;
+ }
+
+ browser = bw;
+
+ return self;
+}
+
+
+- (IBAction) navigate: (id) sender
+{
+ nsurl *urlns;
+ nserror error;
+
+ error = nsurl_create([url UTF8String], &urlns);
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ } else {
+ browser_window_navigate(browser,
+ urlns,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(urlns);
+ }
+}
+
+
+- (void) awakeFromNib
+{
+ [browserView setBrowser: browser];
+}
+
+
+- (IBAction) zoomIn: (id) sender
+{
+ browser_window_set_scale(browser,
+ browser_window_get_scale(browser) * 1.1,
+ true);
+}
+
+
+- (IBAction) zoomOut: (id) sender
+{
+ browser_window_set_scale(browser,
+ browser_window_get_scale(browser) * 0.9,
+ true);
+}
+
+
+- (IBAction) zoomOriginal: (id) sender
+{
+ browser_window_set_scale(browser,
+ (float)nsoption_int(scale) / 100.0,
+ true);
+}
+
+
+- (IBAction) backForwardSelected: (id) sender
+{
+ if ([sender selectedSegment] == 0) {
+ [self goBack: sender];
+ } else {
+ [self goForward: sender];
+ }
+}
+
+
+- (IBAction) goBack: (id) sender
+{
+ if (browser && browser_window_history_back_available( browser )) {
+ browser_window_history_back(browser, false);
+ [self updateBackForward];
+ }
+}
+
+
+- (IBAction) goForward: (id) sender
+{
+ if (browser && browser_window_history_forward_available( browser )) {
+ browser_window_history_forward(browser, false);
+ [self updateBackForward];
+ }
+}
+
+
+- (IBAction) goHome: (id) sender
+{
+ nsurl *urlns;
+ nserror error;
+
+ error = nsurl_create(nsoption_charp(homepage_url), &urlns);
+ if (error == NSERROR_OK) {
+ error = browser_window_navigate(browser,
+ urlns,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(urlns);
+ }
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ }
+}
+
+
+- (IBAction) reloadPage: (id) sender
+{
+ browser_window_reload( browser, true );
+}
+
+
+- (IBAction) stopLoading: (id) sender
+{
+ browser_window_stop( browser );
+}
+
+
+- (IBAction) viewSource: (id) sender
+{
+ struct hlcache_handle *content;
+ size_t size;
+ const char *source;
+ char *path = NULL;
+
+ if (browser == NULL) {
+ return;
+ }
+ content = browser_window_get_content(browser);
+ if (content == NULL) {
+ return;
+ }
+ source = content_get_source_data(content, &size);
+ if (source == NULL) {
+ return;
+ }
+
+ /* try to load local files directly. */
+ netsurf_nsurl_to_path(hlcache_handle_get_url(content), &path);
+
+ if (path == NULL) {
+ /* We cannot release the requested filename until after it
+ * has finished being used. As we can't easily find out when
+ * this is, we simply don't bother releasing it and simply
+ * allow it to be re-used next time NetSurf is started. The
+ * memory overhead from doing this is under 1 byte per
+ * filename. */
+ const char *filename = filename_request();
+ const char *extension = "txt";
+ fprintf(stderr, "filename '%p'\n", filename);
+ if (filename == NULL)
+ return;
+ lwc_string *str = content_get_mime_type(content);
+ if (str) {
+ NSString *mime = [NSString stringWithUTF8String:lwc_string_data(str)];
+ NSString *uti = (NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (CFStringRef)mime, NULL);
+ NSString *ext = (NSString *)UTTypeCopyPreferredTagWithClass((CFStringRef)uti, kUTTagClassFilenameExtension);
+ extension = [ext UTF8String];
+ lwc_string_unref(str);
+ }
+
+ NSURL *dataUrl = [NSURL URLWithString:[NSString stringWithFormat:@"%s.%s", filename, extension]
+ relativeToURL:[NSURL fileURLWithPath:@TEMP_FILENAME_PREFIX]];
+
+
+ NSData *data = [NSData dataWithBytes:source length:size];
+ [data writeToURL:dataUrl atomically:NO];
+ path = [[dataUrl path] UTF8String];
+ }
+
+ if (path) {
+ NSString * p = [NSString stringWithUTF8String: path];
+ NSWorkspace * ws = [NSWorkspace sharedWorkspace];
+ [ws openFile:p withApplication:@"Xcode"];
+ }
+}
+
+
+static inline bool
+compare_float( float a, float b )
+{
+ const float epsilon = 0.00001;
+
+ if (a == b) {
+ return true;
+ }
+
+ return fabs( (a - b) / b ) <= epsilon;
+}
+
+- (BOOL) validateUserInterfaceItem: (id) item
+{
+ SEL action = [item action];
+
+ if (action == @selector(copy:)) {
+ return browser_window_get_editor_flags( browser ) & BW_EDITOR_CAN_COPY;
+ }
+
+ if (action == @selector(cut:)) {
+ return browser_window_get_editor_flags( browser ) & BW_EDITOR_CAN_CUT;
+ }
+
+ if (action == @selector(paste:)) {
+ return browser_window_get_editor_flags( browser ) & BW_EDITOR_CAN_PASTE;
+ }
+
+ if (action == @selector( stopLoading: )) {
+ return browser_window_stop_available( browser );
+ }
+
+ if (action == @selector( zoomOriginal: )) {
+ return !compare_float( browser_window_get_scale(browser), (float)nsoption_int(scale) / 100.0 );
+ }
+
+ if (action == @selector( goBack: )) {
+ return canGoBack;
+ }
+
+ if (action == @selector( goForward: )) {
+ return canGoForward;
+ }
+
+ return YES;
+}
+
+
+- (void) updateBackForward
+{
+ [browserView updateHistory];
+ [self setCanGoBack: browser != NULL && browser_window_history_back_available( browser )];
+ [self setCanGoForward: browser != NULL && browser_window_history_forward_available( browser )];
+}
+
+- (void) contentUpdated
+{
+ [browserView updateHistory];
+}
+
+struct history_add_menu_item_data {
+ NSInteger index;
+ NSMenu *menu;
+ id target;
+};
+
+static bool
+history_add_menu_item_cb(const struct browser_window *bw,
+ int x0, int y0, int x1, int y1,
+ const struct history_entry *page,
+ void *user_data )
+{
+ struct history_add_menu_item_data *data = user_data;
+
+ NSMenuItem *item = nil;
+ if (data->index < [data->menu numberOfItems]) {
+ item = [data->menu itemAtIndex: data->index];
+ } else {
+ item = [[NSMenuItem alloc] initWithTitle: @""
+ action: @selector( historyItemSelected: )
+ keyEquivalent: @""];
+ [data->menu addItem: item];
+ [item release];
+ }
+ ++data->index;
+
+ [item setTarget: data->target];
+ [item setTitle: [NSString stringWithUTF8String: browser_window_history_entry_get_title( page )]];
+ [item setRepresentedObject: [NSValue valueWithPointer: page]];
+
+ return true;
+}
+
+- (IBAction) historyItemSelected: (id) sender
+{
+ struct history_entry *entry = [[sender representedObject] pointerValue];
+ browser_window_history_go( browser, entry, false );
+ [self updateBackForward];
+}
+
+- (void) buildBackMenu: (NSMenu *)menu
+{
+ struct history_add_menu_item_data data = {
+ .index = 0,
+ .menu = menu,
+ .target = self
+ };
+ browser_window_history_enumerate_back(browser,
+ history_add_menu_item_cb,
+ &data);
+ while (data.index < [menu numberOfItems]) {
+ [menu removeItemAtIndex: data.index];
+ }
+}
+
+- (void) buildForwardMenu: (NSMenu *)menu
+{
+ struct history_add_menu_item_data data = {
+ .index = 0,
+ .menu = menu,
+ .target = self
+ };
+ browser_window_history_enumerate_forward(browser,
+ history_add_menu_item_cb,
+ &data);
+ while (data.index < [menu numberOfItems]) {
+ [menu removeItemAtIndex: data.index];
+ }
+}
+
+@end
diff --git a/frontends/cocoa/BrowserWindow.h b/frontends/cocoa/BrowserWindow.h
new file mode 100644
index 000000000..e0b83017f
--- /dev/null
+++ b/frontends/cocoa/BrowserWindow.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+
+@interface BrowserWindow : NSWindow {
+
+}
+
+@end
diff --git a/frontends/cocoa/BrowserWindow.m b/frontends/cocoa/BrowserWindow.m
new file mode 100644
index 000000000..f01b17a25
--- /dev/null
+++ b/frontends/cocoa/BrowserWindow.m
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import "cocoa/BrowserWindow.h"
+#import "cocoa/BrowserWindowController.h"
+
+@implementation BrowserWindow
+
+- (void) performClose: (id) sender
+{
+ [[self windowController] closeCurrentTab: sender];
+}
+
+@end
diff --git a/frontends/cocoa/BrowserWindowController.h b/frontends/cocoa/BrowserWindowController.h
new file mode 100644
index 000000000..22e199509
--- /dev/null
+++ b/frontends/cocoa/BrowserWindowController.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@class PSMTabBarControl;
+@class BrowserViewController;
+@class URLFieldCell;
+
+@interface BrowserWindowController : NSWindowController {
+ PSMTabBarControl *tabBar;
+ NSTabView *tabView;
+ URLFieldCell *urlField;
+ NSObjectController *activeBrowserController;
+ NSSegmentedControl *navigationControl;
+ NSButton *historyButton;
+ BrowserViewController *activeBrowser;
+ NSMenu *historyBackMenu;
+ NSMenu *historyForwardMenu;
+}
+
+@property (readwrite, assign, nonatomic) IBOutlet PSMTabBarControl *tabBar;
+@property (readwrite, assign, nonatomic) IBOutlet NSTabView *tabView;
+@property (readwrite, assign, nonatomic) IBOutlet URLFieldCell *urlField;
+@property (readwrite, assign, nonatomic) IBOutlet NSObjectController *activeBrowserController;
+@property (readwrite, assign, nonatomic) IBOutlet NSSegmentedControl *navigationControl;
+@property (readwrite, assign, nonatomic) IBOutlet NSButton *historyButton;
+@property (readwrite, assign, nonatomic) IBOutlet NSMenu *historyBackMenu;
+@property (readwrite, assign, nonatomic) IBOutlet NSMenu *historyForwardMenu;
+
+@property (readwrite, assign, nonatomic) BrowserViewController *activeBrowser;
+
+@property (readwrite, assign, nonatomic) BOOL canGoBack;
+@property (readwrite, assign, nonatomic) BOOL canGoForward;
+
+- (IBAction) newTab: (id) sender;
+- (IBAction) closeCurrentTab: (id) sender;
+
+- (void) addTab: (BrowserViewController *)browser;
+- (void) removeTab: (BrowserViewController *)browser;
+
+@end
diff --git a/frontends/cocoa/BrowserWindowController.m b/frontends/cocoa/BrowserWindowController.m
new file mode 100644
index 000000000..bfb8be05e
--- /dev/null
+++ b/frontends/cocoa/BrowserWindowController.m
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import "desktop/browser.h"
+#import "utils/nsoption.h"
+#import "utils/messages.h"
+#import "utils/utils.h"
+#import "utils/nsurl.h"
+
+#import "cocoa/BrowserWindowController.h"
+
+#import "cocoa/BrowserViewController.h"
+#import "cocoa/PSMTabBarControl/PSMTabBarControl.h"
+#import "cocoa/PSMTabBarControl/PSMRolloverButton.h"
+#import "cocoa/URLFieldCell.h"
+#import "cocoa/gui.h"
+#import "cocoa/NetsurfApp.h"
+
+
+@interface BrowserWindowController ()
+
+- (void) canCloseAlertDidEnd:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void *)contextInfo;
+
+@end
+
+
+@implementation BrowserWindowController
+
+@synthesize tabBar;
+@synthesize tabView;
+@synthesize urlField;
+@synthesize navigationControl;
+@synthesize historyButton;
+@synthesize historyBackMenu;
+@synthesize historyForwardMenu;
+
+@synthesize activeBrowser;
+@synthesize activeBrowserController;
+
+- (id) init;
+{
+ if (nil == (self = [super initWithWindowNibName: @"BrowserWindow"])) return nil;
+
+ return self;
+}
+
+- (void) dealloc;
+{
+ [self setTabBar: nil];
+ [self setTabView: nil];
+ [self setUrlField: nil];
+ [self setNavigationControl: nil];
+
+ [super dealloc];
+}
+
+- (void) awakeFromNib;
+{
+ [tabBar setShowAddTabButton: YES];
+ [tabBar setTearOffStyle: PSMTabBarTearOffMiniwindow];
+ [tabBar setCanCloseOnlyTab: YES];
+ [tabBar setHideForSingleTab: YES];
+
+ NSButton *b = [tabBar addTabButton];
+ [b setTarget: self];
+ [b setAction: @selector(newTab:)];
+
+ [urlField setRefreshAction: @selector(reloadPage:)];
+ [urlField bind: @"favicon" toObject: activeBrowserController withKeyPath: @"selection.favicon" options: nil];
+
+ [self bind: @"canGoBack"
+ toObject: activeBrowserController withKeyPath: @"selection.canGoBack"
+ options: nil];
+ [self bind: @"canGoForward"
+ toObject: activeBrowserController withKeyPath: @"selection.canGoForward"
+ options: nil];
+
+ [navigationControl setMenu: historyBackMenu forSegment: 0];
+ [navigationControl setMenu: historyForwardMenu forSegment: 1];
+}
+
+- (void) addTab: (BrowserViewController *)browser;
+{
+ NSTabViewItem *item = [[[NSTabViewItem alloc] initWithIdentifier: browser] autorelease];
+
+ [item setView: [browser view]];
+ [item bind: @"label" toObject: browser withKeyPath: @"title" options: nil];
+
+ [tabView addTabViewItem: item];
+ [browser setWindowController: self];
+
+ [tabView selectTabViewItem: item];
+}
+
+- (void) removeTab: (BrowserViewController *)browser;
+{
+ NSUInteger itemIndex = [tabView indexOfTabViewItemWithIdentifier: browser];
+ if (itemIndex != NSNotFound) {
+ NSTabViewItem *item = [tabView tabViewItemAtIndex: itemIndex];
+ [tabView removeTabViewItem: item];
+ [browser setWindowController: nil];
+ }
+}
+
+- (BOOL) windowShouldClose: (NSWindow *) window;
+{
+ if ([tabView numberOfTabViewItems] <= 1) return YES;
+ if ([[NSUserDefaults standardUserDefaults] boolForKey: kAlwaysCloseMultipleTabs]) return YES;
+
+ NSAlert *ask = [NSAlert alertWithMessageText: NSLocalizedString( @"Do you really want to close this window?", nil )
+ defaultButton: NSLocalizedString( @"Yes", @"'Yes' button" )
+ alternateButton: NSLocalizedString( @"No" , @"'No' button" )
+ otherButton:nil
+ informativeTextWithFormat: NSLocalizedString( @"There are %d tabs open, do you want to close them all?", nil ),
+ [tabView numberOfTabViewItems]];
+ [ask setShowsSuppressionButton:YES];
+
+ [ask beginSheetModalForWindow: window modalDelegate:self didEndSelector:@selector(canCloseAlertDidEnd:returnCode:contextInfo:)
+ contextInfo: NULL];
+
+ return NO;
+}
+
+- (void) canCloseAlertDidEnd:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void *)contextInfo;
+{
+ if (returnCode == NSOKButton) {
+ [[NSUserDefaults standardUserDefaults] setBool: [[alert suppressionButton] state] == NSOnState
+ forKey: kAlwaysCloseMultipleTabs];
+ [[self window] close];
+ }
+}
+
+- (void) windowWillClose: (NSNotification *)notification;
+{
+ for (NSTabViewItem *tab in [tabView tabViewItems]) {
+ [tabView removeTabViewItem: tab];
+ }
+}
+
+- (IBAction) newTab: (id) sender;
+{
+ nsurl *url;
+ nserror error;
+
+ if (nsoption_charp(homepage_url) != NULL) {
+ error = nsurl_create(nsoption_charp(homepage_url), &url);
+ } else {
+ error = nsurl_create(NETSURF_HOMEPAGE, &url);
+ }
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY |
+ BW_CREATE_TAB,
+ url,
+ NULL,
+ [activeBrowser browser],
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ }
+}
+
+- (IBAction) closeCurrentTab: (id) sender;
+{
+ [self removeTab: activeBrowser];
+}
+
+- (void) setActiveBrowser: (BrowserViewController *)newBrowser;
+{
+ activeBrowser = newBrowser;
+ [self setNextResponder: activeBrowser];
+}
+
+- (void) setCanGoBack: (BOOL)can;
+{
+ [navigationControl setEnabled: can forSegment: 0];
+}
+
+- (BOOL) canGoBack;
+{
+ return [navigationControl isEnabledForSegment: 0];
+}
+
+- (void) setCanGoForward: (BOOL)can;
+{
+ [navigationControl setEnabled: can forSegment: 1];
+}
+
+- (BOOL) canGoForward;
+{
+ return [navigationControl isEnabledForSegment: 1];
+}
+
+- (void)windowDidBecomeMain: (NSNotification *)note;
+{
+ [(NetSurfApp *)NSApp setFrontTab: [[tabView selectedTabViewItem] identifier]];
+}
+
+- (void)menuNeedsUpdate:(NSMenu *)menu
+{
+ if (menu == historyBackMenu) {
+ [activeBrowser buildBackMenu: menu];
+ } else if (menu == historyForwardMenu) {
+ [activeBrowser buildForwardMenu: menu];
+ }
+}
+
+#pragma mark -
+#pragma mark Tab bar delegate
+
+- (void) tabView: (NSTabView *)tabView didSelectTabViewItem: (NSTabViewItem *)tabViewItem;
+{
+ [self setActiveBrowser: [tabViewItem identifier]];
+ if ([[self window] isMainWindow]) {
+ [(NetSurfApp *)NSApp setFrontTab: [tabViewItem identifier]];
+ }
+}
+
+- (BOOL)tabView:(NSTabView*)aTabView shouldDragTabViewItem:(NSTabViewItem *)tabViewItem fromTabBar:(PSMTabBarControl *)tabBarControl
+{
+ return [aTabView numberOfTabViewItems] > 1;
+}
+
+- (BOOL)tabView:(NSTabView*)aTabView shouldDropTabViewItem:(NSTabViewItem *)tabViewItem inTabBar:(PSMTabBarControl *)tabBarControl
+{
+ [[tabViewItem identifier] setWindowController: self];
+ return YES;
+}
+
+- (PSMTabBarControl *)tabView:(NSTabView *)aTabView newTabBarForDraggedTabViewItem:(NSTabViewItem *)tabViewItem atPoint:(NSPoint)point;
+{
+ BrowserWindowController *newWindow = [[[BrowserWindowController alloc] init] autorelease];
+ [[tabViewItem identifier] setWindowController: newWindow];
+ [[newWindow window] setFrameOrigin: point];
+ return newWindow->tabBar;
+}
+
+- (void) tabView: (NSTabView *)aTabView didCloseTabViewItem: (NSTabViewItem *)tabViewItem;
+{
+ [tabViewItem unbind: @"label"];
+
+ if (activeBrowser == [tabViewItem identifier]) {
+ [self setActiveBrowser: nil];
+ [(NetSurfApp *)NSApp setFrontTab: nil];
+ }
+
+ browser_window_destroy( [[tabViewItem identifier] browser] );
+}
+
+@end
diff --git a/frontends/cocoa/DownloadWindowController.h b/frontends/cocoa/DownloadWindowController.h
new file mode 100644
index 000000000..47a05be39
--- /dev/null
+++ b/frontends/cocoa/DownloadWindowController.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+struct gui_download_table *cocoa_download_table;
+
+@interface DownloadWindowController : NSWindowController {
+ struct download_context *context;
+ unsigned long totalSize;
+ unsigned long receivedSize;
+
+ NSURL *url;
+ NSString *mimeType;
+ NSURL *saveURL;
+ NSFileHandle *outputFile;
+ NSMutableData *savedData;
+ NSDate *startDate;
+
+ BOOL canClose;
+ BOOL shouldClose;
+}
+
+@property (readwrite, copy, nonatomic) NSURL *URL;
+@property (readwrite, copy, nonatomic) NSString *MIMEType;
+@property (readwrite, assign, nonatomic) unsigned long totalSize;
+@property (readwrite, copy, nonatomic) NSURL *saveURL;
+@property (readwrite, assign, nonatomic) unsigned long receivedSize;
+
+@property (readonly, nonatomic) NSString *fileName;
+@property (readonly, nonatomic) NSImage *icon;
+@property (readonly, nonatomic) NSString *statusText;
+
+- (id)initWithContext: (struct download_context *)ctx;
+
+- (void) abort;
+
+@end
diff --git a/frontends/cocoa/DownloadWindowController.m b/frontends/cocoa/DownloadWindowController.m
new file mode 100644
index 000000000..0c9d869f8
--- /dev/null
+++ b/frontends/cocoa/DownloadWindowController.m
@@ -0,0 +1,415 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import "utils/log.h"
+#import "utils/nsurl.h"
+#import "desktop/download.h"
+#import "desktop/gui_download.h"
+
+#import "cocoa/DownloadWindowController.h"
+#import "cocoa/gui.h"
+
+@interface DownloadWindowController ()
+
+@property (readwrite, retain, nonatomic) NSFileHandle *outputFile;
+@property (readwrite, retain, nonatomic) NSMutableData *savedData;
+@property (readwrite, copy, nonatomic) NSDate *startDate;
+
+- (void)savePanelDidEnd:(NSSavePanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
+- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo;
+- (void)askCancelDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo;
+
+- (BOOL) receivedData: (NSData *)data;
+
+- (void) showError: (NSString *)error;
+- (void) downloadDone;
+- (void) removeIfPossible;
+
+@end
+
+static void cocoa_unregister_download( DownloadWindowController *download );
+static void cocoa_register_download( DownloadWindowController *download );
+
+
+@implementation DownloadWindowController
+
+- (id) initWithContext: (struct download_context *)ctx
+{
+ if ((self = [super initWithWindowNibName: @"DownloadWindow"]) == nil) {
+ return nil;
+ }
+
+ context = ctx;
+ totalSize = download_context_get_total_length( context );
+ [self setURL: [NSURL URLWithString: [NSString stringWithUTF8String: nsurl_access(download_context_get_url( context ))]]];
+ [self setMIMEType: [NSString stringWithUTF8String: download_context_get_mime_type( context )]];
+ [self setStartDate: [NSDate date]];
+
+ return self;
+}
+
+- (void) dealloc
+{
+ download_context_destroy( context );
+
+ [self setURL: nil];
+ [self setMIMEType: nil];
+ [self setSaveURL: nil];
+ [self setOutputFile: nil];
+ [self setSavedData: nil];
+ [self setStartDate: nil];
+
+ [super dealloc];
+}
+
+- (void) abort
+{
+ download_context_abort( context );
+ [self removeIfPossible];
+}
+
+- (void) askForSave
+{
+ canClose = NO;
+ [[NSSavePanel savePanel]
+ beginSheetForDirectory: nil
+ file: [NSString stringWithUTF8String: download_context_get_filename( context )]
+ modalForWindow: [self window]
+ modalDelegate: self
+ didEndSelector: @selector(savePanelDidEnd:returnCode:contextInfo:)
+ contextInfo: NULL];
+}
+
+- (void) savePanelDidEnd:(NSSavePanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
+{
+ canClose = YES;
+
+ if (returnCode == NSCancelButton) {
+ [self abort];
+ return;
+ }
+
+ NSURL *targetURL = [sheet URL];
+ NSString *path = [targetURL path];
+
+ [[NSFileManager defaultManager] createFileAtPath: path contents: nil attributes: nil];
+
+ FSRef ref;
+ if (CFURLGetFSRef( (CFURLRef)targetURL, &ref )) {
+ NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
+ url, (NSString *)kLSQuarantineDataURLKey,
+ (NSString *)kLSQuarantineTypeWebDownload, (NSString *)kLSQuarantineTypeKey,
+ nil];
+ LSSetItemAttribute( &ref, kLSRolesAll, kLSItemQuarantineProperties, (CFDictionaryRef)attributes );
+ LOG("Set quarantine attributes on file %s", [path UTF8String]);
+ }
+
+ [self setOutputFile: [NSFileHandle fileHandleForWritingAtPath: path]];
+ [self setSaveURL: targetURL];
+
+ NSWindow *win = [self window];
+ [win setRepresentedURL: targetURL];
+ [win setTitle: [self fileName]];
+
+ if (nil == outputFile) {
+ [self performSelector: @selector(showError:) withObject: @"Cannot create file" afterDelay: 0];
+ return;
+ }
+
+ if (nil != savedData) {
+ [outputFile writeData: savedData];
+ [self setSavedData: nil];
+ }
+
+ [self removeIfPossible];
+}
+
+- (BOOL) receivedData: (NSData *)data
+{
+ if (outputFile) {
+ [outputFile writeData: data];
+ } else {
+ if (nil == savedData) {
+ [self setSavedData: [NSMutableData data]];
+ }
+ [savedData appendData: data];
+ }
+
+ [self setReceivedSize: receivedSize + [data length]];
+
+ return YES;
+}
+
+- (void) showError: (NSString *)error
+{
+ canClose = NO;
+ NSAlert *alert = [NSAlert alertWithMessageText: NSLocalizedString( @"Error", @"show error" )
+ defaultButton: NSLocalizedString( @"OK", @"'OK' button" )
+ alternateButton: nil otherButton: nil
+ informativeTextWithFormat: @"%@", error];
+
+ [alert beginSheetModalForWindow: [self window] modalDelegate: self
+ didEndSelector: @selector(alertDidEnd:returnCode:contextInfo:)
+ contextInfo: NULL];
+}
+
+- (void) alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
+{
+ [self abort];
+}
+
+- (void) removeIfPossible
+{
+ if (canClose && shouldClose) {
+ cocoa_unregister_download( self );
+ }
+}
+- (void) downloadDone
+{
+ shouldClose = YES;
+ [self removeIfPossible];
+}
+
+- (BOOL) windowShouldClose: (id)sender
+{
+ if ([[NSUserDefaults standardUserDefaults] boolForKey: kAlwaysCancelDownload]) {
+ return YES;
+ }
+
+ NSAlert *ask = [NSAlert alertWithMessageText: NSLocalizedString( @"Cancel download?", @"Download" )
+ defaultButton: NSLocalizedString( @"Yes", @"" )
+ alternateButton: NSLocalizedString( @"No", @"" )
+ otherButton: nil
+ informativeTextWithFormat: NSLocalizedString( @"Should the download of '%@' really be cancelled?", @"Download" ),
+ [self fileName]];
+ [ask setShowsSuppressionButton: YES];
+ [ask beginSheetModalForWindow: [self window] modalDelegate: self
+ didEndSelector: @selector(askCancelDidEnd:returnCode:contextInfo:) contextInfo: NULL];
+ return NO;
+}
+
+- (void) windowWillClose: (NSNotification *)notification
+{
+ [self abort];
+}
+
+- (void) askCancelDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
+{
+ if (returnCode == NSOKButton) {
+ [[NSUserDefaults standardUserDefaults]
+ setBool: [[alert suppressionButton] state] == NSOnState
+ forKey: kAlwaysCancelDownload];
+ [self close];
+ }
+}
+
+#pragma mark -
+#pragma mark Properties
+
+@synthesize URL = url;
+@synthesize MIMEType = mimeType;
+@synthesize totalSize;
+@synthesize saveURL;
+@synthesize outputFile;
+@synthesize savedData;
+@synthesize receivedSize;
+@synthesize startDate;
+
++ (NSSet *) keyPathsForValuesAffectingStatusText
+{
+ return [NSSet setWithObjects: @"totalSize", @"receivedSize", nil];
+}
+
+#ifndef NSAppKitVersionNumber10_5
+#define NSAppKitVersionNumber10_5 949
+#endif
+
+static NSString *cocoa_file_size_string( float size )
+{
+ static unsigned factor = 0;
+ if (factor == 0) {
+ if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_5) {
+ factor = 1000;
+ } else {
+ factor = 1024;
+ }
+ }
+
+ if (size == 0) return @"nothing";
+ if (size <= 1.0) return @"1 byte";
+
+ if (size < factor - 1) return [NSString stringWithFormat:@"%1.0f bytes",size];
+
+ size /= factor;
+ if (size < factor - 1) return [NSString stringWithFormat:@"%1.1f KB", size];
+
+ size /= factor;
+ if (size < factor - 1) return [NSString stringWithFormat:@"%1.1f MB", size];
+
+ size /= factor;
+ if (size < factor - 1) return [NSString stringWithFormat:@"%1.1f GB", size];
+
+ size /= factor;
+ return [NSString stringWithFormat:@"%1.1f TB", size];
+}
+
+static NSString *cocoa_time_string( unsigned seconds )
+{
+ if (seconds <= 10) {
+ return NSLocalizedString(@"less than 10 seconds",
+ @"time remaining" );
+ }
+
+ if (seconds < 60) {
+ return [NSString stringWithFormat: NSLocalizedString( @"%u seconds",
+ @"time remaining" ), seconds];
+ }
+
+ unsigned minutes = seconds / 60;
+ seconds = seconds % 60;
+
+ if (minutes < 60) {
+ return [NSString stringWithFormat: NSLocalizedString( @"%u:%02u minutes",
+ @"time remaining: minutes, seconds" ) , minutes, seconds];
+ }
+
+ unsigned hours = minutes / 60;
+ minutes = minutes % 60;
+
+ return [NSString stringWithFormat: NSLocalizedString( @"%2:%02u hours", @"time remaining: hours, minutes" ), hours, minutes];
+}
+
+- (NSString *) statusText
+{
+ NSString *speedString = @"";
+
+ float speed = 0.0;
+ NSTimeInterval elapsedTime = [[NSDate date] timeIntervalSinceDate: startDate];
+ if (elapsedTime >= 0.1) {
+ speed = (float)receivedSize / elapsedTime;
+ speedString = [NSString stringWithFormat: @" (%@/s)", cocoa_file_size_string( speed )];
+ }
+
+ NSString *timeRemainingString = @"";
+ NSString *totalSizeString = @"";
+ if (totalSize != 0) {
+ if (speed > 0.0) {
+ float timeRemaining = (float)(totalSize - receivedSize) / speed;
+ timeRemainingString = [NSString stringWithFormat: @": %@", cocoa_time_string( timeRemaining )];
+ }
+ totalSizeString = [NSString stringWithFormat: NSLocalizedString( @" of %@", @"... of (total size)" ), cocoa_file_size_string( totalSize )];
+ }
+
+ return [NSString stringWithFormat: @"%@%@%@%@", cocoa_file_size_string( receivedSize ),
+ totalSizeString, speedString, timeRemainingString];
+}
+
++ (NSSet *) keyPathsForValuesAffectingFileName
+{
+ return [NSSet setWithObject: @"saveURL"];
+}
+
+- (NSString *) fileName
+{
+ return [[saveURL path] lastPathComponent];
+}
+
++ (NSSet *) keyPathsForValuesAffectingIcon
+{
+ return [NSSet setWithObjects: @"mimeType", @"URL", nil];
+}
+
+- (NSImage *) icon;
+{
+ NSString *type = [(NSString *)UTTypeCreatePreferredIdentifierForTag( kUTTagClassMIMEType, (CFStringRef)mimeType, NULL ) autorelease];
+ if ([type hasPrefix: @"dyn."] || [type isEqualToString: (NSString *)kUTTypeData]) {
+ NSString *pathExt = [[url path] pathExtension];
+ type = [(NSString *)UTTypeCreatePreferredIdentifierForTag( kUTTagClassFilenameExtension, (CFStringRef)pathExt, NULL ) autorelease];
+ }
+ return [[NSWorkspace sharedWorkspace] iconForFileType: type];
+}
+
+
+#pragma mark -
+#pragma mark NetSurf interface functions
+
+static struct gui_download_window *
+gui_download_window_create(download_context *ctx,
+ struct gui_window *parent)
+{
+ DownloadWindowController * const window = [[DownloadWindowController alloc] initWithContext: ctx];
+ cocoa_register_download( window );
+ [window askForSave];
+ [window release];
+
+ return (struct gui_download_window *)window;
+}
+
+static nserror
+gui_download_window_data(struct gui_download_window *dw,
+ const char *data,
+ unsigned int size)
+{
+ DownloadWindowController * const window = (DownloadWindowController *)dw;
+ return [window receivedData: [NSData dataWithBytes: data length: size]] ? NSERROR_OK : NSERROR_SAVE_FAILED;
+}
+
+static void
+gui_download_window_error(struct gui_download_window *dw,
+ const char *error_msg)
+{
+ DownloadWindowController * const window = (DownloadWindowController *)dw;
+ [window showError: [NSString stringWithUTF8String: error_msg]];
+}
+
+static void
+gui_download_window_done(struct gui_download_window *dw)
+{
+ DownloadWindowController * const window = (DownloadWindowController *)dw;
+ [window downloadDone];
+}
+
+@end
+
+#pragma mark -
+static NSMutableSet *cocoa_all_downloads = nil;
+
+static void
+cocoa_register_download( DownloadWindowController *download )
+{
+ if (cocoa_all_downloads == nil) {
+ cocoa_all_downloads = [[NSMutableSet alloc] init];
+ }
+ [cocoa_all_downloads addObject: download];
+}
+
+static void
+cocoa_unregister_download( DownloadWindowController *download )
+{
+ [cocoa_all_downloads removeObject: download];
+}
+
+
+static struct gui_download_table download_table = {
+ .create = gui_download_window_create,
+ .data = gui_download_window_data,
+ .error = gui_download_window_error,
+ .done = gui_download_window_done,
+};
+
+struct gui_download_table *cocoa_download_table = &download_table;
diff --git a/frontends/cocoa/FormSelectMenu.h b/frontends/cocoa/FormSelectMenu.h
new file mode 100644
index 000000000..cec519296
--- /dev/null
+++ b/frontends/cocoa/FormSelectMenu.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@interface FormSelectMenu : NSObject {
+ NSMenu *menu;
+ NSPopUpButtonCell *cell;
+
+ struct browser_window *browser;
+ struct form_control *control;
+}
+
+- (id)initWithControl: (struct form_control *) control forWindow: (struct browser_window *) window;
+- (void) runInView: (NSView *) view;
+
+@end
diff --git a/frontends/cocoa/FormSelectMenu.m b/frontends/cocoa/FormSelectMenu.m
new file mode 100644
index 000000000..84e04cd25
--- /dev/null
+++ b/frontends/cocoa/FormSelectMenu.m
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import "cocoa/FormSelectMenu.h"
+#import "cocoa/coordinates.h"
+
+#import "desktop/browser.h"
+#import "render/form.h"
+
+static inline NSRect cocoa_rect_for_control( struct browser_window *bw, struct form_control *control)
+{
+ struct rect r;
+ form_control_bounding_rect(control, &r);
+ return cocoa_scaled_rect(browser_window_get_scale(bw),
+ r.x0,
+ r.y0,
+ r.x1,
+ r.y1);
+}
+
+@interface FormSelectMenu ()
+
+- (void) itemSelected: (id) sender;
+
+@end
+
+
+@implementation FormSelectMenu
+
+- (id) initWithControl: (struct form_control *) c forWindow: (struct browser_window *) w
+{
+ if ((self = [super init]) == nil) return nil;
+
+ control = c;
+ browser = w;
+
+ menu = [[NSMenu alloc] initWithTitle: @"Select"];
+ if (menu == nil) {
+ [self release];
+ return nil;
+ }
+
+ [menu addItemWithTitle: @"" action: NULL keyEquivalent: @""];
+
+ NSInteger currentItemIndex = 0;
+ struct form_option *opt;
+ for (opt = form_select_get_option(control, 0);
+ opt != NULL;
+ opt = opt->next) {
+ NSMenuItem *item = [[NSMenuItem alloc] initWithTitle: [NSString stringWithUTF8String: opt->text]
+ action: @selector( itemSelected: )
+ keyEquivalent: @""];
+ if (opt->selected) {
+ [item setState: NSOnState];
+ }
+ [item setTarget: self];
+ [item setTag: currentItemIndex++];
+ [menu addItem: item];
+ [item release];
+ }
+
+ [menu setDelegate: self];
+
+ return self;
+}
+
+- (void) dealloc
+{
+ [cell release];
+ [menu release];
+
+ [super dealloc];
+}
+
+- (void) runInView: (NSView *) view
+{
+ [self retain];
+
+ cell = [[NSPopUpButtonCell alloc] initTextCell: @"" pullsDown: YES];
+ [cell setMenu: menu];
+
+ const NSRect rect = cocoa_rect_for_control(browser, control);
+
+ [cell attachPopUpWithFrame: rect inView: view];
+ [cell performClickWithFrame: rect inView: view];
+}
+
+- (void) itemSelected: (id) sender
+{
+ form_select_process_selection( control, [sender tag] );
+}
+
+- (void) menuDidClose: (NSMenu *) sender
+{
+ [self release];
+}
+
+@end
diff --git a/frontends/cocoa/HistoryView.h b/frontends/cocoa/HistoryView.h
new file mode 100644
index 000000000..6ef061321
--- /dev/null
+++ b/frontends/cocoa/HistoryView.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@class HistoryView;
+@class BrowserView;
+
+@interface HistoryView : NSView {
+ struct browser_window *browser;
+ BrowserView *browserView;
+ NSMutableArray *toolTips;
+}
+
+@property (readwrite, assign, nonatomic) BrowserView *browser;
+
+- (void) updateHistory;
+- (NSSize) size;
+
+@end
diff --git a/frontends/cocoa/HistoryView.m b/frontends/cocoa/HistoryView.m
new file mode 100644
index 000000000..7b192336e
--- /dev/null
+++ b/frontends/cocoa/HistoryView.m
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import "cocoa/HistoryView.h"
+#import "cocoa/font.h"
+#import "cocoa/coordinates.h"
+#import "cocoa/plotter.h"
+#import "cocoa/LocalHistoryController.h"
+#import "cocoa/BrowserView.h"
+
+#import "desktop/browser_history.h"
+#import "desktop/plotters.h"
+
+@implementation HistoryView
+
+@synthesize browser = browserView;
+
+- (void) setBrowser: (BrowserView *) bw;
+{
+ browserView = bw;
+ browser = [bw browser];
+ [self updateHistory];
+}
+
+- (NSSize) size;
+{
+ int width, height;
+ browser_window_history_size( browser, &width, &height );
+
+ return cocoa_size( width, height );
+}
+
+- (void) updateHistory;
+{
+ [self setFrameSize: [self size]];
+ [self setNeedsDisplay: YES];
+}
+
+- (void) drawRect: (NSRect)rect;
+{
+ [[NSColor clearColor] set];
+ [NSBezierPath fillRect: rect];
+
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &cocoa_plotters
+ };
+
+ cocoa_set_clip( rect );
+
+ browser_window_history_redraw( browser, &ctx );
+}
+
+- (void) mouseUp: (NSEvent *)theEvent;
+{
+ const NSPoint location = [self convertPoint: [theEvent locationInWindow] fromView: nil];
+ const bool newWindow = [theEvent modifierFlags] & NSCommandKeyMask;
+ if (browser_window_history_click( browser,
+ cocoa_pt_to_px( location.x ), cocoa_pt_to_px( location.y ),
+ newWindow )) {
+ [browserView setHistoryVisible: NO];
+ }
+}
+
+- (BOOL) isFlipped;
+{
+ return YES;
+}
+
+- (void) mouseEntered: (NSEvent *) event;
+{
+ [[NSCursor pointingHandCursor] set];
+}
+
+- (void) mouseExited: (NSEvent *) event;
+{
+ [[NSCursor arrowCursor] set];
+}
+
+static bool cursor_rects_cb( const struct browser_window *bw, int x0, int y0, int x1, int y1,
+ const struct history_entry *page, void *user_data )
+{
+ HistoryView *view = user_data;
+
+ NSRect rect = NSIntersectionRect( [view visibleRect], cocoa_rect( x0, y0, x1, y1 ) );
+ if (!NSIsEmptyRect( rect )) {
+
+ NSString *toolTip = [NSString stringWithFormat: @"%s\n%s", browser_window_history_entry_get_title(page),
+ browser_window_history_entry_get_url( page )];
+
+ [view addToolTipRect: rect owner: toolTip userData: nil];
+ NSTrackingArea *area = [[NSTrackingArea alloc] initWithRect: rect
+ options: NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp
+ owner: view userInfo: nil];
+ [view addTrackingArea: area];
+ [area release];
+ }
+
+ return true;
+}
+
+- (NSToolTipTag)addToolTipRect: (NSRect) rect owner: (id) owner userData: (void *) userData;
+{
+ if (toolTips == nil) toolTips = [[NSMutableArray alloc] init];
+ [toolTips addObject: owner];
+
+ return [super addToolTipRect: rect owner: owner userData: userData];
+}
+
+- (void) removeAllToolTips;
+{
+ [super removeAllToolTips];
+ [toolTips removeAllObjects];
+}
+
+- (void) updateTrackingAreas;
+{
+ [self removeAllToolTips];
+
+ for (NSTrackingArea *area in [self trackingAreas]) {
+ [self removeTrackingArea: area];
+ }
+
+ browser_window_history_enumerate( browser, cursor_rects_cb, self );
+
+ [super updateTrackingAreas];
+}
+
+- (void) dealloc;
+{
+ [self removeAllToolTips];
+ [super dealloc];
+}
+
+@end
diff --git a/frontends/cocoa/HistoryWindowController.h b/frontends/cocoa/HistoryWindowController.h
new file mode 100644
index 000000000..30ba8049a
--- /dev/null
+++ b/frontends/cocoa/HistoryWindowController.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@class Tree;
+@class TreeView;
+
+@interface HistoryWindowController : NSWindowController {
+ Tree *tree;
+ TreeView *view;
+}
+
+@property (readwrite, assign, nonatomic) IBOutlet TreeView *view;
+
+@end
diff --git a/frontends/cocoa/HistoryWindowController.m b/frontends/cocoa/HistoryWindowController.m
new file mode 100644
index 000000000..cae679b9d
--- /dev/null
+++ b/frontends/cocoa/HistoryWindowController.m
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import "cocoa/HistoryWindowController.h"
+#import "cocoa/Tree.h"
+#import "cocoa/TreeView.h"
+
+#import "desktop/global_history.h"
+
+@implementation HistoryWindowController
+
+@synthesize view;
+
+- init;
+{
+ if ((self = [super initWithWindowNibName: @"HistoryWindow"]) == nil) return nil;
+
+ tree = [[Tree alloc] initWithFlags: TREE_HISTORY];
+
+ return self;
+}
+
+- (void) dealloc;
+{
+ [tree release];
+ [self setView: nil];
+
+ [super dealloc];
+}
+
+- (void)awakeFromNib;
+{
+ [view setTree: tree];
+ [[self window] setExcludedFromWindowsMenu: YES];
+}
+
+@end
diff --git a/frontends/cocoa/LocalHistoryController.h b/frontends/cocoa/LocalHistoryController.h
new file mode 100644
index 000000000..3e6d1775e
--- /dev/null
+++ b/frontends/cocoa/LocalHistoryController.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@class HistoryView;
+@class BrowserView;
+
+@interface LocalHistoryController : NSWindowController {
+ HistoryView *history;
+ BrowserView *browser;
+}
+
+@property (readwrite, assign, nonatomic) BrowserView *browser;
+@property (readwrite, assign, nonatomic) IBOutlet HistoryView *history;
+
+- (id)initWithBrowser: (BrowserView *) bw;
+
+- (void) attachToView: (NSView *) view;
+- (void) detach;
+- (void) redraw;
+
+- (void) keyDown: (NSEvent *)theEvent;
+
+@end
diff --git a/frontends/cocoa/LocalHistoryController.m b/frontends/cocoa/LocalHistoryController.m
new file mode 100644
index 000000000..b3992b614
--- /dev/null
+++ b/frontends/cocoa/LocalHistoryController.m
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import "cocoa/LocalHistoryController.h"
+
+#import "cocoa/BrowserView.h"
+#import "cocoa/HistoryView.h"
+#import "cocoa/ArrowWindow.h"
+
+@implementation LocalHistoryController
+
+@synthesize browser;
+@synthesize history;
+
+- initWithBrowser: (BrowserView *) bw;
+{
+ if ((self = [super initWithWindowNibName: @"LocalHistoryPanel"]) == nil) return nil;
+
+ browser = bw;
+
+ return self;
+}
+
+- (void) attachToView: (NSView *) view;
+{
+ NSDisableScreenUpdates();
+
+ ArrowWindow *box = (ArrowWindow *)[self window];
+
+ [box setContentSize: [history size]];
+ [box setArrowPosition: 50];
+ [history updateHistory];
+ [box attachToView: view];
+
+ NSRect frame = [box frame];
+ NSRect screenFrame = [[box screen] visibleFrame];
+
+ const CGFloat arrowSize = [box arrowSize];
+ frame.origin.x += arrowSize;
+ frame.origin.y += arrowSize;
+ frame.size.width -= 2 * arrowSize;
+ frame.size.height -= 2 * arrowSize;
+
+ if (NSMinY( frame ) < NSMinY( screenFrame )) {
+ const CGFloat delta = NSMinY( screenFrame ) - NSMinY( frame );
+ frame.size.height -= delta;
+ frame.origin.y += delta;
+ }
+
+ CGFloat arrowPositionChange = 50;
+ if (NSMaxX( frame ) > NSMaxX( screenFrame )) {
+ const CGFloat delta = NSMaxX( frame ) - NSMaxX( screenFrame );
+ arrowPositionChange += delta;
+ frame.origin.x -= delta;
+ }
+
+ if (NSMinX( frame ) < NSMinX( screenFrame )) {
+ const CGFloat delta = NSMinX( screenFrame ) - NSMinX( frame );
+ arrowPositionChange -= delta;
+ frame.origin.x += delta;
+ frame.size.width -= delta;
+ }
+
+ frame.origin.x -= arrowSize;
+ frame.origin.y -= arrowSize;
+ frame.size.width += 2 * arrowSize;
+ frame.size.height += 2 * arrowSize;
+
+ [box setArrowPosition: arrowPositionChange];
+ [box setFrame: frame display: YES];
+
+ NSEnableScreenUpdates();
+}
+
+- (void) detach;
+{
+ [(ArrowWindow *)[self window] detach];
+}
+
+- (void) windowDidLoad;
+{
+ [history setBrowser: browser];
+}
+
+- (void) redraw;
+{
+ [history setNeedsDisplay: YES];
+}
+
+- (void) keyDown: (NSEvent *)theEvent;
+{
+ unichar key = [[theEvent characters] characterAtIndex: 0];
+ switch (key) {
+ case 27:
+ [browser setHistoryVisible: NO];
+ break;
+
+ default:
+ NSBeep();
+ break;
+ };
+}
+
+@end
diff --git a/frontends/cocoa/Makefile b/frontends/cocoa/Makefile
new file mode 100644
index 000000000..9f345c0b9
--- /dev/null
+++ b/frontends/cocoa/Makefile
@@ -0,0 +1,249 @@
+# ----------------------------------------------------------------------------
+# Mac OS X target setup
+# ----------------------------------------------------------------------------
+
+POSTEXES += NetSurf.app
+
+# shut up zconf.h and zlib.h
+#CFLAGS += -D_LARGEFILE64_SOURCE=1
+
+# add Mac Ports include and library paths for openssl
+ifneq ($(shell test -d /opt/local && echo 'yes'),)
+ LDFLAGS += -L/opt/local/lib
+ CFLAGS += -I/opt/local/include
+endif
+
+ifeq ($(SDK_VERSION),)
+ # if no SDK_VERSION has been specified select one from those available
+ SDK_PARAM := $(shell xcodebuild -showsdks | awk '/^$$/{p=0};p; /OS X SDKs:/{p=1}' | tail -1 | cut -f3)
+ SDK_VERSION := $(MACOSX_VERSION)
+else
+ SDK_PARAM := -sdk macosx$(SDK_VERSION)
+endif
+
+SDK_PATH ?= $(shell xcodebuild -version $(SDK_PARAM) Path)
+SDK_FLAGS := -isysroot $(SDK_PATH) -mmacosx-version-min=$(SDK_VERSION)
+CFLAGS := $(SDK_FLAGS) $(CFLAGS)
+LDFLAGS := $(SDK_FLAGS) -Wl,-syslibroot,$(SDK_PATH) $(LDFLAGS)
+CXXFLAGS := $(SDK_FLAGS) $(CXXFLAGS)
+
+# for timerisset()
+CFLAGS += -D_DARWIN_C_SOURCE
+
+LDFLAGS += -L/usr/lib
+LDFLAGS += -L/usr/X11/lib
+LDFLAGS += -lm -lcurl
+LDFLAGS += -lssl -lcrypto
+
+CFLAGS += -Dnscocoa -D_BSD_SOURCE -D_POSIX_C_SOURCE -std=c99 -g -Os
+
+CFLAGS += -I/usr/X11/include
+CFLAGS += -include cocoa/Prefix.pch
+
+VERSION_FULL := $(shell sed -n '/_version.*=.*"/{s/.*"\(.*\)".*/\1/;p;}' desktop/version.c)
+VERSION_MAJ := $(shell sed -n '/_major/{s/.* = \([0-9]*\).*/\1/;p;}' desktop/version.c)
+VERSION_MIN := $(shell sed -n '/_minor/{s/.* = \([0-9]*\).*/\1/;p;}' desktop/version.c)
+
+LDFLAGS += -Wl,-framework,Cocoa -Wl,-framework,Carbon $(NETLDFLAGS)
+
+$(eval $(call feature_enabled,IMAGEIO,-DWITH_APPLE_IMAGE,,Apple ImageIO ))
+
+ifneq ($(UNIVERSAL),)
+ UNIVERSAL_FLAGS := $(foreach arch,$(UNIVERSAL),-arch $(arch) )
+ CFLAGS += $(UNIVERSAL_FLAGS)
+ LDFLAGS += $(UNIVERSAL_FLAGS)
+ CXXFLAGS += $(UNIVERSAL_FLAGS)
+endif
+
+# ----------------------------------------------------------------------------
+# Source file setup
+# ----------------------------------------------------------------------------
+
+# sources purely for the Mac OS X build
+S_FRONTEND := \
+ BookmarksController.m \
+ BrowserView.m \
+ BrowserViewController.m \
+ BrowserWindowController.m \
+ BrowserWindow.m \
+ DownloadWindowController.m \
+ NetSurfAppDelegate.m \
+ NetsurfApp.m \
+ PreferencesWindowController.m \
+ ScrollableView.m \
+ SearchWindowController.m \
+ URLFieldCell.m \
+ Tree.m \
+ TreeView.m \
+ HistoryView.m \
+ HistoryWindowController.m \
+ FormSelectMenu.m \
+ bitmap.m \
+ fetch.m \
+ font.m \
+ gui.m \
+ plotter.m \
+ schedule.m \
+ selection.m \
+ ArrowBox.m \
+ ArrowWindow.m \
+ BlackScroller.m \
+ LocalHistoryController.m \
+ apple_image.m
+
+S_TABBAR := \
+ NSBezierPath_AMShading.m \
+ NSString_AITruncation.m \
+ PSMOverflowPopUpButton.m \
+ PSMProgressIndicator.m \
+ PSMRolloverButton.m \
+ PSMTabBarCell.m \
+ PSMTabBarControl.m \
+ PSMTabBarController.m \
+ PSMTabDragAssistant.m \
+ PSMTabDragView.m \
+ PSMTabDragWindow.m \
+ PSMTabDragWindowController.m \
+ PSMUnifiedTabStyle.m
+
+S_FRONTEND += $(addprefix PSMTabBarControl/,$(S_TABBAR))
+
+# This is the final source build list
+# Note this is deliberately *not* expanded here as common and image
+# are not yet available
+SOURCES = $(addprefix $(shell pwd)/,$(S_COMMON) $(S_IMAGE) $(S_BROWSER) $(S_FRONTEND))
+
+# Since we prefix the sources with the pwd, also create a special
+# prefixed rule so that the testament is run
+$(shell pwd)/content/fetchers/about.c: testament
+
+EXETARGET := NetSurf
+
+S_XIBS := \
+ MainMenu.xib \
+ Browser.xib \
+ BrowserWindow.xib \
+ DownloadWindow.xib \
+ SearchWindow.xib \
+ PreferencesWindow.xib \
+ HistoryWindow.xib \
+ BookmarksWindow.xib \
+ LocalHistoryPanel.xib
+
+R_RESOURCES := \
+ default.css \
+ adblock.css \
+ internal.css \
+ quirks.css \
+ NetSurf.icns \
+ HomeTemplate.pdf \
+ Icons \
+ ca-bundle \
+ netsurf.png
+
+
+TABBAR_RESOURCES := \
+ AquaTabClose_Front_Pressed.png \
+ AquaTabClose_Front_Rollover.png \
+ AquaTabClose_Front.png \
+ AquaTabCloseDirty_Front_Pressed.png \
+ AquaTabCloseDirty_Front_Rollover.png \
+ AquaTabCloseDirty_Front.png \
+ AquaTabNew.png \
+ AquaTabNewPressed.png \
+ AquaTabNewRollover.png \
+ overflowImage.png \
+ overflowImagePressed.png \
+ pi.png
+
+R_RESOURCES += $(addprefix PSMTabBarControl/Images/,$(TABBAR_RESOURCES))
+
+R_RESOURCES := $(addprefix $(FRONTEND_RESOURCES_DIR)/,$(R_RESOURCES))
+
+LANGUAGES := de en fr it nl
+LOCALIZED_RESOURCES := Localizable.strings
+
+#languiage project macro
+# $1 is language name
+# $2 is list of resources per language
+define make_lproj
+R_RESOURCES += $$(OBJROOT)/$(1).lproj
+$$(OBJROOT)/$(1).lproj: $(2)
+ $(VQ)echo Bundling language $(1)
+ $(Q)mkdir -p $$@
+ $(Q)cp -pLR $(2) $$@
+ $(Q)$(SPLIT_MESSAGES) -l $(1) -p cocoa -f messages resources/FatMessages > $$@/Messages
+endef
+
+# compile_xib (xib) (lang)
+define compile_xib
+$$(OBJROOT)/$(2).lproj: $$(OBJROOT)/$(2).lproj/$(1:.xib=.nib)
+
+$$(OBJROOT)/$(2).lproj/$(1:.xib=.nib): $(FRONTEND_RESOURCES_DIR)/$(1) $$(OBJROOT)/created
+ $(VQ)echo Compiling XIB $(1) for language $(2)
+ $(Q)mkdir -p $$(OBJROOT)/$(2).lproj
+ $(Q)cocoa/compile-xib.sh $(FRONTEND_RESOURCES_DIR)/$(1) $(2) $$@
+
+ifeq ($(wildcard $(FRONTEND_RESOURCES_DIR)/$(2).lproj/$(1).strings),$(FRONTEND_RESOURCES_DIR)/$(2).lproj/$(1).strings)
+$$(OBJROOT)/$(2).lproj/$(1:.xib=.nib): $(FRONTEND_RESOURCES_DIR)/$(2).lproj/$(1).strings
+endif
+
+endef
+
+$(foreach lang,$(LANGUAGES),$(eval $(call make_lproj,$(lang),$(addprefix cocoa/res/$(lang).lproj/,$(LOCALIZED_RESOURCES)))))
+$(foreach lang,$(LANGUAGES),$(foreach xib,$(S_XIBS),$(eval $(call compile_xib,$(xib),$(lang)))))
+
+# ----------------------------------------------------------------------------
+# Install target
+# ----------------------------------------------------------------------------
+
+install-cocoa: NetSurf.app
+
+NetSurf.app: NetSurf $(FRONTEND_SOURCE_DIR)/Makefile $(R_RESOURCES) NetSurf.app/Contents/Info.plist
+ $(VQ)echo Assembling NetSurf.app bundle
+ $(Q)mkdir -p NetSurf.app/Contents/MacOS
+ $(Q)cp NetSurf NetSurf.app/Contents/MacOS
+ $(Q)rm -rf NetSurf.app/Contents/Resources
+ $(Q)mkdir -p NetSurf.app/Contents/Resources
+ $(Q)cp -pLR $(R_RESOURCES) NetSurf.app/Contents/Resources
+ $(Q)echo 'APPL????' > NetSurf.app/Contents/PkgInfo
+
+NetSurf.app/Contents/Info.plist: $(FRONTEND_RESOURCES_DIR)/NetSurf-Info.plist $(FRONTEND_SOURCE_DIR)/Makefile
+ $(VQ)echo Generating Info.plist
+ $(Q)mkdir -p NetSurf.app/Contents
+ $(Q)sed -e 's/$${EXECUTABLE_NAME}/$(EXETARGET)/' \
+ -e 's/$${PRODUCT_NAME.*}/$(EXETARGET)/' \
+ -e 's/$${MACOSX_DEPLOYMENT_TARGET}/$(MACOSX_VERSION)/' \
+ -e 's/$${NETSURF_VERSION}/$(VERSION_FULL)/' \
+ -e 's/$${NETSURF_SHORT_VERSION}/$(VERSION_MAJ).$(VERSION_MIN)/' \
+ < $(FRONTEND_RESOURCES_DIR)/NetSurf-Info.plist > NetSurf.app/Contents/Info.plist
+
+# ----------------------------------------------------------------------------
+# Package target
+# ----------------------------------------------------------------------------
+
+package-cocoa: NetSurf.dmg
+
+.INTERMEDIATE: NetSurf.tmp.dmg
+
+NetSurf.tmp.dmg: NetSurf.app
+ hdiutil create -size 8m -fs HFS+ -volname "NetSurf" $@
+ sleep 2
+ hdiutil attach $@
+ sleep 2
+ cp -pPR $^ /Volumes/NetSurf/
+ hdiutil detach $$(echo $$(hdiutil attach $@ | cut -f 1) | cut -f 1 -d ' ')
+ sleep 2
+
+NetSurf.dmg: NetSurf.tmp.dmg
+ hdiutil convert $^ -format UDZO -o $@
+
+CLEANS += clean-package-cocoa
+
+clean-package-cocoa:
+ $(VQ)echo " CLEAN: NetSurf.tmp.dmg"
+ $(Q)$(RM) NetSurf.tmp.dmg
+ $(VQ)echo " CLEAN: NetSurf.dmg"
+ $(Q)$(RM) NetSurf.dmg
+ $(VQ)echo " CLEAN: NetSurf.app"
+ $(Q)$(RM) -r NetSurf.app
diff --git a/frontends/cocoa/Makefile.defaults b/frontends/cocoa/Makefile.defaults
new file mode 100644
index 000000000..8f5792b0a
--- /dev/null
+++ b/frontends/cocoa/Makefile.defaults
@@ -0,0 +1,30 @@
+# ----------------------------------------------------------------------------
+# Cocoa-specific options
+# ----------------------------------------------------------------------------
+
+# Force using glibc internal iconv implementation instead of external libiconv
+# Valid options: YES, NO
+NETSURF_USE_LIBICONV_PLUG := NO
+
+# Enable NetSurf's use of librosprite for displaying RISC OS Sprites
+# Valid options: YES, NO, AUTO
+NETSURF_USE_ROSPRITE := NO
+
+# Enable NetSurf's use of librsvg in conjunction with Cairo to display SVGs
+# Valid options: YES, NO, AUTO
+NETSURF_USE_RSVG := AUTO
+
+# Enable NetSurf's use of libsvgtiny for displaying SVGs
+# Valid options: YES, NO, AUTO
+NETSURF_USE_NSSVG := AUTO
+
+NETSURF_USE_BMP := NO
+NETSURF_USE_GIF := NO
+NETSURF_USE_PNG := NO
+NETSURF_USE_JPEG := NO
+NETSURF_USE_IMAGEIO := YES
+
+MACOSX_VERSION := $(shell sw_vers -productVersion | awk -F '.' '{print $$1 "." $$2}')
+
+# Optimisation levels
+CFLAGS += -O2
diff --git a/frontends/cocoa/NetSurf.xcodeproj/project.pbxproj b/frontends/cocoa/NetSurf.xcodeproj/project.pbxproj
new file mode 100644
index 000000000..ef25d2f1a
--- /dev/null
+++ b/frontends/cocoa/NetSurf.xcodeproj/project.pbxproj
@@ -0,0 +1,1023 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 45;
+ objects = {
+
+/* Begin PBXFileReference section */
+ 260F1F6312D620E800D9B07F /* content.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = content.c; sourceTree = "<group>"; };
+ 260F1F6412D620E800D9B07F /* content.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = content.h; sourceTree = "<group>"; };
+ 260F1F6512D620E800D9B07F /* content_protected.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = content_protected.h; sourceTree = "<group>"; };
+ 260F1F6612D620E800D9B07F /* content_type.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = content_type.h; sourceTree = "<group>"; };
+ 260F1F6712D620E800D9B07F /* dirlist.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dirlist.c; sourceTree = "<group>"; };
+ 260F1F6812D620E800D9B07F /* dirlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dirlist.h; sourceTree = "<group>"; };
+ 260F1F6912D620E800D9B07F /* fetch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fetch.c; sourceTree = "<group>"; };
+ 260F1F6A12D620E800D9B07F /* fetch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fetch.h; sourceTree = "<group>"; };
+ 260F1F6C12D620E800D9B07F /* curl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = curl.c; sourceTree = "<group>"; };
+ 260F1F6D12D620E800D9B07F /* curl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = curl.h; sourceTree = "<group>"; };
+ 260F1F6E12D620E800D9B07F /* data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = data.c; sourceTree = "<group>"; };
+ 260F1F6F12D620E800D9B07F /* data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = data.h; sourceTree = "<group>"; };
+ 260F1F7012D620E800D9B07F /* file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = file.c; sourceTree = "<group>"; };
+ 260F1F7112D620E800D9B07F /* file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file.h; sourceTree = "<group>"; };
+ 260F1F7212D620E800D9B07F /* hlcache.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hlcache.c; sourceTree = "<group>"; };
+ 260F1F7312D620E800D9B07F /* hlcache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hlcache.h; sourceTree = "<group>"; };
+ 260F1F7412D620E800D9B07F /* llcache.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = llcache.c; sourceTree = "<group>"; };
+ 260F1F7512D620E800D9B07F /* llcache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = llcache.h; sourceTree = "<group>"; };
+ 260F1F7612D620E800D9B07F /* urldb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = urldb.c; sourceTree = "<group>"; };
+ 260F1F7712D620E800D9B07F /* urldb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = urldb.h; sourceTree = "<group>"; };
+ 260F1F7912D620E800D9B07F /* css.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = css.c; sourceTree = "<group>"; };
+ 260F1F7A12D620E800D9B07F /* css.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = css.h; sourceTree = "<group>"; };
+ 260F1F7B12D620E800D9B07F /* dump.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dump.c; sourceTree = "<group>"; };
+ 260F1F7C12D620E800D9B07F /* dump.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dump.h; sourceTree = "<group>"; };
+ 260F1F7D12D620E800D9B07F /* internal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = internal.c; sourceTree = "<group>"; };
+ 260F1F7E12D620E800D9B07F /* internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = internal.h; sourceTree = "<group>"; };
+ 260F1F7F12D620E800D9B07F /* select.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = select.c; sourceTree = "<group>"; };
+ 260F1F8012D620E800D9B07F /* select.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = select.h; sourceTree = "<group>"; };
+ 260F1F8112D620E800D9B07F /* utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = utils.c; sourceTree = "<group>"; };
+ 260F1F8212D620E800D9B07F /* utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utils.h; sourceTree = "<group>"; };
+ 260F1F8412D620E800D9B07F /* 401login.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 401login.h; sourceTree = "<group>"; };
+ 260F1F8512D620E800D9B07F /* browser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = browser.c; sourceTree = "<group>"; };
+ 260F1F8612D620E800D9B07F /* browser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = browser.h; sourceTree = "<group>"; };
+ 260F1F8712D620E800D9B07F /* cookies.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cookies.c; sourceTree = "<group>"; };
+ 260F1F8812D620E800D9B07F /* cookies_old.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cookies_old.h; sourceTree = "<group>"; };
+ 260F1F8912D620E800D9B07F /* download.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = download.c; sourceTree = "<group>"; };
+ 260F1F8A12D620E800D9B07F /* download.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = download.h; sourceTree = "<group>"; };
+ 260F1F8B12D620E800D9B07F /* frames.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = frames.c; sourceTree = "<group>"; };
+ 260F1F8C12D620E800D9B07F /* frames.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = frames.h; sourceTree = "<group>"; };
+ 260F1F8D12D620E800D9B07F /* gui.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gui.h; sourceTree = "<group>"; };
+ 260F1F8E12D620E800D9B07F /* local_history.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = local_history.c; sourceTree = "<group>"; };
+ 260F1F8F12D620E800D9B07F /* local_history.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = local_history.h; sourceTree = "<group>"; };
+ 260F1F9012D620E800D9B07F /* history_global_core.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = history_global_core.c; sourceTree = "<group>"; };
+ 260F1F9112D620E800D9B07F /* history_global_core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = history_global_core.h; sourceTree = "<group>"; };
+ 260F1F9212D620E800D9B07F /* hotlist.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hotlist.c; sourceTree = "<group>"; };
+ 260F1F9312D620E800D9B07F /* hotlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hotlist.h; sourceTree = "<group>"; };
+ 260F1F9412D620E800D9B07F /* knockout.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = knockout.c; sourceTree = "<group>"; };
+ 260F1F9512D620E800D9B07F /* knockout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = knockout.h; sourceTree = "<group>"; };
+ 260F1F9612D620E800D9B07F /* mouse.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mouse.c; sourceTree = "<group>"; };
+ 260F1F9712D620E800D9B07F /* mouse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mouse.h; sourceTree = "<group>"; };
+ 260F1F9812D620E800D9B07F /* netsurf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = netsurf.c; sourceTree = "<group>"; };
+ 260F1F9912D620E800D9B07F /* netsurf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = netsurf.h; sourceTree = "<group>"; };
+ 260F1F9A12D620E800D9B07F /* options.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = options.c; sourceTree = "<group>"; };
+ 260F1F9B12D620E800D9B07F /* options.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = options.h; sourceTree = "<group>"; };
+ 260F1F9C12D620E800D9B07F /* plot_style.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = plot_style.c; sourceTree = "<group>"; };
+ 260F1F9D12D620E800D9B07F /* plot_style.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = plot_style.h; sourceTree = "<group>"; };
+ 260F1F9E12D620E800D9B07F /* plotters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = plotters.h; sourceTree = "<group>"; };
+ 260F1F9F12D620E800D9B07F /* print.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = print.c; sourceTree = "<group>"; };
+ 260F1FA012D620E800D9B07F /* print.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = print.h; sourceTree = "<group>"; };
+ 260F1FA112D620E800D9B07F /* printer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = printer.h; sourceTree = "<group>"; };
+ 260F1FA212D620E800D9B07F /* save_complete.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = save_complete.c; sourceTree = "<group>"; };
+ 260F1FA312D620E800D9B07F /* save_complete.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = save_complete.h; sourceTree = "<group>"; };
+ 260F1FA512D620E800D9B07F /* font_haru.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = font_haru.c; sourceTree = "<group>"; };
+ 260F1FA612D620E800D9B07F /* font_haru.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = font_haru.h; sourceTree = "<group>"; };
+ 260F1FA712D620E800D9B07F /* pdf_plotters.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pdf_plotters.c; sourceTree = "<group>"; };
+ 260F1FA812D620E800D9B07F /* pdf_plotters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pdf_plotters.h; sourceTree = "<group>"; };
+ 260F1FAA12D620E800D9B07F /* save_text.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = save_text.c; sourceTree = "<group>"; };
+ 260F1FAB12D620E800D9B07F /* save_text.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = save_text.h; sourceTree = "<group>"; };
+ 260F1FAC12D620E800D9B07F /* scroll.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = scroll.c; sourceTree = "<group>"; };
+ 260F1FAD12D620E800D9B07F /* scroll.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scroll.h; sourceTree = "<group>"; };
+ 260F1FAE12D620E800D9B07F /* search.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = search.c; sourceTree = "<group>"; };
+ 260F1FAF12D620E800D9B07F /* search.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = search.h; sourceTree = "<group>"; };
+ 260F1FB012D620E800D9B07F /* searchweb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = searchweb.c; sourceTree = "<group>"; };
+ 260F1FB112D620E800D9B07F /* searchweb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = searchweb.h; sourceTree = "<group>"; };
+ 260F1FB212D620E800D9B07F /* selection.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = selection.c; sourceTree = "<group>"; };
+ 260F1FB312D620E800D9B07F /* selection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = selection.h; sourceTree = "<group>"; };
+ 260F1FB412D620E800D9B07F /* sslcert.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sslcert.c; sourceTree = "<group>"; };
+ 260F1FB512D620E800D9B07F /* sslcert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sslcert.h; sourceTree = "<group>"; };
+ 260F1FB612D620E800D9B07F /* textarea.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = textarea.c; sourceTree = "<group>"; };
+ 260F1FB712D620E800D9B07F /* textarea.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = textarea.h; sourceTree = "<group>"; };
+ 260F1FB812D620E800D9B07F /* textinput.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = textinput.c; sourceTree = "<group>"; };
+ 260F1FB912D620E800D9B07F /* textinput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = textinput.h; sourceTree = "<group>"; };
+ 260F1FBA12D620E800D9B07F /* tree.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tree.c; sourceTree = "<group>"; };
+ 260F1FBB12D620E800D9B07F /* tree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tree.h; sourceTree = "<group>"; };
+ 260F1FBC12D620E800D9B07F /* tree_url_node.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tree_url_node.c; sourceTree = "<group>"; };
+ 260F1FBD12D620E800D9B07F /* tree_url_node.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tree_url_node.h; sourceTree = "<group>"; };
+ 260F1FBE12D620E800D9B07F /* version.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = version.c; sourceTree = "<group>"; };
+ 260F1FC712D620E800D9B07F /* box.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = box.c; sourceTree = "<group>"; };
+ 260F1FC812D620E800D9B07F /* box.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = box.h; sourceTree = "<group>"; };
+ 260F1FC912D620E800D9B07F /* box_construct.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = box_construct.c; sourceTree = "<group>"; };
+ 260F1FCA12D620E800D9B07F /* box_normalise.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = box_normalise.c; sourceTree = "<group>"; };
+ 260F1FCB12D620E800D9B07F /* favicon.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = favicon.c; sourceTree = "<group>"; };
+ 260F1FCC12D620E800D9B07F /* favicon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = favicon.h; sourceTree = "<group>"; };
+ 260F1FCD12D620E800D9B07F /* font.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = font.c; sourceTree = "<group>"; };
+ 260F1FCE12D620E800D9B07F /* font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = font.h; sourceTree = "<group>"; };
+ 260F1FCF12D620E800D9B07F /* form.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = form.c; sourceTree = "<group>"; };
+ 260F1FD012D620E800D9B07F /* form.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = form.h; sourceTree = "<group>"; };
+ 260F1FD112D620E800D9B07F /* html.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = html.c; sourceTree = "<group>"; };
+ 260F1FD212D620E800D9B07F /* html.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = html.h; sourceTree = "<group>"; };
+ 260F1FD312D620E800D9B07F /* html_interaction.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = html_interaction.c; sourceTree = "<group>"; };
+ 260F1FD412D620E800D9B07F /* html_redraw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = html_redraw.c; sourceTree = "<group>"; };
+ 260F1FD512D620E800D9B07F /* hubbub_binding.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hubbub_binding.c; sourceTree = "<group>"; };
+ 260F1FD612D620E800D9B07F /* imagemap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = imagemap.c; sourceTree = "<group>"; };
+ 260F1FD712D620E800D9B07F /* imagemap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = imagemap.h; sourceTree = "<group>"; };
+ 260F1FD812D620E800D9B07F /* layout.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = layout.c; sourceTree = "<group>"; };
+ 260F1FD912D620E800D9B07F /* layout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = layout.h; sourceTree = "<group>"; };
+ 260F1FDA12D620E800D9B07F /* list.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = list.c; sourceTree = "<group>"; };
+ 260F1FDB12D620E800D9B07F /* list.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = list.h; sourceTree = "<group>"; };
+ 260F1FDC12D620E800D9B07F /* parser_binding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = parser_binding.h; sourceTree = "<group>"; };
+ 260F1FDD12D620E800D9B07F /* table.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = table.c; sourceTree = "<group>"; };
+ 260F1FDE12D620E800D9B07F /* table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = table.h; sourceTree = "<group>"; };
+ 260F1FE012D620E800D9B07F /* textplain.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = textplain.c; sourceTree = "<group>"; };
+ 260F1FE112D620E800D9B07F /* textplain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = textplain.h; sourceTree = "<group>"; };
+ 260F1FE312D620E800D9B07F /* base64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = base64.c; sourceTree = "<group>"; };
+ 260F1FE412D620E800D9B07F /* base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base64.h; sourceTree = "<group>"; };
+ 260F1FE512D620E800D9B07F /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = "<group>"; };
+ 260F1FE612D620E800D9B07F /* container.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = container.c; sourceTree = "<group>"; };
+ 260F1FE712D620E800D9B07F /* container.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = container.h; sourceTree = "<group>"; };
+ 260F1FE812D620E800D9B07F /* errors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = errors.h; sourceTree = "<group>"; };
+ 260F1FE912D620E800D9B07F /* filename.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = filename.c; sourceTree = "<group>"; };
+ 260F1FEA12D620E800D9B07F /* filename.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = filename.h; sourceTree = "<group>"; };
+ 260F1FEB12D620E800D9B07F /* resource.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = resource.c; sourceTree = "<group>"; };
+ 260F1FEC12D620E800D9B07F /* resource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = resource.h; sourceTree = "<group>"; };
+ 260F1FED12D620E800D9B07F /* hashtable.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hashtable.c; sourceTree = "<group>"; };
+ 260F1FEE12D620E800D9B07F /* hashtable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hashtable.h; sourceTree = "<group>"; };
+ 260F1FEF12D620E800D9B07F /* http.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = http.c; sourceTree = "<group>"; };
+ 260F1FF012D620E800D9B07F /* http.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = http.h; sourceTree = "<group>"; };
+ 260F1FF112D620E800D9B07F /* locale.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = locale.c; sourceTree = "<group>"; };
+ 260F1FF212D620E800D9B07F /* locale.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = locale.h; sourceTree = "<group>"; };
+ 260F1FF312D620E800D9B07F /* log.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = log.c; sourceTree = "<group>"; };
+ 260F1FF412D620E800D9B07F /* log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = log.h; sourceTree = "<group>"; };
+ 260F1FF612D620E800D9B07F /* memdebug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = memdebug.c; sourceTree = "<group>"; };
+ 260F1FF712D620E800D9B07F /* memdebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = memdebug.h; sourceTree = "<group>"; };
+ 260F1FF812D620E800D9B07F /* messages.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = messages.c; sourceTree = "<group>"; };
+ 260F1FF912D620E800D9B07F /* messages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = messages.h; sourceTree = "<group>"; };
+ 260F1FFA12D620E800D9B07F /* ring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ring.h; sourceTree = "<group>"; };
+ 260F1FFB12D620E800D9B07F /* talloc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = talloc.c; sourceTree = "<group>"; };
+ 260F1FFC12D620E800D9B07F /* talloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = talloc.h; sourceTree = "<group>"; };
+ 260F1FFF12D620E800D9B07F /* url.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = url.c; sourceTree = "<group>"; };
+ 260F200012D620E800D9B07F /* url.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = url.h; sourceTree = "<group>"; };
+ 260F200112D620E800D9B07F /* useragent.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = useragent.c; sourceTree = "<group>"; };
+ 260F200212D620E800D9B07F /* useragent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = useragent.h; sourceTree = "<group>"; };
+ 260F200312D620E800D9B07F /* utf8.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = utf8.c; sourceTree = "<group>"; };
+ 260F200412D620E800D9B07F /* utf8.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utf8.h; sourceTree = "<group>"; };
+ 260F200512D620E800D9B07F /* utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = utils.c; sourceTree = "<group>"; };
+ 260F200612D620E800D9B07F /* utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utils.h; sourceTree = "<group>"; };
+ 260F200712D620E800D9B07F /* utsname.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utsname.h; sourceTree = "<group>"; };
+ 260FC03112D85ACE00079C00 /* bitmap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bitmap.h; sourceTree = "<group>"; };
+ 26121DA812D700B800E10F91 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = "<group>"; };
+ 26121EAB12D70E0A00E10F91 /* Browser.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Browser.xib; sourceTree = "<group>"; };
+ 26121EFB12D7132100E10F91 /* BrowserView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BrowserView.h; sourceTree = "<group>"; };
+ 26121EFC12D7132100E10F91 /* BrowserView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BrowserView.m; sourceTree = "<group>"; };
+ 261223B712D77F9C00E10F91 /* font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = font.h; sourceTree = "<group>"; };
+ 261223CB12D7805300E10F91 /* plotter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = plotter.h; sourceTree = "<group>"; };
+ 2612265712D7ACB500E10F91 /* default.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = default.css; sourceTree = "<group>"; };
+ 2612265912D7ACB500E10F91 /* quirks.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = quirks.css; sourceTree = "<group>"; };
+ 2612265D12D7AD6800E10F91 /* adblock.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = adblock.css; sourceTree = "<group>"; };
+ 2612269012D7AE4100E10F91 /* de */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = de; path = de.lproj/Messages; sourceTree = "<group>"; };
+ 2612269312D7AE9B00E10F91 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text; name = nl; path = nl.lproj/Messages; sourceTree = "<group>"; };
+ 2612269412D7AEA800E10F91 /* en */ = {isa = PBXFileReference; lastKnownFileType = text; name = en; path = en.lproj/Messages; sourceTree = "<group>"; };
+ 2612269512D7AEB500E10F91 /* it */ = {isa = PBXFileReference; lastKnownFileType = text; name = it; path = it.lproj/Messages; sourceTree = "<group>"; };
+ 2612269612D7AEBE00E10F91 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text; name = fr; path = fr.lproj/Messages; sourceTree = "<group>"; };
+ 261DB22213180AFF00C59F12 /* en */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
+ 261DB22613180B4F00C59F12 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
+ 261DB23313180CD600C59F12 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
+ 261DB23413180CE000C59F12 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
+ 261DB23513180CEE00C59F12 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
+ 261DB24F1318444F00C59F12 /* compile-xib.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "compile-xib.sh"; sourceTree = "<group>"; };
+ 261DB2501318444F00C59F12 /* extract-strings.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "extract-strings.sh"; sourceTree = "<group>"; };
+ 261DB29013185C0A00C59F12 /* de */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/BookmarksWindow.xib.strings; sourceTree = "<group>"; };
+ 261DB29413185C0A00C59F12 /* de */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/BrowserWindow.xib.strings; sourceTree = "<group>"; };
+ 261DB29613185C0A00C59F12 /* de */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/DownloadWindow.xib.strings; sourceTree = "<group>"; };
+ 261DB29813185C0A00C59F12 /* de */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/HistoryWindow.xib.strings; sourceTree = "<group>"; };
+ 261DB29C13185C0A00C59F12 /* de */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/MainMenu.xib.strings; sourceTree = "<group>"; };
+ 261DB29E13185C0A00C59F12 /* de */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/PreferencesWindow.xib.strings; sourceTree = "<group>"; };
+ 261DB2A013185C0A00C59F12 /* de */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/SearchWindow.xib.strings; sourceTree = "<group>"; };
+ 2622F1D512DCD84600CD5A62 /* TreeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TreeView.h; sourceTree = "<group>"; };
+ 2622F1D612DCD84600CD5A62 /* TreeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TreeView.m; sourceTree = "<group>"; };
+ 2625095012F72A8F0090D236 /* PreferencesWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PreferencesWindow.xib; sourceTree = "<group>"; };
+ 2625095112F72AA70090D236 /* PreferencesWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreferencesWindowController.h; sourceTree = "<group>"; };
+ 2625095212F72AA70090D236 /* PreferencesWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PreferencesWindowController.m; sourceTree = "<group>"; };
+ 2636299412F699250048542C /* NetSurf.app */ = {isa = PBXFileReference; lastKnownFileType = wrapper.application; name = NetSurf.app; path = ../NetSurf.app; sourceTree = SOURCE_ROOT; };
+ 263629B312F69A290048542C /* Makefile.config */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; fileEncoding = 4; name = Makefile.config; path = ../Makefile.config; sourceTree = SOURCE_ROOT; };
+ 263629B412F69A290048542C /* Makefile.defaults */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; fileEncoding = 4; name = Makefile.defaults; path = ../Makefile.defaults; sourceTree = SOURCE_ROOT; };
+ 263629B512F69A290048542C /* Makefile.resources */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; fileEncoding = 4; name = Makefile.resources; path = ../Makefile.resources; sourceTree = SOURCE_ROOT; };
+ 263629B612F69A290048542C /* Makefile.sources */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; fileEncoding = 4; name = Makefile.sources; path = ../Makefile.sources; sourceTree = SOURCE_ROOT; };
+ 263629B712F69A3C0048542C /* Makefile.target */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; fileEncoding = 4; path = Makefile.target; sourceTree = "<group>"; };
+ 263629BC12F69A760048542C /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; name = Makefile; path = ../Makefile; sourceTree = SOURCE_ROOT; };
+ 263629C812F69B120048542C /* NetsurfApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetsurfApp.h; sourceTree = "<group>"; };
+ 263629C912F69B120048542C /* NetsurfApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NetsurfApp.m; sourceTree = "<group>"; };
+ 263629CA12F69B120048542C /* system_colour.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = system_colour.m; sourceTree = "<group>"; };
+ 263769A912F7EBE2000F45FE /* Tree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Tree.h; sourceTree = "<group>"; };
+ 263769AA12F7EBE2000F45FE /* Tree.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Tree.m; sourceTree = "<group>"; };
+ 26376A4112F7FA67000F45FE /* HistoryWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = HistoryWindow.xib; sourceTree = "<group>"; };
+ 26376A4312F7FA86000F45FE /* HistoryWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HistoryWindowController.h; sourceTree = "<group>"; };
+ 26376A4412F7FA86000F45FE /* HistoryWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HistoryWindowController.m; sourceTree = "<group>"; };
+ 26376A8A12F7FF57000F45FE /* BookmarksController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BookmarksController.h; sourceTree = "<group>"; };
+ 26376A8B12F7FF57000F45FE /* BookmarksController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BookmarksController.m; sourceTree = "<group>"; };
+ 26376BAC12F820D7000F45FE /* BookmarksWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BookmarksWindow.xib; sourceTree = "<group>"; };
+ 2639E20512F2ADEE00699678 /* coordinates.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coordinates.h; sourceTree = "<group>"; };
+ 264C344112F0987E00D11246 /* gui.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gui.h; sourceTree = "<group>"; };
+ 265F30A712D6637E0048B600 /* NetSurf-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "NetSurf-Info.plist"; sourceTree = "<group>"; };
+ 265F30AB12D6637E0048B600 /* Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Prefix.pch; sourceTree = "<group>"; };
+ 265F311912D663F50048B600 /* gui.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = gui.m; sourceTree = "<group>"; };
+ 265F314712D666660048B600 /* plotter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = plotter.m; sourceTree = "<group>"; };
+ 265F316112D667E10048B600 /* schedule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = schedule.m; sourceTree = "<group>"; };
+ 265F316612D668130048B600 /* thumbnail.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = thumbnail.m; sourceTree = "<group>"; };
+ 265F316F12D668790048B600 /* fetch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = fetch.m; sourceTree = "<group>"; };
+ 265F319012D668DB0048B600 /* url.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = url.m; sourceTree = "<group>"; };
+ 265F31C412D66A0D0048B600 /* save.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = save.m; sourceTree = "<group>"; };
+ 265F31CA12D66A890048B600 /* bitmap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bitmap.h; sourceTree = "<group>"; };
+ 265F31CB12D66A890048B600 /* bmp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bmp.c; sourceTree = "<group>"; };
+ 265F31CC12D66A890048B600 /* bmp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bmp.h; sourceTree = "<group>"; };
+ 265F31CD12D66A890048B600 /* gif.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gif.c; sourceTree = "<group>"; };
+ 265F31CE12D66A890048B600 /* gif.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gif.h; sourceTree = "<group>"; };
+ 265F31CF12D66A890048B600 /* ico.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ico.c; sourceTree = "<group>"; };
+ 265F31D012D66A890048B600 /* ico.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ico.h; sourceTree = "<group>"; };
+ 265F31D112D66A890048B600 /* jpeg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = jpeg.c; sourceTree = "<group>"; };
+ 265F31D212D66A890048B600 /* jpeg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = jpeg.h; sourceTree = "<group>"; };
+ 265F31D312D66A890048B600 /* mng.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mng.c; sourceTree = "<group>"; };
+ 265F31D412D66A890048B600 /* mng.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mng.h; sourceTree = "<group>"; };
+ 265F31D512D66A890048B600 /* nssprite.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nssprite.c; sourceTree = "<group>"; };
+ 265F31D612D66A890048B600 /* nssprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nssprite.h; sourceTree = "<group>"; };
+ 265F31D712D66A890048B600 /* png.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = png.c; sourceTree = "<group>"; };
+ 265F31D812D66A890048B600 /* png.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = png.h; sourceTree = "<group>"; };
+ 265F31D912D66A890048B600 /* rsvg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rsvg.c; sourceTree = "<group>"; };
+ 265F31DA12D66A890048B600 /* rsvg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rsvg.h; sourceTree = "<group>"; };
+ 265F31DB12D66A890048B600 /* svg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = svg.c; sourceTree = "<group>"; };
+ 265F31DC12D66A890048B600 /* svg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svg.h; sourceTree = "<group>"; };
+ 265F31DD12D66A890048B600 /* webp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = webp.c; sourceTree = "<group>"; };
+ 265F31DE12D66A890048B600 /* webp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = webp.h; sourceTree = "<group>"; };
+ 265F31EB12D66B190048B600 /* bitmap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = bitmap.m; sourceTree = "<group>"; };
+ 265F320512D66C200048B600 /* utf8.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = utf8.m; sourceTree = "<group>"; };
+ 265F321312D66CD90048B600 /* utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = utils.m; sourceTree = "<group>"; };
+ 265F321E12D66D510048B600 /* font.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = font.m; sourceTree = "<group>"; };
+ 2666DC5B12F6D1770045E8B6 /* SearchWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SearchWindow.xib; sourceTree = "<group>"; };
+ 2666DC5D12F6D2E70045E8B6 /* SearchWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SearchWindowController.h; sourceTree = "<group>"; };
+ 2666DC5E12F6D2E70045E8B6 /* SearchWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SearchWindowController.m; sourceTree = "<group>"; };
+ 2666DD5012F706BC0045E8B6 /* BrowserWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BrowserWindow.h; sourceTree = "<group>"; };
+ 2666DD5112F706BC0045E8B6 /* BrowserWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BrowserWindow.m; sourceTree = "<group>"; };
+ 2684028E1301848100850DA2 /* HomeTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = HomeTemplate.pdf; sourceTree = "<group>"; };
+ 26ABD61C12F02EB900407161 /* overflowImage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = overflowImage.png; sourceTree = "<group>"; };
+ 26ABD61D12F02EB900407161 /* overflowImagePressed.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = overflowImagePressed.png; sourceTree = "<group>"; };
+ 26ABD61E12F02EB900407161 /* pi.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = pi.png; sourceTree = "<group>"; };
+ 26AFE63E12DDEB0A005AD082 /* NetSurf.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = NetSurf.icns; sourceTree = "<group>"; };
+ 26AFE8E212DF4200005AD082 /* ScrollableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScrollableView.h; sourceTree = "<group>"; };
+ 26AFE8E312DF4200005AD082 /* ScrollableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScrollableView.m; sourceTree = "<group>"; };
+ 26AFE97A12DF514C005AD082 /* NetSurfAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetSurfAppDelegate.h; sourceTree = "<group>"; };
+ 26AFE97B12DF514C005AD082 /* NetSurfAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NetSurfAppDelegate.m; sourceTree = "<group>"; };
+ 26AFEAE912E04253005AD082 /* DownloadWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DownloadWindowController.h; sourceTree = "<group>"; };
+ 26AFEAEA12E04253005AD082 /* DownloadWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DownloadWindowController.m; sourceTree = "<group>"; };
+ 26AFEAF012E042F9005AD082 /* DownloadWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DownloadWindow.xib; sourceTree = "<group>"; };
+ 26AFED0312E09916005AD082 /* selection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = selection.m; sourceTree = "<group>"; };
+ 26B4E91A130D351F0003B527 /* ArrowBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArrowBox.h; sourceTree = "<group>"; };
+ 26B4E91B130D351F0003B527 /* ArrowBox.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArrowBox.m; sourceTree = "<group>"; };
+ 26B4E91C130D351F0003B527 /* ArrowWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArrowWindow.h; sourceTree = "<group>"; };
+ 26B4E91D130D351F0003B527 /* ArrowWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArrowWindow.m; sourceTree = "<group>"; };
+ 26B4E91E130D351F0003B527 /* BlackScroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlackScroller.h; sourceTree = "<group>"; };
+ 26B4E91F130D351F0003B527 /* BlackScroller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BlackScroller.m; sourceTree = "<group>"; };
+ 26B4E926130D36A90003B527 /* LocalHistoryPanel.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = LocalHistoryPanel.xib; sourceTree = "<group>"; };
+ 26B4E928130D37E50003B527 /* LocalHistoryController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LocalHistoryController.h; sourceTree = "<group>"; };
+ 26B4E929130D37E50003B527 /* LocalHistoryController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LocalHistoryController.m; sourceTree = "<group>"; };
+ 26BA25AB1321653200AEC1DA /* apple_image.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = apple_image.m; sourceTree = "<group>"; };
+ 26BA25AC1321653200AEC1DA /* apple_image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = apple_image.h; sourceTree = "<group>"; };
+ 26CDCEB312E702D8004FC66B /* NSBezierPath_AMShading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSBezierPath_AMShading.h; sourceTree = "<group>"; };
+ 26CDCEB412E702D8004FC66B /* NSBezierPath_AMShading.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSBezierPath_AMShading.m; sourceTree = "<group>"; };
+ 26CDCEB512E702D8004FC66B /* NSString_AITruncation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSString_AITruncation.h; sourceTree = "<group>"; };
+ 26CDCEB612E702D8004FC66B /* NSString_AITruncation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSString_AITruncation.m; sourceTree = "<group>"; };
+ 26CDCEBD12E702D8004FC66B /* PSMOverflowPopUpButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMOverflowPopUpButton.h; sourceTree = "<group>"; };
+ 26CDCEBE12E702D8004FC66B /* PSMOverflowPopUpButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSMOverflowPopUpButton.m; sourceTree = "<group>"; };
+ 26CDCEBF12E702D8004FC66B /* PSMProgressIndicator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMProgressIndicator.h; sourceTree = "<group>"; };
+ 26CDCEC012E702D8004FC66B /* PSMProgressIndicator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSMProgressIndicator.m; sourceTree = "<group>"; };
+ 26CDCEC112E702D8004FC66B /* PSMRolloverButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMRolloverButton.h; sourceTree = "<group>"; };
+ 26CDCEC212E702D8004FC66B /* PSMRolloverButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSMRolloverButton.m; sourceTree = "<group>"; };
+ 26CDCEC312E702D8004FC66B /* PSMTabBarCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMTabBarCell.h; sourceTree = "<group>"; };
+ 26CDCEC412E702D8004FC66B /* PSMTabBarCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSMTabBarCell.m; sourceTree = "<group>"; };
+ 26CDCEC512E702D8004FC66B /* PSMTabBarControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMTabBarControl.h; sourceTree = "<group>"; };
+ 26CDCEC612E702D8004FC66B /* PSMTabBarControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSMTabBarControl.m; sourceTree = "<group>"; };
+ 26CDCEC712E702D8004FC66B /* PSMTabBarController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMTabBarController.h; sourceTree = "<group>"; };
+ 26CDCEC812E702D8004FC66B /* PSMTabBarController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSMTabBarController.m; sourceTree = "<group>"; };
+ 26CDCEC912E702D8004FC66B /* PSMTabDragAssistant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMTabDragAssistant.h; sourceTree = "<group>"; };
+ 26CDCECA12E702D8004FC66B /* PSMTabDragAssistant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSMTabDragAssistant.m; sourceTree = "<group>"; };
+ 26CDCECB12E702D8004FC66B /* PSMTabDragView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMTabDragView.h; sourceTree = "<group>"; };
+ 26CDCECC12E702D8004FC66B /* PSMTabDragView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSMTabDragView.m; sourceTree = "<group>"; };
+ 26CDCECD12E702D8004FC66B /* PSMTabDragWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMTabDragWindow.h; sourceTree = "<group>"; };
+ 26CDCECE12E702D8004FC66B /* PSMTabDragWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSMTabDragWindow.m; sourceTree = "<group>"; };
+ 26CDCECF12E702D8004FC66B /* PSMTabDragWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMTabDragWindowController.h; sourceTree = "<group>"; };
+ 26CDCED012E702D8004FC66B /* PSMTabDragWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSMTabDragWindowController.m; sourceTree = "<group>"; };
+ 26CDCED112E702D8004FC66B /* PSMTabStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMTabStyle.h; sourceTree = "<group>"; };
+ 26CDCED212E702D8004FC66B /* PSMUnifiedTabStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMUnifiedTabStyle.h; sourceTree = "<group>"; };
+ 26CDCED312E702D8004FC66B /* PSMUnifiedTabStyle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSMUnifiedTabStyle.m; sourceTree = "<group>"; };
+ 26CDCFDC12E706FE004FC66B /* AquaTabClose_Front.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AquaTabClose_Front.png; sourceTree = "<group>"; };
+ 26CDCFDD12E706FF004FC66B /* AquaTabClose_Front_Pressed.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AquaTabClose_Front_Pressed.png; sourceTree = "<group>"; };
+ 26CDCFDE12E706FF004FC66B /* AquaTabClose_Front_Rollover.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AquaTabClose_Front_Rollover.png; sourceTree = "<group>"; };
+ 26CDCFDF12E706FF004FC66B /* AquaTabCloseDirty_Front.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AquaTabCloseDirty_Front.png; sourceTree = "<group>"; };
+ 26CDCFE012E706FF004FC66B /* AquaTabCloseDirty_Front_Pressed.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AquaTabCloseDirty_Front_Pressed.png; sourceTree = "<group>"; };
+ 26CDCFE112E706FF004FC66B /* AquaTabCloseDirty_Front_Rollover.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AquaTabCloseDirty_Front_Rollover.png; sourceTree = "<group>"; };
+ 26CDCFE212E706FF004FC66B /* AquaTabNew.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AquaTabNew.png; sourceTree = "<group>"; };
+ 26CDCFE312E706FF004FC66B /* AquaTabNewPressed.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AquaTabNewPressed.png; sourceTree = "<group>"; };
+ 26CDCFE412E706FF004FC66B /* AquaTabNewRollover.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AquaTabNewRollover.png; sourceTree = "<group>"; };
+ 26CDCFF212E70AD1004FC66B /* BrowserWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BrowserWindow.xib; sourceTree = "<group>"; };
+ 26CDD00112E70F56004FC66B /* BrowserWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BrowserWindowController.h; sourceTree = "<group>"; };
+ 26CDD00212E70F56004FC66B /* BrowserWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BrowserWindowController.m; sourceTree = "<group>"; };
+ 26CDD0F412E726E0004FC66B /* BrowserViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BrowserViewController.h; sourceTree = "<group>"; };
+ 26CDD0F512E726E0004FC66B /* BrowserViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BrowserViewController.m; sourceTree = "<group>"; };
+ 26E7A22B131FEBA8005B5B6A /* about.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = about.c; sourceTree = "<group>"; };
+ 26E7A22C131FEBA8005B5B6A /* about.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = about.h; sourceTree = "<group>"; };
+ 26E7A22D131FEBA8005B5B6A /* resource.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = resource.c; sourceTree = "<group>"; };
+ 26E7A22E131FEBA8005B5B6A /* resource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = resource.h; sourceTree = "<group>"; };
+ 26EC3B6812ED62C0000A960C /* URLFieldCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = URLFieldCell.h; sourceTree = "<group>"; };
+ 26EC3B6912ED62C0000A960C /* URLFieldCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = URLFieldCell.m; sourceTree = "<group>"; };
+ 26EC3C4212ED8202000A960C /* HistoryView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HistoryView.h; sourceTree = "<group>"; };
+ 26EC3C4312ED8202000A960C /* HistoryView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HistoryView.m; sourceTree = "<group>"; };
+ 26EC3F1612EF0CBD000A960C /* FormSelectMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FormSelectMenu.h; sourceTree = "<group>"; };
+ 26EC3F1712EF0CBD000A960C /* FormSelectMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FormSelectMenu.m; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXGroup section */
+ 19C28FACFE9D520D11CA2CBB /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 2636299412F699250048542C /* NetSurf.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 260F1F6212D620E800D9B07F /* content */ = {
+ isa = PBXGroup;
+ children = (
+ 260F1F6312D620E800D9B07F /* content.c */,
+ 260F1F6412D620E800D9B07F /* content.h */,
+ 260F1F6512D620E800D9B07F /* content_protected.h */,
+ 260F1F6612D620E800D9B07F /* content_type.h */,
+ 260F1F6712D620E800D9B07F /* dirlist.c */,
+ 260F1F6812D620E800D9B07F /* dirlist.h */,
+ 260F1F6912D620E800D9B07F /* fetch.c */,
+ 260F1F6A12D620E800D9B07F /* fetch.h */,
+ 260F1F6B12D620E800D9B07F /* fetchers */,
+ 260F1F7212D620E800D9B07F /* hlcache.c */,
+ 260F1F7312D620E800D9B07F /* hlcache.h */,
+ 260F1F7412D620E800D9B07F /* llcache.c */,
+ 260F1F7512D620E800D9B07F /* llcache.h */,
+ 260F1F7612D620E800D9B07F /* urldb.c */,
+ 260F1F7712D620E800D9B07F /* urldb.h */,
+ );
+ name = content;
+ path = ../content;
+ sourceTree = SOURCE_ROOT;
+ };
+ 260F1F6B12D620E800D9B07F /* fetchers */ = {
+ isa = PBXGroup;
+ children = (
+ 26E7A22B131FEBA8005B5B6A /* about.c */,
+ 26E7A22C131FEBA8005B5B6A /* about.h */,
+ 26E7A22D131FEBA8005B5B6A /* resource.c */,
+ 26E7A22E131FEBA8005B5B6A /* resource.h */,
+ 260F1F6C12D620E800D9B07F /* curl.c */,
+ 260F1F6D12D620E800D9B07F /* curl.h */,
+ 260F1F6E12D620E800D9B07F /* data.c */,
+ 260F1F6F12D620E800D9B07F /* data.h */,
+ 260F1F7012D620E800D9B07F /* file.c */,
+ 260F1F7112D620E800D9B07F /* file.h */,
+ );
+ path = fetchers;
+ sourceTree = "<group>";
+ };
+ 260F1F7812D620E800D9B07F /* css */ = {
+ isa = PBXGroup;
+ children = (
+ 260F1F7912D620E800D9B07F /* css.c */,
+ 260F1F7A12D620E800D9B07F /* css.h */,
+ 260F1F7B12D620E800D9B07F /* dump.c */,
+ 260F1F7C12D620E800D9B07F /* dump.h */,
+ 260F1F7D12D620E800D9B07F /* internal.c */,
+ 260F1F7E12D620E800D9B07F /* internal.h */,
+ 260F1F7F12D620E800D9B07F /* select.c */,
+ 260F1F8012D620E800D9B07F /* select.h */,
+ 260F1F8112D620E800D9B07F /* utils.c */,
+ 260F1F8212D620E800D9B07F /* utils.h */,
+ );
+ name = css;
+ path = ../css;
+ sourceTree = SOURCE_ROOT;
+ };
+ 260F1F8312D620E800D9B07F /* desktop */ = {
+ isa = PBXGroup;
+ children = (
+ 260F1F8412D620E800D9B07F /* 401login.h */,
+ 260F1F8512D620E800D9B07F /* browser.c */,
+ 260F1F8612D620E800D9B07F /* browser.h */,
+ 260F1F8712D620E800D9B07F /* cookies.c */,
+ 260F1F8812D620E800D9B07F /* cookies_old.h */,
+ 260F1F8912D620E800D9B07F /* download.c */,
+ 260F1F8A12D620E800D9B07F /* download.h */,
+ 260F1F8B12D620E800D9B07F /* frames.c */,
+ 260F1F8C12D620E800D9B07F /* frames.h */,
+ 260F1F8D12D620E800D9B07F /* gui.h */,
+ 260F1F8E12D620E800D9B07F /* local_history.c */,
+ 260F1F8F12D620E800D9B07F /* local_history.h */,
+ 260F1F9012D620E800D9B07F /* history_global_core.c */,
+ 260F1F9112D620E800D9B07F /* history_global_core.h */,
+ 260F1F9212D620E800D9B07F /* hotlist.c */,
+ 260F1F9312D620E800D9B07F /* hotlist.h */,
+ 260F1F9412D620E800D9B07F /* knockout.c */,
+ 260F1F9512D620E800D9B07F /* knockout.h */,
+ 260F1F9612D620E800D9B07F /* mouse.c */,
+ 260F1F9712D620E800D9B07F /* mouse.h */,
+ 260F1F9812D620E800D9B07F /* netsurf.c */,
+ 260F1F9912D620E800D9B07F /* netsurf.h */,
+ 260F1F9A12D620E800D9B07F /* options.c */,
+ 260F1F9B12D620E800D9B07F /* options.h */,
+ 260F1F9C12D620E800D9B07F /* plot_style.c */,
+ 260F1F9D12D620E800D9B07F /* plot_style.h */,
+ 260F1F9E12D620E800D9B07F /* plotters.h */,
+ 260F1F9F12D620E800D9B07F /* print.c */,
+ 260F1FA012D620E800D9B07F /* print.h */,
+ 260F1FA112D620E800D9B07F /* printer.h */,
+ 260F1FA212D620E800D9B07F /* save_complete.c */,
+ 260F1FA312D620E800D9B07F /* save_complete.h */,
+ 260F1FA412D620E800D9B07F /* save_pdf */,
+ 260F1FAA12D620E800D9B07F /* save_text.c */,
+ 260F1FAB12D620E800D9B07F /* save_text.h */,
+ 260F1FAC12D620E800D9B07F /* scroll.c */,
+ 260F1FAD12D620E800D9B07F /* scroll.h */,
+ 260F1FAE12D620E800D9B07F /* search.c */,
+ 260F1FAF12D620E800D9B07F /* search.h */,
+ 260F1FB012D620E800D9B07F /* searchweb.c */,
+ 260F1FB112D620E800D9B07F /* searchweb.h */,
+ 260F1FB212D620E800D9B07F /* selection.c */,
+ 260F1FB312D620E800D9B07F /* selection.h */,
+ 260F1FB412D620E800D9B07F /* sslcert.c */,
+ 260F1FB512D620E800D9B07F /* sslcert.h */,
+ 260F1FB612D620E800D9B07F /* textarea.c */,
+ 260F1FB712D620E800D9B07F /* textarea.h */,
+ 260F1FB812D620E800D9B07F /* textinput.c */,
+ 260F1FB912D620E800D9B07F /* textinput.h */,
+ 260F1FBA12D620E800D9B07F /* tree.c */,
+ 260F1FBB12D620E800D9B07F /* tree.h */,
+ 260F1FBC12D620E800D9B07F /* tree_url_node.c */,
+ 260F1FBD12D620E800D9B07F /* tree_url_node.h */,
+ 260F1FBE12D620E800D9B07F /* version.c */,
+ );
+ name = desktop;
+ path = ../desktop;
+ sourceTree = SOURCE_ROOT;
+ };
+ 260F1FA412D620E800D9B07F /* save_pdf */ = {
+ isa = PBXGroup;
+ children = (
+ 260F1FA512D620E800D9B07F /* font_haru.c */,
+ 260F1FA612D620E800D9B07F /* font_haru.h */,
+ 260F1FA712D620E800D9B07F /* pdf_plotters.c */,
+ 260F1FA812D620E800D9B07F /* pdf_plotters.h */,
+ );
+ path = save_pdf;
+ sourceTree = "<group>";
+ };
+ 260F1FC612D620E800D9B07F /* render */ = {
+ isa = PBXGroup;
+ children = (
+ 260F1FC712D620E800D9B07F /* box.c */,
+ 260F1FC812D620E800D9B07F /* box.h */,
+ 260F1FC912D620E800D9B07F /* box_construct.c */,
+ 260F1FCA12D620E800D9B07F /* box_normalise.c */,
+ 260F1FCB12D620E800D9B07F /* favicon.c */,
+ 260F1FCC12D620E800D9B07F /* favicon.h */,
+ 260F1FCD12D620E800D9B07F /* font.c */,
+ 260F1FCE12D620E800D9B07F /* font.h */,
+ 260F1FCF12D620E800D9B07F /* form.c */,
+ 260F1FD012D620E800D9B07F /* form.h */,
+ 260F1FD112D620E800D9B07F /* html.c */,
+ 260F1FD212D620E800D9B07F /* html.h */,
+ 260F1FD312D620E800D9B07F /* html_interaction.c */,
+ 260F1FD412D620E800D9B07F /* html_redraw.c */,
+ 260F1FD512D620E800D9B07F /* hubbub_binding.c */,
+ 260F1FD612D620E800D9B07F /* imagemap.c */,
+ 260F1FD712D620E800D9B07F /* imagemap.h */,
+ 260F1FD812D620E800D9B07F /* layout.c */,
+ 260F1FD912D620E800D9B07F /* layout.h */,
+ 260F1FDA12D620E800D9B07F /* list.c */,
+ 260F1FDB12D620E800D9B07F /* list.h */,
+ 260F1FDC12D620E800D9B07F /* parser_binding.h */,
+ 260F1FDD12D620E800D9B07F /* table.c */,
+ 260F1FDE12D620E800D9B07F /* table.h */,
+ 260F1FE012D620E800D9B07F /* textplain.c */,
+ 260F1FE112D620E800D9B07F /* textplain.h */,
+ );
+ name = render;
+ path = ../render;
+ sourceTree = SOURCE_ROOT;
+ };
+ 260F1FE212D620E800D9B07F /* utils */ = {
+ isa = PBXGroup;
+ children = (
+ 260F1FE312D620E800D9B07F /* base64.c */,
+ 260F1FE412D620E800D9B07F /* base64.h */,
+ 260F1FE512D620E800D9B07F /* config.h */,
+ 260F1FE612D620E800D9B07F /* container.c */,
+ 260F1FE712D620E800D9B07F /* container.h */,
+ 260F1FE812D620E800D9B07F /* errors.h */,
+ 260F1FE912D620E800D9B07F /* filename.c */,
+ 260F1FEA12D620E800D9B07F /* filename.h */,
+ 260F1FEB12D620E800D9B07F /* resource.c */,
+ 260F1FEC12D620E800D9B07F /* resource.h */,
+ 260F1FED12D620E800D9B07F /* hashtable.c */,
+ 260F1FEE12D620E800D9B07F /* hashtable.h */,
+ 260F1FEF12D620E800D9B07F /* http.c */,
+ 260F1FF012D620E800D9B07F /* http.h */,
+ 260F1FF112D620E800D9B07F /* locale.c */,
+ 260F1FF212D620E800D9B07F /* locale.h */,
+ 260F1FF312D620E800D9B07F /* log.c */,
+ 260F1FF412D620E800D9B07F /* log.h */,
+ 260F1FF612D620E800D9B07F /* memdebug.c */,
+ 260F1FF712D620E800D9B07F /* memdebug.h */,
+ 260F1FF812D620E800D9B07F /* messages.c */,
+ 260F1FF912D620E800D9B07F /* messages.h */,
+ 260F1FFA12D620E800D9B07F /* ring.h */,
+ 260F1FFB12D620E800D9B07F /* talloc.c */,
+ 260F1FFC12D620E800D9B07F /* talloc.h */,
+ 260F1FFF12D620E800D9B07F /* url.c */,
+ 260F200012D620E800D9B07F /* url.h */,
+ 260F200112D620E800D9B07F /* useragent.c */,
+ 260F200212D620E800D9B07F /* useragent.h */,
+ 260F200312D620E800D9B07F /* utf8.c */,
+ 260F200412D620E800D9B07F /* utf8.h */,
+ 260F200512D620E800D9B07F /* utils.c */,
+ 260F200612D620E800D9B07F /* utils.h */,
+ 260F200712D620E800D9B07F /* utsname.h */,
+ );
+ name = utils;
+ path = ../utils;
+ sourceTree = SOURCE_ROOT;
+ };
+ 261DB24E1318443500C59F12 /* Tools */ = {
+ isa = PBXGroup;
+ children = (
+ 261DB24F1318444F00C59F12 /* compile-xib.sh */,
+ 261DB2501318444F00C59F12 /* extract-strings.sh */,
+ );
+ name = Tools;
+ sourceTree = "<group>";
+ };
+ 263629B212F69A080048542C /* Makefiles */ = {
+ isa = PBXGroup;
+ children = (
+ 263629BC12F69A760048542C /* Makefile */,
+ 263629B712F69A3C0048542C /* Makefile.target */,
+ 263629B312F69A290048542C /* Makefile.config */,
+ 263629B412F69A290048542C /* Makefile.defaults */,
+ 263629B512F69A290048542C /* Makefile.resources */,
+ 263629B612F69A290048542C /* Makefile.sources */,
+ );
+ name = Makefiles;
+ sourceTree = "<group>";
+ };
+ 263769A812F7EBC3000F45FE /* Tree View */ = {
+ isa = PBXGroup;
+ children = (
+ 2622F1D512DCD84600CD5A62 /* TreeView.h */,
+ 2622F1D612DCD84600CD5A62 /* TreeView.m */,
+ 263769A912F7EBE2000F45FE /* Tree.h */,
+ 263769AA12F7EBE2000F45FE /* Tree.m */,
+ );
+ name = "Tree View";
+ sourceTree = "<group>";
+ };
+ 26376A8C12F7FF5A000F45FE /* Bookmarks */ = {
+ isa = PBXGroup;
+ children = (
+ 26376A8A12F7FF57000F45FE /* BookmarksController.h */,
+ 26376A8B12F7FF57000F45FE /* BookmarksController.m */,
+ );
+ name = Bookmarks;
+ sourceTree = "<group>";
+ };
+ 265F303F12D6637E0048B600 /* Cocoa Frontend */ = {
+ isa = PBXGroup;
+ children = (
+ 26BA25AB1321653200AEC1DA /* apple_image.m */,
+ 26BA25AC1321653200AEC1DA /* apple_image.h */,
+ 26CDD26512E74402004FC66B /* Browser */,
+ 26CDD26712E74453004FC66B /* Download */,
+ 26CDD26612E7441E004FC66B /* Views */,
+ 263769A812F7EBC3000F45FE /* Tree View */,
+ 26376A8C12F7FF5A000F45FE /* Bookmarks */,
+ 26CDD26812E74461004FC66B /* NSApplication */,
+ 26CDD26912E7446E004FC66B /* Platform Interface */,
+ 265F310F12D663C20048B600 /* Resources */,
+ 26CDCEB212E702D8004FC66B /* PSMTabBarControl */,
+ 265F30AB12D6637E0048B600 /* Prefix.pch */,
+ 2625095112F72AA70090D236 /* PreferencesWindowController.h */,
+ 2625095212F72AA70090D236 /* PreferencesWindowController.m */,
+ 26376A4312F7FA86000F45FE /* HistoryWindowController.h */,
+ 26376A4412F7FA86000F45FE /* HistoryWindowController.m */,
+ );
+ name = "Cocoa Frontend";
+ sourceTree = "<group>";
+ };
+ 265F310F12D663C20048B600 /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 261DB28F13185C0A00C59F12 /* BookmarksWindow.xib.strings */,
+ 261DB29313185C0A00C59F12 /* BrowserWindow.xib.strings */,
+ 261DB29513185C0A00C59F12 /* DownloadWindow.xib.strings */,
+ 261DB29713185C0A00C59F12 /* HistoryWindow.xib.strings */,
+ 261DB29B13185C0A00C59F12 /* MainMenu.xib.strings */,
+ 261DB29D13185C0A00C59F12 /* PreferencesWindow.xib.strings */,
+ 261DB29F13185C0A00C59F12 /* SearchWindow.xib.strings */,
+ 261DB22113180AFF00C59F12 /* Localizable.strings */,
+ 2684028E1301848100850DA2 /* HomeTemplate.pdf */,
+ 26AFE63E12DDEB0A005AD082 /* NetSurf.icns */,
+ 2612268F12D7AE4100E10F91 /* Messages */,
+ 2612265D12D7AD6800E10F91 /* adblock.css */,
+ 2612265712D7ACB500E10F91 /* default.css */,
+ 2612265912D7ACB500E10F91 /* quirks.css */,
+ 265F30A712D6637E0048B600 /* NetSurf-Info.plist */,
+ 26121DA812D700B800E10F91 /* MainMenu.xib */,
+ 26121EAB12D70E0A00E10F91 /* Browser.xib */,
+ 26AFEAF012E042F9005AD082 /* DownloadWindow.xib */,
+ 26CDCFF212E70AD1004FC66B /* BrowserWindow.xib */,
+ 2666DC5B12F6D1770045E8B6 /* SearchWindow.xib */,
+ 2625095012F72A8F0090D236 /* PreferencesWindow.xib */,
+ 26376A4112F7FA67000F45FE /* HistoryWindow.xib */,
+ 26376BAC12F820D7000F45FE /* BookmarksWindow.xib */,
+ 26B4E926130D36A90003B527 /* LocalHistoryPanel.xib */,
+ );
+ name = Resources;
+ path = res;
+ sourceTree = "<group>";
+ };
+ 265F31C912D66A890048B600 /* image */ = {
+ isa = PBXGroup;
+ children = (
+ 265F31CA12D66A890048B600 /* bitmap.h */,
+ 265F31CB12D66A890048B600 /* bmp.c */,
+ 265F31CC12D66A890048B600 /* bmp.h */,
+ 265F31CD12D66A890048B600 /* gif.c */,
+ 265F31CE12D66A890048B600 /* gif.h */,
+ 265F31CF12D66A890048B600 /* ico.c */,
+ 265F31D012D66A890048B600 /* ico.h */,
+ 265F31D112D66A890048B600 /* jpeg.c */,
+ 265F31D212D66A890048B600 /* jpeg.h */,
+ 265F31D312D66A890048B600 /* mng.c */,
+ 265F31D412D66A890048B600 /* mng.h */,
+ 265F31D512D66A890048B600 /* nssprite.c */,
+ 265F31D612D66A890048B600 /* nssprite.h */,
+ 265F31D712D66A890048B600 /* png.c */,
+ 265F31D812D66A890048B600 /* png.h */,
+ 265F31D912D66A890048B600 /* rsvg.c */,
+ 265F31DA12D66A890048B600 /* rsvg.h */,
+ 265F31DB12D66A890048B600 /* svg.c */,
+ 265F31DC12D66A890048B600 /* svg.h */,
+ 265F31DD12D66A890048B600 /* webp.c */,
+ 265F31DE12D66A890048B600 /* webp.h */,
+ );
+ name = image;
+ path = ../image;
+ sourceTree = SOURCE_ROOT;
+ };
+ 26CDCEB212E702D8004FC66B /* PSMTabBarControl */ = {
+ isa = PBXGroup;
+ children = (
+ 26CDCFDB12E706FE004FC66B /* Images */,
+ 26CDCEB312E702D8004FC66B /* NSBezierPath_AMShading.h */,
+ 26CDCEB412E702D8004FC66B /* NSBezierPath_AMShading.m */,
+ 26CDCEB512E702D8004FC66B /* NSString_AITruncation.h */,
+ 26CDCEB612E702D8004FC66B /* NSString_AITruncation.m */,
+ 26CDCEBD12E702D8004FC66B /* PSMOverflowPopUpButton.h */,
+ 26CDCEBE12E702D8004FC66B /* PSMOverflowPopUpButton.m */,
+ 26CDCEBF12E702D8004FC66B /* PSMProgressIndicator.h */,
+ 26CDCEC012E702D8004FC66B /* PSMProgressIndicator.m */,
+ 26CDCEC112E702D8004FC66B /* PSMRolloverButton.h */,
+ 26CDCEC212E702D8004FC66B /* PSMRolloverButton.m */,
+ 26CDCEC312E702D8004FC66B /* PSMTabBarCell.h */,
+ 26CDCEC412E702D8004FC66B /* PSMTabBarCell.m */,
+ 26CDCEC512E702D8004FC66B /* PSMTabBarControl.h */,
+ 26CDCEC612E702D8004FC66B /* PSMTabBarControl.m */,
+ 26CDCEC712E702D8004FC66B /* PSMTabBarController.h */,
+ 26CDCEC812E702D8004FC66B /* PSMTabBarController.m */,
+ 26CDCEC912E702D8004FC66B /* PSMTabDragAssistant.h */,
+ 26CDCECA12E702D8004FC66B /* PSMTabDragAssistant.m */,
+ 26CDCECB12E702D8004FC66B /* PSMTabDragView.h */,
+ 26CDCECC12E702D8004FC66B /* PSMTabDragView.m */,
+ 26CDCECD12E702D8004FC66B /* PSMTabDragWindow.h */,
+ 26CDCECE12E702D8004FC66B /* PSMTabDragWindow.m */,
+ 26CDCECF12E702D8004FC66B /* PSMTabDragWindowController.h */,
+ 26CDCED012E702D8004FC66B /* PSMTabDragWindowController.m */,
+ 26CDCED112E702D8004FC66B /* PSMTabStyle.h */,
+ 26CDCED212E702D8004FC66B /* PSMUnifiedTabStyle.h */,
+ 26CDCED312E702D8004FC66B /* PSMUnifiedTabStyle.m */,
+ );
+ path = PSMTabBarControl;
+ sourceTree = "<group>";
+ };
+ 26CDCFDB12E706FE004FC66B /* Images */ = {
+ isa = PBXGroup;
+ children = (
+ 26ABD61C12F02EB900407161 /* overflowImage.png */,
+ 26ABD61D12F02EB900407161 /* overflowImagePressed.png */,
+ 26ABD61E12F02EB900407161 /* pi.png */,
+ 26CDCFDC12E706FE004FC66B /* AquaTabClose_Front.png */,
+ 26CDCFDD12E706FF004FC66B /* AquaTabClose_Front_Pressed.png */,
+ 26CDCFDE12E706FF004FC66B /* AquaTabClose_Front_Rollover.png */,
+ 26CDCFDF12E706FF004FC66B /* AquaTabCloseDirty_Front.png */,
+ 26CDCFE012E706FF004FC66B /* AquaTabCloseDirty_Front_Pressed.png */,
+ 26CDCFE112E706FF004FC66B /* AquaTabCloseDirty_Front_Rollover.png */,
+ 26CDCFE212E706FF004FC66B /* AquaTabNew.png */,
+ 26CDCFE312E706FF004FC66B /* AquaTabNewPressed.png */,
+ 26CDCFE412E706FF004FC66B /* AquaTabNewRollover.png */,
+ );
+ path = Images;
+ sourceTree = "<group>";
+ };
+ 26CDD23E12E743A3004FC66B /* NetSurf */ = {
+ isa = PBXGroup;
+ children = (
+ 260F1F6212D620E800D9B07F /* content */,
+ 260F1F7812D620E800D9B07F /* css */,
+ 260F1F8312D620E800D9B07F /* desktop */,
+ 260F1FC612D620E800D9B07F /* render */,
+ 265F31C912D66A890048B600 /* image */,
+ 260F1FE212D620E800D9B07F /* utils */,
+ );
+ name = NetSurf;
+ sourceTree = "<group>";
+ };
+ 26CDD26512E74402004FC66B /* Browser */ = {
+ isa = PBXGroup;
+ children = (
+ 26121EFB12D7132100E10F91 /* BrowserView.h */,
+ 26121EFC12D7132100E10F91 /* BrowserView.m */,
+ 26CDD00112E70F56004FC66B /* BrowserWindowController.h */,
+ 26CDD00212E70F56004FC66B /* BrowserWindowController.m */,
+ 26CDD0F412E726E0004FC66B /* BrowserViewController.h */,
+ 26CDD0F512E726E0004FC66B /* BrowserViewController.m */,
+ 2666DC5D12F6D2E70045E8B6 /* SearchWindowController.h */,
+ 2666DC5E12F6D2E70045E8B6 /* SearchWindowController.m */,
+ 2666DD5012F706BC0045E8B6 /* BrowserWindow.h */,
+ 2666DD5112F706BC0045E8B6 /* BrowserWindow.m */,
+ 26B4E928130D37E50003B527 /* LocalHistoryController.h */,
+ 26B4E929130D37E50003B527 /* LocalHistoryController.m */,
+ );
+ name = Browser;
+ sourceTree = "<group>";
+ };
+ 26CDD26612E7441E004FC66B /* Views */ = {
+ isa = PBXGroup;
+ children = (
+ 26B4E91A130D351F0003B527 /* ArrowBox.h */,
+ 26B4E91B130D351F0003B527 /* ArrowBox.m */,
+ 26B4E91C130D351F0003B527 /* ArrowWindow.h */,
+ 26B4E91D130D351F0003B527 /* ArrowWindow.m */,
+ 26B4E91E130D351F0003B527 /* BlackScroller.h */,
+ 26B4E91F130D351F0003B527 /* BlackScroller.m */,
+ 26AFE8E212DF4200005AD082 /* ScrollableView.h */,
+ 26AFE8E312DF4200005AD082 /* ScrollableView.m */,
+ 26EC3B6812ED62C0000A960C /* URLFieldCell.h */,
+ 26EC3B6912ED62C0000A960C /* URLFieldCell.m */,
+ 26EC3C4212ED8202000A960C /* HistoryView.h */,
+ 26EC3C4312ED8202000A960C /* HistoryView.m */,
+ 26EC3F1612EF0CBD000A960C /* FormSelectMenu.h */,
+ 26EC3F1712EF0CBD000A960C /* FormSelectMenu.m */,
+ );
+ name = Views;
+ sourceTree = "<group>";
+ };
+ 26CDD26712E74453004FC66B /* Download */ = {
+ isa = PBXGroup;
+ children = (
+ 26AFEAE912E04253005AD082 /* DownloadWindowController.h */,
+ 26AFEAEA12E04253005AD082 /* DownloadWindowController.m */,
+ );
+ name = Download;
+ sourceTree = "<group>";
+ };
+ 26CDD26812E74461004FC66B /* NSApplication */ = {
+ isa = PBXGroup;
+ children = (
+ 263629C812F69B120048542C /* NetsurfApp.h */,
+ 263629C912F69B120048542C /* NetsurfApp.m */,
+ 26AFE97A12DF514C005AD082 /* NetSurfAppDelegate.h */,
+ 26AFE97B12DF514C005AD082 /* NetSurfAppDelegate.m */,
+ );
+ name = NSApplication;
+ sourceTree = "<group>";
+ };
+ 26CDD26912E7446E004FC66B /* Platform Interface */ = {
+ isa = PBXGroup;
+ children = (
+ 263629CA12F69B120048542C /* system_colour.m */,
+ 264C344112F0987E00D11246 /* gui.h */,
+ 265F311912D663F50048B600 /* gui.m */,
+ 261223CB12D7805300E10F91 /* plotter.h */,
+ 265F314712D666660048B600 /* plotter.m */,
+ 265F316112D667E10048B600 /* schedule.m */,
+ 265F316612D668130048B600 /* thumbnail.m */,
+ 265F316F12D668790048B600 /* fetch.m */,
+ 265F319012D668DB0048B600 /* url.m */,
+ 265F31C412D66A0D0048B600 /* save.m */,
+ 260FC03112D85ACE00079C00 /* bitmap.h */,
+ 265F31EB12D66B190048B600 /* bitmap.m */,
+ 265F320512D66C200048B600 /* utf8.m */,
+ 265F321312D66CD90048B600 /* utils.m */,
+ 261223B712D77F9C00E10F91 /* font.h */,
+ 265F321E12D66D510048B600 /* font.m */,
+ 26AFED0312E09916005AD082 /* selection.m */,
+ 2639E20512F2ADEE00699678 /* coordinates.h */,
+ );
+ name = "Platform Interface";
+ sourceTree = "<group>";
+ };
+ 29B97314FDCFA39411CA2CEA /* Untitled */ = {
+ isa = PBXGroup;
+ children = (
+ 265F303F12D6637E0048B600 /* Cocoa Frontend */,
+ 26CDD23E12E743A3004FC66B /* NetSurf */,
+ 263629B212F69A080048542C /* Makefiles */,
+ 261DB24E1318443500C59F12 /* Tools */,
+ 19C28FACFE9D520D11CA2CBB /* Products */,
+ );
+ name = Untitled;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXLegacyTarget section */
+ 2636298F12F698E00048542C /* NetSurf */ = {
+ isa = PBXLegacyTarget;
+ buildArgumentsString = "$(ACTION) MKDIR=\"mkdir -p\"";
+ buildConfigurationList = 2636299512F699250048542C /* Build configuration list for PBXLegacyTarget "NetSurf" */;
+ buildPhases = (
+ );
+ buildToolPath = /usr/bin/make;
+ buildWorkingDirectory = "$(SRCROOT)/..";
+ dependencies = (
+ );
+ name = NetSurf;
+ passBuildSettingsInEnvironment = 1;
+ productName = NetSurf;
+ };
+/* End PBXLegacyTarget section */
+
+/* Begin PBXProject section */
+ 29B97313FDCFA39411CA2CEA /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = YES;
+ };
+ buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "NetSurf" */;
+ compatibilityVersion = "Xcode 3.1";
+ developmentRegion = English;
+ hasScannedForEncodings = 1;
+ knownRegions = (
+ English,
+ Japanese,
+ French,
+ German,
+ de,
+ en,
+ fr,
+ it,
+ nl,
+ );
+ mainGroup = 29B97314FDCFA39411CA2CEA /* Untitled */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 2636298F12F698E00048542C /* NetSurf */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXVariantGroup section */
+ 2612268F12D7AE4100E10F91 /* Messages */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 2612269012D7AE4100E10F91 /* de */,
+ 2612269312D7AE9B00E10F91 /* nl */,
+ 2612269412D7AEA800E10F91 /* en */,
+ 2612269512D7AEB500E10F91 /* it */,
+ 2612269612D7AEBE00E10F91 /* fr */,
+ );
+ name = Messages;
+ sourceTree = "<group>";
+ };
+ 261DB22113180AFF00C59F12 /* Localizable.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 261DB22213180AFF00C59F12 /* en */,
+ 261DB22613180B4F00C59F12 /* de */,
+ 261DB23313180CD600C59F12 /* fr */,
+ 261DB23413180CE000C59F12 /* it */,
+ 261DB23513180CEE00C59F12 /* nl */,
+ );
+ name = Localizable.strings;
+ sourceTree = "<group>";
+ };
+ 261DB28F13185C0A00C59F12 /* BookmarksWindow.xib.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 261DB29013185C0A00C59F12 /* de */,
+ );
+ name = BookmarksWindow.xib.strings;
+ sourceTree = "<group>";
+ };
+ 261DB29313185C0A00C59F12 /* BrowserWindow.xib.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 261DB29413185C0A00C59F12 /* de */,
+ );
+ name = BrowserWindow.xib.strings;
+ sourceTree = "<group>";
+ };
+ 261DB29513185C0A00C59F12 /* DownloadWindow.xib.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 261DB29613185C0A00C59F12 /* de */,
+ );
+ name = DownloadWindow.xib.strings;
+ sourceTree = "<group>";
+ };
+ 261DB29713185C0A00C59F12 /* HistoryWindow.xib.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 261DB29813185C0A00C59F12 /* de */,
+ );
+ name = HistoryWindow.xib.strings;
+ sourceTree = "<group>";
+ };
+ 261DB29B13185C0A00C59F12 /* MainMenu.xib.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 261DB29C13185C0A00C59F12 /* de */,
+ );
+ name = MainMenu.xib.strings;
+ sourceTree = "<group>";
+ };
+ 261DB29D13185C0A00C59F12 /* PreferencesWindow.xib.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 261DB29E13185C0A00C59F12 /* de */,
+ );
+ name = PreferencesWindow.xib.strings;
+ sourceTree = "<group>";
+ };
+ 261DB29F13185C0A00C59F12 /* SearchWindow.xib.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 261DB2A013185C0A00C59F12 /* de */,
+ );
+ name = SearchWindow.xib.strings;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 2636299012F698E10048542C /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = NetSurf;
+ };
+ name = Debug;
+ };
+ 2636299112F698E10048542C /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ MKDIR = "mkdir -p";
+ PRODUCT_NAME = NetSurf;
+ };
+ name = Release;
+ };
+ C01FCF4F08A954540054247B /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx10.5;
+ };
+ name = Debug;
+ };
+ C01FCF5008A954540054247B /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+ ONLY_ACTIVE_ARCH = NO;
+ SDKROOT = macosx10.5;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 2636299512F699250048542C /* Build configuration list for PBXLegacyTarget "NetSurf" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 2636299012F698E10048542C /* Debug */,
+ 2636299112F698E10048542C /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ C01FCF4E08A954540054247B /* Build configuration list for PBXProject "NetSurf" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ C01FCF4F08A954540054247B /* Debug */,
+ C01FCF5008A954540054247B /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
+}
diff --git a/frontends/cocoa/NetSurfAppDelegate.h b/frontends/cocoa/NetSurfAppDelegate.h
new file mode 100644
index 000000000..a22c4ceee
--- /dev/null
+++ b/frontends/cocoa/NetSurfAppDelegate.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@class SearchWindowController;
+@class PreferencesWindowController;
+@class HistoryWindowController;
+
+@interface NetSurfAppDelegate : NSObject {
+ SearchWindowController *search;
+ PreferencesWindowController *preferences;
+ HistoryWindowController *history;
+}
+
+@property (readwrite, retain, nonatomic) SearchWindowController *search;
+@property (readwrite, retain, nonatomic) PreferencesWindowController *preferences;
+@property (readwrite, retain, nonatomic) HistoryWindowController *history;
+
+- (IBAction) showSearchWindow: (id) sender;
+- (IBAction) searchForward: (id) sender;
+- (IBAction) searchBackward: (id) sender;
+
+- (IBAction) showPreferences: (id) sender;
+- (IBAction) showGlobalHistory: (id) sender;
+
+@end
diff --git a/frontends/cocoa/NetSurfAppDelegate.m b/frontends/cocoa/NetSurfAppDelegate.m
new file mode 100644
index 000000000..f42856380
--- /dev/null
+++ b/frontends/cocoa/NetSurfAppDelegate.m
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import "utils/nsoption.h"
+#import "utils/messages.h"
+#import "utils/utils.h"
+#import "utils/nsurl.h"
+#import "desktop/browser.h"
+
+#import "cocoa/gui.h"
+#import "cocoa/NetSurfAppDelegate.h"
+#import "cocoa/SearchWindowController.h"
+#import "cocoa/PreferencesWindowController.h"
+#import "cocoa/HistoryWindowController.h"
+
+
+@interface NetSurfAppDelegate ()
+
+- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent;
+
+@end
+
+
+@implementation NetSurfAppDelegate
+
+@synthesize history;
+@synthesize search;
+@synthesize preferences;
+
+- (void) newDocument: (id) sender;
+{
+ nsurl *url;
+ nserror error;
+
+ if (nsoption_charp(homepage_url) != NULL) {
+ error = nsurl_create(nsoption_charp(homepage_url), &url);
+ } else {
+ error = nsurl_create(NETSURF_HOMEPAGE, &url);
+ }
+
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ }
+}
+
+- (void) openDocument: (id) sender;
+{
+ nsurl *u;
+ nserror error;
+
+ NSOpenPanel *openPanel = [NSOpenPanel openPanel];
+ [openPanel setAllowsMultipleSelection: YES];
+ if ([openPanel runModalForTypes: nil] == NSOKButton) {
+ for (NSURL *url in [openPanel URLs]) {
+ error = nsurl_create([[url absoluteString] UTF8String], &u);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ u,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(u);
+ }
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ }
+ }
+ }
+}
+
+- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
+{
+ nsurl *url;
+ nserror error;
+ NSString *urlAsString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
+
+ error = nsurl_create([urlAsString UTF8String], &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ }
+}
+
+- (IBAction) showSearchWindow: (id) sender;
+{
+ if (search == nil) {
+ [self setSearch: [[[SearchWindowController alloc] init] autorelease]];
+ }
+ [[search window] makeKeyAndOrderFront: self];
+}
+
+- (IBAction) searchForward: (id) sender;
+{
+ [search search: SearchForward];
+}
+
+- (IBAction) searchBackward: (id) sender;
+{
+ [search search: SearchBackward];
+}
+
+- (BOOL) validateMenuItem: (id) item;
+{
+ SEL action = [item action];
+
+ if (action == @selector( searchForward: )) {
+ return [search canGoForward];
+ } else if (action == @selector( searchBackward: )) {
+ return [search canGoBack];
+ }
+
+ return YES;
+}
+
+- (IBAction) showPreferences: (id) sender;
+{
+ if (preferences == nil) {
+ [self setPreferences: [[[PreferencesWindowController alloc] init] autorelease]];
+ }
+ [preferences showWindow: sender];
+}
+
+- (IBAction) showGlobalHistory: (id) sender;
+{
+ if (history == nil) {
+ [self setHistory: [[[HistoryWindowController alloc] init] autorelease]];
+ }
+ [history showWindow: sender];
+}
+
+// Application delegate methods
+
+- (BOOL) applicationOpenUntitledFile: (NSApplication *)sender;
+{
+ [self newDocument: self];
+ return YES;
+}
+
+-(void)applicationWillFinishLaunching:(NSNotification *)aNotification
+{
+ NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager];
+ [appleEventManager setEventHandler:self
+ andSelector:@selector(handleGetURLEvent:withReplyEvent:)
+ forEventClass:kInternetEventClass andEventID:kAEGetURL];
+}
+
+- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
+{
+ nsurl *url;
+ nserror error;
+ NSURL *urltxt = [NSURL fileURLWithPath: filename];
+
+ error = nsurl_create([[urltxt absoluteString] UTF8String], &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ }
+
+ return YES;
+}
+
+
+@end
diff --git a/frontends/cocoa/NetsurfApp.h b/frontends/cocoa/NetsurfApp.h
new file mode 100644
index 000000000..330f33826
--- /dev/null
+++ b/frontends/cocoa/NetsurfApp.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@class BrowserViewController;
+
+@interface NetSurfApp : NSApplication {
+ BrowserViewController *frontTab;
+}
+
+@property (readwrite, assign, nonatomic) BrowserViewController *frontTab;
+
+@end
+
+NSString *cocoa_get_user_path( NSString *fileName );
diff --git a/frontends/cocoa/NetsurfApp.m b/frontends/cocoa/NetsurfApp.m
new file mode 100644
index 000000000..6fb856098
--- /dev/null
+++ b/frontends/cocoa/NetsurfApp.m
@@ -0,0 +1,302 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import "cocoa/apple_image.h"
+#import "cocoa/NetsurfApp.h"
+#import "cocoa/gui.h"
+#import "cocoa/plotter.h"
+#import "cocoa/DownloadWindowController.h"
+#import "cocoa/SearchWindowController.h"
+#import "cocoa/selection.h"
+#import "cocoa/fetch.h"
+#import "cocoa/bitmap.h"
+#import "cocoa/font.h"
+
+#import "utils/filename.h"
+#import "utils/log.h"
+#import "utils/messages.h"
+#import "utils/utils.h"
+#import "content/urldb.h"
+#import "utils/nsoption.h"
+#import "desktop/plotters.h"
+#import "desktop/save_complete.h"
+#import "desktop/textinput.h"
+#import "desktop/tree.h"
+#import "render/html.h"
+#import "desktop/mouse.h"
+#import "desktop/netsurf.h"
+
+#ifndef NETSURF_HOMEPAGE
+#define NETSURF_HOMEPAGE "http://www.netsurf-browser.org/welcome/"
+#endif
+
+@implementation NetSurfApp
+
+@synthesize frontTab;
+
+static bool cocoa_done = false;
+
+/**
+ * Cause an abnormal program termination.
+ *
+ * \note This never returns and is intended to terminate without any cleanup.
+ *
+ * \param error The message to display to the user.
+ */
+static void die(const char * const error)
+{
+ [NSException raise: @"NetsurfDie" format: @"Error: %s", error];
+}
+
+- (void) loadOptions
+{
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ [defaults registerDefaults: [NSDictionary dictionaryWithObjectsAndKeys:
+ cocoa_get_user_path( @"Cookies" ),
+ kCookiesFileOption,
+ cocoa_get_user_path( @"URLs" ),
+ kURLsFileOption,
+ [NSString stringWithUTF8String: NETSURF_HOMEPAGE],
+ kHomepageURLOption,
+ nil]];
+
+
+ nsoption_setnull_charp(cookie_file, strdup( [[defaults objectForKey: kCookiesFileOption] UTF8String] ));
+
+ nsoption_setnull_charp(cookie_jar, strdup( nsoption_charp(cookie_file) ));
+
+ nsoption_setnull_charp(homepage_url, strdup( [[defaults objectForKey: kHomepageURLOption] UTF8String] ));
+
+ urldb_load( [[defaults objectForKey: kURLsFileOption] UTF8String] );
+ urldb_load_cookies( nsoption_charp(cookie_file) );
+
+ cocoa_update_scale_factor();
+ LOG("done setup");
+}
+
+- (void) saveOptions
+{
+ urldb_save_cookies( nsoption_charp(cookie_file) );
+ urldb_save( [[[NSUserDefaults standardUserDefaults] objectForKey: kURLsFileOption] UTF8String] );
+}
+
+- (void) run
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ [self finishLaunching];
+
+ [self loadOptions];
+
+ while (!cocoa_done) {
+ [pool release];
+ pool = [[NSAutoreleasePool alloc] init];
+
+ NSEvent *event =
+ [self nextEventMatchingMask: NSAnyEventMask
+ untilDate: [NSDate distantFuture]
+ inMode: NSDefaultRunLoopMode
+ dequeue: YES];
+ if (nil != event) {
+ [self sendEvent: event];
+ [self updateWindows];
+ }
+
+ }
+
+ [self saveOptions];
+
+ [pool release];
+}
+
+-(void) terminate: (id)sender
+{
+ [[NSNotificationCenter defaultCenter] postNotificationName:NSApplicationWillTerminateNotification object:self];
+
+ cocoa_done = true;
+ [self postEvent: [NSEvent otherEventWithType: NSApplicationDefined
+ location: NSZeroPoint
+ modifierFlags: 0
+ timestamp: 0
+ windowNumber: 0
+ context: NULL
+ subtype: 0
+ data1: 0
+ data2: 0]
+ atStart: YES];
+}
+
+@end
+
+#pragma mark -
+
+static NSString *cocoa_get_preferences_path( void )
+{
+ NSArray *paths = NSSearchPathForDirectoriesInDomains( NSApplicationSupportDirectory, NSUserDomainMask, YES );
+ NSCAssert( [paths count] >= 1, @"Where is the application support directory?" );
+
+ NSString *netsurfPath = [[paths objectAtIndex: 0] stringByAppendingPathComponent: @"NetSurf"];
+
+ NSFileManager *fm = [NSFileManager defaultManager];
+ BOOL isDirectory = NO;
+ BOOL exists = [fm fileExistsAtPath: netsurfPath isDirectory: &isDirectory];
+
+ if (!exists) {
+ exists = [fm createDirectoryAtPath: netsurfPath withIntermediateDirectories: YES attributes: nil error: NULL];
+ isDirectory = YES;
+ }
+ if (!(exists && isDirectory)) {
+ die( "Cannot create netsurf preferences directory" );
+ }
+
+ return netsurfPath;
+}
+
+NSString *cocoa_get_user_path( NSString *fileName )
+{
+ return [cocoa_get_preferences_path() stringByAppendingPathComponent: fileName];
+}
+
+static const char *cocoa_get_options_file( void )
+{
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ [defaults registerDefaults: [NSDictionary dictionaryWithObjectsAndKeys:
+ cocoa_get_user_path( @"Options" ), kOptionsFileOption,
+ nil]];
+
+ return [[defaults objectForKey: kOptionsFileOption] UTF8String];
+}
+
+static NSApplication *cocoa_prepare_app( void )
+{
+ /* if application instance has already been created return it */
+ if (NSApp != nil) {
+ return NSApp;
+ }
+
+ NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
+
+ /* Obtain principle class of bundle which must implement sharedApplication API */
+ Class principalClass = NSClassFromString([infoDictionary objectForKey:@"NSPrincipalClass"]);
+ NSCAssert([principalClass respondsToSelector:@selector(sharedApplication)],
+ @"Principal class must implement sharedApplication.");
+
+ /* create application instance */
+ [principalClass sharedApplication];
+
+ /* load interface nib */
+ NSString *mainNibName = [infoDictionary objectForKey:@"NSMainNibFile"];
+ NSNib *mainNib = [[NSNib alloc] initWithNibNamed:mainNibName bundle:[NSBundle mainBundle]];
+ [mainNib instantiateNibWithOwner:NSApp topLevelObjects:nil];
+ [mainNib release];
+
+ return NSApp;
+}
+
+/**
+ * Set option defaults for cocoa frontend
+ *
+ * @param defaults The option table to update.
+ * @return error status.
+ */
+static nserror set_defaults(struct nsoption_s *defaults)
+{
+ /* Set defaults for absent option strings */
+ const char * const ca_bundle = [[[NSBundle mainBundle] pathForResource: @"ca-bundle" ofType: @""] UTF8String];
+
+ nsoption_setnull_charp(ca_bundle, strdup(ca_bundle));
+
+ return NSERROR_OK;
+}
+
+int main( int argc, char **argv )
+{
+ nsurl *url;
+ nserror error;
+ struct netsurf_table cocoa_table = {
+ .misc = cocoa_misc_table,
+ .window = cocoa_window_table,
+ .clipboard = cocoa_clipboard_table,
+ .download = cocoa_download_table,
+ .fetch = cocoa_fetch_table,
+ .search = cocoa_search_table,
+ .bitmap = cocoa_bitmap_table,
+ .layout = cocoa_layout_table,
+ };
+
+ error = netsurf_register(&cocoa_table);
+ if (error != NSERROR_OK) {
+ die("NetSurf operation table failed registration");
+ }
+
+ const char * const messages = [[[NSBundle mainBundle] pathForResource: @"Messages" ofType: @""] UTF8String];
+ const char * const options = cocoa_get_options_file();
+
+ /* initialise logging. Not fatal if it fails but not much we
+ * can do about it either.
+ */
+ nslog_init(NULL, &argc, argv);
+
+ /* user options setup */
+ error = nsoption_init(set_defaults, &nsoptions, &nsoptions_default);
+ if (error != NSERROR_OK) {
+ die("Options failed to initialise");
+ }
+ nsoption_read(options, NULL);
+ nsoption_commandline(&argc, argv, NULL);
+
+ error = messages_add_from_file(messages);
+
+ /* common initialisation */
+ error = netsurf_init(NULL);
+ if (error != NSERROR_OK) {
+ die("NetSurf failed to initialise");
+ }
+
+ /* Initialise filename allocator */
+ filename_initialise();
+
+ (void)apple_image_init();
+
+ NSApplication *app = cocoa_prepare_app();
+
+ for (int i = 1; i < argc; i++) {
+ /* skip -psn_* and other possible options */
+ if (argv[i][0] == '-')
+ continue;
+
+ error = nsurl_create(argv[i], &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ }
+ }
+
+ [app run];
+
+ netsurf_exit();
+
+ return 0;
+}
diff --git a/frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front.png b/frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front.png
new file mode 100644
index 000000000..77d220505
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front_Pressed.png b/frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front_Pressed.png
new file mode 100644
index 000000000..197ea95c2
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front_Pressed.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front_Rollover.png b/frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front_Rollover.png
new file mode 100644
index 000000000..2dfe5777e
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front_Rollover.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front.png b/frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front.png
new file mode 100644
index 000000000..02b72d39e
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front_Pressed.png b/frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front_Pressed.png
new file mode 100644
index 000000000..f81125a01
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front_Pressed.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front_Rollover.png b/frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front_Rollover.png
new file mode 100644
index 000000000..4f6b865f7
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front_Rollover.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/Images/AquaTabNew.png b/frontends/cocoa/PSMTabBarControl/Images/AquaTabNew.png
new file mode 100644
index 000000000..10a83705d
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/AquaTabNew.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/Images/AquaTabNewPressed.png b/frontends/cocoa/PSMTabBarControl/Images/AquaTabNewPressed.png
new file mode 100644
index 000000000..cb4dd10f2
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/AquaTabNewPressed.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/Images/AquaTabNewRollover.png b/frontends/cocoa/PSMTabBarControl/Images/AquaTabNewRollover.png
new file mode 100644
index 000000000..4d469f8a6
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/AquaTabNewRollover.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/Images/overflowImage.png b/frontends/cocoa/PSMTabBarControl/Images/overflowImage.png
new file mode 100644
index 000000000..2b762555d
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/overflowImage.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/Images/overflowImagePressed.png b/frontends/cocoa/PSMTabBarControl/Images/overflowImagePressed.png
new file mode 100644
index 000000000..b3918b344
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/overflowImagePressed.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/Images/pi.png b/frontends/cocoa/PSMTabBarControl/Images/pi.png
new file mode 100644
index 000000000..4d598dc73
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/pi.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/NSBezierPath_AMShading.h b/frontends/cocoa/PSMTabBarControl/NSBezierPath_AMShading.h
new file mode 100644
index 000000000..fd2c2dde4
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/NSBezierPath_AMShading.h
@@ -0,0 +1,23 @@
+//
+// NSBezierPath_AMShading.h
+// ------------------------
+//
+// Created by Andreas on 2005-06-01.
+// Copyright 2005 Andreas Mayer. All rights reserved.
+//
+// based on http://www.cocoadev.com/index.pl?GradientFill
+
+
+#import <Cocoa/Cocoa.h>
+
+@interface NSBezierPath (AMShading)
+
+- (void)customHorizontalFillWithCallbacks:(CGFunctionCallbacks) functionCallbacks firstColor:(NSColor *)firstColor secondColor:(NSColor *)secondColor;
+- (void)customVerticalFillWithCallbacks:(CGFunctionCallbacks) functionCallbacks firstColor:(NSColor *)firstColor secondColor:(NSColor *)secondColor;
+
+- (void)linearGradientFillWithStartColor:(NSColor *)startColor endColor:(NSColor *)endColor;
+- (void)linearVerticalGradientFillWithStartColor:(NSColor *)startColor endColor:(NSColor *)endColor;
+
+- (void)bilinearGradientFillWithOuterColor:(NSColor *)outerColor innerColor:(NSColor *)innerColor;
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/NSBezierPath_AMShading.m b/frontends/cocoa/PSMTabBarControl/NSBezierPath_AMShading.m
new file mode 100644
index 000000000..30ee24096
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/NSBezierPath_AMShading.m
@@ -0,0 +1,119 @@
+//
+// NSBezierPath_AMShading.m
+// ------------------------
+//
+// Created by Andreas on 2005-06-01.
+// Copyright 2005 Andreas Mayer. All rights reserved.
+//
+
+#import "NSBezierPath_AMShading.h"
+
+
+@implementation NSBezierPath (AMShading)
+
+static void linearShadedColor(void *info, const CGFloat *in, CGFloat *out){
+ CGFloat *colors = (CGFloat *)info;
+ *out++ = colors[0] + *in * colors[8];
+ *out++ = colors[1] + *in * colors[9];
+ *out++ = colors[2] + *in * colors[10];
+ *out++ = colors[3] + *in * colors[11];
+}
+
+static void bilinearShadedColor(void *info, const CGFloat *in, CGFloat *out){
+ CGFloat *colors = (CGFloat *)info;
+ CGFloat factor = (*in) * 2.0;
+ if(*in > 0.5) {
+ factor = 2 - factor;
+ }
+ *out++ = colors[0] + factor * colors[8];
+ *out++ = colors[1] + factor * colors[9];
+ *out++ = colors[2] + factor * colors[10];
+ *out++ = colors[3] + factor * colors[11];
+}
+
+- (void)linearGradientFillWithStartColor:(NSColor *)startColor endColor:(NSColor *)endColor {
+ static const CGFunctionCallbacks callbacks = {0, &linearShadedColor, NULL};
+
+ [self customHorizontalFillWithCallbacks:callbacks firstColor:startColor secondColor:endColor];
+}
+
+- (void)linearVerticalGradientFillWithStartColor:(NSColor *)startColor endColor:(NSColor *)endColor {
+ static const CGFunctionCallbacks callbacks = {0, &linearShadedColor, NULL};
+
+ [self customVerticalFillWithCallbacks:callbacks firstColor:startColor secondColor:endColor];
+}
+
+- (void)bilinearGradientFillWithOuterColor:(NSColor *)outerColor innerColor:(NSColor *)innerColor {
+ static const CGFunctionCallbacks callbacks = {0, &bilinearShadedColor, NULL};
+
+ [self customHorizontalFillWithCallbacks:callbacks firstColor:innerColor secondColor:outerColor];
+}
+
+- (void)customFillWithCallbacks:(CGFunctionCallbacks)functionCallbacks firstColor:(NSColor *)firstColor secondColor:(NSColor *)secondColor startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint {
+ CGColorSpaceRef colorspace;
+ CGShadingRef shading;
+ CGFunctionRef function;
+ CGFloat colors[12]; // pointer to color values
+
+ // get my context
+ CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+
+ NSColor *deviceDependentFirstColor = [firstColor colorUsingColorSpaceName:NSDeviceRGBColorSpace];
+ NSColor *deviceDependentSecondColor = [secondColor colorUsingColorSpaceName:NSDeviceRGBColorSpace];
+
+ // set up colors for gradient
+ colors[0] = [deviceDependentFirstColor redComponent];
+ colors[1] = [deviceDependentFirstColor greenComponent];
+ colors[2] = [deviceDependentFirstColor blueComponent];
+ colors[3] = [deviceDependentFirstColor alphaComponent];
+
+ colors[4] = [deviceDependentSecondColor redComponent];
+ colors[5] = [deviceDependentSecondColor greenComponent];
+ colors[6] = [deviceDependentSecondColor blueComponent];
+ colors[7] = [deviceDependentSecondColor alphaComponent];
+
+ // difference between start and end color for each color components
+ colors[8] = (colors[4] - colors[0]);
+ colors[9] = (colors[5] - colors[1]);
+ colors[10] = (colors[6] - colors[2]);
+ colors[11] = (colors[7] - colors[3]);
+
+ // draw gradient
+ colorspace = CGColorSpaceCreateDeviceRGB();
+ size_t components = 1 + CGColorSpaceGetNumberOfComponents(colorspace);
+ static const CGFloat domain[2] = {0.0, 1.0};
+ static const CGFloat range[10] = {0, 1, 0, 1, 0, 1, 0, 1, 0, 1};
+ //static const CGFunctionCallbacks callbacks = {0, &bilinearShadedColor, NULL};
+
+ // Create a CGFunctionRef that describes a function taking 1 input and kChannelsPerColor outputs.
+ function = CGFunctionCreate(colors, 1, domain, components, range, &functionCallbacks);
+
+ shading = CGShadingCreateAxial(colorspace, startPoint, endPoint, function, NO, NO);
+
+ CGContextSaveGState(currentContext);
+ [self addClip];
+ CGContextDrawShading(currentContext, shading);
+ CGContextRestoreGState(currentContext);
+
+ CGShadingRelease(shading);
+ CGFunctionRelease(function);
+ CGColorSpaceRelease(colorspace);
+}
+
+- (void)customHorizontalFillWithCallbacks:(CGFunctionCallbacks)functionCallbacks firstColor:(NSColor *)firstColor secondColor:(NSColor *)secondColor {
+ [self customFillWithCallbacks:functionCallbacks
+ firstColor:firstColor
+ secondColor:secondColor
+ startPoint:CGPointMake(0, NSMinY([self bounds]))
+ endPoint:CGPointMake(0, NSMaxY([self bounds]))];
+}
+
+- (void)customVerticalFillWithCallbacks:(CGFunctionCallbacks)functionCallbacks firstColor:(NSColor *)firstColor secondColor:(NSColor *)secondColor {
+ [self customFillWithCallbacks:functionCallbacks
+ firstColor:firstColor
+ secondColor:secondColor
+ startPoint:CGPointMake(NSMinX([self bounds]), 0)
+ endPoint:CGPointMake(NSMaxX([self bounds]), 0)];
+}
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/NSString_AITruncation.h b/frontends/cocoa/PSMTabBarControl/NSString_AITruncation.h
new file mode 100644
index 000000000..cbcbf2c7f
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/NSString_AITruncation.h
@@ -0,0 +1,12 @@
+//
+// NSString_AITruncation.h
+// PSMTabBarControl
+//
+// Created by Evan Schoenberg on 7/14/07.
+//
+
+#import <Cocoa/Cocoa.h>
+
+@interface NSString (AITruncation)
+- (NSString *)stringWithEllipsisByTruncatingToLength:(NSUInteger)length;
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/NSString_AITruncation.m b/frontends/cocoa/PSMTabBarControl/NSString_AITruncation.m
new file mode 100644
index 000000000..1a54502e5
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/NSString_AITruncation.m
@@ -0,0 +1,32 @@
+//
+// NSString_AITruncation.m
+// PSMTabBarControl
+//
+// Created by Evan Schoenberg on 7/14/07.
+// From Adium, which is licensed under the GPL. Used in PSMTabBarControl with permission.
+// The contents of this remain licensed under the GPL.
+//
+
+#import "NSString_AITruncation.h"
+
+@implementation NSString (AITruncation)
+
++ (id)ellipsis {
+ return [NSString stringWithUTF8String:"\xE2\x80\xA6"];
+}
+
+- (NSString *)stringWithEllipsisByTruncatingToLength:(NSUInteger)length {
+ NSString *returnString;
+
+ if(length < [self length]) {
+ //Truncate and append the ellipsis
+ returnString = [[self substringToIndex:length - 1] stringByAppendingString:[NSString ellipsis]];
+ } else {
+ //We don't need to truncate, so don't append an ellipsis
+ returnString = [[self copy] autorelease];
+ }
+
+ return returnString;
+}
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMOverflowPopUpButton.h b/frontends/cocoa/PSMTabBarControl/PSMOverflowPopUpButton.h
new file mode 100644
index 000000000..decda5be4
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMOverflowPopUpButton.h
@@ -0,0 +1,28 @@
+//
+// PSMOverflowPopUpButton.h
+// PSMTabBarControl
+//
+// Created by John Pannell on 11/4/05.
+// Copyright 2005 Positive Spin Media. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+
+@interface PSMOverflowPopUpButton : NSPopUpButton {
+ NSImage *_PSMTabBarOverflowPopUpImage;
+ NSImage *_PSMTabBarOverflowDownPopUpImage;
+ BOOL _down;
+ BOOL _animatingAlternateImage;
+ NSTimer *_animationTimer;
+ CGFloat _animationValue;
+}
+
+//alternate image display
+- (BOOL)animatingAlternateImage;
+- (void)setAnimatingAlternateImage:(BOOL)flag;
+
+// archiving
+- (void)encodeWithCoder:(NSCoder *)aCoder;
+- (id)initWithCoder:(NSCoder *)aDecoder;
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMOverflowPopUpButton.m b/frontends/cocoa/PSMTabBarControl/PSMOverflowPopUpButton.m
new file mode 100644
index 000000000..c316f6973
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMOverflowPopUpButton.m
@@ -0,0 +1,152 @@
+//
+// PSMOverflowPopUpButton.m
+// PSMTabBarControl
+//
+// Created by John Pannell on 11/4/05.
+// Copyright 2005 Positive Spin Media. All rights reserved.
+//
+
+#import "PSMOverflowPopUpButton.h"
+#import "PSMTabBarControl.h"
+
+#define TIMER_INTERVAL 1.0 / 15.0
+#define ANIMATION_STEP 0.033f
+
+@implementation PSMOverflowPopUpButton
+
+- (id)initWithFrame:(NSRect)frameRect pullsDown:(BOOL)flag {
+ if ((self = [super initWithFrame:frameRect pullsDown:YES]) != nil) {
+ [self setBezelStyle:NSRegularSquareBezelStyle];
+ [self setBordered:NO];
+ [self setTitle:@""];
+ [self setPreferredEdge:NSMaxXEdge];
+ _PSMTabBarOverflowPopUpImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"overflowImage"]];
+ _PSMTabBarOverflowDownPopUpImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"overflowImagePressed"]];
+ _animatingAlternateImage = NO;
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [_PSMTabBarOverflowPopUpImage release];
+ [_PSMTabBarOverflowDownPopUpImage release];
+ [super dealloc];
+}
+
+- (void)drawRect:(NSRect)rect {
+ if(_PSMTabBarOverflowPopUpImage == nil) {
+ [super drawRect:rect];
+ return;
+ }
+
+ NSImage *image = (_down) ? _PSMTabBarOverflowDownPopUpImage : _PSMTabBarOverflowPopUpImage;
+ NSSize imageSize = [image size];
+ NSRect bounds = [self bounds];
+
+ NSPoint drawPoint = NSMakePoint(NSMidX(bounds) - (imageSize.width * 0.5f), NSMidY(bounds) - (imageSize.height * 0.5f));
+
+ if([self isFlipped]) {
+ drawPoint.y += imageSize.height;
+ }
+
+ [image compositeToPoint:drawPoint operation:NSCompositeSourceOver fraction:(_animatingAlternateImage ? 0.7f : 1.0f)];
+
+ if(_animatingAlternateImage) {
+ NSImage *alternateImage = [self alternateImage];
+ NSSize altImageSize = [alternateImage size];
+ drawPoint = NSMakePoint(NSMidX(bounds) - (altImageSize.width * 0.5f), NSMidY(bounds) - (altImageSize.height * 0.5f));
+
+ if([self isFlipped]) {
+ drawPoint.y += altImageSize.height;
+ }
+
+ [[self alternateImage] compositeToPoint:drawPoint operation:NSCompositeSourceOver fraction:sin(_animationValue * M_PI)];
+ }
+}
+
+- (void)mouseDown:(NSEvent *)event {
+ _down = YES;
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationReceived:) name:NSMenuDidEndTrackingNotification object:[self menu]];
+ [self setNeedsDisplay:YES];
+ [super mouseDown:event];
+}
+
+- (void)setHidden:(BOOL)value {
+ if([self isHidden] != value) {
+ if(value) {
+ // Stop any animating alternate image if we hide
+ [_animationTimer invalidate], _animationTimer = nil;
+ } else if(_animatingAlternateImage) {
+ // Restart any animating alternate image if we unhide
+ _animationValue = ANIMATION_STEP;
+ _animationTimer = [NSTimer scheduledTimerWithTimeInterval:TIMER_INTERVAL target:self selector:@selector(animateStep:) userInfo:nil repeats:YES];
+ [[NSRunLoop currentRunLoop] addTimer:_animationTimer forMode:NSEventTrackingRunLoopMode];
+ }
+ }
+
+ [super setHidden:value];
+}
+
+- (void)notificationReceived:(NSNotification *)notification {
+ _down = NO;
+ [self setNeedsDisplay:YES];
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+- (void)setAnimatingAlternateImage:(BOOL)flag {
+ if(_animatingAlternateImage != flag) {
+ _animatingAlternateImage = flag;
+
+ if(![self isHidden]) {
+ if(flag) {
+ _animationValue = ANIMATION_STEP;
+ _animationTimer = [NSTimer scheduledTimerWithTimeInterval:TIMER_INTERVAL target:self selector:@selector(animateStep:) userInfo:nil repeats:YES];
+ [[NSRunLoop currentRunLoop] addTimer:_animationTimer forMode:NSEventTrackingRunLoopMode];
+ } else {
+ [_animationTimer invalidate], _animationTimer = nil;
+ }
+
+ [self setNeedsDisplay:YES];
+ }
+ }
+}
+
+- (BOOL)animatingAlternateImage;
+{
+ return _animatingAlternateImage;
+}
+
+- (void)animateStep:(NSTimer *)timer {
+ _animationValue += ANIMATION_STEP;
+
+ if(_animationValue >= 1) {
+ _animationValue = ANIMATION_STEP;
+ }
+
+ [self setNeedsDisplay:YES];
+}
+
+#pragma mark -
+#pragma mark Archiving
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+ [super encodeWithCoder:aCoder];
+ if([aCoder allowsKeyedCoding]) {
+ [aCoder encodeObject:_PSMTabBarOverflowPopUpImage forKey:@"PSMTabBarOverflowPopUpImage"];
+ [aCoder encodeObject:_PSMTabBarOverflowDownPopUpImage forKey:@"PSMTabBarOverflowDownPopUpImage"];
+ [aCoder encodeBool:_animatingAlternateImage forKey:@"PSMTabBarOverflowAnimatingAlternateImage"];
+ }
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder {
+ if((self = [super initWithCoder:aDecoder])) {
+ if([aDecoder allowsKeyedCoding]) {
+ _PSMTabBarOverflowPopUpImage = [[aDecoder decodeObjectForKey:@"PSMTabBarOverflowPopUpImage"] retain];
+ _PSMTabBarOverflowDownPopUpImage = [[aDecoder decodeObjectForKey:@"PSMTabBarOverflowDownPopUpImage"] retain];
+ [self setAnimatingAlternateImage:[aDecoder decodeBoolForKey:@"PSMTabBarOverflowAnimatingAlternateImage"]];
+ }
+ }
+ return self;
+}
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMProgressIndicator.h b/frontends/cocoa/PSMTabBarControl/PSMProgressIndicator.h
new file mode 100644
index 000000000..ffce06926
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMProgressIndicator.h
@@ -0,0 +1,15 @@
+//
+// PSMProgressIndicator.h
+// PSMTabBarControl
+//
+// Created by John Pannell on 2/23/06.
+// Copyright 2006 Positive Spin Media. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+
+@interface PSMProgressIndicator : NSProgressIndicator {
+}
+
+@end \ No newline at end of file
diff --git a/frontends/cocoa/PSMTabBarControl/PSMProgressIndicator.m b/frontends/cocoa/PSMTabBarControl/PSMProgressIndicator.m
new file mode 100644
index 000000000..983609bbc
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMProgressIndicator.m
@@ -0,0 +1,40 @@
+//
+// PSMProgressIndicator.m
+// PSMTabBarControl
+//
+// Created by John Pannell on 2/23/06.
+// Copyright 2006 Positive Spin Media. All rights reserved.
+//
+
+#import "PSMProgressIndicator.h"
+#import "PSMTabBarControl.h"
+
+@interface PSMTabBarControl (PSMProgressIndicatorExtensions)
+
+- (void)update;
+
+@end
+
+@implementation PSMProgressIndicator
+
+- (id) initWithFrame: (NSRect)frameRect;
+{
+ if ((self = [super initWithFrame: frameRect]) == nil) return nil;
+ [self setControlSize: NSSmallControlSize];
+ return self;
+}
+
+// overrides to make tab bar control re-layout things if status changes
+- (void)setHidden:(BOOL)flag {
+ [super setHidden:flag];
+ [(PSMTabBarControl *)[self superview] update];
+}
+
+- (void)stopAnimation:(id)sender {
+ [NSObject cancelPreviousPerformRequestsWithTarget:self
+ selector:@selector(startAnimation:)
+ object:nil];
+ [super stopAnimation:sender];
+}
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMRolloverButton.h b/frontends/cocoa/PSMTabBarControl/PSMRolloverButton.h
new file mode 100644
index 000000000..62fce23e0
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMRolloverButton.h
@@ -0,0 +1,28 @@
+//
+// PSMOverflowPopUpButton.h
+// NetScrape
+//
+// Created by John Pannell on 8/4/04.
+// Copyright 2004 Positive Spin Media. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+@interface PSMRolloverButton : NSButton {
+ NSImage *_rolloverImage;
+ NSImage *_usualImage;
+ NSTrackingRectTag _myTrackingRectTag;
+}
+
+// the regular image
+- (void)setUsualImage:(NSImage *)newImage;
+- (NSImage *)usualImage;
+
+// the rollover image
+- (void)setRolloverImage:(NSImage *)newImage;
+- (NSImage *)rolloverImage;
+
+// tracking rect for mouse events
+- (void)addTrackingRect;
+- (void)removeTrackingRect;
+@end \ No newline at end of file
diff --git a/frontends/cocoa/PSMTabBarControl/PSMRolloverButton.m b/frontends/cocoa/PSMTabBarControl/PSMRolloverButton.m
new file mode 100644
index 000000000..8886560c7
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMRolloverButton.m
@@ -0,0 +1,170 @@
+//
+// PSMOverflowPopUpButton.m
+// NetScrape
+//
+// Created by John Pannell on 8/4/04.
+// Copyright 2004 Positive Spin Media. All rights reserved.
+//
+
+#import "PSMRolloverButton.h"
+
+@implementation PSMRolloverButton
+
+- (void)awakeFromNib {
+ if([[self superclass] instancesRespondToSelector:@selector(awakeFromNib)]) {
+ [super awakeFromNib];
+ }
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(rolloverFrameDidChange:)
+ name:NSViewFrameDidChangeNotification
+ object:self];
+ [self setPostsFrameChangedNotifications:YES];
+ [self resetCursorRects];
+
+ _myTrackingRectTag = -1;
+}
+
+- (void)dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ [self removeTrackingRect];
+
+ [super dealloc];
+}
+
+// the regular image
+- (void)setUsualImage:(NSImage *)newImage {
+ [newImage retain];
+ [_usualImage release];
+ _usualImage = newImage;
+
+ [self setImage:_usualImage];
+}
+
+- (NSImage *)usualImage {
+ return _usualImage;
+}
+
+- (void)setRolloverImage:(NSImage *)newImage {
+ [newImage retain];
+ [_rolloverImage release];
+ _rolloverImage = newImage;
+}
+
+- (NSImage *)rolloverImage {
+ return _rolloverImage;
+}
+
+//Remove old tracking rects when we change superviews
+- (void)viewWillMoveToSuperview:(NSView *)newSuperview {
+ [self removeTrackingRect];
+
+ [super viewWillMoveToSuperview:newSuperview];
+}
+
+- (void)viewDidMoveToSuperview {
+ [super viewDidMoveToSuperview];
+
+ [self resetCursorRects];
+}
+
+- (void)viewWillMoveToWindow:(NSWindow *)newWindow {
+ [self removeTrackingRect];
+
+ [super viewWillMoveToWindow:newWindow];
+}
+
+- (void)viewDidMoveToWindow {
+ [super viewDidMoveToWindow];
+
+ [self resetCursorRects];
+}
+
+- (void)rolloverFrameDidChange:(NSNotification *)inNotification {
+ [self resetCursorRects];
+}
+
+- (void)addTrackingRect {
+ // assign a tracking rect to watch for mouse enter/exit
+ NSRect trackRect = [self bounds];
+ NSPoint localPoint = [self convertPoint:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
+ fromView:nil];
+ BOOL mouseInside = NSPointInRect(localPoint, trackRect);
+
+ _myTrackingRectTag = [self addTrackingRect:trackRect owner:self userData:nil assumeInside:mouseInside];
+ if(mouseInside) {
+ //[self mouseEntered:nil];
+ [self setImage:_rolloverImage];
+ } else{
+ //[self mouseExited:nil];
+ [self setImage:_usualImage];
+ }
+}
+
+- (void)removeTrackingRect {
+ if(_myTrackingRectTag != -1) {
+ [self removeTrackingRect:_myTrackingRectTag];
+ }
+ _myTrackingRectTag = -1;
+}
+
+// override for rollover effect
+- (void)mouseEntered:(NSEvent *)theEvent;
+{
+ // set rollover image
+ [self setImage:_rolloverImage];
+
+ [super mouseEntered:theEvent];
+}
+
+- (void)mouseExited:(NSEvent *)theEvent;
+{
+ // restore usual image
+ [self setImage:_usualImage];
+
+ [super mouseExited:theEvent];
+}
+
+- (void)resetCursorRects {
+ // called when the button rect has been changed
+ [self removeTrackingRect];
+ [self addTrackingRect];
+}
+
+- (void)setFrame:(NSRect)rect {
+ [super setFrame:rect];
+ [self resetCursorRects];
+}
+
+- (void)setBounds:(NSRect)rect {
+ [super setBounds:rect];
+ [self resetCursorRects];
+}
+
+#pragma mark -
+#pragma mark Archiving
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+ [super encodeWithCoder:aCoder];
+ if([aCoder allowsKeyedCoding]) {
+ [aCoder encodeObject:_rolloverImage forKey:@"rolloverImage"];
+ [aCoder encodeObject:_usualImage forKey:@"usualImage"];
+ [aCoder encodeInt64:_myTrackingRectTag forKey:@"myTrackingRectTag"];
+ }
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder {
+ self = [super initWithCoder:aDecoder];
+ if(self) {
+ if([aDecoder allowsKeyedCoding]) {
+ _rolloverImage = [[aDecoder decodeObjectForKey:@"rolloverImage"] retain];
+ _usualImage = [[aDecoder decodeObjectForKey:@"usualImage"] retain];
+ _myTrackingRectTag = [aDecoder decodeInt64ForKey:@"myTrackingRectTag"];
+ }
+ }
+ return self;
+}
+
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabBarCell.h b/frontends/cocoa/PSMTabBarControl/PSMTabBarCell.h
new file mode 100644
index 000000000..c8f6cecdd
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabBarCell.h
@@ -0,0 +1,116 @@
+//
+// PSMTabBarCell.h
+// PSMTabBarControl
+//
+// Created by John Pannell on 10/13/05.
+// Copyright 2005 Positive Spin Media. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "PSMTabBarControl.h"
+#import "PSMProgressIndicator.h"
+
+
+@interface PSMTabBarCell : NSActionCell {
+ // sizing
+ NSRect _frame;
+ NSSize _stringSize;
+ NSInteger _currentStep;
+ BOOL _isPlaceholder;
+
+ // state
+ NSInteger _tabState;
+ NSTrackingRectTag _closeButtonTrackingTag; // left side tracking, if dragging
+ NSTrackingRectTag _cellTrackingTag; // right side tracking, if dragging
+ BOOL _closeButtonOver;
+ BOOL _closeButtonPressed;
+ PSMProgressIndicator *_indicator;
+ BOOL _isInOverflowMenu;
+ BOOL _hasCloseButton;
+ BOOL _isCloseButtonSuppressed;
+ BOOL _hasIcon;
+ BOOL _hasLargeImage;
+ NSInteger _count;
+ NSColor *_countColor;
+ BOOL _isEdited;
+}
+
+// creation/destruction
+- (id)initWithControlView:(PSMTabBarControl *)controlView;
+- (id)initPlaceholderWithFrame:(NSRect) frame expanded:(BOOL) value inControlView:(PSMTabBarControl *)controlView;
+- (void)dealloc;
+
+// accessors
+- (id)controlView;
+- (void)setControlView:(id)view;
+- (NSTrackingRectTag)closeButtonTrackingTag;
+- (void)setCloseButtonTrackingTag:(NSTrackingRectTag)tag;
+- (NSTrackingRectTag)cellTrackingTag;
+- (void)setCellTrackingTag:(NSTrackingRectTag)tag;
+- (CGFloat)width;
+- (NSRect)frame;
+- (void)setFrame:(NSRect)rect;
+- (void)setStringValue:(NSString *)aString;
+- (NSSize)stringSize;
+- (NSAttributedString *)attributedStringValue;
+- (NSInteger)tabState;
+- (void)setTabState:(NSInteger)state;
+- (NSProgressIndicator *)indicator;
+- (BOOL)isInOverflowMenu;
+- (void)setIsInOverflowMenu:(BOOL)value;
+- (BOOL)closeButtonPressed;
+- (void)setCloseButtonPressed:(BOOL)value;
+- (BOOL)closeButtonOver;
+- (void)setCloseButtonOver:(BOOL)value;
+- (BOOL)hasCloseButton;
+- (void)setHasCloseButton:(BOOL)set;
+- (void)setCloseButtonSuppressed:(BOOL)suppress;
+- (BOOL)isCloseButtonSuppressed;
+- (BOOL)hasIcon;
+- (void)setHasIcon:(BOOL)value;
+- (BOOL)hasLargeImage;
+- (void)setHasLargeImage:(BOOL)value;
+- (NSInteger)count;
+- (void)setCount:(NSInteger)value;
+- (NSColor *)countColor;
+- (void)setCountColor:(NSColor *)value;
+- (BOOL)isPlaceholder;
+- (void)setIsPlaceholder:(BOOL)value;
+- (NSInteger)currentStep;
+- (void)setCurrentStep:(NSInteger)value;
+- (BOOL)isEdited;
+- (void)setIsEdited:(BOOL)value;
+
+// component attributes
+- (NSRect)indicatorRectForFrame:(NSRect)cellFrame;
+- (NSRect)closeButtonRectForFrame:(NSRect)cellFrame;
+- (CGFloat)minimumWidthOfCell;
+- (CGFloat)desiredWidthOfCell;
+
+// drawing
+- (void)drawWithFrame:(NSRect) cellFrame inView:(NSView *)controlView;
+
+// tracking the mouse
+- (void)mouseEntered:(NSEvent *)theEvent;
+- (void)mouseExited:(NSEvent *)theEvent;
+
+// drag support
+- (NSImage *)dragImage;
+
+// archiving
+- (void)encodeWithCoder:(NSCoder *)aCoder;
+- (id)initWithCoder:(NSCoder *)aDecoder;
+
+@end
+
+@interface PSMTabBarControl (CellAccessors)
+
+- (id<PSMTabStyle>)style;
+
+@end
+
+@interface NSObject (IdentifierAccesors)
+
+- (NSImage *)largeImage;
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabBarCell.m b/frontends/cocoa/PSMTabBarControl/PSMTabBarCell.m
new file mode 100644
index 000000000..f7a04f202
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabBarCell.m
@@ -0,0 +1,489 @@
+//
+// PSMTabBarCell.m
+// PSMTabBarControl
+//
+// Created by John Pannell on 10/13/05.
+// Copyright 2005 Positive Spin Media. All rights reserved.
+//
+
+#import "PSMTabBarCell.h"
+#import "PSMTabBarControl.h"
+#import "PSMTabStyle.h"
+#import "PSMProgressIndicator.h"
+#import "PSMTabDragAssistant.h"
+
+@interface PSMTabBarControl (Private)
+- (void)update;
+@end
+
+@implementation PSMTabBarCell
+
+#pragma mark -
+#pragma mark Creation/Destruction
+- (id)initWithControlView:(PSMTabBarControl *)controlView {
+ if((self = [super init])) {
+ _controlView = controlView;
+ _closeButtonTrackingTag = 0;
+ _cellTrackingTag = 0;
+ _closeButtonOver = NO;
+ _closeButtonPressed = NO;
+ _indicator = [[PSMProgressIndicator alloc] initWithFrame:NSMakeRect(0.0, 0.0, kPSMTabBarIndicatorWidth, kPSMTabBarIndicatorWidth)];
+ [_indicator setStyle:NSProgressIndicatorSpinningStyle];
+ [_indicator setAutoresizingMask:NSViewMinYMargin];
+ _hasCloseButton = YES;
+ _isCloseButtonSuppressed = NO;
+ _count = 0;
+ _countColor = nil;
+ _isEdited = NO;
+ _isPlaceholder = NO;
+ }
+ return self;
+}
+
+- (id)initPlaceholderWithFrame:(NSRect)frame expanded:(BOOL)value inControlView:(PSMTabBarControl *)controlView {
+ if((self = [super init])) {
+ _controlView = controlView;
+ _isPlaceholder = YES;
+ if(!value) {
+ if([controlView orientation] == PSMTabBarHorizontalOrientation) {
+ frame.size.width = 0.0;
+ } else {
+ frame.size.height = 0.0;
+ }
+ }
+ [self setFrame:frame];
+ _closeButtonTrackingTag = 0;
+ _cellTrackingTag = 0;
+ _closeButtonOver = NO;
+ _closeButtonPressed = NO;
+ _indicator = nil;
+ _hasCloseButton = YES;
+ _isCloseButtonSuppressed = NO;
+ _count = 0;
+ _countColor = nil;
+ _isEdited = NO;
+
+ if(value) {
+ [self setCurrentStep:(kPSMTabDragAnimationSteps - 1)];
+ } else {
+ [self setCurrentStep:0];
+ }
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [_countColor release];
+
+ [_indicator removeFromSuperviewWithoutNeedingDisplay];
+
+ [_indicator release];
+ [super dealloc];
+}
+
+#pragma mark -
+#pragma mark Accessors
+
+- (id)controlView {
+ return _controlView;
+}
+
+- (void)setControlView:(id)view {
+ // no retain release pattern, as this simply switches a tab to another view.
+ _controlView = view;
+}
+
+- (NSTrackingRectTag)closeButtonTrackingTag {
+ return _closeButtonTrackingTag;
+}
+
+- (void)setCloseButtonTrackingTag:(NSTrackingRectTag)tag {
+ _closeButtonTrackingTag = tag;
+}
+
+- (NSTrackingRectTag)cellTrackingTag {
+ return _cellTrackingTag;
+}
+
+- (void)setCellTrackingTag:(NSTrackingRectTag)tag {
+ _cellTrackingTag = tag;
+}
+
+- (CGFloat)width {
+ return _frame.size.width;
+}
+
+- (NSRect)frame {
+ return _frame;
+}
+
+- (void)setFrame:(NSRect)rect {
+ _frame = rect;
+
+ //move the status indicator along with the rest of the cell
+ if(![[self indicator] isHidden] && ![_controlView isTabBarHidden]) {
+ [[self indicator] setFrame:[self indicatorRectForFrame:rect]];
+ }
+}
+
+- (void)setStringValue:(NSString *)aString {
+ [super setStringValue:aString];
+ _stringSize = [[self attributedStringValue] size];
+ // need to redisplay now - binding observation was too quick.
+ [_controlView update];
+}
+
+- (NSSize)stringSize {
+ return _stringSize;
+}
+
+- (NSAttributedString *)attributedStringValue {
+ return [(id < PSMTabStyle >)[_controlView style] attributedStringValueForTabCell:self];
+}
+
+- (NSInteger)tabState {
+ return _tabState;
+}
+
+- (void)setTabState:(NSInteger)state {
+ _tabState = state;
+}
+
+- (NSProgressIndicator *)indicator {
+ return _indicator;
+}
+
+- (BOOL)isInOverflowMenu {
+ return _isInOverflowMenu;
+}
+
+- (void)setIsInOverflowMenu:(BOOL)value {
+ if(_isInOverflowMenu != value) {
+ _isInOverflowMenu = value;
+ if([[[self controlView] delegate] respondsToSelector:@selector(tabView:tabViewItem:isInOverflowMenu:)]) {
+ [[[self controlView] delegate] tabView:[self controlView] tabViewItem:[self representedObject] isInOverflowMenu:_isInOverflowMenu];
+ }
+ }
+}
+
+- (BOOL)closeButtonPressed {
+ return _closeButtonPressed;
+}
+
+- (void)setCloseButtonPressed:(BOOL)value {
+ _closeButtonPressed = value;
+}
+
+- (BOOL)closeButtonOver {
+ return(_closeButtonOver && ([_controlView allowsBackgroundTabClosing] || ([self tabState] & PSMTab_SelectedMask) || [[NSApp currentEvent] modifierFlags] & NSCommandKeyMask));
+}
+
+- (void)setCloseButtonOver:(BOOL)value {
+ _closeButtonOver = value;
+}
+
+- (BOOL)hasCloseButton {
+ return _hasCloseButton;
+}
+
+- (void)setHasCloseButton:(BOOL)set;
+{
+ _hasCloseButton = set;
+}
+
+- (void)setCloseButtonSuppressed:(BOOL)suppress;
+{
+ _isCloseButtonSuppressed = suppress;
+}
+
+- (BOOL)isCloseButtonSuppressed;
+{
+ return _isCloseButtonSuppressed;
+}
+
+- (BOOL)hasIcon {
+ return _hasIcon;
+}
+
+- (void)setHasIcon:(BOOL)value {
+ _hasIcon = value;
+ //[_controlView update:[[self controlView] automaticallyAnimates]]; // binding notice is too fast
+}
+
+- (BOOL)hasLargeImage {
+ return _hasLargeImage;
+}
+
+- (void)setHasLargeImage:(BOOL)value {
+ _hasLargeImage = value;
+}
+
+
+- (NSInteger)count {
+ return _count;
+}
+
+- (void)setCount:(NSInteger)value {
+ _count = value;
+ //[_controlView update:[[self controlView] automaticallyAnimates]]; // binding notice is too fast
+}
+
+- (NSColor *)countColor {
+ return _countColor;
+}
+
+- (void)setCountColor:(NSColor *)color {
+ [_countColor release];
+ _countColor = [color retain];
+}
+
+- (BOOL)isPlaceholder {
+ return _isPlaceholder;
+}
+
+- (void)setIsPlaceholder:(BOOL)value;
+{
+ _isPlaceholder = value;
+}
+
+- (NSInteger)currentStep {
+ return _currentStep;
+}
+
+- (void)setCurrentStep:(NSInteger)value {
+ if(value < 0) {
+ value = 0;
+ }
+
+ if(value > (kPSMTabDragAnimationSteps - 1)) {
+ value = (kPSMTabDragAnimationSteps - 1);
+ }
+
+ _currentStep = value;
+}
+
+- (BOOL)isEdited {
+ return _isEdited;
+}
+
+- (void)setIsEdited:(BOOL)value {
+ _isEdited = value;
+ //[_controlView update:[[self controlView] automaticallyAnimates]]; // binding notice is too fast
+}
+
+#pragma mark -
+#pragma mark Bindings
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
+ // the progress indicator, label, icon, or count has changed - redraw the control view
+ //[_controlView update];
+ //I seem to have run into some odd issue with update not being called at the right time. This seems to avoid the problem.
+ [_controlView performSelector:@selector(update) withObject:nil afterDelay:0.0];
+}
+
+#pragma mark -
+#pragma mark Component Attributes
+
+- (NSRect)indicatorRectForFrame:(NSRect)cellFrame {
+ return [(id < PSMTabStyle >)[_controlView style] indicatorRectForTabCell:self];
+}
+
+- (NSRect)closeButtonRectForFrame:(NSRect)cellFrame {
+ return [(id < PSMTabStyle >)[_controlView style] closeButtonRectForTabCell:self withFrame:cellFrame];
+}
+
+- (CGFloat)minimumWidthOfCell {
+ return [(id < PSMTabStyle >)[_controlView style] minimumWidthOfTabCell:self];
+}
+
+- (CGFloat)desiredWidthOfCell {
+ return [(id < PSMTabStyle >)[_controlView style] desiredWidthOfTabCell:self];
+}
+
+#pragma mark -
+#pragma mark Drawing
+
+- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
+ if(_isPlaceholder) {
+ [[NSColor colorWithCalibratedWhite:0.0 alpha:0.2] set];
+ NSRectFillUsingOperation(cellFrame, NSCompositeSourceAtop);
+ return;
+ }
+
+ [(id < PSMTabStyle >)[_controlView style] drawTabCell:self];
+}
+
+#pragma mark -
+#pragma mark Tracking
+
+- (void)mouseEntered:(NSEvent *)theEvent {
+ // check for which tag
+ if([theEvent trackingNumber] == _closeButtonTrackingTag) {
+ _closeButtonOver = YES;
+ }
+ if([theEvent trackingNumber] == _cellTrackingTag) {
+ [self setHighlighted:YES];
+ [_controlView setNeedsDisplay:NO];
+ }
+
+ // scrubtastic
+ if([_controlView allowsScrubbing] && ([theEvent modifierFlags] & NSAlternateKeyMask)) {
+ [_controlView performSelector:@selector(tabClick:) withObject:self];
+ }
+
+ // tell the control we only need to redraw the affected tab
+ [_controlView setNeedsDisplayInRect:NSInsetRect([self frame], -2, -2)];
+}
+
+- (void)mouseExited:(NSEvent *)theEvent {
+ // check for which tag
+ if([theEvent trackingNumber] == _closeButtonTrackingTag) {
+ _closeButtonOver = NO;
+ }
+
+ if([theEvent trackingNumber] == _cellTrackingTag) {
+ [self setHighlighted:NO];
+ [_controlView setNeedsDisplay:NO];
+ }
+
+ //tell the control we only need to redraw the affected tab
+ [_controlView setNeedsDisplayInRect:NSInsetRect([self frame], -2, -2)];
+}
+
+#pragma mark -
+#pragma mark Drag Support
+
+- (NSImage *)dragImage {
+ NSRect cellFrame = [(id < PSMTabStyle >)[(PSMTabBarControl *)_controlView style] dragRectForTabCell:self orientation:(PSMTabBarOrientation)[(PSMTabBarControl *)_controlView orientation]];
+ //NSRect cellFrame = [self frame];
+
+ [_controlView lockFocus];
+ NSBitmapImageRep *rep = [[[NSBitmapImageRep alloc] initWithFocusedViewRect:cellFrame] autorelease];
+ [_controlView unlockFocus];
+ NSImage *image = [[[NSImage alloc] initWithSize:[rep size]] autorelease];
+ [image addRepresentation:rep];
+ NSImage *returnImage = [[[NSImage alloc] initWithSize:[rep size]] autorelease];
+ [returnImage lockFocus];
+ [image compositeToPoint:NSMakePoint(0.0, 0.0) operation:NSCompositeSourceOver fraction:1.0];
+ [returnImage unlockFocus];
+ if(![[self indicator] isHidden]) {
+ NSImage *pi = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"pi"]];
+ [returnImage lockFocus];
+ NSPoint indicatorPoint = NSMakePoint([self frame].size.width - MARGIN_X - kPSMTabBarIndicatorWidth, MARGIN_Y);
+ [pi compositeToPoint:indicatorPoint operation:NSCompositeSourceOver fraction:1.0];
+ [returnImage unlockFocus];
+ [pi release];
+ }
+ return returnImage;
+}
+
+#pragma mark -
+#pragma mark Archiving
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+ [super encodeWithCoder:aCoder];
+ if([aCoder allowsKeyedCoding]) {
+ [aCoder encodeRect:_frame forKey:@"frame"];
+ [aCoder encodeSize:_stringSize forKey:@"stringSize"];
+ [aCoder encodeInteger:_currentStep forKey:@"currentStep"];
+ [aCoder encodeBool:_isPlaceholder forKey:@"isPlaceholder"];
+ [aCoder encodeInteger:_tabState forKey:@"tabState"];
+ [aCoder encodeInteger:_closeButtonTrackingTag forKey:@"closeButtonTrackingTag"];
+ [aCoder encodeInteger:_cellTrackingTag forKey:@"cellTrackingTag"];
+ [aCoder encodeBool:_closeButtonOver forKey:@"closeButtonOver"];
+ [aCoder encodeBool:_closeButtonPressed forKey:@"closeButtonPressed"];
+ [aCoder encodeObject:_indicator forKey:@"indicator"];
+ [aCoder encodeBool:_isInOverflowMenu forKey:@"isInOverflowMenu"];
+ [aCoder encodeBool:_hasCloseButton forKey:@"hasCloseButton"];
+ [aCoder encodeBool:_isCloseButtonSuppressed forKey:@"isCloseButtonSuppressed"];
+ [aCoder encodeBool:_hasIcon forKey:@"hasIcon"];
+ [aCoder encodeBool:_hasLargeImage forKey:@"hasLargeImage"];
+ [aCoder encodeInteger:_count forKey:@"count"];
+ [aCoder encodeBool:_isEdited forKey:@"isEdited"];
+ }
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder {
+ self = [super initWithCoder:aDecoder];
+ if(self) {
+ if([aDecoder allowsKeyedCoding]) {
+ _frame = [aDecoder decodeRectForKey:@"frame"];
+ _stringSize = [aDecoder decodeSizeForKey:@"stringSize"];
+ _currentStep = [aDecoder decodeIntegerForKey:@"currentStep"];
+ _isPlaceholder = [aDecoder decodeBoolForKey:@"isPlaceholder"];
+ _tabState = [aDecoder decodeIntegerForKey:@"tabState"];
+ _closeButtonTrackingTag = [aDecoder decodeIntegerForKey:@"closeButtonTrackingTag"];
+ _cellTrackingTag = [aDecoder decodeIntegerForKey:@"cellTrackingTag"];
+ _closeButtonOver = [aDecoder decodeBoolForKey:@"closeButtonOver"];
+ _closeButtonPressed = [aDecoder decodeBoolForKey:@"closeButtonPressed"];
+ _indicator = [[aDecoder decodeObjectForKey:@"indicator"] retain];
+ _isInOverflowMenu = [aDecoder decodeBoolForKey:@"isInOverflowMenu"];
+ _hasCloseButton = [aDecoder decodeBoolForKey:@"hasCloseButton"];
+ _isCloseButtonSuppressed = [aDecoder decodeBoolForKey:@"isCloseButtonSuppressed"];
+ _hasIcon = [aDecoder decodeBoolForKey:@"hasIcon"];
+ _hasLargeImage = [aDecoder decodeBoolForKey:@"hasLargeImage"];
+ _count = [aDecoder decodeIntegerForKey:@"count"];
+ _isEdited = [aDecoder decodeBoolForKey:@"isEdited"];
+ }
+ }
+ return self;
+}
+
+#pragma mark -
+#pragma mark Accessibility
+
+-(BOOL)accessibilityIsIgnored {
+ return NO;
+}
+
+- (id)accessibilityAttributeValue:(NSString *)attribute {
+ id attributeValue = nil;
+
+ if([attribute isEqualToString: NSAccessibilityRoleAttribute]) {
+ attributeValue = NSAccessibilityButtonRole;
+ } else if([attribute isEqualToString: NSAccessibilityHelpAttribute]) {
+ if([[[self controlView] delegate] respondsToSelector:@selector(accessibilityStringForTabView:objectCount:)]) {
+ attributeValue = [NSString stringWithFormat:@"%@, %lu %@", [self stringValue],
+ (unsigned long)[self count],
+ [[[self controlView] delegate] accessibilityStringForTabView:[[self controlView] tabView] objectCount:[self count]]];
+ } else {
+ attributeValue = [self stringValue];
+ }
+ } else if([attribute isEqualToString: NSAccessibilityFocusedAttribute]) {
+ attributeValue = [NSNumber numberWithBool:([self tabState] == 2)];
+ } else {
+ attributeValue = [super accessibilityAttributeValue:attribute];
+ }
+
+ return attributeValue;
+}
+
+- (NSArray *)accessibilityActionNames {
+ static NSArray *actions;
+
+ if(!actions) {
+ actions = [[NSArray alloc] initWithObjects:NSAccessibilityPressAction, nil];
+ }
+ return actions;
+}
+
+- (NSString *)accessibilityActionDescription:(NSString *)action {
+ return NSAccessibilityActionDescription(action);
+}
+
+- (void)accessibilityPerformAction:(NSString *)action {
+ if([action isEqualToString:NSAccessibilityPressAction]) {
+ // this tab was selected
+ [_controlView performSelector:@selector(tabClick:) withObject:self];
+ }
+}
+
+- (id)accessibilityHitTest:(NSPoint)point {
+ return NSAccessibilityUnignoredAncestor(self);
+}
+
+- (id)accessibilityFocusedUIElement:(NSPoint)point {
+ return NSAccessibilityUnignoredAncestor(self);
+}
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabBarControl.h b/frontends/cocoa/PSMTabBarControl/PSMTabBarControl.h
new file mode 100644
index 000000000..980c43d38
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabBarControl.h
@@ -0,0 +1,241 @@
+//
+// PSMTabBarControl.h
+// PSMTabBarControl
+//
+// Created by John Pannell on 10/13/05.
+// Copyright 2005 Positive Spin Media. All rights reserved.
+//
+
+/*
+ This view provides a control interface to manage a regular NSTabView. It looks and works like the tabbed browsing interface of many popular browsers.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#define PSMTabDragDidEndNotification @ "PSMTabDragDidEndNotification"
+#define PSMTabDragDidBeginNotification @ "PSMTabDragDidBeginNotification"
+
+#define kPSMTabBarControlHeight 22
+// internal cell border
+#define MARGIN_X 6
+#define MARGIN_Y 3
+// padding between objects
+#define kPSMTabBarCellPadding 4
+// fixed size objects
+#define kPSMMinimumTitleWidth 30
+#define kPSMTabBarIndicatorWidth 16.0
+#define kPSMTabBarIconWidth 16.0
+#define kPSMHideAnimationSteps 3.0
+
+// Value used in _currentStep to indicate that resizing operation is not in progress
+#define kPSMIsNotBeingResized -1
+
+// Value used in _currentStep when a resizing operation has just been started
+#define kPSMStartResizeAnimation 0
+
+@class PSMOverflowPopUpButton;
+@class PSMRolloverButton;
+@class PSMTabBarCell;
+@class PSMTabBarController;
+@protocol PSMTabStyle;
+
+typedef enum {
+ PSMTabBarHorizontalOrientation,
+ PSMTabBarVerticalOrientation
+} PSMTabBarOrientation;
+
+typedef enum {
+ PSMTabBarTearOffAlphaWindow,
+ PSMTabBarTearOffMiniwindow
+} PSMTabBarTearOffStyle;
+
+enum {
+ PSMTab_SelectedMask = 1 << 1,
+ PSMTab_LeftIsSelectedMask = 1 << 2,
+ PSMTab_RightIsSelectedMask = 1 << 3,
+ PSMTab_PositionLeftMask = 1 << 4,
+ PSMTab_PositionMiddleMask = 1 << 5,
+ PSMTab_PositionRightMask = 1 << 6,
+ PSMTab_PositionSingleMask = 1 << 7,
+};
+
+@interface PSMTabBarControl : NSControl {
+
+ // control basics
+ NSMutableArray *_cells; // the cells that draw the tabs
+ IBOutlet NSTabView *tabView; // the tab view being navigated
+ PSMOverflowPopUpButton *_overflowPopUpButton; // for too many tabs
+ PSMRolloverButton *_addTabButton;
+ PSMTabBarController *_controller;
+
+ // Spring-loading.
+ NSTimer *_springTimer;
+ NSTabViewItem *_tabViewItemWithSpring;
+
+ // drawing style
+ id<PSMTabStyle> style;
+ BOOL _canCloseOnlyTab;
+ BOOL _disableTabClose;
+ BOOL _hideForSingleTab;
+ BOOL _showAddTabButton;
+ BOOL _sizeCellsToFit;
+ BOOL _useOverflowMenu;
+ BOOL _alwaysShowActiveTab;
+ BOOL _allowsScrubbing;
+ NSInteger _resizeAreaCompensation;
+ PSMTabBarOrientation _orientation;
+ BOOL _automaticallyAnimates;
+ NSTimer *_animationTimer;
+ PSMTabBarTearOffStyle _tearOffStyle;
+
+ // behavior
+ BOOL _allowsBackgroundTabClosing;
+ BOOL _selectsTabsOnMouseDown;
+
+ // vertical tab resizing
+ BOOL _allowsResizing;
+ BOOL _resizing;
+
+ // cell width
+ NSInteger _cellMinWidth;
+ NSInteger _cellMaxWidth;
+ NSInteger _cellOptimumWidth;
+
+ // animation for hide/show
+ NSInteger _currentStep;
+ BOOL _isHidden;
+ IBOutlet id partnerView; // gets resized when hide/show
+ BOOL _awakenedFromNib;
+ NSInteger _tabBarWidth;
+ NSTimer *_showHideAnimationTimer;
+
+ // drag and drop
+ NSEvent *_lastMouseDownEvent; // keep this for dragging reference
+ BOOL _didDrag;
+ BOOL _closeClicked;
+
+ // MVC help
+ IBOutlet id delegate;
+}
+
+// control characteristics
++ (NSBundle *)bundle;
+- (CGFloat)availableCellWidth;
+- (NSRect)genericCellRect;
+
+// control configuration
+- (PSMTabBarOrientation)orientation;
+- (void)setOrientation:(PSMTabBarOrientation)value;
+- (BOOL)canCloseOnlyTab;
+- (void)setCanCloseOnlyTab:(BOOL)value;
+- (BOOL)disableTabClose;
+- (void)setDisableTabClose:(BOOL)value;
+- (id<PSMTabStyle>)style;
+- (void)setStyle:(id <PSMTabStyle>)newStyle;
+- (NSString *)styleName;
+- (void)setStyleNamed:(NSString *)name;
+- (BOOL)hideForSingleTab;
+- (void)setHideForSingleTab:(BOOL)value;
+- (BOOL)showAddTabButton;
+- (void)setShowAddTabButton:(BOOL)value;
+- (NSInteger)cellMinWidth;
+- (void)setCellMinWidth:(NSInteger)value;
+- (NSInteger)cellMaxWidth;
+- (void)setCellMaxWidth:(NSInteger)value;
+- (NSInteger)cellOptimumWidth;
+- (void)setCellOptimumWidth:(NSInteger)value;
+- (BOOL)sizeCellsToFit;
+- (void)setSizeCellsToFit:(BOOL)value;
+- (BOOL)useOverflowMenu;
+- (void)setUseOverflowMenu:(BOOL)value;
+- (BOOL)allowsBackgroundTabClosing;
+- (void)setAllowsBackgroundTabClosing:(BOOL)value;
+- (BOOL)allowsResizing;
+- (void)setAllowsResizing:(BOOL)value;
+- (BOOL)selectsTabsOnMouseDown;
+- (void)setSelectsTabsOnMouseDown:(BOOL)value;
+- (BOOL)automaticallyAnimates;
+- (void)setAutomaticallyAnimates:(BOOL)value;
+- (BOOL)alwaysShowActiveTab;
+- (void)setAlwaysShowActiveTab:(BOOL)value;
+- (BOOL)allowsScrubbing;
+- (void)setAllowsScrubbing:(BOOL)value;
+- (PSMTabBarTearOffStyle)tearOffStyle;
+- (void)setTearOffStyle:(PSMTabBarTearOffStyle)tearOffStyle;
+
+// Factory for default style
++ (Class)defaultStyleClass;
+
+// accessors
+- (NSTabView *)tabView;
+- (void)setTabView:(NSTabView *)view;
+- (id)delegate;
+- (void)setDelegate:(id)object;
+- (id)partnerView;
+- (void)setPartnerView:(id)view;
+
+// the buttons
+- (PSMRolloverButton *)addTabButton;
+- (PSMOverflowPopUpButton *)overflowPopUpButton;
+
+// tab information
+- (NSMutableArray *)representedTabViewItems;
+- (NSInteger)numberOfVisibleTabs;
+- (PSMTabBarCell *)lastVisibleTab;
+
+// special effects
+- (void)hideTabBar:(BOOL) hide animate:(BOOL)animate;
+- (BOOL)isTabBarHidden;
+- (BOOL)isAnimating;
+
+// internal bindings methods also used by the tab drag assistant
+- (void)bindPropertiesForCell:(PSMTabBarCell *)cell andTabViewItem:(NSTabViewItem *)item;
+- (void)removeTabForCell:(PSMTabBarCell *)cell;
+
+@end
+
+
+@interface NSObject (TabBarControlDelegateMethods)
+
+//Standard NSTabView methods
+- (BOOL)tabView:(NSTabView *)aTabView shouldCloseTabViewItem:(NSTabViewItem *)tabViewItem;
+- (void)tabView:(NSTabView *)aTabView didCloseTabViewItem:(NSTabViewItem *)tabViewItem;
+
+//"Spring-loaded" tabs methods
+- (NSArray *)allowedDraggedTypesForTabView:(NSTabView *)aTabView;
+- (void)tabView:(NSTabView *)aTabView acceptedDraggingInfo:(id <NSDraggingInfo>) draggingInfo onTabViewItem:(NSTabViewItem *)tabViewItem;
+
+//Contextual menu method
+- (NSMenu *)tabView:(NSTabView *)aTabView menuForTabViewItem:(NSTabViewItem *)tabViewItem;
+
+//Drag and drop methods
+- (BOOL)tabView:(NSTabView *)aTabView shouldDragTabViewItem:(NSTabViewItem *)tabViewItem fromTabBar:(PSMTabBarControl *)tabBarControl;
+- (BOOL)tabView:(NSTabView *)aTabView shouldDropTabViewItem:(NSTabViewItem *)tabViewItem inTabBar:(PSMTabBarControl *)tabBarControl;
+- (BOOL)tabView:(NSTabView *)aTabView shouldAllowTabViewItem:(NSTabViewItem *)tabViewItem toLeaveTabBar:(PSMTabBarControl *)tabBarControl;
+- (void)tabView:(NSTabView*)aTabView didDropTabViewItem:(NSTabViewItem *)tabViewItem inTabBar:(PSMTabBarControl *)tabBarControl;
+
+
+//Tear-off tabs methods
+- (NSImage *)tabView:(NSTabView *)aTabView imageForTabViewItem:(NSTabViewItem *)tabViewItem offset:(NSSize *)offset styleMask:(NSUInteger *)styleMask;
+- (PSMTabBarControl *)tabView:(NSTabView *)aTabView newTabBarForDraggedTabViewItem:(NSTabViewItem *)tabViewItem atPoint:(NSPoint)point;
+- (void)tabView:(NSTabView *)aTabView closeWindowForLastTabViewItem:(NSTabViewItem *)tabViewItem;
+
+//Overflow menu validation
+- (BOOL)tabView:(NSTabView *)aTabView validateOverflowMenuItem:(NSMenuItem *)menuItem forTabViewItem:(NSTabViewItem *)tabViewItem;
+- (void)tabView:(NSTabView *)aTabView tabViewItem:(NSTabViewItem *)tabViewItem isInOverflowMenu:(BOOL)inOverflowMenu;
+
+//tab bar hiding methods
+- (void)tabView:(NSTabView *)aTabView tabBarDidHide:(PSMTabBarControl *)tabBarControl;
+- (void)tabView:(NSTabView *)aTabView tabBarDidUnhide:(PSMTabBarControl *)tabBarControl;
+- (CGFloat)desiredWidthForVerticalTabBar:(PSMTabBarControl *)tabBarControl;
+
+//closing
+- (BOOL)tabView:(NSTabView *)aTabView disableTabCloseForTabViewItem:(NSTabViewItem *)tabViewItem;
+
+//tooltips
+- (NSString *)tabView:(NSTabView *)aTabView toolTipForTabViewItem:(NSTabViewItem *)tabViewItem;
+
+//accessibility
+- (NSString *)accessibilityStringForTabView:(NSTabView *)aTabView objectCount:(NSInteger)objectCount;
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabBarControl.m b/frontends/cocoa/PSMTabBarControl/PSMTabBarControl.m
new file mode 100644
index 000000000..cb0e0342c
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabBarControl.m
@@ -0,0 +1,1995 @@
+//
+// PSMTabBarControl.m
+// PSMTabBarControl
+//
+// Created by John Pannell on 10/13/05.
+// Copyright 2005 Positive Spin Media. All rights reserved.
+//
+
+#import <objc/runtime.h>
+
+#import "PSMTabBarControl.h"
+#import "PSMTabBarCell.h"
+#import "PSMOverflowPopUpButton.h"
+#import "PSMRolloverButton.h"
+#import "PSMTabStyle.h"
+#import "PSMUnifiedTabStyle.h"
+#import "PSMTabDragAssistant.h"
+#import "PSMTabBarController.h"
+
+@interface PSMTabBarControl (Private)
+
+// constructor/destructor
+- (void)initAddedProperties;
+
+// accessors
+- (NSEvent *)lastMouseDownEvent;
+- (void)setLastMouseDownEvent:(NSEvent *)event;
+
+// contents
+- (void)addTabViewItem:(NSTabViewItem *)item;
+- (void)removeTabForCell:(PSMTabBarCell *)cell;
+
+// draw
+- (void)update;
+- (void)update:(BOOL)animate;
+- (void)_setupTrackingRectsForCell:(PSMTabBarCell *)cell;
+- (void)_positionOverflowMenu;
+- (void)_checkWindowFrame;
+
+// actions
+- (void)overflowMenuAction:(id)sender;
+- (void)closeTabClick:(id)sender;
+- (void)tabClick:(id)sender;
+- (void)tabNothing:(id)sender;
+
+// notification handlers
+- (void)frameDidChange:(NSNotification *)notification;
+- (void)windowDidMove:(NSNotification *)aNotification;
+- (void)windowDidUpdate:(NSNotification *)notification;
+
+// NSTabView delegate
+- (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem;
+- (BOOL)tabView:(NSTabView *)tabView shouldSelectTabViewItem:(NSTabViewItem *)tabViewItem;
+- (void)tabView:(NSTabView *)tabView willSelectTabViewItem:(NSTabViewItem *)tabViewItem;
+- (void)tabViewDidChangeNumberOfTabViewItems:(NSTabView *)tabView;
+
+// archiving
+- (void)encodeWithCoder:(NSCoder *)aCoder;
+- (id)initWithCoder:(NSCoder *)aDecoder;
+
+// convenience
+- (void)_bindPropertiesForCell:(PSMTabBarCell *)cell andTabViewItem:(NSTabViewItem *)item;
+- (id)cellForPoint:(NSPoint)point cellFrame:(NSRectPointer)outFrame;
+
+- (void)_animateCells:(NSTimer *)timer;
+@end
+
+@implementation PSMTabBarControl
+
+#pragma mark -
+#pragma mark Characteristics
+
++ (NSBundle *)bundle
+{
+ static NSBundle *bundle = nil;
+ if(!bundle) {
+ bundle = [NSBundle bundleForClass:[PSMTabBarControl class]];
+ }
+ return bundle;
+}
+
+/*!
+ @method availableCellWidth
+ @abstract The number of pixels available for cells
+ @discussion Calculates the number of pixels available for cells based on margins and the window resize badge.
+ @returns Returns the amount of space for cells.
+ */
+
+- (CGFloat)availableCellWidth {
+ return [self frame].size.width - [style leftMarginForTabBarControl] - [style rightMarginForTabBarControl] - _resizeAreaCompensation;
+}
+
+/*!
+ @method genericCellRect
+ @abstract The basic rect for a tab cell.
+ @discussion Creates a generic frame for a tab cell based on the current control state.
+ @returns Returns a basic rect for a tab cell.
+ */
+
+- (NSRect)genericCellRect {
+ NSRect aRect = [self frame];
+ aRect.origin.x = [style leftMarginForTabBarControl];
+ aRect.origin.y = 0.0;
+ aRect.size.width = [self availableCellWidth];
+ aRect.size.height = [style tabCellHeight];
+ return aRect;
+}
+
+#pragma mark -
+#pragma mark Constructor/destructor
+
+- (void)initAddedProperties {
+ _cells = [[NSMutableArray alloc] initWithCapacity:10];
+ _controller = [[PSMTabBarController alloc] initWithTabBarControl:self];
+ _animationTimer = nil;
+
+ // default config
+ _currentStep = kPSMIsNotBeingResized;
+ _orientation = PSMTabBarHorizontalOrientation;
+ _canCloseOnlyTab = NO;
+ _disableTabClose = NO;
+ _showAddTabButton = NO;
+ _hideForSingleTab = NO;
+ _sizeCellsToFit = NO;
+ _isHidden = NO;
+ _awakenedFromNib = NO;
+ _automaticallyAnimates = NO;
+ _useOverflowMenu = YES;
+ _allowsBackgroundTabClosing = YES;
+ _allowsResizing = NO;
+ _selectsTabsOnMouseDown = NO;
+ _alwaysShowActiveTab = NO;
+ _allowsScrubbing = NO;
+ _cellMinWidth = 100;
+ _cellMaxWidth = 280;
+ _cellOptimumWidth = 130;
+ _tearOffStyle = PSMTabBarTearOffAlphaWindow;
+ style = [[[[self class] defaultStyleClass] alloc] init];
+
+ // the overflow button/menu
+ NSRect overflowButtonRect = NSMakeRect([self frame].size.width - [style rightMarginForTabBarControl] + 1, 0, [style rightMarginForTabBarControl] - 1, [self frame].size.height);
+ _overflowPopUpButton = [[PSMOverflowPopUpButton alloc] initWithFrame:overflowButtonRect pullsDown:YES];
+ [_overflowPopUpButton setAutoresizingMask:NSViewNotSizable | NSViewMinXMargin];
+ [_overflowPopUpButton setHidden:YES];
+ [self addSubview:_overflowPopUpButton];
+ [self _positionOverflowMenu];
+
+ // new tab button
+ NSRect addTabButtonRect = NSMakeRect([self frame].size.width - [style rightMarginForTabBarControl] + 1, 3.0, 16.0, 16.0);
+ _addTabButton = [[PSMRolloverButton alloc] initWithFrame:addTabButtonRect];
+ if(_addTabButton) {
+ NSImage *newButtonImage = [style addTabButtonImage];
+ if(newButtonImage) {
+ [_addTabButton setUsualImage:newButtonImage];
+ }
+ newButtonImage = [style addTabButtonPressedImage];
+ if(newButtonImage) {
+ [_addTabButton setAlternateImage:newButtonImage];
+ }
+ newButtonImage = [style addTabButtonRolloverImage];
+ if(newButtonImage) {
+ [_addTabButton setRolloverImage:newButtonImage];
+ }
+ [_addTabButton setTitle:@""];
+ [_addTabButton setImagePosition:NSImageOnly];
+ [_addTabButton setButtonType:NSMomentaryChangeButton];
+ [_addTabButton setBordered:NO];
+ [_addTabButton setBezelStyle:NSShadowlessSquareBezelStyle];
+ [self addSubview:_addTabButton];
+
+ if(_showAddTabButton) {
+ [_addTabButton setHidden:NO];
+ } else {
+ [_addTabButton setHidden:YES];
+ }
+ [_addTabButton setNeedsDisplay:YES];
+ }
+}
+
++ (Class) defaultStyleClass
+{
+ return [PSMUnifiedTabStyle class];
+}
+
+- (id)initWithFrame:(NSRect)frame {
+ self = [super initWithFrame:frame];
+ if(self) {
+ // Initialization
+ [self initAddedProperties];
+ [self registerForDraggedTypes:[NSArray arrayWithObjects:@"PSMTabBarControlItemPBType", nil]];
+
+ // resize
+ [self setPostsFrameChangedNotifications:YES];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(frameDidChange:) name:NSViewFrameDidChangeNotification object:self];
+ }
+ [self setTarget:self];
+ return self;
+}
+
+- (void)dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ //stop any animations that may be running
+ [_animationTimer invalidate];
+ [_animationTimer release]; _animationTimer = nil;
+
+ [_showHideAnimationTimer invalidate];
+ [_showHideAnimationTimer release]; _showHideAnimationTimer = nil;
+
+ //Also unwind the spring, if it's wound.
+ [_springTimer invalidate];
+ [_springTimer release]; _springTimer = nil;
+
+ //unbind all the items to prevent crashing
+ //not sure if this is necessary or not
+ // http://code.google.com/p/maccode/issues/detail?id=35
+ NSEnumerator *enumerator = [[[_cells copy] autorelease] objectEnumerator];
+ PSMTabBarCell *nextCell;
+ while((nextCell = [enumerator nextObject])) {
+ [self removeTabForCell:nextCell];
+ }
+
+ [_overflowPopUpButton release];
+ [_cells release];
+ [_controller release];
+ [tabView release];
+ [_addTabButton release];
+ [partnerView release];
+ [_lastMouseDownEvent release];
+ [style release];
+
+ [self unregisterDraggedTypes];
+
+ [super dealloc];
+}
+
+- (void)awakeFromNib {
+ // build cells from existing tab view items
+ NSArray *existingItems = [tabView tabViewItems];
+ NSEnumerator *e = [existingItems objectEnumerator];
+ NSTabViewItem *item;
+ while((item = [e nextObject])) {
+ if(![[self representedTabViewItems] containsObject:item]) {
+ [self addTabViewItem:item];
+ }
+ }
+}
+
+- (void)viewWillMoveToWindow:(NSWindow *)aWindow {
+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+
+ [center removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil];
+ [center removeObserver:self name:NSWindowDidResignKeyNotification object:nil];
+ [center removeObserver:self name:NSWindowDidUpdateNotification object:nil];
+ [center removeObserver:self name:NSWindowDidMoveNotification object:nil];
+
+ if(_showHideAnimationTimer) {
+ [_showHideAnimationTimer invalidate];
+ [_showHideAnimationTimer release]; _showHideAnimationTimer = nil;
+ }
+
+ if(aWindow) {
+ [center addObserver:self selector:@selector(windowStatusDidChange:) name:NSWindowDidBecomeKeyNotification object:aWindow];
+ [center addObserver:self selector:@selector(windowStatusDidChange:) name:NSWindowDidResignKeyNotification object:aWindow];
+ [center addObserver:self selector:@selector(windowDidUpdate:) name:NSWindowDidUpdateNotification object:aWindow];
+ [center addObserver:self selector:@selector(windowDidMove:) name:NSWindowDidMoveNotification object:aWindow];
+ }
+}
+
+- (void)windowStatusDidChange:(NSNotification *)notification {
+ [self setNeedsDisplay:YES];
+}
+
+#pragma mark -
+#pragma mark Accessors
+
+- (NSMutableArray *)cells {
+ return _cells;
+}
+
+- (NSEvent *)lastMouseDownEvent {
+ return _lastMouseDownEvent;
+}
+
+- (void)setLastMouseDownEvent:(NSEvent *)event {
+ [event retain];
+ [_lastMouseDownEvent release];
+ _lastMouseDownEvent = event;
+}
+
+- (id)delegate {
+ return delegate;
+}
+
+- (void)setDelegate:(id)object {
+ delegate = object;
+
+ NSMutableArray *types = [NSMutableArray arrayWithObject:@"PSMTabBarControlItemPBType"];
+
+ //Update the allowed drag types
+ if([self delegate] && [[self delegate] respondsToSelector:@selector(allowedDraggedTypesForTabView:)]) {
+ [types addObjectsFromArray:[[self delegate] allowedDraggedTypesForTabView:tabView]];
+ }
+ [self unregisterDraggedTypes];
+ [self registerForDraggedTypes:types];
+}
+
+- (NSTabView *)tabView {
+ return tabView;
+}
+
+- (void)setTabView:(NSTabView *)view {
+ [view retain];
+ [tabView release];
+ tabView = view;
+}
+
+- (id<PSMTabStyle>)style {
+ return style;
+}
+
+- (NSString *)styleName {
+ return [style name];
+}
+
+- (void)setStyle:(id <PSMTabStyle>)newStyle {
+ if(style != newStyle) {
+ [style autorelease];
+ style = [newStyle retain];
+
+ // restyle add tab button
+ if(_addTabButton) {
+ NSImage *newButtonImage = [style addTabButtonImage];
+ if(newButtonImage) {
+ [_addTabButton setUsualImage:newButtonImage];
+ }
+
+ newButtonImage = [style addTabButtonPressedImage];
+ if(newButtonImage) {
+ [_addTabButton setAlternateImage:newButtonImage];
+ }
+
+ newButtonImage = [style addTabButtonRolloverImage];
+ if(newButtonImage) {
+ [_addTabButton setRolloverImage:newButtonImage];
+ }
+ }
+
+ [self update];
+ }
+}
+
+- (void)setStyleNamed:(NSString *)name {
+
+ Class styleClass = NSClassFromString( [NSString stringWithFormat: @"PSM%@TabStyle", [name capitalizedString]] );
+ if (styleClass == Nil) {
+ styleClass = object_getClass([PSMTabBarControl defaultStyleClass]);
+ }
+
+ id <PSMTabStyle> newStyle = [[styleClass alloc] init];
+ [self setStyle:newStyle];
+ [newStyle release];
+}
+
+- (PSMTabBarOrientation)orientation {
+ return _orientation;
+}
+
+- (void)setOrientation:(PSMTabBarOrientation)value {
+ PSMTabBarOrientation lastOrientation = _orientation;
+ _orientation = value;
+
+ if(_tabBarWidth < 10) {
+ _tabBarWidth = 120;
+ }
+
+ if (lastOrientation != _orientation) {
+ [[self style] setOrientation:_orientation];
+
+ [self _positionOverflowMenu]; //move the overflow popup button to the right place
+ [self update:NO];
+ }
+}
+
+- (BOOL)canCloseOnlyTab {
+ return _canCloseOnlyTab;
+}
+
+- (void)setCanCloseOnlyTab:(BOOL)value {
+ _canCloseOnlyTab = value;
+ if([_cells count] == 1) {
+ [self update];
+ }
+}
+
+- (BOOL)disableTabClose {
+ return _disableTabClose;
+}
+
+- (void)setDisableTabClose:(BOOL)value {
+ _disableTabClose = value;
+ [self update];
+}
+
+- (BOOL)hideForSingleTab {
+ return _hideForSingleTab;
+}
+
+- (void)setHideForSingleTab:(BOOL)value {
+ _hideForSingleTab = value;
+ [self update];
+}
+
+- (BOOL)showAddTabButton {
+ return _showAddTabButton;
+}
+
+- (void)setShowAddTabButton:(BOOL)value {
+ _showAddTabButton = value;
+ if(!NSIsEmptyRect([_controller addButtonRect])) {
+ [_addTabButton setFrame:[_controller addButtonRect]];
+ }
+
+ [_addTabButton setHidden:!_showAddTabButton];
+ [_addTabButton setNeedsDisplay:YES];
+
+ [self update];
+}
+
+- (NSInteger)cellMinWidth {
+ return _cellMinWidth;
+}
+
+- (void)setCellMinWidth:(NSInteger)value {
+ _cellMinWidth = value;
+ [self update];
+}
+
+- (NSInteger)cellMaxWidth {
+ return _cellMaxWidth;
+}
+
+- (void)setCellMaxWidth:(NSInteger)value {
+ _cellMaxWidth = value;
+ [self update];
+}
+
+- (NSInteger)cellOptimumWidth {
+ return _cellOptimumWidth;
+}
+
+- (void)setCellOptimumWidth:(NSInteger)value {
+ _cellOptimumWidth = value;
+ [self update];
+}
+
+- (BOOL)sizeCellsToFit {
+ return _sizeCellsToFit;
+}
+
+- (void)setSizeCellsToFit:(BOOL)value {
+ _sizeCellsToFit = value;
+ [self update];
+}
+
+- (BOOL)useOverflowMenu {
+ return _useOverflowMenu;
+}
+
+- (void)setUseOverflowMenu:(BOOL)value {
+ _useOverflowMenu = value;
+ [self update];
+}
+
+- (PSMRolloverButton *)addTabButton {
+ return _addTabButton;
+}
+
+- (PSMOverflowPopUpButton *)overflowPopUpButton {
+ return _overflowPopUpButton;
+}
+
+- (BOOL)allowsBackgroundTabClosing {
+ return _allowsBackgroundTabClosing;
+}
+
+- (void)setAllowsBackgroundTabClosing:(BOOL)value {
+ _allowsBackgroundTabClosing = value;
+}
+
+- (BOOL)allowsResizing {
+ return _allowsResizing;
+}
+
+- (void)setAllowsResizing:(BOOL)value {
+ _allowsResizing = value;
+}
+
+- (BOOL)selectsTabsOnMouseDown {
+ return _selectsTabsOnMouseDown;
+}
+
+- (void)setSelectsTabsOnMouseDown:(BOOL)value {
+ _selectsTabsOnMouseDown = value;
+}
+
+- (BOOL)automaticallyAnimates {
+ return _automaticallyAnimates;
+}
+
+- (void)setAutomaticallyAnimates:(BOOL)value {
+ _automaticallyAnimates = value;
+}
+
+- (BOOL)alwaysShowActiveTab {
+ return _alwaysShowActiveTab;
+}
+
+- (void)setAlwaysShowActiveTab:(BOOL)value {
+ _alwaysShowActiveTab = value;
+}
+
+- (BOOL)allowsScrubbing {
+ return _allowsScrubbing;
+}
+
+- (void)setAllowsScrubbing:(BOOL)value {
+ _allowsScrubbing = value;
+}
+
+- (PSMTabBarTearOffStyle)tearOffStyle {
+ return _tearOffStyle;
+}
+
+- (void)setTearOffStyle:(PSMTabBarTearOffStyle)tearOffStyle {
+ _tearOffStyle = tearOffStyle;
+}
+
+#pragma mark -
+#pragma mark Functionality
+
+- (void)addTabViewItem:(NSTabViewItem *)item {
+ // create cell
+ PSMTabBarCell *cell = [[PSMTabBarCell alloc] initWithControlView:self];
+ NSRect cellRect, lastCellFrame;
+ if([_cells lastObject] != nil) {
+ cellRect = lastCellFrame = [[_cells lastObject] frame];
+ } else {
+ cellRect = lastCellFrame = NSZeroRect;
+ }
+
+ if([self orientation] == PSMTabBarHorizontalOrientation) {
+ cellRect = [self genericCellRect];
+ cellRect.size.width = 30;
+ cellRect.origin.x = lastCellFrame.origin.x + lastCellFrame.size.width;
+ } else {
+ cellRect = /*lastCellFrame*/ [self genericCellRect];
+ cellRect.size.width = lastCellFrame.size.width;
+ cellRect.size.height = 0;
+ cellRect.origin.y = lastCellFrame.origin.y + lastCellFrame.size.height;
+ }
+
+ [cell setRepresentedObject:item];
+ [cell setFrame:cellRect];
+
+ // bind it up
+ [self bindPropertiesForCell:cell andTabViewItem:item];
+
+ // add to collection
+ [_cells addObject:cell];
+ [cell release];
+ if([_cells count] == (NSUInteger)[tabView numberOfTabViewItems]) {
+ [self update]; // don't update unless all are accounted for!
+ }
+}
+
+- (void)removeTabForCell:(PSMTabBarCell *)cell {
+ NSTabViewItem *item = [cell representedObject];
+
+ // unbind
+ [[cell indicator] unbind:@"animate"];
+ [[cell indicator] unbind:@"hidden"];
+ [cell unbind:@"hasIcon"];
+ [cell unbind:@"hasLargeImage"];
+ [cell unbind:@"title"];
+ [cell unbind:@"count"];
+ [cell unbind:@"countColor"];
+ [cell unbind:@"isEdited"];
+
+ if([item identifier] != nil) {
+ if([[item identifier] respondsToSelector:@selector(isProcessing)]) {
+ [[item identifier] removeObserver:cell forKeyPath:@"isProcessing"];
+ }
+ }
+
+ if([item identifier] != nil) {
+ if([[item identifier] respondsToSelector:@selector(icon)]) {
+ [[item identifier] removeObserver:cell forKeyPath:@"icon"];
+ }
+ }
+
+ if([item identifier] != nil) {
+ if([[item identifier] respondsToSelector:@selector(objectCount)]) {
+ [[item identifier] removeObserver:cell forKeyPath:@"objectCount"];
+ }
+ }
+
+ if([item identifier] != nil) {
+ if([[item identifier] respondsToSelector:@selector(countColor)]) {
+ [[item identifier] removeObserver:cell forKeyPath:@"countColor"];
+ }
+ }
+
+ if([item identifier] != nil) {
+ if([[item identifier] respondsToSelector:@selector(largeImage)]) {
+ [[item identifier] removeObserver:cell forKeyPath:@"largeImage"];
+ }
+ }
+
+ if([item identifier] != nil) {
+ if([[item identifier] respondsToSelector:@selector(isEdited)]) {
+ [[item identifier] removeObserver:cell forKeyPath:@"isEdited"];
+ }
+ }
+
+ // stop watching identifier
+ [item removeObserver:self forKeyPath:@"identifier"];
+
+ // remove indicator
+ if([[self subviews] containsObject:[cell indicator]]) {
+ [[cell indicator] removeFromSuperview];
+ }
+ // remove tracking
+ [[NSNotificationCenter defaultCenter] removeObserver:cell];
+
+ if([cell closeButtonTrackingTag] != 0) {
+ [self removeTrackingRect:[cell closeButtonTrackingTag]];
+ [cell setCloseButtonTrackingTag:0];
+ }
+ if([cell cellTrackingTag] != 0) {
+ [self removeTrackingRect:[cell cellTrackingTag]];
+ [cell setCellTrackingTag:0];
+ }
+
+ // pull from collection
+ [_cells removeObject:cell];
+
+ [self update];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
+ // did the tab's identifier change?
+ if([keyPath isEqualToString:@"identifier"]) {
+ NSEnumerator *e = [_cells objectEnumerator];
+ PSMTabBarCell *cell;
+ while((cell = [e nextObject])) {
+ if([cell representedObject] == object) {
+ [self _bindPropertiesForCell:cell andTabViewItem:object];
+ }
+ }
+ }
+}
+
+#pragma mark -
+#pragma mark Hide/Show
+
+- (void)hideTabBar:(BOOL)hide animate:(BOOL)animate {
+ if(!_awakenedFromNib || (_isHidden && hide) || (!_isHidden && !hide) || (_currentStep != kPSMIsNotBeingResized)) {
+ return;
+ }
+
+ [[self subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
+
+ _isHidden = hide;
+ _currentStep = 0;
+ if(!animate) {
+ _currentStep = (NSInteger)kPSMHideAnimationSteps;
+ }
+
+ if(hide) {
+ [_overflowPopUpButton removeFromSuperview];
+ [_addTabButton removeFromSuperview];
+ } else if(!animate) {
+ [self addSubview:_overflowPopUpButton];
+ [self addSubview:_addTabButton];
+ }
+
+ CGFloat partnerOriginalSize, partnerOriginalOrigin, myOriginalSize, myOriginalOrigin, partnerTargetSize, partnerTargetOrigin, myTargetSize, myTargetOrigin;
+
+ // target values for partner
+ if([self orientation] == PSMTabBarHorizontalOrientation) {
+ // current (original) values
+ myOriginalSize = [self frame].size.height;
+ myOriginalOrigin = [self frame].origin.y;
+ if(partnerView) {
+ partnerOriginalSize = [partnerView frame].size.height;
+ partnerOriginalOrigin = [partnerView frame].origin.y;
+ } else {
+ partnerOriginalSize = [[self window] frame].size.height;
+ partnerOriginalOrigin = [[self window] frame].origin.y;
+ }
+
+ if(partnerView) {
+ // above or below me?
+ if((myOriginalOrigin - 22) > partnerOriginalOrigin) {
+ // partner is below me
+ if(_isHidden) {
+ // I'm shrinking
+ myTargetOrigin = myOriginalOrigin + 21;
+ myTargetSize = myOriginalSize - 21;
+ partnerTargetOrigin = partnerOriginalOrigin;
+ partnerTargetSize = partnerOriginalSize + 21;
+ } else {
+ // I'm growing
+ myTargetOrigin = myOriginalOrigin - 21;
+ myTargetSize = myOriginalSize + 21;
+ partnerTargetOrigin = partnerOriginalOrigin;
+ partnerTargetSize = partnerOriginalSize - 21;
+ }
+ } else {
+ // partner is above me
+ if(_isHidden) {
+ // I'm shrinking
+ myTargetOrigin = myOriginalOrigin;
+ myTargetSize = myOriginalSize - 21;
+ partnerTargetOrigin = partnerOriginalOrigin - 21;
+ partnerTargetSize = partnerOriginalSize + 21;
+ } else {
+ // I'm growing
+ myTargetOrigin = myOriginalOrigin;
+ myTargetSize = myOriginalSize + 21;
+ partnerTargetOrigin = partnerOriginalOrigin + 21;
+ partnerTargetSize = partnerOriginalSize - 21;
+ }
+ }
+ } else {
+ // for window movement
+ if(_isHidden) {
+ // I'm shrinking
+ myTargetOrigin = myOriginalOrigin;
+ myTargetSize = myOriginalSize - 21;
+ partnerTargetOrigin = partnerOriginalOrigin + 21;
+ partnerTargetSize = partnerOriginalSize - 21;
+ } else {
+ // I'm growing
+ myTargetOrigin = myOriginalOrigin;
+ myTargetSize = myOriginalSize + 21;
+ partnerTargetOrigin = partnerOriginalOrigin - 21;
+ partnerTargetSize = partnerOriginalSize + 21;
+ }
+ }
+ } else { /* vertical */
+ // current (original) values
+ myOriginalSize = [self frame].size.width;
+ myOriginalOrigin = [self frame].origin.x;
+ if(partnerView) {
+ partnerOriginalSize = [partnerView frame].size.width;
+ partnerOriginalOrigin = [partnerView frame].origin.x;
+ } else {
+ partnerOriginalSize = [[self window] frame].size.width;
+ partnerOriginalOrigin = [[self window] frame].origin.x;
+ }
+
+ if(partnerView) {
+ //to the left or right?
+ if(myOriginalOrigin < partnerOriginalOrigin + partnerOriginalSize) {
+ // partner is to the left
+ if(_isHidden) {
+ // I'm shrinking
+ myTargetOrigin = myOriginalOrigin;
+ myTargetSize = 1;
+ partnerTargetOrigin = partnerOriginalOrigin - myOriginalSize + 1;
+ partnerTargetSize = partnerOriginalSize + myOriginalSize - 1;
+ _tabBarWidth = myOriginalSize;
+ } else {
+ // I'm growing
+ myTargetOrigin = myOriginalOrigin;
+ myTargetSize = myOriginalSize + _tabBarWidth;
+ partnerTargetOrigin = partnerOriginalOrigin + _tabBarWidth;
+ partnerTargetSize = partnerOriginalSize - _tabBarWidth;
+ }
+ } else {
+ // partner is to the right
+ if(_isHidden) {
+ // I'm shrinking
+ myTargetOrigin = myOriginalOrigin + myOriginalSize;
+ myTargetSize = 1;
+ partnerTargetOrigin = partnerOriginalOrigin;
+ partnerTargetSize = partnerOriginalSize + myOriginalSize;
+ _tabBarWidth = myOriginalSize;
+ } else {
+ // I'm growing
+ myTargetOrigin = myOriginalOrigin - _tabBarWidth;
+ myTargetSize = myOriginalSize + _tabBarWidth;
+ partnerTargetOrigin = partnerOriginalOrigin;
+ partnerTargetSize = partnerOriginalSize - _tabBarWidth;
+ }
+ }
+ } else {
+ // for window movement
+ if(_isHidden) {
+ // I'm shrinking
+ myTargetOrigin = myOriginalOrigin;
+ myTargetSize = 1;
+ partnerTargetOrigin = partnerOriginalOrigin + myOriginalSize - 1;
+ partnerTargetSize = partnerOriginalSize - myOriginalSize + 1;
+ _tabBarWidth = myOriginalSize;
+ } else {
+ // I'm growing
+ myTargetOrigin = myOriginalOrigin;
+ myTargetSize = _tabBarWidth;
+ partnerTargetOrigin = partnerOriginalOrigin - _tabBarWidth + 1;
+ partnerTargetSize = partnerOriginalSize + _tabBarWidth - 1;
+ }
+ }
+
+ if(!_isHidden && [[self delegate] respondsToSelector:@selector(desiredWidthForVerticalTabBar:)]) {
+ myTargetSize = [[self delegate] desiredWidthForVerticalTabBar:self];
+ }
+ }
+
+ NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithDouble:myOriginalOrigin], @"myOriginalOrigin", [NSNumber numberWithDouble:partnerOriginalOrigin], @"partnerOriginalOrigin", [NSNumber numberWithDouble:myOriginalSize], @"myOriginalSize", [NSNumber numberWithDouble:partnerOriginalSize], @"partnerOriginalSize", [NSNumber numberWithDouble:myTargetOrigin], @"myTargetOrigin", [NSNumber numberWithDouble:partnerTargetOrigin], @"partnerTargetOrigin", [NSNumber numberWithDouble:myTargetSize], @"myTargetSize", [NSNumber numberWithDouble:partnerTargetSize], @"partnerTargetSize", nil];
+ if(_showHideAnimationTimer) {
+ [_showHideAnimationTimer invalidate];
+ [_showHideAnimationTimer release];
+ }
+ _showHideAnimationTimer = [[NSTimer scheduledTimerWithTimeInterval:(1.0 / 30.0) target:self selector:@selector(animateShowHide:) userInfo:userInfo repeats:YES] retain];
+}
+
+- (void)animateShowHide:(NSTimer *)timer {
+ // moves the frame of the tab bar and window (or partner view) linearly to hide or show the tab bar
+ NSRect myFrame = [self frame];
+ NSDictionary *userInfo = [timer userInfo];
+ CGFloat myCurrentOrigin = ([[userInfo objectForKey:@"myOriginalOrigin"] doubleValue] + (([[userInfo objectForKey:@"myTargetOrigin"] doubleValue] - [[userInfo objectForKey:@"myOriginalOrigin"] doubleValue]) * (_currentStep / kPSMHideAnimationSteps)));
+ CGFloat myCurrentSize = ([[userInfo objectForKey:@"myOriginalSize"] doubleValue] + (([[userInfo objectForKey:@"myTargetSize"] doubleValue] - [[userInfo objectForKey:@"myOriginalSize"] doubleValue]) * (_currentStep / kPSMHideAnimationSteps)));
+ CGFloat partnerCurrentOrigin = ([[userInfo objectForKey:@"partnerOriginalOrigin"] doubleValue] + (([[userInfo objectForKey:@"partnerTargetOrigin"] doubleValue] - [[userInfo objectForKey:@"partnerOriginalOrigin"] doubleValue]) * (_currentStep / kPSMHideAnimationSteps)));
+ CGFloat partnerCurrentSize = ([[userInfo objectForKey:@"partnerOriginalSize"] doubleValue] + (([[userInfo objectForKey:@"partnerTargetSize"] doubleValue] - [[userInfo objectForKey:@"partnerOriginalSize"] doubleValue]) * (_currentStep / kPSMHideAnimationSteps)));
+
+ NSRect myNewFrame;
+ if([self orientation] == PSMTabBarHorizontalOrientation) {
+ myNewFrame = NSMakeRect(myFrame.origin.x, myCurrentOrigin, myFrame.size.width, myCurrentSize);
+ } else {
+ myNewFrame = NSMakeRect(myCurrentOrigin, myFrame.origin.y, myCurrentSize, myFrame.size.height);
+ }
+
+ if(partnerView) {
+ // resize self and view
+ NSRect resizeRect;
+ if([self orientation] == PSMTabBarHorizontalOrientation) {
+ resizeRect = NSMakeRect([partnerView frame].origin.x, partnerCurrentOrigin, [partnerView frame].size.width, partnerCurrentSize);
+ } else {
+ resizeRect = NSMakeRect(partnerCurrentOrigin, [partnerView frame].origin.y, partnerCurrentSize, [partnerView frame].size.height);
+ }
+ [partnerView setFrame:resizeRect];
+ [partnerView setNeedsDisplay:YES];
+ [self setFrame:myNewFrame];
+ } else {
+ // resize self and window
+ NSRect resizeRect;
+ if([self orientation] == PSMTabBarHorizontalOrientation) {
+ resizeRect = NSMakeRect([[self window] frame].origin.x, partnerCurrentOrigin, [[self window] frame].size.width, partnerCurrentSize);
+ } else {
+ resizeRect = NSMakeRect(partnerCurrentOrigin, [[self window] frame].origin.y, partnerCurrentSize, [[self window] frame].size.height);
+ }
+ [[self window] setFrame:resizeRect display:YES];
+ [self setFrame:myNewFrame];
+ }
+
+ // next
+ _currentStep++;
+ if(_currentStep == kPSMHideAnimationSteps + 1) {
+ _currentStep = kPSMIsNotBeingResized;
+ [self viewDidEndLiveResize];
+ [self update:NO];
+
+ //send the delegate messages
+ if(_isHidden) {
+ if([[self delegate] respondsToSelector:@selector(tabView:tabBarDidHide:)]) {
+ [[self delegate] tabView:[self tabView] tabBarDidHide:self];
+ }
+ } else {
+ [self addSubview:_overflowPopUpButton];
+ [self addSubview:_addTabButton];
+
+ if([[self delegate] respondsToSelector:@selector(tabView:tabBarDidUnhide:)]) {
+ [[self delegate] tabView:[self tabView] tabBarDidUnhide:self];
+ }
+ }
+
+ [_showHideAnimationTimer invalidate];
+ [_showHideAnimationTimer release]; _showHideAnimationTimer = nil;
+ }
+ [[self window] display];
+}
+
+- (BOOL)isTabBarHidden {
+ return _isHidden;
+}
+
+- (BOOL)isAnimating {
+ return _animationTimer != nil;
+}
+
+- (id)partnerView {
+ return partnerView;
+}
+
+- (void)setPartnerView:(id)view {
+ [partnerView release];
+ [view retain];
+ partnerView = view;
+}
+
+#pragma mark -
+#pragma mark Drawing
+
+- (BOOL)isFlipped {
+ return YES;
+}
+
+- (void)drawRect:(NSRect)rect {
+ [style drawTabBar:self inRect:rect];
+}
+
+- (void)update {
+ [self update:_automaticallyAnimates];
+}
+
+- (void)update:(BOOL)animate {
+ // make sure all of our tabs are accounted for before updating
+ if((NSUInteger)[[self tabView] numberOfTabViewItems] != [_cells count]) {
+ return;
+ }
+
+ // hide/show? (these return if already in desired state)
+ if((_hideForSingleTab) && ([_cells count] <= 1)) {
+ [self hideTabBar:YES animate:YES];
+ return;
+ } else {
+ [self hideTabBar:NO animate:YES];
+ }
+
+ [self removeAllToolTips];
+ [_controller layoutCells]; //eventually we should only have to call this when we know something has changed
+
+ PSMTabBarCell *currentCell;
+
+ NSMenu *overflowMenu = [_controller overflowMenu];
+ [_overflowPopUpButton setHidden:(overflowMenu == nil)];
+ [_overflowPopUpButton setMenu:overflowMenu];
+
+ if(_animationTimer) {
+ [_animationTimer invalidate];
+ [_animationTimer release]; _animationTimer = nil;
+ }
+
+ if(animate) {
+ NSMutableArray *targetFrames = [NSMutableArray arrayWithCapacity:[_cells count]];
+
+ for(NSUInteger i = 0; i < [_cells count]; i++) {
+ currentCell = [_cells objectAtIndex:i];
+
+ //we're going from NSRect -> NSValue -> NSRect -> NSValue here - oh well
+ [targetFrames addObject:[NSValue valueWithRect:[_controller cellFrameAtIndex:i]]];
+ }
+
+ [_addTabButton setHidden:!_showAddTabButton];
+
+ NSAnimation *animation = [[NSAnimation alloc] initWithDuration:0.50 animationCurve:NSAnimationEaseInOut];
+ [animation setAnimationBlockingMode:NSAnimationNonblocking];
+ [animation startAnimation];
+ _animationTimer = [[NSTimer scheduledTimerWithTimeInterval:1.0 / 30.0
+ target:self
+ selector:@selector(_animateCells:)
+ userInfo:[NSArray arrayWithObjects:targetFrames, animation, nil]
+ repeats:YES] retain];
+ [animation release];
+ [[NSRunLoop currentRunLoop] addTimer:_animationTimer forMode:NSEventTrackingRunLoopMode];
+ [self _animateCells:_animationTimer];
+ } else {
+ for(NSUInteger i = 0; i < [_cells count]; i++) {
+ currentCell = [_cells objectAtIndex:i];
+ [currentCell setFrame:[_controller cellFrameAtIndex:i]];
+
+ if(![currentCell isInOverflowMenu]) {
+ [self _setupTrackingRectsForCell:currentCell];
+ }
+ }
+
+ [_addTabButton setFrame:[_controller addButtonRect]];
+ [_addTabButton setHidden:!_showAddTabButton];
+ [self setNeedsDisplay:YES];
+ }
+}
+
+- (void)_animateCells:(NSTimer *)timer {
+ NSAnimation *animation = [[timer userInfo] objectAtIndex:1];
+ NSArray *targetFrames = [[timer userInfo] objectAtIndex:0];
+ PSMTabBarCell *currentCell;
+ NSUInteger cellCount = [_cells count];
+
+ if((cellCount > 0) && [animation isAnimating]) {
+ //compare our target position with the current position and move towards the target
+ for(NSUInteger i = 0; i < [targetFrames count] && i < cellCount; i++) {
+ currentCell = [_cells objectAtIndex:i];
+ NSRect cellFrame = [currentCell frame], targetFrame = [[targetFrames objectAtIndex:i] rectValue];
+ CGFloat sizeChange;
+ CGFloat originChange;
+
+ if([self orientation] == PSMTabBarHorizontalOrientation) {
+ sizeChange = (targetFrame.size.width - cellFrame.size.width) * [animation currentProgress];
+ originChange = (targetFrame.origin.x - cellFrame.origin.x) * [animation currentProgress];
+ cellFrame.size.width += sizeChange;
+ cellFrame.origin.x += originChange;
+ } else {
+ sizeChange = (targetFrame.size.height - cellFrame.size.height) * [animation currentProgress];
+ originChange = (targetFrame.origin.y - cellFrame.origin.y) * [animation currentProgress];
+ cellFrame.size.height += sizeChange;
+ cellFrame.origin.y += originChange;
+ }
+
+ [currentCell setFrame:cellFrame];
+
+ //highlight the cell if the mouse is over it
+ NSPoint mousePoint = [self convertPoint:[[self window] mouseLocationOutsideOfEventStream] fromView:nil];
+ NSRect closeRect = [currentCell closeButtonRectForFrame:cellFrame];
+ [currentCell setHighlighted:NSMouseInRect(mousePoint, cellFrame, [self isFlipped])];
+ [currentCell setCloseButtonOver:NSMouseInRect(mousePoint, closeRect, [self isFlipped])];
+ }
+
+ if(_showAddTabButton) {
+ //animate the add tab button
+ NSRect target = [_controller addButtonRect], frame = [_addTabButton frame];
+ frame.origin.x += (target.origin.x - frame.origin.x) * [animation currentProgress];
+ [_addTabButton setFrame:frame];
+ }
+ } else {
+ //put all the cells where they should be in their final position
+ if(cellCount > 0) {
+ for(NSUInteger i = 0; i < [targetFrames count] && i < cellCount; i++) {
+ PSMTabBarCell *currentCell = [_cells objectAtIndex:i];
+ NSRect cellFrame = [currentCell frame], targetFrame = [[targetFrames objectAtIndex:i] rectValue];
+
+ if([self orientation] == PSMTabBarHorizontalOrientation) {
+ cellFrame.size.width = targetFrame.size.width;
+ cellFrame.origin.x = targetFrame.origin.x;
+ } else {
+ cellFrame.size.height = targetFrame.size.height;
+ cellFrame.origin.y = targetFrame.origin.y;
+ }
+
+ [currentCell setFrame:cellFrame];
+
+ //highlight the cell if the mouse is over it
+ NSPoint mousePoint = [self convertPoint:[[self window] mouseLocationOutsideOfEventStream] fromView:nil];
+ NSRect closeRect = [currentCell closeButtonRectForFrame:cellFrame];
+ [currentCell setHighlighted:NSMouseInRect(mousePoint, cellFrame, [self isFlipped])];
+ [currentCell setCloseButtonOver:NSMouseInRect(mousePoint, closeRect, [self isFlipped])];
+ }
+ }
+
+ //set the frame for the add tab button
+ if(_showAddTabButton) {
+ NSRect frame = [_addTabButton frame];
+ frame.origin.x = [_controller addButtonRect].origin.x;
+ [_addTabButton setFrame:frame];
+ }
+
+ [_animationTimer invalidate];
+ [_animationTimer release]; _animationTimer = nil;
+
+ for(NSUInteger i = 0; i < cellCount; i++) {
+ currentCell = [_cells objectAtIndex:i];
+
+ //we've hit the cells that are in overflow, stop setting up tracking rects
+ if([currentCell isInOverflowMenu]) {
+ break;
+ }
+
+ [self _setupTrackingRectsForCell:currentCell];
+ }
+ }
+
+ [self setNeedsDisplay:YES];
+}
+
+- (void)_setupTrackingRectsForCell:(PSMTabBarCell *)cell {
+ NSInteger tag, index = [_cells indexOfObject:cell];
+ NSRect cellTrackingRect = [_controller cellTrackingRectAtIndex:index];
+ NSPoint mousePoint = [self convertPoint:[[self window] mouseLocationOutsideOfEventStream] fromView:nil];
+ BOOL mouseInCell = NSMouseInRect(mousePoint, cellTrackingRect, [self isFlipped]);
+
+ //set the cell tracking rect
+ [self removeTrackingRect:[cell cellTrackingTag]];
+ tag = [self addTrackingRect:cellTrackingRect owner:cell userData:nil assumeInside:mouseInCell];
+ [cell setCellTrackingTag:tag];
+ [cell setHighlighted:mouseInCell];
+
+ if([cell hasCloseButton] && ![cell isCloseButtonSuppressed]) {
+ NSRect closeRect = [_controller closeButtonTrackingRectAtIndex:index];
+ BOOL mouseInCloseRect = NSMouseInRect(mousePoint, closeRect, [self isFlipped]);
+
+ //set the close button tracking rect
+ [self removeTrackingRect:[cell closeButtonTrackingTag]];
+ tag = [self addTrackingRect:closeRect owner:cell userData:nil assumeInside:mouseInCloseRect];
+ [cell setCloseButtonTrackingTag:tag];
+
+ [cell setCloseButtonOver:mouseInCloseRect];
+ }
+
+ //set the tooltip tracking rect
+ [self addToolTipRect:[cell frame] owner:self userData:nil];
+}
+
+- (void)_positionOverflowMenu {
+ NSRect cellRect, frame = [self frame];
+ cellRect.size.height = [style tabCellHeight];
+ cellRect.size.width = [style rightMarginForTabBarControl];
+
+ if([self orientation] == PSMTabBarHorizontalOrientation) {
+ cellRect.origin.y = 0;
+ cellRect.origin.x = frame.size.width - [style rightMarginForTabBarControl] + (_resizeAreaCompensation ? -(_resizeAreaCompensation - 1) : 1);
+ [_overflowPopUpButton setAutoresizingMask:NSViewNotSizable | NSViewMinXMargin];
+ } else {
+ cellRect.origin.x = 0;
+ cellRect.origin.y = frame.size.height - [style tabCellHeight];
+ cellRect.size.width = frame.size.width;
+ [_overflowPopUpButton setAutoresizingMask:NSViewNotSizable | NSViewMinXMargin | NSViewMinYMargin];
+ }
+
+ [_overflowPopUpButton setFrame:cellRect];
+}
+
+- (void)_checkWindowFrame {
+ //figure out if the new frame puts the control in the way of the resize widget
+ NSWindow *window = [self window];
+
+ if(window) {
+ NSRect resizeWidgetFrame = [[window contentView] frame];
+ resizeWidgetFrame.origin.x += resizeWidgetFrame.size.width - 22;
+ resizeWidgetFrame.size.width = 22;
+ resizeWidgetFrame.size.height = 22;
+
+ if([window showsResizeIndicator] && NSIntersectsRect([self frame], resizeWidgetFrame)) {
+ //the resize widgets are larger on metal windows
+ _resizeAreaCompensation = [window styleMask] & NSTexturedBackgroundWindowMask ? 20 : 8;
+ } else {
+ _resizeAreaCompensation = 0;
+ }
+
+ [self _positionOverflowMenu];
+ }
+}
+
+#pragma mark -
+#pragma mark Mouse Tracking
+
+- (BOOL)mouseDownCanMoveWindow {
+ return NO;
+}
+
+- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent {
+ return YES;
+}
+
+- (void)mouseDown:(NSEvent *)theEvent {
+ _didDrag = NO;
+
+ // keep for dragging
+ [self setLastMouseDownEvent:theEvent];
+ // what cell?
+ NSPoint mousePt = [self convertPoint:[theEvent locationInWindow] fromView:nil];
+ NSRect frame = [self frame];
+
+ if([self orientation] == PSMTabBarVerticalOrientation && [self allowsResizing] && partnerView && (mousePt.x > frame.size.width - 3)) {
+ _resizing = YES;
+ }
+
+ NSRect cellFrame;
+ PSMTabBarCell *cell = [self cellForPoint:mousePt cellFrame:&cellFrame];
+ if(cell) {
+ BOOL overClose = NSMouseInRect(mousePt, [cell closeButtonRectForFrame:cellFrame], [self isFlipped]);
+ if(overClose &&
+ ![self disableTabClose] &&
+ ![cell isCloseButtonSuppressed] &&
+ ([self allowsBackgroundTabClosing] || [[cell representedObject] isEqualTo:[tabView selectedTabViewItem]] || [theEvent modifierFlags] & NSCommandKeyMask)) {
+ [cell setCloseButtonOver:NO];
+ [cell setCloseButtonPressed:YES];
+ _closeClicked = YES;
+ } else {
+ [cell setCloseButtonPressed:NO];
+ if(_selectsTabsOnMouseDown) {
+ [self performSelector:@selector(tabClick:) withObject:cell];
+ }
+ }
+ [self setNeedsDisplay:YES];
+ }
+}
+
+- (void)mouseDragged:(NSEvent *)theEvent {
+ if([self lastMouseDownEvent] == nil) {
+ return;
+ }
+
+ NSPoint currentPoint = [self convertPoint:[theEvent locationInWindow] fromView:nil];
+
+ if(_resizing) {
+ NSRect frame = [self frame];
+ CGFloat resizeAmount = [theEvent deltaX];
+ if((currentPoint.x > frame.size.width && resizeAmount > 0) || (currentPoint.x < frame.size.width && resizeAmount < 0)) {
+ [[NSCursor resizeLeftRightCursor] push];
+
+ NSRect partnerFrame = [partnerView frame];
+
+ //do some bounds checking
+ if((frame.size.width + resizeAmount > [self cellMinWidth]) && (frame.size.width + resizeAmount < [self cellMaxWidth])) {
+ frame.size.width += resizeAmount;
+ partnerFrame.size.width -= resizeAmount;
+ partnerFrame.origin.x += resizeAmount;
+
+ [self setFrame:frame];
+ [partnerView setFrame:partnerFrame];
+ [[self superview] setNeedsDisplay:YES];
+ }
+ }
+ return;
+ }
+
+ NSRect cellFrame;
+ NSPoint trackingStartPoint = [self convertPoint:[[self lastMouseDownEvent] locationInWindow] fromView:nil];
+ PSMTabBarCell *cell = [self cellForPoint:trackingStartPoint cellFrame:&cellFrame];
+ if(cell) {
+ //check to see if the close button was the target in the clicked cell
+ //highlight/unhighlight the close button as necessary
+ NSRect iconRect = [cell closeButtonRectForFrame:cellFrame];
+
+ if(_closeClicked && NSMouseInRect(trackingStartPoint, iconRect, [self isFlipped]) &&
+ ([self allowsBackgroundTabClosing] || [[cell representedObject] isEqualTo:[tabView selectedTabViewItem]])) {
+ [cell setCloseButtonPressed:NSMouseInRect(currentPoint, iconRect, [self isFlipped])];
+ [self setNeedsDisplay:YES];
+ return;
+ }
+
+ CGFloat dx = fabs(currentPoint.x - trackingStartPoint.x);
+ CGFloat dy = fabs(currentPoint.y - trackingStartPoint.y);
+ CGFloat distance = sqrt(dx * dx + dy * dy);
+
+ if(distance >= 10 && !_didDrag && ![[PSMTabDragAssistant sharedDragAssistant] isDragging] &&
+ [self delegate] && [[self delegate] respondsToSelector:@selector(tabView:shouldDragTabViewItem:fromTabBar:)] &&
+ [[self delegate] tabView:tabView shouldDragTabViewItem:[cell representedObject] fromTabBar:self]) {
+ _didDrag = YES;
+ [[PSMTabDragAssistant sharedDragAssistant] startDraggingCell:cell fromTabBar:self withMouseDownEvent:[self lastMouseDownEvent]];
+ }
+ }
+}
+
+- (void)mouseUp:(NSEvent *)theEvent {
+ if(_resizing) {
+ _resizing = NO;
+ [[NSCursor arrowCursor] set];
+ } else {
+ // what cell?
+ NSPoint mousePt = [self convertPoint:[theEvent locationInWindow] fromView:nil];
+ NSRect cellFrame, mouseDownCellFrame;
+ PSMTabBarCell *cell = [self cellForPoint:mousePt cellFrame:&cellFrame];
+ PSMTabBarCell *mouseDownCell = [self cellForPoint:[self convertPoint:[[self lastMouseDownEvent] locationInWindow] fromView:nil] cellFrame:&mouseDownCellFrame];
+ if(cell) {
+ NSPoint trackingStartPoint = [self convertPoint:[[self lastMouseDownEvent] locationInWindow] fromView:nil];
+ NSRect iconRect = [mouseDownCell closeButtonRectForFrame:mouseDownCellFrame];
+
+ if((NSMouseInRect(mousePt, iconRect, [self isFlipped])) && ![self disableTabClose] && ![cell isCloseButtonSuppressed] && [mouseDownCell closeButtonPressed]) {
+ if(([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0) {
+ //If the user is holding Option, close all other tabs
+ NSEnumerator *enumerator = [[[[self cells] copy] autorelease] objectEnumerator];
+ PSMTabBarCell *otherCell;
+
+ while((otherCell = [enumerator nextObject])) {
+ if(otherCell != cell) {
+ [self performSelector:@selector(closeTabClick:) withObject:otherCell];
+ }
+ }
+
+ //Fix the close button for the clicked tab not to be pressed
+ [cell setCloseButtonPressed:NO];
+ } else {
+ //Otherwise, close this tab
+ [self performSelector:@selector(closeTabClick:) withObject:cell];
+ }
+ } else if(NSMouseInRect(mousePt, mouseDownCellFrame, [self isFlipped]) &&
+ (!NSMouseInRect(trackingStartPoint, [cell closeButtonRectForFrame:cellFrame], [self isFlipped]) || ![self allowsBackgroundTabClosing] || [self disableTabClose])) {
+ [mouseDownCell setCloseButtonPressed:NO];
+ // If -[self selectsTabsOnMouseDown] is TRUE, we already performed tabClick: on mouseDown.
+ if(![self selectsTabsOnMouseDown]) {
+ [self performSelector:@selector(tabClick:) withObject:cell];
+ }
+ } else {
+ [mouseDownCell setCloseButtonPressed:NO];
+ [self performSelector:@selector(tabNothing:) withObject:cell];
+ }
+ }
+
+ _closeClicked = NO;
+ }
+}
+
+- (NSMenu *)menuForEvent:(NSEvent *)event {
+ NSMenu *menu = nil;
+ NSTabViewItem *item = [[self cellForPoint:[self convertPoint:[event locationInWindow] fromView:nil] cellFrame:nil] representedObject];
+
+ if(item && [[self delegate] respondsToSelector:@selector(tabView:menuForTabViewItem:)]) {
+ menu = [[self delegate] tabView:tabView menuForTabViewItem:item];
+ }
+ return menu;
+}
+
+#pragma mark -
+#pragma mark Drag and Drop
+
+- (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)theEvent {
+ return YES;
+}
+
+// NSDraggingSource
+- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal {
+ return(isLocal ? NSDragOperationMove : NSDragOperationNone);
+}
+
+- (BOOL)ignoreModifierKeysWhileDragging {
+ return YES;
+}
+
+- (void)draggedImage:(NSImage *)anImage beganAt:(NSPoint)screenPoint {
+ [[PSMTabDragAssistant sharedDragAssistant] draggingBeganAt:screenPoint];
+}
+
+- (void)draggedImage:(NSImage *)image movedTo:(NSPoint)screenPoint {
+ [[PSMTabDragAssistant sharedDragAssistant] draggingMovedTo:screenPoint];
+}
+
+// NSDraggingDestination
+- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender {
+ if([[[sender draggingPasteboard] types] indexOfObject:@"PSMTabBarControlItemPBType"] != NSNotFound) {
+ if([self delegate] && [[self delegate] respondsToSelector:@selector(tabView:shouldDropTabViewItem:inTabBar:)] &&
+ ![[self delegate] tabView:[[sender draggingSource] tabView] shouldDropTabViewItem:[[[PSMTabDragAssistant sharedDragAssistant] draggedCell] representedObject] inTabBar:self]) {
+ return NSDragOperationNone;
+ }
+
+ [[PSMTabDragAssistant sharedDragAssistant] draggingEnteredTabBar:self atPoint:[self convertPoint:[sender draggingLocation] fromView:nil]];
+ return NSDragOperationMove;
+ }
+
+ return NSDragOperationNone;
+}
+
+- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender {
+ PSMTabBarCell *cell = [self cellForPoint:[self convertPoint:[sender draggingLocation] fromView:nil] cellFrame:nil];
+
+ if([[[sender draggingPasteboard] types] indexOfObject:@"PSMTabBarControlItemPBType"] != NSNotFound) {
+ if([self delegate] && [[self delegate] respondsToSelector:@selector(tabView:shouldDropTabViewItem:inTabBar:)] &&
+ ![[self delegate] tabView:[[sender draggingSource] tabView] shouldDropTabViewItem:[[[PSMTabDragAssistant sharedDragAssistant] draggedCell] representedObject] inTabBar:self]) {
+ return NSDragOperationNone;
+ }
+
+ [[PSMTabDragAssistant sharedDragAssistant] draggingUpdatedInTabBar:self atPoint:[self convertPoint:[sender draggingLocation] fromView:nil]];
+ return NSDragOperationMove;
+ } else if(cell) {
+ //something that was accepted by the delegate was dragged on
+
+ //Test for the space bar (the skip-the-delay key).
+ /*enum { virtualKeycodeForSpace = 49 }; //Source: IM:Tx (Fig. C-2)
+ union {
+ KeyMap keymap;
+ char bits[16];
+ } keymap;
+ GetKeys(keymap.keymap);
+ if ((GetCurrentEventKeyModifiers() == 0) && bit_test(keymap.bits, virtualKeycodeForSpace)) {
+ //The user pressed the space bar. This skips the delay; the user wants to pop the spring on this tab *now*.
+
+ //For some reason, it crashes if I call -fire here. I don't know why. It doesn't crash if I simply set the fire date to now.
+ [_springTimer setFireDate:[NSDate date]];
+ } else {*/
+ //Wind the spring for a spring-loaded drop.
+ //The delay time comes from Finder's defaults, which specifies it in milliseconds.
+ //If the delegate can't handle our spring-loaded drop, we'll abort it when the timer fires. See fireSpring:. This is simpler than constantly (checking for spring-loaded awareness and tearing down/rebuilding the timer) at every delegate change.
+
+ //If the user has dragged to a different tab, reset the timer.
+ if(_tabViewItemWithSpring != [cell representedObject]) {
+ [_springTimer invalidate];
+ [_springTimer release]; _springTimer = nil;
+ _tabViewItemWithSpring = [cell representedObject];
+ }
+ if(!_springTimer) {
+ //Finder's default delay time, as of Tiger, is 668 ms. If the user has never changed it, there's no setting in its defaults, so we default to that amount.
+ NSNumber *delayNumber = [(NSNumber *)CFPreferencesCopyAppValue((CFStringRef)@"SpringingDelayMilliseconds", (CFStringRef)@"com.apple.finder") autorelease];
+ NSTimeInterval delaySeconds = delayNumber ?[delayNumber doubleValue] / 1000.0 : 0.668;
+ _springTimer = [[NSTimer scheduledTimerWithTimeInterval:delaySeconds
+ target:self
+ selector:@selector(fireSpring:)
+ userInfo:sender
+ repeats:NO] retain];
+ }
+ return NSDragOperationCopy;
+ }
+
+ return NSDragOperationNone;
+}
+
+- (void)draggingExited:(id <NSDraggingInfo>)sender {
+ [_springTimer invalidate];
+ [_springTimer release]; _springTimer = nil;
+
+ [[PSMTabDragAssistant sharedDragAssistant] draggingExitedTabBar:self];
+}
+
+- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender {
+ //validate the drag operation only if there's a valid tab bar to drop into
+ return [[[sender draggingPasteboard] types] indexOfObject:@"PSMTabBarControlItemPBType"] == NSNotFound ||
+ [[PSMTabDragAssistant sharedDragAssistant] destinationTabBar] != nil;
+}
+
+- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
+ if([[[sender draggingPasteboard] types] indexOfObject:@"PSMTabBarControlItemPBType"] != NSNotFound) {
+ [[PSMTabDragAssistant sharedDragAssistant] performDragOperation];
+ } else if([self delegate] && [[self delegate] respondsToSelector:@selector(tabView:acceptedDraggingInfo:onTabViewItem:)]) {
+ //forward the drop to the delegate
+ [[self delegate] tabView:tabView acceptedDraggingInfo:sender onTabViewItem:[[self cellForPoint:[self convertPoint:[sender draggingLocation] fromView:nil] cellFrame:nil] representedObject]];
+ }
+ return YES;
+}
+
+- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation {
+ [[PSMTabDragAssistant sharedDragAssistant] draggedImageEndedAt:aPoint operation:operation];
+}
+
+- (void)concludeDragOperation:(id <NSDraggingInfo>)sender {
+}
+
+#pragma mark -
+#pragma mark Spring-loading
+
+- (void)fireSpring:(NSTimer *)timer {
+ NSAssert1(timer == _springTimer, @"Spring fired by unrecognized timer %@", timer);
+
+ id <NSDraggingInfo> sender = [timer userInfo];
+ PSMTabBarCell *cell = [self cellForPoint:[self convertPoint:[sender draggingLocation] fromView:nil] cellFrame:nil];
+ [tabView selectTabViewItem:[cell representedObject]];
+
+ _tabViewItemWithSpring = nil;
+ [_springTimer invalidate];
+ [_springTimer release]; _springTimer = nil;
+}
+
+#pragma mark -
+#pragma mark Actions
+
+- (void)overflowMenuAction:(id)sender {
+ NSTabViewItem *tabViewItem = (NSTabViewItem *)[sender representedObject];
+ [tabView selectTabViewItem:tabViewItem];
+}
+
+- (void)closeTabClick:(id)sender {
+ NSTabViewItem *item = [sender representedObject];
+ [sender retain];
+ if(([_cells count] == 1) && (![self canCloseOnlyTab])) {
+ return;
+ }
+
+ if([[self delegate] respondsToSelector:@selector(tabView:shouldCloseTabViewItem:)]) {
+ if(![[self delegate] tabView:tabView shouldCloseTabViewItem:item]) {
+ // fix mouse downed close button
+ [sender setCloseButtonPressed:NO];
+ return;
+ }
+ }
+
+ [item retain];
+
+ [tabView removeTabViewItem:item];
+ [item release];
+ [sender release];
+}
+
+- (void)tabClick:(id)sender {
+ [tabView selectTabViewItem:[sender representedObject]];
+}
+
+- (void)tabNothing:(id)sender {
+ //[self update]; // takes care of highlighting based on state
+}
+
+- (void)frameDidChange:(NSNotification *)notification {
+ [self _checkWindowFrame];
+
+ // trying to address the drawing artifacts for the progress indicators - hackery follows
+ // this one fixes the "blanking" effect when the control hides and shows itself
+ NSEnumerator *e = [_cells objectEnumerator];
+ PSMTabBarCell *cell;
+ while((cell = [e nextObject])) {
+ [[cell indicator] stopAnimation:self];
+
+ [[cell indicator] performSelector:@selector(startAnimation:)
+ withObject:nil
+ afterDelay:0];
+ }
+
+ [self update:NO];
+}
+
+- (void)viewDidMoveToWindow {
+ [self _checkWindowFrame];
+}
+
+- (void)viewWillStartLiveResize {
+ NSEnumerator *e = [_cells objectEnumerator];
+ PSMTabBarCell *cell;
+ while((cell = [e nextObject])) {
+ [[cell indicator] stopAnimation:self];
+ }
+ [self setNeedsDisplay:YES];
+}
+
+-(void)viewDidEndLiveResize {
+ NSEnumerator *e = [_cells objectEnumerator];
+ PSMTabBarCell *cell;
+ while((cell = [e nextObject])) {
+ [[cell indicator] startAnimation:self];
+ }
+
+ [self _checkWindowFrame];
+ [self update:NO];
+}
+
+- (void)resetCursorRects {
+ [super resetCursorRects];
+ if([self orientation] == PSMTabBarVerticalOrientation) {
+ NSRect frame = [self frame];
+ [self addCursorRect:NSMakeRect(frame.size.width - 2, 0, 2, frame.size.height) cursor:[NSCursor resizeLeftRightCursor]];
+ }
+}
+
+- (void)windowDidMove:(NSNotification *)aNotification {
+ [self setNeedsDisplay:YES];
+}
+
+- (void)windowDidUpdate:(NSNotification *)notification {
+ // hide? must readjust things if I'm not supposed to be showing
+ // this block of code only runs when the app launches
+ if([self hideForSingleTab] && ([_cells count] <= 1) && !_awakenedFromNib) {
+ // must adjust frames now before display
+ NSRect myFrame = [self frame];
+ if([self orientation] == PSMTabBarHorizontalOrientation) {
+ if(partnerView) {
+ NSRect partnerFrame = [partnerView frame];
+ // above or below me?
+ if(myFrame.origin.y - 22 > [partnerView frame].origin.y) {
+ // partner is below me
+ [self setFrame:NSMakeRect(myFrame.origin.x, myFrame.origin.y + 21, myFrame.size.width, myFrame.size.height - 21)];
+ [partnerView setFrame:NSMakeRect(partnerFrame.origin.x, partnerFrame.origin.y, partnerFrame.size.width, partnerFrame.size.height + 21)];
+ } else {
+ // partner is above me
+ [self setFrame:NSMakeRect(myFrame.origin.x, myFrame.origin.y, myFrame.size.width, myFrame.size.height - 21)];
+ [partnerView setFrame:NSMakeRect(partnerFrame.origin.x, partnerFrame.origin.y - 21, partnerFrame.size.width, partnerFrame.size.height + 21)];
+ }
+ [partnerView setNeedsDisplay:YES];
+ [self setNeedsDisplay:YES];
+ } else {
+ // for window movement
+ NSRect windowFrame = [[self window] frame];
+ [[self window] setFrame:NSMakeRect(windowFrame.origin.x, windowFrame.origin.y + 21, windowFrame.size.width, windowFrame.size.height - 21) display:YES];
+ [self setFrame:NSMakeRect(myFrame.origin.x, myFrame.origin.y, myFrame.size.width, myFrame.size.height - 21)];
+ }
+ } else {
+ if(partnerView) {
+ NSRect partnerFrame = [partnerView frame];
+ //to the left or right?
+ if(myFrame.origin.x < [partnerView frame].origin.x) {
+ // partner is to the left
+ [self setFrame:NSMakeRect(myFrame.origin.x, myFrame.origin.y, 1, myFrame.size.height)];
+ [partnerView setFrame:NSMakeRect(partnerFrame.origin.x - myFrame.size.width + 1, partnerFrame.origin.y, partnerFrame.size.width + myFrame.size.width - 1, partnerFrame.size.height)];
+ } else {
+ // partner to the right
+ [self setFrame:NSMakeRect(myFrame.origin.x + myFrame.size.width, myFrame.origin.y, 1, myFrame.size.height)];
+ [partnerView setFrame:NSMakeRect(partnerFrame.origin.x, partnerFrame.origin.y, partnerFrame.size.width + myFrame.size.width, partnerFrame.size.height)];
+ }
+ _tabBarWidth = myFrame.size.width;
+ [partnerView setNeedsDisplay:YES];
+ [self setNeedsDisplay:YES];
+ } else {
+ // for window movement
+ NSRect windowFrame = [[self window] frame];
+ [[self window] setFrame:NSMakeRect(windowFrame.origin.x + myFrame.size.width - 1, windowFrame.origin.y, windowFrame.size.width - myFrame.size.width + 1, windowFrame.size.height) display:YES];
+ [self setFrame:NSMakeRect(myFrame.origin.x, myFrame.origin.y, 1, myFrame.size.height)];
+ }
+ }
+
+ _isHidden = YES;
+
+ if([[self delegate] respondsToSelector:@selector(tabView:tabBarDidHide:)]) {
+ [[self delegate] tabView:[self tabView] tabBarDidHide:self];
+ }
+ }
+
+ _awakenedFromNib = YES;
+ [self setNeedsDisplay:YES];
+
+ //we only need to do this once
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidUpdateNotification object:nil];
+}
+
+#pragma mark -
+#pragma mark Menu Validation
+
+- (BOOL)validateMenuItem:(NSMenuItem *)sender {
+ [sender setState:([[sender representedObject] isEqualTo:[tabView selectedTabViewItem]]) ? NSOnState : NSOffState];
+
+ return [[self delegate] respondsToSelector:@selector(tabView:validateOverflowMenuItem:forTabViewItem:)] ?
+ [[self delegate] tabView:[self tabView] validateOverflowMenuItem:sender forTabViewItem:[sender representedObject]] : YES;
+}
+
+#pragma mark -
+#pragma mark NSTabView Delegate
+
+- (void)tabView:(NSTabView *)aTabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem {
+ // here's a weird one - this message is sent before the "tabViewDidChangeNumberOfTabViewItems"
+ // message, thus I can end up updating when there are no cells, if no tabs were (yet) present
+ NSUInteger tabIndex = [aTabView indexOfTabViewItem:tabViewItem];
+
+ if([_cells count] > 0 && tabIndex < [_cells count]) {
+ PSMTabBarCell *thisCell = [_cells objectAtIndex:tabIndex];
+ if(_alwaysShowActiveTab && [thisCell isInOverflowMenu]) {
+ //temporarily disable the delegate in order to move the tab to a different index
+ id tempDelegate = [aTabView delegate];
+ [aTabView setDelegate:nil];
+
+ // move it all around first
+ [tabViewItem retain];
+ [thisCell retain];
+ [aTabView removeTabViewItem:tabViewItem];
+ [aTabView insertTabViewItem:tabViewItem atIndex:0];
+ [_cells removeObjectAtIndex:tabIndex];
+ [_cells insertObject:thisCell atIndex:0];
+ [thisCell setIsInOverflowMenu:NO]; //very important else we get a fun recursive loop going
+ [[_cells objectAtIndex:[_cells count] - 1] setIsInOverflowMenu:YES]; //these 2 lines are pretty uncool and this logic needs to be updated
+ [thisCell release];
+ [tabViewItem release];
+
+ [aTabView setDelegate:tempDelegate];
+
+ //reset the selection since removing it changed the selection
+ [aTabView selectTabViewItem:tabViewItem];
+
+ [self update];
+ } else {
+ [_controller setSelectedCell:thisCell];
+ [self setNeedsDisplay:YES];
+ }
+ }
+
+ if([[self delegate] respondsToSelector:@selector(tabView:didSelectTabViewItem:)]) {
+ [[self delegate] performSelector:@selector(tabView:didSelectTabViewItem:) withObject:aTabView withObject:tabViewItem];
+ }
+}
+
+- (BOOL)tabView:(NSTabView *)aTabView shouldSelectTabViewItem:(NSTabViewItem *)tabViewItem {
+ if([[self delegate] respondsToSelector:@selector(tabView:shouldSelectTabViewItem:)]) {
+ return [[self delegate] tabView:aTabView shouldSelectTabViewItem:tabViewItem];
+ } else {
+ return YES;
+ }
+}
+- (void)tabView:(NSTabView *)aTabView willSelectTabViewItem:(NSTabViewItem *)tabViewItem {
+ if([[self delegate] respondsToSelector:@selector(tabView:willSelectTabViewItem:)]) {
+ [[self delegate] performSelector:@selector(tabView:willSelectTabViewItem:) withObject:aTabView withObject:tabViewItem];
+ }
+}
+
+- (void)tabViewDidChangeNumberOfTabViewItems:(NSTabView *)aTabView {
+ NSArray *tabItems = [tabView tabViewItems];
+ // go through cells, remove any whose representedObjects are not in [tabView tabViewItems]
+ NSEnumerator *e = [[[_cells copy] autorelease] objectEnumerator];
+ PSMTabBarCell *cell;
+ while((cell = [e nextObject])) {
+ //remove the observer binding
+ if([cell representedObject] && ![tabItems containsObject:[cell representedObject]]) {
+ if([[self delegate] respondsToSelector:@selector(tabView:didCloseTabViewItem:)]) {
+ [[self delegate] tabView:aTabView didCloseTabViewItem:[cell representedObject]];
+ }
+
+ [self removeTabForCell:cell];
+ }
+ }
+
+ // go through tab view items, add cell for any not present
+ NSMutableArray *cellItems = [self representedTabViewItems];
+ NSEnumerator *ex = [tabItems objectEnumerator];
+ NSTabViewItem *item;
+ while((item = [ex nextObject])) {
+ if(![cellItems containsObject:item]) {
+ [self addTabViewItem:item];
+ }
+ }
+
+ // pass along for other delegate responses
+ if([[self delegate] respondsToSelector:@selector(tabViewDidChangeNumberOfTabViewItems:)]) {
+ [[self delegate] performSelector:@selector(tabViewDidChangeNumberOfTabViewItems:) withObject:aTabView];
+ }
+
+ // reset cursor tracking for the add tab button if one exists
+ if([self addTabButton]) {
+ [[self addTabButton] resetCursorRects];
+ }
+}
+
+#pragma mark -
+#pragma mark Tooltips
+
+- (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)userData {
+ if([[self delegate] respondsToSelector:@selector(tabView:toolTipForTabViewItem:)]) {
+ return [[self delegate] tabView:[self tabView] toolTipForTabViewItem:[[self cellForPoint:point cellFrame:nil] representedObject]];
+ }
+ return nil;
+}
+
+#pragma mark -
+#pragma mark Archiving
+
+- (void)encodeWithCoder:(NSCoder *)aCoder
+{
+ [super encodeWithCoder:aCoder];
+ if ([aCoder allowsKeyedCoding]) {
+ [aCoder encodeObject:_cells forKey:@"PSMcells"];
+ [aCoder encodeObject:tabView forKey:@"PSMtabView"];
+ [aCoder encodeObject:_overflowPopUpButton forKey:@"PSMoverflowPopUpButton"];
+ [aCoder encodeObject:_addTabButton forKey:@"PSMaddTabButton"];
+ [aCoder encodeObject:style forKey:@"PSMstyle"];
+ [aCoder encodeInteger:_orientation forKey:@"PSMorientation"];
+ [aCoder encodeBool:_canCloseOnlyTab forKey:@"PSMcanCloseOnlyTab"];
+ [aCoder encodeBool:_disableTabClose forKey:@"PSMdisableTabClose"];
+ [aCoder encodeBool:_hideForSingleTab forKey:@"PSMhideForSingleTab"];
+ [aCoder encodeBool:_allowsBackgroundTabClosing forKey:@"PSMallowsBackgroundTabClosing"];
+ [aCoder encodeBool:_allowsResizing forKey:@"PSMallowsResizing"];
+ [aCoder encodeBool:_selectsTabsOnMouseDown forKey:@"PSMselectsTabsOnMouseDown"];
+ [aCoder encodeBool:_showAddTabButton forKey:@"PSMshowAddTabButton"];
+ [aCoder encodeBool:_sizeCellsToFit forKey:@"PSMsizeCellsToFit"];
+ [aCoder encodeInteger:_cellMinWidth forKey:@"PSMcellMinWidth"];
+ [aCoder encodeInteger:_cellMaxWidth forKey:@"PSMcellMaxWidth"];
+ [aCoder encodeInteger:_cellOptimumWidth forKey:@"PSMcellOptimumWidth"];
+ [aCoder encodeInteger:_currentStep forKey:@"PSMcurrentStep"];
+ [aCoder encodeBool:_isHidden forKey:@"PSMisHidden"];
+ [aCoder encodeObject:partnerView forKey:@"PSMpartnerView"];
+ [aCoder encodeBool:_awakenedFromNib forKey:@"PSMawakenedFromNib"];
+ [aCoder encodeObject:_lastMouseDownEvent forKey:@"PSMlastMouseDownEvent"];
+ [aCoder encodeObject:delegate forKey:@"PSMdelegate"];
+ [aCoder encodeBool:_useOverflowMenu forKey:@"PSMuseOverflowMenu"];
+ [aCoder encodeBool:_automaticallyAnimates forKey:@"PSMautomaticallyAnimates"];
+ [aCoder encodeBool:_alwaysShowActiveTab forKey:@"PSMalwaysShowActiveTab"];
+ }
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+ self = [super initWithCoder:aDecoder];
+ if (self) {
+ // Initialization
+ [self initAddedProperties];
+ [self registerForDraggedTypes:[NSArray arrayWithObjects:@"PSMTabBarControlItemPBType", nil]];
+
+ // resize
+ [self setPostsFrameChangedNotifications:YES];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(frameDidChange:) name:NSViewFrameDidChangeNotification object:self];
+ if ([aDecoder allowsKeyedCoding]) {
+ _cells = [[aDecoder decodeObjectForKey:@"PSMcells"] retain];
+ tabView = [[aDecoder decodeObjectForKey:@"PSMtabView"] retain];
+ _overflowPopUpButton = [[aDecoder decodeObjectForKey:@"PSMoverflowPopUpButton"] retain];
+ _addTabButton = [[aDecoder decodeObjectForKey:@"PSMaddTabButton"] retain];
+ style = [[aDecoder decodeObjectForKey:@"PSMstyle"] retain];
+ _orientation = (PSMTabBarOrientation)[aDecoder decodeIntegerForKey:@"PSMorientation"];
+ _canCloseOnlyTab = [aDecoder decodeBoolForKey:@"PSMcanCloseOnlyTab"];
+ _disableTabClose = [aDecoder decodeBoolForKey:@"PSMdisableTabClose"];
+ _hideForSingleTab = [aDecoder decodeBoolForKey:@"PSMhideForSingleTab"];
+ _allowsBackgroundTabClosing = [aDecoder decodeBoolForKey:@"PSMallowsBackgroundTabClosing"];
+ _allowsResizing = [aDecoder decodeBoolForKey:@"PSMallowsResizing"];
+ _selectsTabsOnMouseDown = [aDecoder decodeBoolForKey:@"PSMselectsTabsOnMouseDown"];
+ _showAddTabButton = [aDecoder decodeBoolForKey:@"PSMshowAddTabButton"];
+ _sizeCellsToFit = [aDecoder decodeBoolForKey:@"PSMsizeCellsToFit"];
+ _cellMinWidth = [aDecoder decodeIntegerForKey:@"PSMcellMinWidth"];
+ _cellMaxWidth = [aDecoder decodeIntegerForKey:@"PSMcellMaxWidth"];
+ _cellOptimumWidth = [aDecoder decodeIntegerForKey:@"PSMcellOptimumWidth"];
+ _currentStep = [aDecoder decodeIntegerForKey:@"PSMcurrentStep"];
+ _isHidden = [aDecoder decodeBoolForKey:@"PSMisHidden"];
+ partnerView = [[aDecoder decodeObjectForKey:@"PSMpartnerView"] retain];
+ _awakenedFromNib = [aDecoder decodeBoolForKey:@"PSMawakenedFromNib"];
+ _lastMouseDownEvent = [[aDecoder decodeObjectForKey:@"PSMlastMouseDownEvent"] retain];
+ _useOverflowMenu = [aDecoder decodeBoolForKey:@"PSMuseOverflowMenu"];
+ _automaticallyAnimates = [aDecoder decodeBoolForKey:@"PSMautomaticallyAnimates"];
+ _alwaysShowActiveTab = [aDecoder decodeBoolForKey:@"PSMalwaysShowActiveTab"];
+ delegate = [[aDecoder decodeObjectForKey:@"PSMdelegate"] retain];
+ }
+ }
+ [self setTarget:self];
+ return self;
+}
+
+#pragma mark -
+#pragma mark IB Palette
+
+- (NSSize)minimumFrameSizeFromKnobPosition:(NSInteger)position {
+ return NSMakeSize(100.0, 22.0);
+}
+
+- (NSSize)maximumFrameSizeFromKnobPosition:(NSInteger)knobPosition {
+ return NSMakeSize(10000.0, 22.0);
+}
+
+- (void)placeView:(NSRect)newFrame {
+ // this is called any time the view is resized in IB
+ [self setFrame:newFrame];
+ [self update:NO];
+}
+
+#pragma mark -
+#pragma mark Convenience
+
+- (void)bindPropertiesForCell:(PSMTabBarCell *)cell andTabViewItem:(NSTabViewItem *)item {
+ [self _bindPropertiesForCell:cell andTabViewItem:item];
+
+ // watch for changes in the identifier
+ [item addObserver:self forKeyPath:@"identifier" options:0 context:nil];
+}
+
+- (void)_bindPropertiesForCell:(PSMTabBarCell *)cell andTabViewItem:(NSTabViewItem *)item {
+ // bind the indicator to the represented object's status (if it exists)
+ [[cell indicator] setHidden:YES];
+ if([item identifier] != nil) {
+ if([[[cell representedObject] identifier] respondsToSelector:@selector(isProcessing)]) {
+ NSMutableDictionary *bindingOptions = [NSMutableDictionary dictionary];
+ [bindingOptions setObject:NSNegateBooleanTransformerName forKey:@"NSValueTransformerName"];
+ [[cell indicator] bind:@"animate" toObject:[item identifier] withKeyPath:@"isProcessing" options:nil];
+ [[cell indicator] bind:@"hidden" toObject:[item identifier] withKeyPath:@"isProcessing" options:bindingOptions];
+ [[item identifier] addObserver:cell forKeyPath:@"isProcessing" options:0 context:nil];
+ }
+ }
+
+ // bind for the existence of an icon
+ [cell setHasIcon:NO];
+ if([item identifier] != nil) {
+ if([[[cell representedObject] identifier] respondsToSelector:@selector(icon)]) {
+ NSMutableDictionary *bindingOptions = [NSMutableDictionary dictionary];
+ [bindingOptions setObject:NSIsNotNilTransformerName forKey:@"NSValueTransformerName"];
+ [cell bind:@"hasIcon" toObject:[item identifier] withKeyPath:@"icon" options:bindingOptions];
+ [[item identifier] addObserver:cell forKeyPath:@"icon" options:0 context:nil];
+ }
+ }
+
+ // bind for the existence of a counter
+ [cell setCount:0];
+ if([item identifier] != nil) {
+ if([[[cell representedObject] identifier] respondsToSelector:@selector(objectCount)]) {
+ [cell bind:@"count" toObject:[item identifier] withKeyPath:@"objectCount" options:nil];
+ [[item identifier] addObserver:cell forKeyPath:@"objectCount" options:0 context:nil];
+ }
+ }
+
+ // bind for the color of a counter
+ [cell setCountColor:nil];
+ if([item identifier] != nil) {
+ if([[[cell representedObject] identifier] respondsToSelector:@selector(countColor)]) {
+ [cell bind:@"countColor" toObject:[item identifier] withKeyPath:@"countColor" options:nil];
+ [[item identifier] addObserver:cell forKeyPath:@"countColor" options:0 context:nil];
+ }
+ }
+
+ // bind for a large image
+ [cell setHasLargeImage:NO];
+ if([item identifier] != nil) {
+ if([[[cell representedObject] identifier] respondsToSelector:@selector(largeImage)]) {
+ NSMutableDictionary *bindingOptions = [NSMutableDictionary dictionary];
+ [bindingOptions setObject:NSIsNotNilTransformerName forKey:@"NSValueTransformerName"];
+ [cell bind:@"hasLargeImage" toObject:[item identifier] withKeyPath:@"largeImage" options:bindingOptions];
+ [[item identifier] addObserver:cell forKeyPath:@"largeImage" options:0 context:nil];
+ }
+ }
+
+ [cell setIsEdited:NO];
+ if([item identifier] != nil) {
+ if([[[cell representedObject] identifier] respondsToSelector:@selector(isEdited)]) {
+ [cell bind:@"isEdited" toObject:[item identifier] withKeyPath:@"isEdited" options:nil];
+ [[item identifier] addObserver:cell forKeyPath:@"isEdited" options:0 context:nil];
+ }
+ }
+
+ // bind my string value to the label on the represented tab
+ [cell bind:@"title" toObject:item withKeyPath:@"label" options:nil];
+}
+
+- (NSMutableArray *)representedTabViewItems {
+ NSMutableArray *temp = [NSMutableArray arrayWithCapacity:[_cells count]];
+ NSEnumerator *e = [_cells objectEnumerator];
+ PSMTabBarCell *cell;
+ while((cell = [e nextObject])) {
+ if([cell representedObject]) {
+ [temp addObject:[cell representedObject]];
+ }
+ }
+ return temp;
+}
+
+- (id)cellForPoint:(NSPoint)point cellFrame:(NSRectPointer)outFrame {
+ if([self orientation] == PSMTabBarHorizontalOrientation && !NSPointInRect(point, [self genericCellRect])) {
+ return nil;
+ }
+
+ NSInteger i, cnt = [_cells count];
+ for(i = 0; i < cnt; i++) {
+ PSMTabBarCell *cell = [_cells objectAtIndex:i];
+
+ if(NSPointInRect(point, [cell frame])) {
+ if(outFrame) {
+ *outFrame = [cell frame];
+ }
+ return cell;
+ }
+ }
+ return nil;
+}
+
+- (PSMTabBarCell *)lastVisibleTab {
+ NSInteger i, cellCount = [_cells count];
+ for(i = 0; i < cellCount; i++) {
+ if([[_cells objectAtIndex:i] isInOverflowMenu]) {
+ return [_cells objectAtIndex:(i - 1)];
+ }
+ }
+ return [_cells objectAtIndex:(cellCount - 1)];
+}
+
+- (NSInteger)numberOfVisibleTabs {
+ NSUInteger i, cellCount = 0;
+ PSMTabBarCell *nextCell;
+
+ for(i = 0; i < [_cells count]; i++) {
+ nextCell = [_cells objectAtIndex:i];
+
+ if([nextCell isInOverflowMenu]) {
+ break;
+ }
+
+ if(![nextCell isPlaceholder]) {
+ cellCount++;
+ }
+ }
+
+ return cellCount;
+}
+
+#pragma mark -
+#pragma mark Accessibility
+
+-(BOOL)accessibilityIsIgnored {
+ return NO;
+}
+
+- (id)accessibilityAttributeValue:(NSString *)attribute {
+ id attributeValue = nil;
+ if([attribute isEqualToString: NSAccessibilityRoleAttribute]) {
+ attributeValue = NSAccessibilityGroupRole;
+ } else if([attribute isEqualToString: NSAccessibilityChildrenAttribute]) {
+ attributeValue = NSAccessibilityUnignoredChildren(_cells);
+ } else {
+ attributeValue = [super accessibilityAttributeValue:attribute];
+ }
+ return attributeValue;
+}
+
+- (id)accessibilityHitTest:(NSPoint)point {
+ id hitTestResult = self;
+
+ NSEnumerator *enumerator = [_cells objectEnumerator];
+ PSMTabBarCell *cell = nil;
+ PSMTabBarCell *highlightedCell = nil;
+
+ while(!highlightedCell && (cell = [enumerator nextObject])) {
+ if([cell isHighlighted]) {
+ highlightedCell = cell;
+ }
+ }
+
+ if(highlightedCell) {
+ hitTestResult = [highlightedCell accessibilityHitTest:point];
+ }
+
+ return hitTestResult;
+}
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabBarController.h b/frontends/cocoa/PSMTabBarControl/PSMTabBarController.h
new file mode 100644
index 000000000..a73a04f1f
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabBarController.h
@@ -0,0 +1,38 @@
+//
+// PSMTabBarController.h
+// PSMTabBarControl
+//
+// Created by Kent Sutherland on 11/24/06.
+// Copyright 2006 Kent Sutherland. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+@class PSMTabBarControl, PSMTabBarCell;
+
+@interface PSMTabBarController : NSObject
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
+ <NSMenuDelegate>
+#endif
+{
+ PSMTabBarControl *_control;
+ NSMutableArray *_cellTrackingRects;
+ NSMutableArray *_closeButtonTrackingRects;
+ NSMutableArray *_cellFrames;
+ NSRect _addButtonRect;
+ NSMenu *_overflowMenu;
+}
+
+- (id)initWithTabBarControl:(PSMTabBarControl *)control;
+
+- (NSRect)addButtonRect;
+- (NSMenu *)overflowMenu;
+- (NSRect)cellTrackingRectAtIndex:(NSUInteger)index;
+- (NSRect)closeButtonTrackingRectAtIndex:(NSUInteger)index;
+- (NSRect)cellFrameAtIndex:(NSUInteger)index;
+
+- (void)setSelectedCell:(PSMTabBarCell *)cell;
+
+- (void)layoutCells;
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabBarController.m b/frontends/cocoa/PSMTabBarControl/PSMTabBarController.m
new file mode 100644
index 000000000..68e1bc498
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabBarController.m
@@ -0,0 +1,643 @@
+//
+// PSMTabBarController.m
+// PSMTabBarControl
+//
+// Created by Kent Sutherland on 11/24/06.
+// Copyright 2006 Kent Sutherland. All rights reserved.
+//
+
+#import "PSMTabBarController.h"
+#import "PSMTabBarControl.h"
+#import "PSMTabBarCell.h"
+#import "PSMTabStyle.h"
+#import "NSString_AITruncation.h"
+
+#define MAX_OVERFLOW_MENUITEM_TITLE_LENGTH 60
+
+@interface PSMTabBarController (Private)
+- (NSArray *)_generateWidthsFromCells:(NSArray *)cells;
+- (void)_setupCells:(NSArray *)cells withWidths:(NSArray *)widths;
+@end
+
+@implementation PSMTabBarController
+
+/*!
+ @method initWithTabBarControl:
+ @abstract Creates a new PSMTabBarController instance.
+ @discussion Creates a new PSMTabBarController for controlling a PSMTabBarControl. Should only be called by
+ PSMTabBarControl.
+ @param A PSMTabBarControl.
+ @returns A newly created PSMTabBarController instance.
+ */
+
+- (id)initWithTabBarControl:(PSMTabBarControl *)control {
+ if((self = [super init])) {
+ _control = control;
+ _cellTrackingRects = [[NSMutableArray alloc] init];
+ _closeButtonTrackingRects = [[NSMutableArray alloc] init];
+ _cellFrames = [[NSMutableArray alloc] init];
+ _addButtonRect = NSZeroRect;
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [_cellTrackingRects release];
+ [_closeButtonTrackingRects release];
+ [_cellFrames release];
+ [super dealloc];
+}
+
+/*!
+ @method addButtonRect
+ @abstract Returns the position for the add tab button.
+ @discussion Returns the position for the add tab button.
+ @returns The rect for the add button rect.
+ */
+
+- (NSRect)addButtonRect {
+ return _addButtonRect;
+}
+
+/*!
+ @method overflowMenu
+ @abstract Returns current overflow menu or nil if there is none.
+ @discussion Returns current overflow menu or nil if there is none.
+ @returns The current overflow menu.
+ */
+
+- (NSMenu *)overflowMenu {
+ return _overflowMenu;
+}
+
+/*!
+ @method cellTrackingRectAtIndex:
+ @abstract Returns the rect for the tracking rect at the requested index.
+ @discussion Returns the rect for the tracking rect at the requested index.
+ @param Index of a cell.
+ @returns The tracking rect of the cell at the requested index.
+ */
+
+- (NSRect)cellTrackingRectAtIndex:(NSUInteger)index {
+ NSRect rect;
+ if(index < [_cellTrackingRects count]) {
+ rect = [[_cellTrackingRects objectAtIndex:index] rectValue];
+ } else {
+ NSLog(@"cellTrackingRectAtIndex: Invalid index (%ld)", (long)index);
+ rect = NSZeroRect;
+ }
+ return rect;
+}
+
+/*!
+ @method closeButtonTrackingRectAtIndex:
+ @abstract Returns the tracking rect for the close button at the requested index.
+ @discussion Returns the tracking rect for the close button at the requested index.
+ @param Index of a cell.
+ @returns The close button tracking rect of the cell at the requested index.
+ */
+
+- (NSRect)closeButtonTrackingRectAtIndex:(NSUInteger)index {
+ NSRect rect;
+ if(index < [_closeButtonTrackingRects count]) {
+ rect = [[_closeButtonTrackingRects objectAtIndex:index] rectValue];
+ } else {
+ NSLog(@"closeButtonTrackingRectAtIndex: Invalid index (%ld)", (long)index);
+ rect = NSZeroRect;
+ }
+ return rect;
+}
+
+/*!
+ @method cellFrameAtIndex:
+ @abstract Returns the frame for the cell at the requested index.
+ @discussion Returns the frame for the cell at the requested index.
+ @param Index of a cell.
+ @returns The frame of the cell at the requested index.
+ */
+
+- (NSRect)cellFrameAtIndex:(NSUInteger)index {
+ NSRect rect;
+
+ if(index < [_cellFrames count]) {
+ rect = [[_cellFrames objectAtIndex:index] rectValue];
+ } else {
+ NSLog(@"cellFrameAtIndex: Invalid index (%ld)", (long)index);
+ rect = NSZeroRect;
+ }
+ return rect;
+}
+
+/*!
+ @method setSelectedCell:
+ @abstract Changes the cell states so the given cell is the currently selected cell.
+ @discussion Makes the given cell the active cell and properly recalculates the tab states for surrounding cells.
+ @param An instance of PSMTabBarCell to make active.
+ */
+
+- (void)setSelectedCell:(PSMTabBarCell *)cell {
+ NSArray *cells = [_control cells];
+ NSEnumerator *enumerator = [cells objectEnumerator];
+ PSMTabBarCell *lastCell = nil, *nextCell;
+
+ //deselect the previously selected tab
+ while((nextCell = [enumerator nextObject]) && ([nextCell state] == NSOffState)) {
+ lastCell = nextCell;
+ }
+
+ [nextCell setState:NSOffState];
+ [nextCell setTabState:PSMTab_PositionMiddleMask];
+
+ if(lastCell && lastCell != [_control lastVisibleTab]) {
+ [lastCell setTabState:~[lastCell tabState] & PSMTab_RightIsSelectedMask];
+ }
+
+ if((nextCell = [enumerator nextObject])) {
+ [nextCell setTabState:~[lastCell tabState] & PSMTab_LeftIsSelectedMask];
+ }
+
+ [cell setState:NSOnState];
+ [cell setTabState:PSMTab_SelectedMask];
+
+ if(![cell isInOverflowMenu]) {
+ NSUInteger cellIndex = [cells indexOfObject:cell];
+
+ if(cellIndex > 0) {
+ nextCell = [cells objectAtIndex:cellIndex - 1];
+ [nextCell setTabState:[nextCell tabState] | PSMTab_RightIsSelectedMask];
+ }
+
+ if(cellIndex < [cells count] - 1) {
+ nextCell = [cells objectAtIndex:cellIndex + 1];
+ [nextCell setTabState:[nextCell tabState] | PSMTab_LeftIsSelectedMask];
+ }
+ }
+}
+
+/*!
+ @method layoutCells
+ @abstract Recalculates cell positions and states.
+ @discussion This method calculates the proper frame, tabState and overflow menu status for all cells in the
+ tab bar control.
+ */
+
+- (void)layoutCells {
+ NSArray *cells = [_control cells];
+ NSInteger cellCount = [cells count];
+
+ // make sure all of our tabs are accounted for before updating
+ if([[_control tabView] numberOfTabViewItems] != cellCount) {
+ return;
+ }
+
+ [_cellTrackingRects removeAllObjects];
+ [_closeButtonTrackingRects removeAllObjects];
+ [_cellFrames removeAllObjects];
+
+ NSArray *cellWidths = [self _generateWidthsFromCells:cells];
+ [self _setupCells:cells withWidths:cellWidths];
+
+ //set up the rect from the add tab button
+ _addButtonRect = [_control genericCellRect];
+ _addButtonRect.size = [[_control addTabButton] frame].size;
+ if([_control orientation] == PSMTabBarHorizontalOrientation) {
+ _addButtonRect.origin.y = MARGIN_Y;
+ _addButtonRect.origin.x += [[cellWidths valueForKeyPath:@"@sum.floatValue"] doubleValue] + 2;
+ } else {
+ _addButtonRect.origin.x = 0;
+ _addButtonRect.origin.y = [[cellWidths lastObject] doubleValue];
+ }
+}
+
+/*!
+ * @method _shrinkWidths:towardMinimum:withAvailableWidth:
+ * @abstract Decreases widths in an array toward a minimum until they fit within availableWidth, if possible
+ * @param An array of NSNumbers
+ * @param The target minimum
+ * @param The maximum available width
+ * @returns The amount by which the total array width was shrunk
+ */
+- (NSInteger)_shrinkWidths:(NSMutableArray *)newWidths towardMinimum:(NSInteger)minimum withAvailableWidth:(CGFloat)availableWidth {
+ BOOL changed = NO;
+ NSInteger count = [newWidths count];
+ NSInteger totalWidths = [[newWidths valueForKeyPath:@"@sum.intValue"] integerValue];
+ NSInteger originalTotalWidths = totalWidths;
+
+ do {
+ changed = NO;
+
+ for(NSInteger q = (count - 1); q >= 0; q--) {
+ CGFloat cellWidth = [[newWidths objectAtIndex:q] doubleValue];
+ if(cellWidth - 1 >= minimum) {
+ cellWidth--;
+ totalWidths--;
+
+ [newWidths replaceObjectAtIndex:q
+ withObject:[NSNumber numberWithDouble:cellWidth]];
+
+ changed = YES;
+ }
+ }
+ } while(changed && (totalWidths > availableWidth));
+
+ return(originalTotalWidths - totalWidths);
+}
+
+/*!
+ * @function potentialMinimumForArray()
+ * @abstract Calculate the minimum total for a given array of widths
+ * @discussion The array is summed using, for each item, the minimum between the current value and the passed minimum value.
+ * This is useful for getting a sum if the array has size-to-fit widths which will be allowed to be less than the
+ * specified minimum.
+ * @param An array of widths
+ * @param The minimum
+ * @returns The smallest possible sum for the array
+ */
+static NSInteger potentialMinimumForArray(NSArray *array, NSInteger minimum){
+ NSInteger runningTotal = 0;
+ NSInteger count = [array count];
+
+ for(NSInteger i = 0; i < count; i++) {
+ NSInteger currentValue = [[array objectAtIndex:i] integerValue];
+ runningTotal += MIN(currentValue, minimum);
+ }
+
+ return runningTotal;
+}
+
+/*!
+ @method _generateWidthsFromCells:
+ @abstract Calculates the width of cells that would be visible.
+ @discussion Calculates the width of cells in the tab bar and returns an array of widths for the cells that would be
+ visible. Uses large blocks of code that were previously in PSMTabBarControl's update method.
+ @param An array of PSMTabBarCells.
+ @returns An array of numbers representing the widths of cells that would be visible.
+ */
+
+- (NSArray *)_generateWidthsFromCells:(NSArray *)cells {
+ NSInteger cellCount = [cells count], i, numberOfVisibleCells = ([_control orientation] == PSMTabBarHorizontalOrientation) ? 1 : 0;
+ NSMutableArray *newWidths = [NSMutableArray arrayWithCapacity:cellCount];
+ id <PSMTabStyle> style = [_control style];
+ CGFloat availableWidth = [_control availableCellWidth], currentOrigin = 0, totalOccupiedWidth = 0.0, width;
+ NSRect cellRect = [_control genericCellRect], controlRect = [_control frame];
+ PSMTabBarCell *currentCell;
+
+ if([_control orientation] == PSMTabBarVerticalOrientation) {
+ currentOrigin = [style topMarginForTabBarControl];
+ }
+
+ //Don't let cells overlap the add tab button if it is visible
+ if([_control showAddTabButton]) {
+ availableWidth -= [self addButtonRect].size.width;
+ }
+
+ for(i = 0; i < cellCount; i++) {
+ currentCell = [cells objectAtIndex:i];
+
+ // supress close button?
+ [currentCell setCloseButtonSuppressed:((cellCount == 1 && [_control canCloseOnlyTab] == NO) ||
+ [_control disableTabClose] ||
+ ([[_control delegate] respondsToSelector:@selector(tabView:disableTabCloseForTabViewItem:)] &&
+ [[_control delegate] tabView:[_control tabView] disableTabCloseForTabViewItem:[currentCell representedObject]]))];
+
+ if([_control orientation] == PSMTabBarHorizontalOrientation) {
+ // Determine cell width
+ if([_control sizeCellsToFit]) {
+ width = [currentCell desiredWidthOfCell];
+ if(width > [_control cellMaxWidth]) {
+ width = [_control cellMaxWidth];
+ }
+ } else {
+ width = [_control cellOptimumWidth];
+ }
+
+ width = ceil(width);
+
+ //check to see if there is not enough space to place all tabs as preferred
+ if(totalOccupiedWidth + width >= availableWidth) {
+ //There's not enough space to add currentCell at its preferred width!
+
+ //If we're not going to use the overflow menu, cram all the tab cells into the bar regardless of minimum width
+ if(![_control useOverflowMenu]) {
+ NSInteger j, averageWidth = (availableWidth / cellCount);
+
+ numberOfVisibleCells = cellCount;
+ [newWidths removeAllObjects];
+
+ for(j = 0; j < cellCount; j++) {
+ CGFloat desiredWidth = [[cells objectAtIndex:j] desiredWidthOfCell];
+ [newWidths addObject:[NSNumber numberWithDouble:(desiredWidth < averageWidth && [_control sizeCellsToFit]) ? desiredWidth : averageWidth]];
+ }
+
+ totalOccupiedWidth = [[newWidths valueForKeyPath:@"@sum.intValue"] integerValue];
+ break;
+ }
+
+ //We'll be using the overflow menu if needed.
+ numberOfVisibleCells = i;
+ if([_control sizeCellsToFit]) {
+ BOOL remainingCellsMustGoToOverflow = NO;
+
+ totalOccupiedWidth = [[newWidths valueForKeyPath:@"@sum.intValue"] integerValue];
+
+ /* Can I squeeze it in without violating min cell width? This is the width we would take up
+ * if every cell so far were at the control minimum size (or their current size if that is less than the control minimum).
+ */
+ if((potentialMinimumForArray(newWidths, [_control cellMinWidth]) + MIN(width, [_control cellMinWidth])) <= availableWidth) {
+ /* It's definitely possible for cells so far to be visible.
+ * Shrink other cells to allow this one to fit
+ */
+ NSInteger cellMinWidth = [_control cellMinWidth];
+
+ /* Start off adding it to the array; we know that it will eventually fit because
+ * (the potential minimum <= availableWidth)
+ *
+ * This allows average and minimum aggregates on the NSArray to work.
+ */
+ [newWidths addObject:[NSNumber numberWithDouble:width]];
+ numberOfVisibleCells++;
+
+ totalOccupiedWidth += width;
+
+ //First, try to shrink tabs toward the average. Tabs smaller than average won't change
+ totalOccupiedWidth -= [self _shrinkWidths:newWidths
+ towardMinimum:[[newWidths valueForKeyPath:@"@avg.intValue"] integerValue]
+ withAvailableWidth:availableWidth];
+
+
+
+ if(totalOccupiedWidth > availableWidth) {
+ //Next, shrink tabs toward the smallest of the existing tabs. The smallest tab won't change.
+ NSInteger smallestTabWidth = [[newWidths valueForKeyPath:@"@min.intValue"] integerValue];
+ if(smallestTabWidth > cellMinWidth) {
+ totalOccupiedWidth -= [self _shrinkWidths:newWidths
+ towardMinimum:smallestTabWidth
+ withAvailableWidth:availableWidth];
+ }
+ }
+
+ if(totalOccupiedWidth > availableWidth) {
+ //Finally, shrink tabs toward the imposed minimum size. All tabs larger than the minimum wll change.
+ totalOccupiedWidth -= [self _shrinkWidths:newWidths
+ towardMinimum:cellMinWidth
+ withAvailableWidth:availableWidth];
+ }
+
+ if(totalOccupiedWidth > availableWidth) {
+ NSLog(@"**** -[PSMTabBarController generateWidthsFromCells:] This is a failure (available %f, total %f, width is %f)",
+ availableWidth, totalOccupiedWidth, width);
+ remainingCellsMustGoToOverflow = YES;
+ }
+
+ if(totalOccupiedWidth < availableWidth) {
+ /* We're not using all available space not but exceeded available width before;
+ * stretch all cells to fully fit the bar
+ */
+ NSInteger leftoverWidth = availableWidth - totalOccupiedWidth;
+ if(leftoverWidth > 0) {
+ NSInteger q;
+ for(q = numberOfVisibleCells - 1; q >= 0; q--) {
+ NSInteger desiredAddition = (NSInteger)leftoverWidth / (q + 1);
+ NSInteger newCellWidth = (NSInteger)[[newWidths objectAtIndex:q] doubleValue] + desiredAddition;
+ [newWidths replaceObjectAtIndex:q withObject:[NSNumber numberWithDouble:newCellWidth]];
+ leftoverWidth -= desiredAddition;
+ totalOccupiedWidth += desiredAddition;
+ }
+ }
+ }
+ } else {
+ // stretch - distribute leftover room among cells, since we can't add this cell
+ NSInteger leftoverWidth = availableWidth - totalOccupiedWidth;
+ NSInteger q;
+ for(q = i - 1; q >= 0; q--) {
+ NSInteger desiredAddition = (NSInteger)leftoverWidth / (q + 1);
+ NSInteger newCellWidth = (NSInteger)[[newWidths objectAtIndex:q] doubleValue] + desiredAddition;
+ [newWidths replaceObjectAtIndex:q withObject:[NSNumber numberWithDouble:newCellWidth]];
+ leftoverWidth -= desiredAddition;
+ }
+
+ remainingCellsMustGoToOverflow = YES;
+ }
+
+ // done assigning widths; remaining cells go in overflow menu
+ if(remainingCellsMustGoToOverflow) {
+ break;
+ }
+ } else {
+ //We're not using size-to-fit
+ NSInteger revisedWidth = availableWidth / (i + 1);
+ if(revisedWidth >= [_control cellMinWidth]) {
+ NSUInteger q;
+ totalOccupiedWidth = 0;
+
+ for(q = 0; q < [newWidths count]; q++) {
+ [newWidths replaceObjectAtIndex:q withObject:[NSNumber numberWithDouble:revisedWidth]];
+ totalOccupiedWidth += revisedWidth;
+ }
+ // just squeezed this one in...
+ [newWidths addObject:[NSNumber numberWithDouble:revisedWidth]];
+ totalOccupiedWidth += revisedWidth;
+ numberOfVisibleCells++;
+ } else {
+ // couldn't fit that last one...
+ break;
+ }
+ }
+ } else {
+ //(totalOccupiedWidth < availableWidth)
+ numberOfVisibleCells = cellCount;
+ [newWidths addObject:[NSNumber numberWithDouble:width]];
+ totalOccupiedWidth += width;
+ }
+ } else {
+ //lay out vertical tabs
+ if(currentOrigin + cellRect.size.height <= controlRect.size.height) {
+ [newWidths addObject:[NSNumber numberWithDouble:currentOrigin]];
+ numberOfVisibleCells++;
+ currentOrigin += cellRect.size.height;
+ } else {
+ //out of room, the remaining tabs go into overflow
+ if([newWidths count] > 0 && controlRect.size.height - currentOrigin < 17) {
+ [newWidths removeLastObject];
+ numberOfVisibleCells--;
+ }
+ break;
+ }
+ }
+ }
+
+ //make sure there are at least two items in the horizontal tab bar
+ if([_control orientation] == PSMTabBarHorizontalOrientation) {
+ if(numberOfVisibleCells < 2 && [cells count] > 1) {
+ PSMTabBarCell *cell1 = [cells objectAtIndex:0], *cell2 = [cells objectAtIndex:1];
+ NSNumber *cellWidth;
+
+ [newWidths removeAllObjects];
+ totalOccupiedWidth = 0;
+
+ cellWidth = [NSNumber numberWithDouble:[cell1 desiredWidthOfCell] < availableWidth * 0.5f ?[cell1 desiredWidthOfCell] : availableWidth * 0.5f];
+ [newWidths addObject:cellWidth];
+ totalOccupiedWidth += [cellWidth doubleValue];
+
+ cellWidth = [NSNumber numberWithDouble:[cell2 desiredWidthOfCell] < (availableWidth - totalOccupiedWidth) ?[cell2 desiredWidthOfCell] : (availableWidth - totalOccupiedWidth)];
+ [newWidths addObject:cellWidth];
+ totalOccupiedWidth += [cellWidth doubleValue];
+
+ if(totalOccupiedWidth < availableWidth) {
+ [newWidths replaceObjectAtIndex:0 withObject:[NSNumber numberWithDouble:availableWidth - [cellWidth doubleValue]]];
+ }
+
+ numberOfVisibleCells = 2;
+ }
+ }
+
+ return newWidths;
+}
+
+/*!
+ @method _setupCells:withWidths
+ @abstract Creates tracking rect arrays and sets the frames of the visible cells.
+ @discussion Creates tracking rect arrays and sets the cells given in the widths array.
+ */
+
+- (void)_setupCells:(NSArray *)cells withWidths:(NSArray *)widths {
+ NSUInteger i, tabState, cellCount = [cells count];
+ NSRect cellRect = [_control genericCellRect];
+ PSMTabBarCell *cell;
+ NSTabViewItem *selectedTabViewItem = [[_control tabView] selectedTabViewItem];
+ NSMenuItem *menuItem;
+
+ [_overflowMenu release], _overflowMenu = nil;
+
+ for(i = 0; i < cellCount; i++) {
+ cell = [cells objectAtIndex:i];
+
+ if(i < [widths count]) {
+ tabState = 0;
+
+ // set cell frame
+ if([_control orientation] == PSMTabBarHorizontalOrientation) {
+ cellRect.size.width = [[widths objectAtIndex:i] doubleValue];
+ } else {
+ cellRect.size.width = [_control frame].size.width;
+ cellRect.origin.y = [[widths objectAtIndex:i] doubleValue];
+ cellRect.origin.x = 0;
+ }
+
+ [_cellFrames addObject:[NSValue valueWithRect:cellRect]];
+
+ //add tracking rects to arrays
+ [_closeButtonTrackingRects addObject:[NSValue valueWithRect:[cell closeButtonRectForFrame:cellRect]]];
+ [_cellTrackingRects addObject:[NSValue valueWithRect:cellRect]];
+
+ if([[cell representedObject] isEqualTo:selectedTabViewItem]) {
+ [cell setState:NSOnState];
+ tabState |= PSMTab_SelectedMask;
+ // previous cell
+ if(i > 0) {
+ [[cells objectAtIndex:i - 1] setTabState:([(PSMTabBarCell *)[cells objectAtIndex:i - 1] tabState] | PSMTab_RightIsSelectedMask)];
+ }
+ // next cell - see below
+ } else {
+ [cell setState:NSOffState];
+ // see if prev cell was selected
+ if((i > 0) && ([[cells objectAtIndex:i - 1] state] == NSOnState)) {
+ tabState |= PSMTab_LeftIsSelectedMask;
+ }
+ }
+
+ // more tab states
+ if([widths count] == 1) {
+ tabState |= PSMTab_PositionLeftMask | PSMTab_PositionRightMask | PSMTab_PositionSingleMask;
+ } else if(i == 0) {
+ tabState |= PSMTab_PositionLeftMask;
+ } else if(i == [widths count] - 1) {
+ tabState |= PSMTab_PositionRightMask;
+ }
+
+ [cell setTabState:tabState];
+ [cell setIsInOverflowMenu:NO];
+
+ // indicator
+ if(![[cell indicator] isHidden] && ![_control isTabBarHidden]) {
+ if(![[_control subviews] containsObject:[cell indicator]]) {
+ [_control addSubview:[cell indicator]];
+ [[cell indicator] startAnimation:self];
+ }
+ }
+
+ // next...
+ cellRect.origin.x += [[widths objectAtIndex:i] doubleValue];
+ } else {
+ [cell setState:NSOffState];
+ [cell setIsInOverflowMenu:YES];
+ [[cell indicator] removeFromSuperview];
+
+ //position the cell well offscreen
+ if([_control orientation] == PSMTabBarHorizontalOrientation) {
+ cellRect.origin.x += [[_control style] rightMarginForTabBarControl] + 20;
+ } else {
+ cellRect.origin.y = [_control frame].size.height + 2;
+ }
+
+ [_cellFrames addObject:[NSValue valueWithRect:cellRect]];
+
+ if(_overflowMenu == nil) {
+ _overflowMenu = [[NSMenu alloc] init];
+ [_overflowMenu insertItemWithTitle:@"" action:nil keyEquivalent:@"" atIndex:0]; // Because the overflowPupUpButton is a pull down menu
+ [_overflowMenu setDelegate:self];
+ }
+
+ // Each item's title is limited to 60 characters. If more than 60 characters, use an ellipsis to indicate that more exists.
+ menuItem = [_overflowMenu addItemWithTitle:[[[cell attributedStringValue] string] stringWithEllipsisByTruncatingToLength:MAX_OVERFLOW_MENUITEM_TITLE_LENGTH]
+ action:@selector(overflowMenuAction:)
+ keyEquivalent:@""];
+ [menuItem setTarget:_control];
+ [menuItem setRepresentedObject:[cell representedObject]];
+
+ if([cell count] > 0) {
+ [menuItem setTitle:[[menuItem title] stringByAppendingFormat:@" (%lu)", (unsigned long)[cell count]]];
+ }
+ }
+ }
+}
+
+- (BOOL)menu:(NSMenu *)menu updateItem:(NSMenuItem *)menuItem atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel {
+ if(menu == _overflowMenu) {
+ if([[[menuItem representedObject] identifier] respondsToSelector:@selector(icon)]) {
+ [menuItem setImage:[[[menuItem representedObject] identifier] valueForKey:@"icon"]];
+ }
+ }
+
+ return TRUE;
+}
+
+- (NSInteger)numberOfItemsInMenu:(NSMenu *)menu {
+ if(menu == _overflowMenu) {
+ return [_overflowMenu numberOfItems];
+ } else {
+ NSLog(@"Warning: Unexpected menu delegate call for menu %@", menu);
+ return 0;
+ }
+}
+
+@end
+
+/*
+ PSMTabBarController will store what the current tab frame state should be like based off the last layout. PSMTabBarControl
+ has to handle fetching the new frame and then changing the tab cell frame.
+ Tab states will probably be changed immediately.
+
+ Tabs that aren't going to be visible need to have their frame set offscreen. Treat them as if they were visible.
+
+ The overflow menu is rebuilt and stored by the controller.
+
+ Arrays of tracking rects will be created here, but not applied.
+ Tracking rects are removed and added by PSMTabBarControl at the end of an animate/display cycle.
+
+ The add tab button frame is handled by this controller. Visibility and location are set by the control.
+
+ isInOverflowMenu should probably be removed in favor of a call that returns yes/no to if a cell is in overflow. (Not yet implemented)
+
+ Still need to rewrite most of the code in PSMTabDragAssistant.
+ */
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabDragAssistant.h b/frontends/cocoa/PSMTabBarControl/PSMTabDragAssistant.h
new file mode 100644
index 000000000..2632e11a0
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabDragAssistant.h
@@ -0,0 +1,101 @@
+//
+// PSMTabDragAssistant.h
+// PSMTabBarControl
+//
+// Created by John Pannell on 4/10/06.
+// Copyright 2006 Positive Spin Media. All rights reserved.
+//
+
+/*
+ This class is a sigleton that manages the details of a tab drag and drop. The details were beginning to overwhelm me when keeping all of this in the control and cells :-)
+ */
+
+#import <Cocoa/Cocoa.h>
+#import "PSMTabBarControl.h"
+
+#define kPSMTabDragAnimationSteps 8
+
+@class PSMTabBarCell, PSMTabDragWindowController;
+
+@interface PSMTabDragAssistant : NSObject {
+ PSMTabBarControl *_sourceTabBar;
+ PSMTabBarControl *_destinationTabBar;
+ NSMutableSet *_participatingTabBars;
+ PSMTabBarCell *_draggedCell;
+ NSUInteger _draggedCellIndex; // for snap back
+ BOOL _isDragging;
+
+ // Support for dragging into new windows
+ PSMTabDragWindowController *_draggedTab;
+ PSMTabDragWindowController *_draggedView;
+ NSSize _dragWindowOffset;
+ NSTimer *_fadeTimer;
+ BOOL _centersDragWindows;
+ PSMTabBarTearOffStyle _currentTearOffStyle;
+
+ // Animation
+ NSTimer *_animationTimer;
+ NSMutableArray *_sineCurveWidths;
+ NSPoint _currentMouseLoc;
+ PSMTabBarCell *_targetCell;
+}
+
+// Creation/destruction
++ (PSMTabDragAssistant *)sharedDragAssistant;
+
+// Accessors
+- (PSMTabBarControl *)sourceTabBar;
+- (void)setSourceTabBar:(PSMTabBarControl *)tabBar;
+- (PSMTabBarControl *)destinationTabBar;
+- (void)setDestinationTabBar:(PSMTabBarControl *)tabBar;
+- (PSMTabBarCell *)draggedCell;
+- (void)setDraggedCell:(PSMTabBarCell *)cell;
+- (NSInteger)draggedCellIndex;
+- (void)setDraggedCellIndex:(NSInteger)value;
+- (BOOL)isDragging;
+- (void)setIsDragging:(BOOL)value;
+- (NSPoint)currentMouseLoc;
+- (void)setCurrentMouseLoc:(NSPoint)point;
+- (PSMTabBarCell *)targetCell;
+- (void)setTargetCell:(PSMTabBarCell *)cell;
+
+// Functionality
+- (void)startDraggingCell:(PSMTabBarCell *)cell fromTabBar:(PSMTabBarControl *)control withMouseDownEvent:(NSEvent *)event;
+- (void)draggingEnteredTabBar:(PSMTabBarControl *)control atPoint:(NSPoint)mouseLoc;
+- (void)draggingUpdatedInTabBar:(PSMTabBarControl *)control atPoint:(NSPoint)mouseLoc;
+- (void)draggingExitedTabBar:(PSMTabBarControl *)control;
+- (void)performDragOperation;
+- (void)draggedImageEndedAt:(NSPoint) aPoint operation:(NSDragOperation)operation;
+- (void)finishDrag;
+
+- (void)draggingBeganAt:(NSPoint)aPoint;
+- (void)draggingMovedTo:(NSPoint)aPoint;
+
+// Animation
+- (void)animateDrag:(NSTimer *)timer;
+- (void)calculateDragAnimationForTabBar:(PSMTabBarControl *)control;
+
+// Placeholder
+- (void)distributePlaceholdersInTabBar:(PSMTabBarControl *)control withDraggedCell:(PSMTabBarCell *)cell;
+- (void)distributePlaceholdersInTabBar:(PSMTabBarControl *)control;
+- (void)removeAllPlaceholdersFromTabBar:(PSMTabBarControl *)control;
+
+@end
+
+@interface PSMTabBarControl (DragAccessors)
+
+- (id<PSMTabStyle>)style;
+- (NSMutableArray *)cells;
+- (void)setControlView:(id)view;
+- (id)cellForPoint:(NSPoint) point cellFrame:(NSRectPointer)outFrame;
+- (PSMTabBarCell *)lastVisibleTab;
+- (NSInteger)numberOfVisibleTabs;
+
+@end
+
+void CGContextCopyWindowCaptureContentsToRect(void *grafport, CGRect rect, NSInteger cid, NSInteger wid, NSInteger zero);
+OSStatus CGSSetWindowTransform(NSInteger cid, NSInteger wid, CGAffineTransform transform);
+
+@interface NSApplication (CoreGraphicsUndocumented)
+- (NSInteger)contextID;
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabDragAssistant.m b/frontends/cocoa/PSMTabBarControl/PSMTabDragAssistant.m
new file mode 100644
index 000000000..4542e8d02
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabDragAssistant.m
@@ -0,0 +1,834 @@
+//
+// PSMTabDragAssistant.m
+// PSMTabBarControl
+//
+// Created by John Pannell on 4/10/06.
+// Copyright 2006 Positive Spin Media. All rights reserved.
+//
+
+#import "PSMTabDragAssistant.h"
+#import "PSMTabBarCell.h"
+#import "PSMTabStyle.h"
+#import "PSMTabDragWindowController.h"
+
+#define PI 3.1417
+
+@interface PSMTabBarControl (Private)
+- (void)update:(BOOL)animate;
+@end
+
+@interface PSMTabDragAssistant (Private)
+- (NSImage *)_imageForViewOfCell:(PSMTabBarCell *)cell styleMask:(NSUInteger *)outMask;
+- (NSImage *)_miniwindowImageOfWindow:(NSWindow *)window;
+- (void)_expandWindow:(NSWindow *)window atPoint:(NSPoint)point;
+@end
+
+@implementation PSMTabDragAssistant
+
+static PSMTabDragAssistant *sharedDragAssistant = nil;
+
+#pragma mark -
+#pragma mark Creation/Destruction
+
++ (PSMTabDragAssistant *)sharedDragAssistant {
+ if(!sharedDragAssistant) {
+ sharedDragAssistant = [[PSMTabDragAssistant alloc] init];
+ }
+
+ return sharedDragAssistant;
+}
+
+- (id)init {
+ if((self = [super init])) {
+ _sourceTabBar = nil;
+ _destinationTabBar = nil;
+ _participatingTabBars = [[NSMutableSet alloc] init];
+ _draggedCell = nil;
+ _animationTimer = nil;
+ _sineCurveWidths = [[NSMutableArray alloc] initWithCapacity:kPSMTabDragAnimationSteps];
+ _targetCell = nil;
+ _isDragging = NO;
+ }
+
+ return self;
+}
+
+- (void)dealloc {
+ [_sourceTabBar release];
+ [_destinationTabBar release];
+ [_participatingTabBars release];
+ [_draggedCell release];
+ [_animationTimer release];
+ [_sineCurveWidths release];
+ [_targetCell release];
+ [super dealloc];
+}
+
+#pragma mark -
+#pragma mark Accessors
+
+- (PSMTabBarControl *)sourceTabBar {
+ return _sourceTabBar;
+}
+
+- (void)setSourceTabBar:(PSMTabBarControl *)tabBar {
+ [tabBar retain];
+ [_sourceTabBar release];
+ _sourceTabBar = tabBar;
+}
+
+- (PSMTabBarControl *)destinationTabBar {
+ return _destinationTabBar;
+}
+
+- (void)setDestinationTabBar:(PSMTabBarControl *)tabBar {
+ [tabBar retain];
+ [_destinationTabBar release];
+ _destinationTabBar = tabBar;
+}
+
+- (PSMTabBarCell *)draggedCell {
+ return _draggedCell;
+}
+
+- (void)setDraggedCell:(PSMTabBarCell *)cell {
+ [cell retain];
+ [_draggedCell release];
+ _draggedCell = cell;
+}
+
+- (NSInteger)draggedCellIndex {
+ return _draggedCellIndex;
+}
+
+- (void)setDraggedCellIndex:(NSInteger)value {
+ _draggedCellIndex = value;
+}
+
+- (BOOL)isDragging {
+ return _isDragging;
+}
+
+- (void)setIsDragging:(BOOL)value {
+ _isDragging = value;
+}
+
+- (NSPoint)currentMouseLoc {
+ return _currentMouseLoc;
+}
+
+- (void)setCurrentMouseLoc:(NSPoint)point {
+ _currentMouseLoc = point;
+}
+
+- (PSMTabBarCell *)targetCell {
+ return _targetCell;
+}
+
+- (void)setTargetCell:(PSMTabBarCell *)cell {
+ [cell retain];
+ [_targetCell release];
+ _targetCell = cell;
+}
+
+#pragma mark -
+#pragma mark Functionality
+
+- (void)startDraggingCell:(PSMTabBarCell *)cell fromTabBar:(PSMTabBarControl *)control withMouseDownEvent:(NSEvent *)event {
+ [self setIsDragging:YES];
+ [self setSourceTabBar:control];
+ [self setDestinationTabBar:control];
+ [_participatingTabBars addObject:control];
+ [self setDraggedCell:cell];
+ [self setDraggedCellIndex:[[control cells] indexOfObject:cell]];
+
+ NSRect cellFrame = [cell frame];
+ // list of widths for animation
+ NSInteger i;
+ CGFloat cellStepSize = ([control orientation] == PSMTabBarHorizontalOrientation) ? (cellFrame.size.width + 6) : (cellFrame.size.height + 1);
+ for(i = 0; i < kPSMTabDragAnimationSteps - 1; i++) {
+ NSInteger thisWidth = (NSInteger)(cellStepSize - ((cellStepSize / 2.0) + ((sin((PI / 2.0) + ((CGFloat)i / (CGFloat)kPSMTabDragAnimationSteps) * PI) * cellStepSize) / 2.0)));
+ [_sineCurveWidths addObject:[NSNumber numberWithInteger:thisWidth]];
+ }
+ [_sineCurveWidths addObject:[NSNumber numberWithInteger:([control orientation] == PSMTabBarHorizontalOrientation) ? cellFrame.size.width : cellFrame.size.height]];
+
+ // hide UI buttons
+ [[control overflowPopUpButton] setHidden:YES];
+ [[control addTabButton] setHidden:YES];
+
+ [[NSCursor closedHandCursor] set];
+
+ NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
+ NSImage *dragImage = [cell dragImage];
+ [[cell indicator] removeFromSuperview];
+ [self distributePlaceholdersInTabBar:control withDraggedCell:cell];
+
+ if([control isFlipped]) {
+ cellFrame.origin.y += cellFrame.size.height;
+ }
+ [cell setHighlighted:NO];
+ NSSize offset = NSZeroSize;
+ [pboard declareTypes:[NSArray arrayWithObjects:@"PSMTabBarControlItemPBType", nil] owner: nil];
+ [pboard setString:[[NSNumber numberWithInteger:[[control cells] indexOfObject:cell]] stringValue] forType:@"PSMTabBarControlItemPBType"];
+ _animationTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / 30.0) target:self selector:@selector(animateDrag:) userInfo:nil repeats:YES];
+
+ [[NSNotificationCenter defaultCenter] postNotificationName:PSMTabDragDidBeginNotification object:nil];
+
+ //retain the control in case the drag operation causes the control to be released
+ [control retain];
+
+ if([control delegate] && [[control delegate] respondsToSelector:@selector(tabView:shouldDropTabViewItem:inTabBar:)] &&
+ [[control delegate] tabView:[control tabView] shouldDropTabViewItem:[[self draggedCell] representedObject] inTabBar:nil]) {
+ _currentTearOffStyle = [control tearOffStyle];
+ _draggedTab = [[PSMTabDragWindowController alloc] initWithImage:dragImage styleMask:NSBorderlessWindowMask tearOffStyle:_currentTearOffStyle];
+
+ cellFrame.origin.y -= cellFrame.size.height;
+ [control dragImage:[[[NSImage alloc] initWithSize:NSMakeSize(1, 1)] autorelease] at:cellFrame.origin offset:offset event:event pasteboard:pboard source:control slideBack:NO];
+ } else {
+ [control dragImage:dragImage at:cellFrame.origin offset:offset event:event pasteboard:pboard source:control slideBack:YES];
+ }
+
+ [control release];
+}
+
+- (void)draggingEnteredTabBar:(PSMTabBarControl *)control atPoint:(NSPoint)mouseLoc {
+ if(_currentTearOffStyle == PSMTabBarTearOffMiniwindow && ![self destinationTabBar]) {
+ [_draggedTab switchImages];
+ }
+
+ [self setDestinationTabBar:control];
+ [self setCurrentMouseLoc:mouseLoc];
+ // hide UI buttons
+ [[control overflowPopUpButton] setHidden:YES];
+ [[control addTabButton] setHidden:YES];
+ if([[control cells] count] == 0 || ![[[control cells] objectAtIndex:0] isPlaceholder]) {
+ [self distributePlaceholdersInTabBar:control];
+ }
+ [_participatingTabBars addObject:control];
+
+ //tell the drag window to display only the header if there is one
+ if(_currentTearOffStyle == PSMTabBarTearOffAlphaWindow && _draggedView) {
+ if(_fadeTimer) {
+ [_fadeTimer invalidate];
+ }
+
+ [[_draggedTab window] orderFront:nil];
+ _fadeTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 / 30.0 target:self selector:@selector(fadeOutDragWindow:) userInfo:nil repeats:YES];
+ }
+}
+
+- (void)draggingUpdatedInTabBar:(PSMTabBarControl *)control atPoint:(NSPoint)mouseLoc {
+ if([self destinationTabBar] != control) {
+ [self setDestinationTabBar:control];
+ }
+ [self setCurrentMouseLoc:mouseLoc];
+}
+
+- (void)draggingExitedTabBar:(PSMTabBarControl *)control {
+ if([[control delegate] respondsToSelector:@selector(tabView:shouldAllowTabViewItem:toLeaveTabBar:)] &&
+ ![[control delegate] tabView:[control tabView] shouldAllowTabViewItem:[[self draggedCell] representedObject] toLeaveTabBar:control]) {
+ return;
+ }
+
+ [self setDestinationTabBar:nil];
+ [self setCurrentMouseLoc:NSMakePoint(-1.0, -1.0)];
+
+ if(_fadeTimer) {
+ [_fadeTimer invalidate];
+ _fadeTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 / 30.0 target:self selector:@selector(fadeInDragWindow:) userInfo:nil repeats:YES];
+ } else if(_draggedTab) {
+ if(_currentTearOffStyle == PSMTabBarTearOffAlphaWindow) {
+ //create a new floating drag window
+ if(!_draggedView) {
+ NSUInteger styleMask;
+ NSImage *viewImage = [self _imageForViewOfCell:[self draggedCell] styleMask:&styleMask];
+
+ _draggedView = [[PSMTabDragWindowController alloc] initWithImage:viewImage styleMask:styleMask tearOffStyle:PSMTabBarTearOffAlphaWindow];
+ [[_draggedView window] setAlphaValue:0.0];
+ }
+
+ NSPoint windowOrigin = [[control window] frame].origin;
+
+ windowOrigin.x -= _dragWindowOffset.width;
+ windowOrigin.y += _dragWindowOffset.height;
+ [[_draggedView window] setFrameOrigin:windowOrigin];
+ [[_draggedView window] orderWindow:NSWindowBelow relativeTo:[[_draggedTab window] windowNumber]];
+ } else if(_currentTearOffStyle == PSMTabBarTearOffMiniwindow && ![_draggedTab alternateImage]) {
+ NSImage *image;
+ NSSize imageSize;
+ NSUInteger mask; //we don't need this but we can't pass nil in for the style mask, as some delegate implementations will crash
+
+ if(!(image = [self _miniwindowImageOfWindow:[control window]])) {
+ image = [[self _imageForViewOfCell:[self draggedCell] styleMask:&mask] copy];
+ }
+
+ imageSize = [image size];
+ [image setScalesWhenResized:YES];
+
+ if(imageSize.width > imageSize.height) {
+ [image setSize:NSMakeSize(125, 125 * (imageSize.height / imageSize.width))];
+ } else {
+ [image setSize:NSMakeSize(125 * (imageSize.width / imageSize.height), 125)];
+ }
+
+ [_draggedTab setAlternateImage:image];
+ }
+
+ //set the window's alpha mask to zero if the last tab is being dragged
+ //don't fade out the old window if the delegate doesn't respond to the new tab bar method, just to be safe
+ if([[[self sourceTabBar] tabView] numberOfTabViewItems] == 1 && [self sourceTabBar] == control &&
+ [[[self sourceTabBar] delegate] respondsToSelector:@selector(tabView:newTabBarForDraggedTabViewItem:atPoint:)]) {
+ [[[self sourceTabBar] window] setAlphaValue:0.0];
+
+ if([_sourceTabBar tearOffStyle] == PSMTabBarTearOffAlphaWindow) {
+ [[_draggedView window] setAlphaValue:kPSMTabDragWindowAlpha];
+ } else {
+ //#warning fix me - what should we do when the last tab is dragged as a miniwindow?
+ }
+ } else {
+ if([_sourceTabBar tearOffStyle] == PSMTabBarTearOffAlphaWindow) {
+ _fadeTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 / 30.0 target:self selector:@selector(fadeInDragWindow:) userInfo:nil repeats:YES];
+ } else {
+ [_draggedTab switchImages];
+ _centersDragWindows = YES;
+ }
+ }
+ }
+}
+
+- (void)performDragOperation {
+ // move cell
+ NSUInteger destinationIndex = [[[self destinationTabBar] cells] indexOfObject:[self targetCell]];
+
+ //there is the slight possibility of the targetCell now being set properly, so avoid errors
+ if(destinationIndex >= [[[self destinationTabBar] cells] count]) {
+ destinationIndex = [[[self destinationTabBar] cells] count] - 1;
+ }
+
+ [[[self destinationTabBar] cells] replaceObjectAtIndex:destinationIndex withObject:[self draggedCell]];
+ [[self draggedCell] setControlView:[self destinationTabBar]];
+
+ // move actual NSTabViewItem
+ if([self sourceTabBar] != [self destinationTabBar]) {
+ //remove the tracking rects and bindings registered on the old tab
+ [[self sourceTabBar] removeTrackingRect:[[self draggedCell] closeButtonTrackingTag]];
+ [[self sourceTabBar] removeTrackingRect:[[self draggedCell] cellTrackingTag]];
+ [[self sourceTabBar] removeTabForCell:[self draggedCell]];
+
+ NSUInteger i, insertIndex;
+ NSArray *cells = [[self destinationTabBar] cells];
+
+ //find the index of where the dragged cell was just dropped
+ for(i = 0, insertIndex = 0; (i < [cells count]) && ([cells objectAtIndex:i] != [self draggedCell]); i++, insertIndex++) {
+ if([[cells objectAtIndex:i] isPlaceholder]) {
+ insertIndex--;
+ }
+ }
+
+ [[[self sourceTabBar] tabView] removeTabViewItem:[[self draggedCell] representedObject]];
+ [[[self destinationTabBar] tabView] insertTabViewItem:[[self draggedCell] representedObject] atIndex:insertIndex];
+
+ //calculate the position for the dragged cell
+ if([[self destinationTabBar] automaticallyAnimates]) {
+ if(insertIndex > 0) {
+ NSRect cellRect = [[cells objectAtIndex:insertIndex - 1] frame];
+ cellRect.origin.x += cellRect.size.width;
+ [[self draggedCell] setFrame:cellRect];
+ }
+ }
+
+ //rebind the cell to the new control
+ [[self destinationTabBar] bindPropertiesForCell:[self draggedCell] andTabViewItem:[[self draggedCell] representedObject]];
+
+ //select the newly moved item in the destination tab view
+ [[[self destinationTabBar] tabView] selectTabViewItem:[[self draggedCell] representedObject]];
+ } else {
+ //have to do this before checking the index of a cell otherwise placeholders will be counted
+ [self removeAllPlaceholdersFromTabBar:[self sourceTabBar]];
+
+ //rearrange the tab view items
+ NSTabView *tabView = [[self sourceTabBar] tabView];
+ NSTabViewItem *item = [[self draggedCell] representedObject];
+ BOOL reselect = ([tabView selectedTabViewItem] == item);
+ NSArray *cells = [[self sourceTabBar] cells];
+ NSUInteger index;
+ //find the index of where the dragged cell was just dropped
+ for(index = 0; index < [cells count] && [cells objectAtIndex:index] != [self draggedCell]; index++) {
+ ;
+ }
+
+ //temporarily disable the delegate in order to move the tab to a different index
+ id tempDelegate = [tabView delegate];
+ [tabView setDelegate:nil];
+ [item retain];
+ [tabView removeTabViewItem:item];
+ [tabView insertTabViewItem:item atIndex:index];
+ if(reselect) {
+ [tabView selectTabViewItem:item];
+ }
+ [tabView setDelegate:tempDelegate];
+ }
+
+ if(([self sourceTabBar] != [self destinationTabBar] || [[[self sourceTabBar] cells] indexOfObject:[self draggedCell]] != _draggedCellIndex) && [[[self sourceTabBar] delegate] respondsToSelector:@selector(tabView:didDropTabViewItem:inTabBar:)]) {
+ [[[self sourceTabBar] delegate] tabView:[[self sourceTabBar] tabView] didDropTabViewItem:[[self draggedCell] representedObject] inTabBar:[self destinationTabBar]];
+ }
+
+ [[NSNotificationCenter defaultCenter] postNotificationName:PSMTabDragDidEndNotification object:nil];
+
+ [self finishDrag];
+}
+
+- (void)draggedImageEndedAt:(NSPoint)aPoint operation:(NSDragOperation)operation {
+ if([self isDragging]) { // means there was not a successful drop (performDragOperation)
+ id sourceDelegate = [[self sourceTabBar] delegate];
+
+ //split off the dragged tab into a new window
+ if([self destinationTabBar] == nil &&
+ sourceDelegate && [sourceDelegate respondsToSelector:@selector(tabView:shouldDropTabViewItem:inTabBar:)] &&
+ [sourceDelegate tabView:[[self sourceTabBar] tabView] shouldDropTabViewItem:[[self draggedCell] representedObject] inTabBar:nil] &&
+ [sourceDelegate respondsToSelector:@selector(tabView:newTabBarForDraggedTabViewItem:atPoint:)]) {
+ PSMTabBarControl *control = [sourceDelegate tabView:[[self sourceTabBar] tabView] newTabBarForDraggedTabViewItem:[[self draggedCell] representedObject] atPoint:aPoint];
+
+ if(control) {
+ //add the dragged tab to the new window
+ [[control cells] insertObject:[self draggedCell] atIndex:0];
+
+ //remove the tracking rects and bindings registered on the old tab
+ [[self sourceTabBar] removeTrackingRect:[[self draggedCell] closeButtonTrackingTag]];
+ [[self sourceTabBar] removeTrackingRect:[[self draggedCell] cellTrackingTag]];
+ [[self sourceTabBar] removeTabForCell:[self draggedCell]];
+
+ //rebind the cell to the new control
+ [control bindPropertiesForCell:[self draggedCell] andTabViewItem:[[self draggedCell] representedObject]];
+
+ [[self draggedCell] setControlView:control];
+
+ [[[self sourceTabBar] tabView] removeTabViewItem:[[self draggedCell] representedObject]];
+
+ [[control tabView] addTabViewItem:[[self draggedCell] representedObject]];
+ [control update:NO]; //make sure the new tab is set in the correct position
+
+ if(_currentTearOffStyle == PSMTabBarTearOffAlphaWindow) {
+ [[control window] makeKeyAndOrderFront:nil];
+ } else {
+ //center the window over where we ended dragging
+ [self _expandWindow:[control window] atPoint:[NSEvent mouseLocation]];
+ }
+
+ if([sourceDelegate respondsToSelector:@selector(tabView:didDropTabViewItem:inTabBar:)]) {
+ [sourceDelegate tabView:[[self sourceTabBar] tabView] didDropTabViewItem:[[self draggedCell] representedObject] inTabBar:control];
+ }
+ } else {
+ NSLog(@"Delegate returned no control to add to.");
+ [[[self sourceTabBar] cells] insertObject:[self draggedCell] atIndex:[self draggedCellIndex]];
+ }
+ } else {
+ // put cell back
+ [[[self sourceTabBar] cells] insertObject:[self draggedCell] atIndex:[self draggedCellIndex]];
+ }
+
+ [[NSNotificationCenter defaultCenter] postNotificationName:PSMTabDragDidEndNotification object:nil];
+
+ [self finishDrag];
+ }
+}
+
+- (void)finishDrag {
+ if([[[self sourceTabBar] tabView] numberOfTabViewItems] == 0 && [[[self sourceTabBar] delegate] respondsToSelector:@selector(tabView:closeWindowForLastTabViewItem:)]) {
+ [[[self sourceTabBar] delegate] tabView:[[self sourceTabBar] tabView] closeWindowForLastTabViewItem:[[self draggedCell] representedObject]];
+ }
+
+ if(_draggedTab) {
+ [[_draggedTab window] orderOut:nil];
+ [_draggedTab release];
+ _draggedTab = nil;
+ }
+
+ if(_draggedView) {
+ [[_draggedView window] orderOut:nil];
+ [_draggedView release];
+ _draggedView = nil;
+ }
+
+ _centersDragWindows = NO;
+
+ [self setIsDragging:NO];
+ [self removeAllPlaceholdersFromTabBar:[self sourceTabBar]];
+ [self setSourceTabBar:nil];
+ [self setDestinationTabBar:nil];
+ NSEnumerator *e = [_participatingTabBars objectEnumerator];
+ PSMTabBarControl *tabBar;
+ while((tabBar = [e nextObject])) {
+ [self removeAllPlaceholdersFromTabBar:tabBar];
+ }
+ [_participatingTabBars removeAllObjects];
+ [self setDraggedCell:nil];
+ [_animationTimer invalidate];
+ _animationTimer = nil;
+ [_sineCurveWidths removeAllObjects];
+ [self setTargetCell:nil];
+}
+
+- (void)draggingBeganAt:(NSPoint)aPoint {
+ if(_draggedTab) {
+ [[_draggedTab window] setFrameTopLeftPoint:aPoint];
+ [[_draggedTab window] orderFront:nil];
+
+ if([[[self sourceTabBar] tabView] numberOfTabViewItems] == 1) {
+ [self draggingExitedTabBar:[self sourceTabBar]];
+ [[_draggedTab window] setAlphaValue:0.0];
+ }
+ }
+}
+
+- (void)draggingMovedTo:(NSPoint)aPoint {
+ if(_draggedTab) {
+ if(_centersDragWindows) {
+ if([_draggedTab isAnimating]) {
+ return;
+ }
+
+ //Ignore aPoint, as it seems to give wacky values
+ NSRect frame = [[_draggedTab window] frame];
+ frame.origin = [NSEvent mouseLocation];
+ frame.origin.x -= frame.size.width / 2;
+ frame.origin.y -= frame.size.height / 2;
+ [[_draggedTab window] setFrame:frame display:NO];
+ } else {
+ [[_draggedTab window] setFrameTopLeftPoint:aPoint];
+ }
+
+ if(_draggedView) {
+ //move the view representation with the tab
+ //the relative position of the dragged view window will be different
+ //depending on the position of the tab bar relative to the controlled tab view
+
+ aPoint.y -= [[_draggedTab window] frame].size.height;
+ aPoint.x -= _dragWindowOffset.width;
+ aPoint.y += _dragWindowOffset.height;
+ [[_draggedView window] setFrameTopLeftPoint:aPoint];
+ }
+ }
+}
+
+- (void)fadeInDragWindow:(NSTimer *)timer {
+ CGFloat value = [[_draggedView window] alphaValue];
+ if(value >= kPSMTabDragWindowAlpha || _draggedTab == nil) {
+ [timer invalidate];
+ _fadeTimer = nil;
+ } else {
+ [[_draggedTab window] setAlphaValue:[[_draggedTab window] alphaValue] - kPSMTabDragAlphaInterval];
+ [[_draggedView window] setAlphaValue:value + kPSMTabDragAlphaInterval];
+ }
+}
+
+- (void)fadeOutDragWindow:(NSTimer *)timer {
+ CGFloat value = [[_draggedView window] alphaValue];
+ NSWindow *tabWindow = [_draggedTab window], *viewWindow = [_draggedView window];
+
+ if(value <= 0.0) {
+ [viewWindow setAlphaValue:0.0];
+ [tabWindow setAlphaValue:kPSMTabDragWindowAlpha];
+
+ [timer invalidate];
+ _fadeTimer = nil;
+ } else {
+ if([tabWindow alphaValue] < kPSMTabDragWindowAlpha) {
+ [tabWindow setAlphaValue:[tabWindow alphaValue] + kPSMTabDragAlphaInterval];
+ }
+ [viewWindow setAlphaValue:value - kPSMTabDragAlphaInterval];
+ }
+}
+
+#pragma mark -
+#pragma mark Private
+
+- (NSImage *)_imageForViewOfCell:(PSMTabBarCell *)cell styleMask:(NSUInteger *)outMask {
+ PSMTabBarControl *control = [cell controlView];
+ NSImage *viewImage = nil;
+
+ if(outMask) {
+ *outMask = NSBorderlessWindowMask;
+ }
+
+ if([control delegate] && [[control delegate] respondsToSelector:@selector(tabView:imageForTabViewItem:offset:styleMask:)]) {
+ //get a custom image representation of the view to drag from the delegate
+ NSImage *tabImage = [_draggedTab image];
+ NSPoint drawPoint;
+ _dragWindowOffset = NSZeroSize;
+ viewImage = [[control delegate] tabView:[control tabView] imageForTabViewItem:[cell representedObject] offset:&_dragWindowOffset styleMask:outMask];
+ [viewImage lockFocus];
+
+ //draw the tab into the returned window, that way we don't have two windows being dragged (this assumes the tab will be on the window)
+ drawPoint = NSMakePoint(_dragWindowOffset.width, [viewImage size].height - _dragWindowOffset.height);
+
+ if([control orientation] == PSMTabBarHorizontalOrientation) {
+ drawPoint.y += [[control style] tabCellHeight] - [tabImage size].height;
+ _dragWindowOffset.height -= [[control style] tabCellHeight] - [tabImage size].height;
+ } else {
+ drawPoint.x += [control frame].size.width - [tabImage size].width;
+ }
+
+ [tabImage compositeToPoint:drawPoint operation:NSCompositeSourceOver];
+
+ [viewImage unlockFocus];
+ } else {
+ //the delegate doesn't give a custom image, so use an image of the view
+ NSView *tabView = [[cell representedObject] view];
+ viewImage = [[[NSImage alloc] initWithSize:[tabView frame].size] autorelease];
+ [viewImage lockFocus];
+ [tabView drawRect:[tabView bounds]];
+ [viewImage unlockFocus];
+ }
+
+ if(*outMask | NSBorderlessWindowMask) {
+ _dragWindowOffset.height += 22;
+ }
+
+ return viewImage;
+}
+
+- (NSImage *)_miniwindowImageOfWindow:(NSWindow *)window {
+ NSRect rect = [window frame];
+ NSImage *image = [[[NSImage alloc] initWithSize:rect.size] autorelease];
+ [image lockFocus];
+ rect.origin = NSZeroPoint;
+ CGContextCopyWindowCaptureContentsToRect([[NSGraphicsContext currentContext] graphicsPort], *(CGRect *)&rect, [NSApp contextID], [window windowNumber], 0);
+ [image unlockFocus];
+
+ return image;
+}
+
+- (void)_expandWindow:(NSWindow *)window atPoint:(NSPoint)point {
+ NSRect frame = [window frame];
+ [window setFrameTopLeftPoint:NSMakePoint(point.x - frame.size.width / 2, point.y + frame.size.height / 2)];
+ [window setAlphaValue:0.0];
+ [window makeKeyAndOrderFront:nil];
+
+ NSAnimation *animation = [[NSAnimation alloc] initWithDuration:0.25 animationCurve:NSAnimationEaseInOut];
+ [animation setAnimationBlockingMode:NSAnimationNonblocking];
+ [animation setCurrentProgress:0.1];
+ [animation startAnimation];
+ NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 / 30.0 target:self selector:@selector(_expandWindowTimerFired:) userInfo:[NSDictionary dictionaryWithObjectsAndKeys:window, @"Window", animation, @"Animation", nil] repeats:YES];
+ [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSEventTrackingRunLoopMode];
+}
+
+- (void)_expandWindowTimerFired:(NSTimer *)timer {
+ NSWindow *window = [[timer userInfo] objectForKey:@"Window"];
+ NSAnimation *animation = [[timer userInfo] objectForKey:@"Animation"];
+ CGAffineTransform transform;
+ NSPoint translation;
+ NSRect winFrame = [window frame];
+
+ translation.x = (winFrame.size.width / 2.0);
+ translation.y = (winFrame.size.height / 2.0);
+ transform = CGAffineTransformMakeTranslation(translation.x, translation.y);
+ transform = CGAffineTransformScale(transform, 1.0 / [animation currentValue], 1.0 / [animation currentValue]);
+ transform = CGAffineTransformTranslate(transform, -translation.x, -translation.y);
+
+ translation.x = -winFrame.origin.x;
+ translation.y = winFrame.origin.y + winFrame.size.height - [[NSScreen mainScreen] frame].size.height;
+
+ transform = CGAffineTransformTranslate(transform, translation.x, translation.y);
+
+ CGSSetWindowTransform([NSApp contextID], [window windowNumber], transform);
+
+ [window setAlphaValue:[animation currentValue]];
+
+ if(![animation isAnimating]) {
+ [timer invalidate];
+ [animation release];
+ }
+}
+
+#pragma mark -
+#pragma mark Animation
+
+- (void)animateDrag:(NSTimer *)timer {
+ NSEnumerator *e = [[[_participatingTabBars copy] autorelease] objectEnumerator];
+ PSMTabBarControl *tabBar;
+ while((tabBar = [e nextObject])) {
+ [self calculateDragAnimationForTabBar:tabBar];
+ [[NSRunLoop currentRunLoop] performSelector:@selector(display) target:tabBar argument:nil order:1 modes:[NSArray arrayWithObjects:@"NSEventTrackingRunLoopMode", @"NSDefaultRunLoopMode", nil]];
+ }
+}
+
+- (void)calculateDragAnimationForTabBar:(PSMTabBarControl *)control {
+ BOOL removeFlag = YES;
+ NSMutableArray *cells = [control cells];
+ NSInteger i, cellCount = [cells count];
+ CGFloat position = [control orientation] == PSMTabBarHorizontalOrientation ?[[control style] leftMarginForTabBarControl] :[[control style] topMarginForTabBarControl];
+
+ // identify target cell
+ // mouse at beginning of tabs
+ NSPoint mouseLoc = [self currentMouseLoc];
+ if([self destinationTabBar] == control) {
+ removeFlag = NO;
+ if(mouseLoc.x < [[control style] leftMarginForTabBarControl]) {
+ [self setTargetCell:[cells objectAtIndex:0]];
+ } else {
+ NSRect overCellRect;
+ PSMTabBarCell *overCell = [control cellForPoint:mouseLoc cellFrame:&overCellRect];
+ if(overCell) {
+ // mouse among cells - placeholder
+ if([overCell isPlaceholder]) {
+ [self setTargetCell:overCell];
+ } else if([control orientation] == PSMTabBarHorizontalOrientation) {
+ // non-placeholders - horizontal orientation
+ if(mouseLoc.x < (overCellRect.origin.x + (overCellRect.size.width / 2.0))) {
+ // mouse on left side of cell
+ [self setTargetCell:[cells objectAtIndex:([cells indexOfObject:overCell] - 1)]];
+ } else {
+ // mouse on right side of cell
+ [self setTargetCell:[cells objectAtIndex:([cells indexOfObject:overCell] + 1)]];
+ }
+ } else {
+ // non-placeholders - vertical orientation
+ if(mouseLoc.y < (overCellRect.origin.y + (overCellRect.size.height / 2.0))) {
+ // mouse on top of cell
+ [self setTargetCell:[cells objectAtIndex:([cells indexOfObject:overCell] - 1)]];
+ } else {
+ // mouse on bottom of cell
+ [self setTargetCell:[cells objectAtIndex:([cells indexOfObject:overCell] + 1)]];
+ }
+ }
+ } else {
+ // out at end - must find proper cell (could be more in overflow menu)
+ [self setTargetCell:[control lastVisibleTab]];
+ }
+ }
+ } else {
+ [self setTargetCell:nil];
+ }
+
+ for(i = 0; i < cellCount; i++) {
+ PSMTabBarCell *cell = [cells objectAtIndex:i];
+ NSRect newRect = [cell frame];
+ if(![cell isInOverflowMenu]) {
+ if([cell isPlaceholder]) {
+ if(cell == [self targetCell]) {
+ [cell setCurrentStep:([cell currentStep] + 1)];
+ } else {
+ [cell setCurrentStep:([cell currentStep] - 1)];
+ if([cell currentStep] > 0) {
+ removeFlag = NO;
+ }
+ }
+
+ if([control orientation] == PSMTabBarHorizontalOrientation) {
+ newRect.size.width = [[_sineCurveWidths objectAtIndex:[cell currentStep]] integerValue];
+ } else {
+ newRect.size.height = [[_sineCurveWidths objectAtIndex:[cell currentStep]] integerValue];
+ }
+ }
+ } else {
+ break;
+ }
+
+ if([control orientation] == PSMTabBarHorizontalOrientation) {
+ newRect.origin.x = position;
+ position += newRect.size.width;
+ } else {
+ newRect.origin.y = position;
+ position += newRect.size.height;
+ }
+ [cell setFrame:newRect];
+ if([cell indicator]) {
+ [[cell indicator] setFrame:[[control style] indicatorRectForTabCell:cell]];
+ }
+ }
+ if(removeFlag) {
+ [_participatingTabBars removeObject:control];
+ [self removeAllPlaceholdersFromTabBar:control];
+ }
+}
+
+#pragma mark -
+#pragma mark Placeholders
+
+- (void)distributePlaceholdersInTabBar:(PSMTabBarControl *)control withDraggedCell:(PSMTabBarCell *)cell {
+ // called upon first drag - must distribute placeholders
+ [self distributePlaceholdersInTabBar:control];
+
+ NSMutableArray *cells = [control cells];
+
+ // replace dragged cell with a placeholder, and clean up surrounding cells
+ NSInteger cellIndex = [cells indexOfObject:cell];
+ PSMTabBarCell *pc = [[[PSMTabBarCell alloc] initPlaceholderWithFrame:[[self draggedCell] frame] expanded:YES inControlView:control] autorelease];
+ [cells replaceObjectAtIndex:cellIndex withObject:pc];
+ [cells removeObjectAtIndex:(cellIndex + 1)];
+ [cells removeObjectAtIndex:(cellIndex - 1)];
+
+ if(cellIndex - 2 >= 0) {
+ pc = [cells objectAtIndex:cellIndex - 2];
+ [pc setTabState:~[pc tabState] & PSMTab_RightIsSelectedMask];
+ }
+}
+
+- (void)distributePlaceholdersInTabBar:(PSMTabBarControl *)control {
+ NSUInteger i, numVisibleTabs = [control numberOfVisibleTabs];
+ for(i = 0; i < numVisibleTabs; i++) {
+ PSMTabBarCell *pc = [[[PSMTabBarCell alloc] initPlaceholderWithFrame:[[self draggedCell] frame] expanded:NO inControlView:control] autorelease];
+ [[control cells] insertObject:pc atIndex:(2 * i)];
+ }
+
+ PSMTabBarCell *pc = [[[PSMTabBarCell alloc] initPlaceholderWithFrame:[[self draggedCell] frame] expanded:NO inControlView:control] autorelease];
+ if([[control cells] count] > (2 * numVisibleTabs)) {
+ [[control cells] insertObject:pc atIndex:(2 * numVisibleTabs)];
+ } else {
+ [[control cells] addObject:pc];
+ }
+}
+
+- (void)removeAllPlaceholdersFromTabBar:(PSMTabBarControl *)control {
+ NSInteger i, cellCount = [[control cells] count];
+ for(i = (cellCount - 1); i >= 0; i--) {
+ PSMTabBarCell *cell = [[control cells] objectAtIndex:i];
+ if([cell isPlaceholder]) {
+ [control removeTabForCell:cell];
+ }
+ }
+ // redraw
+ [control update:NO];
+}
+
+#pragma mark -
+#pragma mark Archiving
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+ //[super encodeWithCoder:aCoder];
+ if([aCoder allowsKeyedCoding]) {
+ [aCoder encodeObject:_sourceTabBar forKey:@"sourceTabBar"];
+ [aCoder encodeObject:_destinationTabBar forKey:@"destinationTabBar"];
+ [aCoder encodeObject:_participatingTabBars forKey:@"participatingTabBars"];
+ [aCoder encodeObject:_draggedCell forKey:@"draggedCell"];
+ [aCoder encodeInteger:_draggedCellIndex forKey:@"draggedCellIndex"];
+ [aCoder encodeBool:_isDragging forKey:@"isDragging"];
+ [aCoder encodeObject:_animationTimer forKey:@"animationTimer"];
+ [aCoder encodeObject:_sineCurveWidths forKey:@"sineCurveWidths"];
+ [aCoder encodePoint:_currentMouseLoc forKey:@"currentMouseLoc"];
+ [aCoder encodeObject:_targetCell forKey:@"targetCell"];
+ }
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder {
+ //self = [super initWithCoder:aDecoder];
+ //if (self) {
+ if([aDecoder allowsKeyedCoding]) {
+ _sourceTabBar = [[aDecoder decodeObjectForKey:@"sourceTabBar"] retain];
+ _destinationTabBar = [[aDecoder decodeObjectForKey:@"destinationTabBar"] retain];
+ _participatingTabBars = [[aDecoder decodeObjectForKey:@"participatingTabBars"] retain];
+ _draggedCell = [[aDecoder decodeObjectForKey:@"draggedCell"] retain];
+ _draggedCellIndex = [aDecoder decodeIntegerForKey:@"draggedCellIndex"];
+ _isDragging = [aDecoder decodeBoolForKey:@"isDragging"];
+ _animationTimer = [[aDecoder decodeObjectForKey:@"animationTimer"] retain];
+ _sineCurveWidths = [[aDecoder decodeObjectForKey:@"sineCurveWidths"] retain];
+ _currentMouseLoc = [aDecoder decodePointForKey:@"currentMouseLoc"];
+ _targetCell = [[aDecoder decodeObjectForKey:@"targetCell"] retain];
+ }
+ //}
+ return self;
+}
+
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabDragView.h b/frontends/cocoa/PSMTabBarControl/PSMTabDragView.h
new file mode 100644
index 000000000..f8018d290
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabDragView.h
@@ -0,0 +1,21 @@
+//
+// PSMTabDragView.h
+// PSMTabBarControl
+//
+// Created by Kent Sutherland on 6/17/07.
+// Copyright 2007 Kent Sutherland. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+@interface PSMTabDragView : NSView {
+ NSImage *_image;
+ NSImage *_alternateImage;
+ CGFloat _alpha;
+}
+- (void)setFadeValue:(CGFloat)value;
+- (NSImage *)image;
+- (void)setImage:(NSImage *)image;
+- (NSImage *)alternateImage;
+- (void)setAlternateImage:(NSImage *)image;
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabDragView.m b/frontends/cocoa/PSMTabBarControl/PSMTabDragView.m
new file mode 100644
index 000000000..2c9781dbc
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabDragView.m
@@ -0,0 +1,62 @@
+//
+// PSMTabDragView.m
+// PSMTabBarControl
+//
+// Created by Kent Sutherland on 6/17/07.
+// Copyright 2007 Kent Sutherland. All rights reserved.
+//
+
+#import "PSMTabDragView.h"
+
+
+@implementation PSMTabDragView
+
+- (id)initWithFrame:(NSRect)frame {
+ if((self = [super initWithFrame:frame])) {
+ _alpha = 1.0;
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [_image release];
+ [_alternateImage release];
+ [super dealloc];
+}
+
+- (void)drawRect:(NSRect)rect {
+ //1.0 fade means show the primary image
+ //0.0 fade means show the secondary image
+ CGFloat primaryAlpha = _alpha + 0.001f, alternateAlpha = 1.001f - _alpha;
+ NSRect srcRect;
+ srcRect.origin = NSZeroPoint;
+ srcRect.size = [_image size];
+
+ [_image drawInRect:[self bounds] fromRect:srcRect operation:NSCompositeSourceOver fraction:primaryAlpha];
+ srcRect.size = [_alternateImage size];
+ [_alternateImage drawInRect:[self bounds] fromRect:srcRect operation:NSCompositeSourceOver fraction:alternateAlpha];
+}
+
+- (void)setFadeValue:(CGFloat)value {
+ _alpha = value;
+}
+
+- (NSImage *)image {
+ return _image;
+}
+
+- (void)setImage:(NSImage *)image {
+ [_image release];
+ _image = [image retain];
+}
+
+- (NSImage *)alternateImage {
+ return _alternateImage;
+}
+
+- (void)setAlternateImage:(NSImage *)image {
+ [_alternateImage release];
+ _alternateImage = [image retain];
+}
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabDragWindow.h b/frontends/cocoa/PSMTabBarControl/PSMTabDragWindow.h
new file mode 100644
index 000000000..04cde248c
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabDragWindow.h
@@ -0,0 +1,20 @@
+//
+// PSMTabDragWindow.h
+// PSMTabBarControl
+//
+// Created by Kent Sutherland on 6/1/06.
+// Copyright 2006 Kent Sutherland. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+@class PSMTabDragView;
+
+@interface PSMTabDragWindow : NSWindow {
+ PSMTabDragView *_dragView;
+}
++ (PSMTabDragWindow *)dragWindowWithImage:(NSImage *)image styleMask:(NSUInteger)styleMask;
+
+- (id)initWithImage:(NSImage *)image styleMask:(NSUInteger)styleMask;
+- (PSMTabDragView *)dragView;
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabDragWindow.m b/frontends/cocoa/PSMTabBarControl/PSMTabDragWindow.m
new file mode 100644
index 000000000..d6fcc95a3
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabDragWindow.m
@@ -0,0 +1,48 @@
+//
+// PSMTabDragWindow.m
+// PSMTabBarControl
+//
+// Created by Kent Sutherland on 6/1/06.
+// Copyright 2006 Kent Sutherland. All rights reserved.
+//
+
+#import "PSMTabDragWindow.h"
+#import "PSMTabDragView.h"
+
+@implementation PSMTabDragWindow
+
++ (PSMTabDragWindow *)dragWindowWithImage:(NSImage *)image styleMask:(NSUInteger)styleMask {
+ return [[[PSMTabDragWindow alloc] initWithImage:image styleMask:styleMask] autorelease];
+}
+
+- (id)initWithImage:(NSImage *)image styleMask:(NSUInteger)styleMask {
+ NSSize size = [image size];
+
+ if((self = [super initWithContentRect:NSMakeRect(0, 0, size.width, size.height) styleMask:styleMask backing:NSBackingStoreBuffered defer:NO])) {
+ _dragView = [[[PSMTabDragView alloc] initWithFrame:NSMakeRect(0, 0, size.width, size.height)] autorelease];
+ [self setContentView:_dragView];
+ [self setLevel:NSStatusWindowLevel];
+ [self setIgnoresMouseEvents:YES];
+ [self setOpaque:NO];
+
+ [_dragView setImage:image];
+
+ //Set the size of the window to be the exact size of the drag image
+ NSRect windowFrame = [self frame];
+ windowFrame.origin.y += windowFrame.size.height - size.height;
+ windowFrame.size = size;
+
+ if(styleMask | NSBorderlessWindowMask) {
+ windowFrame.size.height += 22;
+ }
+
+ [self setFrame:windowFrame display:YES];
+ }
+ return self;
+}
+
+- (PSMTabDragView *)dragView {
+ return _dragView;
+}
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabDragWindowController.h b/frontends/cocoa/PSMTabBarControl/PSMTabDragWindowController.h
new file mode 100644
index 000000000..5948207f2
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabDragWindowController.h
@@ -0,0 +1,33 @@
+//
+// PSMTabDragWindowController.h
+// PSMTabBarControl
+//
+// Created by Kent Sutherland on 6/18/07.
+// Copyright 2007 Kent Sutherland. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "PSMTabBarControl.h"
+
+#define kPSMTabDragWindowAlpha 0.75
+#define kPSMTabDragAlphaInterval 0.15
+
+@class PSMTabDragView;
+
+@interface PSMTabDragWindowController : NSWindowController {
+ PSMTabBarTearOffStyle _tearOffStyle;
+ PSMTabDragView *_view;
+ NSAnimation *_animation;
+ NSTimer *_timer;
+
+ BOOL _showingAlternate;
+ NSRect _originalWindowFrame;
+}
+- (id)initWithImage:(NSImage *)image styleMask:(NSUInteger) styleMask tearOffStyle:(PSMTabBarTearOffStyle)tearOffStyle;
+
+- (NSImage *)image;
+- (NSImage *)alternateImage;
+- (void)setAlternateImage:(NSImage *)image;
+- (BOOL)isAnimating;
+- (void)switchImages;
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabDragWindowController.m b/frontends/cocoa/PSMTabBarControl/PSMTabDragWindowController.m
new file mode 100644
index 000000000..3a6e8c663
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabDragWindowController.m
@@ -0,0 +1,111 @@
+//
+// PSMTabDragWindowController.m
+// PSMTabBarControl
+//
+// Created by Kent Sutherland on 6/18/07.
+// Copyright 2007 Kent Sutherland. All rights reserved.
+//
+
+#import "PSMTabDragWindowController.h"
+#import "PSMTabDragWindow.h"
+#import "PSMTabDragView.h"
+
+@implementation PSMTabDragWindowController
+
+- (id)initWithImage:(NSImage *)image styleMask:(NSUInteger)styleMask tearOffStyle:(PSMTabBarTearOffStyle)tearOffStyle {
+ PSMTabDragWindow *window = [PSMTabDragWindow dragWindowWithImage:image styleMask:styleMask];
+ if((self = [super initWithWindow:window])) {
+ _view = [[window dragView] retain];
+ _tearOffStyle = tearOffStyle;
+
+ if(tearOffStyle == PSMTabBarTearOffMiniwindow) {
+ [window setBackgroundColor:[NSColor clearColor]];
+ [window setHasShadow:YES];
+ }
+
+ [window setAlphaValue:kPSMTabDragWindowAlpha];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ if(_timer) {
+ [_timer invalidate];
+ }
+
+ if(_animation) {
+ [_animation release];
+ }
+
+ [_view release];
+ [super dealloc];
+}
+
+- (NSImage *)image {
+ return [_view image];
+}
+
+- (NSImage *)alternateImage {
+ return [_view alternateImage];
+}
+
+- (void)setAlternateImage:(NSImage *)image {
+ [_view setAlternateImage:image];
+}
+
+- (BOOL)isAnimating {
+ return _animation != nil;
+}
+
+- (void)switchImages {
+ if(_tearOffStyle != PSMTabBarTearOffMiniwindow || ![_view alternateImage]) {
+ return;
+ }
+
+ CGFloat progress = 0;
+ _showingAlternate = !_showingAlternate;
+
+ if(_animation) {
+ //An animation already exists, get the current progress
+ progress = 1.0f - [_animation currentProgress];
+ [_animation stopAnimation];
+ [_animation release];
+ }
+
+ //begin animating
+ _animation = [[NSAnimation alloc] initWithDuration:0.25 animationCurve:NSAnimationEaseInOut];
+ [_animation setAnimationBlockingMode:NSAnimationNonblocking];
+ [_animation setCurrentProgress:progress];
+ [_animation startAnimation];
+
+ _originalWindowFrame = [[self window] frame];
+
+ if(_timer) {
+ [_timer invalidate];
+ }
+ _timer = [NSTimer scheduledTimerWithTimeInterval:1.0f / 30.0f target:self selector:@selector(animateTimer:) userInfo:nil repeats:YES];
+}
+
+- (void)animateTimer:(NSTimer *)timer {
+ NSRect frame = _originalWindowFrame;
+ NSImage *currentImage = _showingAlternate ?[_view alternateImage] :[_view image];
+ NSSize size = [currentImage size];
+ NSPoint mousePoint = [NSEvent mouseLocation];
+ CGFloat animationValue = [_animation currentValue];
+
+ frame.size.width = _originalWindowFrame.size.width + (size.width - _originalWindowFrame.size.width) * animationValue;
+ frame.size.height = _originalWindowFrame.size.height + (size.height - _originalWindowFrame.size.height) * animationValue;
+ frame.origin.x = mousePoint.x - (frame.size.width / 2);
+ frame.origin.y = mousePoint.y - (frame.size.height / 2);
+
+ [_view setFadeValue:_showingAlternate ? 1.0f - animationValue : animationValue];
+ [[self window] setFrame:frame display:YES];
+
+ if(![_animation isAnimating]) {
+ [_animation release], _animation = nil;
+ [timer invalidate];
+ _timer = nil;
+ }
+}
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabStyle.h b/frontends/cocoa/PSMTabBarControl/PSMTabStyle.h
new file mode 100644
index 000000000..ca3717435
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabStyle.h
@@ -0,0 +1,57 @@
+//
+// PSMTabStyle.h
+// PSMTabBarControl
+//
+// Created by John Pannell on 2/17/06.
+// Copyright 2006 Positive Spin Media. All rights reserved.
+//
+
+/*
+ Protocol to be observed by all style delegate objects. These objects handle the drawing responsibilities for PSMTabBarCell; once the control has been assigned a style, the background and cells draw consistent with that style. Design pattern and implementation by David Smith, Seth Willits, and Chris Forsythe, all touch up and errors by John P. :-)
+ */
+
+#import "PSMTabBarCell.h"
+#import "PSMTabBarControl.h"
+
+@protocol PSMTabStyle <NSObject>
+
+// identity
+- (NSString *)name;
+
+// control specific parameters
+- (CGFloat)leftMarginForTabBarControl;
+- (CGFloat)rightMarginForTabBarControl;
+- (CGFloat)topMarginForTabBarControl;
+- (void)setOrientation:(PSMTabBarOrientation)value;
+
+// add tab button
+- (NSImage *)addTabButtonImage;
+- (NSImage *)addTabButtonPressedImage;
+- (NSImage *)addTabButtonRolloverImage;
+
+// cell specific parameters
+- (NSRect)dragRectForTabCell:(PSMTabBarCell *)cell orientation:(PSMTabBarOrientation)orientation;
+- (NSRect)closeButtonRectForTabCell:(PSMTabBarCell *)cell withFrame:(NSRect)cellFrame;
+- (NSRect)iconRectForTabCell:(PSMTabBarCell *)cell;
+- (NSRect)indicatorRectForTabCell:(PSMTabBarCell *)cell;
+- (NSRect)objectCounterRectForTabCell:(PSMTabBarCell *)cell;
+- (CGFloat)minimumWidthOfTabCell:(PSMTabBarCell *)cell;
+- (CGFloat)desiredWidthOfTabCell:(PSMTabBarCell *)cell;
+- (CGFloat)tabCellHeight;
+
+// cell values
+- (NSAttributedString *)attributedObjectCountValueForTabCell:(PSMTabBarCell *)cell;
+- (NSAttributedString *)attributedStringValueForTabCell:(PSMTabBarCell *)cell;
+
+// drawing
+- (void)drawTabCell:(PSMTabBarCell *)cell;
+- (void)drawBackgroundInRect:(NSRect)rect;
+- (void)drawTabBar:(PSMTabBarControl *)bar inRect:(NSRect)rect;
+
+@end
+
+@interface PSMTabBarControl (StyleAccessors)
+
+- (NSMutableArray *)cells;
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMUnifiedTabStyle.h b/frontends/cocoa/PSMTabBarControl/PSMUnifiedTabStyle.h
new file mode 100644
index 000000000..20202536a
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMUnifiedTabStyle.h
@@ -0,0 +1,29 @@
+//
+// PSMUnifiedTabStyle.h
+// --------------------
+//
+// Created by Keith Blount on 30/04/2006.
+// Copyright 2006 __MyCompanyName__. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "PSMTabStyle.h"
+
+@interface PSMUnifiedTabStyle : NSObject <PSMTabStyle> {
+ NSImage *unifiedCloseButton;
+ NSImage *unifiedCloseButtonDown;
+ NSImage *unifiedCloseButtonOver;
+ NSImage *unifiedCloseDirtyButton;
+ NSImage *unifiedCloseDirtyButtonDown;
+ NSImage *unifiedCloseDirtyButtonOver;
+ NSImage *_addTabButtonImage;
+ NSImage *_addTabButtonPressedImage;
+ NSImage *_addTabButtonRolloverImage;
+
+ NSDictionary *_objectCountStringAttributes;
+
+ CGFloat leftMargin;
+ PSMTabBarControl *tabBar;
+}
+- (void)setLeftMarginForTabBarControl:(CGFloat)margin;
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMUnifiedTabStyle.m b/frontends/cocoa/PSMTabBarControl/PSMUnifiedTabStyle.m
new file mode 100644
index 000000000..77be601be
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMUnifiedTabStyle.m
@@ -0,0 +1,573 @@
+//
+// PSMUnifiedTabStyle.m
+// --------------------
+//
+// Created by Keith Blount on 30/04/2006.
+// Copyright 2006 __MyCompanyName__. All rights reserved.
+//
+
+#import "PSMUnifiedTabStyle.h"
+#import "PSMTabBarCell.h"
+#import "PSMTabBarControl.h"
+#import "NSBezierPath_AMShading.h"
+
+#define kPSMUnifiedObjectCounterRadius 7.0
+#define kPSMUnifiedCounterMinWidth 20
+
+@interface PSMUnifiedTabStyle (Private)
+- (void)drawInteriorWithTabCell:(PSMTabBarCell *)cell inView:(NSView*)controlView;
+@end
+
+@implementation PSMUnifiedTabStyle
+
+- (NSString *)name {
+ return @"Unified";
+}
+
+#pragma mark -
+#pragma mark Creation/Destruction
+
+- (id) init {
+ if((self = [super init])) {
+ unifiedCloseButton = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front"]];
+ unifiedCloseButtonDown = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front_Pressed"]];
+ unifiedCloseButtonOver = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front_Rollover"]];
+
+ unifiedCloseDirtyButton = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabCloseDirty_Front"]];
+ unifiedCloseDirtyButtonDown = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabCloseDirty_Front_Pressed"]];
+ unifiedCloseDirtyButtonOver = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabCloseDirty_Front_Rollover"]];
+
+ _addTabButtonImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNew"]];
+ _addTabButtonPressedImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNewPressed"]];
+ _addTabButtonRolloverImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNewRollover"]];
+
+ _objectCountStringAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:[[NSFontManager sharedFontManager] convertFont:[NSFont fontWithName:@"Helvetica" size:11.0] toHaveTrait:NSBoldFontMask], NSFontAttributeName,
+ [[NSColor whiteColor] colorWithAlphaComponent:0.85], NSForegroundColorAttributeName,
+ nil, nil];
+
+ leftMargin = 5.0;
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [unifiedCloseButton release];
+ [unifiedCloseButtonDown release];
+ [unifiedCloseButtonOver release];
+ [unifiedCloseDirtyButton release];
+ [unifiedCloseDirtyButtonDown release];
+ [unifiedCloseDirtyButtonOver release];
+ [_addTabButtonImage release];
+ [_addTabButtonPressedImage release];
+ [_addTabButtonRolloverImage release];
+
+ [_objectCountStringAttributes release];
+
+ [super dealloc];
+}
+
+#pragma mark -
+#pragma mark Control Specific
+
+- (void)setLeftMarginForTabBarControl:(CGFloat)margin {
+ leftMargin = margin;
+}
+
+- (CGFloat)leftMarginForTabBarControl {
+ return leftMargin;
+}
+
+- (CGFloat)rightMarginForTabBarControl {
+ return 24.0f;
+}
+
+- (CGFloat)topMarginForTabBarControl {
+ return 10.0f;
+}
+
+- (void)setOrientation:(PSMTabBarOrientation)value {
+}
+
+#pragma mark -
+#pragma mark Add Tab Button
+
+- (NSImage *)addTabButtonImage {
+ return _addTabButtonImage;
+}
+
+- (NSImage *)addTabButtonPressedImage {
+ return _addTabButtonPressedImage;
+}
+
+- (NSImage *)addTabButtonRolloverImage {
+ return _addTabButtonRolloverImage;
+}
+
+#pragma mark -
+#pragma mark Cell Specific
+
+- (NSRect)dragRectForTabCell:(PSMTabBarCell *)cell orientation:(PSMTabBarOrientation)orientation {
+ NSRect dragRect = [cell frame];
+ dragRect.size.width++;
+ return dragRect;
+}
+
+- (NSRect)closeButtonRectForTabCell:(PSMTabBarCell *)cell withFrame:(NSRect)cellFrame {
+ if([cell hasCloseButton] == NO) {
+ return NSZeroRect;
+ }
+
+ NSRect result;
+ result.size = [unifiedCloseButton size];
+ result.origin.x = cellFrame.origin.x + MARGIN_X;
+ result.origin.y = cellFrame.origin.y + MARGIN_Y + 1.0;
+
+ return result;
+}
+
+- (NSRect)iconRectForTabCell:(PSMTabBarCell *)cell {
+ NSRect cellFrame = [cell frame];
+
+ if([cell hasIcon] == NO) {
+ return NSZeroRect;
+ }
+
+ NSRect result;
+ result.size = NSMakeSize(kPSMTabBarIconWidth, kPSMTabBarIconWidth);
+ result.origin.x = cellFrame.origin.x + MARGIN_X;
+ result.origin.y = cellFrame.origin.y + MARGIN_Y - 1.0;
+
+ if([cell hasCloseButton] && ![cell isCloseButtonSuppressed]) {
+ result.origin.x += [unifiedCloseButton size].width + kPSMTabBarCellPadding;
+ }
+
+ return result;
+}
+
+- (NSRect)indicatorRectForTabCell:(PSMTabBarCell *)cell {
+ NSRect cellFrame = [cell frame];
+
+ if([[cell indicator] isHidden]) {
+ return NSZeroRect;
+ }
+
+ NSRect result;
+ result.size = NSMakeSize(kPSMTabBarIndicatorWidth, kPSMTabBarIndicatorWidth);
+ result.origin.x = cellFrame.origin.x + cellFrame.size.width - MARGIN_X - kPSMTabBarIndicatorWidth;
+ result.origin.y = cellFrame.origin.y + MARGIN_Y - 1.0;
+
+ return result;
+}
+
+- (NSRect)objectCounterRectForTabCell:(PSMTabBarCell *)cell {
+ NSRect cellFrame = [cell frame];
+
+ if([cell count] == 0) {
+ return NSZeroRect;
+ }
+
+ CGFloat countWidth = [[self attributedObjectCountValueForTabCell:cell] size].width;
+ countWidth += (2 * kPSMUnifiedObjectCounterRadius - 6.0);
+ if(countWidth < kPSMUnifiedCounterMinWidth) {
+ countWidth = kPSMUnifiedCounterMinWidth;
+ }
+
+ NSRect result;
+ result.size = NSMakeSize(countWidth, 2 * kPSMUnifiedObjectCounterRadius); // temp
+ result.origin.x = cellFrame.origin.x + cellFrame.size.width - MARGIN_X - result.size.width;
+ result.origin.y = cellFrame.origin.y + MARGIN_Y + 1.0;
+
+ if(![[cell indicator] isHidden]) {
+ result.origin.x -= kPSMTabBarIndicatorWidth + kPSMTabBarCellPadding;
+ }
+
+ return result;
+}
+
+
+- (CGFloat)minimumWidthOfTabCell:(PSMTabBarCell *)cell {
+ CGFloat resultWidth = 0.0;
+
+ // left margin
+ resultWidth = MARGIN_X;
+
+ // close button?
+ if([cell hasCloseButton] && ![cell isCloseButtonSuppressed]) {
+ resultWidth += [unifiedCloseButton size].width + kPSMTabBarCellPadding;
+ }
+
+ // icon?
+ if([cell hasIcon]) {
+ resultWidth += kPSMTabBarIconWidth + kPSMTabBarCellPadding;
+ }
+
+ // the label
+ resultWidth += kPSMMinimumTitleWidth;
+
+ // object counter?
+ if([cell count] > 0) {
+ resultWidth += [self objectCounterRectForTabCell:cell].size.width + kPSMTabBarCellPadding;
+ }
+
+ // indicator?
+ if([[cell indicator] isHidden] == NO) {
+ resultWidth += kPSMTabBarCellPadding + kPSMTabBarIndicatorWidth;
+ }
+
+ // right margin
+ resultWidth += MARGIN_X;
+
+ return ceil(resultWidth);
+}
+
+- (CGFloat)desiredWidthOfTabCell:(PSMTabBarCell *)cell {
+ CGFloat resultWidth = 0.0;
+
+ // left margin
+ resultWidth = MARGIN_X;
+
+ // close button?
+ if([cell hasCloseButton] && ![cell isCloseButtonSuppressed]) {
+ resultWidth += [unifiedCloseButton size].width + kPSMTabBarCellPadding;
+ }
+
+ // icon?
+ if([cell hasIcon]) {
+ resultWidth += kPSMTabBarIconWidth + kPSMTabBarCellPadding;
+ }
+
+ // the label
+ resultWidth += [[cell attributedStringValue] size].width;
+
+ // object counter?
+ if([cell count] > 0) {
+ resultWidth += [self objectCounterRectForTabCell:cell].size.width + kPSMTabBarCellPadding;
+ }
+
+ // indicator?
+ if([[cell indicator] isHidden] == NO) {
+ resultWidth += kPSMTabBarCellPadding + kPSMTabBarIndicatorWidth;
+ }
+
+ // right margin
+ resultWidth += MARGIN_X;
+
+ return ceil(resultWidth);
+}
+
+- (CGFloat)tabCellHeight {
+ return kPSMTabBarControlHeight;
+}
+
+#pragma mark -
+#pragma mark Cell Values
+
+- (NSAttributedString *)attributedObjectCountValueForTabCell:(PSMTabBarCell *)cell {
+ NSString *contents = [NSString stringWithFormat:@"%lu", (unsigned long)[cell count]];
+ return [[[NSMutableAttributedString alloc] initWithString:contents attributes:_objectCountStringAttributes] autorelease];
+}
+
+- (NSAttributedString *)attributedStringValueForTabCell:(PSMTabBarCell *)cell {
+ NSMutableAttributedString *attrStr;
+ NSString * contents = [cell stringValue];
+ attrStr = [[[NSMutableAttributedString alloc] initWithString:contents] autorelease];
+ NSRange range = NSMakeRange(0, [contents length]);
+
+ [attrStr addAttribute:NSFontAttributeName value:[NSFont systemFontOfSize:11.0] range:range];
+
+ // Paragraph Style for Truncating Long Text
+ static NSMutableParagraphStyle *TruncatingTailParagraphStyle = nil;
+ if(!TruncatingTailParagraphStyle) {
+ TruncatingTailParagraphStyle = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain];
+ [TruncatingTailParagraphStyle setLineBreakMode:NSLineBreakByTruncatingTail];
+ }
+ [attrStr addAttribute:NSParagraphStyleAttributeName value:TruncatingTailParagraphStyle range:range];
+
+ return attrStr;
+}
+
+#pragma mark -
+#pragma mark ---- drawing ----
+
+- (void)drawTabCell:(PSMTabBarCell *)cell {
+ NSRect cellFrame = [cell frame];
+
+ NSToolbar *toolbar = [[[cell controlView] window] toolbar];
+ BOOL showsBaselineSeparator = (toolbar && [toolbar respondsToSelector:@selector(showsBaselineSeparator)] && [toolbar showsBaselineSeparator]);
+ if(!showsBaselineSeparator) {
+ cellFrame.origin.y += 1.0;
+ cellFrame.size.height -= 1.0;
+ }
+
+ NSColor * lineColor = nil;
+ NSBezierPath* bezier = [NSBezierPath bezierPath];
+ lineColor = [NSColor colorWithCalibratedWhite:0.576 alpha:1.0];
+
+ if(!showsBaselineSeparator || [cell state] == NSOnState) {
+ // selected tab
+ NSRect aRect = NSMakeRect(cellFrame.origin.x + 0.5, cellFrame.origin.y - 0.5, cellFrame.size.width, cellFrame.size.height);
+
+ // frame
+ CGFloat radius = MIN(6.0, 0.5f * MIN(NSWidth(aRect), NSHeight(aRect)));
+ NSRect rect = NSInsetRect(aRect, radius, radius);
+
+ [bezier appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(rect), NSMinY(rect)) radius:radius startAngle:180.0 endAngle:270.0];
+
+ [bezier appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(rect), NSMinY(rect)) radius:radius startAngle:270.0 endAngle:360.0];
+
+ NSPoint cornerPoint = NSMakePoint(NSMaxX(aRect), NSMaxY(aRect));
+ [bezier appendBezierPathWithPoints:&cornerPoint count:1];
+
+ cornerPoint = NSMakePoint(NSMinX(aRect), NSMaxY(aRect));
+ [bezier appendBezierPathWithPoints:&cornerPoint count:1];
+
+ [bezier closePath];
+
+ //[[NSColor windowBackgroundColor] set];
+ //[bezier fill];
+ if([NSApp isActive]) {
+ if([cell state] == NSOnState) {
+ [bezier linearGradientFillWithStartColor:[NSColor colorWithCalibratedWhite:0.99 alpha:1.0]
+ endColor:[NSColor colorWithCalibratedWhite:0.941 alpha:1.0]];
+ } else if([cell isHighlighted]) {
+ [bezier linearGradientFillWithStartColor:[NSColor colorWithCalibratedWhite:0.80 alpha:1.0]
+ endColor:[NSColor colorWithCalibratedWhite:0.80 alpha:1.0]];
+ } else {
+ [bezier linearGradientFillWithStartColor:[NSColor colorWithCalibratedWhite:0.835 alpha:1.0]
+ endColor:[NSColor colorWithCalibratedWhite:0.843 alpha:1.0]];
+ }
+ }
+
+ [lineColor set];
+ [bezier stroke];
+ } else{
+ // unselected tab
+ NSRect aRect = NSMakeRect(cellFrame.origin.x, cellFrame.origin.y, cellFrame.size.width, cellFrame.size.height);
+ aRect.origin.y += 0.5;
+ aRect.origin.x += 1.5;
+ aRect.size.width -= 1;
+
+ aRect.origin.x -= 1;
+ aRect.size.width += 1;
+
+ // rollover
+ if([cell isHighlighted]) {
+ [[NSColor colorWithCalibratedWhite:0.0 alpha:0.1] set];
+ NSRectFillUsingOperation(aRect, NSCompositeSourceAtop);
+ }
+
+ // frame
+
+ [lineColor set];
+ [bezier moveToPoint:NSMakePoint(aRect.origin.x + aRect.size.width, aRect.origin.y - 0.5)];
+ if(!([cell tabState] & PSMTab_RightIsSelectedMask)) {
+ [bezier lineToPoint:NSMakePoint(NSMaxX(aRect), NSMaxY(aRect))];
+ }
+
+ [bezier stroke];
+
+ // Create a thin lighter line next to the dividing line for a bezel effect
+ if(!([cell tabState] & PSMTab_RightIsSelectedMask)) {
+ [[[NSColor whiteColor] colorWithAlphaComponent:0.5] set];
+ [NSBezierPath strokeLineFromPoint:NSMakePoint(NSMaxX(aRect) + 1.0, aRect.origin.y - 0.5)
+ toPoint:NSMakePoint(NSMaxX(aRect) + 1.0, NSMaxY(aRect) - 2.5)];
+ }
+
+ // If this is the leftmost tab, we want to draw a line on the left, too
+ if([cell tabState] & PSMTab_PositionLeftMask) {
+ [lineColor set];
+ [NSBezierPath strokeLineFromPoint:NSMakePoint(aRect.origin.x, aRect.origin.y - 0.5)
+ toPoint:NSMakePoint(aRect.origin.x, NSMaxY(aRect) - 2.5)];
+ [[[NSColor whiteColor] colorWithAlphaComponent:0.5] set];
+ [NSBezierPath strokeLineFromPoint:NSMakePoint(aRect.origin.x + 1.0, aRect.origin.y - 0.5)
+ toPoint:NSMakePoint(aRect.origin.x + 1.0, NSMaxY(aRect) - 2.5)];
+ }
+ }
+
+ [self drawInteriorWithTabCell:cell inView:[cell controlView]];
+}
+
+
+- (void)drawInteriorWithTabCell:(PSMTabBarCell *)cell inView:(NSView*)controlView {
+ NSRect cellFrame = [cell frame];
+ CGFloat labelPosition = cellFrame.origin.x + MARGIN_X;
+
+ // close button
+ if([cell hasCloseButton] && ![cell isCloseButtonSuppressed]) {
+ NSSize closeButtonSize = NSZeroSize;
+ NSRect closeButtonRect = [cell closeButtonRectForFrame:cellFrame];
+ NSImage * closeButton = nil;
+
+ closeButton = [cell isEdited] ? unifiedCloseDirtyButton : unifiedCloseButton;
+
+ if([cell closeButtonOver]) {
+ closeButton = [cell isEdited] ? unifiedCloseDirtyButtonOver : unifiedCloseButtonOver;
+ }
+ if([cell closeButtonPressed]) {
+ closeButton = [cell isEdited] ? unifiedCloseDirtyButtonDown : unifiedCloseButtonDown;
+ }
+
+ closeButtonSize = [closeButton size];
+ if([controlView isFlipped]) {
+ closeButtonRect.origin.y += closeButtonRect.size.height;
+ }
+
+ [closeButton compositeToPoint:closeButtonRect.origin operation:NSCompositeSourceOver fraction:1.0];
+
+ // scoot label over
+ labelPosition += closeButtonSize.width + kPSMTabBarCellPadding;
+ }
+
+ // icon
+ if([cell hasIcon]) {
+ NSRect iconRect = [self iconRectForTabCell:cell];
+ NSImage *icon = [[[cell representedObject] identifier] icon];
+ if([controlView isFlipped]) {
+ iconRect.origin.y += iconRect.size.height;
+ }
+
+ // center in available space (in case icon image is smaller than kPSMTabBarIconWidth)
+ if([icon size].width < kPSMTabBarIconWidth) {
+ iconRect.origin.x += (kPSMTabBarIconWidth - [icon size].width) / 2.0;
+ }
+ if([icon size].height < kPSMTabBarIconWidth) {
+ iconRect.origin.y -= (kPSMTabBarIconWidth - [icon size].height) / 2.0;
+ }
+
+ [icon compositeToPoint:iconRect.origin operation:NSCompositeSourceOver fraction:1.0];
+
+ // scoot label over
+ labelPosition += iconRect.size.width + kPSMTabBarCellPadding;
+ }
+
+ // label rect
+ NSRect labelRect;
+ labelRect.origin.x = labelPosition;
+ labelRect.size.width = cellFrame.size.width - (labelRect.origin.x - cellFrame.origin.x) - kPSMTabBarCellPadding;
+ NSSize s = [[cell attributedStringValue] size];
+ labelRect.origin.y = cellFrame.origin.y + (cellFrame.size.height - s.height) / 2.0 - 1.0;
+ labelRect.size.height = s.height;
+
+ if(![[cell indicator] isHidden]) {
+ labelRect.size.width -= (kPSMTabBarIndicatorWidth + kPSMTabBarCellPadding);
+ }
+
+ // object counter
+ if([cell count] > 0) {
+ [[cell countColor] ?: [NSColor colorWithCalibratedWhite:0.3 alpha:0.6] set];
+ NSBezierPath *path = [NSBezierPath bezierPath];
+ NSRect myRect = [self objectCounterRectForTabCell:cell];
+ myRect.origin.y -= 1.0;
+ [path moveToPoint:NSMakePoint(myRect.origin.x + kPSMUnifiedObjectCounterRadius, myRect.origin.y)];
+ [path lineToPoint:NSMakePoint(myRect.origin.x + myRect.size.width - kPSMUnifiedObjectCounterRadius, myRect.origin.y)];
+ [path appendBezierPathWithArcWithCenter:NSMakePoint(myRect.origin.x + myRect.size.width - kPSMUnifiedObjectCounterRadius, myRect.origin.y + kPSMUnifiedObjectCounterRadius) radius:kPSMUnifiedObjectCounterRadius startAngle:270.0 endAngle:90.0];
+ [path lineToPoint:NSMakePoint(myRect.origin.x + kPSMUnifiedObjectCounterRadius, myRect.origin.y + myRect.size.height)];
+ [path appendBezierPathWithArcWithCenter:NSMakePoint(myRect.origin.x + kPSMUnifiedObjectCounterRadius, myRect.origin.y + kPSMUnifiedObjectCounterRadius) radius:kPSMUnifiedObjectCounterRadius startAngle:90.0 endAngle:270.0];
+ [path fill];
+
+ // draw attributed string centered in area
+ NSRect counterStringRect;
+ NSAttributedString *counterString = [self attributedObjectCountValueForTabCell:cell];
+ counterStringRect.size = [counterString size];
+ counterStringRect.origin.x = myRect.origin.x + ((myRect.size.width - counterStringRect.size.width) / 2.0) + 0.25;
+ counterStringRect.origin.y = myRect.origin.y + ((myRect.size.height - counterStringRect.size.height) / 2.0) + 0.5;
+ [counterString drawInRect:counterStringRect];
+
+ labelRect.size.width -= myRect.size.width + kPSMTabBarCellPadding;
+ }
+
+ // label
+ [[cell attributedStringValue] drawInRect:labelRect];
+}
+
+- (void)drawBackgroundInRect:(NSRect)rect {
+ //Draw for our whole bounds; it'll be automatically clipped to fit the appropriate drawing area
+ rect = [tabBar bounds];
+
+ NSRect gradientRect = rect;
+ gradientRect.size.height -= 1.0;
+
+ NSBezierPath *path = [NSBezierPath bezierPathWithRect:gradientRect];
+ [path linearGradientFillWithStartColor:[NSColor colorWithCalibratedWhite:0.835 alpha:1.0]
+ endColor:[NSColor colorWithCalibratedWhite:0.843 alpha:1.0]];
+ [[NSColor colorWithCalibratedWhite:0.576 alpha:1.0] set];
+ [NSBezierPath strokeLineFromPoint:NSMakePoint(rect.origin.x, NSMaxY(rect) - 0.5)
+ toPoint:NSMakePoint(NSMaxX(rect), NSMaxY(rect) - 0.5)];
+
+ if(![[[tabBar tabView] window] isKeyWindow]) {
+ [[NSColor windowBackgroundColor] set];
+ NSRectFill(gradientRect);
+ }
+}
+
+- (void)drawTabBar:(PSMTabBarControl *)bar inRect:(NSRect)rect {
+ tabBar = bar;
+ [self drawBackgroundInRect:rect];
+
+ // no tab view == not connected
+ if(![bar tabView]) {
+ NSRect labelRect = rect;
+ labelRect.size.height -= 4.0;
+ labelRect.origin.y += 4.0;
+ NSMutableAttributedString *attrStr;
+ NSString *contents = @"PSMTabBarControl";
+ attrStr = [[[NSMutableAttributedString alloc] initWithString:contents] autorelease];
+ NSRange range = NSMakeRange(0, [contents length]);
+ [attrStr addAttribute:NSFontAttributeName value:[NSFont systemFontOfSize:11.0] range:range];
+ NSMutableParagraphStyle *centeredParagraphStyle = nil;
+ if(!centeredParagraphStyle) {
+ centeredParagraphStyle = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain];
+ [centeredParagraphStyle setAlignment:NSCenterTextAlignment];
+ }
+ [attrStr addAttribute:NSParagraphStyleAttributeName value:centeredParagraphStyle range:range];
+ [attrStr drawInRect:labelRect];
+ return;
+ }
+
+ // draw cells
+ NSEnumerator *e = [[bar cells] objectEnumerator];
+ PSMTabBarCell *cell;
+ while((cell = [e nextObject])) {
+ if([bar isAnimating] || (![cell isInOverflowMenu] && NSIntersectsRect([cell frame], rect))) {
+ [cell drawWithFrame:[cell frame] inView:bar];
+ }
+ }
+}
+
+#pragma mark -
+#pragma mark Archiving
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+ //[super encodeWithCoder:aCoder];
+ if([aCoder allowsKeyedCoding]) {
+ [aCoder encodeObject:unifiedCloseButton forKey:@"unifiedCloseButton"];
+ [aCoder encodeObject:unifiedCloseButtonDown forKey:@"unifiedCloseButtonDown"];
+ [aCoder encodeObject:unifiedCloseButtonOver forKey:@"unifiedCloseButtonOver"];
+ [aCoder encodeObject:unifiedCloseDirtyButton forKey:@"unifiedCloseDirtyButton"];
+ [aCoder encodeObject:unifiedCloseDirtyButtonDown forKey:@"unifiedCloseDirtyButtonDown"];
+ [aCoder encodeObject:unifiedCloseDirtyButtonOver forKey:@"unifiedCloseDirtyButtonOver"];
+ [aCoder encodeObject:_addTabButtonImage forKey:@"addTabButtonImage"];
+ [aCoder encodeObject:_addTabButtonPressedImage forKey:@"addTabButtonPressedImage"];
+ [aCoder encodeObject:_addTabButtonRolloverImage forKey:@"addTabButtonRolloverImage"];
+ }
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder {
+ // self = [super initWithCoder:aDecoder];
+ //if (self) {
+ if([aDecoder allowsKeyedCoding]) {
+ unifiedCloseButton = [[aDecoder decodeObjectForKey:@"unifiedCloseButton"] retain];
+ unifiedCloseButtonDown = [[aDecoder decodeObjectForKey:@"unifiedCloseButtonDown"] retain];
+ unifiedCloseButtonOver = [[aDecoder decodeObjectForKey:@"unifiedCloseButtonOver"] retain];
+ unifiedCloseDirtyButton = [[aDecoder decodeObjectForKey:@"unifiedCloseDirtyButton"] retain];
+ unifiedCloseDirtyButtonDown = [[aDecoder decodeObjectForKey:@"unifiedCloseDirtyButtonDown"] retain];
+ unifiedCloseDirtyButtonOver = [[aDecoder decodeObjectForKey:@"unifiedCloseDirtyButtonOver"] retain];
+ _addTabButtonImage = [[aDecoder decodeObjectForKey:@"addTabButtonImage"] retain];
+ _addTabButtonPressedImage = [[aDecoder decodeObjectForKey:@"addTabButtonPressedImage"] retain];
+ _addTabButtonRolloverImage = [[aDecoder decodeObjectForKey:@"addTabButtonRolloverImage"] retain];
+ }
+ //}
+ return self;
+}
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/ReadMe.rtfd/TXT.rtf b/frontends/cocoa/PSMTabBarControl/ReadMe.rtfd/TXT.rtf
new file mode 100644
index 000000000..acd9372a2
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/ReadMe.rtfd/TXT.rtf
@@ -0,0 +1,186 @@
+{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf380
+{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;\f1\fswiss\fcharset77 Helvetica;\f2\fswiss\fcharset77 Helvetica-Oblique;
+\f3\fnil\fcharset77 Monaco;}
+{\colortbl;\red255\green255\blue255;\red118\green15\blue80;\red0\green0\blue255;\red35\green110\blue37;
+}
+{\*\listtable{\list\listtemplateid1\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc2\leveljcn2\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid0\'02\'05.;}{\levelnumbers\'01;}}{\listname ;}\listid1}}
+{\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}}
+\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\qc\pardirnatural
+
+\f0\b\fs24 \cf0 \
+PSMTabBarControl (and related classes)\
+
+\f1\b0 developed by John Pannell, Positive Spin Media\
+\
+as seen in the super-cool app...\
+\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\qc\pardirnatural
+\cf0 {{\NeXTGraphic startpage.gif \width7200 \height2820
+}¬}\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\qc\pardirnatural
+\cf0 \
+\
+\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\ql\qnatural\pardirnatural
+\cf0 This source code and all related materials are released under the BSD license, which is explained at the end of this document, along with some other legalese. I've made my best effort to make everything bug free, but please let me know of any bugs found or suggestions you have: johnp@positivespinmedia.com.\
+\
+
+\f0\b Purpose
+\f1\b0 \
+\
+PSMTabBarControl seeks to provide developers with a high-quality, easy to use GUI to manage an NSTabView (or subclasses) in a manner similar to Safari's tabbed browsing implementation. It attempts to add a few features as well. Here's what you get:\
+\
+
+\f0\b The look:
+\f1\b0 a control/cell architecture that draws the expected tab appearance below a toolbar or similar view. Included styles work consistently in Aqua, Metal, or customized metal variations by basing fills on the window's background color. Includes drawing of a close button, and rollover states for the close button and tab cell. Also provides pop-up button and menu when tabs overflow available space, and support for individual tab progress indicators, icons, and object counters. Tabs can be drawn sized to fit the string content of the label, or uniformly sized.\
+\
+
+\f0\b The functionality:
+\f1\b0 Close button removes tabs, click on a tab cell selects. Indicators start, stop, and hide if things are hooked up correctly.\
+\
+
+\f0\b Extras:
+\f1\b0 Supports multi-window drag-and-drop reordering of the tabs with aqua-licious animation.\
+\
+
+\f0\b Files
+\f1\b0 \
+\
+Your project will need the files in the "Framework" folder of the project. The actual framework packages these (and some images) up nicely for you, if desired. Please look over the "TabBarControlDemo" target of the source code project to see exactly what is needed to get everything to build. Building and playing with the demo is also a good way to get a feel for the features provided by these classes.\
+\
+
+\f0\b Usage
+\f1\b0 \
+\
+Simply drag a custom view object from the views palette in IB, read the PSMTabBarControl class into IB, and set the view's custom class to PSMTabBarControl. Then connect the control's tabview outlet to the tab view being controlled, and make the control the delegate of the tab view. You can also connect the control's "partner view" outlet to another view that will resize in response to the hide/show behavior of the control.\
+\
+Alternately, you can build the Palette subproject and add the built IB palette to Interface Builder. In this case, creating and configuring an instance is as easy and drag, drop, and a few clicks. A demo movie and the built palette are available in a separate download from my website: http://www.positivespinmedia.com/dev/PSMTabBarControl.html\
+\
+
+\f2\i Please read the PSMTabBarControlDoc.html file in the documentation folder of this project. It provides an Apple-ish page describing the interface and usage of this object.
+\f1\i0 \
+\
+
+\f0\b Patterns of Use
+\f1\b0 \
+\
+There are a few random notes I can think of for usage guidelines...\
+\
+- You may see a line between the toolbar and the control in your app; it is part of the toolbar. In Tiger, you can eliminate the appearance of this line:\
+\
+\pard\tx960\tx1920\tx2880\tx3840\tx4800\tx5760\tx6720\tx7680\tx8640\tx9600\tx10560\tx11520\tx12480\tx13440\tx14400\tx15360\tx16320\tx17280\tx18240\tx19200\tx20160\tx21120\tx22080\tx23040\tx24000\tx24960\tx25920\tx26880\tx27840\tx28800\tx29760\tx30720\tx31680\tx32640\tx33600\tx34560\tx35520\tx36480\tx37440\tx38400\tx39360\tx40320\tx41280\tx42240\tx43200\tx44160\tx45120\tx46080\tx47040\tx48000\tx48960\tx49920\tx50880\tx51840\tx52800\tx53760\tx54720\tx55680\tx56640\tx57600\tx58560\tx59520\tx60480\tx61440\tx62400\tx63360\tx64320\tx65280\tx66240\tx67200\tx68160\tx69120\tx70080\tx71040\tx72000\tx72960\tx73920\tx74880\tx75840\tx76800\tx77760\tx78720\tx79680\tx80640\tx81600\tx82560\tx83520\tx84480\tx85440\tx86400\tx87360\tx88320\tx89280\tx90240\tx91200\tx92160\tx93120\tx94080\tx95040\tx96000\ql\qnatural\pardirnatural
+
+\f3\fs20 \cf0 \CocoaLigature0 SInt32 MacVersion;\
+\pard\tx960\tx1920\tx2880\tx3840\tx4800\tx5760\tx6720\tx7680\tx8640\tx9600\tx10560\tx11520\tx12480\tx13440\tx14400\tx15360\tx16320\tx17280\tx18240\tx19200\tx20160\tx21120\tx22080\tx23040\tx24000\tx24960\tx25920\tx26880\tx27840\tx28800\tx29760\tx30720\tx31680\tx32640\tx33600\tx34560\tx35520\tx36480\tx37440\tx38400\tx39360\tx40320\tx41280\tx42240\tx43200\tx44160\tx45120\tx46080\tx47040\tx48000\tx48960\tx49920\tx50880\tx51840\tx52800\tx53760\tx54720\tx55680\tx56640\tx57600\tx58560\tx59520\tx60480\tx61440\tx62400\tx63360\tx64320\tx65280\tx66240\tx67200\tx68160\tx69120\tx70080\tx71040\tx72000\tx72960\tx73920\tx74880\tx75840\tx76800\tx77760\tx78720\tx79680\tx80640\tx81600\tx82560\tx83520\tx84480\tx85440\tx86400\tx87360\tx88320\tx89280\tx90240\tx91200\tx92160\tx93120\tx94080\tx95040\tx96000\ql\qnatural\pardirnatural
+\cf2 if\cf0 (Gestalt(gestaltSystemVersion, &MacVersion) == noErr)\{\
+ \cf2 if\cf0 (MacVersion >= \cf3 0x1040\cf0 )\{\
+ \cf4 // this call is Tiger only\cf0 \
+ [toolbar setShowsBaselineSeparator:\cf2 NO\cf0 ];\
+ \}\
+\}\
+\
+\pard\tx960\tx1920\tx2880\tx3840\tx4800\tx5760\tx6720\tx7680\tx8640\tx9600\tx10560\tx11520\tx12480\tx13440\tx14400\tx15360\tx16320\tx17280\tx18240\tx19200\tx20160\tx21120\tx22080\tx23040\tx24000\tx24960\tx25920\tx26880\tx27840\tx28800\tx29760\tx30720\tx31680\tx32640\tx33600\tx34560\tx35520\tx36480\tx37440\tx38400\tx39360\tx40320\tx41280\tx42240\tx43200\tx44160\tx45120\tx46080\tx47040\tx48000\tx48960\tx49920\tx50880\tx51840\tx52800\tx53760\tx54720\tx55680\tx56640\tx57600\tx58560\tx59520\tx60480\tx61440\tx62400\tx63360\tx64320\tx65280\tx66240\tx67200\tx68160\tx69120\tx70080\tx71040\tx72000\tx72960\tx73920\tx74880\tx75840\tx76800\tx77760\tx78720\tx79680\tx80640\tx81600\tx82560\tx83520\tx84480\tx85440\tx86400\tx87360\tx88320\tx89280\tx90240\tx91200\tx92160\tx93120\tx94080\tx95040\tx96000\ql\qnatural\pardirnatural
+
+\f1\fs24 \cf0 - In general, there is no reason for your app objects to communicate (outside of configuration) with the PSMTabBarControl at all. Changes made to the NSTabView instance programmatically should be directed at the NSTabView instance itself, and the control will update to reflect the changes made.\
+\
+- Your app might want to receive tab view delegate notifications in order to perform some actions. No problem, simply make the desired object the delegate of the PSMTabBarControl instance... it passes along all tab view notifications. Note that it uses these notifications to make changes itself - read the source code to make sure you aren't tripping over something.\
+\
+- The control creates bindings between each cell's progress indicator and the represented NSTabViewItem's identifier object, if it can. In my app design, I set an instance of NSObjectController as the NSTabViewItem's identifier, and then bind to the "isProcessing" key of the controller's content object. All of this can be seen in the source of the demo app...\
+\
+- The control can be set to hide itself when there is only a single tab, and can also be told to hide/show on demand. It can animate to appear and disappear, and will resize something to compensate for the missing window real estate. By default, it will resize the window, but you can also connect the "partnerView" outlet in IB to specify another view to resize to take up the missing space. Note that this takes some attention to sizing springs and wires to get right, and complex views may need a container view to achieve the desired effect.\
+\
+- The control can be configured to draw an attractive "Add Tab" button at the end of the tab cells. Unfortunately, the button is all looks and no brains - it has no idea what your app wants to do when adding a tab. If you configure your app to show the add tab button, you need to hook up the add tab button to the proper target with the proper selector. Something like this will do nicely in your app controller's awakeFromNib:\
+\
+\pard\tx960\tx1920\tx2880\tx3840\tx4800\tx5760\tx6720\tx7680\tx8640\tx9600\tx10560\tx11520\tx12480\tx13440\tx14400\tx15360\tx16320\tx17280\tx18240\tx19200\tx20160\tx21120\tx22080\tx23040\tx24000\tx24960\tx25920\tx26880\tx27840\tx28800\tx29760\tx30720\tx31680\tx32640\tx33600\tx34560\tx35520\tx36480\tx37440\tx38400\tx39360\tx40320\tx41280\tx42240\tx43200\tx44160\tx45120\tx46080\tx47040\tx48000\tx48960\tx49920\tx50880\tx51840\tx52800\tx53760\tx54720\tx55680\tx56640\tx57600\tx58560\tx59520\tx60480\tx61440\tx62400\tx63360\tx64320\tx65280\tx66240\tx67200\tx68160\tx69120\tx70080\tx71040\tx72000\tx72960\tx73920\tx74880\tx75840\tx76800\tx77760\tx78720\tx79680\tx80640\tx81600\tx82560\tx83520\tx84480\tx85440\tx86400\tx87360\tx88320\tx89280\tx90240\tx91200\tx92160\tx93120\tx94080\tx95040\tx96000\ql\qnatural\pardirnatural
+
+\f3\fs20 \cf4 // hook up add tab button\cf0 \
+[[tabBar addTabButton] setTarget:\cf2 self\cf0 ];\
+[[tabBar addTabButton] setAction:\cf2 @selector\cf0 (addNewTab:)];
+\f1\fs24 \
+\
+- The tabs have some sizing options: You can specify the minimum width, maximum width, and optimum width, as well as spcifying if the tabs should size to fit their label or not. The sizing bahavior of the tabs is as follows: If "size to fit" is specified, then tabs will be generated to fit the label, but will never exceed the specified max or min widths. Once the end of the control is reached, the overflow menu will appear as tabs are added; the last tab will squeeze in if it can, or the remaining tabs will stretch to occupy the full control. If "size to fit" is not specified, then all successive tabs will appear at the optimum width. Once the end of the control is reached, adding new tabs will cause all tabs to shrink to accomodate, until the minumum width is reached, and then the overflow menu will be used; max width is ignored in this case. Hopefully that all makes sense :-)\
+\
+- PSMTabBarControl will load the existing tabs from the tabView outlet at startup. However, many of the advanced features (icon display, progress indicator, object count) rely on binding to a controller that is likely not set up in IB. Solution? Nuke the existing tabs in the NSTabView and add new ones, configured the way you like. The demo app does this in the awakeFromNib: method of the app controller.\
+\
+- As a design choice, I elected to keep a cell object around until its tab was closed, instead of "churning" cell objects in each update cycle. Each cell keeps its NSTabViewItem as its representedObject and maintains reference that way, rather than by any index. As a result of this, drag-and-drop reordering of tabs does not change the underlying NSTabView instance at all. All that to say: don't rely on numerical indices if communicating with both the control and the tab view - the indices may not correlate if the user moved some tabs around (and remember - you shouldn't need to communicate with the control anyway :-). The Shiira Project, from which I gained much insight and inspiration from for this UI element, elected to scrap and rebuild the array of cells each time through the update cycle, and rely on indices to correlate between cells and NSTabViewItems. I felt the representedObject route was cleaner, and preferred not to churn objects.\
+\
+\pard\tx960\tx1920\tx2880\tx3840\tx4800\tx5760\tx6720\tx7680\tx8640\tx9600\tx10560\tx11520\tx12480\tx13440\tx14400\tx15360\tx16320\tx17280\tx18240\tx19200\tx20160\tx21120\tx22080\tx23040\tx24000\tx24960\tx25920\tx26880\tx27840\tx28800\tx29760\tx30720\tx31680\tx32640\tx33600\tx34560\tx35520\tx36480\tx37440\tx38400\tx39360\tx40320\tx41280\tx42240\tx43200\tx44160\tx45120\tx46080\tx47040\tx48000\tx48960\tx49920\tx50880\tx51840\tx52800\tx53760\tx54720\tx55680\tx56640\tx57600\tx58560\tx59520\tx60480\tx61440\tx62400\tx63360\tx64320\tx65280\tx66240\tx67200\tx68160\tx69120\tx70080\tx71040\tx72000\tx72960\tx73920\tx74880\tx75840\tx76800\tx77760\tx78720\tx79680\tx80640\tx81600\tx82560\tx83520\tx84480\tx85440\tx86400\tx87360\tx88320\tx89280\tx90240\tx91200\tx92160\tx93120\tx94080\tx95040\tx96000\ql\qnatural\pardirnatural
+
+\f0\b \cf0 Improvements?
+\f1\b0 \
+\
+Pipe up if you think of something you'd like to see; here's my current list:\
+\
+- "Pop-up" tabs - like pop-up folders in the finder, in case you want to drag to a destination in another tab.\
+- Support for the
+\f3\fs22 \CocoaLigature1 NSUnifiedTitleAndToolbarWindowMask
+\f1\fs24 \CocoaLigature0 "unified" window appearance. (Help! I really searched around to try to make this work... the color pattern of the title and toolbar seem to be top secret! The new "unified" style is an excellent replication of a unified look, but isn't "built from" the unified appearance like the metal is.)\
+- During multi-window drag, having a "drag window/image" that shows the represented view getting moved to the other window.\
+- During multi-window drag, support for dragging out solo tabs to consolidate in another window, removing the source window in the process.\
+- Support vertical as well as horizontal alignment.\
+\
+
+\f0\b Version History
+\f1\b0 \
+\
+Version 1.3 (May 29, 2006)\
+- new feature: Unified tab style, compliments of Keith Blount\
+- new feature: allow multi-window drag config option (again from Keith).\
+- fixed bug: Palette installation/usage instructions were wrong.\
+- enhancement: exposed the
+\f3\fs20 representedTabViewItems
+\f1\fs24 method, which can be used to retrieve the order of the tabs as displayed in the control, since the underlying NSTabView does not get reordered during drag and drop rearrangement.\
+\
+Version 1.2 (April 20, 2006)\
+- new feature: multi-window drag and drop support.\
+- bug fixed: zombie issue with tabView:didCloseTabViewItem\
+- bugs fixed: some drawing issues around the progress indicators in tabs, and the add tab button.\
+- enhancement: the hide/show animation has been improved with less "flickering" of progress indicators during the hide and show.\
+\
+Version 1.1.2 (April 5, 2006)\
+- fixed bug: tabs of non-integer width resulted in occasional anti-aliased drawing issues of dividers between tabs in the Metal style (Thanks, Kent).\
+- added feature: delegate can now respond to -tabView:shouldCloseTabViewItem: and -tabView:willCloseTabViewItem:, and -tabView:didCloseTabViewItem: messages, so your app can take care of any needed setup/cleanup for these actions.\
+- fixed bug: tab close buttons now show down state when pressed down.\
+\
+Version 1.1.1 (March 16, 2006)\
+- fixed bug: Palette inspector would not reflect state of previously instantiated control. This has been fixed (Thanks, Guillaume).\
+- enhancement: Overflow button now highlights when mouse down (Thanks, Kent).\
+- fixed bug: when set to not close a solo tab, the close button would be hidden for the tab, but could still be closed if you clicked the tab in the right location. This has been fixed (Thanks, malcom).\
+\
+Version 1.1 (March 10, 2006)\
+- Bound the "title" of the cell to the "label" of the source tabview item. Just in case you wanted to change the label on the tab during the running of your application.\
+- PSMTabBarCell factored to support new tab "styles", or appearances in drawing. Now supported are the existing "Metal" style and a new "Aqua" style. Many thanks to David Smith, Seth Willits, and Chris Forsythe for their contributions!\
+- Control can be configured to "Hide for single tab", so it doesn't appear unless there are more than a single tab view present. Features animated show/hide behavior (that can be called anytime, and is called automatically in the case that a single tab exists). The show/hide behavior can also be set up to resize either the window (default) or a selected "partner view" to compensate for the lost height of the tab bar.\
+- Control can be configured for "Can close only tab" behavior. If set to NO, no close button will appear on a lone tab.\
+- Cells can be set to "size to fit", or given uniform min/max/optimum sizes.\
+- Added support for display of an icon and an object count, if the proper app design pattern is followed.\
+- Sweet animated drag-and-drop drawing!\
+- A few drawing bugs surrounding the progress indicators in cells were squished.\
+- New documentation, in case you found this read me a little pithy.\
+\
+Version 1.0 (December 2005)\
+Initial release of safari-like tab implementation.\
+\
+
+\f0\b The standard disavowal of this beautiful mess
+\f1\b0 \
+\
+I should note that portions of this source code were inspired by the Shiira project's implementation of Safari-style tabs. While I made some different design decisions, the drawing and some other aspects are only slight modifications of their excellent work. As such, I note their copyright under their BSD licence:\
+\
+\pard\tx960\tx1920\tx2880\tx3840\tx4800\tx5760\tx6720\tx7680\tx8640\tx9600\tx10560\tx11520\tx12480\tx13440\tx14400\tx15360\tx16320\tx17280\tx18240\tx19200\tx20160\tx21120\tx22080\tx23040\tx24000\tx24960\tx25920\tx26880\tx27840\tx28800\tx29760\tx30720\tx31680\tx32640\tx33600\tx34560\tx35520\tx36480\tx37440\tx38400\tx39360\tx40320\tx41280\tx42240\tx43200\tx44160\tx45120\tx46080\tx47040\tx48000\tx48960\tx49920\tx50880\tx51840\tx52800\tx53760\tx54720\tx55680\tx56640\tx57600\tx58560\tx59520\tx60480\tx61440\tx62400\tx63360\tx64320\tx65280\tx66240\tx67200\tx68160\tx69120\tx70080\tx71040\tx72000\tx72960\tx73920\tx74880\tx75840\tx76800\tx77760\tx78720\tx79680\tx80640\tx81600\tx82560\tx83520\tx84480\tx85440\tx86400\tx87360\tx88320\tx89280\tx90240\tx91200\tx92160\tx93120\tx94080\tx95040\tx96000\ql\qnatural\pardirnatural
+
+\f3\fs20 \cf4 Portions of this software Copyright 2004 The Shiira Project. All rights reserved.\
+\pard\tx960\tx1920\tx2880\tx3840\tx4800\tx5760\tx6720\tx7680\tx8640\tx9600\tx10560\tx11520\tx12480\tx13440\tx14400\tx15360\tx16320\tx17280\tx18240\tx19200\tx20160\tx21120\tx22080\tx23040\tx24000\tx24960\tx25920\tx26880\tx27840\tx28800\tx29760\tx30720\tx31680\tx32640\tx33600\tx34560\tx35520\tx36480\tx37440\tx38400\tx39360\tx40320\tx41280\tx42240\tx43200\tx44160\tx45120\tx46080\tx47040\tx48000\tx48960\tx49920\tx50880\tx51840\tx52800\tx53760\tx54720\tx55680\tx56640\tx57600\tx58560\tx59520\tx60480\tx61440\tx62400\tx63360\tx64320\tx65280\tx66240\tx67200\tx68160\tx69120\tx70080\tx71040\tx72000\tx72960\tx73920\tx74880\tx75840\tx76800\tx77760\tx78720\tx79680\tx80640\tx81600\tx82560\tx83520\tx84480\tx85440\tx86400\tx87360\tx88320\tx89280\tx90240\tx91200\tx92160\tx93120\tx94080\tx95040\tx96000\ql\qnatural\pardirnatural
+
+\f1\fs24 \cf0 Check them out at: http://hmdt-web.net/shiira/\
+\
+This source code is provided under BSD license, the conditions of which are listed below. I hope you'll make note somewhere in your about window or ReadMe stating the sweet coding goodness of Positive Spin Media and link to the fascinating and informative website at www.positivespinmedia.com\
+\
+\pard\pardeftab720\sa320\ql\qnatural
+\cf0 \CocoaLigature1 Copyright (c) 2005, Positive Spin Media\uc0\u8232 All rights reserved.\
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\
+\pard\tx220\tx720\pardeftab720\li720\fi-720\ql\qnatural
+\ls1\ilvl0\cf0 {\listtext \'a5 }Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\
+{\listtext \'a5 }Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\
+{\listtext \'a5 }Neither the name of Positive Spin Media nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\
+ \
+\pard\pardeftab720\sa320\ql\qnatural
+\cf0 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\CocoaLigature0 \
+} \ No newline at end of file
diff --git a/frontends/cocoa/PSMTabBarControl/ReadMe.rtfd/startpage.gif b/frontends/cocoa/PSMTabBarControl/ReadMe.rtfd/startpage.gif
new file mode 100644
index 000000000..8707a77ab
--- /dev/null
+++ b/frontends/cocoa/PSMTabBarControl/ReadMe.rtfd/startpage.gif
Binary files differ
diff --git a/frontends/cocoa/PreferencesWindowController.h b/frontends/cocoa/PreferencesWindowController.h
new file mode 100644
index 000000000..8f72907a3
--- /dev/null
+++ b/frontends/cocoa/PreferencesWindowController.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+
+#import <Cocoa/Cocoa.h>
+
+
+@interface PreferencesWindowController : NSWindowController
+
+@property (readwrite, copy, nonatomic) NSString *homepageURL;
+
+- (IBAction) useCurrentPageAsHomepage: (id) sender;
+
+@end
diff --git a/frontends/cocoa/PreferencesWindowController.m b/frontends/cocoa/PreferencesWindowController.m
new file mode 100644
index 000000000..f314736e3
--- /dev/null
+++ b/frontends/cocoa/PreferencesWindowController.m
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import "utils/nsoption.h"
+#import "desktop/browser.h"
+#import "content/content.h"
+#import "content/hlcache.h"
+
+#import "cocoa/PreferencesWindowController.h"
+#import "cocoa/NetsurfApp.h"
+#import "cocoa/gui.h"
+#import "cocoa/BrowserViewController.h"
+
+@implementation PreferencesWindowController
+
+- init;
+{
+ if ((self = [super initWithWindowNibName: @"PreferencesWindow"]) == nil) return nil;
+
+ return self;
+}
+
+- (IBAction) useCurrentPageAsHomepage: (id) sender;
+{
+ struct browser_window *bw = [[(NetSurfApp *)NSApp frontTab] browser];
+ const char *url = nsurl_access(browser_window_get_url(bw));
+ [self setHomepageURL: [NSString stringWithUTF8String: url]];
+}
+
+- (void) setHomepageURL: (NSString *) newUrl;
+{
+ nsoption_set_charp(homepage_url, strdup( [newUrl UTF8String] ));
+ [[NSUserDefaults standardUserDefaults] setObject: newUrl forKey: kHomepageURLOption];
+ [[NSUserDefaults standardUserDefaults] synchronize];
+}
+
+- (NSString *) homepageURL;
+{
+ return [NSString stringWithUTF8String: nsoption_charp(homepage_url)];
+}
+
+@end
diff --git a/frontends/cocoa/Prefix.pch b/frontends/cocoa/Prefix.pch
new file mode 100644
index 000000000..7fa2c0547
--- /dev/null
+++ b/frontends/cocoa/Prefix.pch
@@ -0,0 +1,11 @@
+#include <Carbon/Carbon.h>
+
+#ifdef __OBJC__
+#import <Cocoa/Cocoa.h>
+#endif
+
+#undef offsetof
+
+#define HISTORY_COLOUR_BACKGROUND 0x000000
+#define HISTORY_COLOUR_FOREGROUND 0xFFFFFF
+#define HISTORY_COLOUR_SELECTED 0xFF6D27 \ No newline at end of file
diff --git a/frontends/cocoa/ScrollableView.h b/frontends/cocoa/ScrollableView.h
new file mode 100644
index 000000000..071a11825
--- /dev/null
+++ b/frontends/cocoa/ScrollableView.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+
+@interface ScrollableView : NSView {
+ NSSize minimumSize;
+ NSView *observedSuperview;
+}
+
+@property (readwrite, assign, nonatomic) NSSize minimumSize;
+
+- (void) adjustFrame;
+
+@end
diff --git a/frontends/cocoa/ScrollableView.m b/frontends/cocoa/ScrollableView.m
new file mode 100644
index 000000000..8f27b2b56
--- /dev/null
+++ b/frontends/cocoa/ScrollableView.m
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import "cocoa/ScrollableView.h"
+
+@interface ScrollableView ()
+
+- (void) frameChangeNotification: (NSNotification *) note;
+
+@end
+
+@implementation ScrollableView
+@synthesize minimumSize;
+
+- (void) setMinimumSize: (NSSize)newSize
+{
+ minimumSize = newSize;
+ [self adjustFrame];
+}
+
+- (void) adjustFrame
+{
+ NSSize frameSize = [[self superview] frame].size;
+ [self setFrameSize: NSMakeSize( MAX( minimumSize.width, frameSize.width ),
+ MAX( minimumSize.height, frameSize.height ) )];
+}
+
+- (void) frameChangeNotification: (NSNotification *) note
+{
+ [self adjustFrame];
+}
+
+- (void) viewDidMoveToSuperview
+{
+ if (observedSuperview) {
+ [[NSNotificationCenter defaultCenter]
+ removeObserver: self
+ name: NSViewFrameDidChangeNotification
+ object: observedSuperview];
+ observedSuperview = nil;
+ }
+
+ NSView *newSuperView = [self superview];
+
+ if (nil != newSuperView) {
+ observedSuperview = newSuperView;
+ [[NSNotificationCenter defaultCenter]
+ addObserver: self
+ selector: @selector(frameChangeNotification:)
+ name: NSViewFrameDidChangeNotification
+ object: observedSuperview];
+ [observedSuperview setPostsFrameChangedNotifications: YES];
+ }
+}
+
+@end
diff --git a/frontends/cocoa/SearchWindowController.h b/frontends/cocoa/SearchWindowController.h
new file mode 100644
index 000000000..7ce8c00c9
--- /dev/null
+++ b/frontends/cocoa/SearchWindowController.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+
+#import <Cocoa/Cocoa.h>
+
+@class BrowserViewController;
+
+typedef enum {
+ SearchBackward,
+ SearchForward
+} SearchDirection;
+
+@interface SearchWindowController : NSWindowController {
+ BOOL caseSensitive;
+ BOOL selectAll;
+ BOOL canGoBack;
+ BOOL canGoForward;
+ NSString *searchString;
+ BrowserViewController *browser;
+}
+
+@property (readwrite, assign, nonatomic) BOOL caseSensitive;
+@property (readwrite, assign, nonatomic) BOOL selectAll;
+@property (readwrite, assign, nonatomic) BOOL canGoBack;
+@property (readwrite, assign, nonatomic) BOOL canGoForward;
+@property (readwrite, copy, nonatomic) NSString *searchString;
+@property (readwrite, assign, nonatomic) BrowserViewController *browser;
+
+- (IBAction) searchNext: (id) sender;
+- (IBAction) searchPrevious: (id) sender;
+
+- (IBAction) searchStringDidChange: (id) sender;
+
+- (void) search: (SearchDirection)direction;
+
+@end
+
+struct gui_search_table *cocoa_search_table;
diff --git a/frontends/cocoa/SearchWindowController.m b/frontends/cocoa/SearchWindowController.m
new file mode 100644
index 000000000..1c896a958
--- /dev/null
+++ b/frontends/cocoa/SearchWindowController.m
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+
+#import "cocoa/SearchWindowController.h"
+#import "cocoa/BrowserViewController.h"
+
+#import "desktop/gui_search.h"
+#import "desktop/browser.h"
+#import "desktop/search.h"
+
+static void cocoa_search_set_back( bool active, void *p );
+static void cocoa_search_set_forward( bool active, void *p );
+
+static struct gui_search_table search_table = {
+ .forward_state = cocoa_search_set_forward,
+ .back_state = cocoa_search_set_back,
+};
+
+struct gui_search_table *cocoa_search_table = &search_table;
+
+@implementation SearchWindowController
+
+@synthesize caseSensitive;
+@synthesize selectAll;
+@synthesize canGoBack;
+@synthesize canGoForward;
+@synthesize searchString;
+@synthesize browser;
+
+- init;
+{
+ if ((self = [super initWithWindowNibName: @"SearchWindow"]) == nil) return nil;
+
+ [self bind: @"browser" toObject: NSApp withKeyPath: @"frontTab" options: nil];
+ canGoBack = canGoForward = YES;
+
+ return self;
+}
+
+- (void) dealloc;
+{
+ [self unbind: @"browser"];
+ [super dealloc];
+}
+
+- (IBAction) searchNext: (id) sender;
+{
+ [self search: SearchForward];
+}
+
+- (IBAction) searchPrevious: (id) sender;
+{
+ [self search: SearchBackward];
+}
+
+- (void) search: (SearchDirection)direction;
+{
+ search_flags_t flags = (direction == SearchForward) ? SEARCH_FLAG_FORWARDS : 0;
+ if (caseSensitive) flags |= SEARCH_FLAG_CASE_SENSITIVE;
+ if (selectAll) flags |= SEARCH_FLAG_SHOWALL;
+
+ struct browser_window *bw = [browser browser];
+ browser_window_search( bw, self, flags, [searchString UTF8String] );
+}
+
+- (IBAction) searchStringDidChange: (id) sender;
+{
+ struct browser_window *bw = [browser browser];
+ browser_window_search_clear( bw );
+
+ [self setCanGoBack: YES];
+ [self setCanGoForward: YES];
+}
+
+- (void) setCaseSensitive: (BOOL) newValue;
+{
+ if (caseSensitive != newValue) {
+ caseSensitive = newValue;
+ [self setCanGoBack: YES];
+ [self setCanGoForward: YES];
+ }
+}
+
+- (void) setSelectAll: (BOOL) newValue;
+{
+ if (selectAll != newValue) {
+ selectAll = newValue;
+ [self setCanGoBack: YES];
+ [self setCanGoForward: YES];
+ }
+}
+
+static void cocoa_search_set_back( bool active, void *p )
+{
+ [(SearchWindowController *)p setCanGoBack: active];
+}
+
+static void cocoa_search_set_forward( bool active, void *p )
+{
+ [(SearchWindowController *)p setCanGoForward: active];
+}
+
+@end
diff --git a/frontends/cocoa/Tree.h b/frontends/cocoa/Tree.h
new file mode 100644
index 000000000..422478182
--- /dev/null
+++ b/frontends/cocoa/Tree.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#import "desktop/tree.h"
+
+@class Tree;
+
+@protocol TreeDelegate
+
+- (void) tree: (Tree *)tree requestedRedrawInRect: (NSRect) rect;
+- (void) tree: (Tree *)tree resized: (NSSize) size;
+- (void) tree: (Tree *)tree scrollPoint: (NSPoint) point;
+- (NSSize) treeWindowSize: (Tree *)tree;
+
+@end
+
+
+@interface Tree : NSObject {
+ id <TreeDelegate> delegate;
+ struct tree *tree;
+}
+
+@property (readwrite, assign, nonatomic) id <TreeDelegate> delegate;
+
+- (id)initWithFlags: (unsigned int) flags;
+
+- (struct tree *) tree;
+
+@end
+
+
+@interface Tree (ViewInterface)
+
+- (void) drawRect: (NSRect) rect inView: (NSView *) view;
+- (void) mouseAction: (browser_mouse_state)state atPoint: (NSPoint)point;
+- (void) mouseDragEnd: (browser_mouse_state)state fromPoint: (NSPoint)p0 toPoint: (NSPoint) p1;
+- (void) keyPress: (uint32_t) key;
+
+@end
diff --git a/frontends/cocoa/Tree.m b/frontends/cocoa/Tree.m
new file mode 100644
index 000000000..5cd796ceb
--- /dev/null
+++ b/frontends/cocoa/Tree.m
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import "cocoa/Tree.h"
+#import "cocoa/coordinates.h"
+#import "cocoa/font.h"
+#import "cocoa/plotter.h"
+
+#import "desktop/plotters.h"
+#import "desktop/tree.h"
+
+@implementation Tree
+
+@synthesize delegate;
+
+static void tree_redraw_request( int x, int y, int w, int h, void *data );
+static void tree_resized( struct tree *tree, int w, int h, void *data );
+static void tree_scroll_visible( int y, int height, void *data );
+static void tree_get_window_dimensions( int *width, int *height, void *data );
+
+static const struct treeview_table cocoa_tree_callbacks = {
+ .redraw_request = tree_redraw_request,
+ .resized = tree_resized,
+ .scroll_visible = tree_scroll_visible,
+ .get_window_dimensions = tree_get_window_dimensions
+};
+
+- (id)initWithFlags: (unsigned int)flags
+{
+ if ((self = [super init]) == nil) return nil;
+
+ tree = tree_create( flags, &cocoa_tree_callbacks, self );
+ if (tree == NULL) {
+ [self release];
+ return nil;
+ }
+
+ return self;
+}
+
+
+- (void) dealloc
+{
+ tree_delete( tree );
+ [super dealloc];
+}
+
+- (struct tree *) tree
+{
+ return tree;
+}
+
+- (void) setRedrawing: (BOOL) newRedrawing
+{
+}
+
+
++ (void) initialize
+{
+}
+
+//MARK: -
+//MARK: Callbacks
+
+static void tree_redraw_request( int x, int y, int w, int h, void *data )
+{
+ id <TreeDelegate> delegate = ((Tree *)data)->delegate;
+ [delegate tree: (Tree *)data requestedRedrawInRect: cocoa_rect_wh( x, y, w, h )];
+}
+
+static void tree_resized( struct tree *tree, int w, int h, void *data )
+{
+ id <TreeDelegate> delegate = ((Tree *)data)->delegate;
+ [delegate tree: (Tree *)data resized: cocoa_size( w, h )];
+}
+
+static void tree_scroll_visible( int y, int height, void *data )
+{
+ id <TreeDelegate> delegate = ((Tree *)data)->delegate;
+ [delegate tree: (Tree *)data scrollPoint: cocoa_point( 0, y )];
+}
+
+static void tree_get_window_dimensions( int *width, int *height, void *data )
+{
+ id <TreeDelegate> delegate = ((Tree *)data)->delegate;
+ if (delegate == nil) return;
+
+ NSSize size = [delegate treeWindowSize: (Tree *)data];
+
+ if (width != NULL) *width = cocoa_pt_to_px( size.width );
+ if (height != NULL) *height = cocoa_pt_to_px( size.height );
+}
+
+@end
+
+@implementation Tree (ViewInterface)
+
+- (void) drawRect: (NSRect) rect inView: (NSView *) view
+{
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &cocoa_plotters
+ };
+
+ tree_draw(tree, 0, 0,
+ cocoa_pt_to_px(NSMinX( rect )),
+ cocoa_pt_to_px(NSMinY( rect )),
+ cocoa_pt_to_px(NSWidth( rect )),
+ cocoa_pt_to_px(NSHeight( rect )),
+ &ctx );
+}
+
+- (void) mouseAction: (browser_mouse_state)state atPoint: (NSPoint)point
+{
+ tree_mouse_action(tree, state,
+ cocoa_pt_to_px( point.x ), cocoa_pt_to_px( point.y ));
+}
+
+- (void) mouseDragEnd: (browser_mouse_state)state fromPoint: (NSPoint)p0 toPoint: (NSPoint) p1
+{
+ tree_drag_end(tree, state,
+ cocoa_pt_to_px( p0.x ), cocoa_pt_to_px( p0.y ),
+ cocoa_pt_to_px( p1.x ), cocoa_pt_to_px( p1.y ));
+}
+
+- (void) keyPress: (uint32_t) key;
+{
+ tree_keypress( tree, key );
+}
+
+@end
diff --git a/frontends/cocoa/TreeView.h b/frontends/cocoa/TreeView.h
new file mode 100644
index 000000000..31dedbb0f
--- /dev/null
+++ b/frontends/cocoa/TreeView.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#import "cocoa/ScrollableView.h"
+@class Tree;
+
+@interface TreeView : ScrollableView {
+ Tree *tree;
+
+ BOOL isDragging;
+ NSPoint dragStart;
+
+}
+
+@property (readwrite, retain, nonatomic) Tree *tree;
+
+@end
diff --git a/frontends/cocoa/TreeView.m b/frontends/cocoa/TreeView.m
new file mode 100644
index 000000000..a58a49623
--- /dev/null
+++ b/frontends/cocoa/TreeView.m
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import "cocoa/TreeView.h"
+#import "cocoa/Tree.h"
+
+#import "desktop/plotters.h"
+#import "desktop/textinput.h"
+
+@interface TreeView () <TreeDelegate>
+@end
+
+@implementation TreeView
+
+@synthesize tree;
+
+- (void)drawRect:(NSRect)dirtyRect
+{
+ [tree drawRect: dirtyRect inView: self];
+}
+
+- (BOOL) isFlipped
+{
+ return YES;
+}
+
+- (BOOL) acceptsFirstResponder
+{
+ return YES;
+}
+
+- (void) dealloc
+{
+ [self setTree: nil];
+ [super dealloc];
+}
+
+- (void) setTree: (Tree *)newTree
+{
+ if (tree != newTree) {
+ [tree setRedrawing: NO];
+ [tree setDelegate: nil];
+ [tree release];
+
+ tree = [newTree retain];
+ [tree setDelegate: self];
+ [tree setRedrawing: YES];
+
+ [self setNeedsDisplay: YES];
+ }
+}
+
+//MARK: -
+//MARK: Event handlers
+
+- (void)mouseDown: (NSEvent *)event
+{
+ isDragging = NO;
+ dragStart = [self convertPoint: [event locationInWindow] fromView: nil];
+ [tree mouseAction: BROWSER_MOUSE_PRESS_1 atPoint: dragStart];
+}
+
+#define squared(x) ((x)*(x))
+#define MinDragDistance (5.0)
+
+- (void) mouseDragged: (NSEvent *)event
+{
+ const NSPoint point = [self convertPoint: [event locationInWindow] fromView: nil];
+
+ if (!isDragging) {
+ const CGFloat distance = squared( dragStart.x - point.x ) + squared( dragStart.y - point.y );
+ if (distance >= squared( MinDragDistance)) {
+ isDragging = YES;
+ }
+ }
+}
+
+- (void) mouseUp: (NSEvent *)event
+{
+ const NSPoint point = [self convertPoint: [event locationInWindow] fromView: nil];
+
+ browser_mouse_state modifierFlags = 0;
+
+ if (isDragging) {
+ isDragging = NO;
+ [tree mouseDragEnd: modifierFlags fromPoint: dragStart toPoint: point];
+ } else {
+ modifierFlags |= BROWSER_MOUSE_CLICK_1;
+ if ([event clickCount] == 2) {
+ modifierFlags |= BROWSER_MOUSE_DOUBLE_CLICK;
+ }
+ [tree mouseAction: modifierFlags atPoint: point];
+ }
+}
+
+//MARK: Keyboard events
+
+- (void) keyDown: (NSEvent *)theEvent
+{
+ [self interpretKeyEvents: [NSArray arrayWithObject: theEvent]];
+}
+
+- (void) insertText: (id)string
+{
+ for (NSUInteger i = 0, length = [string length]; i < length; i++) {
+ unichar ch = [string characterAtIndex: i];
+ [tree keyPress: ch];
+ }
+}
+
+- (void) moveLeft: (id)sender
+{
+ [tree keyPress: NS_KEY_LEFT];
+}
+
+- (void) moveRight: (id)sender
+{
+ [tree keyPress: NS_KEY_RIGHT];
+}
+
+- (void) moveUp: (id)sender
+{
+ [tree keyPress: NS_KEY_UP];
+}
+
+- (void) moveDown: (id)sender
+{
+ [tree keyPress: NS_KEY_DOWN];
+}
+
+- (void) deleteBackward: (id)sender
+{
+ [tree keyPress: NS_KEY_DELETE_LEFT];
+}
+
+- (void) deleteForward: (id)sender
+{
+ [tree keyPress: NS_KEY_DELETE_RIGHT];
+}
+
+- (void) cancelOperation: (id)sender
+{
+ [tree keyPress: NS_KEY_ESCAPE];
+}
+
+- (void) scrollPageUp: (id)sender
+{
+ [tree keyPress: NS_KEY_PAGE_UP];
+}
+
+- (void) scrollPageDown: (id)sender
+{
+ [tree keyPress: NS_KEY_PAGE_DOWN];
+}
+
+- (void) insertTab: (id)sender
+{
+ [tree keyPress: NS_KEY_TAB];
+}
+
+- (void) insertBacktab: (id)sender
+{
+ [tree keyPress: NS_KEY_SHIFT_TAB];
+}
+
+- (void) moveToBeginningOfLine: (id)sender
+{
+ [tree keyPress: NS_KEY_LINE_START];
+}
+
+- (void) moveToEndOfLine: (id)sender
+{
+ [tree keyPress: NS_KEY_LINE_END];
+}
+
+- (void) moveToBeginningOfDocument: (id)sender
+{
+ [tree keyPress: NS_KEY_TEXT_START];
+}
+
+- (void) moveToEndOfDocument: (id)sender
+{
+ [tree keyPress: NS_KEY_TEXT_END];
+}
+
+- (void) insertNewline: (id)sender
+{
+ [tree keyPress: NS_KEY_NL];
+}
+
+- (void) selectAll: (id)sender
+{
+ [tree keyPress: NS_KEY_SELECT_ALL];
+}
+
+- (void) copy: (id) sender
+{
+ [tree keyPress: NS_KEY_COPY_SELECTION];
+}
+
+- (void) cut: (id) sender
+{
+ [tree keyPress: NS_KEY_CUT_SELECTION];
+}
+
+- (void) paste: (id) sender
+{
+ [tree keyPress: NS_KEY_PASTE];
+}
+
+//MARK: -
+//MARK: Tree delegate methods
+
+- (void) tree: (Tree *)t requestedRedrawInRect: (NSRect) rect
+{
+ [self setNeedsDisplayInRect: rect];
+}
+
+- (void) tree: (Tree *)t resized: (NSSize) size
+{
+ [self setMinimumSize: size];
+}
+
+- (void) tree: (Tree *)t scrollPoint: (NSPoint) point
+{
+ [self scrollPoint: point];
+}
+
+- (NSSize) treeWindowSize: (Tree *)t
+{
+ return [self frame].size;
+}
+
+@end
diff --git a/frontends/cocoa/URLFieldCell.h b/frontends/cocoa/URLFieldCell.h
new file mode 100644
index 000000000..38a75a139
--- /dev/null
+++ b/frontends/cocoa/URLFieldCell.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+
+@interface URLFieldCell : NSTextFieldCell {
+ NSButtonCell *refreshCell;
+ NSImage *favicon;
+}
+
+@property (readwrite, assign, nonatomic) SEL refreshAction;
+@property (readwrite, assign, nonatomic) id refreshTarget;
+@property (readwrite, retain, nonatomic) NSImage *favicon;
+
+@end
diff --git a/frontends/cocoa/URLFieldCell.m b/frontends/cocoa/URLFieldCell.m
new file mode 100644
index 000000000..1b4344b6c
--- /dev/null
+++ b/frontends/cocoa/URLFieldCell.m
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import "cocoa/URLFieldCell.h"
+
+#import "content/urldb.h"
+
+@interface URLFieldCell ()
+
+@property (readonly, retain, nonatomic) NSButtonCell *refreshCell;
+
+- (NSRect) buttonFrame: (NSRect) cellFrame;
+- (NSRect) urlFrame: (NSRect) cellFrame;
+- (NSRect) iconFrame: (NSRect) cellFrame;
+
+@end
+
+
+@implementation URLFieldCell
+
+@synthesize favicon;
+
+- (void) setFavicon: (NSImage *)newIcon;
+{
+ if (favicon != newIcon) {
+ [favicon release];
+ favicon = [newIcon retain];
+ [[self controlView] setNeedsDisplay: YES];
+ }
+}
+
+#define BUTTON_SIZE 32
+#define PADDING 2
+
+- (void) drawInteriorWithFrame: (NSRect)cellFrame inView: (NSView *)controlView;
+{
+ [favicon drawInRect: [self iconFrame: cellFrame] fromRect: NSZeroRect
+ operation: NSCompositeSourceOver fraction: 1.0];
+
+ [super drawInteriorWithFrame: [self urlFrame: cellFrame] inView: controlView];
+
+ [[self refreshCell] drawInteriorWithFrame: [self buttonFrame: cellFrame]
+ inView: controlView];
+}
+
+- (void) selectWithFrame: (NSRect)aRect inView: (NSView *)controlView editor: (NSText *)textObj
+ delegate: (id)anObject start: (NSInteger)selStart length: (NSInteger)selLength;
+{
+ const NSRect textFrame = [self urlFrame: aRect];
+ [super selectWithFrame: textFrame inView: controlView editor: textObj
+ delegate: anObject start: selStart length: selLength];
+}
+
+- (void) editWithFrame: (NSRect)aRect inView: (NSView *)controlView editor: (NSText *)textObj
+ delegate: (id)anObject event: (NSEvent *)theEvent;
+{
+ const NSRect textFrame = [self urlFrame: aRect];
+ [super editWithFrame: textFrame inView: controlView editor: textObj
+ delegate: anObject event: theEvent];
+}
+
+- (void) startDragURLAt: (NSPoint) point inView: (NSView *) view;
+{
+ NSString *url = [self stringValue];
+ NSString *title = url;
+ nsurl *nsurl;
+
+ if (nsurl_create( [url UTF8String] , &nsurl ) != NSERROR_OK)
+ return;
+
+ const struct url_data *data = urldb_get_url_data( nsurl );
+
+ nsurl_unref(nsurl);
+
+ if (data && data->title) title = [NSString stringWithUTF8String: data->title];
+
+ NSPasteboard *pb = [NSPasteboard pasteboardWithName: NSDragPboard];
+ [pb declareTypes: [NSArray arrayWithObjects: NSStringPboardType, NSURLPboardType,
+ @"public.url", @"public.url-name", nil] owner: nil];
+ [pb setString: url forType: NSStringPboardType];
+ [pb setString: url forType: @"public.url"];
+ [pb setString: title forType: @"public.url-name"];
+ [[NSURL URLWithString: url] writeToPasteboard: pb];
+
+ NSRect urlBounds = NSZeroRect;
+ urlBounds.size = [title sizeWithAttributes: nil];
+ urlBounds.size.width += urlBounds.size.height + 2;
+
+ NSImage *image = [[NSImage alloc] initWithSize: urlBounds.size];
+
+ [image lockFocus];
+ [favicon drawInRect: NSMakeRect( urlBounds.origin.x, urlBounds.origin.y, urlBounds.size.height, urlBounds.size.height )
+ fromRect: NSZeroRect operation: NSCompositeCopy fraction: 1.0];
+ urlBounds.origin.x += urlBounds.size.height + 2;
+ [title drawInRect: urlBounds withAttributes: nil];
+ [image unlockFocus];
+
+ point.x -= urlBounds.size.height / 2;
+ point.y += urlBounds.size.height / 2;
+
+ [view dragImage: image at: point offset: NSZeroSize event: [NSApp currentEvent]
+ pasteboard: pb source: self slideBack: YES];
+
+ [image release];
+}
+
+- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
+{
+ return NSDragOperationCopy | NSDragOperationGeneric;
+}
+
+- (BOOL) trackMouse: (NSEvent *)theEvent inRect: (NSRect)cellFrame ofView: (NSView *)controlView untilMouseUp: (BOOL)flag;
+{
+ const NSPoint point = [controlView convertPoint: [theEvent locationInWindow] fromView: nil];
+ const NSRect buttonRect = [self buttonFrame: cellFrame];
+ if (NSPointInRect( point, [self iconFrame: cellFrame] )) {
+ [self startDragURLAt: point inView: controlView];
+ return NO;
+ } else if (NSPointInRect( point, buttonRect )) {
+ return [[self refreshCell] trackMouse: theEvent inRect: buttonRect
+ ofView: controlView untilMouseUp: flag];
+ } else {
+ cellFrame.size.width -= BUTTON_SIZE + PADDING;
+ return [super trackMouse: theEvent inRect: cellFrame ofView: controlView untilMouseUp: YES];
+ }
+}
+
+- (void) dealloc;
+{
+ [refreshCell release];
+
+ [super dealloc];
+}
+
+- (NSRect) buttonFrame: (NSRect) cellFrame;
+{
+ NSRect buttonRect = cellFrame;
+ buttonRect.origin.x = NSMaxX( cellFrame ) - BUTTON_SIZE;
+ buttonRect.size.width = BUTTON_SIZE;
+ return buttonRect;
+}
+
+- (NSRect) urlFrame: (NSRect) cellFrame;
+{
+ NSRect textFrame = cellFrame;
+ textFrame.origin.x += cellFrame.size.height;
+ textFrame.size.width -= cellFrame.size.height + BUTTON_SIZE + PADDING;
+ return textFrame;
+}
+
+- (NSRect) iconFrame: (NSRect)cellFrame;
+{
+ NSRect iconFrame = {
+ .origin = {
+ .x = cellFrame.origin.x + PADDING,
+ .y = cellFrame.origin.y,
+ },
+ .size = NSMakeSize( NSHeight( cellFrame ), NSHeight( cellFrame ) )
+ };
+ return NSInsetRect( iconFrame, 2 * PADDING, 2 * PADDING );
+}
+
+- (NSButtonCell *) refreshCell;
+{
+ if (nil == refreshCell) {
+ refreshCell = [[NSButtonCell alloc] initImageCell: [NSImage imageNamed: NSImageNameRefreshTemplate]];
+ [refreshCell setButtonType: NSMomentaryPushInButton];
+ [refreshCell setBordered: NO];
+ }
+ return refreshCell;
+}
+
+- (void) setRefreshTarget: (id) newTarget;
+{
+ [[self refreshCell] setTarget: newTarget];
+}
+
+- (id) refreshTarget;
+{
+ return [[self refreshCell] target];
+}
+
+- (void) setRefreshAction: (SEL) newAction;
+{
+ [[self refreshCell] setAction: newAction];
+}
+
+- (SEL) refreshAction;
+{
+ return [[self refreshCell] action];
+}
+
+@end
diff --git a/frontends/cocoa/apple_image.h b/frontends/cocoa/apple_image.h
new file mode 100644
index 000000000..11248a6fb
--- /dev/null
+++ b/frontends/cocoa/apple_image.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_COCOA_APPLE_IMAGE_H_
+#define _NETSURF_COCOA_APPLE_IMAGE_H_
+
+#include "utils/config.h"
+#include "utils/errors.h"
+
+#ifdef WITH_APPLE_IMAGE
+
+/**
+ * Initialise apple image handlers instead of generic core ones.
+ */
+nserror apple_image_init(void);
+
+#else
+
+#define apple_image_init() NSERROR_OK
+
+#endif /* WITH_APPLE_IMAGE */
+
+#endif
diff --git a/frontends/cocoa/apple_image.m b/frontends/cocoa/apple_image.m
new file mode 100644
index 000000000..8261e8bc6
--- /dev/null
+++ b/frontends/cocoa/apple_image.m
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#ifdef WITH_APPLE_IMAGE
+
+#import "cocoa/apple_image.h"
+
+#include "utils/config.h"
+#include "content/content_protected.h"
+#include "image/bitmap.h"
+#include "desktop/plotters.h"
+#include "utils/utils.h"
+
+#import "cocoa/schedule.h"
+#import "cocoa/bitmap.h"
+
+typedef struct apple_image_content {
+ struct content base;
+
+ struct bitmap *bitmap; /**< Created NetSurf bitmap */
+
+ NSUInteger frames;
+ NSUInteger currentFrame;
+ int *frameTimes;
+} apple_image_content;
+
+
+static void *apple_image_get_internal(const struct content *c, void *context)
+{
+ apple_image_content *ai_c = (apple_image_content *)c;
+
+ return ai_c->bitmap;
+}
+
+static nserror apple_image_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c)
+{
+ apple_image_content *ai;
+ nserror error;
+
+ ai = calloc(1, sizeof(apple_image_content));
+ if (ai == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__init(&ai->base, handler, imime_type, params,
+ llcache, fallback_charset, quirks);
+ if (error != NSERROR_OK) {
+ free(ai);
+ return error;
+ }
+
+ *c = (struct content *) ai;
+
+ return NSERROR_OK;
+}
+
+
+static void animate_image_cb( void *ptr )
+{
+ struct apple_image_content *ai = ptr;
+ ++ai->currentFrame;
+ if (ai->currentFrame >= ai->frames) ai->currentFrame = 0;
+
+ [(NSBitmapImageRep *)ai->bitmap setProperty: NSImageCurrentFrame withValue: [NSNumber numberWithUnsignedInteger: ai->currentFrame]];
+ cocoa_bitmap_modified( ai->bitmap );
+
+ union content_msg_data data;
+ data.redraw.full_redraw = true;
+ data.redraw.x = data.redraw.object_x = 0;
+ data.redraw.y = data.redraw.object_y = 0;
+ data.redraw.width = data.redraw.object_width = ai->base.width;
+ data.redraw.height = data.redraw.object_height = ai->base.height;
+ data.redraw.object = &ai->base;
+ content_broadcast( &ai->base, CONTENT_MSG_REDRAW, data );
+
+ cocoa_schedule(ai->frameTimes[ai->currentFrame], animate_image_cb, ai );
+}
+
+/**
+ * Convert a CONTENT_APPLE_IMAGE for display.
+ */
+static bool apple_image_convert(struct content *c)
+{
+ apple_image_content *ai_c = (apple_image_content *)c;
+ unsigned long size;
+ const char *bytes = content__get_source_data(c, &size);
+
+ NSData *data = [NSData dataWithBytesNoCopy: (char *)bytes length: size freeWhenDone: NO];
+ NSBitmapImageRep *image = [[NSBitmapImageRep imageRepWithData: data] retain];
+
+ if (image == nil) {
+ union content_msg_data msg_data;
+ msg_data.error = "cannot decode image";
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ c->width = [image pixelsWide];
+ c->height = [image pixelsHigh];
+ ai_c->bitmap = (void *)image;
+
+ NSString *url = [NSString stringWithUTF8String: nsurl_access(llcache_handle_get_url( content_get_llcache_handle( c )) )];
+ NSString *title = [NSString stringWithFormat: @"%@ (%dx%d)", [url lastPathComponent], c->width, c->height];
+ content__set_title(c, [title UTF8String] );
+
+ content_set_ready(c);
+ content_set_done(c);
+ content_set_status(c, "");
+
+ struct apple_image_content *ai = (struct apple_image_content *)c;
+ NSUInteger frames = [[image valueForProperty: NSImageFrameCount] unsignedIntegerValue];
+ if (frames > 1) {
+ ai->frames = frames;
+ ai->currentFrame = 0;
+ ai->frameTimes = calloc( ai->frames , sizeof(int));
+ for (NSUInteger i = 0; i < frames; i++) {
+ [image setProperty: NSImageCurrentFrame withValue: [NSNumber numberWithUnsignedInteger: i]];
+ ai->frameTimes[i] = 1000 * [[image valueForProperty: NSImageCurrentFrameDuration] floatValue];
+ }
+ [image setProperty: NSImageCurrentFrame withValue: [NSNumber numberWithUnsignedInteger: 0]];
+ cocoa_schedule( ai->frameTimes[0], animate_image_cb, ai );
+ }
+
+ return true;
+}
+
+
+static void apple_image_destroy(struct content *c)
+{
+ apple_image_content *ai_c = (apple_image_content *)c;
+
+ [(id)ai_c->bitmap release];
+ ai_c->bitmap = NULL;
+ cocoa_schedule(-1, animate_image_cb, c );
+}
+
+
+static nserror apple_image_clone(const struct content *old, struct content **newc)
+{
+ apple_image_content *ai;
+ apple_image_content *ai_old = (apple_image_content *)old;
+ nserror error;
+
+ ai = calloc(1, sizeof(apple_image_content));
+ if (ai == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__clone(old, &ai->base);
+ if (error != NSERROR_OK) {
+ content_destroy(&ai->base);
+ return error;
+ }
+
+ if (old->status == CONTENT_STATUS_READY ||
+ old->status == CONTENT_STATUS_DONE) {
+ ai->base.width = old->width;
+ ai->base.height = old->height;
+ ai->bitmap = (void *)[(id)ai_old->bitmap retain];
+ }
+
+ *newc = (struct content *) ai;
+
+ return NSERROR_OK;
+}
+
+static content_type apple_image_content_type(void)
+{
+ return CONTENT_IMAGE;
+}
+
+/**
+ * Redraw a CONTENT_APPLE_IMAGE with appropriate tiling.
+ */
+static bool apple_image_redraw(struct content *c, struct content_redraw_data *data,
+ const struct rect *clip, const struct redraw_context *ctx)
+{
+ apple_image_content *ai_c = (apple_image_content *)c;
+ bitmap_flags_t flags = BITMAPF_NONE;
+
+ if (data->repeat_x)
+ flags |= BITMAPF_REPEAT_X;
+ if (data->repeat_y)
+ flags |= BITMAPF_REPEAT_Y;
+
+ return ctx->plot->bitmap(data->x, data->y, data->width, data->height,
+ ai_c->bitmap, data->background_colour, flags);
+}
+
+static const content_handler apple_image_content_handler = {
+ .create = apple_image_create,
+ .data_complete = apple_image_convert,
+ .destroy = apple_image_destroy,
+ .redraw = apple_image_redraw,
+ .clone = apple_image_clone,
+ .get_internal = apple_image_get_internal,
+ .type = apple_image_content_type,
+ .no_share = false
+};
+
+static nserror register_for_type( NSString *mime )
+{
+ const char *type = [mime UTF8String];
+ /* nsgif has priority since it supports animated GIF */
+#ifdef WITH_GIF
+ if (strcmp(type, "image/gif") == 0)
+ return NSERROR_OK;
+#endif
+
+ nserror error = content_factory_register_handler( type, &apple_image_content_handler );
+ if (error != NSERROR_OK) return error;
+
+ return NSERROR_OK;
+}
+
+/* exported interface documented in cocoa/apple_image.h */
+nserror apple_image_init(void)
+{
+ NSArray *utis = [NSBitmapImageRep imageTypes];
+ for (NSString *uti in utis) {
+ NSDictionary *declaration = [(NSDictionary *)UTTypeCopyDeclaration( (CFStringRef)uti ) autorelease];
+ id mimeTypes = [[declaration objectForKey: (NSString *)kUTTypeTagSpecificationKey] objectForKey: (NSString *)kUTTagClassMIMEType];
+
+ if (mimeTypes == nil) continue;
+
+ if (![mimeTypes isKindOfClass: [NSArray class]]) {
+ mimeTypes = [NSArray arrayWithObject: mimeTypes];
+ }
+
+ for (NSString *mime in mimeTypes) {
+ nserror error = register_for_type( mime );
+ if (error != NSERROR_OK) return error;
+ }
+ }
+
+ return NSERROR_OK;
+}
+
+#endif /* WITH_APPLE_IMAGE */
diff --git a/frontends/cocoa/bitmap.h b/frontends/cocoa/bitmap.h
new file mode 100644
index 000000000..1eeed1767
--- /dev/null
+++ b/frontends/cocoa/bitmap.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 COCOA_BITMAP_H
+#define COCOA_BITMAP_H
+
+CGImageRef cocoa_get_cgimage( void *bitmap );
+
+void cocoa_bitmap_modified(void *bitmap);
+
+struct gui_bitmap_table *cocoa_bitmap_table;
+
+#endif
diff --git a/frontends/cocoa/bitmap.m b/frontends/cocoa/bitmap.m
new file mode 100644
index 000000000..6e263a1ea
--- /dev/null
+++ b/frontends/cocoa/bitmap.m
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * Cocoa implementation of bitmap operations.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#import "desktop/browser.h"
+#import "desktop/plotters.h"
+#import "image/bitmap.h"
+#import "content/urldb.h"
+#import "content/content.h"
+
+#import "cocoa/plotter.h"
+#import "cocoa/bitmap.h"
+
+#define BITS_PER_SAMPLE (8)
+#define SAMPLES_PER_PIXEL (4)
+#define BITS_PER_PIXEL (BITS_PER_SAMPLE * SAMPLES_PER_PIXEL)
+#define BYTES_PER_PIXEL (BITS_PER_PIXEL / 8)
+#define RED_OFFSET (0)
+#define GREEN_OFFSET (1)
+#define BLUE_OFFSET (2)
+#define ALPHA_OFFSET (3)
+
+static CGImageRef cocoa_prepare_bitmap( void *bitmap );
+static NSMapTable *cocoa_get_bitmap_cache( void );
+
+static int bitmap_get_width(void *bitmap)
+{
+ NSCParameterAssert( NULL != bitmap );
+ NSBitmapImageRep *bmp = (NSBitmapImageRep *)bitmap;
+ return [bmp pixelsWide];
+}
+
+static int bitmap_get_height(void *bitmap)
+{
+ NSCParameterAssert( NULL != bitmap );
+ NSBitmapImageRep *bmp = (NSBitmapImageRep *)bitmap;
+ return [bmp pixelsHigh];
+}
+
+static bool bitmap_get_opaque(void *bitmap)
+{
+ NSCParameterAssert( NULL != bitmap );
+ NSBitmapImageRep *bmp = (NSBitmapImageRep *)bitmap;
+ return [bmp isOpaque];
+}
+
+static void bitmap_destroy(void *bitmap)
+{
+ NSCParameterAssert( NULL != bitmap );
+
+ NSMapTable *cache = cocoa_get_bitmap_cache();
+ CGImageRef image = NSMapGet( cache, bitmap );
+ if (NULL != image) {
+ CGImageRelease( image );
+ NSMapRemove( cache, bitmap );
+ }
+
+ NSBitmapImageRep *bmp = (NSBitmapImageRep *)bitmap;
+ [bmp release];
+}
+
+static void *bitmap_create(int width, int height, unsigned int state)
+{
+ NSBitmapImageRep *bmp = [[NSBitmapImageRep alloc]
+ initWithBitmapDataPlanes: NULL
+ pixelsWide: width
+ pixelsHigh: height
+ bitsPerSample: BITS_PER_SAMPLE
+ samplesPerPixel: SAMPLES_PER_PIXEL
+ hasAlpha: YES
+ isPlanar: NO
+ colorSpaceName: NSDeviceRGBColorSpace
+ bitmapFormat: NSAlphaNonpremultipliedBitmapFormat
+ bytesPerRow: BYTES_PER_PIXEL * width
+ bitsPerPixel: BITS_PER_PIXEL];
+
+ return bmp;
+}
+
+static void bitmap_set_opaque(void *bitmap, bool opaque)
+{
+ NSCParameterAssert( NULL != bitmap );
+ NSBitmapImageRep *bmp = (NSBitmapImageRep *)bitmap;
+ [bmp setOpaque: opaque ? YES : NO];
+}
+
+static unsigned char *bitmap_get_buffer(void *bitmap)
+{
+ NSCParameterAssert( NULL != bitmap );
+ NSBitmapImageRep *bmp = (NSBitmapImageRep *)bitmap;
+ return [bmp bitmapData];
+}
+
+static size_t bitmap_get_rowstride(void *bitmap)
+{
+ NSCParameterAssert( NULL != bitmap );
+ NSBitmapImageRep *bmp = (NSBitmapImageRep *)bitmap;
+ return [bmp bytesPerRow];
+}
+
+static size_t bitmap_get_bpp(void *bitmap)
+{
+ NSCParameterAssert( NULL != bitmap );
+ NSBitmapImageRep *bmp = (NSBitmapImageRep *)bitmap;
+ return [bmp bitsPerPixel] / 8;
+}
+
+static bool bitmap_test_opaque(void *bitmap)
+{
+ NSCParameterAssert( bitmap_get_bpp( bitmap ) == BYTES_PER_PIXEL );
+
+ unsigned char *buf = bitmap_get_buffer( bitmap );
+
+ const size_t height = bitmap_get_height( bitmap );
+ const size_t width = bitmap_get_width( bitmap );
+
+ const size_t line_step = bitmap_get_rowstride( bitmap ) - BYTES_PER_PIXEL * width;
+
+ for (size_t y = 0; y < height; y++) {
+ for (size_t x = 0; x < height; x++) {
+ if (buf[ALPHA_OFFSET] != 0xFF) return false;
+ buf += BYTES_PER_PIXEL;
+ }
+ buf += line_step;
+ }
+
+ return true;
+}
+
+static bool bitmap_save(void *bitmap, const char *path, unsigned flags)
+{
+ NSCParameterAssert( NULL != bitmap );
+ NSBitmapImageRep *bmp = (NSBitmapImageRep *)bitmap;
+
+ NSData *tiff = [bmp TIFFRepresentation];
+ return [tiff writeToFile: [NSString stringWithUTF8String: path] atomically: YES];
+}
+
+void cocoa_bitmap_modified(void *bitmap)
+{
+ NSMapTable *cache = cocoa_get_bitmap_cache();
+ CGImageRef image = NSMapGet( cache, bitmap );
+ if (NULL != image) {
+ CGImageRelease( image );
+ NSMapRemove( cache, bitmap );
+ }
+}
+
+CGImageRef cocoa_get_cgimage( void *bitmap )
+{
+ NSMapTable *cache = cocoa_get_bitmap_cache();
+
+ CGImageRef result = NSMapGet( cache, bitmap );
+ if (NULL == result) {
+ result = cocoa_prepare_bitmap( bitmap );
+ NSMapInsertKnownAbsent( cache, bitmap, result );
+ }
+
+ return result;
+}
+
+static inline NSMapTable *cocoa_get_bitmap_cache( void )
+{
+ static NSMapTable *cache = nil;
+ if (cache == nil) {
+ cache = NSCreateMapTable( NSNonOwnedPointerMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, 0 );
+ }
+ return cache;
+}
+
+static CGImageRef cocoa_prepare_bitmap( void *bitmap )
+{
+ NSCParameterAssert( NULL != bitmap );
+
+ NSBitmapImageRep *bmp = (NSBitmapImageRep *)bitmap;
+
+ size_t w = [bmp pixelsWide];
+ size_t h = [bmp pixelsHigh];
+
+ CGImageRef original = [bmp CGImage];
+
+ if (h <= 1) return CGImageRetain( original );
+
+ void *data = malloc( 4 * w * h );
+
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+ CGContextRef context = CGBitmapContextCreate( data, w, h, BITS_PER_SAMPLE,
+ BYTES_PER_PIXEL * w, colorSpace,
+ [bmp isOpaque] ? kCGImageAlphaNoneSkipLast
+ : kCGImageAlphaPremultipliedLast );
+ CGColorSpaceRelease( colorSpace );
+
+ CGContextTranslateCTM( context, 0.0, h );
+ CGContextScaleCTM( context, 1.0, -1.0 );
+
+ CGRect rect = CGRectMake( 0, 0, w, h );
+ CGContextClearRect( context, rect );
+ CGContextDrawImage( context, rect, original );
+
+ CGImageRef result = CGBitmapContextCreateImage( context );
+
+ CGContextRelease( context );
+ free( data );
+
+ return result;
+}
+
+static nserror bitmap_render(struct bitmap *bitmap, struct hlcache_handle *content)
+{
+ int bwidth = bitmap_get_width( bitmap );
+ int bheight = bitmap_get_height( bitmap );
+
+ struct redraw_context ctx = {
+ .interactive = false,
+ .background_images = true,
+ .plot = &cocoa_plotters
+ };
+
+ CGColorSpaceRef cspace = CGColorSpaceCreateWithName( kCGColorSpaceGenericRGB );
+ CGContextRef bitmapContext = CGBitmapContextCreate( bitmap_get_buffer( bitmap ),
+ bwidth, bheight,
+ bitmap_get_bpp( bitmap ) * 8 / 4,
+ bitmap_get_rowstride( bitmap ),
+ cspace, kCGImageAlphaNoneSkipLast );
+ CGColorSpaceRelease( cspace );
+
+ size_t width = MIN( content_get_width( content ), 1024 );
+ size_t height = ((width * bheight) + bwidth / 2) / bwidth;
+
+ CGContextTranslateCTM( bitmapContext, 0, bheight );
+ CGContextScaleCTM( bitmapContext, (CGFloat)bwidth / width, -(CGFloat)bheight / height );
+
+ [NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithGraphicsPort: bitmapContext flipped: YES]];
+
+ content_scaled_redraw( content, width, height, &ctx );
+
+ [NSGraphicsContext setCurrentContext: nil];
+ CGContextRelease( bitmapContext );
+
+ cocoa_bitmap_modified( bitmap );
+
+ return true;
+}
+
+static struct gui_bitmap_table bitmap_table = {
+ .create = bitmap_create,
+ .destroy = bitmap_destroy,
+ .set_opaque = bitmap_set_opaque,
+ .get_opaque = bitmap_get_opaque,
+ .test_opaque = bitmap_test_opaque,
+ .get_buffer = bitmap_get_buffer,
+ .get_rowstride = bitmap_get_rowstride,
+ .get_width = bitmap_get_width,
+ .get_height = bitmap_get_height,
+ .get_bpp = bitmap_get_bpp,
+ .save = bitmap_save,
+ .modified = cocoa_bitmap_modified,
+ .render = bitmap_render,
+};
+
+struct gui_bitmap_table *cocoa_bitmap_table = &bitmap_table;
diff --git a/frontends/cocoa/compile-xib.sh b/frontends/cocoa/compile-xib.sh
new file mode 100755
index 000000000..576f9bfd0
--- /dev/null
+++ b/frontends/cocoa/compile-xib.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+# call: compile-xib.sh [xib file] [language] [(optional output nib file)]
+DIR=`dirname "$1"`
+XIB=`basename -s .xib "$1"`
+
+STRINGS_FILE="$DIR/$2.lproj/$XIB.xib.strings"
+TRANSLATE=""
+if [ -f $STRINGS_FILE ]
+then
+ TRANSLATE="--strings-file $STRINGS_FILE"
+fi
+
+OUTPUT="$2.$XIB.nib"
+
+if [ "x$3" != "x" ]
+then
+ OUTPUT="$3"
+fi
+
+exec /usr/bin/ibtool $TRANSLATE --compile $OUTPUT $1
diff --git a/frontends/cocoa/coordinates.h b/frontends/cocoa/coordinates.h
new file mode 100644
index 000000000..a43db0b65
--- /dev/null
+++ b/frontends/cocoa/coordinates.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 COCOA_COORDINATES_H
+#define COCOA_COORDINATES_H
+
+#include "utils/utils.h"
+#import "desktop/browser.h"
+
+extern CGFloat cocoa_scale_factor;
+
+static inline CGFloat cocoa_px_to_pt( int location ) __attribute__((always_inline,pure));
+static inline CGFloat cocoa_px_to_pt_f( CGFloat location ) __attribute__((always_inline,pure));
+
+static inline int cocoa_pt_to_px( CGFloat location ) __attribute__((always_inline,pure));
+
+static inline NSPoint cocoa_point( int x, int y ) __attribute__((always_inline,pure));
+static inline NSPoint cocoa_scaled_point( CGFloat scale, int x, int y ) __attribute__((always_inline,pure));
+
+static inline NSSize cocoa_size( int w, int h ) __attribute__((always_inline,pure));
+static inline NSSize cocoa_scaled_size( CGFloat scale, int w, int h ) __attribute__((always_inline,pure));
+
+static inline NSRect cocoa_rect( int x0, int y0, int x1, int y1 ) __attribute__((always_inline,pure));
+static inline NSRect cocoa_rect_wh( int x, int y, int w, int h ) __attribute__((always_inline,pure));
+
+static inline NSRect cocoa_scaled_rect( CGFloat scale, int x0, int y0, int x1, int y1 ) __attribute__((always_inline,pure));
+static inline NSRect cocoa_scaled_rect_wh( CGFloat scale, int x, int y, int w, int h ) __attribute__((always_inline,pure));
+
+static inline CGFloat cocoa_px_to_pt( int location )
+{
+ return (CGFloat)location * cocoa_scale_factor;
+}
+
+static inline CGFloat cocoa_px_to_pt_f( CGFloat location )
+{
+ return floor( location ) * cocoa_scale_factor;
+}
+
+static inline int cocoa_pt_to_px( CGFloat location )
+{
+ return location / cocoa_scale_factor;
+}
+
+static inline NSPoint cocoa_point( int x, int y )
+{
+ return NSMakePoint( cocoa_px_to_pt( x ), cocoa_px_to_pt( y ) );
+}
+
+static inline NSPoint cocoa_scaled_point( CGFloat scale, int x, int y )
+{
+ return NSMakePoint( cocoa_px_to_pt_f( scale * x ), cocoa_px_to_pt_f( scale * y ) );
+}
+
+static inline NSSize cocoa_size( int w, int h )
+{
+ return NSMakeSize( cocoa_px_to_pt( w ), cocoa_px_to_pt( h ) );
+}
+
+static inline NSSize cocoa_scaled_size( CGFloat scale, int w, int h )
+{
+ return NSMakeSize( cocoa_px_to_pt_f( scale * w ), cocoa_px_to_pt_f( scale * h ) );
+}
+
+static inline NSRect cocoa_rect( int x0, int y0, int x1, int y1 )
+{
+ return cocoa_rect_wh( x0, y0, x1 - x0, y1 - y0 );
+}
+
+static inline NSRect cocoa_rect_wh( int x, int y, int w, int h )
+{
+ const NSRect result = {
+ .origin = cocoa_point( x, y ),
+ .size = cocoa_size( w, h )
+ };
+ return result;
+}
+
+static inline NSRect cocoa_scaled_rect_wh( CGFloat scale, int x, int y, int w, int h )
+{
+ const NSRect result = {
+ .origin = cocoa_scaled_point( scale, x, y ),
+ .size = cocoa_scaled_size( scale, w, h )
+ };
+ return result;
+}
+
+static inline NSRect cocoa_scaled_rect( CGFloat scale, int x0, int y0, int x1, int y1 )
+{
+ return cocoa_scaled_rect_wh( scale, x0, y0, x1 - x0, y1 - y0 );
+}
+
+
+#endif
diff --git a/frontends/cocoa/extract-strings.sh b/frontends/cocoa/extract-strings.sh
new file mode 100755
index 000000000..c3f582619
--- /dev/null
+++ b/frontends/cocoa/extract-strings.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+for i in $1/*.xib
+do
+ xib=`basename "$i"`
+ strings="$2/$xib.strings"
+
+ ibtool "$i" --generate-strings-file "$strings"
+done
+
+
diff --git a/frontends/cocoa/fetch.h b/frontends/cocoa/fetch.h
new file mode 100644
index 000000000..1b0991ef5
--- /dev/null
+++ b/frontends/cocoa/fetch.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+extern struct gui_fetch_table *cocoa_fetch_table;
diff --git a/frontends/cocoa/fetch.m b/frontends/cocoa/fetch.m
new file mode 100644
index 000000000..1c36bb8a5
--- /dev/null
+++ b/frontends/cocoa/fetch.m
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#import "utils/log.h"
+#import "utils/nsurl.h"
+#import "desktop/gui_fetch.h"
+
+#import "cocoa/fetch.h"
+
+static char cocoafiletype[200];
+
+static const struct mimemap_s {
+ const char const *extension;
+ const char const *mimetype;
+} cocoamimemap[] = {
+ { "css", "text/css" },
+ { "f79", "text/css" },
+ { "jpg", "image/jpeg" },
+ { "jpeg", "image/jpeg" },
+ { "gif", "image/gif" },
+ { "png", "image/png" },
+ { "b60", "image/png" },
+ { "jng", "image/jng" },
+ { "svg", "image/svg" },
+ { NULL, "text/html" }
+};
+
+
+static const char *fetch_filetype(const char *unix_path)
+{
+ NSString *uti;
+ NSString *mimeType = nil;
+ NSError *utiError = nil;
+
+ uti = [[NSWorkspace sharedWorkspace] typeOfFile: [NSString stringWithUTF8String: unix_path] error:&utiError];
+ if (nil != uti) {
+ LOG("Looking for mimetype from uti \"%s\"", [uti UTF8String] );
+ mimeType = (NSString *)UTTypeCopyPreferredTagWithClass( (CFStringRef)uti, kUTTagClassMIMEType );
+ } else {
+ NSAlert *utiAlert = [NSAlert alertWithError:utiError];
+ [utiAlert runModal]; // Ignore return value.
+
+ LOG("uti call failed");
+
+ strncpy(cocoafiletype, "text/html", sizeof(cocoafiletype));
+ return cocoafiletype;
+ }
+
+ if (nil != mimeType) {
+ strncpy(cocoafiletype, [mimeType UTF8String], sizeof(cocoafiletype));
+ [mimeType release];
+ } else {
+ const char *extension;
+
+ LOG("mimetype from uti failed");
+
+ extension = [(NSString *)UTTypeCopyPreferredTagWithClass( (CFStringRef)uti, kUTTagClassFilenameExtension) UTF8String];
+
+ if (extension == NULL) {
+ /* give up and go with default */
+ LOG("No extension going with default type");
+ strncpy(cocoafiletype, "text/html", sizeof(cocoafiletype)); } else {
+ int eidx = 0; /* index of extension entry */
+
+ while ((cocoamimemap[eidx].extension != NULL) &&
+ (strcmp(cocoamimemap[eidx].extension, extension) != 0)) {
+ eidx++;
+ }
+
+ strncpy(cocoafiletype,
+ cocoamimemap[eidx].mimetype,
+ sizeof(cocoafiletype));
+ }
+ }
+
+ LOG("\tMIME type for '%s' is '%s'", unix_path, cocoafiletype);
+
+ return cocoafiletype;
+}
+
+static nsurl *gui_get_resource_url(const char *path)
+{
+ nsurl *url = NULL;
+ NSString *nspath = [[NSBundle mainBundle] pathForResource: [NSString stringWithUTF8String: path] ofType: @""];
+ if (nspath == nil) return NULL;
+ nsurl_create([[[NSURL fileURLWithPath: nspath] absoluteString] UTF8String], &url);
+ return url;
+}
+
+static struct gui_fetch_table fetch_table = {
+ .filetype = fetch_filetype,
+
+ .get_resource_url = gui_get_resource_url,
+};
+
+struct gui_fetch_table *cocoa_fetch_table = &fetch_table;
diff --git a/frontends/cocoa/font.h b/frontends/cocoa/font.h
new file mode 100644
index 000000000..cabd2b933
--- /dev/null
+++ b/frontends/cocoa/font.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 COCOA_FONT_H
+#define COCOA_FONT_H
+
+#import "desktop/plot_style.h"
+
+void cocoa_draw_string( CGFloat x, CGFloat y, const char *bytes, size_t length, const struct plot_font_style *style );
+
+struct gui_layout_table *cocoa_layout_table;
+
+#endif
diff --git a/frontends/cocoa/font.m b/frontends/cocoa/font.m
new file mode 100644
index 000000000..f52ec85bc
--- /dev/null
+++ b/frontends/cocoa/font.m
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#import "cocoa/plotter.h"
+#import "cocoa/font.h"
+
+#import "utils/nsoption.h"
+#import "desktop/gui_layout.h"
+#import "desktop/plotters.h"
+
+
+static NSLayoutManager *cocoa_prepare_layout_manager( const char *string, size_t length,
+ const plot_font_style_t *style );
+
+static CGFloat cocoa_layout_width( NSLayoutManager *layout );
+static CGFloat cocoa_layout_width_chars( NSLayoutManager *layout, size_t characters );
+static NSUInteger cocoa_glyph_for_location( NSLayoutManager *layout, CGFloat x );
+static size_t cocoa_bytes_for_characters( const char *string, size_t characters );
+static NSDictionary *cocoa_font_attributes( const plot_font_style_t *style );
+
+static NSTextStorage *cocoa_text_storage = nil;
+static NSTextContainer *cocoa_text_container = nil;
+
+static nserror cocoa_font_width(const plot_font_style_t *style,
+ const char *string, size_t length,
+ int *width)
+{
+ NSLayoutManager *layout;
+ layout = cocoa_prepare_layout_manager( string, length, style );
+ *width = cocoa_layout_width( layout );
+ return NSERROR_OK;
+}
+
+static nserror cocoa_font_position(const plot_font_style_t *style,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x)
+{
+ NSLayoutManager *layout = cocoa_prepare_layout_manager( string, length, style );
+ if (layout == nil) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ NSUInteger glyphIndex = cocoa_glyph_for_location( layout, x );
+ NSUInteger chars = [layout characterIndexForGlyphAtIndex: glyphIndex];
+
+ if (chars >= [cocoa_text_storage length]) *char_offset = length;
+ else *char_offset = cocoa_bytes_for_characters( string, chars );
+
+ *actual_x = cocoa_pt_to_px( NSMaxX( [layout boundingRectForGlyphRange: NSMakeRange( glyphIndex - 1, 1 )
+ inTextContainer: cocoa_text_container] ) );
+
+ return NSERROR_OK;
+}
+
+static nserror cocoa_font_split(const plot_font_style_t *style,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x)
+{
+ NSLayoutManager *layout = cocoa_prepare_layout_manager( string, length, style );
+ if (layout == nil) return NSERROR_BAD_PARAMETER;
+
+ NSUInteger glyphIndex = cocoa_glyph_for_location( layout, x );
+ NSUInteger chars = [layout characterIndexForGlyphAtIndex: glyphIndex];
+
+ if (chars >= [cocoa_text_storage length]) {
+ *char_offset = length;
+ *actual_x = cocoa_layout_width( layout );
+ return NSERROR_OK;
+ }
+
+
+ chars = [[cocoa_text_storage string] rangeOfString: @" " options: NSBackwardsSearch range: NSMakeRange( 0, chars + 1 )].location;
+ if (chars == NSNotFound) {
+ *char_offset = 0;
+ *actual_x = 0;
+ return NSERROR_OK;
+ }
+
+ *char_offset = cocoa_bytes_for_characters( string, chars );
+ *actual_x = cocoa_layout_width_chars( layout, chars );
+
+ return NSERROR_OK;
+}
+
+
+static struct gui_layout_table layout_table = {
+ .width = cocoa_font_width,
+ .position = cocoa_font_position,
+ .split = cocoa_font_split,
+};
+
+struct gui_layout_table *cocoa_layout_table = &layout_table;
+
+
+#pragma mark -
+
+void cocoa_draw_string( CGFloat x, CGFloat y, const char *bytes, size_t length, const plot_font_style_t *style )
+{
+ NSLayoutManager *layout = cocoa_prepare_layout_manager( bytes, length, style );
+ if (layout == nil) return;
+
+ NSFont *font = [cocoa_text_storage attribute: NSFontAttributeName atIndex: 0 effectiveRange: NULL];
+ CGFloat baseline = [layout defaultLineHeightForFont: font] * 3.0 / 4.0;
+
+ NSRange glyphRange = [layout glyphRangeForTextContainer: cocoa_text_container];
+ [layout drawGlyphsForGlyphRange: glyphRange atPoint: NSMakePoint( x, y - baseline )];
+}
+
+
+#pragma mark -
+
+static inline CGFloat cocoa_layout_width( NSLayoutManager *layout )
+{
+ if (layout == nil) return 0.0;
+
+ return cocoa_pt_to_px( NSWidth( [layout usedRectForTextContainer: cocoa_text_container] ) );
+}
+
+static inline CGFloat cocoa_layout_width_chars( NSLayoutManager *layout, size_t characters )
+{
+ NSUInteger glyphIndex = [layout glyphIndexForCharacterAtIndex: characters];
+ return cocoa_pt_to_px( [layout locationForGlyphAtIndex: glyphIndex].x );
+}
+
+static inline NSUInteger cocoa_glyph_for_location( NSLayoutManager *layout, CGFloat x )
+{
+ CGFloat fraction = 0.0;
+ NSUInteger glyphIndex = [layout glyphIndexForPoint: NSMakePoint( cocoa_px_to_pt( x ), 0 )
+ inTextContainer: cocoa_text_container
+ fractionOfDistanceThroughGlyph: &fraction];
+ if (fraction >= 1.0) ++glyphIndex;
+ return glyphIndex;
+}
+
+static inline size_t cocoa_bytes_for_characters( const char *string, size_t chars )
+{
+ size_t offset = 0;
+ while (chars-- > 0) {
+ uint8_t ch = ((uint8_t *)string)[offset];
+
+ if (0xC2 <= ch && ch <= 0xDF) offset += 2;
+ else if (0xE0 <= ch && ch <= 0xEF) offset += 3;
+ else if (0xF0 <= ch && ch <= 0xF4) offset += 4;
+ else offset++;
+ }
+ return offset;
+}
+
+static NSLayoutManager *cocoa_prepare_layout_manager( const char *bytes, size_t length,
+ const plot_font_style_t *style )
+{
+ if (NULL == bytes || 0 == length) return nil;
+
+ NSString *string = [[[NSString alloc] initWithBytes: bytes length:length encoding:NSUTF8StringEncoding] autorelease];
+ if (string == nil) return nil;
+
+ static NSLayoutManager *layout = nil;
+ if (nil == layout) {
+ cocoa_text_container = [[NSTextContainer alloc] initWithContainerSize: NSMakeSize( CGFLOAT_MAX, CGFLOAT_MAX )];
+ [cocoa_text_container setLineFragmentPadding: 0];
+
+ layout = [[NSLayoutManager alloc] init];
+ [layout addTextContainer: cocoa_text_container];
+ }
+
+ static NSString *oldString = 0;
+ static plot_font_style_t oldStyle = { 0, 0, 0, 0, 0, 0 };
+
+ const bool styleChanged = memcmp( style, &oldStyle, sizeof oldStyle ) != 0;
+
+ if ([oldString isEqualToString: string] && !styleChanged) {
+ return layout;
+ }
+
+ [oldString release];
+ oldString = [string copy];
+ oldStyle = *style;
+
+ static NSDictionary *attributes = nil;
+ if (styleChanged || attributes == nil) {
+ [attributes release];
+ attributes = [cocoa_font_attributes( style ) retain];
+ }
+
+ [cocoa_text_storage release];
+ cocoa_text_storage = [[NSTextStorage alloc] initWithString: string attributes: attributes];
+ [cocoa_text_storage addLayoutManager: layout];
+
+ [layout ensureLayoutForTextContainer: cocoa_text_container];
+
+ return layout;
+}
+
+static NSString * const cocoa_font_families[PLOT_FONT_FAMILY_COUNT] = {
+ [PLOT_FONT_FAMILY_SERIF] = @"Times",
+ [PLOT_FONT_FAMILY_SANS_SERIF] = @"Helvetica",
+ [PLOT_FONT_FAMILY_MONOSPACE] = @"Courier",
+ [PLOT_FONT_FAMILY_CURSIVE] = @"Apple Chancery",
+ [PLOT_FONT_FAMILY_FANTASY] = @"Marker Felt"
+};
+
+static inline NSFont *cocoa_font_get_nsfont( const plot_font_style_t *style )
+{
+ NSFont *font = [NSFont fontWithName: cocoa_font_families[style->family]
+ size: (CGFloat)style->size / FONT_SIZE_SCALE];
+
+ NSFontTraitMask traits = 0;
+ if (style->flags & FONTF_ITALIC || style->flags & FONTF_OBLIQUE) traits |= NSItalicFontMask;
+ if (style->flags & FONTF_SMALLCAPS) traits |= NSSmallCapsFontMask;
+ if (style->weight > 400) traits |= NSBoldFontMask;
+
+ if (0 != traits) {
+ NSFontManager *fm = [NSFontManager sharedFontManager];
+ font = [fm convertFont: font toHaveTrait: traits];
+ }
+
+ return font;
+}
+
+static inline NSDictionary *cocoa_font_attributes( const plot_font_style_t *style )
+{
+ return [NSDictionary dictionaryWithObjectsAndKeys:
+ cocoa_font_get_nsfont( style ), NSFontAttributeName,
+ cocoa_convert_colour( style->foreground ), NSForegroundColorAttributeName,
+ nil];
+}
diff --git a/frontends/cocoa/gui.h b/frontends/cocoa/gui.h
new file mode 100644
index 000000000..b34e9b702
--- /dev/null
+++ b/frontends/cocoa/gui.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+extern struct gui_window_table *cocoa_window_table;
+extern struct gui_clipboard_table *cocoa_clipboard_table;
+extern struct gui_misc_table *cocoa_misc_table;
+
+extern NSString * const kCookiesFileOption;
+extern NSString * const kURLsFileOption;
+extern NSString * const kHotlistFileOption;
+extern NSString * const kHomepageURLOption;
+extern NSString * const kOptionsFileOption;
+extern NSString * const kAlwaysCancelDownload;
+extern NSString * const kAlwaysCloseMultipleTabs;
+
+void cocoa_autorelease( void );
+
+nserror cocoa_warning(const char *warning, const char *detail);
diff --git a/frontends/cocoa/gui.m b/frontends/cocoa/gui.m
new file mode 100644
index 000000000..a634f6a9e
--- /dev/null
+++ b/frontends/cocoa/gui.m
@@ -0,0 +1,335 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#import "utils/nsoption.h"
+#import "utils/utils.h"
+#import "utils/log.h"
+#import "desktop/mouse.h"
+#import "desktop/gui_window.h"
+#import "desktop/gui_misc.h"
+#import "desktop/browser.h"
+#import "desktop/textinput.h"
+#import "image/ico.h"
+#import "content/fetchers/resource.h"
+#import "content/hlcache.h"
+#import "content/content.h"
+
+#import "cocoa/gui.h"
+#import "cocoa/plotter.h"
+#import "cocoa/BrowserView.h"
+#import "cocoa/BrowserViewController.h"
+#import "cocoa/BrowserWindowController.h"
+#import "cocoa/FormSelectMenu.h"
+#import "cocoa/fetch.h"
+#import "cocoa/schedule.h"
+
+
+NSString * const kCookiesFileOption = @"CookiesFile";
+NSString * const kURLsFileOption = @"URLsFile";
+NSString * const kHotlistFileOption = @"Hotlist";
+NSString * const kHomepageURLOption = @"HomepageURL";
+NSString * const kOptionsFileOption = @"ClassicOptionsFile";
+NSString * const kAlwaysCancelDownload = @"AlwaysCancelDownload";
+NSString * const kAlwaysCloseMultipleTabs = @"AlwaysCloseMultipleTabs";
+
+#define UNIMPL() NSLog( @"Function '%s' unimplemented", __func__ )
+
+struct browser_window;
+
+/* exported function docuemnted in cocoa/gui.h */
+nserror cocoa_warning(const char *warning, const char *detail)
+{
+ NSRunAlertPanel( NSLocalizedString( @"Warning",
+ @"Warning title" ),
+ NSLocalizedString( @"Warning %s%s%s",
+ @"Warning message" ),
+ NSLocalizedString( @"OK", @"" ), nil, nil,
+ warning, detail != NULL ? ": " : "",
+ detail != NULL ? detail : "" );
+ return NSERROR_OK;
+}
+
+
+static struct gui_window *
+gui_window_create(struct browser_window *bw,
+ struct gui_window *existing,
+ gui_window_create_flags flags)
+{
+ BrowserWindowController *window = nil;
+ BrowserViewController *result;
+
+ browser_window_set_scale(bw, (float)nsoption_int(scale) / 100, false);
+ if (existing != NULL) {
+ window = [(BrowserViewController *)(existing) windowController];
+ }
+
+ result = [[BrowserViewController alloc] initWithBrowser: bw];
+
+ if (!(flags & GW_CREATE_TAB) || nil == window) {
+ window = [[[BrowserWindowController alloc] init] autorelease];
+ [[window window] makeKeyAndOrderFront: nil];
+ }
+ [window addTab: result];
+
+ return (struct gui_window *)result;
+}
+
+static void gui_window_destroy(struct gui_window *g)
+{
+ BrowserViewController *vc = (BrowserViewController *)g;
+ [vc release];
+}
+
+static void gui_window_set_title(struct gui_window *g, const char *title)
+{
+ [(BrowserViewController *)g setTitle: [NSString stringWithUTF8String: title]];
+}
+
+static void gui_window_redraw_window(struct gui_window *g)
+{
+ [[(BrowserViewController *)g browserView] setNeedsDisplay: YES];
+}
+
+static void gui_window_update_box(struct gui_window *g, const struct rect *rect)
+{
+ const NSRect nsrect = cocoa_scaled_rect_wh(
+ browser_window_get_scale([(BrowserViewController *)g browser]),
+ rect->x0, rect->y0,
+ rect->x1 - rect->x0, rect->y1 - rect->y0 );
+ [[(BrowserViewController *)g browserView] setNeedsDisplayInRect: nsrect];
+}
+
+static bool gui_window_get_scroll(struct gui_window *g, int *sx, int *sy)
+{
+ NSCParameterAssert( g != NULL && sx != NULL && sy != NULL );
+
+ NSRect visible = [[(BrowserViewController *)g browserView] visibleRect];
+ *sx = cocoa_pt_to_px( NSMinX( visible ) );
+ *sy = cocoa_pt_to_px( NSMinY( visible ) );
+ return true;
+}
+
+static void gui_window_set_scroll(struct gui_window *g, int sx, int sy)
+{
+ [[(BrowserViewController *)g browserView] scrollPoint: cocoa_point( sx, sy )];
+}
+
+/**
+ * callback from core to reformat a window.
+ */
+static void cocoa_window_reformat(struct gui_window *gw)
+{
+ if (gw != NULL) {
+ [[(BrowserViewController *)gw browserView] reformat ];
+ }
+}
+
+
+static void gui_window_get_dimensions(struct gui_window *g,
+ int *width, int *height,
+ bool scaled)
+{
+ NSCParameterAssert( width != NULL && height != NULL );
+
+ NSRect frame = [[[(BrowserViewController *)g browserView] superview] frame];
+ if (scaled) {
+ const CGFloat scale = browser_window_get_scale([(BrowserViewController *)g browser]);
+ frame.size.width /= scale;
+ frame.size.height /= scale;
+ }
+ *width = cocoa_pt_to_px( NSWidth( frame ) );
+ *height = cocoa_pt_to_px( NSHeight( frame ) );
+}
+
+static void gui_window_update_extent(struct gui_window *g)
+{
+ BrowserViewController * const window = (BrowserViewController *)g;
+ int width;
+ int height;
+ struct browser_window *browser = [window browser];
+
+ browser_window_get_extents(browser, false, &width, &height);
+
+ [[window browserView] setMinimumSize:
+ cocoa_scaled_size( browser_window_get_scale(browser), width, height )];
+}
+
+static void gui_window_set_status(struct gui_window *g, const char *text)
+{
+ [(BrowserViewController *)g setStatus: [NSString stringWithUTF8String: text]];
+}
+
+static void gui_window_set_pointer(struct gui_window *g, gui_pointer_shape shape)
+{
+ switch (shape) {
+ case GUI_POINTER_DEFAULT:
+ case GUI_POINTER_WAIT:
+ case GUI_POINTER_PROGRESS:
+ [[NSCursor arrowCursor] set];
+ break;
+
+ case GUI_POINTER_CROSS:
+ [[NSCursor crosshairCursor] set];
+ break;
+
+ case GUI_POINTER_POINT:
+ case GUI_POINTER_MENU:
+ [[NSCursor pointingHandCursor] set];
+ break;
+
+ case GUI_POINTER_CARET:
+ [[NSCursor IBeamCursor] set];
+ break;
+
+ case GUI_POINTER_MOVE:
+ [[NSCursor closedHandCursor] set];
+ break;
+
+ default:
+ NSLog( @"Other cursor %d requested", shape );
+ [[NSCursor arrowCursor] set];
+ break;
+ }
+}
+
+static nserror gui_window_set_url(struct gui_window *g, struct nsurl *url)
+{
+ [(BrowserViewController *)g setUrl: [NSString stringWithUTF8String: nsurl_access(url)]];
+ return NSERROR_OK;
+}
+
+static void gui_window_start_throbber(struct gui_window *g)
+{
+ [(BrowserViewController *)g setIsProcessing: YES];
+ [(BrowserViewController *)g updateBackForward];
+}
+
+static void gui_window_stop_throbber(struct gui_window *g)
+{
+ [(BrowserViewController *)g setIsProcessing: NO];
+ [(BrowserViewController *)g updateBackForward];
+}
+
+static void gui_window_set_icon(struct gui_window *g, hlcache_handle *icon)
+{
+ NSBitmapImageRep *bmp = NULL;
+ NSImage *image = nil;
+
+ if (icon != NULL) {
+ bmp = (NSBitmapImageRep *)content_get_bitmap( icon );
+ }
+
+ if (bmp != nil) {
+ image = [[NSImage alloc] initWithSize: NSMakeSize( 32, 32 )];
+ [image addRepresentation: bmp];
+ } else {
+ image = [[NSImage imageNamed: @"NetSurf"] copy];
+ }
+ [image setFlipped: YES];
+
+ [(BrowserViewController *)g setFavicon: image];
+ [image release];
+}
+
+static void
+gui_window_place_caret(struct gui_window *g, int x, int y, int height,
+ const struct rect *clip)
+{
+ [[(BrowserViewController *)g browserView]
+ addCaretAt: cocoa_point( x, y )
+ height: cocoa_px_to_pt( height )];
+}
+
+static void gui_window_remove_caret(struct gui_window *g)
+{
+ [[(BrowserViewController *)g browserView] removeCaret];
+}
+
+static void gui_window_new_content(struct gui_window *g)
+{
+ [(BrowserViewController *)g contentUpdated];
+}
+
+
+static void gui_create_form_select_menu(struct gui_window *g,
+ struct form_control *control)
+{
+ BrowserViewController * const window = (BrowserViewController *)g;
+ FormSelectMenu *menu = [[FormSelectMenu alloc]
+ initWithControl: control
+ forWindow: [window browser]];
+ [menu runInView: [window browserView]];
+ [menu release];
+}
+
+static nserror gui_launch_url(nsurl *url)
+{
+ [[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString: [NSString stringWithUTF8String: nsurl_access(url)]]];
+ return NSERROR_OK;
+}
+
+struct ssl_cert_info;
+
+static void
+gui_cert_verify(nsurl *url,
+ const struct ssl_cert_info *certs,
+ unsigned long num,
+ nserror (*cb)(bool proceed,void *pw), void *cbpw)
+{
+ cb( false, cbpw );
+}
+
+
+static struct gui_window_table window_table = {
+ .create = gui_window_create,
+ .destroy = gui_window_destroy,
+ .redraw = gui_window_redraw_window,
+ .update = gui_window_update_box,
+ .get_scroll = gui_window_get_scroll,
+ .set_scroll = gui_window_set_scroll,
+ .get_dimensions = gui_window_get_dimensions,
+ .update_extent = gui_window_update_extent,
+ .reformat = cocoa_window_reformat,
+
+ .set_title = gui_window_set_title,
+ .set_url = gui_window_set_url,
+ .set_icon = gui_window_set_icon,
+ .set_status = gui_window_set_status,
+ .set_pointer = gui_window_set_pointer,
+ .place_caret = gui_window_place_caret,
+ .remove_caret = gui_window_remove_caret,
+ .new_content = gui_window_new_content,
+ .start_throbber = gui_window_start_throbber,
+ .stop_throbber = gui_window_stop_throbber,
+ .create_form_select_menu = gui_create_form_select_menu,
+};
+
+struct gui_window_table *cocoa_window_table = &window_table;
+
+
+static struct gui_misc_table browser_table = {
+ .schedule = cocoa_schedule,
+ .warning = cocoa_warning,
+
+ .launch_url = gui_launch_url,
+ .cert_verify = gui_cert_verify,
+};
+
+struct gui_misc_table *cocoa_misc_table = &browser_table;
diff --git a/frontends/cocoa/plotter.h b/frontends/cocoa/plotter.h
new file mode 100644
index 000000000..8a26e5246
--- /dev/null
+++ b/frontends/cocoa/plotter.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 COCOA_PLOTTER_H
+#define COCOA_PLOTTER_H
+
+#import <Cocoa/Cocoa.h>
+#import "desktop/plot_style.h"
+#import "cocoa/coordinates.h"
+
+extern const struct plotter_table cocoa_plotters;
+
+NSColor *cocoa_convert_colour( colour clr );
+
+void cocoa_update_scale_factor( void );
+
+void cocoa_set_clip( NSRect rect );
+
+#endif
diff --git a/frontends/cocoa/plotter.m b/frontends/cocoa/plotter.m
new file mode 100644
index 000000000..dbc9460c4
--- /dev/null
+++ b/frontends/cocoa/plotter.m
@@ -0,0 +1,335 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <Cocoa/Cocoa.h>
+
+#import "utils/log.h"
+#import "utils/utils.h"
+#import "desktop/browser.h"
+#import "desktop/plotters.h"
+#import "desktop/plot_style.h"
+
+#import "cocoa/font.h"
+#import "cocoa/plotter.h"
+#import "cocoa/bitmap.h"
+
+static void cocoa_plot_render_path(NSBezierPath *path,const plot_style_t *pstyle);
+static void cocoa_plot_path_set_stroke_pattern(NSBezierPath *path,const plot_style_t *pstyle);
+static inline void cocoa_center_pixel( bool x, bool y );
+
+static NSRect cocoa_plot_clip_rect;
+
+#define colour_red_component( c ) (((c) >> 0) & 0xFF)
+#define colour_green_component( c ) (((c) >> 8) & 0xFF)
+#define colour_blue_component( c ) (((c) >> 16) & 0xFF)
+#define colour_alpha_component( c ) (((c) >> 24) & 0xFF)
+#define colour_from_rgba( r, g, b, a) ((((colour)(r)) << 0) | \
+ (((colour)(g)) << 8) | \
+ (((colour)(b)) << 16) | \
+ (((colour)(a)) << 24))
+#define colour_from_rgb( r, g, b ) colour_from_rgba( (r), (g), (b), 0xFF )
+
+NSColor *cocoa_convert_colour( colour clr )
+{
+ return [NSColor colorWithDeviceRed: (float)colour_red_component( clr ) / 0xFF
+ green: (float)colour_green_component( clr ) / 0xFF
+ blue: (float)colour_blue_component( clr ) / 0xFF
+ alpha: 1.0];
+}
+
+static void cocoa_plot_path_set_stroke_pattern(NSBezierPath *path,const plot_style_t *pstyle)
+{
+ static const CGFloat dashed_pattern[2] = { 5.0, 2.0 };
+ static const CGFloat dotted_pattern[2] = { 2.0, 2.0 };
+
+ switch (pstyle->stroke_type) {
+ case PLOT_OP_TYPE_DASH:
+ [path setLineDash: dashed_pattern count: 2 phase: 0];
+ break;
+
+ case PLOT_OP_TYPE_DOT:
+ [path setLineDash: dotted_pattern count: 2 phase: 0];
+ break;
+
+ default:
+ // ignore
+ break;
+ }
+
+ [path setLineWidth: cocoa_px_to_pt( pstyle->stroke_width > 0 ? pstyle->stroke_width : 1 )];
+}
+
+static bool plot_line(int x0, int y0, int x1, int y1, const plot_style_t *pstyle)
+{
+ if (pstyle->stroke_type == PLOT_OP_TYPE_NONE) return true;
+
+ [NSGraphicsContext saveGraphicsState];
+ [NSBezierPath clipRect: cocoa_plot_clip_rect];
+
+ NSBezierPath *path = [NSBezierPath bezierPath];
+ [path moveToPoint: cocoa_point( x0, y0 )];
+ [path lineToPoint: cocoa_point( x1, y1 )];
+ cocoa_plot_path_set_stroke_pattern( path, pstyle );
+
+ const bool horizontal = y0 == y1;
+ const bool vertical = x0 == x1;
+ const bool oddThickness = pstyle->stroke_width != 0 ? (pstyle->stroke_width % 2) != 0 : true;
+
+ if (oddThickness) cocoa_center_pixel( !horizontal, !vertical );
+
+ [cocoa_convert_colour( pstyle->stroke_colour ) set];
+ [path stroke];
+
+ [NSGraphicsContext restoreGraphicsState];
+
+ return true;
+}
+
+static bool plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *pstyle)
+{
+ NSRect rect = cocoa_rect( x0, y0, x1, y1 );
+ NSBezierPath *path = [NSBezierPath bezierPathWithRect: rect];
+ cocoa_plot_render_path( path, pstyle );
+
+ return true;
+}
+
+static bool plot_text(int x, int y, const char *text, size_t length,
+ const plot_font_style_t *fstyle)
+{
+ [NSGraphicsContext saveGraphicsState];
+ [NSBezierPath clipRect: cocoa_plot_clip_rect];
+
+ cocoa_draw_string( cocoa_px_to_pt( x ), cocoa_px_to_pt( y ), text, length, fstyle );
+
+ [NSGraphicsContext restoreGraphicsState];
+
+ return true;
+}
+
+void cocoa_set_clip( NSRect rect )
+{
+ cocoa_plot_clip_rect = rect;
+}
+
+static bool plot_clip(const struct rect *clip)
+{
+ cocoa_plot_clip_rect = cocoa_rect( clip->x0, clip->y0, clip->x1, clip->y1 );
+ return true;
+}
+
+void cocoa_plot_render_path(NSBezierPath *path,const plot_style_t *pstyle)
+{
+ [NSGraphicsContext saveGraphicsState];
+ [NSBezierPath clipRect: cocoa_plot_clip_rect];
+
+ if (pstyle->fill_type != PLOT_OP_TYPE_NONE) {
+ [cocoa_convert_colour( pstyle->fill_colour ) setFill];
+ [path fill];
+ }
+
+ if (pstyle->stroke_type != PLOT_OP_TYPE_NONE) {
+ if (pstyle->stroke_width == 0 || pstyle->stroke_width % 2 != 0) cocoa_center_pixel( true, true );
+
+ cocoa_plot_path_set_stroke_pattern(path,pstyle);
+
+ [cocoa_convert_colour( pstyle->stroke_colour ) set];
+
+ [path stroke];
+ }
+
+ [NSGraphicsContext restoreGraphicsState];
+}
+
+static bool plot_arc(int x, int y, int radius, int angle1, int angle2, const plot_style_t *pstyle)
+{
+ NSBezierPath *path = [NSBezierPath bezierPath];
+ [path appendBezierPathWithArcWithCenter: NSMakePoint( x, y ) radius: radius
+ startAngle: angle1 endAngle: angle2
+ clockwise: NO];
+
+ cocoa_plot_render_path( path, pstyle);
+
+ return true;
+}
+
+static bool plot_disc(int x, int y, int radius, const plot_style_t *pstyle)
+{
+ NSBezierPath *path = [NSBezierPath bezierPathWithOvalInRect:
+ NSMakeRect( x - radius, y-radius, 2*radius, 2*radius )];
+
+ cocoa_plot_render_path( path, pstyle );
+
+ return true;
+}
+
+static bool plot_polygon(const int *p, unsigned int n, const plot_style_t *pstyle)
+{
+ if (n <= 1) return true;
+
+ NSBezierPath *path = [NSBezierPath bezierPath];
+ [path moveToPoint: cocoa_point( p[0], p[1] )];
+ for (unsigned i = 1; i < n; i++) {
+ [path lineToPoint: cocoa_point( p[2*i], p[2*i+1] )];
+ }
+ [path closePath];
+
+ cocoa_plot_render_path( path, pstyle );
+
+ return true;
+}
+
+/* complex path (for SVG) */
+static bool plot_path(const float *p, unsigned int n, colour fill, float width,
+ colour c, const float transform[6])
+{
+ if (n == 0) return true;
+
+ if (*p != PLOTTER_PATH_MOVE) {
+ LOG("Path does not start with move");
+ return false;
+ }
+
+ NSBezierPath *path = [NSBezierPath bezierPath];
+
+#define NEXT_POINT() NSMakePoint( *p++, *p++ )
+
+ while (n--) {
+ switch ((int)*p++) {
+ case PLOTTER_PATH_MOVE: {
+ const NSPoint pt = NEXT_POINT();
+ [path moveToPoint: pt];
+ break;
+ }
+
+ case PLOTTER_PATH_LINE: {
+ const NSPoint pt = NEXT_POINT();
+ [path lineToPoint: pt];
+ break;
+ }
+
+ case PLOTTER_PATH_BEZIER: {
+ const NSPoint cp1 = NEXT_POINT();
+ const NSPoint cp2 = NEXT_POINT();
+ const NSPoint ep = NEXT_POINT();
+ [path curveToPoint: ep controlPoint1: cp1 controlPoint2: cp2];
+ break;
+ }
+
+ case PLOTTER_PATH_CLOSE:
+ [path closePath];
+ break;
+
+ default:
+ LOG("Invalid path");
+ return false;
+ }
+ }
+
+#undef NEXT_POINT
+
+ [path setLineWidth: width];
+
+ CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
+ CGContextSaveGState( context );
+
+ CGContextClipToRect( context, NSRectToCGRect( cocoa_plot_clip_rect ) );
+
+ CGContextConcatCTM( context, CGAffineTransformMake( transform[0], transform[1], transform[2],
+ transform[3], transform[4], transform[5] ) );
+
+ if (fill != NS_TRANSPARENT) {
+ [cocoa_convert_colour( fill ) setFill];
+ [path fill];
+ }
+
+ if (c != NS_TRANSPARENT) {
+ cocoa_center_pixel( true, true );
+ [cocoa_convert_colour( c ) set];
+ [path stroke];
+ }
+
+ CGContextRestoreGState( context );
+
+ return true;
+}
+
+/* Image */
+static bool plot_bitmap(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg,
+ bitmap_flags_t flags)
+{
+ CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
+ CGContextSaveGState( context );
+
+ CGContextClipToRect( context, NSRectToCGRect( cocoa_plot_clip_rect ) );
+
+ const bool tileX = flags & BITMAPF_REPEAT_X;
+ const bool tileY = flags & BITMAPF_REPEAT_Y;
+
+ CGImageRef img = cocoa_get_cgimage( bitmap );
+
+ CGRect rect = NSRectToCGRect( cocoa_rect_wh( x, y, width, height ) );
+
+ if (tileX || tileY) {
+ CGContextDrawTiledImage( context, rect, img );
+ } else {
+ CGContextDrawImage( context, rect, img );
+ }
+
+ CGContextRestoreGState( context );
+
+ return true;
+}
+
+const struct plotter_table cocoa_plotters = {
+ .clip = plot_clip,
+ .arc = plot_arc,
+ .disc = plot_disc,
+ .rectangle = plot_rectangle,
+ .line = plot_line,
+ .polygon = plot_polygon,
+
+ .path = plot_path,
+
+ .bitmap = plot_bitmap,
+
+ .text = plot_text,
+
+ .option_knockout = true
+};
+
+
+CGFloat cocoa_scale_factor;
+static const CGFloat points_per_inch = 72.0;
+static CGFloat cocoa_half_pixel;
+
+void cocoa_update_scale_factor( void )
+{
+ const CGFloat scale = [[NSScreen mainScreen] userSpaceScaleFactor];
+ cocoa_scale_factor = scale == 1.0 ? 1.0 : 1.0 / scale;
+ cocoa_half_pixel = 0.5 * cocoa_scale_factor;
+ browser_set_dpi( points_per_inch * scale );
+}
+
+static inline void cocoa_center_pixel( bool x, bool y )
+{
+ NSAffineTransform *transform = [NSAffineTransform transform];
+ [transform translateXBy: x ? cocoa_half_pixel : 0.0 yBy: y ? cocoa_half_pixel : 0.0];
+ [transform concat];
+}
diff --git a/frontends/cocoa/res/BookmarksWindow.xib b/frontends/cocoa/res/BookmarksWindow.xib
new file mode 100644
index 000000000..b038e6ca1
--- /dev/null
+++ b/frontends/cocoa/res/BookmarksWindow.xib
@@ -0,0 +1,610 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
+ <data>
+ <int key="IBDocument.SystemTarget">1060</int>
+ <string key="IBDocument.SystemVersion">10J567</string>
+ <string key="IBDocument.InterfaceBuilderVersion">804</string>
+ <string key="IBDocument.AppKitVersion">1038.35</string>
+ <string key="IBDocument.HIToolboxVersion">462.00</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="NS.object.0">804</string>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="1"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys" id="0">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomObject" id="1001">
+ <string key="NSClassName">BookmarksController</string>
+ </object>
+ <object class="NSCustomObject" id="1003">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1004">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSWindowTemplate" id="1005">
+ <int key="NSWindowStyleMask">15</int>
+ <int key="NSWindowBacking">2</int>
+ <string key="NSWindowRect">{{196, 80}, {350, 400}}</string>
+ <int key="NSWTFlags">1618477056</int>
+ <string key="NSWindowTitle">Bookmarks</string>
+ <string key="NSWindowClass">NSWindow</string>
+ <nil key="NSViewClass"/>
+ <string key="NSWindowContentMaxSize">{1.79769e+308, 1.79769e+308}</string>
+ <string key="NSWindowContentMinSize">{200, 100}</string>
+ <object class="NSView" key="NSWindowView" id="1006">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">256</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSScrollView" id="79136560">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">274</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSClipView" id="286793088">
+ <reference key="NSNextResponder" ref="79136560"/>
+ <int key="NSvFlags">2304</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomView" id="347174519">
+ <reference key="NSNextResponder" ref="286793088"/>
+ <int key="NSvFlags">274</int>
+ <string key="NSFrameSize">{350, 360}</string>
+ <reference key="NSSuperview" ref="286793088"/>
+ <string key="NSClassName">TreeView</string>
+ </object>
+ </object>
+ <string key="NSFrame">{{1, 1}, {350, 360}}</string>
+ <reference key="NSSuperview" ref="79136560"/>
+ <reference key="NSNextKeyView" ref="347174519"/>
+ <reference key="NSDocView" ref="347174519"/>
+ <object class="NSColor" key="NSBGColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MC42NjY2NjY2NjY3AA</bytes>
+ </object>
+ </object>
+ <int key="NScvFlags">4</int>
+ </object>
+ <object class="NSScroller" id="343618412">
+ <reference key="NSNextResponder" ref="79136560"/>
+ <int key="NSvFlags">-2147483392</int>
+ <string key="NSFrame">{{353, 1}, {15, 313}}</string>
+ <reference key="NSSuperview" ref="79136560"/>
+ <reference key="NSTarget" ref="79136560"/>
+ <string key="NSAction">_doScroller:</string>
+ <double key="NSCurValue">1</double>
+ <double key="NSPercent">0.96363627910614014</double>
+ </object>
+ <object class="NSScroller" id="885399726">
+ <reference key="NSNextResponder" ref="79136560"/>
+ <int key="NSvFlags">-2147483392</int>
+ <string key="NSFrame">{{1, 314}, {352, 15}}</string>
+ <reference key="NSSuperview" ref="79136560"/>
+ <int key="NSsFlags">1</int>
+ <reference key="NSTarget" ref="79136560"/>
+ <string key="NSAction">_doScroller:</string>
+ <double key="NSPercent">0.50602412223815918</double>
+ </object>
+ </object>
+ <string key="NSFrame">{{-1, 39}, {352, 362}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <reference key="NSNextKeyView" ref="286793088"/>
+ <int key="NSsFlags">562</int>
+ <reference key="NSVScroller" ref="343618412"/>
+ <reference key="NSHScroller" ref="885399726"/>
+ <reference key="NSContentView" ref="286793088"/>
+ </object>
+ <object class="NSButton" id="1013386414">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">292</int>
+ <string key="NSFrame">{{7, 7}, {29, 25}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="1030859690">
+ <int key="NSCellFlags">-2080244224</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents"/>
+ <object class="NSFont" key="NSSupport" id="1037042855">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">13</double>
+ <int key="NSfFlags">1044</int>
+ </object>
+ <reference key="NSControlView" ref="1013386414"/>
+ <int key="NSButtonFlags">-2033958657</int>
+ <int key="NSButtonFlags2">163</int>
+ <object class="NSCustomResource" key="NSNormalImage">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSAddTemplate</string>
+ </object>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">400</int>
+ <int key="NSPeriodicInterval">75</int>
+ </object>
+ </object>
+ <object class="NSButton" id="812276353">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">292</int>
+ <string key="NSFrame">{{43, 7}, {29, 25}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="142633288">
+ <int key="NSCellFlags">-2080244224</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents"/>
+ <reference key="NSSupport" ref="1037042855"/>
+ <reference key="NSControlView" ref="812276353"/>
+ <int key="NSButtonFlags">-2033958657</int>
+ <int key="NSButtonFlags2">163</int>
+ <object class="NSCustomResource" key="NSNormalImage">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSRemoveTemplate</string>
+ </object>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">400</int>
+ <int key="NSPeriodicInterval">75</int>
+ </object>
+ </object>
+ <object class="NSButton" id="482147213">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">292</int>
+ <string key="NSFrame">{{80, 7}, {38, 25}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="586666285">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents">Edit</string>
+ <reference key="NSSupport" ref="1037042855"/>
+ <reference key="NSControlView" ref="482147213"/>
+ <int key="NSButtonFlags">-2038152961</int>
+ <int key="NSButtonFlags2">163</int>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">400</int>
+ <int key="NSPeriodicInterval">75</int>
+ </object>
+ </object>
+ </object>
+ <string key="NSFrameSize">{350, 400}</string>
+ <reference key="NSSuperview"/>
+ </object>
+ <string key="NSScreenRect">{{0, 0}, {1680, 1028}}</string>
+ <string key="NSMinSize">{200, 122}</string>
+ <string key="NSMaxSize">{1.79769e+308, 1.79769e+308}</string>
+ <bool key="NSAutorecalculatesContentBorderThicknessMinY">NO</bool>
+ <double key="NSContentBorderThicknessMinY">39</double>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">window</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="1005"/>
+ </object>
+ <int key="connectionID">3</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">view</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="347174519"/>
+ </object>
+ <int key="connectionID">8</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">addFolder:</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="1013386414"/>
+ </object>
+ <int key="connectionID">17</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">deleteSelected:</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="812276353"/>
+ </object>
+ <int key="connectionID">18</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">editSelected:</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="482147213"/>
+ </object>
+ <int key="connectionID">19</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">initialFirstResponder</string>
+ <reference key="source" ref="1005"/>
+ <reference key="destination" ref="347174519"/>
+ </object>
+ <int key="connectionID">20</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <reference key="object" ref="0"/>
+ <reference key="children" ref="1000"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1001"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">File's Owner</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1003"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1004"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">1</int>
+ <reference key="object" ref="1005"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1006"/>
+ </object>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">2</int>
+ <reference key="object" ref="1006"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="79136560"/>
+ <reference ref="1013386414"/>
+ <reference ref="812276353"/>
+ <reference ref="482147213"/>
+ </object>
+ <reference key="parent" ref="1005"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">4</int>
+ <reference key="object" ref="79136560"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="343618412"/>
+ <reference ref="885399726"/>
+ <reference ref="347174519"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5</int>
+ <reference key="object" ref="343618412"/>
+ <reference key="parent" ref="79136560"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">6</int>
+ <reference key="object" ref="885399726"/>
+ <reference key="parent" ref="79136560"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">7</int>
+ <reference key="object" ref="347174519"/>
+ <reference key="parent" ref="79136560"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">11</int>
+ <reference key="object" ref="1013386414"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1030859690"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">12</int>
+ <reference key="object" ref="1030859690"/>
+ <reference key="parent" ref="1013386414"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">13</int>
+ <reference key="object" ref="812276353"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="142633288"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">14</int>
+ <reference key="object" ref="142633288"/>
+ <reference key="parent" ref="812276353"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">15</int>
+ <reference key="object" ref="482147213"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="586666285"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">16</int>
+ <reference key="object" ref="586666285"/>
+ <reference key="parent" ref="482147213"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>1.IBEditorWindowLastContentRect</string>
+ <string>1.IBPluginDependency</string>
+ <string>1.IBWindowTemplateEditedContentRect</string>
+ <string>1.NSWindowTemplate.visibleAtLaunch</string>
+ <string>1.WindowOrigin</string>
+ <string>1.editorWindowContentRectSynchronizationRect</string>
+ <string>1.windowTemplate.hasMinSize</string>
+ <string>1.windowTemplate.minSize</string>
+ <string>11.IBPluginDependency</string>
+ <string>11.IBViewBoundsToFrameTransform</string>
+ <string>12.IBPluginDependency</string>
+ <string>13.IBPluginDependency</string>
+ <string>13.IBViewBoundsToFrameTransform</string>
+ <string>14.IBPluginDependency</string>
+ <string>15.IBPluginDependency</string>
+ <string>15.IBViewBoundsToFrameTransform</string>
+ <string>16.IBPluginDependency</string>
+ <string>2.IBPluginDependency</string>
+ <string>4.IBPluginDependency</string>
+ <string>4.IBViewBoundsToFrameTransform</string>
+ <string>5.IBPluginDependency</string>
+ <string>6.IBPluginDependency</string>
+ <string>7.IBPluginDependency</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>{{226, 417}, {350, 400}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{226, 417}, {350, 400}}</string>
+ <integer value="1"/>
+ <string>{196, 240}</string>
+ <string>{{202, 428}, {480, 270}}</string>
+ <boolean value="YES"/>
+ <string>{200, 100}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABAwAAAwfAAAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABCJAAAwfAAAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABCmAAAwfAAAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAAC/gAAAw7eAAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">20</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">BookmarksController</string>
+ <string key="superclassName">NSWindowController</string>
+ <object class="NSMutableDictionary" key="actions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>addBookmark:</string>
+ <string>addFolder:</string>
+ <string>deleteSelected:</string>
+ <string>editSelected:</string>
+ <string>openBookmarkURL:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="actionInfosByName">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>addBookmark:</string>
+ <string>addFolder:</string>
+ <string>deleteSelected:</string>
+ <string>editSelected:</string>
+ <string>openBookmarkURL:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBActionInfo">
+ <string key="name">addBookmark:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">addFolder:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">deleteSelected:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">editSelected:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">openBookmarkURL:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="outlets">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>defaultMenu</string>
+ <string>view</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>NSMenu</string>
+ <string>TreeView</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>defaultMenu</string>
+ <string>view</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBToOneOutletInfo">
+ <string key="name">defaultMenu</string>
+ <string key="candidateClassName">NSMenu</string>
+ </object>
+ <object class="IBToOneOutletInfo">
+ <string key="name">view</string>
+ <string key="candidateClassName">TreeView</string>
+ </object>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">BookmarksController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabDragAssistant.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabBarCell.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabBarControl.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">ScrollableView</string>
+ <string key="superclassName">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">ScrollableView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">TreeView</string>
+ <string key="superclassName">ScrollableView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">TreeView.h</string>
+ </object>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
+ <integer value="3000" key="NS.object.0"/>
+ </object>
+ <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../NetSurf.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ <object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>NSAddTemplate</string>
+ <string>NSRemoveTemplate</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>{8, 8}</string>
+ <string>{8, 8}</string>
+ </object>
+ </object>
+ </data>
+</archive>
diff --git a/frontends/cocoa/res/Browser.xib b/frontends/cocoa/res/Browser.xib
new file mode 100644
index 000000000..35ff885b5
--- /dev/null
+++ b/frontends/cocoa/res/Browser.xib
@@ -0,0 +1,399 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
+ <data>
+ <int key="IBDocument.SystemTarget">1050</int>
+ <string key="IBDocument.SystemVersion">10J567</string>
+ <string key="IBDocument.InterfaceBuilderVersion">804</string>
+ <string key="IBDocument.AppKitVersion">1038.35</string>
+ <string key="IBDocument.HIToolboxVersion">462.00</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="NS.object.0">804</string>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="41"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys" id="0">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomObject" id="1001">
+ <string key="NSClassName">BrowserViewController</string>
+ </object>
+ <object class="NSCustomObject" id="1003">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1004">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSView" id="716999560">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">274</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSScrollView" id="140458632">
+ <reference key="NSNextResponder" ref="716999560"/>
+ <int key="NSvFlags">274</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSClipView" id="461870317">
+ <reference key="NSNextResponder" ref="140458632"/>
+ <int key="NSvFlags">2304</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomView" id="623715071">
+ <reference key="NSNextResponder" ref="461870317"/>
+ <int key="NSvFlags">274</int>
+ <string key="NSFrameSize">{691, 631}</string>
+ <reference key="NSSuperview" ref="461870317"/>
+ <string key="NSClassName">BrowserView</string>
+ </object>
+ </object>
+ <string key="NSFrame">{{1, 1}, {691, 631}}</string>
+ <reference key="NSSuperview" ref="140458632"/>
+ <reference key="NSNextKeyView" ref="623715071"/>
+ <reference key="NSDocView" ref="623715071"/>
+ <object class="NSColor" key="NSBGColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MC42NjY2NjY2NjY3AA</bytes>
+ </object>
+ </object>
+ <int key="NScvFlags">4</int>
+ </object>
+ <object class="NSScroller" id="411009984">
+ <reference key="NSNextResponder" ref="140458632"/>
+ <int key="NSvFlags">-2147483392</int>
+ <string key="NSFrame">{{677, 1}, {15, 616}}</string>
+ <reference key="NSSuperview" ref="140458632"/>
+ <reference key="NSTarget" ref="140458632"/>
+ <string key="NSAction">_doScroller:</string>
+ <double key="NSCurValue">1</double>
+ <double key="NSPercent">0.96363627910614014</double>
+ </object>
+ <object class="NSScroller" id="17116469">
+ <reference key="NSNextResponder" ref="140458632"/>
+ <int key="NSvFlags">-2147483392</int>
+ <string key="NSFrame">{{1, 617}, {676, 15}}</string>
+ <reference key="NSSuperview" ref="140458632"/>
+ <int key="NSsFlags">1</int>
+ <reference key="NSTarget" ref="140458632"/>
+ <string key="NSAction">_doScroller:</string>
+ <double key="NSPercent">0.50602412223815918</double>
+ </object>
+ </object>
+ <string key="NSFrame">{{-1, 0}, {693, 633}}</string>
+ <reference key="NSSuperview" ref="716999560"/>
+ <reference key="NSNextKeyView" ref="461870317"/>
+ <int key="NSsFlags">562</int>
+ <reference key="NSVScroller" ref="411009984"/>
+ <reference key="NSHScroller" ref="17116469"/>
+ <reference key="NSContentView" ref="461870317"/>
+ </object>
+ </object>
+ <string key="NSFrameSize">{691, 632}</string>
+ <reference key="NSSuperview"/>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">view</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="716999560"/>
+ </object>
+ <int key="connectionID">52</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">browserView</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="623715071"/>
+ </object>
+ <int key="connectionID">53</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <reference key="object" ref="0"/>
+ <reference key="children" ref="1000"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1001"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">File's Owner</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1003"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1004"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">41</int>
+ <reference key="object" ref="716999560"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="140458632"/>
+ </object>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">44</int>
+ <reference key="object" ref="140458632"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="623715071"/>
+ <reference ref="17116469"/>
+ <reference ref="411009984"/>
+ </object>
+ <reference key="parent" ref="716999560"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">45</int>
+ <reference key="object" ref="623715071"/>
+ <reference key="parent" ref="140458632"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">46</int>
+ <reference key="object" ref="17116469"/>
+ <reference key="parent" ref="140458632"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">47</int>
+ <reference key="object" ref="411009984"/>
+ <reference key="parent" ref="140458632"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>41.IBEditorWindowLastContentRect</string>
+ <string>41.IBPluginDependency</string>
+ <string>44.IBPluginDependency</string>
+ <string>44.IBViewBoundsToFrameTransform</string>
+ <string>45.IBPluginDependency</string>
+ <string>46.IBPluginDependency</string>
+ <string>47.IBPluginDependency</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>{{290, 199}, {691, 632}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAAC/gAAAw5EAAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">53</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">BrowserView</string>
+ <string key="superclassName">ScrollableView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">BrowserView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">BrowserViewController</string>
+ <string key="superclassName">NSViewController</string>
+ <object class="NSMutableDictionary" key="actions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>goBack:</string>
+ <string>goForward:</string>
+ <string>navigate:</string>
+ <string>reloadPage:</string>
+ <string>stopLoading:</string>
+ <string>zoomIn:</string>
+ <string>zoomOriginal:</string>
+ <string>zoomOut:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="actionInfosByName">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>goBack:</string>
+ <string>goForward:</string>
+ <string>navigate:</string>
+ <string>reloadPage:</string>
+ <string>stopLoading:</string>
+ <string>zoomIn:</string>
+ <string>zoomOriginal:</string>
+ <string>zoomOut:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBActionInfo">
+ <string key="name">goBack:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">goForward:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">navigate:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">reloadPage:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">stopLoading:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">zoomIn:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">zoomOriginal:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">zoomOut:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="outlets">
+ <string key="NS.key.0">browserView</string>
+ <string key="NS.object.0">BrowserView</string>
+ </object>
+ <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <string key="NS.key.0">browserView</string>
+ <object class="IBToOneOutletInfo" key="NS.object.0">
+ <string key="name">browserView</string>
+ <string key="candidateClassName">BrowserView</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">BrowserViewController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabDragAssistant.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabBarCell.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabBarControl.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">ScrollableView</string>
+ <string key="superclassName">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">ScrollableView.h</string>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Print.framework/Headers/PDEPluginInterface.h</string>
+ </object>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
+ <integer value="1050" key="NS.object.0"/>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
+ <integer value="3000" key="NS.object.0"/>
+ </object>
+ <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../NetSurf.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ </data>
+</archive>
diff --git a/frontends/cocoa/res/BrowserWindow.xib b/frontends/cocoa/res/BrowserWindow.xib
new file mode 100644
index 000000000..982144cb8
--- /dev/null
+++ b/frontends/cocoa/res/BrowserWindow.xib
@@ -0,0 +1,1395 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.03">
+ <data>
+ <int key="IBDocument.SystemTarget">1050</int>
+ <string key="IBDocument.SystemVersion">9L31a</string>
+ <string key="IBDocument.InterfaceBuilderVersion">680</string>
+ <string key="IBDocument.AppKitVersion">949.54</string>
+ <string key="IBDocument.HIToolboxVersion">353.00</string>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="1" id="9"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomObject" id="1001">
+ <string key="NSClassName">BrowserWindowController</string>
+ </object>
+ <object class="NSCustomObject" id="1003">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1004">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSWindowTemplate" id="1005">
+ <int key="NSWindowStyleMask">4111</int>
+ <int key="NSWindowBacking">2</int>
+ <string key="NSWindowRect">{{139, 364}, {774, 554}}</string>
+ <int key="NSWTFlags">1618477056</int>
+ <string key="NSWindowTitle">NetSurf</string>
+ <string key="NSWindowClass">BrowserWindow</string>
+ <object class="NSToolbar" key="NSViewClass" id="71746575">
+ <object class="NSMutableString" key="NSToolbarIdentifier">
+ <characters key="NS.bytes">8335B5EA-A088-4DE8-BF4F-777E98920BB3</characters>
+ </object>
+ <nil key="NSToolbarDelegate"/>
+ <bool key="NSToolbarPrefersToBeShown">YES</bool>
+ <bool key="NSToolbarShowsBaselineSeparator">YES</bool>
+ <bool key="NSToolbarAllowsUserCustomization">YES</bool>
+ <bool key="NSToolbarAutosavesConfiguration">NO</bool>
+ <int key="NSToolbarDisplayMode">2</int>
+ <int key="NSToolbarSizeMode">1</int>
+ <object class="NSMutableDictionary" key="NSToolbarIBIdentifiedItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>6D497003-6D4B-4335-ADCE-368C7CD87371</string>
+ <string>9DB83278-4E60-41F8-8A7C-C0B2E00A552B</string>
+ <string>BC5CEBFC-2E3B-420C-A75F-BE0760149C45</string>
+ <string>E2E89C48-DD3F-47A5-9E6C-25985A970F69</string>
+ <string>NSToolbarCustomizeToolbarItem</string>
+ <string>NSToolbarFlexibleSpaceItem</string>
+ <string>NSToolbarSeparatorItem</string>
+ <string>NSToolbarSpaceItem</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSToolbarItem" id="16676378">
+ <object class="NSMutableString" key="NSToolbarItemIdentifier">
+ <characters key="NS.bytes">6D497003-6D4B-4335-ADCE-368C7CD87371</characters>
+ </object>
+ <string key="NSToolbarItemLabel">History</string>
+ <string key="NSToolbarItemPaletteLabel">History</string>
+ <nil key="NSToolbarItemToolTip"/>
+ <object class="NSButton" key="NSToolbarItemView" id="229385913">
+ <nil key="NSNextResponder"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{8, 14}, {30, 25}}</string>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="296571644">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents"/>
+ <object class="NSFont" key="NSSupport" id="770988704">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">1.300000e+01</double>
+ <int key="NSfFlags">1044</int>
+ </object>
+ <reference key="NSControlView" ref="229385913"/>
+ <int key="NSButtonFlags">919355647</int>
+ <int key="NSButtonFlags2">163</int>
+ <object class="NSCustomResource" key="NSNormalImage" id="235904051">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSIconViewTemplate</string>
+ </object>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">400</int>
+ <int key="NSPeriodicInterval">75</int>
+ </object>
+ </object>
+ <reference key="NSToolbarItemImage" ref="235904051"/>
+ <nil key="NSToolbarItemTarget"/>
+ <nil key="NSToolbarItemAction"/>
+ <string key="NSToolbarItemMinSize">{22, 25}</string>
+ <string key="NSToolbarItemMaxSize">{32, 25}</string>
+ <bool key="NSToolbarItemEnabled">YES</bool>
+ <bool key="NSToolbarItemAutovalidates">YES</bool>
+ <int key="NSToolbarItemTag">0</int>
+ <bool key="NSToolbarIsUserRemovable">YES</bool>
+ <int key="NSToolbarItemVisibilityPriority">0</int>
+ </object>
+ <object class="NSToolbarItem" id="694471322">
+ <object class="NSMutableString" key="NSToolbarItemIdentifier">
+ <characters key="NS.bytes">9DB83278-4E60-41F8-8A7C-C0B2E00A552B</characters>
+ </object>
+ <string key="NSToolbarItemLabel">Homepage</string>
+ <string key="NSToolbarItemPaletteLabel">Homepage</string>
+ <nil key="NSToolbarItemToolTip"/>
+ <object class="NSButton" key="NSToolbarItemView" id="518219892">
+ <nil key="NSNextResponder"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{18, 14}, {30, 25}}</string>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="413663381">
+ <int key="NSCellFlags">-2080244224</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents"/>
+ <reference key="NSSupport" ref="770988704"/>
+ <reference key="NSControlView" ref="518219892"/>
+ <int key="NSButtonFlags">-2033434369</int>
+ <int key="NSButtonFlags2">99</int>
+ <object class="NSCustomResource" key="NSNormalImage" id="967303005">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">HomeTemplate</string>
+ </object>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">400</int>
+ <int key="NSPeriodicInterval">75</int>
+ </object>
+ </object>
+ <reference key="NSToolbarItemImage" ref="967303005"/>
+ <nil key="NSToolbarItemTarget"/>
+ <nil key="NSToolbarItemAction"/>
+ <string key="NSToolbarItemMinSize">{30, 25}</string>
+ <string key="NSToolbarItemMaxSize">{30, 25}</string>
+ <bool key="NSToolbarItemEnabled">YES</bool>
+ <bool key="NSToolbarItemAutovalidates">YES</bool>
+ <int key="NSToolbarItemTag">0</int>
+ <bool key="NSToolbarIsUserRemovable">YES</bool>
+ <int key="NSToolbarItemVisibilityPriority">0</int>
+ </object>
+ <object class="NSToolbarItem" id="685547192">
+ <object class="NSMutableString" key="NSToolbarItemIdentifier">
+ <characters key="NS.bytes">BC5CEBFC-2E3B-420C-A75F-BE0760149C45</characters>
+ </object>
+ <string key="NSToolbarItemLabel"/>
+ <string key="NSToolbarItemPaletteLabel">Back/Forward</string>
+ <nil key="NSToolbarItemToolTip"/>
+ <object class="NSSegmentedControl" key="NSToolbarItemView" id="692457026">
+ <nil key="NSNextResponder"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{7, 14}, {67, 25}}</string>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSSegmentedCell" key="NSCell" id="845979064">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">0</int>
+ <object class="NSFont" key="NSSupport">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">1.300000e+01</double>
+ <int key="NSfFlags">16</int>
+ </object>
+ <reference key="NSControlView" ref="692457026"/>
+ <object class="NSMutableArray" key="NSSegmentImages">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSSegmentItem">
+ <double key="NSSegmentItemWidth">3.000000e+01</double>
+ <object class="NSCustomResource" key="NSSegmentItemImage">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSLeftFacingTriangleTemplate</string>
+ </object>
+ <string key="NSSegmentItemLabel"/>
+ <string key="NSSegmentItemTooltip">Back</string>
+ <int key="NSSegmentItemImageScaling">0</int>
+ </object>
+ <object class="NSSegmentItem">
+ <double key="NSSegmentItemWidth">3.000000e+01</double>
+ <object class="NSCustomResource" key="NSSegmentItemImage">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSRightFacingTriangleTemplate</string>
+ </object>
+ <string key="NSSegmentItemLabel"/>
+ <string key="NSSegmentItemTooltip">Forward</string>
+ <int key="NSSegmentItemTag">1</int>
+ <int key="NSSegmentItemImageScaling">0</int>
+ </object>
+ </object>
+ <int key="NSSelectedSegment">1</int>
+ <int key="NSTrackingMode">2</int>
+ <int key="NSSegmentStyle">2</int>
+ </object>
+ </object>
+ <nil key="NSToolbarItemImage"/>
+ <nil key="NSToolbarItemTarget"/>
+ <nil key="NSToolbarItemAction"/>
+ <string key="NSToolbarItemMinSize">{67, 25}</string>
+ <string key="NSToolbarItemMaxSize">{71, 25}</string>
+ <bool key="NSToolbarItemEnabled">YES</bool>
+ <bool key="NSToolbarItemAutovalidates">YES</bool>
+ <int key="NSToolbarItemTag">0</int>
+ <bool key="NSToolbarIsUserRemovable">YES</bool>
+ <int key="NSToolbarItemVisibilityPriority">0</int>
+ </object>
+ <object class="NSToolbarItem" id="192029103">
+ <object class="NSMutableString" key="NSToolbarItemIdentifier">
+ <characters key="NS.bytes">E2E89C48-DD3F-47A5-9E6C-25985A970F69</characters>
+ </object>
+ <string key="NSToolbarItemLabel"/>
+ <string key="NSToolbarItemPaletteLabel">URL</string>
+ <nil key="NSToolbarItemToolTip"/>
+ <object class="NSTextField" key="NSToolbarItemView" id="77748234">
+ <nil key="NSNextResponder"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{0, 14}, {96, 22}}</string>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="1053649244">
+ <int key="NSCellFlags">-1804468671</int>
+ <int key="NSCellFlags2">268436480</int>
+ <string key="NSContents"/>
+ <reference key="NSSupport" ref="770988704"/>
+ <string key="NSPlaceholderString">Open this URL</string>
+ <reference key="NSControlView" ref="77748234"/>
+ <bool key="NSDrawsBackground">YES</bool>
+ <object class="NSColor" key="NSBackgroundColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">textBackgroundColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MQA</bytes>
+ </object>
+ </object>
+ <object class="NSColor" key="NSTextColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">textColor</string>
+ <object class="NSColor" key="NSColor" id="733901069">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MAA</bytes>
+ </object>
+ </object>
+ </object>
+ </object>
+ <nil key="NSToolbarItemImage"/>
+ <nil key="NSToolbarItemTarget"/>
+ <nil key="NSToolbarItemAction"/>
+ <string key="NSToolbarItemMinSize">{96, 22}</string>
+ <string key="NSToolbarItemMaxSize">{10000, 22}</string>
+ <bool key="NSToolbarItemEnabled">YES</bool>
+ <bool key="NSToolbarItemAutovalidates">YES</bool>
+ <int key="NSToolbarItemTag">0</int>
+ <bool key="NSToolbarIsUserRemovable">YES</bool>
+ <int key="NSToolbarItemVisibilityPriority">0</int>
+ </object>
+ <object class="NSToolbarItem" id="276197344">
+ <string key="NSToolbarItemIdentifier">NSToolbarCustomizeToolbarItem</string>
+ <string key="NSToolbarItemLabel">Customize</string>
+ <string key="NSToolbarItemPaletteLabel">Customize</string>
+ <string key="NSToolbarItemToolTip">Customize Toolbar</string>
+ <nil key="NSToolbarItemView"/>
+ <object class="NSImage" key="NSToolbarItemImage">
+ <int key="NSImageFlags">683671552</int>
+ <string key="NSSize">{32, 32}</string>
+ <object class="NSMutableArray" key="NSReps">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="0" id="8"/>
+ <object class="NSBitmapImageRep">
+ <object class="NSData" key="NSTIFFRepresentation">
+ <bytes key="NS.bytes">TU0AKgAAEAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAICAgbAAAABAAAAAAEBAQIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAQEBAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAENDQ0dJSUlW11dXbBpaWnDb29vzyAgIGUPDw8xAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAABsbGyUoKChIHh4ePSkpKUonJycsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwMDCEJCQoWvr6/i9fX1/fX19f7h4eH82dnZ+YODg9sYGBg8
+AAAAAAAAAAAAAAAAAAAAAAAAAAJFRUV6ZGRkvf39/f/+/v7//////0NDQ7QAAAAEAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8PDx9ISEiH2tra8/7+/v/j4+P/2NjY/7a2tv+ysrL/
+i4uL7j09PeUKCgoSAAAAAAAAAAAAAAAAUVFRlo2NjfTIyMj6x8fH/56env97e3v/ISEhMAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPDw8lRUVFjtDQ0Pb+/v7/4eHh/dDQ0P+NjY3/
+Tk5O6yoqKrIfHx+gGhoarCIiImwAAAABAAAAADg4OGWenp7/y8vL/d3d3f+8vLz/hYWF/0RERE0AAAAA
+AAAAAAAAAAAfHx8sAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAj8/P2za2try9/f3/+vr6/7Kysr/
+dnZ2/D4+PvwgICB+EBAQNAICAgcAAAAPERERPQMDAwkQEBASh4eH2MHBwf/o6Oj/39/f/3R0dP88PDxO
+AAAAAAAAAAAAAAAAcnJylkNDQ58HBwcIAAAAAAAAAAAAAAAAAAAAAAAAAAAGBgYMfn5+uMvLy/3Hx8f+
+u7u7/5OTk/9CQkL7HRYL7AICAgQAAAAAAAAAAAAAAAAAAAAAAgICBS4uLjOWlpbz6urq/+7u7v+rq6v/
+IyMj0wAAAAAAAAAAAAAAAIGBgZGLi4v/QkJC2Q0NDSYAAAAAAAAAAAAAAAAAAAAAGRkZJF5eXoylpaXz
+pqam/4qKiv91dXX/YWFh+iwkHvojEADrJhcGoAgICA0AAAAAAAAAAAAAAAAAAAAAPj4+RJaWlvr19fX/
+6+vr/7W1tfVaWlrlAwMDbAAAAACKioqcoqKi/7+/v/9HR0fnDg4OJwAAAAAAAAAAHR0dK3V1dZ+pqanC
++vr6/9vb2/99fX3+Y2Nj+EhISOgMCAP4JhIA8jgaAPBxNADsNyMMjgcHBwsAAAAAAAAAAAAAAAAMDAwO
+m5ub+Orq6v/y8vL/0NDQ7ICAgMtkZGTqcHBwzLOzs//Pz8//0tLS/0xMTNoJCQkhAAAAABoaGjZ/f3+4
+//////j4+P/9/f3/9vb2/0dHR/8oKCjkKSkpoAQCAJUNBQDXVSgA5XY5AOuTSADcOiUOewYGBgoAAAAA
+AAAAAB4eHmFlZWX76Ojo/+7u7v/5+fn/5OTk9Li4uO3f39//8PDw/+Hh4f/Ly8v/Ozs7rQICAgMAAAAA
+GBgYL3x8fP///////////7S0tP+np6f/QEBA+xUVFXUJCQkkBAQEDgcFA20jEQC7YzIA3n9BAOiiUwDK
+NiMObgYGBgoTExNIOzs74bGxsf3Dw8P/1dXV////////////////////////////8/Pz/3R0dOcPDw9H
+AAAAAQAAAAAAAAAAVVVVooCAgP/v7+//v7+//2VlZf86OjrLAQEBBgAAAAAAAAAAAAAAABENCVctFwCl
+ZTQA1oZFAOCqWgDAMiMTgzExMcKzs7P4wcHB/5+fn//W1tb///////v7+/v//////////729vfOFhYXy
+IyMjdAAAAAMAAAABAAAAAAAAAAAAAAAASkpKmlpaWv+RkZH/Nzc38hgYGG4AAAABAAAAAAAAAAAAAAAA
+AAAAAAsFAEUwGACbaDcAzXpCAOFnRiLjs7Kx+MTExP+pqan/09PT/3p6evpdXV3rbGxs6l1dXfVXV1fs
+UlJSxykpKWMAAAAGAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAQUFBmiMjI/8cHBysBQUFHQAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAsFAEAuGACfSzAT47y4s/24uLj/lJSU/9TU1P+Dg4PkMzMzzwYGBgw6OjpB
+QEBASQ8PDxYbGxshAAAAAgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIimgoKCi4AAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAwJBYS9u7n4r6+v/4GBgf/Pz8//iISB6CYmJqoDAwMK
+AAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAABUwcHB7qurq/9ubm7/zs7O/5yOgPhlPhbf
+MhsCegAAAA0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMnCgoKZsvLy++pqan/Wlpa/9LS0v+Nh4Hq
+SCYE4IdDAeSbTgDLNxsAhwAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMOxoaGobV1dXzq6ur/0ZGRv/Z2dn/
+hoaGxQkEAIY6HgCyaDMA2YVAAOuWSQDZPh4AngAAABEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgYGFEoKCio0tLS86urq/8xMTH/
+4uLi/4+Pj84AAABIAAAAAg8HAGg6HQDKaDIA4YI+APCVRQDsRSEAuAAAABMAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWFhYrNTU1z9ra2vb8/Pz/
+FxcX/+rq6v+amprZAAAASAAAAAIAAAAAAAAAABAIAIY4GwDiaTEA6X46APeVRAD/SyIA0gAAABIAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgICBT8/P4asrKz3
+0dHR//39/f/z8/P/paWl5gUFBVkAAAACAAAAAAAAAAAAAAAAAAAAABIJAKUxFgD6bTMA9no4AP9uLwD/
+KxIAxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBwcO
+QUFBmpqamv+pqan/+Pj4/7GxsfIXFxeBAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAHAMogDgD/
+WSkA/zwaAPcuEwCyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAEBAQMnJydWUFBQ85aWlvqoqKjfKioqrQEBAQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAsFAOUXCQD6IA4AwgQCADMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAgICBBR0dHmi4uLnACAgIHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAgDAMwFAgA9AAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE
+AAAACQAAABAAAAAYAAAAIAAAACoAAAAxAAAANgAAADkAAAA4AQEBOQAAAC8AAAAnAAAAHQAAABUAAAAO
+AAAACgAAAAsAAAAPAAAAFwAAACAAAAAqAAAAMQAAADQAAAAzAAAALQAAACQAAAAbAAAAEgAAAAsAAAAA
+AAAAAAAAAAQAAAAJAAAAEAAAABgAAAAgAAAAKgAAADEAAAA2AAAAOQAAADgAAAA1AAAALwAAACcAAAAd
+AAAAFQAAAA4AAAAKAAAACwAAAA8AAAAXAAAAIAAAACoAAAAxAAAANAAAADMAAAAtAAAAJAAAABsAAAAS
+AAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0BAAADAAAAAQAgAAABAQADAAAAAQAgAAABAgADAAAABAAA
+EKoBAwADAAAAAQABAAABBgADAAAAAQACAAABEQAEAAAAAQAAAAgBEgADAAAAAQABAAABFQADAAAAAQAE
+AAABFgADAAAAAQD8AAABFwAEAAAAAQAAEAABHAADAAAAAQABAAABUgADAAAAAQABAAABUwADAAAABAAA
+ELIAAAAAAAgACAAIAAgAAQABAAEAAQ</bytes>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MCAwAA</bytes>
+ </object>
+ </object>
+ <nil key="NSToolbarItemTarget"/>
+ <string key="NSToolbarItemAction">runToolbarCustomizationPalette:</string>
+ <string key="NSToolbarItemMinSize">{0, 0}</string>
+ <string key="NSToolbarItemMaxSize">{0, 0}</string>
+ <bool key="NSToolbarItemEnabled">YES</bool>
+ <bool key="NSToolbarItemAutovalidates">YES</bool>
+ <int key="NSToolbarItemTag">-1</int>
+ <bool key="NSToolbarIsUserRemovable">YES</bool>
+ <int key="NSToolbarItemVisibilityPriority">0</int>
+ </object>
+ <object class="NSToolbarFlexibleSpaceItem" id="568640167">
+ <string key="NSToolbarItemIdentifier">NSToolbarFlexibleSpaceItem</string>
+ <string key="NSToolbarItemLabel"/>
+ <string key="NSToolbarItemPaletteLabel">Flexible Space</string>
+ <nil key="NSToolbarItemToolTip"/>
+ <nil key="NSToolbarItemView"/>
+ <nil key="NSToolbarItemImage"/>
+ <nil key="NSToolbarItemTarget"/>
+ <nil key="NSToolbarItemAction"/>
+ <string key="NSToolbarItemMinSize">{1, 5}</string>
+ <string key="NSToolbarItemMaxSize">{20000, 32}</string>
+ <bool key="NSToolbarItemEnabled">YES</bool>
+ <bool key="NSToolbarItemAutovalidates">YES</bool>
+ <int key="NSToolbarItemTag">-1</int>
+ <bool key="NSToolbarIsUserRemovable">YES</bool>
+ <int key="NSToolbarItemVisibilityPriority">0</int>
+ <object class="NSMenuItem" key="NSToolbarItemMenuFormRepresentation">
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <object class="NSCustomResource" key="NSOnImage" id="945310746">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSMenuCheckmark</string>
+ </object>
+ <object class="NSCustomResource" key="NSMixedImage" id="969998504">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSMenuMixedState</string>
+ </object>
+ </object>
+ </object>
+ <object class="NSToolbarSeparatorItem" id="1012010237">
+ <string key="NSToolbarItemIdentifier">NSToolbarSeparatorItem</string>
+ <string key="NSToolbarItemLabel"/>
+ <string key="NSToolbarItemPaletteLabel">Separator</string>
+ <nil key="NSToolbarItemToolTip"/>
+ <nil key="NSToolbarItemView"/>
+ <nil key="NSToolbarItemImage"/>
+ <nil key="NSToolbarItemTarget"/>
+ <nil key="NSToolbarItemAction"/>
+ <string key="NSToolbarItemMinSize">{12, 5}</string>
+ <string key="NSToolbarItemMaxSize">{12, 1000}</string>
+ <bool key="NSToolbarItemEnabled">YES</bool>
+ <bool key="NSToolbarItemAutovalidates">YES</bool>
+ <int key="NSToolbarItemTag">-1</int>
+ <bool key="NSToolbarIsUserRemovable">YES</bool>
+ <int key="NSToolbarItemVisibilityPriority">0</int>
+ <object class="NSMenuItem" key="NSToolbarItemMenuFormRepresentation">
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="945310746"/>
+ <reference key="NSMixedImage" ref="969998504"/>
+ </object>
+ </object>
+ <object class="NSToolbarSpaceItem" id="661775936">
+ <string key="NSToolbarItemIdentifier">NSToolbarSpaceItem</string>
+ <string key="NSToolbarItemLabel"/>
+ <string key="NSToolbarItemPaletteLabel">Space</string>
+ <nil key="NSToolbarItemToolTip"/>
+ <nil key="NSToolbarItemView"/>
+ <nil key="NSToolbarItemImage"/>
+ <nil key="NSToolbarItemTarget"/>
+ <nil key="NSToolbarItemAction"/>
+ <string key="NSToolbarItemMinSize">{32, 5}</string>
+ <string key="NSToolbarItemMaxSize">{32, 32}</string>
+ <bool key="NSToolbarItemEnabled">YES</bool>
+ <bool key="NSToolbarItemAutovalidates">YES</bool>
+ <int key="NSToolbarItemTag">-1</int>
+ <bool key="NSToolbarIsUserRemovable">YES</bool>
+ <int key="NSToolbarItemVisibilityPriority">0</int>
+ <object class="NSMenuItem" key="NSToolbarItemMenuFormRepresentation">
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="945310746"/>
+ <reference key="NSMixedImage" ref="969998504"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSArray" key="NSToolbarIBAllowedItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="685547192"/>
+ <reference ref="694471322"/>
+ <reference ref="16676378"/>
+ <reference ref="192029103"/>
+ <reference ref="1012010237"/>
+ <reference ref="661775936"/>
+ <reference ref="568640167"/>
+ <reference ref="276197344"/>
+ </object>
+ <object class="NSMutableArray" key="NSToolbarIBDefaultItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="685547192"/>
+ <reference ref="694471322"/>
+ <reference ref="16676378"/>
+ <reference ref="192029103"/>
+ </object>
+ <object class="NSMutableArray" key="NSToolbarIBSelectableItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <string key="NSWindowContentMaxSize">{3.40282e+38, 3.40282e+38}</string>
+ <string key="NSWindowContentMinSize">{273, 43}</string>
+ <object class="NSView" key="NSWindowView" id="1006">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">256</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomView" id="720246950">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">266</int>
+ <string key="NSFrame">{{0, 532}, {774, 22}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <string key="NSClassName">PSMTabBarControl</string>
+ </object>
+ <object class="NSTextField" id="795357547">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">290</int>
+ <string key="NSFrame">{{25, 3}, {732, 14}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="717772067">
+ <int key="NSCellFlags">68288064</int>
+ <int key="NSCellFlags2">272761856</int>
+ <string key="NSContents">Status bar</string>
+ <object class="NSFont" key="NSSupport">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">1.100000e+01</double>
+ <int key="NSfFlags">3100</int>
+ </object>
+ <reference key="NSControlView" ref="795357547"/>
+ <object class="NSColor" key="NSBackgroundColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MC42NjY2NjY2OQA</bytes>
+ </object>
+ </object>
+ <object class="NSColor" key="NSTextColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlTextColor</string>
+ <reference key="NSColor" ref="733901069"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSProgressIndicator" id="528651909">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">1316</int>
+ <object class="NSPSMatrix" key="NSDrawMatrix"/>
+ <string key="NSFrame">{{4, 2}, {16, 16}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <int key="NSpiFlags">28938</int>
+ <double key="NSMaxValue">1.000000e+02</double>
+ </object>
+ <object class="NSTabView" id="477345536">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">18</int>
+ <string key="NSFrame">{{0, 20}, {774, 512}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <object class="NSMutableArray" key="NSTabViewItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="NSFont" ref="770988704"/>
+ <int key="NSTvFlags">6</int>
+ <bool key="NSAllowTruncatedLabels">YES</bool>
+ <bool key="NSDrawsBackground">YES</bool>
+ </object>
+ </object>
+ <string key="NSFrameSize">{774, 554}</string>
+ <reference key="NSSuperview"/>
+ </object>
+ <string key="NSScreenRect">{{0, 0}, {1680, 1028}}</string>
+ <string key="NSMinSize">{273, 97}</string>
+ <string key="NSMaxSize">{3.40282e+38, 3.40282e+38}</string>
+ </object>
+ <object class="NSObjectController" id="177599630">
+ <object class="NSMutableArray" key="NSDeclaredKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>URL</string>
+ <string>url</string>
+ <string>status</string>
+ <string>processing</string>
+ <string>title</string>
+ <string>isProcessing</string>
+ <string>browserView.historyVisible</string>
+ </object>
+ <string key="NSObjectClassName">BrowserViewController</string>
+ <bool key="NSEditable">YES</bool>
+ <object class="_NSManagedProxy" key="_NSManagedProxy"/>
+ </object>
+ <object class="NSMenu" id="237938373">
+ <string key="NSTitle">Forward</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="NSMenu" id="353660550">
+ <string key="NSTitle">Back</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">window</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="1005"/>
+ </object>
+ <int key="connectionID">18</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="720246950"/>
+ <reference key="destination" ref="1001"/>
+ </object>
+ <int key="connectionID">19</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">tabBar</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="720246950"/>
+ </object>
+ <int key="connectionID">20</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">tabView</string>
+ <reference key="source" ref="720246950"/>
+ <reference key="destination" ref="477345536"/>
+ </object>
+ <int key="connectionID">42</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="477345536"/>
+ <reference key="destination" ref="720246950"/>
+ </object>
+ <int key="connectionID">43</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">tabView</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="477345536"/>
+ </object>
+ <int key="connectionID">56</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">navigate:</string>
+ <reference key="source" ref="1003"/>
+ <reference key="destination" ref="192029103"/>
+ </object>
+ <int key="connectionID">60</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">contentObject: activeBrowser</string>
+ <reference key="source" ref="177599630"/>
+ <reference key="destination" ref="1001"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="177599630"/>
+ <reference key="NSDestination" ref="1001"/>
+ <string key="NSLabel">contentObject: activeBrowser</string>
+ <string key="NSBinding">contentObject</string>
+ <string key="NSKeyPath">activeBrowser</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">62</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">value: selection.url</string>
+ <reference key="source" ref="77748234"/>
+ <reference key="destination" ref="177599630"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="77748234"/>
+ <reference key="NSDestination" ref="177599630"/>
+ <string key="NSLabel">value: selection.url</string>
+ <string key="NSBinding">value</string>
+ <string key="NSKeyPath">selection.url</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">64</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">value: selection.status</string>
+ <reference key="source" ref="795357547"/>
+ <reference key="destination" ref="177599630"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="795357547"/>
+ <reference key="NSDestination" ref="177599630"/>
+ <string key="NSLabel">value: selection.status</string>
+ <string key="NSBinding">value</string>
+ <string key="NSKeyPath">selection.status</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">65</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">title: selection.title</string>
+ <reference key="source" ref="1005"/>
+ <reference key="destination" ref="177599630"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="1005"/>
+ <reference key="NSDestination" ref="177599630"/>
+ <string key="NSLabel">title: selection.title</string>
+ <string key="NSBinding">title</string>
+ <string key="NSKeyPath">selection.title</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">67</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="1005"/>
+ <reference key="destination" ref="1001"/>
+ </object>
+ <int key="connectionID">68</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">animate: selection.isProcessing</string>
+ <reference key="source" ref="528651909"/>
+ <reference key="destination" ref="177599630"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="528651909"/>
+ <reference key="NSDestination" ref="177599630"/>
+ <string key="NSLabel">animate: selection.isProcessing</string>
+ <string key="NSBinding">animate</string>
+ <string key="NSKeyPath">selection.isProcessing</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">69</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">urlField</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="1053649244"/>
+ </object>
+ <int key="connectionID">70</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">backForwardSelected:</string>
+ <reference key="source" ref="1003"/>
+ <reference key="destination" ref="685547192"/>
+ </object>
+ <int key="connectionID">74</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">value: selection.browserView.historyVisible</string>
+ <reference key="source" ref="296571644"/>
+ <reference key="destination" ref="177599630"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="296571644"/>
+ <reference key="NSDestination" ref="177599630"/>
+ <string key="NSLabel">value: selection.browserView.historyVisible</string>
+ <string key="NSBinding">value</string>
+ <string key="NSKeyPath">selection.browserView.historyVisible</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">79</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">activeBrowserController</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="177599630"/>
+ </object>
+ <int key="connectionID">80</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">navigationControl</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="692457026"/>
+ </object>
+ <int key="connectionID">81</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">goHome:</string>
+ <reference key="source" ref="1003"/>
+ <reference key="destination" ref="518219892"/>
+ </object>
+ <int key="connectionID">85</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">partnerView</string>
+ <reference key="source" ref="720246950"/>
+ <reference key="destination" ref="477345536"/>
+ </object>
+ <int key="connectionID">86</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">historyButton</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="229385913"/>
+ </object>
+ <int key="connectionID">87</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">historyForwardMenu</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="237938373"/>
+ </object>
+ <int key="connectionID">96</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">historyBackMenu</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="353660550"/>
+ </object>
+ <int key="connectionID">97</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="237938373"/>
+ <reference key="destination" ref="1001"/>
+ </object>
+ <int key="connectionID">98</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="353660550"/>
+ <reference key="destination" ref="1001"/>
+ </object>
+ <int key="connectionID">99</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <object class="NSArray" key="object" id="209349352">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="children" ref="1000"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1001"/>
+ <reference key="parent" ref="209349352"/>
+ <string type="base64-UTF8" key="objectName">RmlsZSdzIE93bmVyA</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1003"/>
+ <reference key="parent" ref="209349352"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1004"/>
+ <reference key="parent" ref="209349352"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">1</int>
+ <reference key="object" ref="1005"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1006"/>
+ <reference ref="71746575"/>
+ </object>
+ <reference key="parent" ref="209349352"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">2</int>
+ <reference key="object" ref="1006"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="528651909"/>
+ <reference ref="795357547"/>
+ <reference ref="720246950"/>
+ <reference ref="477345536"/>
+ </object>
+ <reference key="parent" ref="1005"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">26</int>
+ <reference key="object" ref="795357547"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="717772067"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">27</int>
+ <reference key="object" ref="717772067"/>
+ <reference key="parent" ref="795357547"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">28</int>
+ <reference key="object" ref="528651909"/>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">3</int>
+ <reference key="object" ref="720246950"/>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">37</int>
+ <reference key="object" ref="477345536"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">44</int>
+ <reference key="object" ref="71746575"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="192029103"/>
+ <reference ref="1012010237"/>
+ <reference ref="276197344"/>
+ <reference ref="568640167"/>
+ <reference ref="661775936"/>
+ <reference ref="685547192"/>
+ <reference ref="16676378"/>
+ <reference ref="694471322"/>
+ </object>
+ <reference key="parent" ref="1005"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">49</int>
+ <reference key="object" ref="192029103"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="77748234"/>
+ </object>
+ <reference key="parent" ref="71746575"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">50</int>
+ <reference key="object" ref="1012010237"/>
+ <reference key="parent" ref="71746575"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">51</int>
+ <reference key="object" ref="276197344"/>
+ <reference key="parent" ref="71746575"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">52</int>
+ <reference key="object" ref="568640167"/>
+ <reference key="parent" ref="71746575"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">53</int>
+ <reference key="object" ref="661775936"/>
+ <reference key="parent" ref="71746575"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">54</int>
+ <reference key="object" ref="77748234"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1053649244"/>
+ </object>
+ <reference key="parent" ref="192029103"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">55</int>
+ <reference key="object" ref="1053649244"/>
+ <reference key="parent" ref="77748234"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">61</int>
+ <reference key="object" ref="177599630"/>
+ <reference key="parent" ref="209349352"/>
+ <string key="objectName">Active Browser</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">73</int>
+ <reference key="object" ref="685547192"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="692457026"/>
+ </object>
+ <reference key="parent" ref="71746575"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">71</int>
+ <reference key="object" ref="692457026"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="845979064"/>
+ </object>
+ <reference key="parent" ref="685547192"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">72</int>
+ <reference key="object" ref="845979064"/>
+ <reference key="parent" ref="692457026"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">77</int>
+ <reference key="object" ref="16676378"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="229385913"/>
+ </object>
+ <reference key="parent" ref="71746575"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">75</int>
+ <reference key="object" ref="229385913"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="296571644"/>
+ </object>
+ <reference key="parent" ref="16676378"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">76</int>
+ <reference key="object" ref="296571644"/>
+ <reference key="parent" ref="229385913"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">84</int>
+ <reference key="object" ref="694471322"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="518219892"/>
+ </object>
+ <reference key="parent" ref="71746575"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">82</int>
+ <reference key="object" ref="518219892"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="413663381"/>
+ </object>
+ <reference key="parent" ref="694471322"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">83</int>
+ <reference key="object" ref="413663381"/>
+ <reference key="parent" ref="518219892"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">88</int>
+ <reference key="object" ref="237938373"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="parent" ref="209349352"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">92</int>
+ <reference key="object" ref="353660550"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="parent" ref="209349352"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>1.IBEditorWindowLastContentRect</string>
+ <string>1.IBPluginDependency</string>
+ <string>1.IBWindowTemplateEditedContentRect</string>
+ <string>1.NSWindowTemplate.visibleAtLaunch</string>
+ <string>1.WindowOrigin</string>
+ <string>1.editorWindowContentRectSynchronizationRect</string>
+ <string>1.windowTemplate.hasMinSize</string>
+ <string>1.windowTemplate.minSize</string>
+ <string>2.IBPluginDependency</string>
+ <string>26.IBPluginDependency</string>
+ <string>26.IBViewBoundsToFrameTransform</string>
+ <string>27.IBPluginDependency</string>
+ <string>28.IBPluginDependency</string>
+ <string>28.IBViewBoundsToFrameTransform</string>
+ <string>3.IBPluginDependency</string>
+ <string>3.IBViewBoundsToFrameTransform</string>
+ <string>37.IBPluginDependency</string>
+ <string>37.IBViewBoundsToFrameTransform</string>
+ <string>44.IBEditorWindowLastContentRect</string>
+ <string>44.IBPluginDependency</string>
+ <string>50.IBPluginDependency</string>
+ <string>51.IBPluginDependency</string>
+ <string>52.IBPluginDependency</string>
+ <string>53.IBPluginDependency</string>
+ <string>54.IBPluginDependency</string>
+ <string>55.CustomClassName</string>
+ <string>55.IBPluginDependency</string>
+ <string>61.IBPluginDependency</string>
+ <string>71.IBPluginDependency</string>
+ <string>72.IBPluginDependency</string>
+ <string>72.IBSegmentedControlInspectorSelectedSegmentMetadataKey</string>
+ <string>75.IBAttributePlaceholdersKey</string>
+ <string>75.IBPluginDependency</string>
+ <string>76.IBPluginDependency</string>
+ <string>82.IBAttributePlaceholdersKey</string>
+ <string>82.IBPluginDependency</string>
+ <string>83.IBPluginDependency</string>
+ <string>88.IBPluginDependency</string>
+ <string>92.IBPluginDependency</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>{{103, 62}, {774, 554}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{103, 62}, {774, 554}}</string>
+ <reference ref="9"/>
+ <string>{196, 240}</string>
+ <string>{{202, 428}, {480, 270}}</string>
+ <boolean value="YES"/>
+ <string>{273, 43}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABDCAAAwaAAAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABAwAAAwWAAAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">AQAAAABEE8AAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABDiwAAxAVAAA</bytes>
+ </object>
+ <string>{{355, 640}, {616, 0}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>URLFieldCell</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="8"/>
+ <object class="NSMutableDictionary">
+ <string key="NS.key.0">ToolTip</string>
+ <object class="IBToolTipAttribute" key="NS.object.0">
+ <string key="name">ToolTip</string>
+ <reference key="object" ref="229385913"/>
+ <string key="toolTip">Show local history</string>
+ </object>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSMutableDictionary">
+ <string key="NS.key.0">ToolTip</string>
+ <object class="IBToolTipAttribute" key="NS.object.0">
+ <string key="name">ToolTip</string>
+ <reference key="object" ref="518219892"/>
+ <string key="toolTip">Go to your homepage</string>
+ </object>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">99</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">BrowserView</string>
+ <string key="superclassName">ScrollableView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">BrowserView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">BrowserViewController</string>
+ <string key="superclassName">NSViewController</string>
+ <object class="NSMutableDictionary" key="actions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>backForwardSelected:</string>
+ <string>goBack:</string>
+ <string>goForward:</string>
+ <string>goHome:</string>
+ <string>navigate:</string>
+ <string>reloadPage:</string>
+ <string>stopLoading:</string>
+ <string>zoomIn:</string>
+ <string>zoomOriginal:</string>
+ <string>zoomOut:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="outlets">
+ <string key="NS.key.0">browserView</string>
+ <string key="NS.object.0">BrowserView</string>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">BrowserViewController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">BrowserWindow</string>
+ <string key="superclassName">NSWindow</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">BrowserWindow.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">BrowserWindowController</string>
+ <string key="superclassName">NSWindowController</string>
+ <object class="NSMutableDictionary" key="actions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>closeCurrentTab:</string>
+ <string>newTab:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="outlets">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>activeBrowserController</string>
+ <string>historyBackMenu</string>
+ <string>historyButton</string>
+ <string>historyForwardMenu</string>
+ <string>navigationControl</string>
+ <string>tabBar</string>
+ <string>tabView</string>
+ <string>urlField</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>NSObjectController</string>
+ <string>NSMenu</string>
+ <string>NSButton</string>
+ <string>NSMenu</string>
+ <string>NSSegmentedControl</string>
+ <string>PSMTabBarControl</string>
+ <string>NSTabView</string>
+ <string>URLFieldCell</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">BrowserWindowController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="238543186">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabDragAssistant.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="472370996">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabBarCell.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="395663776">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabBarControl.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">PSMTabBarControl</string>
+ <reference key="sourceIdentifier" ref="472370996"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">PSMTabBarControl</string>
+ <string key="superclassName">NSControl</string>
+ <object class="NSMutableDictionary" key="outlets">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>delegate</string>
+ <string>partnerView</string>
+ <string>style</string>
+ <string>tabView</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>NSTabView</string>
+ </object>
+ </object>
+ <reference key="sourceIdentifier" ref="395663776"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">PSMTabBarControl</string>
+ <reference key="sourceIdentifier" ref="238543186"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">PSMTabBarControl</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabStyle.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">ScrollableView</string>
+ <string key="superclassName">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">ScrollableView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">URLFieldCell</string>
+ <string key="superclassName">NSTextFieldCell</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">URLFieldCell.h</string>
+ </object>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../NetSurf.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ </data>
+</archive>
diff --git a/frontends/cocoa/res/DownloadWindow.xib b/frontends/cocoa/res/DownloadWindow.xib
new file mode 100644
index 000000000..039ff1914
--- /dev/null
+++ b/frontends/cocoa/res/DownloadWindow.xib
@@ -0,0 +1,493 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
+ <data>
+ <int key="IBDocument.SystemTarget">1050</int>
+ <string key="IBDocument.SystemVersion">10J567</string>
+ <string key="IBDocument.InterfaceBuilderVersion">804</string>
+ <string key="IBDocument.AppKitVersion">1038.35</string>
+ <string key="IBDocument.HIToolboxVersion">462.00</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="NS.object.0">804</string>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="1"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys" id="0">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomObject" id="1001">
+ <string key="NSClassName">DownloadWindowController</string>
+ </object>
+ <object class="NSCustomObject" id="1003">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1004">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSWindowTemplate" id="1005">
+ <int key="NSWindowStyleMask">15</int>
+ <int key="NSWindowBacking">2</int>
+ <string key="NSWindowRect">{{196, 429}, {376, 90}}</string>
+ <int key="NSWTFlags">544735232</int>
+ <string key="NSWindowTitle">Download</string>
+ <string key="NSWindowClass">NSWindow</string>
+ <nil key="NSViewClass"/>
+ <string key="NSWindowContentMaxSize">{1000, 90}</string>
+ <string key="NSWindowContentMinSize">{330, 90}</string>
+ <object class="NSView" key="NSWindowView" id="1006">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">256</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSProgressIndicator" id="663127685">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">1290</int>
+ <object class="NSPSMatrix" key="NSDrawMatrix"/>
+ <string key="NSFrame">{{79, 33}, {279, 20}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <int key="NSpiFlags">16392</int>
+ <double key="NSMaxValue">100</double>
+ </object>
+ <object class="NSImageView" id="454520484">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">268</int>
+ <object class="NSMutableSet" key="NSDragTypes">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="set.sortedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>Apple PDF pasteboard type</string>
+ <string>Apple PICT pasteboard type</string>
+ <string>Apple PNG pasteboard type</string>
+ <string>NSFilenamesPboardType</string>
+ <string>NeXT Encapsulated PostScript v1.2 pasteboard type</string>
+ <string>NeXT TIFF v4.0 pasteboard type</string>
+ </object>
+ </object>
+ <string key="NSFrame">{{17, 17}, {56, 56}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSImageCell" key="NSCell" id="596224379">
+ <int key="NSCellFlags">130560</int>
+ <int key="NSCellFlags2">33554432</int>
+ <int key="NSAlign">0</int>
+ <int key="NSScale">3</int>
+ <int key="NSStyle">0</int>
+ <bool key="NSAnimates">NO</bool>
+ </object>
+ <bool key="NSEditable">YES</bool>
+ </object>
+ <object class="NSTextField" id="355449439">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">266</int>
+ <string key="NSFrame">{{78, 56}, {261, 17}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="578533771">
+ <int key="NSCellFlags">68288064</int>
+ <int key="NSCellFlags2">272630784</int>
+ <string key="NSContents">Label</string>
+ <object class="NSFont" key="NSSupport">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">13</double>
+ <int key="NSfFlags">1044</int>
+ </object>
+ <reference key="NSControlView" ref="355449439"/>
+ <object class="NSColor" key="NSBackgroundColor" id="388192080">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MC42NjY2NjY2NjY3AA</bytes>
+ </object>
+ </object>
+ <object class="NSColor" key="NSTextColor" id="1042936865">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlTextColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MAA</bytes>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSTextField" id="1027859209">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">266</int>
+ <string key="NSFrame">{{78, 17}, {281, 14}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="882473472">
+ <int key="NSCellFlags">68288064</int>
+ <int key="NSCellFlags2">272761856</int>
+ <string key="NSContents">Label</string>
+ <object class="NSFont" key="NSSupport">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">11</double>
+ <int key="NSfFlags">3100</int>
+ </object>
+ <reference key="NSControlView" ref="1027859209"/>
+ <reference key="NSBackgroundColor" ref="388192080"/>
+ <reference key="NSTextColor" ref="1042936865"/>
+ </object>
+ </object>
+ </object>
+ <string key="NSFrameSize">{376, 90}</string>
+ <reference key="NSSuperview"/>
+ </object>
+ <string key="NSScreenRect">{{0, 0}, {1680, 1028}}</string>
+ <string key="NSMinSize">{330, 112}</string>
+ <string key="NSMaxSize">{1000, 112}</string>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">window</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="1005"/>
+ </object>
+ <int key="connectionID">3</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">maxValue: totalSize</string>
+ <reference key="source" ref="663127685"/>
+ <reference key="destination" ref="1001"/>
+ <object class="NSNibBindingConnector" key="connector" id="721881472">
+ <reference key="NSSource" ref="663127685"/>
+ <reference key="NSDestination" ref="1001"/>
+ <string key="NSLabel">maxValue: totalSize</string>
+ <string key="NSBinding">maxValue</string>
+ <string key="NSKeyPath">totalSize</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">6</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">value: receivedSize</string>
+ <reference key="source" ref="663127685"/>
+ <reference key="destination" ref="1001"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="663127685"/>
+ <reference key="NSDestination" ref="1001"/>
+ <string key="NSLabel">value: receivedSize</string>
+ <string key="NSBinding">value</string>
+ <string key="NSKeyPath">receivedSize</string>
+ <reference key="NSPreviousConnector" ref="721881472"/>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">7</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">value: fileName</string>
+ <reference key="source" ref="355449439"/>
+ <reference key="destination" ref="1001"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="355449439"/>
+ <reference key="NSDestination" ref="1001"/>
+ <string key="NSLabel">value: fileName</string>
+ <string key="NSBinding">value</string>
+ <string key="NSKeyPath">fileName</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">22</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">value: icon</string>
+ <reference key="source" ref="454520484"/>
+ <reference key="destination" ref="1001"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="454520484"/>
+ <reference key="NSDestination" ref="1001"/>
+ <string key="NSLabel">value: icon</string>
+ <string key="NSBinding">value</string>
+ <string key="NSKeyPath">icon</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">23</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">value: statusText</string>
+ <reference key="source" ref="1027859209"/>
+ <reference key="destination" ref="1001"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="1027859209"/>
+ <reference key="NSDestination" ref="1001"/>
+ <string key="NSLabel">value: statusText</string>
+ <string key="NSBinding">value</string>
+ <string key="NSKeyPath">statusText</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">24</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="1005"/>
+ <reference key="destination" ref="1001"/>
+ </object>
+ <int key="connectionID">25</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <reference key="object" ref="0"/>
+ <reference key="children" ref="1000"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1001"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">File's Owner</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1003"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1004"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">1</int>
+ <reference key="object" ref="1005"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1006"/>
+ </object>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">2</int>
+ <reference key="object" ref="1006"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="454520484"/>
+ <reference ref="355449439"/>
+ <reference ref="1027859209"/>
+ <reference ref="663127685"/>
+ </object>
+ <reference key="parent" ref="1005"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">4</int>
+ <reference key="object" ref="663127685"/>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">14</int>
+ <reference key="object" ref="454520484"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="596224379"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">15</int>
+ <reference key="object" ref="596224379"/>
+ <reference key="parent" ref="454520484"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">16</int>
+ <reference key="object" ref="355449439"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="578533771"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">17</int>
+ <reference key="object" ref="578533771"/>
+ <reference key="parent" ref="355449439"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">18</int>
+ <reference key="object" ref="1027859209"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="882473472"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">19</int>
+ <reference key="object" ref="882473472"/>
+ <reference key="parent" ref="1027859209"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>1.IBEditorWindowLastContentRect</string>
+ <string>1.IBPluginDependency</string>
+ <string>1.IBWindowTemplateEditedContentRect</string>
+ <string>1.NSWindowTemplate.visibleAtLaunch</string>
+ <string>1.WindowOrigin</string>
+ <string>1.editorWindowContentRectSynchronizationRect</string>
+ <string>1.windowTemplate.hasMaxSize</string>
+ <string>1.windowTemplate.hasMinSize</string>
+ <string>1.windowTemplate.maxSize</string>
+ <string>1.windowTemplate.minSize</string>
+ <string>14.IBPluginDependency</string>
+ <string>14.IBViewBoundsToFrameTransform</string>
+ <string>15.IBPluginDependency</string>
+ <string>16.IBPluginDependency</string>
+ <string>16.IBViewBoundsToFrameTransform</string>
+ <string>17.IBPluginDependency</string>
+ <string>18.IBPluginDependency</string>
+ <string>18.IBViewBoundsToFrameTransform</string>
+ <string>19.IBPluginDependency</string>
+ <string>2.IBPluginDependency</string>
+ <string>4.IBPluginDependency</string>
+ <string>4.IBViewBoundsToFrameTransform</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>{{305, 231}, {376, 90}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{305, 231}, {376, 90}}</string>
+ <integer value="1"/>
+ <string>{196, 240}</string>
+ <string>{{202, 428}, {480, 270}}</string>
+ <boolean value="YES"/>
+ <boolean value="YES"/>
+ <string>{1000, 90}</string>
+ <string>{330, 90}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">AUGIAABBiAAAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABCzAAAwo4AAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABCnAAAwgAAAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABCngAAwkAAAA</bytes>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">25</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">DownloadWindowController</string>
+ <string key="superclassName">NSWindowController</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">DownloadWindowController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabDragAssistant.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabBarCell.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabBarControl.h</string>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Print.framework/Headers/PDEPluginInterface.h</string>
+ </object>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
+ <integer value="1050" key="NS.object.0"/>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
+ <integer value="3000" key="NS.object.0"/>
+ </object>
+ <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../NetSurf.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ </data>
+</archive>
diff --git a/frontends/cocoa/res/HistoryWindow.xib b/frontends/cocoa/res/HistoryWindow.xib
new file mode 100644
index 000000000..a5ec90e1a
--- /dev/null
+++ b/frontends/cocoa/res/HistoryWindow.xib
@@ -0,0 +1,338 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
+ <data>
+ <int key="IBDocument.SystemTarget">1060</int>
+ <string key="IBDocument.SystemVersion">10J567</string>
+ <string key="IBDocument.InterfaceBuilderVersion">804</string>
+ <string key="IBDocument.AppKitVersion">1038.35</string>
+ <string key="IBDocument.HIToolboxVersion">462.00</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="NS.object.0">804</string>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="1"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys" id="0">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomObject" id="1001">
+ <string key="NSClassName">HistoryWindowController</string>
+ </object>
+ <object class="NSCustomObject" id="1003">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1004">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSWindowTemplate" id="1005">
+ <int key="NSWindowStyleMask">15</int>
+ <int key="NSWindowBacking">2</int>
+ <string key="NSWindowRect">{{196, 112}, {327, 398}}</string>
+ <int key="NSWTFlags">1618477056</int>
+ <string key="NSWindowTitle">History</string>
+ <string key="NSWindowClass">NSWindow</string>
+ <nil key="NSViewClass"/>
+ <string key="NSWindowContentMaxSize">{1.79769e+308, 1.79769e+308}</string>
+ <object class="NSView" key="NSWindowView" id="1006">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">256</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSScrollView" id="329245506">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">274</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSClipView" id="429990844">
+ <reference key="NSNextResponder" ref="329245506"/>
+ <int key="NSvFlags">2304</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomView" id="920629225">
+ <reference key="NSNextResponder" ref="429990844"/>
+ <int key="NSvFlags">274</int>
+ <string key="NSFrameSize">{312, 383}</string>
+ <reference key="NSSuperview" ref="429990844"/>
+ <string key="NSClassName">TreeView</string>
+ </object>
+ </object>
+ <string key="NSFrame">{{1, 1}, {312, 383}}</string>
+ <reference key="NSSuperview" ref="329245506"/>
+ <reference key="NSNextKeyView" ref="920629225"/>
+ <reference key="NSDocView" ref="920629225"/>
+ <object class="NSColor" key="NSBGColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MC42NjY2NjY2NjY3AA</bytes>
+ </object>
+ </object>
+ <int key="NScvFlags">4</int>
+ </object>
+ <object class="NSScroller" id="909448709">
+ <reference key="NSNextResponder" ref="329245506"/>
+ <int key="NSvFlags">256</int>
+ <string key="NSFrame">{{313, 1}, {15, 383}}</string>
+ <reference key="NSSuperview" ref="329245506"/>
+ <reference key="NSTarget" ref="329245506"/>
+ <string key="NSAction">_doScroller:</string>
+ <double key="NSCurValue">1</double>
+ <double key="NSPercent">0.96363627910614014</double>
+ </object>
+ <object class="NSScroller" id="886582390">
+ <reference key="NSNextResponder" ref="329245506"/>
+ <int key="NSvFlags">256</int>
+ <string key="NSFrame">{{1, 384}, {312, 15}}</string>
+ <reference key="NSSuperview" ref="329245506"/>
+ <int key="NSsFlags">1</int>
+ <reference key="NSTarget" ref="329245506"/>
+ <string key="NSAction">_doScroller:</string>
+ <double key="NSPercent">0.50602412223815918</double>
+ </object>
+ </object>
+ <string key="NSFrame">{{-1, -1}, {329, 400}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <reference key="NSNextKeyView" ref="429990844"/>
+ <int key="NSsFlags">50</int>
+ <reference key="NSVScroller" ref="909448709"/>
+ <reference key="NSHScroller" ref="886582390"/>
+ <reference key="NSContentView" ref="429990844"/>
+ </object>
+ </object>
+ <string key="NSFrameSize">{327, 398}</string>
+ <reference key="NSSuperview"/>
+ </object>
+ <string key="NSScreenRect">{{0, 0}, {1680, 1028}}</string>
+ <string key="NSMaxSize">{1.79769e+308, 1.79769e+308}</string>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">window</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="1005"/>
+ </object>
+ <int key="connectionID">3</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">view</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="920629225"/>
+ </object>
+ <int key="connectionID">8</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <reference key="object" ref="0"/>
+ <reference key="children" ref="1000"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1001"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">File's Owner</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1003"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1004"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">1</int>
+ <reference key="object" ref="1005"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1006"/>
+ </object>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">2</int>
+ <reference key="object" ref="1006"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="329245506"/>
+ </object>
+ <reference key="parent" ref="1005"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">4</int>
+ <reference key="object" ref="329245506"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="909448709"/>
+ <reference ref="886582390"/>
+ <reference ref="920629225"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5</int>
+ <reference key="object" ref="909448709"/>
+ <reference key="parent" ref="329245506"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">6</int>
+ <reference key="object" ref="886582390"/>
+ <reference key="parent" ref="329245506"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">7</int>
+ <reference key="object" ref="920629225"/>
+ <reference key="parent" ref="329245506"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>1.IBEditorWindowLastContentRect</string>
+ <string>1.IBPluginDependency</string>
+ <string>1.IBWindowTemplateEditedContentRect</string>
+ <string>1.NSWindowTemplate.visibleAtLaunch</string>
+ <string>1.WindowOrigin</string>
+ <string>1.editorWindowContentRectSynchronizationRect</string>
+ <string>2.IBPluginDependency</string>
+ <string>4.IBPluginDependency</string>
+ <string>5.IBPluginDependency</string>
+ <string>6.IBPluginDependency</string>
+ <string>7.IBPluginDependency</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>{{361, 416}, {327, 398}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{361, 416}, {327, 398}}</string>
+ <boolean value="NO"/>
+ <string>{196, 240}</string>
+ <string>{{202, 428}, {480, 270}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">8</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">HistoryWindowController</string>
+ <string key="superclassName">NSWindowController</string>
+ <object class="NSMutableDictionary" key="outlets">
+ <string key="NS.key.0">view</string>
+ <string key="NS.object.0">TreeView</string>
+ </object>
+ <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <string key="NS.key.0">view</string>
+ <object class="IBToOneOutletInfo" key="NS.object.0">
+ <string key="name">view</string>
+ <string key="candidateClassName">TreeView</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">HistoryWindowController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabDragAssistant.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabBarCell.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabBarControl.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">ScrollableView</string>
+ <string key="superclassName">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">ScrollableView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">TreeView</string>
+ <string key="superclassName">ScrollableView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">TreeView.h</string>
+ </object>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
+ <integer value="3000" key="NS.object.0"/>
+ </object>
+ <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../NetSurf.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ </data>
+</archive>
diff --git a/frontends/cocoa/res/HomeTemplate.pdf b/frontends/cocoa/res/HomeTemplate.pdf
new file mode 100644
index 000000000..42b88e9eb
--- /dev/null
+++ b/frontends/cocoa/res/HomeTemplate.pdf
@@ -0,0 +1,106 @@
+%PDF-1.5 %âãÏÓ
+1 0 obj <</Metadata 9 0 R/Pages 2 0 R/Type/Catalog>> endobj 9 0 obj <</Subtype/XML/Length 16417/Type/Metadata>>stream
+<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
+<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.1-c036 46.277092, Fri Feb 23 2007 14:16:18 ">
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <rdf:Description rdf:about=""
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <dc:format>application/pdf</dc:format>
+ <dc:title>
+ <rdf:Alt>
+ <rdf:li xml:lang="x-default">HomeTemplate</rdf:li>
+ </rdf:Alt>
+ </dc:title>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:xap="http://ns.adobe.com/xap/1.0/"
+ xmlns:xapGImg="http://ns.adobe.com/xap/1.0/g/img/">
+ <xap:CreatorTool>Adobe Illustrator CS3</xap:CreatorTool>
+ <xap:CreateDate>2011-02-08T14:59:01+01:00</xap:CreateDate>
+ <xap:ModifyDate>2011-02-08T14:59:01+01:00</xap:ModifyDate>
+ <xap:MetadataDate>2011-02-08T14:59:01+01:00</xap:MetadataDate>
+ <xap:Thumbnails>
+ <rdf:Alt>
+ <rdf:li rdf:parseType="Resource">
+ <xapGImg:width>256</xapGImg:width>
+ <xapGImg:height>256</xapGImg:height>
+ <xapGImg:format>JPEG</xapGImg:format>
+ <xapGImg:image>/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA&#xA;AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK&#xA;DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f&#xA;Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAEAAwER&#xA;AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA&#xA;AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB&#xA;UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE&#xA;1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ&#xA;qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy&#xA;obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp&#xA;0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo&#xA;+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7&#xA;FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F&#xA;XYq7FXYq7FXYq7FXYq7FXYqwz80/zT8uflz5cfVtWf1bqXkmm6ajAS3MoH2V68UWo5vSijxJAKrv&#xA;ys/NPy5+Y3lxNW0l/SuouKalprsDLbSkfZbpyRqHg9KMPAggKszxV2KuxV2KuxV2KuxV2KuxV2Ku&#xA;xV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVhn5p/mn5c/Lny4+ras/q3UvJNN01G&#xA;AluZQPsr14otRzelFHiSAVXwD5+8/eY/PPmOfXten9W4l+GGFaiKCIElYolJPFVr8ydzUnFXeQfP&#xA;3mPyN5jg17QZ/SuIvhmhapiniJBaKVQRyVqfMHcUIxV9/flZ+aflz8xvLiatpL+ldRcU1LTXYGW2&#xA;lI+y3TkjUPB6UYeBBAVZnirsVflXirsVdirsVfqpirsVdirsVdirsVdirsVdirsVdirsVdirsVdi&#xA;rsVdirsVdirsVdirDPzT/NPy5+XPlx9W1Z/VupeSabpqMBLcygfZXrxRajm9KKPEkAqvgHz95+8x&#xA;+efMc+va9P6txL8MMK1EUEQJKxRKSeKrX5k7mpOKscxV2Ksj8g+fvMfkbzHBr2gz+lcRfDNC1TFP&#xA;ESC0UqgjkrU+YO4oRir7+/Kz80/Ln5jeXE1bSX9K6i4pqWmuwMttKR9lunJGoeD0ow8CCAqzPFX5&#xA;V4q7FXYq7FX6qYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FWGfmn+aflz8ufL&#xA;j6tqz+rdS8k03TUYCW5lA+yvXii1HN6UUeJIBVfAPn7z95j88+Y59e16f1biX4YYVqIoIgSViiUk&#xA;8VWvzJ3NScVY5irsVdirsVZH5B8/eY/I3mODXtBn9K4i+GaFqmKeIkFopVBHJWp8wdxQjFX39+Vn&#xA;5p+XPzG8uJq2kv6V1FxTUtNdgZbaUj7LdOSNQ8HpRh4EEBV4H/zkj/zjd6H1rzr5Ktf3PxTazo0K&#xA;/Y7vcW6D9nu6Dp1G1QFXy1irsVdir9VMVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdir&#xA;sVYZ+af5p+XPy58uPq2rP6t1LyTTdNRgJbmUD7K9eKLUc3pRR4kgFV8A+fvP3mPzz5jn17Xp/VuJ&#xA;fhhhWoigiBJWKJSTxVa/Mnc1JxVjmKuxV2KuxV2KuxVkfkHz95j8jeY4Ne0Gf0riL4ZoWqYp4iQW&#xA;ilUEclanzB3FCMVff35Wfmn5c/Mby4mraS/pXUXFNS012BltpSPst05I1DwelGHgQQFXgf8Azkj/&#xA;AM43eh9a86+SrX9z8U2s6NCv2O73Fug/Z7ug6dRtUBV8tYq7FX6qYq7FXYq7FXYq7FXYq7FXYq7F&#xA;XYq7FXYq7FXYq7FXYq7FWGfmn+aflz8ufLj6tqz+rdS8k03TUYCW5lA+yvXii1HN6UUeJIBVfAPn&#xA;7z95j88+Y59e16f1biX4YYVqIoIgSViiUk8VWvzJ3NScVY5irsVdirYBJoNycVV7/T7/AE68lstQ&#xA;tpbS8hIE1tOjRyISKjkjAEbGuKofFXYq7FWR+QfP3mPyN5jg17QZ/SuIvhmhapiniJBaKVQRyVqf&#xA;MHcUIxV9/flZ+aflz8xvLiatpL+ldRcU1LTXYGW2lI+y3TkjUPB6UYeBBAVeB/8AOSP/ADjd6H1r&#xA;zr5Ktf3PxTazo0K/Y7vcW6D9nu6Dp1G1QFXy1ir9VMVdirsVdirsVdirsVdirsVdirsVdirsVdir&#xA;sVdirsVYZ+af5p+XPy58uPq2rP6t1LyTTdNRgJbmUD7K9eKLUc3pRR4kgFV8A+fvP3mPzz5jn17X&#xA;p/VuJfhhhWoigiBJWKJSTxVa/Mnc1JxVjmKuxV2KtgEmg3JxV9c/843f843DTRa+dPOlrXUTxm0f&#xA;R5l/3n7rPOp/3b3RD9jqfi+yqz/8+fyG0v8AMbSzf2Ajs/NtnHSzvDsk6DcQTkdv5W6qfaoxV8Ja&#xA;xo+qaNqlzpWq20lnqNnIYrm2lFHRx2P6wRsRuMVQeKuxV2Ksj8g+fvMfkbzHBr2gz+lcRfDNC1TF&#xA;PESC0UqgjkrU+YO4oRir7+/Kz80/Ln5jeXE1bSX9K6i4pqWmuwMttKR9lunJGoeD0ow8CCAq8D/5&#xA;yR/5xu9D61518lWv7n4ptZ0aFfsd3uLdB+z3dB06jaoCr6vxV2KuxV2KuxV2KuxV2KuxV2KuxV2K&#xA;uxV2KuxV2KsM/NP80/Ln5c+XH1bVn9W6l5JpumowEtzKB9levFFqOb0oo8SQCq+AfP3n7zH558xz&#xA;69r0/q3EvwwwrURQRAkrFEpJ4qtfmTuak4qxzFXYq7FWwCTQbk4q+uf+cbv+cbhpotfOnnS1rqJ4&#xA;zaPo8y/7z91nnU/7t7oh+x1PxfZVfTWKuxV5J+fP5DaX+Y2lm/sBHZ+bbOOlneHZJ0G4gnI7fyt1&#xA;U+1Rir4S1jR9U0bVLnStVtpLPUbOQxXNtKKOjjsf1gjYjcYqg8VdirsVZH5B8/eY/I3mODXtBn9K&#xA;4i+GaFqmKeIkFopVBHJWp8wdxQjFX39+Vn5p+XPzG8uJq2kv6V1FxTUtNdgZbaUj7LdOSNQ8HpRh&#xA;4EEBVmYAAoNgOgxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVhn5p/mn5c/Lny4+ras/q3UvJN&#xA;N01GAluZQPsr14otRzelFHiSAVXwD5+8/eY/PPmOfXten9W4l+GGFaiKCIElYolJPFVr8ydzUnFW&#xA;OYq7FXYq2ASaDcnFX1z/AM43f843DTRa+dPOlrXUTxm0fR5l/wB5+6zzqf8AdvdEP2Op+L7Kr6ax&#xA;V2KuxV2KvJPz5/IbS/zG0s39gI7PzbZx0s7w7JOg3EE5Hb+Vuqn2qMVfCWsaPqmjapc6VqttJZ6j&#xA;ZyGK5tpRR0cdj+sEbEbjFUHirsVdirI/IPn7zH5G8xwa9oM/pXEXwzQtUxTxEgtFKoI5K1PmDuKE&#xA;Yq+/vys/NPy5+Y3lxNW0l/SuouKalprsDLbSkfZbpyRqHg9KMPAggKszxV2KuxV2KuxV2KuxV2Ku&#xA;xV2KuxV2KuxVhn5p/mn5c/Lny4+ras/q3UvJNN01GAluZQPsr14otRzelFHiSAVXwD5+8/eY/PPm&#xA;OfXten9W4l+GGFaiKCIElYolJPFVr8ydzUnFWOYq7FXYq2ASaDcnFX1z/wA43f8AONw00WvnTzpa&#xA;11E8ZtH0eZf95+6zzqf9290Q/Y6n4vsqvprFXYq7FXYq7FXYq8k/Pn8htL/MbSzf2Ajs/NtnHSzv&#xA;Dsk6DcQTkdv5W6qfaoxV8Jaxo+qaNqlzpWq20lnqNnIYrm2lFHRx2P6wRsRuMVQeKuxV2Ksj8g+f&#xA;vMfkbzHBr2gz+lcRfDNC1TFPESC0UqgjkrU+YO4oRir7+/Kz80/Ln5jeXE1bSX9K6i4pqWmuwMtt&#xA;KR9lunJGoeD0ow8CCAqzPFXYq7FXYq7FXYq7FXYq7FXYq7FWGfmn+aflz8ufLj6tqz+rdS8k03TU&#xA;YCW5lA+yvXii1HN6UUeJIBVfAPn7z95j88+Y59e16f1biX4YYVqIoIgSViiUk8VWvzJ3NScVY5ir&#xA;sVdirYBJoNycVfXP/ON3/ONw00WvnTzpa11E8ZtH0eZf95+6zzqf9290Q/Y6n4vsqvprFXYq7FXY&#xA;q7FXYq7FXYq8k/Pn8htL/MbSzf2Ajs/NtnHSzvDsk6DcQTkdv5W6qfaoxV8Jaxo+qaNqlzpWq20l&#xA;nqNnIYrm2lFHRx2P6wRsRuMVQeKuxV2Ksj8g+fvMfkbzHBr2gz+lcRfDNC1TFPESC0UqgjkrU+YO&#xA;4oRir7+/Kz80/Ln5jeXE1bSX9K6i4pqWmuwMttKR9lunJGoeD0ow8CCAqzPFXYq7FXYq7FXYq7FX&#xA;Yq7FWGfmn+aflz8ufLj6tqz+rdS8k03TUYCW5lA+yvXii1HN6UUeJIBVfAPn7z95j88+Y59e16f1&#xA;biX4YYVqIoIgSViiUk8VWvzJ3NScVY5irsVdirYBJoNycVfXP/ON3/ONw00WvnTzpa11E8ZtH0eZ&#xA;f95+6zzqf9290Q/Y6n4vsqvprFXYq7FXYq7FXYq7FXYq7FXYq8k/Pn8htL/MbSzf2Ajs/NtnHSzv&#xA;Dsk6DcQTkdv5W6qfaoxV8Jaxo+qaNqlzpWq20lnqNnIYrm2lFHRx2P6wRsRuMVQeKuxV2Ksj8g+f&#xA;vMfkbzHBr2gz+lcRfDNC1TFPESC0UqgjkrU+YO4oRir7+/Kz80/Ln5jeXE1bSX9K6i4pqWmuwMtt&#xA;KR9lunJGoeD0ow8CCAqzPFXYq7FXYq7FXYq7FWGfmn+aflz8ufLj6tqz+rdS8k03TUYCW5lA+yvX&#xA;ii1HN6UUeJIBVfAPn7z95j88+Y59e16f1biX4YYVqIoIgSViiUk8VWvzJ3NScVY5irsVdirYBJoN&#xA;ycVfXP8Azjd/zjcNNFr5086WtdRPGbR9HmX/AHn7rPOp/wB290Q/Y6n4vsqvprFXYq7FXYq7FXYq&#xA;7FXYq7FXYq7FXYq8k/Pn8htL/MbSzf2Ajs/NtnHSzvDsk6DcQTkdv5W6qfaoxV8Jaxo+qaNqlzpW&#xA;q20lnqNnIYrm2lFHRx2P6wRsRuMVQeKuxV2Ksj8g+fvMfkbzHBr2gz+lcRfDNC1TFPESC0Uqgjkr&#xA;U+YO4oRir7+/Kz80/Ln5jeXE1bSX9K6i4pqWmuwMttKR9lunJGoeD0ow8CCAqzPFXYq7FXYq7FWG&#xA;fmn+aflz8ufLj6tqz+rdS8k03TUYCW5lA+yvXii1HN6UUeJIBVfAPn7z95j88+Y59e16f1biX4YY&#xA;VqIoIgSViiUk8VWvzJ3NScVY5irsVdirYBJoNycVfXP/ADjd/wA43DTRa+dPOlrXUTxm0fR5l/3n&#xA;7rPOp/3b3RD9jqfi+yq+lzPAJ1gMiid1Z0iJHMohAZgvWgLqCfcYqvxV2KuxV2KuxV2KuxV2KuxV&#xA;2KuxV2KpNF5v0GTzXceVPrITXbe1jvjaPsXt5GZOcZ/a4stG8NvHFXnv58/kNpf5jaWb+wEdn5ts&#xA;46Wd4dknQbiCcjt/K3VT7VGKvhLWNH1TRtUudK1W2ks9Rs5DFc20oo6OOx/WCNiNxiqDxV2KuxVk&#xA;fkHz95j8jeY4Ne0Gf0riL4ZoWqYp4iQWilUEclanzB3FCMVff35Wfmn5c/Mby4mraS/pXUXFNS01&#xA;2BltpSPst05I1DwelGHgQQFWZ4q7FXYqwz80/wA0/Ln5c+XH1bVn9W6l5JpumowEtzKB9levFFqO&#xA;b0oo8SQCq+AfP3n7zH558xz69r0/q3EvwwwrURQRAkrFEpJ4qtfmTuak4qxzFXYq7FWwCTQbk4q+&#xA;uf8AnG7/AJxuGmi186edLWuonjNo+jzL/vP3WedT/u3uiH7HU/F9lV7x5/8APnl/yN5ZufMGtzcL&#xA;aH4YYV3knmYEpDEO7NT6BUnYHFXzz/zjl+ZXmPz9+e2vazrEpCSaJOLSxViYbeJLy24Rxg+AY1P7&#xA;RqcVfVGKuxV2KuxV2KuxV2KuxV2KuxV2KuxV8X/85V69q+gfnpp+saRdPZ6jZabay29xGaFWEk23&#xA;uCNmB2I2OKvof8kfzo0j8yvL/qfu7TzFZKBqumA9D0E0VdzE5/4E/CexKqW/nz+Q2l/mNpZv7AR2&#xA;fm2zjpZ3h2SdBuIJyO38rdVPtUYq+EtY0fVNG1S50rVbaSz1GzkMVzbSijo47H9YI2I3GKoPFXYq&#xA;7FWR+QfP3mPyN5jg17QZ/SuIvhmhapiniJBaKVQRyVqfMHcUIxV9/flZ+aflz8xvLiatpL+ldRcU&#xA;1LTXYGW2lI+y3TkjUPB6UYeBBAVZnirsVeT/AJ+fkZY/mTpCXdm62vmnToyun3LEiOVKlvq83+SW&#xA;JKt+yfaoxV8H6xo+qaNqlzpWq20lnqNnIYrm2lFHRx2P6wRsRuMVQeKuxVsAk0G5OKvrn/nG7/nG&#xA;4aaLXzp50ta6ieM2j6PMv+8/dZ51P+7e6IfsdT8X2VX0N5n8zaL5Y0K813WrlbXTbJDJNKdz4Kqr&#xA;1ZmOyqOpxV+fv5x/m5rX5k+Zm1C65W+k2vKPSdNr8MMRP2mANDK+3Nvo6AYq9C/5wq/8mnqv/bDu&#xA;P+oy0xV9qYq7FXwB/wA5R/8Ak9vM3/Rj/wB0+3xV5VirsVdir7//AOcXP/JE+Wf+j7/uoXGKvVcV&#xA;dirsVdirsVfEH/OZP/k3If8AtlW3/J2bFXkvk7zjr/k/zDa69oVyba/tWr4pIh+3FKv7SONiP44q&#xA;/QT8pvzU0H8x/LEeracRBexUj1TTGYNJbTeB6ckelUem48CCAqxn8+fyG0v8xtLN/YCOz822cdLO&#xA;8OyToNxBOR2/lbqp9qjFXwlrGj6po2qXOlarbSWeo2chiubaUUdHHY/rBGxG4xVB4q7FU+8keSPM&#xA;XnTzFbaDoNsZ72c1dzURwxgjlLK1DxRa7n6BUkDFX37+Uv5S+Xfy38urp2nKJ9RnCvqmqOoElxIB&#xA;9PGNangldvckkqs5xV2KuxV5J+fP5DaX+Y2lm/sBHZ+bbOOlneHZJ0G4gnI7fyt1U+1Rir4S1jR9&#xA;U0bVLnStVtpLPUbOQxXNtKKOjjsf1gjYjcYqhACTQbk4q+uf+cbv+cbhpotfOnnS1rqJ4zaPo8y/&#xA;7z91nnU/7t7oh+x1PxfZVfSOp6np+l6fcajqNxHaWNpG0tzcysFREUVLMTir4K/Pv877/wDMjXRB&#xA;aF7byrp7n9G2bbNI1KG4mA/bb9kfsrt1LEqvKcVfQH/OFX/k09V/7Ydx/wBRlpir7UxV2KvgD/nK&#xA;P/ye3mb/AKMf+6fb4q8qxV2KuxV9/wD/ADi5/wCSJ8s/9H3/AHULjFXquKuxV2KuxV2KviD/AJzJ&#xA;/wDJuQ/9sq2/5OzYq8KxVlP5b/mL5g8g+Z7fXdGkNUIS8tGNIrmAkF4pOvWmxpVTuMVfoN+Xv5ge&#xA;X/Pflm21/RJeUMo43FsxHq28wHxwygdGX8RQjY4qwn8+fyG0v8xtLN/YCOz822cdLO8OyToNxBOR&#xA;2/lbqp9qjFXwlrGj6po2qXOlarbSWeo2chiubaUUdHHY/rBGxG4xVMvJHkjzF508xW2g6DbGe9nN&#xA;Xc1EcMYI5SytQ8UWu5+gVJAxV9+/lL+Uvl38t/Lq6dpyifUZwr6pqjqBJcSAfTxjWp4JXb3JJKrO&#xA;cVdirsVdirsVeSfnz+Q2l/mNpZv7AR2fm2zjpZ3h2SdBuIJyO38rdVPtUYqwT/nHf/nGaTRLiLzZ&#xA;55tV/S0L8tL0dyrrbsp2nm4llaTaqL0X7X2qcVX0tir4d/5yO/PybzxqL+XfL8zR+UrKT45AafXp&#xA;kO0rCgPpKf7tT1+0d6BVXhuKuxV9Af8AOFX/AJNPVf8Ath3H/UZaYq+1MVdir4A/5yj/APJ7eZv+&#xA;jH/un2+KvKsVdirsVff/APzi5/5Inyz/ANH3/dQuMVeq4q7FXYq7FXYq+IP+cyf/ACbkP/bKtv8A&#xA;k7NirwrFXYqzr8ofzY1z8t/MyanYkz6dcFY9W00miXEIPav2ZEqSjdvkSCq/RrFXk356/kHpP5k2&#xA;C3tk0Wn+a7VQtrqDgiOWMf7puOIZiv8AKwBK+42xVkH5S/lL5d/Lfy6unacon1GcK+qao6gSXEgH&#xA;08Y1qeCV29ySSqznFXYq7FXYq7FXYq7FXYq7FX5V4q7FXYq+gP8AnCr/AMmnqv8A2w7j/qMtMVfa&#xA;mKuxV8Af85R/+T28zf8ARj/3T7fFXlWKuxV2Kvv/AP5xc/8AJE+Wf+j7/uoXGKvVcVdirsVdirsV&#xA;fEn/ADmdA0f5sWbkgibR7d1p2AnuE3+lMVeDYq7FXYq/VTFXYq7FXYq7FXYq7FXYq7FXYq7FXYq/&#xA;KvFXYq7FX0B/zhV/5NPVf+2Hcf8AUZaYq+1MVdir4A/5yj/8nt5m/wCjH/un2+KvKsVdirsVff8A&#xA;/wA4uf8AkifLP/R9/wB1C4xV6rirsVdirsVdir4r/wCc1f8Ayaelf9sO3/6jLvFXz/irsVdir9VM&#xA;VdirsVdirsVdirsVdirsVdirsVdir8q8VdirsVfQH/OFX/k09V/7Ydx/1GWmKvtTFXYq+AP+co//&#xA;ACe3mb/ox/7p9viryrFXYq7FX3//AM4uf+SJ8s/9H3/dQuMVeq4q7FXYq7FXYq+K/wDnNX/yaelf&#xA;9sO3/wCoy7xV8/4q7FXYq/VTFXYq7FXYq7FXYq7FXYq7FXYq7FXYq/KvFXYq7FX0B/zhV/5NPVf+&#xA;2Hcf9Rlpir7UxV2KvgD/AJyj/wDJ7eZv+jH/ALp9viryrFXYq7FX3/8A84uf+SJ8s/8AR9/3ULjF&#xA;XquKuxV2KuxV2Kviv/nNX/yaelf9sO3/AOoy7xV8/wCKuxV2Kv1UxV2KuxV2KuxV2KuxV2KuxV2K&#xA;uxV2KvyrxV2KuxV9Af8AOFX/AJNPVf8Ath3H/UZaYq+1MVdir4A/5yj/APJ7eZv+jH/un2+KvKsV&#xA;dirsVff/APzi5/5Inyz/ANH3/dQuMVeq4q7FXYq7FXYq+K/+c1f/ACaelf8AbDt/+oy7xV8/4q7F&#xA;XYq/VTFXYq7FXYq7FXYq7FXYq7FXYq7FXYq/KvFXYq7FXoH5Kfmv/wAqy81XWvfov9L/AFmxksfq&#xA;3r/VuPqTRS8+fpzVp6NKce/XFXtX/Q8//fk/9zT/ALM8Vd/0PP8A9+T/ANzT/szxV3/Q8/8A35P/&#xA;AHNP+zPFXf8AQ8//AH5P/c0/7M8Vd/0PP/35P/c0/wCzPFXf9Dz/APfk/wDc0/7M8Vd/0PP/AN+T&#xA;/wBzT/szxV3/AEPP/wB+T/3NP+zPFXf9Dz/9+T/3NP8AszxV3/Q8/wD35P8A3NP+zPFXf9Dz/wDf&#xA;k/8Ac0/7M8Vd/wBDz/8Afk/9zT/szxV4r+df5r/8rN81Wuvfov8ARH1axjsfq3r/AFnl6c0svPn6&#xA;cNK+tSnHt1xV5/irsVdir9VMVdirsVdirsVdirsVdirsVdirsVdir8q8VdirsVdirsVdirsVdirs&#xA;VdirsVdirsVdirsVdirsVdirsVdir9VMVdirsVdirsVdirsVdirsVdirsVdir8q8VdirsVdirsVd&#xA;irsVdirsVdirsVdirsVdirsVdirsVdirsVdir9VMVdirsVdirsVdirsVdirsVdirsVdir8q8Vdir&#xA;sVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdir9VMVdirsVdirsVdirsVdirsVdirsVdi&#xA;rsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdir&#xA;sVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirs&#xA;VdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsV&#xA;dirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVd&#xA;ir//2Q==</xapGImg:image>
+ </rdf:li>
+ </rdf:Alt>
+ </xap:Thumbnails>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:xapMM="http://ns.adobe.com/xap/1.0/mm/">
+ <xapMM:DocumentID>uuid:FF364C322635E01192F88CC5416A78CF</xapMM:DocumentID>
+ <xapMM:InstanceID>uuid:be6b4bae-323e-c246-9a75-1280d64495f2</xapMM:InstanceID>
+ <xapMM:DerivedFrom rdf:parseType="Resource"/>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:xapTPg="http://ns.adobe.com/xap/1.0/t/pg/"
+ xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#"
+ xmlns:xapG="http://ns.adobe.com/xap/1.0/g/">
+ <xapTPg:NPages>1</xapTPg:NPages>
+ <xapTPg:HasVisibleTransparency>False</xapTPg:HasVisibleTransparency>
+ <xapTPg:HasVisibleOverprint>False</xapTPg:HasVisibleOverprint>
+ <xapTPg:MaxPageSize rdf:parseType="Resource">
+ <stDim:w>16.000000</stDim:w>
+ <stDim:h>16.000000</stDim:h>
+ <stDim:unit>Points</stDim:unit>
+ </xapTPg:MaxPageSize>
+ <xapTPg:PlateNames>
+ <rdf:Seq>
+ <rdf:li>Cyan</rdf:li>
+ <rdf:li>Magenta</rdf:li>
+ <rdf:li>Yellow</rdf:li>
+ <rdf:li>Black</rdf:li>
+ </rdf:Seq>
+ </xapTPg:PlateNames>
+ <xapTPg:SwatchGroups>
+ <rdf:Seq>
+ <rdf:li rdf:parseType="Resource">
+ <xapG:groupName>Default Swatch Group</xapG:groupName>
+ <xapG:groupType>0</xapG:groupType>
+ </rdf:li>
+ </rdf:Seq>
+ </xapTPg:SwatchGroups>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:pdf="http://ns.adobe.com/pdf/1.3/">
+ <pdf:Producer>Adobe PDF library 8.00</pdf:Producer>
+ </rdf:Description>
+ </rdf:RDF>
+</x:xmpmeta>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<?xpacket end="w"?> endstream endobj 2 0 obj <</Count 1/Type/Pages/Kids[5 0 R]>> endobj 5 0 obj <</Parent 2 0 R/Contents 7 0 R/BleedBox[0.0 0.0 16.0 16.0]/ArtBox[0.0 0.0 15.7812 15.7812]/MediaBox[0.0 0.0 16.0 16.0]/TrimBox[0.0 0.0 16.0 16.0]/Resources<</Properties<</MC0<</Color[20224 32768 65535]/Visible true/Editable true/Dimmed false/Preview true/Printed true/Title(Layer 1)>>>>/ExtGState<</GS0 6 0 R>>>>/Type/Page>> endobj 7 0 obj <</Length 218/Filter/FlateDecode>>stream
+H‰d‘;R1 †{ŸB°V²lÉnI24¤`8Ã@(’"¡âöH»!³q!}–~ëáééõûýÓ~Cð°Ý@"ˆs9¤éñ…àð•ÎÀóƒ¡u6à†TeÀÛiN>¥l(dp5Ç”ŽêÜQ5˜š@.Wôhm&Ž†EgqÑîø‘žW%™p˜*6îº*©(½º{œÝ1 bçØ‘Gƒò3£Igìt‡ÅûåHöNYý©?ÿ7ñ
+0000000016 00000 n
+0000016570 00000 n
+0000000004 00001 f
+0000000000 00000 f
+0000016621 00000 n
+0000017247 00000 n
+0000016961 00000 n
+0000017359 00000 n
+0000000076 00000 n
+trailer <</Size 10/Root 1 0 R/Info 8 0 R/ID[<ADA186DB0F3649B8AC5BA40BFA30D11D><8A6AD6F969574B3A87EA4AAF57E7FEB8>]>> startxref 17534 %%EOF \ No newline at end of file
diff --git a/frontends/cocoa/res/Icons b/frontends/cocoa/res/Icons
new file mode 120000
index 000000000..187efd6f9
--- /dev/null
+++ b/frontends/cocoa/res/Icons
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/Icons/ \ No newline at end of file
diff --git a/frontends/cocoa/res/LocalHistoryPanel.xib b/frontends/cocoa/res/LocalHistoryPanel.xib
new file mode 100644
index 000000000..794d2db54
--- /dev/null
+++ b/frontends/cocoa/res/LocalHistoryPanel.xib
@@ -0,0 +1,357 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
+ <data>
+ <int key="IBDocument.SystemTarget">1060</int>
+ <string key="IBDocument.SystemVersion">10J567</string>
+ <string key="IBDocument.InterfaceBuilderVersion">804</string>
+ <string key="IBDocument.AppKitVersion">1038.35</string>
+ <string key="IBDocument.HIToolboxVersion">462.00</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="NS.object.0">804</string>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="2"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys" id="0">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomObject" id="1001">
+ <string key="NSClassName">LocalHistoryController</string>
+ </object>
+ <object class="NSCustomObject" id="1003">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1004">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSWindowTemplate" id="1005">
+ <int key="NSWindowStyleMask">15</int>
+ <int key="NSWindowBacking">2</int>
+ <string key="NSWindowRect">{{196, 240}, {480, 270}}</string>
+ <int key="NSWTFlags">1618477056</int>
+ <string key="NSWindowTitle">Window</string>
+ <string key="NSWindowClass">ArrowWindow</string>
+ <nil key="NSViewClass"/>
+ <string key="NSWindowContentMaxSize">{1.79769e+308, 1.79769e+308}</string>
+ <object class="NSView" key="NSWindowView" id="1006">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">256</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSScrollView" id="488267087">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">274</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSClipView" id="753770525">
+ <reference key="NSNextResponder" ref="488267087"/>
+ <int key="NSvFlags">2304</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomView" id="820702167">
+ <reference key="NSNextResponder" ref="753770525"/>
+ <int key="NSvFlags">256</int>
+ <string key="NSFrameSize">{480, 270}</string>
+ <reference key="NSSuperview" ref="753770525"/>
+ <string key="NSClassName">HistoryView</string>
+ </object>
+ </object>
+ <string key="NSFrame">{{1, 1}, {480, 270}}</string>
+ <reference key="NSSuperview" ref="488267087"/>
+ <reference key="NSNextKeyView" ref="820702167"/>
+ <reference key="NSDocView" ref="820702167"/>
+ <object class="NSColor" key="NSBGColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MC42NjY2NjY2NjY3AA</bytes>
+ </object>
+ </object>
+ <int key="NScvFlags">2</int>
+ </object>
+ <object class="NSScroller" id="84195230">
+ <reference key="NSNextResponder" ref="488267087"/>
+ <int key="NSvFlags">-2147483392</int>
+ <string key="NSFrame">{{470, 1}, {11, 255}}</string>
+ <reference key="NSSuperview" ref="488267087"/>
+ <int key="NSsFlags">256</int>
+ <int key="NSArrowsLoc">2</int>
+ <reference key="NSTarget" ref="488267087"/>
+ <string key="NSAction">_doScroller:</string>
+ <double key="NSCurValue">1</double>
+ <double key="NSPercent">0.96363627910614014</double>
+ </object>
+ <object class="NSScroller" id="645365106">
+ <reference key="NSNextResponder" ref="488267087"/>
+ <int key="NSvFlags">-2147483392</int>
+ <string key="NSFrame">{{1, 260}, {465, 11}}</string>
+ <reference key="NSSuperview" ref="488267087"/>
+ <int key="NSsFlags">257</int>
+ <int key="NSArrowsLoc">2</int>
+ <reference key="NSTarget" ref="488267087"/>
+ <string key="NSAction">_doScroller:</string>
+ <double key="NSPercent">0.50602412223815918</double>
+ </object>
+ </object>
+ <string key="NSFrame">{{-1, -1}, {482, 272}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <reference key="NSNextKeyView" ref="753770525"/>
+ <int key="NSsFlags">562</int>
+ <reference key="NSVScroller" ref="84195230"/>
+ <reference key="NSHScroller" ref="645365106"/>
+ <reference key="NSContentView" ref="753770525"/>
+ </object>
+ </object>
+ <string key="NSFrameSize">{480, 270}</string>
+ <reference key="NSSuperview"/>
+ </object>
+ <string key="NSScreenRect">{{0, 0}, {1680, 1028}}</string>
+ <string key="NSMaxSize">{1.79769e+308, 1.79769e+308}</string>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">window</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="1005"/>
+ </object>
+ <int key="connectionID">7</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">history</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="820702167"/>
+ </object>
+ <int key="connectionID">8</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <reference key="object" ref="0"/>
+ <reference key="children" ref="1000"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1001"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">File's Owner</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1003"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1004"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">1</int>
+ <reference key="object" ref="1005"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1006"/>
+ </object>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">2</int>
+ <reference key="object" ref="1006"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="488267087"/>
+ </object>
+ <reference key="parent" ref="1005"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">3</int>
+ <reference key="object" ref="488267087"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="84195230"/>
+ <reference ref="645365106"/>
+ <reference ref="820702167"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">4</int>
+ <reference key="object" ref="84195230"/>
+ <reference key="parent" ref="488267087"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5</int>
+ <reference key="object" ref="645365106"/>
+ <reference key="parent" ref="488267087"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">6</int>
+ <reference key="object" ref="820702167"/>
+ <reference key="parent" ref="488267087"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>1.IBEditorWindowLastContentRect</string>
+ <string>1.IBPluginDependency</string>
+ <string>1.IBWindowTemplateEditedContentRect</string>
+ <string>1.NSWindowTemplate.visibleAtLaunch</string>
+ <string>1.WindowOrigin</string>
+ <string>1.editorWindowContentRectSynchronizationRect</string>
+ <string>2.IBPluginDependency</string>
+ <string>3.IBPluginDependency</string>
+ <string>3.IBViewBoundsToFrameTransform</string>
+ <string>4.CustomClassName</string>
+ <string>4.IBPluginDependency</string>
+ <string>5.CustomClassName</string>
+ <string>5.IBPluginDependency</string>
+ <string>6.IBPluginDependency</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>{{364, 310}, {480, 270}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{364, 310}, {480, 270}}</string>
+ <boolean value="NO"/>
+ <string>{196, 240}</string>
+ <string>{{202, 428}, {480, 270}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAAC/gAAAw4aAAA</bytes>
+ </object>
+ <string>BlackScroller</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>BlackScroller</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">8</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">ArrowWindow</string>
+ <string key="superclassName">NSWindow</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">ArrowWindow.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">BlackScroller</string>
+ <string key="superclassName">NSScroller</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">BlackScroller.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">HistoryView</string>
+ <string key="superclassName">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">HistoryView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">LocalHistoryController</string>
+ <string key="superclassName">NSWindowController</string>
+ <object class="NSMutableDictionary" key="outlets">
+ <string key="NS.key.0">history</string>
+ <string key="NS.object.0">HistoryView</string>
+ </object>
+ <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <string key="NS.key.0">history</string>
+ <object class="IBToOneOutletInfo" key="NS.object.0">
+ <string key="name">history</string>
+ <string key="candidateClassName">HistoryView</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">LocalHistoryController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabDragAssistant.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabBarCell.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabBarControl.h</string>
+ </object>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
+ <integer value="3000" key="NS.object.0"/>
+ </object>
+ <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../NetSurf.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ </data>
+</archive>
diff --git a/frontends/cocoa/res/MainMenu.xib b/frontends/cocoa/res/MainMenu.xib
new file mode 100644
index 000000000..d38240bfd
--- /dev/null
+++ b/frontends/cocoa/res/MainMenu.xib
@@ -0,0 +1,2369 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
+ <data>
+ <int key="IBDocument.SystemTarget">1050</int>
+ <string key="IBDocument.SystemVersion">10J567</string>
+ <string key="IBDocument.InterfaceBuilderVersion">804</string>
+ <string key="IBDocument.AppKitVersion">1038.35</string>
+ <string key="IBDocument.HIToolboxVersion">462.00</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="NS.object.0">804</string>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="29"/>
+ <integer value="853"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <string key="NS.key.0">PluginDependencyRecalculationVersion</string>
+ <integer value="1" key="NS.object.0"/>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1048">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomObject" id="1021">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSCustomObject" id="1014">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1050">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSMenu" id="649796088">
+ <string key="NSTitle">Main Menu</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="694149608">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">NetSurf</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <object class="NSCustomResource" key="NSOnImage" id="756751024">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSMenuCheckmark</string>
+ </object>
+ <object class="NSCustomResource" key="NSMixedImage" id="908425081">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSMenuMixedState</string>
+ </object>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="110575045">
+ <string key="NSTitle">NetSurf</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="238522557">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">About NetSurf</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="304266470">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="609285721">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Preferences…</string>
+ <string key="NSKeyEquiv">,</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="481834944">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="1046388886">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Services</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="752062318">
+ <string key="NSTitle">Services</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <string key="NSName">_NSServicesMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="646227648">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="755159360">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Hide NetSurf</string>
+ <string key="NSKeyEquiv">h</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="342932134">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Hide Others</string>
+ <string key="NSKeyEquiv">h</string>
+ <int key="NSKeyEquivModMask">1572864</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="908899353">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Show All</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="1056857174">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="632727374">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Quit NetSurf</string>
+ <string key="NSKeyEquiv">q</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ </object>
+ <string key="NSName">_NSAppleMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="379814623">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">File</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="720053764">
+ <string key="NSTitle">File</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="705341025">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">New Window</string>
+ <string key="NSKeyEquiv">n</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="72022292">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">New Tab</string>
+ <string key="NSKeyEquiv">t</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="722745758">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Open File…</string>
+ <string key="NSKeyEquiv">o</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="425164168">
+ <reference key="NSMenu" ref="720053764"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="776162233">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Close</string>
+ <string key="NSKeyEquiv">w</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="117038363">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Save As…</string>
+ <string key="NSKeyEquiv">s</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="1010469920">
+ <reference key="NSMenu" ref="720053764"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="294629803">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Page Setup...</string>
+ <string key="NSKeyEquiv">P</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ <string key="NSToolTip"/>
+ </object>
+ <object class="NSMenuItem" id="49223823">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Print…</string>
+ <string key="NSKeyEquiv">p</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="584895621">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Edit</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="141080932">
+ <string key="NSTitle">Edit</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="80034836">
+ <reference key="NSMenu" ref="141080932"/>
+ <string key="NSTitle">Undo</string>
+ <string key="NSKeyEquiv">z</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="128588396">
+ <reference key="NSMenu" ref="141080932"/>
+ <string key="NSTitle">Redo</string>
+ <string key="NSKeyEquiv">Z</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="165057028">
+ <reference key="NSMenu" ref="141080932"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="5858980">
+ <reference key="NSMenu" ref="141080932"/>
+ <string key="NSTitle">Cut</string>
+ <string key="NSKeyEquiv">x</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="704355768">
+ <reference key="NSMenu" ref="141080932"/>
+ <string key="NSTitle">Copy</string>
+ <string key="NSKeyEquiv">c</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="275307167">
+ <reference key="NSMenu" ref="141080932"/>
+ <string key="NSTitle">Paste</string>
+ <string key="NSKeyEquiv">v</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="714155551">
+ <reference key="NSMenu" ref="141080932"/>
+ <string key="NSTitle">Delete</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="197377228">
+ <reference key="NSMenu" ref="141080932"/>
+ <string key="NSTitle">Select All</string>
+ <string key="NSKeyEquiv">a</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="246962120">
+ <reference key="NSMenu" ref="141080932"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="602982148">
+ <reference key="NSMenu" ref="141080932"/>
+ <string key="NSTitle">Find</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="930654435">
+ <string key="NSTitle">Find</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="671868626">
+ <reference key="NSMenu" ref="930654435"/>
+ <string key="NSTitle">Find…</string>
+ <string key="NSKeyEquiv">f</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ <int key="NSTag">1</int>
+ </object>
+ <object class="NSMenuItem" id="497741775">
+ <reference key="NSMenu" ref="930654435"/>
+ <string key="NSTitle">Find Next</string>
+ <string key="NSKeyEquiv">g</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ <int key="NSTag">2</int>
+ </object>
+ <object class="NSMenuItem" id="285322108">
+ <reference key="NSMenu" ref="930654435"/>
+ <string key="NSTitle">Find Previous</string>
+ <string key="NSKeyEquiv">G</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ <int key="NSTag">3</int>
+ </object>
+ <object class="NSMenuItem" id="456308224">
+ <reference key="NSMenu" ref="930654435"/>
+ <string key="NSTitle">Use Selection for Find</string>
+ <string key="NSKeyEquiv">e</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ <int key="NSTag">7</int>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="1046338161">
+ <reference key="NSMenu" ref="141080932"/>
+ <string key="NSTitle">Speech</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="390929284">
+ <string key="NSTitle">Speech</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="787796378">
+ <reference key="NSMenu" ref="390929284"/>
+ <string key="NSTitle">Start Speaking</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="831785675">
+ <reference key="NSMenu" ref="390929284"/>
+ <string key="NSTitle">Stop Speaking</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="586577488">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">View</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="466310130">
+ <string key="NSTitle">View</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="102151532">
+ <reference key="NSMenu" ref="466310130"/>
+ <string key="NSTitle">Show Toolbar</string>
+ <string key="NSKeyEquiv">t</string>
+ <int key="NSKeyEquivModMask">1572864</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="237841660">
+ <reference key="NSMenu" ref="466310130"/>
+ <string key="NSTitle">Customize Toolbar…</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="588542073">
+ <reference key="NSMenu" ref="466310130"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="1008284068">
+ <reference key="NSMenu" ref="466310130"/>
+ <string key="NSTitle">Stop Loading</string>
+ <string key="NSKeyEquiv">.</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="997106205">
+ <reference key="NSMenu" ref="466310130"/>
+ <string key="NSTitle">Reload Page</string>
+ <string key="NSKeyEquiv">r</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="403460345">
+ <reference key="NSMenu" ref="466310130"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="924072330">
+ <reference key="NSMenu" ref="466310130"/>
+ <string key="NSTitle">Original Size</string>
+ <string key="NSKeyEquiv">0</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="157577355">
+ <reference key="NSMenu" ref="466310130"/>
+ <string key="NSTitle">Zoom In</string>
+ <string key="NSKeyEquiv">+</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="148271458">
+ <reference key="NSMenu" ref="466310130"/>
+ <string key="NSTitle">Zoom Out</string>
+ <string key="NSKeyEquiv">-</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="603467951">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Bookmarks</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="1062528031">
+ <string key="NSTitle">Bookmarks</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="713487014">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Window</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="835318025">
+ <string key="NSTitle">Window</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="1011231497">
+ <reference key="NSMenu" ref="835318025"/>
+ <string key="NSTitle">Minimize</string>
+ <string key="NSKeyEquiv">m</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="575023229">
+ <reference key="NSMenu" ref="835318025"/>
+ <string key="NSTitle">Zoom</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="971013910">
+ <reference key="NSMenu" ref="835318025"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="532573582">
+ <reference key="NSMenu" ref="835318025"/>
+ <string key="NSTitle">History</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="299356726">
+ <reference key="NSMenu" ref="835318025"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="625202149">
+ <reference key="NSMenu" ref="835318025"/>
+ <string key="NSTitle">Bring All to Front</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ </object>
+ <string key="NSName">_NSWindowsMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="391199113">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Help</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="374024848">
+ <string key="NSTitle">Help</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="238773614">
+ <reference key="NSMenu" ref="374024848"/>
+ <string key="NSTitle">NetSurf Help</string>
+ <string key="NSKeyEquiv">?</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ </object>
+ <string key="NSName">_NSHelpMenu</string>
+ </object>
+ </object>
+ </object>
+ <string key="NSName">_NSMainMenu</string>
+ </object>
+ <object class="NSCustomObject" id="1026802243">
+ <string key="NSClassName">NetSurfAppDelegate</string>
+ </object>
+ <object class="NSCustomObject" id="867741866">
+ <string key="NSClassName">BookmarksController</string>
+ </object>
+ <object class="NSMenu" id="509997857">
+ <string key="NSTitle">Default Bookmark Actions</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="844807595">
+ <reference key="NSMenu" ref="509997857"/>
+ <string key="NSTitle">Add bookmark</string>
+ <string key="NSKeyEquiv">d</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ <object class="NSMenuItem" id="832858329">
+ <reference key="NSMenu" ref="509997857"/>
+ <string key="NSTitle">Show bookmarks...</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="756751024"/>
+ <reference key="NSMixedImage" ref="908425081"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performMiniaturize:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1011231497"/>
+ </object>
+ <int key="connectionID">37</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">arrangeInFront:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="625202149"/>
+ </object>
+ <int key="connectionID">39</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">print:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="49223823"/>
+ </object>
+ <int key="connectionID">86</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">runPageLayout:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="294629803"/>
+ </object>
+ <int key="connectionID">87</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">orderFrontStandardAboutPanel:</string>
+ <reference key="source" ref="1021"/>
+ <reference key="destination" ref="238522557"/>
+ </object>
+ <int key="connectionID">142</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performZoom:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="575023229"/>
+ </object>
+ <int key="connectionID">240</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">showHelp:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="238773614"/>
+ </object>
+ <int key="connectionID">360</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">saveDocumentAs:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="117038363"/>
+ </object>
+ <int key="connectionID">363</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">runToolbarCustomizationPalette:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="237841660"/>
+ </object>
+ <int key="connectionID">365</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleToolbarShown:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="102151532"/>
+ </object>
+ <int key="connectionID">366</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">hide:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="755159360"/>
+ </object>
+ <int key="connectionID">369</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">hideOtherApplications:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="342932134"/>
+ </object>
+ <int key="connectionID">370</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">unhideAllApplications:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="908899353"/>
+ </object>
+ <int key="connectionID">372</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">terminate:</string>
+ <reference key="source" ref="1021"/>
+ <reference key="destination" ref="632727374"/>
+ </object>
+ <int key="connectionID">448</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">cut:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="5858980"/>
+ </object>
+ <int key="connectionID">741</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">paste:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="275307167"/>
+ </object>
+ <int key="connectionID">742</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">redo:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="128588396"/>
+ </object>
+ <int key="connectionID">745</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">undo:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="80034836"/>
+ </object>
+ <int key="connectionID">749</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">startSpeaking:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="787796378"/>
+ </object>
+ <int key="connectionID">751</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">copy:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="704355768"/>
+ </object>
+ <int key="connectionID">755</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">delete:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="714155551"/>
+ </object>
+ <int key="connectionID">756</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">selectAll:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="197377228"/>
+ </object>
+ <int key="connectionID">758</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">stopSpeaking:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="831785675"/>
+ </object>
+ <int key="connectionID">759</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="1050"/>
+ <reference key="destination" ref="1026802243"/>
+ </object>
+ <int key="connectionID">821</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">newDocument:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="705341025"/>
+ </object>
+ <int key="connectionID">823</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">openDocument:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="722745758"/>
+ </object>
+ <int key="connectionID">824</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">zoomIn:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="157577355"/>
+ </object>
+ <int key="connectionID">829</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">zoomOut:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="148271458"/>
+ </object>
+ <int key="connectionID">830</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">stopLoading:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1008284068"/>
+ </object>
+ <int key="connectionID">835</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">reloadPage:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="997106205"/>
+ </object>
+ <int key="connectionID">836</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">zoomOriginal:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="924072330"/>
+ </object>
+ <int key="connectionID">837</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">newTab:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="72022292"/>
+ </object>
+ <int key="connectionID">839</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">showSearchWindow:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="671868626"/>
+ </object>
+ <int key="connectionID">841</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">searchNext:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="497741775"/>
+ </object>
+ <int key="connectionID">842</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">searchPrevious:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="285322108"/>
+ </object>
+ <int key="connectionID">843</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performClose:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="776162233"/>
+ </object>
+ <int key="connectionID">844</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">showPreferences:</string>
+ <reference key="source" ref="1026802243"/>
+ <reference key="destination" ref="609285721"/>
+ </object>
+ <int key="connectionID">845</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">showGlobalHistory:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="532573582"/>
+ </object>
+ <int key="connectionID">846</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="1062528031"/>
+ <reference key="destination" ref="867741866"/>
+ </object>
+ <int key="connectionID">851</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">defaultMenu</string>
+ <reference key="source" ref="867741866"/>
+ <reference key="destination" ref="509997857"/>
+ </object>
+ <int key="connectionID">856</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">addBookmark:</string>
+ <reference key="source" ref="867741866"/>
+ <reference key="destination" ref="844807595"/>
+ </object>
+ <int key="connectionID">857</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">showWindow:</string>
+ <reference key="source" ref="867741866"/>
+ <reference key="destination" ref="832858329"/>
+ </object>
+ <int key="connectionID">859</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <object class="NSArray" key="object" id="0">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="children" ref="1048"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1021"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">File's Owner</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1014"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1050"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">29</int>
+ <reference key="object" ref="649796088"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="713487014"/>
+ <reference ref="694149608"/>
+ <reference ref="391199113"/>
+ <reference ref="379814623"/>
+ <reference ref="586577488"/>
+ <reference ref="584895621"/>
+ <reference ref="603467951"/>
+ </object>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">19</int>
+ <reference key="object" ref="713487014"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="835318025"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">56</int>
+ <reference key="object" ref="694149608"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="110575045"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">103</int>
+ <reference key="object" ref="391199113"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="374024848"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">83</int>
+ <reference key="object" ref="379814623"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="720053764"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">81</int>
+ <reference key="object" ref="720053764"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="117038363"/>
+ <reference ref="49223823"/>
+ <reference ref="722745758"/>
+ <reference ref="705341025"/>
+ <reference ref="294629803"/>
+ <reference ref="776162233"/>
+ <reference ref="1010469920"/>
+ <reference ref="425164168"/>
+ <reference ref="72022292"/>
+ </object>
+ <reference key="parent" ref="379814623"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">80</int>
+ <reference key="object" ref="117038363"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">78</int>
+ <reference key="object" ref="49223823"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">72</int>
+ <reference key="object" ref="722745758"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">82</int>
+ <reference key="object" ref="705341025"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">77</int>
+ <reference key="object" ref="294629803"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">73</int>
+ <reference key="object" ref="776162233"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">74</int>
+ <reference key="object" ref="1010469920"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">106</int>
+ <reference key="object" ref="374024848"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="238773614"/>
+ </object>
+ <reference key="parent" ref="391199113"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">111</int>
+ <reference key="object" ref="238773614"/>
+ <reference key="parent" ref="374024848"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">57</int>
+ <reference key="object" ref="110575045"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="238522557"/>
+ <reference ref="755159360"/>
+ <reference ref="908899353"/>
+ <reference ref="632727374"/>
+ <reference ref="646227648"/>
+ <reference ref="609285721"/>
+ <reference ref="481834944"/>
+ <reference ref="304266470"/>
+ <reference ref="1046388886"/>
+ <reference ref="1056857174"/>
+ <reference ref="342932134"/>
+ </object>
+ <reference key="parent" ref="694149608"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">58</int>
+ <reference key="object" ref="238522557"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">134</int>
+ <reference key="object" ref="755159360"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">150</int>
+ <reference key="object" ref="908899353"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">136</int>
+ <reference key="object" ref="632727374"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">144</int>
+ <reference key="object" ref="646227648"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">129</int>
+ <reference key="object" ref="609285721"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">143</int>
+ <reference key="object" ref="481834944"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">236</int>
+ <reference key="object" ref="304266470"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">131</int>
+ <reference key="object" ref="1046388886"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="752062318"/>
+ </object>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">149</int>
+ <reference key="object" ref="1056857174"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">145</int>
+ <reference key="object" ref="342932134"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">130</int>
+ <reference key="object" ref="752062318"/>
+ <reference key="parent" ref="1046388886"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">24</int>
+ <reference key="object" ref="835318025"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="299356726"/>
+ <reference ref="625202149"/>
+ <reference ref="575023229"/>
+ <reference ref="1011231497"/>
+ <reference ref="971013910"/>
+ <reference ref="532573582"/>
+ </object>
+ <reference key="parent" ref="713487014"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">92</int>
+ <reference key="object" ref="299356726"/>
+ <reference key="parent" ref="835318025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5</int>
+ <reference key="object" ref="625202149"/>
+ <reference key="parent" ref="835318025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">239</int>
+ <reference key="object" ref="575023229"/>
+ <reference key="parent" ref="835318025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">23</int>
+ <reference key="object" ref="1011231497"/>
+ <reference key="parent" ref="835318025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">295</int>
+ <reference key="object" ref="586577488"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="466310130"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">296</int>
+ <reference key="object" ref="466310130"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="102151532"/>
+ <reference ref="237841660"/>
+ <reference ref="588542073"/>
+ <reference ref="148271458"/>
+ <reference ref="924072330"/>
+ <reference ref="157577355"/>
+ <reference ref="403460345"/>
+ <reference ref="1008284068"/>
+ <reference ref="997106205"/>
+ </object>
+ <reference key="parent" ref="586577488"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">297</int>
+ <reference key="object" ref="102151532"/>
+ <reference key="parent" ref="466310130"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">298</int>
+ <reference key="object" ref="237841660"/>
+ <reference key="parent" ref="466310130"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">79</int>
+ <reference key="object" ref="425164168"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">684</int>
+ <reference key="object" ref="584895621"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="141080932"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">685</int>
+ <reference key="object" ref="141080932"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="80034836"/>
+ <reference ref="128588396"/>
+ <reference ref="165057028"/>
+ <reference ref="5858980"/>
+ <reference ref="704355768"/>
+ <reference ref="275307167"/>
+ <reference ref="714155551"/>
+ <reference ref="197377228"/>
+ <reference ref="246962120"/>
+ <reference ref="602982148"/>
+ <reference ref="1046338161"/>
+ </object>
+ <reference key="parent" ref="584895621"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">686</int>
+ <reference key="object" ref="80034836"/>
+ <reference key="parent" ref="141080932"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">687</int>
+ <reference key="object" ref="128588396"/>
+ <reference key="parent" ref="141080932"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">688</int>
+ <reference key="object" ref="165057028"/>
+ <reference key="parent" ref="141080932"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">689</int>
+ <reference key="object" ref="5858980"/>
+ <reference key="parent" ref="141080932"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">690</int>
+ <reference key="object" ref="704355768"/>
+ <reference key="parent" ref="141080932"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">691</int>
+ <reference key="object" ref="275307167"/>
+ <reference key="parent" ref="141080932"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">693</int>
+ <reference key="object" ref="714155551"/>
+ <reference key="parent" ref="141080932"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">694</int>
+ <reference key="object" ref="197377228"/>
+ <reference key="parent" ref="141080932"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">695</int>
+ <reference key="object" ref="246962120"/>
+ <reference key="parent" ref="141080932"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">696</int>
+ <reference key="object" ref="602982148"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="930654435"/>
+ </object>
+ <reference key="parent" ref="141080932"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">700</int>
+ <reference key="object" ref="1046338161"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="390929284"/>
+ </object>
+ <reference key="parent" ref="141080932"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">711</int>
+ <reference key="object" ref="390929284"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="787796378"/>
+ <reference ref="831785675"/>
+ </object>
+ <reference key="parent" ref="1046338161"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">712</int>
+ <reference key="object" ref="787796378"/>
+ <reference key="parent" ref="390929284"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">713</int>
+ <reference key="object" ref="831785675"/>
+ <reference key="parent" ref="390929284"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">734</int>
+ <reference key="object" ref="930654435"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="671868626"/>
+ <reference ref="497741775"/>
+ <reference ref="285322108"/>
+ <reference ref="456308224"/>
+ </object>
+ <reference key="parent" ref="602982148"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">735</int>
+ <reference key="object" ref="671868626"/>
+ <reference key="parent" ref="930654435"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">736</int>
+ <reference key="object" ref="497741775"/>
+ <reference key="parent" ref="930654435"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">737</int>
+ <reference key="object" ref="285322108"/>
+ <reference key="parent" ref="930654435"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">738</int>
+ <reference key="object" ref="456308224"/>
+ <reference key="parent" ref="930654435"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">817</int>
+ <reference key="object" ref="971013910"/>
+ <reference key="parent" ref="835318025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">818</int>
+ <reference key="object" ref="532573582"/>
+ <reference key="parent" ref="835318025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">820</int>
+ <reference key="object" ref="1026802243"/>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">825</int>
+ <reference key="object" ref="588542073"/>
+ <reference key="parent" ref="466310130"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">826</int>
+ <reference key="object" ref="148271458"/>
+ <reference key="parent" ref="466310130"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">827</int>
+ <reference key="object" ref="924072330"/>
+ <reference key="parent" ref="466310130"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">828</int>
+ <reference key="object" ref="157577355"/>
+ <reference key="parent" ref="466310130"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">832</int>
+ <reference key="object" ref="403460345"/>
+ <reference key="parent" ref="466310130"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">833</int>
+ <reference key="object" ref="1008284068"/>
+ <reference key="parent" ref="466310130"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">834</int>
+ <reference key="object" ref="997106205"/>
+ <reference key="parent" ref="466310130"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">838</int>
+ <reference key="object" ref="72022292"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">847</int>
+ <reference key="object" ref="867741866"/>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">848</int>
+ <reference key="object" ref="603467951"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1062528031"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">849</int>
+ <reference key="object" ref="1062528031"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="parent" ref="603467951"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">853</int>
+ <reference key="object" ref="509997857"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="844807595"/>
+ <reference ref="832858329"/>
+ </object>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">854</int>
+ <reference key="object" ref="844807595"/>
+ <reference key="parent" ref="509997857"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">855</int>
+ <reference key="object" ref="832858329"/>
+ <reference key="parent" ref="509997857"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>-3.IBPluginDependency</string>
+ <string>103.IBPluginDependency</string>
+ <string>103.ImportedFromIB2</string>
+ <string>106.IBEditorWindowLastContentRect</string>
+ <string>106.IBPluginDependency</string>
+ <string>106.ImportedFromIB2</string>
+ <string>106.editorWindowContentRectSynchronizationRect</string>
+ <string>111.IBPluginDependency</string>
+ <string>111.ImportedFromIB2</string>
+ <string>129.IBPluginDependency</string>
+ <string>129.ImportedFromIB2</string>
+ <string>130.IBEditorWindowLastContentRect</string>
+ <string>130.IBPluginDependency</string>
+ <string>130.ImportedFromIB2</string>
+ <string>130.editorWindowContentRectSynchronizationRect</string>
+ <string>131.IBPluginDependency</string>
+ <string>131.ImportedFromIB2</string>
+ <string>134.IBPluginDependency</string>
+ <string>134.ImportedFromIB2</string>
+ <string>136.IBPluginDependency</string>
+ <string>136.ImportedFromIB2</string>
+ <string>143.IBPluginDependency</string>
+ <string>143.ImportedFromIB2</string>
+ <string>144.IBPluginDependency</string>
+ <string>144.ImportedFromIB2</string>
+ <string>145.IBPluginDependency</string>
+ <string>145.ImportedFromIB2</string>
+ <string>149.IBPluginDependency</string>
+ <string>149.ImportedFromIB2</string>
+ <string>150.IBPluginDependency</string>
+ <string>150.ImportedFromIB2</string>
+ <string>19.IBPluginDependency</string>
+ <string>19.ImportedFromIB2</string>
+ <string>23.IBPluginDependency</string>
+ <string>23.ImportedFromIB2</string>
+ <string>236.IBPluginDependency</string>
+ <string>236.ImportedFromIB2</string>
+ <string>239.IBPluginDependency</string>
+ <string>239.ImportedFromIB2</string>
+ <string>24.IBEditorWindowLastContentRect</string>
+ <string>24.IBPluginDependency</string>
+ <string>24.ImportedFromIB2</string>
+ <string>24.editorWindowContentRectSynchronizationRect</string>
+ <string>29.IBEditorWindowLastContentRect</string>
+ <string>29.IBPluginDependency</string>
+ <string>29.ImportedFromIB2</string>
+ <string>29.WindowOrigin</string>
+ <string>29.editorWindowContentRectSynchronizationRect</string>
+ <string>295.IBPluginDependency</string>
+ <string>296.IBEditorWindowLastContentRect</string>
+ <string>296.IBPluginDependency</string>
+ <string>296.editorWindowContentRectSynchronizationRect</string>
+ <string>297.IBPluginDependency</string>
+ <string>298.IBPluginDependency</string>
+ <string>5.IBPluginDependency</string>
+ <string>5.ImportedFromIB2</string>
+ <string>56.IBPluginDependency</string>
+ <string>56.ImportedFromIB2</string>
+ <string>57.IBEditorWindowLastContentRect</string>
+ <string>57.IBPluginDependency</string>
+ <string>57.ImportedFromIB2</string>
+ <string>57.editorWindowContentRectSynchronizationRect</string>
+ <string>58.IBPluginDependency</string>
+ <string>58.ImportedFromIB2</string>
+ <string>684.IBPluginDependency</string>
+ <string>685.IBEditorWindowLastContentRect</string>
+ <string>685.IBPluginDependency</string>
+ <string>686.IBPluginDependency</string>
+ <string>687.IBPluginDependency</string>
+ <string>688.IBPluginDependency</string>
+ <string>689.IBPluginDependency</string>
+ <string>690.IBPluginDependency</string>
+ <string>691.IBPluginDependency</string>
+ <string>693.IBPluginDependency</string>
+ <string>694.IBPluginDependency</string>
+ <string>695.IBPluginDependency</string>
+ <string>696.IBPluginDependency</string>
+ <string>700.IBPluginDependency</string>
+ <string>711.IBEditorWindowLastContentRect</string>
+ <string>711.IBPluginDependency</string>
+ <string>712.IBPluginDependency</string>
+ <string>713.IBPluginDependency</string>
+ <string>72.IBPluginDependency</string>
+ <string>72.ImportedFromIB2</string>
+ <string>73.IBPluginDependency</string>
+ <string>73.ImportedFromIB2</string>
+ <string>734.IBEditorWindowLastContentRect</string>
+ <string>734.IBPluginDependency</string>
+ <string>735.IBPluginDependency</string>
+ <string>736.IBPluginDependency</string>
+ <string>737.IBPluginDependency</string>
+ <string>738.IBPluginDependency</string>
+ <string>74.IBPluginDependency</string>
+ <string>74.ImportedFromIB2</string>
+ <string>77.IBPluginDependency</string>
+ <string>77.ImportedFromIB2</string>
+ <string>78.IBPluginDependency</string>
+ <string>78.ImportedFromIB2</string>
+ <string>79.IBPluginDependency</string>
+ <string>79.ImportedFromIB2</string>
+ <string>80.IBPluginDependency</string>
+ <string>80.ImportedFromIB2</string>
+ <string>81.IBEditorWindowLastContentRect</string>
+ <string>81.IBPluginDependency</string>
+ <string>81.ImportedFromIB2</string>
+ <string>81.editorWindowContentRectSynchronizationRect</string>
+ <string>817.IBPluginDependency</string>
+ <string>818.IBPluginDependency</string>
+ <string>82.IBPluginDependency</string>
+ <string>82.ImportedFromIB2</string>
+ <string>820.IBPluginDependency</string>
+ <string>825.IBPluginDependency</string>
+ <string>826.IBPluginDependency</string>
+ <string>827.IBPluginDependency</string>
+ <string>828.IBPluginDependency</string>
+ <string>83.IBPluginDependency</string>
+ <string>83.ImportedFromIB2</string>
+ <string>832.IBPluginDependency</string>
+ <string>833.IBPluginDependency</string>
+ <string>834.IBPluginDependency</string>
+ <string>838.IBPluginDependency</string>
+ <string>847.IBPluginDependency</string>
+ <string>848.IBPluginDependency</string>
+ <string>849.IBEditorWindowLastContentRect</string>
+ <string>849.IBPluginDependency</string>
+ <string>853.IBEditorWindowLastContentRect</string>
+ <string>853.IBPluginDependency</string>
+ <string>854.IBPluginDependency</string>
+ <string>855.IBPluginDependency</string>
+ <string>92.IBPluginDependency</string>
+ <string>92.ImportedFromIB2</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{881, 774}, {157, 23}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{596, 852}, {216, 23}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{509, 573}, {64, 6}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{436, 809}, {64, 6}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{876, 712}, {194, 103}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{525, 802}, {197, 73}}</string>
+ <string>{{604, 815}, {446, 20}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{74, 862}</string>
+ <string>{{11, 977}, {478, 20}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{823, 465}, {234, 163}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{475, 832}, {234, 43}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{668, 632}, {186, 183}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{23, 794}, {245, 183}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{782, 612}, {151, 203}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{933, 592}, {150, 43}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{933, 572}, {238, 83}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{740, 652}, {179, 163}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{323, 672}, {199, 203}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{824, 809}, {64, 6}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{616, 718}, {206, 43}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">859</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">BookmarksController</string>
+ <string key="superclassName">NSWindowController</string>
+ <object class="NSMutableDictionary" key="actions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>addBookmark:</string>
+ <string>openBookmarkURL:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="actionInfosByName">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>addBookmark:</string>
+ <string>openBookmarkURL:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBActionInfo">
+ <string key="name">addBookmark:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">openBookmarkURL:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="outlets">
+ <string key="NS.key.0">defaultMenu</string>
+ <string key="NS.object.0">NSMenu</string>
+ </object>
+ <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <string key="NS.key.0">defaultMenu</string>
+ <object class="IBToOneOutletInfo" key="NS.object.0">
+ <string key="name">defaultMenu</string>
+ <string key="candidateClassName">NSMenu</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">BookmarksController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">BrowserView</string>
+ <string key="superclassName">ScrollableView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">BrowserView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">BrowserViewController</string>
+ <string key="superclassName">NSViewController</string>
+ <object class="NSMutableDictionary" key="actions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>backForwardSelected:</string>
+ <string>goBack:</string>
+ <string>goForward:</string>
+ <string>goHome:</string>
+ <string>navigate:</string>
+ <string>reloadPage:</string>
+ <string>stopLoading:</string>
+ <string>zoomIn:</string>
+ <string>zoomOriginal:</string>
+ <string>zoomOut:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="actionInfosByName">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>backForwardSelected:</string>
+ <string>goBack:</string>
+ <string>goForward:</string>
+ <string>goHome:</string>
+ <string>navigate:</string>
+ <string>reloadPage:</string>
+ <string>stopLoading:</string>
+ <string>zoomIn:</string>
+ <string>zoomOriginal:</string>
+ <string>zoomOut:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBActionInfo">
+ <string key="name">backForwardSelected:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">goBack:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">goForward:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">goHome:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">navigate:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">reloadPage:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">stopLoading:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">zoomIn:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">zoomOriginal:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">zoomOut:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="outlets">
+ <string key="NS.key.0">browserView</string>
+ <string key="NS.object.0">BrowserView</string>
+ </object>
+ <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <string key="NS.key.0">browserView</string>
+ <object class="IBToOneOutletInfo" key="NS.object.0">
+ <string key="name">browserView</string>
+ <string key="candidateClassName">BrowserView</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">BrowserViewController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">BrowserWindowController</string>
+ <string key="superclassName">NSWindowController</string>
+ <object class="NSMutableDictionary" key="actions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>closeCurrentTab:</string>
+ <string>newTab:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="actionInfosByName">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>closeCurrentTab:</string>
+ <string>newTab:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBActionInfo">
+ <string key="name">closeCurrentTab:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">newTab:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="outlets">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>activeBrowserController</string>
+ <string>navigationControl</string>
+ <string>tabBar</string>
+ <string>tabView</string>
+ <string>urlField</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>NSObjectController</string>
+ <string>NSSegmentedControl</string>
+ <string>PSMTabBarControl</string>
+ <string>NSTabView</string>
+ <string>URLFieldCell</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>activeBrowserController</string>
+ <string>navigationControl</string>
+ <string>tabBar</string>
+ <string>tabView</string>
+ <string>urlField</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBToOneOutletInfo">
+ <string key="name">activeBrowserController</string>
+ <string key="candidateClassName">NSObjectController</string>
+ </object>
+ <object class="IBToOneOutletInfo">
+ <string key="name">navigationControl</string>
+ <string key="candidateClassName">NSSegmentedControl</string>
+ </object>
+ <object class="IBToOneOutletInfo">
+ <string key="name">tabBar</string>
+ <string key="candidateClassName">PSMTabBarControl</string>
+ </object>
+ <object class="IBToOneOutletInfo">
+ <string key="name">tabView</string>
+ <string key="candidateClassName">NSTabView</string>
+ </object>
+ <object class="IBToOneOutletInfo">
+ <string key="name">urlField</string>
+ <string key="candidateClassName">URLFieldCell</string>
+ </object>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">BrowserWindowController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="294109393">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabDragAssistant.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="831067236">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabBarCell.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="354078772">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabBarControl.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NetSurfAppDelegate</string>
+ <string key="superclassName">NSObject</string>
+ <object class="NSMutableDictionary" key="actions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>searchBackward:</string>
+ <string>searchForward:</string>
+ <string>showGlobalHistory:</string>
+ <string>showPreferences:</string>
+ <string>showSearchWindow:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="actionInfosByName">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>searchBackward:</string>
+ <string>searchForward:</string>
+ <string>showGlobalHistory:</string>
+ <string>showPreferences:</string>
+ <string>showSearchWindow:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBActionInfo">
+ <string key="name">searchBackward:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">searchForward:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">showGlobalHistory:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">showPreferences:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">showSearchWindow:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">NetSurfAppDelegate.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">PSMTabBarControl</string>
+ <reference key="sourceIdentifier" ref="831067236"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">PSMTabBarControl</string>
+ <string key="superclassName">NSControl</string>
+ <object class="NSMutableDictionary" key="outlets">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>delegate</string>
+ <string>partnerView</string>
+ <string>style</string>
+ <string>tabView</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>NSTabView</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>delegate</string>
+ <string>partnerView</string>
+ <string>style</string>
+ <string>tabView</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBToOneOutletInfo">
+ <string key="name">delegate</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBToOneOutletInfo">
+ <string key="name">partnerView</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBToOneOutletInfo">
+ <string key="name">style</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBToOneOutletInfo">
+ <string key="name">tabView</string>
+ <string key="candidateClassName">NSTabView</string>
+ </object>
+ </object>
+ </object>
+ <reference key="sourceIdentifier" ref="354078772"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">PSMTabBarControl</string>
+ <reference key="sourceIdentifier" ref="294109393"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">PSMTabBarControl</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabStyle.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">ScrollableView</string>
+ <string key="superclassName">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">ScrollableView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">SearchWindowController</string>
+ <string key="superclassName">NSWindowController</string>
+ <object class="NSMutableDictionary" key="actions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>searchNext:</string>
+ <string>searchPrevious:</string>
+ <string>searchStringDidChange:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="actionInfosByName">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>searchNext:</string>
+ <string>searchPrevious:</string>
+ <string>searchStringDidChange:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBActionInfo">
+ <string key="name">searchNext:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">searchPrevious:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">searchStringDidChange:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">SearchWindowController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">URLFieldCell</string>
+ <string key="superclassName">NSTextFieldCell</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">URLFieldCell.h</string>
+ </object>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
+ <integer value="1050" key="NS.object.0"/>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
+ <integer value="3000" key="NS.object.0"/>
+ </object>
+ <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../NetSurf.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ <object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>NSMenuCheckmark</string>
+ <string>NSMenuMixedState</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>{9, 8}</string>
+ <string>{7, 2}</string>
+ </object>
+ </object>
+ </data>
+</archive>
diff --git a/frontends/cocoa/res/NetSurf-Info.plist b/frontends/cocoa/res/NetSurf-Info.plist
new file mode 100644
index 000000000..60bf6820e
--- /dev/null
+++ b/frontends/cocoa/res/NetSurf-Info.plist
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleDocumentTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleTypeExtensions</key>
+ <array/>
+ <key>CFBundleTypeMIMETypes</key>
+ <array/>
+ <key>CFBundleTypeName</key>
+ <string>HTML</string>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ <key>LSItemContentTypes</key>
+ <array>
+ <string>public.html</string>
+ </array>
+ </dict>
+ </array>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string>NetSurf</string>
+ <key>CFBundleIdentifier</key>
+ <string>org.netsurf-browser.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>${NETSURF_VERSION}</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleURLTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleURLName</key>
+ <string>org.netsurf-browser.NetSurf.URI</string>
+ <key>CFBundleURLSchemes</key>
+ <array>
+ <string>http</string>
+ <string>https</string>
+ </array>
+ </dict>
+ </array>
+ <key>CFBundleVersion</key>
+ <string>${NETSURF_SHORT_VERSION}</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>${MACOSX_DEPLOYMENT_TARGET}</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NetSurfApp</string>
+ <key>NSServices</key>
+ <array/>
+ <key>UTExportedTypeDeclarations</key>
+ <array/>
+ <key>UTImportedTypeDeclarations</key>
+ <array>
+ <dict>
+ <key>UTTypeConformsTo</key>
+ <array>
+ <string>public.text</string>
+ </array>
+ <key>UTTypeDescription</key>
+ <string>HTML</string>
+ <key>UTTypeIdentifier</key>
+ <string>public.html</string>
+ <key>UTTypeTagSpecification</key>
+ <dict>
+ <key>public.filename-extension</key>
+ <array>
+ <string>html</string>
+ <string>htm</string>
+ </array>
+ <key>public.mime-type</key>
+ <array>
+ <string>text/html</string>
+ </array>
+ </dict>
+ </dict>
+ <dict>
+ <key>UTTypeConformsTo</key>
+ <array>
+ <string>public.source-code</string>
+ </array>
+ <key>UTTypeDescription</key>
+ <string>CSS</string>
+ <key>UTTypeIdentifier</key>
+ <string>org.w3.css</string>
+ <key>UTTypeTagSpecification</key>
+ <dict>
+ <key>public.filename-extension</key>
+ <array>
+ <string>css</string>
+ </array>
+ <key>public.mime-type</key>
+ <array>
+ <string>text/css</string>
+ </array>
+ </dict>
+ </dict>
+ </array>
+</dict>
+</plist>
diff --git a/frontends/cocoa/res/NetSurf.icns b/frontends/cocoa/res/NetSurf.icns
new file mode 100644
index 000000000..654942373
--- /dev/null
+++ b/frontends/cocoa/res/NetSurf.icns
Binary files differ
diff --git a/frontends/cocoa/res/PreferencesWindow.xib b/frontends/cocoa/res/PreferencesWindow.xib
new file mode 100644
index 000000000..c4ea6b3b6
--- /dev/null
+++ b/frontends/cocoa/res/PreferencesWindow.xib
@@ -0,0 +1,512 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.03">
+ <data>
+ <int key="IBDocument.SystemTarget">1060</int>
+ <string key="IBDocument.SystemVersion">9L31a</string>
+ <string key="IBDocument.InterfaceBuilderVersion">680</string>
+ <string key="IBDocument.AppKitVersion">949.54</string>
+ <string key="IBDocument.HIToolboxVersion">353.00</string>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="2"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomObject" id="1001">
+ <string key="NSClassName">PreferencesWindowController</string>
+ </object>
+ <object class="NSCustomObject" id="1003">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1004">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSWindowTemplate" id="1005">
+ <int key="NSWindowStyleMask">7</int>
+ <int key="NSWindowBacking">2</int>
+ <string key="NSWindowRect">{{196, 362}, {455, 148}}</string>
+ <int key="NSWTFlags">1618477056</int>
+ <string key="NSWindowTitle">Preferences</string>
+ <string key="NSWindowClass">NSWindow</string>
+ <nil key="NSViewClass"/>
+ <string key="NSWindowContentMaxSize">{3.40282e+38, 3.40282e+38}</string>
+ <object class="NSView" key="NSWindowView" id="1006">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">256</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSTextField" id="343066491">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{98, 106}, {337, 22}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="218234675">
+ <int key="NSCellFlags">-1804468671</int>
+ <int key="NSCellFlags2">272630784</int>
+ <string key="NSContents"/>
+ <object class="NSFont" key="NSSupport" id="1015231142">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">1.300000e+01</double>
+ <int key="NSfFlags">1044</int>
+ </object>
+ <reference key="NSControlView" ref="343066491"/>
+ <bool key="NSDrawsBackground">YES</bool>
+ <object class="NSColor" key="NSBackgroundColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">textBackgroundColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MQA</bytes>
+ </object>
+ </object>
+ <object class="NSColor" key="NSTextColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">textColor</string>
+ <object class="NSColor" key="NSColor" id="751207648">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MAA</bytes>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSTextField" id="662601838">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{17, 108}, {76, 17}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="633183289">
+ <int key="NSCellFlags">68288064</int>
+ <int key="NSCellFlags2">272630784</int>
+ <string key="NSContents">Homepage:</string>
+ <reference key="NSSupport" ref="1015231142"/>
+ <reference key="NSControlView" ref="662601838"/>
+ <object class="NSColor" key="NSBackgroundColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MC42NjY2NjY2OQA</bytes>
+ </object>
+ </object>
+ <object class="NSColor" key="NSTextColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlTextColor</string>
+ <reference key="NSColor" ref="751207648"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSButton" id="272890940">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{222, 70}, {219, 32}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="136693114">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents">Use current page</string>
+ <reference key="NSSupport" ref="1015231142"/>
+ <reference key="NSControlView" ref="272890940"/>
+ <int key="NSButtonFlags">-2038284033</int>
+ <int key="NSButtonFlags2">129</int>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">200</int>
+ <int key="NSPeriodicInterval">25</int>
+ </object>
+ </object>
+ <object class="NSButton" id="748375959">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{96, 38}, {341, 18}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="497902592">
+ <int key="NSCellFlags">-2080244224</int>
+ <int key="NSCellFlags2">0</int>
+ <string key="NSContents">Cancel downloads without asking</string>
+ <reference key="NSSupport" ref="1015231142"/>
+ <reference key="NSControlView" ref="748375959"/>
+ <int key="NSButtonFlags">1211912703</int>
+ <int key="NSButtonFlags2">2</int>
+ <object class="NSCustomResource" key="NSNormalImage" id="118883570">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSSwitch</string>
+ </object>
+ <object class="NSButtonImageSource" key="NSAlternateImage" id="21608943">
+ <string key="NSImageName">NSSwitch</string>
+ </object>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">200</int>
+ <int key="NSPeriodicInterval">25</int>
+ </object>
+ </object>
+ <object class="NSButton" id="832107887">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{96, 18}, {341, 18}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="616812964">
+ <int key="NSCellFlags">-2080244224</int>
+ <int key="NSCellFlags2">0</int>
+ <string key="NSContents">Close multiple tabs without asking</string>
+ <reference key="NSSupport" ref="1015231142"/>
+ <reference key="NSControlView" ref="832107887"/>
+ <int key="NSButtonFlags">1211912703</int>
+ <int key="NSButtonFlags2">2</int>
+ <reference key="NSNormalImage" ref="118883570"/>
+ <reference key="NSAlternateImage" ref="21608943"/>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">200</int>
+ <int key="NSPeriodicInterval">25</int>
+ </object>
+ </object>
+ </object>
+ <string key="NSFrameSize">{455, 148}</string>
+ <reference key="NSSuperview"/>
+ </object>
+ <string key="NSScreenRect">{{0, 0}, {1680, 1028}}</string>
+ <string key="NSMaxSize">{3.40282e+38, 3.40282e+38}</string>
+ </object>
+ <object class="NSUserDefaultsController" id="562818373">
+ <bool key="NSSharedInstance">YES</bool>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">window</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="1005"/>
+ </object>
+ <int key="connectionID">3</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">value: values.AlwaysCancelDownload</string>
+ <reference key="source" ref="748375959"/>
+ <reference key="destination" ref="562818373"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="748375959"/>
+ <reference key="NSDestination" ref="562818373"/>
+ <string key="NSLabel">value: values.AlwaysCancelDownload</string>
+ <string key="NSBinding">value</string>
+ <string key="NSKeyPath">values.AlwaysCancelDownload</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">21</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">value: values.AlwaysCloseMultipleTabs</string>
+ <reference key="source" ref="832107887"/>
+ <reference key="destination" ref="562818373"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="832107887"/>
+ <reference key="NSDestination" ref="562818373"/>
+ <string key="NSLabel">value: values.AlwaysCloseMultipleTabs</string>
+ <string key="NSBinding">value</string>
+ <string key="NSKeyPath">values.AlwaysCloseMultipleTabs</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">22</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">useCurrentPageAsHomepage:</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="272890940"/>
+ </object>
+ <int key="connectionID">23</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">value: homepageURL</string>
+ <reference key="source" ref="343066491"/>
+ <reference key="destination" ref="1001"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="343066491"/>
+ <reference key="NSDestination" ref="1001"/>
+ <string key="NSLabel">value: homepageURL</string>
+ <string key="NSBinding">value</string>
+ <string key="NSKeyPath">homepageURL</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">25</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <object class="NSArray" key="object" id="854144324">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="children" ref="1000"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1001"/>
+ <reference key="parent" ref="854144324"/>
+ <string type="base64-UTF8" key="objectName">RmlsZSdzIE93bmVyA</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1003"/>
+ <reference key="parent" ref="854144324"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1004"/>
+ <reference key="parent" ref="854144324"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">1</int>
+ <reference key="object" ref="1005"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1006"/>
+ </object>
+ <reference key="parent" ref="854144324"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">2</int>
+ <reference key="object" ref="1006"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="343066491"/>
+ <reference ref="662601838"/>
+ <reference ref="272890940"/>
+ <reference ref="748375959"/>
+ <reference ref="832107887"/>
+ </object>
+ <reference key="parent" ref="1005"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">9</int>
+ <reference key="object" ref="343066491"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="218234675"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">10</int>
+ <reference key="object" ref="218234675"/>
+ <reference key="parent" ref="343066491"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">11</int>
+ <reference key="object" ref="662601838"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="633183289"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">12</int>
+ <reference key="object" ref="633183289"/>
+ <reference key="parent" ref="662601838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">13</int>
+ <reference key="object" ref="272890940"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="136693114"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">14</int>
+ <reference key="object" ref="136693114"/>
+ <reference key="parent" ref="272890940"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">15</int>
+ <reference key="object" ref="748375959"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="497902592"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">16</int>
+ <reference key="object" ref="497902592"/>
+ <reference key="parent" ref="748375959"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">17</int>
+ <reference key="object" ref="832107887"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="616812964"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">18</int>
+ <reference key="object" ref="616812964"/>
+ <reference key="parent" ref="832107887"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">19</int>
+ <reference key="object" ref="562818373"/>
+ <reference key="parent" ref="854144324"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>1.IBEditorWindowLastContentRect</string>
+ <string>1.IBPluginDependency</string>
+ <string>1.IBWindowTemplateEditedContentRect</string>
+ <string>1.NSWindowTemplate.visibleAtLaunch</string>
+ <string>1.WindowOrigin</string>
+ <string>1.editorWindowContentRectSynchronizationRect</string>
+ <string>10.IBPluginDependency</string>
+ <string>11.IBPluginDependency</string>
+ <string>11.IBViewBoundsToFrameTransform</string>
+ <string>12.IBPluginDependency</string>
+ <string>13.IBPluginDependency</string>
+ <string>13.IBViewBoundsToFrameTransform</string>
+ <string>14.IBPluginDependency</string>
+ <string>15.IBPluginDependency</string>
+ <string>16.IBPluginDependency</string>
+ <string>17.IBPluginDependency</string>
+ <string>17.IBViewBoundsToFrameTransform</string>
+ <string>18.IBPluginDependency</string>
+ <string>2.IBPluginDependency</string>
+ <string>9.IBPluginDependency</string>
+ <string>9.IBViewBoundsToFrameTransform</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>{{176, 384}, {455, 148}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{176, 384}, {455, 148}}</string>
+ <integer value="1"/>
+ <string>{196, 240}</string>
+ <string>{{202, 428}, {480, 270}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABCYAAAw2sAAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABClgAAw3wAAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABCwAAAwggAAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABDSQAAw2sAAA</bytes>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">25</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabDragAssistant.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabBarCell.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabBarControl.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">PreferencesWindowController</string>
+ <string key="superclassName">NSWindowController</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PreferencesWindowController.h</string>
+ </object>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../NetSurf.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ </data>
+</archive>
diff --git a/frontends/cocoa/res/SearchWindow.xib b/frontends/cocoa/res/SearchWindow.xib
new file mode 100644
index 000000000..8e9315b51
--- /dev/null
+++ b/frontends/cocoa/res/SearchWindow.xib
@@ -0,0 +1,614 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.03">
+ <data>
+ <int key="IBDocument.SystemTarget">1060</int>
+ <string key="IBDocument.SystemVersion">9L31a</string>
+ <string key="IBDocument.InterfaceBuilderVersion">680</string>
+ <string key="IBDocument.AppKitVersion">949.54</string>
+ <string key="IBDocument.HIToolboxVersion">353.00</string>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="2"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomObject" id="1001">
+ <string key="NSClassName">SearchWindowController</string>
+ </object>
+ <object class="NSCustomObject" id="1003">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1004">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSWindowTemplate" id="1005">
+ <int key="NSWindowStyleMask">7</int>
+ <int key="NSWindowBacking">2</int>
+ <string key="NSWindowRect">{{196, 354}, {480, 156}}</string>
+ <int key="NSWTFlags">1618477056</int>
+ <string key="NSWindowTitle">Search</string>
+ <string key="NSWindowClass">NSWindow</string>
+ <nil key="NSViewClass"/>
+ <string key="NSWindowContentMaxSize">{3.40282e+38, 3.40282e+38}</string>
+ <object class="NSView" key="NSWindowView" id="1006">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">256</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSTextField" id="656125083">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{67, 114}, {393, 22}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="175732045">
+ <int key="NSCellFlags">-1803944383</int>
+ <int key="NSCellFlags2">272630784</int>
+ <string key="NSContents"/>
+ <object class="NSFont" key="NSSupport" id="926601808">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">1.300000e+01</double>
+ <int key="NSfFlags">1044</int>
+ </object>
+ <reference key="NSControlView" ref="656125083"/>
+ <bool key="NSDrawsBackground">YES</bool>
+ <object class="NSColor" key="NSBackgroundColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">textBackgroundColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MQA</bytes>
+ </object>
+ </object>
+ <object class="NSColor" key="NSTextColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">textColor</string>
+ <object class="NSColor" key="NSColor" id="92132461">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MAA</bytes>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSTextField" id="90768291">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{17, 116}, {45, 14}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="874604921">
+ <int key="NSCellFlags">68288064</int>
+ <int key="NSCellFlags2">71435264</int>
+ <string key="NSContents">Find:</string>
+ <object class="NSFont" key="NSSupport">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">1.100000e+01</double>
+ <int key="NSfFlags">3100</int>
+ </object>
+ <reference key="NSControlView" ref="90768291"/>
+ <object class="NSColor" key="NSBackgroundColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MC42NjY2NjY2OQA</bytes>
+ </object>
+ </object>
+ <object class="NSColor" key="NSTextColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlTextColor</string>
+ <reference key="NSColor" ref="92132461"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSButton" id="1016680565">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{370, 12}, {96, 32}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="567819516">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents">Next</string>
+ <reference key="NSSupport" ref="926601808"/>
+ <reference key="NSControlView" ref="1016680565"/>
+ <int key="NSButtonFlags">-2038284033</int>
+ <int key="NSButtonFlags2">129</int>
+ <string key="NSAlternateContents"/>
+ <string type="base64-UTF8" key="NSKeyEquivalent">DQ</string>
+ <int key="NSPeriodicDelay">200</int>
+ <int key="NSPeriodicInterval">25</int>
+ </object>
+ </object>
+ <object class="NSButton" id="88766619">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{274, 12}, {96, 32}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="450885469">
+ <int key="NSCellFlags">-2080244224</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents">Previous</string>
+ <reference key="NSSupport" ref="926601808"/>
+ <reference key="NSControlView" ref="88766619"/>
+ <int key="NSButtonFlags">-2038284033</int>
+ <int key="NSButtonFlags2">129</int>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">200</int>
+ <int key="NSPeriodicInterval">25</int>
+ </object>
+ </object>
+ <object class="NSButton" id="650505141">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{139, 90}, {323, 18}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="657985060">
+ <int key="NSCellFlags">-2080244224</int>
+ <int key="NSCellFlags2">0</int>
+ <string key="NSContents">Case sensitive</string>
+ <reference key="NSSupport" ref="926601808"/>
+ <reference key="NSControlView" ref="650505141"/>
+ <int key="NSButtonFlags">1211912703</int>
+ <int key="NSButtonFlags2">2</int>
+ <object class="NSCustomResource" key="NSNormalImage" id="312987126">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSSwitch</string>
+ </object>
+ <object class="NSButtonImageSource" key="NSAlternateImage" id="973359313">
+ <string key="NSImageName">NSSwitch</string>
+ </object>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">200</int>
+ <int key="NSPeriodicInterval">25</int>
+ </object>
+ </object>
+ <object class="NSButton" id="331870754">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{139, 70}, {323, 18}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="239506938">
+ <int key="NSCellFlags">-2080244224</int>
+ <int key="NSCellFlags2">0</int>
+ <string key="NSContents">Select all</string>
+ <reference key="NSSupport" ref="926601808"/>
+ <reference key="NSControlView" ref="331870754"/>
+ <int key="NSButtonFlags">1211912703</int>
+ <int key="NSButtonFlags2">2</int>
+ <reference key="NSNormalImage" ref="312987126"/>
+ <reference key="NSAlternateImage" ref="973359313"/>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">200</int>
+ <int key="NSPeriodicInterval">25</int>
+ </object>
+ </object>
+ </object>
+ <string key="NSFrameSize">{480, 156}</string>
+ <reference key="NSSuperview"/>
+ </object>
+ <string key="NSScreenRect">{{0, 0}, {1680, 1028}}</string>
+ <string key="NSMaxSize">{3.40282e+38, 3.40282e+38}</string>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">searchNext:</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="1016680565"/>
+ </object>
+ <int key="connectionID">15</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">searchPrevious:</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="88766619"/>
+ </object>
+ <int key="connectionID">16</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">enabled: canGoForward</string>
+ <reference key="source" ref="1016680565"/>
+ <reference key="destination" ref="1001"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="1016680565"/>
+ <reference key="NSDestination" ref="1001"/>
+ <string key="NSLabel">enabled: canGoForward</string>
+ <string key="NSBinding">enabled</string>
+ <string key="NSKeyPath">canGoForward</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">17</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">enabled: canGoBack</string>
+ <reference key="source" ref="88766619"/>
+ <reference key="destination" ref="1001"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="88766619"/>
+ <reference key="NSDestination" ref="1001"/>
+ <string key="NSLabel">enabled: canGoBack</string>
+ <string key="NSBinding">enabled</string>
+ <string key="NSKeyPath">canGoBack</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">18</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">searchStringDidChange:</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="656125083"/>
+ </object>
+ <int key="connectionID">22</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">window</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="1005"/>
+ </object>
+ <int key="connectionID">23</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">value: searchString</string>
+ <reference key="source" ref="656125083"/>
+ <reference key="destination" ref="1001"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="656125083"/>
+ <reference key="NSDestination" ref="1001"/>
+ <string key="NSLabel">value: searchString</string>
+ <string key="NSBinding">value</string>
+ <string key="NSKeyPath">searchString</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">24</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">value: caseSensitive</string>
+ <reference key="source" ref="650505141"/>
+ <reference key="destination" ref="1001"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="650505141"/>
+ <reference key="NSDestination" ref="1001"/>
+ <string key="NSLabel">value: caseSensitive</string>
+ <string key="NSBinding">value</string>
+ <string key="NSKeyPath">caseSensitive</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">25</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">value: selectAll</string>
+ <reference key="source" ref="331870754"/>
+ <reference key="destination" ref="1001"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="331870754"/>
+ <reference key="NSDestination" ref="1001"/>
+ <string key="NSLabel">value: selectAll</string>
+ <string key="NSBinding">value</string>
+ <string key="NSKeyPath">selectAll</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">27</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <object class="NSArray" key="object" id="729119712">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="children" ref="1000"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1001"/>
+ <reference key="parent" ref="729119712"/>
+ <string type="base64-UTF8" key="objectName">RmlsZSdzIE93bmVyA</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1003"/>
+ <reference key="parent" ref="729119712"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1004"/>
+ <reference key="parent" ref="729119712"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">1</int>
+ <reference key="object" ref="1005"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1006"/>
+ </object>
+ <reference key="parent" ref="729119712"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">2</int>
+ <reference key="object" ref="1006"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="656125083"/>
+ <reference ref="90768291"/>
+ <reference ref="1016680565"/>
+ <reference ref="88766619"/>
+ <reference ref="331870754"/>
+ <reference ref="650505141"/>
+ </object>
+ <reference key="parent" ref="1005"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">3</int>
+ <reference key="object" ref="656125083"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="175732045"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">4</int>
+ <reference key="object" ref="175732045"/>
+ <reference key="parent" ref="656125083"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5</int>
+ <reference key="object" ref="90768291"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="874604921"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">6</int>
+ <reference key="object" ref="874604921"/>
+ <reference key="parent" ref="90768291"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">7</int>
+ <reference key="object" ref="1016680565"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="567819516"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">8</int>
+ <reference key="object" ref="567819516"/>
+ <reference key="parent" ref="1016680565"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">9</int>
+ <reference key="object" ref="88766619"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="450885469"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">10</int>
+ <reference key="object" ref="450885469"/>
+ <reference key="parent" ref="88766619"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">11</int>
+ <reference key="object" ref="650505141"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="657985060"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">12</int>
+ <reference key="object" ref="657985060"/>
+ <reference key="parent" ref="650505141"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">13</int>
+ <reference key="object" ref="331870754"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="239506938"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">14</int>
+ <reference key="object" ref="239506938"/>
+ <reference key="parent" ref="331870754"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>1.IBEditorWindowLastContentRect</string>
+ <string>1.IBPluginDependency</string>
+ <string>1.IBWindowTemplateEditedContentRect</string>
+ <string>1.NSWindowTemplate.visibleAtLaunch</string>
+ <string>1.WindowOrigin</string>
+ <string>1.editorWindowContentRectSynchronizationRect</string>
+ <string>10.IBPluginDependency</string>
+ <string>11.IBPluginDependency</string>
+ <string>11.IBViewBoundsToFrameTransform</string>
+ <string>12.IBPluginDependency</string>
+ <string>13.IBPluginDependency</string>
+ <string>13.IBViewBoundsToFrameTransform</string>
+ <string>14.IBPluginDependency</string>
+ <string>2.IBPluginDependency</string>
+ <string>3.IBPluginDependency</string>
+ <string>4.IBPluginDependency</string>
+ <string>5.IBPluginDependency</string>
+ <string>5.IBViewBoundsToFrameTransform</string>
+ <string>6.IBPluginDependency</string>
+ <string>7.IBPluginDependency</string>
+ <string>7.IBViewBoundsToFrameTransform</string>
+ <string>8.IBPluginDependency</string>
+ <string>9.IBPluginDependency</string>
+ <string>9.IBViewBoundsToFrameTransform</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>{{185, 223}, {480, 156}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{185, 223}, {480, 156}}</string>
+ <boolean value="NO"/>
+ <string>{196, 240}</string>
+ <string>{{202, 428}, {480, 270}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABDCwAAwpQAAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABDk4AAwpQAAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABBiAAAw3IAAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABDuQAAwigAAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABDiQAAwigAAA</bytes>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">27</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabDragAssistant.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabBarCell.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">PSMTabBarControl/PSMTabBarControl.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">SearchWindowController</string>
+ <string key="superclassName">NSWindowController</string>
+ <object class="NSMutableDictionary" key="actions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>searchNext:</string>
+ <string>searchPrevious:</string>
+ <string>searchStringDidChange:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">SearchWindowController.h</string>
+ </object>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../NetSurf.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ </data>
+</archive>
diff --git a/frontends/cocoa/res/adblock.css b/frontends/cocoa/res/adblock.css
new file mode 120000
index 000000000..ff2485622
--- /dev/null
+++ b/frontends/cocoa/res/adblock.css
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/AdBlock,f79 \ No newline at end of file
diff --git a/frontends/cocoa/res/ca-bundle b/frontends/cocoa/res/ca-bundle
new file mode 120000
index 000000000..0b0e416ad
--- /dev/null
+++ b/frontends/cocoa/res/ca-bundle
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/ca-bundle \ No newline at end of file
diff --git a/frontends/cocoa/res/de.lproj/BookmarksWindow.xib.strings b/frontends/cocoa/res/de.lproj/BookmarksWindow.xib.strings
new file mode 100644
index 000000000..5fa44a11e
--- /dev/null
+++ b/frontends/cocoa/res/de.lproj/BookmarksWindow.xib.strings
Binary files differ
diff --git a/frontends/cocoa/res/de.lproj/BrowserWindow.xib.strings b/frontends/cocoa/res/de.lproj/BrowserWindow.xib.strings
new file mode 100644
index 000000000..a0c782cd6
--- /dev/null
+++ b/frontends/cocoa/res/de.lproj/BrowserWindow.xib.strings
Binary files differ
diff --git a/frontends/cocoa/res/de.lproj/DownloadWindow.xib.strings b/frontends/cocoa/res/de.lproj/DownloadWindow.xib.strings
new file mode 100644
index 000000000..d92040249
--- /dev/null
+++ b/frontends/cocoa/res/de.lproj/DownloadWindow.xib.strings
Binary files differ
diff --git a/frontends/cocoa/res/de.lproj/HistoryWindow.xib.strings b/frontends/cocoa/res/de.lproj/HistoryWindow.xib.strings
new file mode 100644
index 000000000..c43bc418e
--- /dev/null
+++ b/frontends/cocoa/res/de.lproj/HistoryWindow.xib.strings
Binary files differ
diff --git a/frontends/cocoa/res/de.lproj/Localizable.strings b/frontends/cocoa/res/de.lproj/Localizable.strings
new file mode 100644
index 000000000..04180f485
--- /dev/null
+++ b/frontends/cocoa/res/de.lproj/Localizable.strings
@@ -0,0 +1,78 @@
+/* ... of (total size) */
+" of %@" = " von %@";
+
+/* time remaining: hours, minutes */
+"%2:%02u hours" = "%1$:%2$u Stunden";
+
+/* time remaining */
+"%u seconds" = "%u Sekunden";
+
+/* time remaining: minutes, seconds */
+"%u:%02u minutes" = "%1$u:%2$u Minuten";
+
+/* Context menu */
+"Back" = "Zurück";
+
+/* Download */
+"Cancel download?" = "Download abbrechen?";
+
+/* Context menu */
+"Copy image" = "Bild kopieren";
+
+/* Context menu */
+"Copy link" = "Link kopieren";
+
+/* No comment provided by engineer. */
+"Do you really want to close this window?" = "Wollen Sie dieses Fenster wirklich schließen?";
+
+/* show error */
+"Error" = "Fehler";
+
+/* Context menu */
+"Forward" = "Vorwärts";
+
+/* time remaining */
+"less than 10 seconds" = "weniger als 10 Sekunden";
+
+/* 'No' button */
+"No" = "Nein";
+
+/* 'OK' button */
+"OK" = "OK";
+
+/* Context menu */
+"Open image in new tab" = "Bild in neuem Tab öffnen";
+
+/* Context menu */
+"Open image in new window" = "Bild in neuem Fenster öffnen";
+
+/* Context menu */
+"Open link in new tab" = "Link in neuem Tab öffnen";
+
+/* Context menu */
+"Open link in new window" = "Link in neuem Fenster öffnen";
+
+/* Context menu */
+"Reload" = "Neu laden";
+
+/* Context menu */
+"Save image as" = "Bild speichern";
+
+/* Context menu */
+"Save link target" = "Linkziel speichern";
+
+/* Download */
+"Should the download of '%@' really be cancelled?" = "Soll der Download von '%@' wirklich abgebrochen werden?";
+
+/* No comment provided by engineer. */
+"There are %d tabs open, do you want to close them all?" = "Es sind noch %d tabs offen. Sollen alle geschlossen werden?";
+
+/* Warning title */
+"Warning" = "Warnung";
+
+/* Warning message */
+"Warning %s%s%s" = "Warnung %1$s%2$s%3$s";
+
+/* 'Yes' button */
+"Yes" = "Ja";
+
diff --git a/frontends/cocoa/res/de.lproj/MainMenu.xib.strings b/frontends/cocoa/res/de.lproj/MainMenu.xib.strings
new file mode 100644
index 000000000..3fc91c48c
--- /dev/null
+++ b/frontends/cocoa/res/de.lproj/MainMenu.xib.strings
Binary files differ
diff --git a/frontends/cocoa/res/de.lproj/Messages b/frontends/cocoa/res/de.lproj/Messages
new file mode 120000
index 000000000..32530a151
--- /dev/null
+++ b/frontends/cocoa/res/de.lproj/Messages
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/de/Messages \ No newline at end of file
diff --git a/frontends/cocoa/res/de.lproj/PreferencesWindow.xib.strings b/frontends/cocoa/res/de.lproj/PreferencesWindow.xib.strings
new file mode 100644
index 000000000..fbe9762f2
--- /dev/null
+++ b/frontends/cocoa/res/de.lproj/PreferencesWindow.xib.strings
Binary files differ
diff --git a/frontends/cocoa/res/de.lproj/SearchWindow.xib.strings b/frontends/cocoa/res/de.lproj/SearchWindow.xib.strings
new file mode 100644
index 000000000..31fdea76b
--- /dev/null
+++ b/frontends/cocoa/res/de.lproj/SearchWindow.xib.strings
Binary files differ
diff --git a/frontends/cocoa/res/default.css b/frontends/cocoa/res/default.css
new file mode 120000
index 000000000..a8579eb7c
--- /dev/null
+++ b/frontends/cocoa/res/default.css
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/CSS,f79 \ No newline at end of file
diff --git a/frontends/cocoa/res/en.lproj/Localizable.strings b/frontends/cocoa/res/en.lproj/Localizable.strings
new file mode 100644
index 000000000..9ac7a7b4d
--- /dev/null
+++ b/frontends/cocoa/res/en.lproj/Localizable.strings
Binary files differ
diff --git a/frontends/cocoa/res/en.lproj/Messages b/frontends/cocoa/res/en.lproj/Messages
new file mode 120000
index 000000000..a26483244
--- /dev/null
+++ b/frontends/cocoa/res/en.lproj/Messages
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/en/Messages \ No newline at end of file
diff --git a/frontends/cocoa/res/fr.lproj/Localizable.strings b/frontends/cocoa/res/fr.lproj/Localizable.strings
new file mode 100644
index 000000000..9ac7a7b4d
--- /dev/null
+++ b/frontends/cocoa/res/fr.lproj/Localizable.strings
Binary files differ
diff --git a/frontends/cocoa/res/fr.lproj/Messages b/frontends/cocoa/res/fr.lproj/Messages
new file mode 120000
index 000000000..467559b12
--- /dev/null
+++ b/frontends/cocoa/res/fr.lproj/Messages
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/fr/Messages \ No newline at end of file
diff --git a/frontends/cocoa/res/internal.css b/frontends/cocoa/res/internal.css
new file mode 120000
index 000000000..17f9f1504
--- /dev/null
+++ b/frontends/cocoa/res/internal.css
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/internal.css,f79 \ No newline at end of file
diff --git a/frontends/cocoa/res/it.lproj/Localizable.strings b/frontends/cocoa/res/it.lproj/Localizable.strings
new file mode 100644
index 000000000..5927796a6
--- /dev/null
+++ b/frontends/cocoa/res/it.lproj/Localizable.strings
Binary files differ
diff --git a/frontends/cocoa/res/it.lproj/Messages b/frontends/cocoa/res/it.lproj/Messages
new file mode 120000
index 000000000..00fc6d1ed
--- /dev/null
+++ b/frontends/cocoa/res/it.lproj/Messages
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/it/Messages \ No newline at end of file
diff --git a/frontends/cocoa/res/netsurf.png b/frontends/cocoa/res/netsurf.png
new file mode 120000
index 000000000..905512c25
--- /dev/null
+++ b/frontends/cocoa/res/netsurf.png
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/netsurf.png,b60 \ No newline at end of file
diff --git a/frontends/cocoa/res/nl.lproj/Localizable.strings b/frontends/cocoa/res/nl.lproj/Localizable.strings
new file mode 100644
index 000000000..9ac7a7b4d
--- /dev/null
+++ b/frontends/cocoa/res/nl.lproj/Localizable.strings
Binary files differ
diff --git a/frontends/cocoa/res/nl.lproj/Messages b/frontends/cocoa/res/nl.lproj/Messages
new file mode 120000
index 000000000..c8a9cbe81
--- /dev/null
+++ b/frontends/cocoa/res/nl.lproj/Messages
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/nl/Messages \ No newline at end of file
diff --git a/frontends/cocoa/res/quirks.css b/frontends/cocoa/res/quirks.css
new file mode 120000
index 000000000..88aabe48c
--- /dev/null
+++ b/frontends/cocoa/res/quirks.css
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/Quirks,f79 \ No newline at end of file
diff --git a/frontends/cocoa/schedule.h b/frontends/cocoa/schedule.h
new file mode 100644
index 000000000..43b2c1462
--- /dev/null
+++ b/frontends/cocoa/schedule.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+nserror cocoa_schedule(int t, void (*callback)(void *p), void *p);
diff --git a/frontends/cocoa/schedule.m b/frontends/cocoa/schedule.m
new file mode 100644
index 000000000..f0896bd9d
--- /dev/null
+++ b/frontends/cocoa/schedule.m
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#import "utils/errors.h"
+
+#import "cocoa/schedule.h"
+
+@interface ScheduledCallback : NSObject {
+ void (*callback)( void *userData );
+ void *userData;
+}
+
+- initWithCallback: (void (*)(void *))cb userData: (void *)ud;
+- (void) schedule: (NSTimeInterval) ti;
+
+@end
+
+@implementation ScheduledCallback
+
+- initWithCallback: (void (*)(void *))cb userData: (void *)ud;
+{
+ callback = cb;
+ userData = ud;
+
+ return self;
+}
+
+static NSMutableSet *timerSet = nil;
+
+- (void) schedule: (NSTimeInterval) ti;
+{
+ if (nil == timerSet) {
+ timerSet = [[NSMutableSet alloc] init];
+ }
+
+ [self performSelector: @selector(timerFired) withObject: nil afterDelay: ti];
+ [timerSet addObject: self];
+}
+
+- (void) timerFired;
+{
+ if ([timerSet containsObject: self]) {
+ [timerSet removeObject: self];
+ callback( userData );
+ }
+}
+
+- (BOOL) isEqual: (id)object
+{
+ if (object == self) return YES;
+ if ([object class] != [self class]) return NO;
+ return ((ScheduledCallback *)object)->callback == callback && ((ScheduledCallback *)object)->userData == userData;
+}
+
+- (NSUInteger) hash;
+{
+ return (NSUInteger)callback + (NSUInteger)userData;
+}
+
+@end
+
+/* exported interface documented in cocoa/schedule.h */
+nserror cocoa_schedule(int t, void (*callback)(void *p), void *p)
+{
+ ScheduledCallback *cb = [[ScheduledCallback alloc] initWithCallback: callback userData: p];
+ [timerSet removeObject: cb];
+ if (t >= 0) {
+ [cb schedule: (NSTimeInterval)t / 1000];
+ }
+ [cb release];
+
+ return NSERROR_OK;
+}
diff --git a/frontends/cocoa/selection.h b/frontends/cocoa/selection.h
new file mode 100644
index 000000000..67331ea83
--- /dev/null
+++ b/frontends/cocoa/selection.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+struct gui_clipboard_table *cocoa_clipboard_table;
diff --git a/frontends/cocoa/selection.m b/frontends/cocoa/selection.m
new file mode 100644
index 000000000..808b70683
--- /dev/null
+++ b/frontends/cocoa/selection.m
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#import "cocoa/BrowserViewController.h"
+#import "cocoa/selection.h"
+
+#import "desktop/gui_clipboard.h"
+
+
+static NSMutableString *cocoa_clipboard_string;
+
+/**
+ * Core asks front end for clipboard contents.
+ *
+ * \param buffer UTF-8 text, allocated by front end, ownership yeilded to core
+ * \param length Byte length of UTF-8 text in buffer
+ */
+static void gui_get_clipboard(char **buffer, size_t *length)
+{
+ NSPasteboard *pb = [NSPasteboard generalPasteboard];
+ NSString *string = [pb stringForType: NSStringPboardType];
+
+ *buffer = NULL;
+ *length = 0;
+
+ if (string) {
+ const char *text = [string UTF8String];
+ *length = strlen(text);
+
+ *buffer = malloc(*length);
+
+ if (*buffer != NULL) {
+ memcpy(*buffer, text, *length);
+ } else {
+ *length = 0;
+ }
+ }
+}
+
+/**
+ * Core tells front end to put given text in clipboard
+ *
+ * \param buffer UTF-8 text, owned by core
+ * \param length Byte length of UTF-8 text in buffer
+ * \param styles Array of styles given to text runs, owned by core, or NULL
+ * \param n_styles Number of text run styles in array
+ */
+static void gui_set_clipboard(const char *buffer, size_t length,
+ nsclipboard_styles styles[], int n_styles)
+{
+ /* Empty clipboard string */
+ if (nil == cocoa_clipboard_string) {
+ cocoa_clipboard_string = [[NSMutableString alloc] init];
+ } else {
+ [cocoa_clipboard_string setString: @""];
+ }
+
+ /* Add text to clipboard string */
+ if (nil == cocoa_clipboard_string) return;
+
+ [cocoa_clipboard_string appendString: [[[NSString alloc]
+ initWithBytes: buffer
+ length: length
+ encoding: NSUTF8StringEncoding]
+ autorelease]];
+
+ /* Stick it on the pasteboard */
+ NSPasteboard *pb = [NSPasteboard generalPasteboard];
+ [pb declareTypes: [NSArray arrayWithObject: NSStringPboardType] owner: nil];
+ bool result = [pb setString: cocoa_clipboard_string forType: NSStringPboardType];
+
+ if (result) {
+ /* Empty clipboard string */
+ if (nil == cocoa_clipboard_string) {
+ cocoa_clipboard_string = [[NSMutableString alloc] init];
+ } else {
+ [cocoa_clipboard_string setString: @""];
+ }
+ }
+}
+
+static struct gui_clipboard_table clipboard_table = {
+ .get = gui_get_clipboard,
+ .set = gui_set_clipboard,
+};
+
+struct gui_clipboard_table *cocoa_clipboard_table = &clipboard_table;
diff --git a/frontends/framebuffer/Makefile b/frontends/framebuffer/Makefile
new file mode 100644
index 000000000..15888aebc
--- /dev/null
+++ b/frontends/framebuffer/Makefile
@@ -0,0 +1,184 @@
+# ----------------------------------------------------------------------------
+# Framebuffer target setup
+# ----------------------------------------------------------------------------
+
+CFLAGS += -Dnsframebuffer
+
+#resource path
+CFLAGS += '-DNETSURF_FB_RESPATH="$(NETSURF_FB_RESPATH)"'
+
+# compile time font locations
+CFLAGS += '-DNETSURF_FB_FONTPATH="$(NETSURF_FB_FONTPATH)"'
+CFLAGS += '-DNETSURF_FB_FONT_SANS_SERIF="$(NETSURF_FB_FONT_SANS_SERIF)"'
+CFLAGS += '-DNETSURF_FB_FONT_SANS_SERIF_BOLD="$(NETSURF_FB_FONT_SANS_SERIF_BOLD)"'
+CFLAGS += '-DNETSURF_FB_FONT_SANS_SERIF_ITALIC="$(NETSURF_FB_FONT_SANS_SERIF_ITALIC)"'
+CFLAGS += '-DNETSURF_FB_FONT_SANS_SERIF_ITALIC_BOLD="$(NETSURF_FB_FONT_SANS_SERIF_ITALIC_BOLD)"'
+CFLAGS += '-DNETSURF_FB_FONT_SERIF="$(NETSURF_FB_FONT_SERIF)"'
+CFLAGS += '-DNETSURF_FB_FONT_SERIF_BOLD="$(NETSURF_FB_FONT_SERIF_BOLD)"'
+CFLAGS += '-DNETSURF_FB_FONT_MONOSPACE="$(NETSURF_FB_FONT_MONOSPACE)"'
+CFLAGS += '-DNETSURF_FB_FONT_MONOSPACE_BOLD="$(NETSURF_FB_FONT_MONOSPACE_BOLD)"'
+CFLAGS += '-DNETSURF_FB_FONT_CURSIVE="$(NETSURF_FB_FONT_CURSIVE)"'
+CFLAGS += '-DNETSURF_FB_FONT_FANTASY="$(NETSURF_FB_FONT_FANTASY)"'
+
+CFLAGS += -std=c99 -g -Dsmall \
+ -D_BSD_SOURCE \
+ -D_DEFAULT_SOURCE \
+ -D_XOPEN_SOURCE=600 \
+ -D_POSIX_C_SOURCE=200112L
+
+LDFLAGS += -lm
+
+# non optional pkg-configed libs
+LDFLAGS += -Wl,--whole-archive
+$(eval $(call pkg_config_find_and_add,libnsfb,libnsfb))
+LDFLAGS += -Wl,--no-whole-archive
+
+# freetype is optional but does not use pkg-config
+ifeq ($(NETSURF_FB_FONTLIB),freetype)
+ CFLAGS += -DFB_USE_FREETYPE $(shell freetype-config --cflags)
+ LDFLAGS += $(shell freetype-config --libs)
+endif
+
+
+# ----------------------------------------------------------------------------
+# built-in resource setup
+# ----------------------------------------------------------------------------
+
+FB_IMAGE_left_arrow := icons/back.png
+FB_IMAGE_right_arrow := icons/forward.png
+FB_IMAGE_reload := icons/reload.png
+FB_IMAGE_stop_image := icons/stop.png
+FB_IMAGE_history_image := icons/history.png
+
+FB_IMAGE_left_arrow_g := icons/back_g.png
+FB_IMAGE_right_arrow_g := icons/forward_g.png
+FB_IMAGE_reload_g := icons/reload_g.png
+FB_IMAGE_stop_image_g := icons/stop_g.png
+FB_IMAGE_history_image_g := icons/history_g.png
+
+FB_IMAGE_scrolll := icons/scrolll.png
+FB_IMAGE_scrollr := icons/scrollr.png
+FB_IMAGE_scrollu := icons/scrollu.png
+FB_IMAGE_scrolld := icons/scrolld.png
+
+FB_IMAGE_osk_image := icons/osk.png
+
+FB_IMAGE_pointer_image := pointers/default.png
+FB_IMAGE_hand_image := pointers/point.png
+FB_IMAGE_caret_image := pointers/caret.png
+FB_IMAGE_menu_image := pointers/menu.png
+FB_IMAGE_progress_image := pointers/progress.png
+FB_IMAGE_move_image := pointers/move.png
+
+FB_IMAGE_throbber0 := throbber/throbber0.png
+FB_IMAGE_throbber1 := throbber/throbber1.png
+FB_IMAGE_throbber2 := throbber/throbber2.png
+FB_IMAGE_throbber3 := throbber/throbber3.png
+FB_IMAGE_throbber4 := throbber/throbber4.png
+FB_IMAGE_throbber5 := throbber/throbber5.png
+FB_IMAGE_throbber6 := throbber/throbber6.png
+FB_IMAGE_throbber7 := throbber/throbber7.png
+FB_IMAGE_throbber8 := throbber/throbber8.png
+
+# local compiler flags
+ifeq ($(HOST),OpenBSD)
+ HOST_CFLAGS += $(shell $(PKG_CONFIG) --cflags libpng)
+ HOST_LDFLAGS += $(shell $(PKG_CONFIG) --libs libpng)
+else
+ HOST_CFLAGS +=
+ HOST_LDFLAGS += -lpng
+endif
+
+# Host tool to convert image bitmaps to source code.
+#
+# convert_image dependd on fb_bitmap.h so that if we change that
+# header, we get new images built.
+$(TOOLROOT)/convert_image: $(TOOLROOT)/created $(FRONTEND_SOURCE_DIR)/convert_image.c $(FRONTEND_SOURCE_DIR)/fbtk.h
+ $(VQ)echo " HOST CC: $@"
+ $(Q)$(HOST_CC) $(HOST_CFLAGS) -o $@ $(FRONTEND_SOURCE_DIR)/convert_image.c $(HOST_LDFLAGS)
+
+# 1: input file
+# 2: output file
+# 3: bitmap name
+define convert_image
+
+S_IMAGES += $(2)
+
+$(2): $(1) $(TOOLROOT)/convert_image
+ $(Q)$(TOOLROOT)/convert_image $(1) $(2) $(3)
+
+endef
+
+S_IMAGES :=
+
+$(eval $(foreach V,$(filter FB_IMAGE_%,$(.VARIABLES)),$(call convert_image,$(FRONTEND_RESOURCES_DIR)/$($(V)),$(OBJROOT)/image-$(patsubst FB_IMAGE_%,%,$(V)).c,$(patsubst FB_IMAGE_%,%,$(V)))))
+
+
+# Internal fonts to generate
+FB_FONT_internal_ns-sans := fonts/glyph_data
+
+# Internal font conversion
+$(TOOLROOT)/convert_font: $(TOOLROOT)/created $(FRONTEND_SOURCE_DIR)/convert_font.c
+ $(VQ)echo " HOST CC: $@"
+ $(Q)$(HOST_CC) -o $@ $(FRONTEND_SOURCE_DIR)/convert_font.c
+
+# 1: input file
+# 2: output source code file
+# 3: output header file
+# 4: font name
+define convert_font
+
+S_FONTS += $(2)
+
+$(2): $(1) $(TOOLROOT)/convert_font
+ $(VQ)echo " FONT: $(1) ($(4))"
+ $(Q)$(TOOLROOT)/convert_font -H $(3) $(1) $(2)
+
+endef
+
+S_FONTS :=
+
+$(eval $(foreach V,$(filter FB_FONT_$(NETSURF_FB_FONTLIB)_%,$(.VARIABLES)),$(call convert_font,$(FRONTEND_RESOURCES_DIR)/$($(V)),$(OBJROOT)/font-$(patsubst FB_FONT_$(NETSURF_FB_FONTLIB)_%,%,$(V)).c,$(OBJROOT)/font-$(patsubst FB_FONT_$(NETSURF_FB_FONTLIB)_%,%,$(V)).h,$(patsubst FB_FONT_$(NETSURF_FB_FONTLIB)_%,%,$(V)))))
+
+# ----------------------------------------------------------------------------
+# Source file setup
+# ----------------------------------------------------------------------------
+
+# S_FRONTEND are sources purely for the framebuffer build
+S_FRONTEND := gui.c framebuffer.c schedule.c bitmap.c fetch.c \
+ findfile.c localhistory.c clipboard.c
+
+# toolkit sources
+S_FRAMEBUFFER_FBTK := fbtk.c event.c fill.c bitmap.c user.c window.c \
+ text.c scroll.c osk.c
+
+S_FRONTEND += font_$(NETSURF_FB_FONTLIB).c
+
+S_FRONTEND += $(addprefix fbtk/,$(S_FRAMEBUFFER_FBTK))
+
+# This is the final source build list
+# Note this is deliberately *not* expanded here as common and image
+# are not yet available
+SOURCES = $(S_COMMON) $(S_IMAGE) $(S_BROWSER) $(S_FRONTEND) $(S_IMAGES) $(S_FONTS)
+EXETARGET := nsfb
+
+# ----------------------------------------------------------------------------
+# Install target
+# ----------------------------------------------------------------------------
+
+NETSURF_FRAMEBUFFER_RESOURCE_LIST := adblock.css credits.html \
+ default.css internal.css licence.html \
+ netsurf.png quirks.css welcome.html maps.html Messages
+
+install-framebuffer:
+ $(Q)mkdir -p $(DESTDIR)$(NETSURF_FRAMEBUFFER_BIN)
+ $(Q)mkdir -p $(DESTDIR)$(NETSURF_FRAMEBUFFER_RESOURCES)
+ $(Q)cp -v $(EXETARGET) $(DESTDIR)/$(NETSURF_FRAMEBUFFER_BIN)netsurf$(SUBTARGET)
+ $(Q)for F in $(NETSURF_FRAMEBUFFER_RESOURCE_LIST); do cp -vL framebuffer/res/$$F $(DESTDIR)/$(NETSURF_FRAMEBUFFER_RESOURCES); done
+ $(Q)$(SPLIT_MESSAGES) -l en -p fb -f messages resources/FatMessages | gzip -9n > $(DESTDIR)$(NETSURF_FRAMEBUFFER_RESOURCES)messages
+
+# ----------------------------------------------------------------------------
+# Package target
+# ----------------------------------------------------------------------------
+
+package-framebuffer:
diff --git a/frontends/framebuffer/Makefile.defaults b/frontends/framebuffer/Makefile.defaults
new file mode 100644
index 000000000..fe54daafe
--- /dev/null
+++ b/frontends/framebuffer/Makefile.defaults
@@ -0,0 +1,47 @@
+# ----------------------------------------------------------------------------
+# Framebuffer-target-specific options
+# ----------------------------------------------------------------------------
+
+# Optimisation levels
+CFLAGS += -O2
+
+# Framebuffer default surface provider.
+# Valid values are: x, sdl, linux, vnc, able,
+NETSURF_FB_FRONTEND := sdl
+
+# Use libharu to enable PDF export and GTK printing support.
+# Valid options: YES, NO
+NETSURF_USE_HARU_PDF := NO
+
+# Enable NetSurf's use of librosprite for displaying RISC OS Sprites
+# Valid options: YES, NO, AUTO
+NETSURF_USE_ROSPRITE := AUTO
+
+# Library to use for font plotting
+# Valid options: internal, freetype
+NETSURF_FB_FONTLIB := internal
+
+# Default freetype font files
+NETSURF_FB_FONT_SANS_SERIF := DejaVuSans.ttf
+NETSURF_FB_FONT_SANS_SERIF_BOLD := DejaVuSans-Bold.ttf
+NETSURF_FB_FONT_SANS_SERIF_ITALIC := DejaVuSans-Oblique.ttf
+NETSURF_FB_FONT_SANS_SERIF_ITALIC_BOLD := DejaVuSans-BoldOblique.ttf
+NETSURF_FB_FONT_SERIF := DejaVuSerif.ttf
+NETSURF_FB_FONT_SERIF_BOLD := DejaVuSerif-Bold.ttf
+NETSURF_FB_FONT_MONOSPACE := DejaVuSansMono.ttf
+NETSURF_FB_FONT_MONOSPACE_BOLD := DejaVuSansMono-Bold.ttf
+NETSURF_FB_FONT_CURSIVE := Comic_Sans_MS.ttf
+NETSURF_FB_FONT_FANTASY := Impact.ttf
+
+# Default binary install path
+NETSURF_FRAMEBUFFER_BIN := $(PREFIX)/bin/
+
+# Default resource install path
+NETSURF_FRAMEBUFFER_RESOURCES := $(PREFIX)/share/netsurf/
+
+# Default framebuffer search path
+NETSURF_FB_RESPATH := $${HOME}/.netsurf/:$${NETSURFRES}:$(NETSURF_FRAMEBUFFER_RESOURCES):./frontends/framebuffer/res
+
+# freetype compiled in font serch path
+NETSURF_FB_FONTPATH := /usr/share/fonts/truetype/ttf-dejavu:/usr/share/fonts/truetype/msttcorefonts
+
diff --git a/frontends/framebuffer/bitmap.c b/frontends/framebuffer/bitmap.c
new file mode 100644
index 000000000..b4907ada6
--- /dev/null
+++ b/frontends/framebuffer/bitmap.c
@@ -0,0 +1,352 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * Framebuffer implementation of generic bitmap interface.
+ */
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <libnsfb.h>
+#include <libnsfb_plot.h>
+
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "image/bitmap.h"
+#include "desktop/plotters.h"
+#include "content/content.h"
+
+#include "framebuffer/gui.h"
+#include "framebuffer/fbtk.h"
+#include "framebuffer/framebuffer.h"
+#include "framebuffer/bitmap.h"
+
+/**
+ * Create a bitmap.
+ *
+ * \param width width of image in pixels
+ * \param height width of image in pixels
+ * \param state a flag word indicating the initial state
+ * \return an opaque struct bitmap, or NULL on memory exhaustion
+ */
+static void *bitmap_create(int width, int height, unsigned int state)
+{
+ nsfb_t *bm;
+
+ LOG("width %d, height %d, state %u", width, height, state);
+
+ bm = nsfb_new(NSFB_SURFACE_RAM);
+ if (bm == NULL) {
+ return NULL;
+ }
+
+ if ((state & BITMAP_OPAQUE) == 0) {
+ nsfb_set_geometry(bm, width, height, NSFB_FMT_ABGR8888);
+ } else {
+ nsfb_set_geometry(bm, width, height, NSFB_FMT_XBGR8888);
+ }
+
+ if (nsfb_init(bm) == -1) {
+ nsfb_free(bm);
+ return NULL;
+ }
+
+ LOG("bitmap %p", bm);
+
+ return bm;
+}
+
+
+/**
+ * Return a pointer to the pixel data in a bitmap.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ * \return pointer to the pixel buffer
+ *
+ * The pixel data is packed as BITMAP_FORMAT, possibly with padding at the end
+ * of rows. The width of a row in bytes is given by bitmap_get_rowstride().
+ */
+static unsigned char *bitmap_get_buffer(void *bitmap)
+{
+ nsfb_t *bm = bitmap;
+ unsigned char *bmpptr;
+
+ assert(bm != NULL);
+
+ nsfb_get_buffer(bm, &bmpptr, NULL);
+
+ return bmpptr;
+}
+
+
+/**
+ * Find the width of a pixel row in bytes.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ * \return width of a pixel row in the bitmap
+ */
+static size_t bitmap_get_rowstride(void *bitmap)
+{
+ nsfb_t *bm = bitmap;
+ int bmpstride;
+
+ assert(bm != NULL);
+
+ nsfb_get_buffer(bm, NULL, &bmpstride);
+
+ return bmpstride;
+}
+
+
+/**
+ * Free a bitmap.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ */
+static void bitmap_destroy(void *bitmap)
+{
+ nsfb_t *bm = bitmap;
+
+ assert(bm != NULL);
+
+ nsfb_free(bm);
+}
+
+
+/**
+ * Save a bitmap in the platform's native format.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ * \param path pathname for file
+ * \param flags flags controlling how the bitmap is saved.
+ * \return true on success, false on error and error reported
+ */
+static bool bitmap_save(void *bitmap, const char *path, unsigned flags)
+{
+ return true;
+}
+
+
+/**
+ * The bitmap image has changed, so flush any persistant cache.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ */
+static void bitmap_modified(void *bitmap) {
+}
+
+/**
+ * Sets wether a bitmap should be plotted opaque
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ * \param opaque whether the bitmap should be plotted opaque
+ */
+static void bitmap_set_opaque(void *bitmap, bool opaque)
+{
+ nsfb_t *bm = bitmap;
+
+ assert(bm != NULL);
+
+ if (opaque) {
+ nsfb_set_geometry(bm, 0, 0, NSFB_FMT_XBGR8888);
+ } else {
+ nsfb_set_geometry(bm, 0, 0, NSFB_FMT_ABGR8888);
+ }
+}
+
+
+/**
+ * Tests whether a bitmap has an opaque alpha channel
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ * \return whether the bitmap is opaque
+ */
+static bool bitmap_test_opaque(void *bitmap)
+{
+ int tst;
+ nsfb_t *bm = bitmap;
+ unsigned char *bmpptr;
+ int width;
+ int height;
+
+ assert(bm != NULL);
+
+ nsfb_get_buffer(bm, &bmpptr, NULL);
+
+ nsfb_get_geometry(bm, &width, &height, NULL);
+
+ tst = width * height;
+
+ while (tst-- > 0) {
+ if (bmpptr[(tst << 2) + 3] != 0xff) {
+ LOG("bitmap %p has transparency", bm);
+ return false;
+ }
+ }
+ LOG("bitmap %p is opaque", bm);
+ return true;
+}
+
+
+/**
+ * Gets weather a bitmap should be plotted opaque
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ */
+bool framebuffer_bitmap_get_opaque(void *bitmap)
+{
+ nsfb_t *bm = bitmap;
+ enum nsfb_format_e format;
+
+ assert(bm != NULL);
+
+ nsfb_get_geometry(bm, NULL, NULL, &format);
+
+ if (format == NSFB_FMT_ABGR8888)
+ return false;
+
+ return true;
+}
+
+static int bitmap_get_width(void *bitmap)
+{
+ nsfb_t *bm = bitmap;
+ int width;
+
+ assert(bm != NULL);
+
+ nsfb_get_geometry(bm, &width, NULL, NULL);
+
+ return(width);
+}
+
+static int bitmap_get_height(void *bitmap)
+{
+ nsfb_t *bm = bitmap;
+ int height;
+
+ assert(bm != NULL);
+
+ nsfb_get_geometry(bm, NULL, &height, NULL);
+
+ return(height);
+}
+
+/* get bytes per pixel */
+static size_t bitmap_get_bpp(void *bitmap)
+{
+ return 4;
+}
+
+/**
+ * Render content into a bitmap.
+ *
+ * \param bitmap the bitmap to draw to
+ * \param content content structure to render
+ * \return true on success and bitmap updated else false
+ */
+static nserror
+bitmap_render(struct bitmap *bitmap,
+ struct hlcache_handle *content)
+{
+ nsfb_t *tbm = (nsfb_t *)bitmap; /* target bitmap */
+ nsfb_t *bm; /* temporary bitmap */
+ nsfb_t *current; /* current main fb */
+ int width, height; /* target bitmap width height */
+ int cwidth, cheight;/* content width /height */
+ nsfb_bbox_t loc;
+
+ struct redraw_context ctx = {
+ .interactive = false,
+ .background_images = true,
+ .plot = &fb_plotters
+ };
+
+ nsfb_get_geometry(tbm, &width, &height, NULL);
+
+ LOG("width %d, height %d", width, height);
+
+ /* Calculate size of buffer to render the content into */
+ /* We get the width from the content width, unless it exceeds 1024,
+ * in which case we use 1024. This means we never create excessively
+ * large render buffers for huge contents, which would eat memory and
+ * cripple performance. */
+ cwidth = min(content_get_width(content), 1024);
+ /* The height is set in proportion with the width, according to the
+ * aspect ratio of the required thumbnail. */
+ cheight = ((cwidth * height) + (width / 2)) / width;
+
+ /* create temporary surface */
+ bm = nsfb_new(NSFB_SURFACE_RAM);
+ if (bm == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ nsfb_set_geometry(bm, cwidth, cheight, NSFB_FMT_XBGR8888);
+
+ if (nsfb_init(bm) == -1) {
+ nsfb_free(bm);
+ return NSERROR_NOMEM;
+ }
+
+ current = framebuffer_set_surface(bm);
+
+ /* render the content into temporary surface */
+ content_scaled_redraw(content, cwidth, cheight, &ctx);
+
+ framebuffer_set_surface(current);
+
+ loc.x0 = 0;
+ loc.y0 = 0;
+ loc.x1 = width;
+ loc.y1 = height;
+
+ nsfb_plot_copy(bm, NULL, tbm, &loc);
+
+ nsfb_free(bm);
+
+ return NSERROR_OK;
+}
+
+static struct gui_bitmap_table bitmap_table = {
+ .create = bitmap_create,
+ .destroy = bitmap_destroy,
+ .set_opaque = bitmap_set_opaque,
+ .get_opaque = framebuffer_bitmap_get_opaque,
+ .test_opaque = bitmap_test_opaque,
+ .get_buffer = bitmap_get_buffer,
+ .get_rowstride = bitmap_get_rowstride,
+ .get_width = bitmap_get_width,
+ .get_height = bitmap_get_height,
+ .get_bpp = bitmap_get_bpp,
+ .save = bitmap_save,
+ .modified = bitmap_modified,
+ .render = bitmap_render,
+};
+
+struct gui_bitmap_table *framebuffer_bitmap_table = &bitmap_table;
+
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */
diff --git a/frontends/framebuffer/bitmap.h b/frontends/framebuffer/bitmap.h
new file mode 100644
index 000000000..0a72f19c8
--- /dev/null
+++ b/frontends/framebuffer/bitmap.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2015 Vincent Sanders <vince@netsurf-browser.h>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_FB_BITMAP_H
+#define NS_FB_BITMAP_H
+
+extern struct gui_bitmap_table *framebuffer_bitmap_table;
+
+bool framebuffer_bitmap_get_opaque(void *bitmap);
+
+#endif /* NS_FB_BITMAP_H */
diff --git a/frontends/framebuffer/clipboard.c b/frontends/framebuffer/clipboard.c
new file mode 100644
index 000000000..05defe8f9
--- /dev/null
+++ b/frontends/framebuffer/clipboard.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2012 Michael Drake <tlsa@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * nsfb internal clipboard handling
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "utils/log.h"
+#include "desktop/browser.h"
+#include "desktop/gui_clipboard.h"
+
+#include "framebuffer/gui.h"
+#include "framebuffer/clipboard.h"
+
+
+static struct gui_clipboard {
+ char *buffer;
+ size_t buffer_len;
+ size_t length;
+} gui_clipboard;
+
+
+/**
+ * Core asks front end for clipboard contents.
+ *
+ * \param buffer UTF-8 text, allocated by front end, ownership yeilded to core
+ * \param length Byte length of UTF-8 text in buffer
+ */
+static void gui_get_clipboard(char **buffer, size_t *length)
+{
+ *buffer = NULL;
+ *length = 0;
+
+ if (gui_clipboard.length > 0) {
+ assert(gui_clipboard.buffer != NULL);
+ LOG("Pasting %zd bytes: \"%s\"\n",
+ gui_clipboard.length, gui_clipboard.buffer);
+
+ *buffer = malloc(gui_clipboard.length);
+
+ if (*buffer != NULL) {
+ memcpy(*buffer, gui_clipboard.buffer,
+ gui_clipboard.length);
+ *length = gui_clipboard.length;
+ }
+ }
+}
+
+
+/**
+ * Core tells front end to put given text in clipboard
+ *
+ * \param buffer UTF-8 text, owned by core
+ * \param length Byte length of UTF-8 text in buffer
+ * \param styles Array of styles given to text runs, owned by core, or NULL
+ * \param n_styles Number of text run styles in array
+ */
+static void gui_set_clipboard(const char *buffer, size_t length,
+ nsclipboard_styles styles[], int n_styles)
+{
+ if (gui_clipboard.buffer_len < length + 1) {
+ /* Make buffer big enough */
+ char *new_buff;
+
+ new_buff = realloc(gui_clipboard.buffer, length + 1);
+ if (new_buff == NULL)
+ return;
+
+ gui_clipboard.buffer = new_buff;
+ gui_clipboard.buffer_len = length + 1;
+ }
+
+ gui_clipboard.length = 0;
+
+ memcpy(gui_clipboard.buffer, buffer, length);
+ gui_clipboard.length = length;
+ gui_clipboard.buffer[gui_clipboard.length] = '\0';
+}
+
+static struct gui_clipboard_table clipboard_table = {
+ .get = gui_get_clipboard,
+ .set = gui_set_clipboard,
+};
+
+struct gui_clipboard_table *framebuffer_clipboard_table = &clipboard_table;
diff --git a/frontends/framebuffer/clipboard.h b/frontends/framebuffer/clipboard.h
new file mode 100644
index 000000000..b5f7b0f29
--- /dev/null
+++ b/frontends/framebuffer/clipboard.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_FB_CLIPBOARD_H
+#define NETSURF_FB_CLIPBOARD_H
+
+extern struct gui_clipboard_table *framebuffer_clipboard_table;
+
+#endif
diff --git a/frontends/framebuffer/convert_font.c b/frontends/framebuffer/convert_font.c
new file mode 100644
index 000000000..010af857a
--- /dev/null
+++ b/frontends/framebuffer/convert_font.c
@@ -0,0 +1,1215 @@
+/*
+ * Copyright 2014 Michael Drake <tlsa@netsurf-browser.org>
+ * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of the convert_font tool used to convert font
+ * glyph data into a compilable representation.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#define GLYPH_LEN 16
+#define BUCKETS 512
+#define CHUNK_SIZE (64 * 1024)
+#define HEADER_MAX 2000
+
+#define SECTION_SIZE (sizeof(uint16_t) * 256)
+
+const char *labels[4] = {
+ " Regular",
+ " Italic",
+ " Bold",
+ "Bold & Italic"
+};
+
+const char *var_lables[4] = {
+ "fb_regular",
+ "fb_italic",
+ "fb_bold",
+ "fb_bold_italic"
+};
+
+const char *short_labels[4] = {
+ " ",
+ " i",
+ "b ",
+ "bi"
+};
+
+enum font_style {
+ REGULAR = 0,
+ ITALIC = (1 << 0),
+ BOLD = (1 << 1),
+ ITALIC_BOLD = (1 << 2)
+};
+
+enum log_level {
+ LOG_DEBUG,
+ LOG_INFO,
+ LOG_RESULT,
+ LOG_WARNING,
+ LOG_ERROR
+};
+
+enum log_level level;
+
+typedef struct glyph_entry {
+ union {
+ uint32_t u32[GLYPH_LEN / 4];
+ uint8_t u8[GLYPH_LEN];
+ } data;
+ uint32_t index;
+ struct glyph_entry *next;
+} glyph_entry;
+
+/** Scratch glyph for generated code points */
+uint8_t code_point[GLYPH_LEN];
+
+/** Hash table */
+glyph_entry *ht[BUCKETS];
+
+#define LOG(lev, fmt, ...) \
+ if (lev >= level) \
+ printf(fmt, ##__VA_ARGS__);
+
+/**
+ * Get hash for glyph data
+ * \param g Glyph data (GLYPH_LEN bytes)
+ * \return glyph's hash
+ */
+static inline uint32_t glyph_hash(const uint8_t *g)
+{
+ uint32_t hash = 0x811c9dc5;
+ unsigned int len = GLYPH_LEN;
+
+ while (len > 0) {
+ hash *= 0x01000193;
+ hash ^= *g++;
+ len--;
+ }
+
+ return hash;
+}
+
+
+/**
+ * Check whether glyphs are identical (compares glyph data)
+ *
+ * \param g1 First glyph's data (GLYPH_LEN bytes)
+ * \param g2 Second glyph's data (GLYPH_LEN bytes)
+ * \return true iff both glyphs are identical, else false
+ */
+static inline bool glyphs_match(const uint8_t *g1, const uint8_t *g2)
+{
+ return (memcmp(g1, g2, GLYPH_LEN) == 0);
+}
+
+
+/**
+ * Add a glyph to a hash chain (or free, and return pointer to existing glyph)
+ *
+ * Note that if new glyph already exists in chain, it is freed and a pointer to
+ * the existing glyph is returned. If the glyph does not exist in the chain
+ * it is added and its pointer is returned.
+ *
+ * \param head Head of hash chain
+ * \param new New glyph to add (may be freed)
+ * \return pointer to glyph in hash chain
+ */
+static glyph_entry * glyph_add_to_chain(glyph_entry **head, glyph_entry *new)
+{
+ glyph_entry *e = *head;
+
+ if (*head == NULL) {
+ new->next = NULL;
+ *head = new;
+ return new;
+ }
+
+ do {
+ if (glyphs_match(new->data.u8, e->data.u8)) {
+ free(new);
+ return e;
+ }
+ if (e->next == NULL)
+ break;
+ e = e->next;
+ } while (1);
+
+ new->next = e->next;
+ e->next = new;
+ return new;
+}
+
+
+/**
+ * Free a glyph entry chain
+ *
+ * \param head Head of hash chain
+ */
+static void free_chain(glyph_entry *head)
+{
+ glyph_entry *e = head;
+
+ if (head == NULL)
+ return;
+
+ while (e != NULL) {
+ head = e->next;
+ free(e);
+ e = head;
+ };
+}
+
+
+/**
+ * Add new glyph to hash table (or free, and return pointer to existing glyph)
+ *
+ * Note that if new glyph already exists in table, it is freed and a pointer to
+ * the existing glyph is returned. If the glyph does not exist in the table
+ * it is added and its pointer is returned.
+ *
+ * \param new New glyph to add (may be freed)
+ * \return pointer to glyph in hash table
+ */
+static glyph_entry * glyph_add_to_table(glyph_entry *new)
+{
+ uint32_t hash = glyph_hash(new->data.u8);
+
+ return glyph_add_to_chain(&ht[hash % BUCKETS], new);
+}
+
+
+/**
+ * Free glyph table.
+ */
+static void free_table(void)
+{
+ int i;
+
+ for (i = 0; i < BUCKETS; i++) {
+ free_chain(ht[i]);
+ }
+}
+
+struct parse_context {
+ enum {
+ START,
+ IN_HEADER,
+ BEFORE_ID,
+ GLYPH_ID,
+ BEFORE_GLYPH_DATA,
+ IN_GLYPH_DATA
+ } state; /**< Current parser state */
+
+ union {
+ struct {
+ bool new_line;
+ } in_header;
+ struct {
+ bool new_line;
+ bool u;
+ } before_id;
+ struct {
+ int c;
+ } g_id;
+ struct {
+ bool new_line;
+ bool prev_h;
+ bool prev_s;
+ int c;
+ } before_gd;
+ struct {
+ int line;
+ int pos;
+ int styles;
+ int line_styles;
+ glyph_entry *e[4];
+ } in_gd;
+ } data; /**< The state specific data */
+
+ int id; /**< Current ID */
+
+ int codepoints; /**< Glyphs containing codepoints */
+ int count[4]; /**< Count of glyphs in file */
+};
+
+struct font_data {
+ char header[HEADER_MAX];
+ int header_len;
+
+ uint8_t section_table[4][256];
+ uint8_t sec_count[4];
+ uint16_t *sections[4];
+
+ glyph_entry *e[0xffff];
+ int glyphs;
+};
+
+bool generate_font_header(const char *path, struct font_data *data)
+{
+ FILE *fp;
+ int s;
+
+ fp = fopen(path, "wb");
+ if (fp == NULL) {
+ LOG(LOG_ERROR, "Couldn't open header file \"%s\"\n", path);
+ return false;
+ }
+
+ fprintf(fp, "/*\n");
+ fwrite(data->header, 1, data->header_len, fp);
+ fprintf(fp, " */\n\n");
+ fprintf(fp, "/* Don't edit this file, it was generated from the "
+ "plain text source data. */\n\n");
+
+
+ for (s = 0; s < 4; s++) {
+ fprintf(fp, "const uint8_t *%s_section_table;\n",
+ var_lables[s]);
+ fprintf(fp, "const uint16_t *%s_sections;\n",
+ var_lables[s]);
+
+ }
+
+ fprintf(fp, "const uint8_t *font_glyph_data;\n");
+
+ fprintf(fp, "\n\n");
+
+ fclose(fp);
+
+ return true;
+
+}
+
+bool generate_font_source(const char *path, struct font_data *data)
+{
+ int s, i, y;
+ int limit;
+ FILE *fp;
+
+ fp = fopen(path, "wb");
+ if (fp == NULL) {
+ LOG(LOG_ERROR, "Couldn't open output file \"%s\"\n", path);
+ return false;
+ }
+
+ fprintf(fp, "/*\n");
+ fwrite(data->header, 1, data->header_len, fp);
+ fprintf(fp, " */\n\n");
+ fprintf(fp, "/* Don't edit this file, it was generated from the "
+ "plain text source data. */\n\n");
+
+ fprintf(fp, "#include <stdint.h>\n");
+ fprintf(fp, "\n");
+
+ for (s = 0; s < 4; s++) {
+
+ fprintf(fp, "static const uint8_t %s_section_table_c[256] = {\n",
+ var_lables[s]);
+
+ for (i = 0; i < 256; i++) {
+ if (i == 255)
+ fprintf(fp, "0x%.2X\n",
+ data->section_table[s][i]);
+ else if (i % 8 == 7)
+ fprintf(fp, "0x%.2X,\n",
+ data->section_table[s][i]);
+ else if (i % 8 == 0)
+ fprintf(fp, "\t0x%.2X, ",
+ data->section_table[s][i]);
+ else
+ fprintf(fp, "0x%.2X, ",
+ data->section_table[s][i]);
+ }
+
+ fprintf(fp, "};\nconst uint8_t *%s_section_table = &%s_section_table_c[0];\n\n",
+ var_lables[s], var_lables[s]);
+ fprintf(fp, "static const uint16_t %s_sections_c[%i] = {\n",
+ var_lables[s], data->sec_count[s] * 256);
+
+ limit = data->sec_count[s] * 256;
+ for (i = 0; i < limit; i++) {
+ uint16_t offset = data->sections[s][i];
+ if (i == limit - 1)
+ fprintf(fp, "0x%.4X\n", offset);
+ else if (i % 4 == 3)
+ fprintf(fp, "0x%.4X,\n", offset);
+ else if (i % 4 == 0)
+ fprintf(fp, "\t0x%.4X, ", offset);
+ else
+ fprintf(fp, "0x%.4X, ", offset);
+ }
+
+ fprintf(fp, "};\nconst uint16_t *%s_sections = &%s_sections_c[0];\n\n", var_lables[s], var_lables[s]);
+ }
+
+ fprintf(fp, "static const uint8_t font_glyph_data_c[%i] = {\n",
+ (data->glyphs + 1) * 16);
+
+ fprintf(fp, "\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n"
+ "\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n");
+
+ limit = data->glyphs;
+ for (i = 0; i < limit; i++) {
+ glyph_entry *e = data->e[i];
+
+ for (y = 0; y < 16; y++) {
+ if (i == limit - 1 && y == 15)
+ fprintf(fp, "0x%.2X\n", e->data.u8[y]);
+ else if (y % 8 == 7)
+ fprintf(fp, "0x%.2X,\n", e->data.u8[y]);
+ else if (y % 8 == 0)
+ fprintf(fp, "\t0x%.2X, ", e->data.u8[y]);
+ else
+ fprintf(fp, "0x%.2X, ", e->data.u8[y]);
+ }
+ }
+
+ fprintf(fp, "};\n");
+ fprintf(fp, "const uint8_t *font_glyph_data = &font_glyph_data_c[0];\n\n");
+
+ fclose(fp);
+
+ return true;
+}
+
+static bool add_glyph_to_data(glyph_entry *add, int id, int style,
+ struct font_data *d)
+{
+ glyph_entry *e;
+ int offset;
+ int s;
+
+ /* Find out if 'add' is unique, and get its unique table entry */
+ e = glyph_add_to_table(add);
+ if (e == add) {
+ /* Unique glyph */
+ d->e[d->glyphs++] = e;
+ e->index = d->glyphs;
+ if (d->glyphs >= 0xfffd) {
+ LOG(LOG_ERROR, " Too many glyphs for internal data "
+ "representation\n");
+ return false;
+ }
+ } else {
+ /* Duplicate glyph */
+ LOG(LOG_DEBUG, " U+%.4X (%s) is duplicate\n",
+ id, short_labels[style]);
+ }
+
+ /* Find glyph's section */
+ s = id / 256;
+
+ /* Allocate section if needed */
+ if ((s == 0 && d->sections[style] == NULL) ||
+ (s != 0 && d->section_table[style][s] == 0)) {
+ size_t size = (d->sec_count[style] + 1) * SECTION_SIZE;
+ uint16_t *temp = realloc(d->sections[style], size);
+ if (temp == NULL) {
+ LOG(LOG_ERROR, " Couldn't increase sections "
+ "allocation\n");
+ return false;
+ }
+ memset(temp + d->sec_count[style] * 256, 0,
+ SECTION_SIZE);
+ d->section_table[style][s] = d->sec_count[style];
+ d->sections[style] = temp;
+ d->sec_count[style]++;
+ }
+
+ offset = d->section_table[style][s] * 256 + (id & 0xff);
+ d->sections[style][offset] = e->index;
+
+ return true;
+}
+
+
+static bool check_glyph_data_valid(int pos, char c)
+{
+ int offset = pos % 11;
+
+ if (pos == 44) {
+ if (c != '\n') {
+ LOG(LOG_ERROR, " Invalid glyph data: "
+ "expecting '\\n', got '%c' (%i)\n",
+ c, c);
+ return false;
+ } else {
+ return true;
+ }
+ } else if (pos < 3) {
+ if (c != ' ') {
+ LOG(LOG_ERROR, " Invalid glyph data: "
+ "expecting ' ', got '%c' (%i)\n",
+ c, c);
+ return false;
+ } else {
+ return true;
+ }
+ } else if (offset == 0) {
+ if (c != '\n' && c != ' ') {
+ LOG(LOG_ERROR, " Invalid glyph data: "
+ "expecting '\\n' or ' ', "
+ "got '%c' (%i)\n",
+ c, c);
+ return false;
+ } else {
+ return true;
+ }
+ } else if (offset < 3) {
+ if (c != ' ') {
+ LOG(LOG_ERROR, " Invalid glyph data: "
+ "expecting ' ', got '%c' (%i)\n",
+ c, c);
+ return false;
+ } else {
+ return true;
+ }
+ } else if (offset >= 3 && pos < 11) {
+ if (c != '.' && c != '#') {
+ LOG(LOG_ERROR, " Invalid glyph data: "
+ "expecting '.' or '#', "
+ "got '%c' (%i)\n",
+ c, c);
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /* offset must be >=3 */
+ if (c != '.' && c != '#' && c != ' ') {
+ LOG(LOG_ERROR, " Invalid glyph data: "
+ "expecting '.', '#', or ' ', "
+ "got '%c' (%i)\n",
+ c, c);
+ return false;
+ }
+
+ return true;
+}
+
+#define SEVEN_SET ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | \
+ (1 << 4) | (1 << 5) | (1 << 6))
+
+#define THREE_SSS ((1 << 0) | (1 << 1) | (1 << 2))
+#define THREE_S_S ((1 << 0) | (1 << 2))
+#define THREE__SS ((1 << 0) | (1 << 1) )
+#define THREE_SS_ ( (1 << 1) | (1 << 2))
+#define THREE_S__ (1 << 2)
+#define THREE__S_ (1 << 1)
+#define THREE___S (1 << 0)
+
+uint8_t frag[16][5] = {
+ { THREE_SSS,
+ THREE_S_S,
+ THREE_S_S,
+ THREE_S_S,
+ THREE_SSS },
+
+ { THREE__S_,
+ THREE_SS_,
+ THREE__S_,
+ THREE__S_,
+ THREE_SSS },
+
+ { THREE_SS_,
+ THREE___S,
+ THREE__S_,
+ THREE_S__,
+ THREE_SSS },
+
+ { THREE_SS_,
+ THREE___S,
+ THREE_SS_,
+ THREE___S,
+ THREE_SS_ },
+
+ { THREE_S_S,
+ THREE_S_S,
+ THREE_SSS,
+ THREE___S,
+ THREE___S },
+
+ { THREE_SSS,
+ THREE_S__,
+ THREE_SSS,
+ THREE___S,
+ THREE_SSS },
+
+ { THREE__SS,
+ THREE_S__,
+ THREE_SSS,
+ THREE_S_S,
+ THREE_SSS },
+
+ { THREE_SSS,
+ THREE___S,
+ THREE__S_,
+ THREE__S_,
+ THREE__S_ },
+
+ { THREE_SSS,
+ THREE_S_S,
+ THREE_SSS,
+ THREE_S_S,
+ THREE_SSS },
+
+ { THREE_SSS,
+ THREE_S_S,
+ THREE_SSS,
+ THREE___S,
+ THREE___S },
+
+ { THREE__S_,
+ THREE_S_S,
+ THREE_SSS,
+ THREE_S_S,
+ THREE_S_S },
+
+ { THREE_SS_,
+ THREE_S_S,
+ THREE_SS_,
+ THREE_S_S,
+ THREE_SS_ },
+
+ { THREE__S_,
+ THREE_S_S,
+ THREE_S__,
+ THREE_S_S,
+ THREE__S_ },
+
+ { THREE_SS_,
+ THREE_S_S,
+ THREE_S_S,
+ THREE_S_S,
+ THREE_SS_ },
+
+ { THREE_SSS,
+ THREE_S__,
+ THREE_SS_,
+ THREE_S__,
+ THREE_SSS },
+
+ { THREE_SSS,
+ THREE_S__,
+ THREE_SS_,
+ THREE_S__,
+ THREE_S__ }
+};
+
+void build_codepoint(int id, bool italic, uint8_t *code_point)
+{
+ int shift = 0;
+ int l;
+ int r;
+
+ if (!italic)
+ shift = 1;
+
+ l = (id >> 12);
+ r = 0xf & (id >> 8);
+
+ code_point[ 0] = 0;
+ code_point[ 1] = SEVEN_SET << shift;
+ code_point[ 2] = 0;
+
+ code_point[ 3] = (frag[l][0] << (4 + shift)) | (frag[r][0] << shift);
+ code_point[ 4] = (frag[l][1] << (4 + shift)) | (frag[r][1] << shift);
+ code_point[ 5] = (frag[l][2] << (4 + shift)) | (frag[r][2] << shift);
+ code_point[ 6] = (frag[l][3] << (4 + shift)) | (frag[r][3] << shift);
+ code_point[ 7] = (frag[l][4] << (4 + shift)) | (frag[r][4] << shift);
+
+ code_point[ 8] = 0;
+
+ shift = 1;
+
+ l = 0xf & (id >> 4);
+ r = 0xf & id ;
+
+ code_point[ 9] = (frag[l][0] << (4 + shift)) | (frag[r][0] << shift);
+ code_point[10] = (frag[l][1] << (4 + shift)) | (frag[r][1] << shift);
+ code_point[11] = (frag[l][2] << (4 + shift)) | (frag[r][2] << shift);
+ code_point[12] = (frag[l][3] << (4 + shift)) | (frag[r][3] << shift);
+ code_point[13] = (frag[l][4] << (4 + shift)) | (frag[r][4] << shift);
+
+ code_point[14] = 0;
+ code_point[15] = SEVEN_SET << shift;
+}
+
+#undef SEVEN_SET
+#undef THREE_SSS
+#undef THREE_S_S
+#undef THREE__SS
+#undef THREE_SS_
+#undef THREE_S__
+#undef THREE__S_
+#undef THREE___S
+
+static bool glyph_is_codepoint(const glyph_entry *e, int id, int style)
+{
+ bool italic = false;
+
+ if (style == 1 || style == 3) {
+ italic = true;
+ }
+
+ build_codepoint(id, italic, code_point);
+
+ return glyphs_match(code_point, e->data.u8);
+}
+
+
+static bool parse_glyph_data(struct parse_context *ctx, char c,
+ struct font_data *d)
+{
+ int glyph = ctx->data.in_gd.pos / 11;
+ int g_pos = ctx->data.in_gd.pos % 11 - 3;
+ uint8_t *row;
+ bool ok;
+ int i;
+
+ /* Check that character is valid */
+ if (check_glyph_data_valid(ctx->data.in_gd.pos, c) == false) {
+ LOG(LOG_ERROR, " Error in U+%.4X data: "
+ "glyph line: %i, pos: %i\n",
+ ctx->id,
+ ctx->data.in_gd.line,
+ ctx->data.in_gd.pos);
+ goto error;
+ }
+
+ /* Allocate glyph data if needed */
+ if (ctx->data.in_gd.line == 0 &&
+ (c == '.' || c == '#')) {
+ if (ctx->data.in_gd.e[glyph] == NULL) {
+ ctx->data.in_gd.e[glyph] =
+ calloc(sizeof(struct glyph_entry), 1);
+ if (ctx->data.in_gd.e[glyph] == NULL) {
+ LOG(LOG_ERROR, " Couldn't allocate memory for "
+ "glyph entry\n");
+ goto error;
+ }
+
+ ctx->data.in_gd.styles |= 1 << glyph;
+ }
+ }
+
+ /* Build glyph data */
+ if (c == '#') {
+ row = &ctx->data.in_gd.e[glyph]->data.u8[ctx->data.in_gd.line];
+ *row += 1 << (7 - g_pos);
+
+ ctx->data.in_gd.line_styles |= 1 << glyph;
+ } else if (c == '.') {
+ ctx->data.in_gd.line_styles |= 1 << glyph;
+ }
+
+ /* Deal with current position */
+ if (c == '\n') {
+ if (ctx->data.in_gd.line == 0) {
+ if (ctx->data.in_gd.e[0] == NULL) {
+ LOG(LOG_ERROR, " Error in U+%.4X data: "
+ "\"Regular\" glyph style must "
+ "be present\n", ctx->id);
+ goto error;
+ }
+ } else if (ctx->data.in_gd.styles !=
+ ctx->data.in_gd.line_styles) {
+ LOG(LOG_ERROR, " Error in U+%.4X data: "
+ "glyph line: %i "
+ "styles don't match first line\n",
+ ctx->id,
+ ctx->data.in_gd.line);
+ goto error;
+ }
+
+ ctx->data.in_gd.pos = 0;
+ ctx->data.in_gd.line++;
+ ctx->data.in_gd.line_styles = 0;
+ } else {
+ ctx->data.in_gd.pos++;
+ }
+
+ /* If we've got all the glyph data, tidy up and advance state */
+ if (ctx->data.in_gd.line == 16) {
+ for (i = 0; i < 4; i++) {
+ if (ctx->data.in_gd.e[i] != NULL) {
+ ctx->count[i] += 1;
+ if (glyph_is_codepoint(ctx->data.in_gd.e[i],
+ ctx->id, i)) {
+ LOG(LOG_DEBUG, " U+%.4X (%s) is "
+ "codepoint\n",
+ ctx->id,
+ short_labels[i]);
+ ctx->codepoints += 1;
+ free(ctx->data.in_gd.e[i]);
+ ctx->data.in_gd.e[i] = NULL;
+ continue;
+ }
+
+ ok = add_glyph_to_data(ctx->data.in_gd.e[i],
+ ctx->id, i, d);
+ if (!ok) {
+ goto error;
+ }
+ }
+ }
+
+ ctx->data.before_id.new_line = false;
+ ctx->data.before_id.u = false;
+ ctx->state = BEFORE_ID;
+ }
+
+ return true;
+
+error:
+
+ for (i = 0; i < 4; i++) {
+ free(ctx->data.in_gd.e[i]);
+ }
+
+ return false;
+}
+
+static void parse_init(struct parse_context *ctx)
+{
+ memset(ctx, 0, sizeof(struct parse_context));
+}
+
+static bool get_hex_digit_value(char c, int *v)
+{
+ if (c >= '0' && c <= '9')
+ *v = (c - '0');
+ else if (c >= 'A' && c <= 'F')
+ *v = (10 + c - 'A');
+ else {
+ LOG(LOG_ERROR, "Invalid hex digit '%c' (%i)\n", c, c);
+ return false;
+ }
+
+ return true;
+}
+
+static bool assemble_codepoint(const char* c, int n, int *id)
+{
+ bool ok;
+ int v;
+
+ ok = get_hex_digit_value(*c, &v);
+ if (!ok) {
+ return false;
+ }
+
+ *id += v << (4 * (3 - n));
+
+ return true;
+}
+
+static bool parse_chunk(struct parse_context *ctx, const char *buf, size_t len,
+ struct font_data *d)
+{
+ int i;
+ bool ok;
+ int count[4];
+ const char *pos = buf;
+ const char *end = buf + len;
+
+ for (i = 0; i < 4; i++) {
+ count[i] = ctx->count[i];
+ }
+
+ while (pos < end) {
+ if (*pos == '\r') {
+ LOG(LOG_ERROR, "Detected \'\\r\': Bad line ending\n");
+ return false;
+ }
+
+ switch (ctx->state) {
+ case START:
+ if (*pos != '*') {
+ LOG(LOG_ERROR, "First character must be '*'\n");
+ printf("Got: %c (%i)\n", *pos, *pos);
+ return false;
+ }
+ d->header_len = 0;
+ ctx->data.in_header.new_line = true;
+ ctx->state = IN_HEADER;
+
+ /* Fall through */
+ case IN_HEADER:
+ if (ctx->data.in_header.new_line == true) {
+ if (*pos != '*') {
+ LOG(LOG_INFO, " Got header "
+ "(%i bytes)\n",
+ d->header_len);
+ LOG(LOG_DEBUG, " Header:\n\n%.*s\n",
+ d->header_len,
+ d->header);
+ ctx->data.before_id.new_line = false;
+ ctx->data.before_id.u = false;
+ ctx->state = BEFORE_ID;
+ continue;
+ } else if (*pos == '*') {
+ d->header[d->header_len++] = ' ';
+ }
+ ctx->data.in_header.new_line = false;
+
+ } else if (*pos == '\n') {
+ ctx->data.in_header.new_line = true;
+ }
+
+ if (d->header_len == HEADER_MAX) {
+ LOG(LOG_ERROR, " Header too long "
+ "(>%i bytes)\n",
+ d->header_len);
+ return false;
+ }
+
+ d->header[d->header_len++] = *pos;
+ break;
+
+ case BEFORE_ID:
+ if (*pos == '+' &&
+ ctx->data.before_id.new_line == true &&
+ ctx->data.before_id.u == true) {
+ ctx->data.g_id.c = 0;
+ ctx->id = 0;
+ ctx->state = GLYPH_ID;
+ break;
+
+ } else if (*pos == 'U' &&
+ ctx->data.before_id.new_line == true) {
+ ctx->data.before_id.u = true;
+
+ } else if (*pos == '\n') {
+ ctx->data.before_id.new_line = true;
+ ctx->data.before_id.u = false;
+
+ } else {
+ ctx->data.before_id.new_line = false;
+ ctx->data.before_id.u = false;
+ }
+ break;
+
+ case GLYPH_ID:
+ ok = assemble_codepoint(pos, ctx->data.g_id.c++,
+ &ctx->id);
+ if (!ok) {
+ LOG(LOG_ERROR, " Invalid glyph ID\n");
+ return false;
+ }
+
+ if (ctx->data.g_id.c == 4) {
+ ctx->data.before_gd.new_line = false;
+ ctx->data.before_gd.prev_h = false;
+ ctx->data.before_gd.prev_s = false;
+ ctx->data.before_gd.c = 0;
+ ctx->state = BEFORE_GLYPH_DATA;
+ break;
+ }
+ break;
+
+ case BEFORE_GLYPH_DATA:
+ /* Skip until end of dashed line */
+ if (*pos == '\n' && ctx->data.before_gd.c == 53) {
+ ctx->state = IN_GLYPH_DATA;
+ ctx->data.in_gd.e[0] = NULL;
+ ctx->data.in_gd.e[1] = NULL;
+ ctx->data.in_gd.e[2] = NULL;
+ ctx->data.in_gd.e[3] = NULL;
+ ctx->data.in_gd.line = 0;
+ ctx->data.in_gd.pos = 0;
+ ctx->data.in_gd.line_styles = 0;
+ ctx->data.in_gd.styles = 0;
+ break;
+
+ } else if (*pos == '\n') {
+ ctx->data.before_gd.new_line = true;
+ ctx->data.before_gd.prev_h = false;
+ ctx->data.before_gd.prev_s = false;
+ ctx->data.before_gd.c = 0;
+ } else if (*pos == '-' &&
+ ctx->data.before_gd.new_line == true) {
+ assert(ctx->data.before_gd.c == 0);
+ ctx->data.before_gd.new_line = false;
+ ctx->data.before_gd.c++;
+ ctx->data.before_gd.prev_h = true;
+ } else if (*pos == ' ' &&
+ ctx->data.before_gd.prev_h == true) {
+ assert(ctx->data.before_gd.prev_s == false);
+ ctx->data.before_gd.c++;
+ ctx->data.before_gd.prev_h = false;
+ ctx->data.before_gd.prev_s = true;
+ } else if (*pos == '-' &&
+ ctx->data.before_gd.prev_s == true) {
+ assert(ctx->data.before_gd.prev_h == false);
+ ctx->data.before_gd.c++;
+ ctx->data.before_gd.prev_h = true;
+ ctx->data.before_gd.prev_s = false;
+ } else {
+ ctx->data.before_gd.new_line = false;
+ ctx->data.before_gd.prev_h = false;
+ ctx->data.before_gd.prev_s = false;
+ ctx->data.before_gd.c = 0;
+ }
+ break;
+
+ case IN_GLYPH_DATA:
+ ok = parse_glyph_data(ctx, *pos, d);
+ if (!ok) {
+ return false;
+ }
+
+ break;
+ }
+
+ pos++;
+ }
+
+ for (i = 0; i < 4; i++) {
+ LOG(LOG_DEBUG, " %s: %i gylphs\n", labels[i],
+ ctx->count[i] - count[i]);
+ }
+
+ return true;
+}
+
+
+bool load_font(const char *path, struct font_data **data)
+{
+ struct parse_context ctx;
+ struct font_data *d;
+ size_t file_len;
+ size_t done;
+ size_t len;
+ int count;
+ char *buf;
+ FILE *fp;
+ bool ok;
+ int i;
+
+ *data = NULL;
+
+ fp = fopen(path, "rb");
+ if (fp == NULL) {
+ LOG(LOG_ERROR, "Couldn't open font data file\n");
+ return false;
+ }
+
+ d = calloc(sizeof(struct font_data), 1);
+ if (d == NULL) {
+ LOG(LOG_ERROR, "Couldn't allocate memory for font data\n");
+ fclose(fp);
+ return false;
+ }
+
+ /* Find filesize */
+ fseek(fp, 0L, SEEK_END);
+ file_len = ftell(fp);
+ if (file_len == -1) {
+ LOG(LOG_ERROR, "Could not size input file\n");
+ free(d);
+ fclose(fp);
+ return false;
+ }
+ fseek(fp, 0L, SEEK_SET);
+ LOG(LOG_DEBUG, "Input size: %zu bytes\n", file_len);
+
+ /* Allocate buffer for data chunks */
+ buf = malloc(CHUNK_SIZE);
+ if (buf == NULL) {
+ LOG(LOG_ERROR, "Couldn't allocate memory for input buffer\n");
+ free(d);
+ fclose(fp);
+ return false;
+ }
+
+ /* Initialise parser */
+ parse_init(&ctx);
+
+ LOG(LOG_DEBUG, "Using chunk size of %i bytes\n", CHUNK_SIZE);
+
+ /* Parse the input file in chunks */
+ for (done = 0; done < file_len; done += CHUNK_SIZE) {
+ LOG(LOG_INFO, "Parsing input chunk %zu\n", done / CHUNK_SIZE);
+
+ /* Read chunk */
+ len = fread(buf, 1, CHUNK_SIZE, fp);
+ if (file_len - done < CHUNK_SIZE &&
+ len != file_len - done) {
+ LOG(LOG_WARNING, "Last chunk has suspicious size\n");
+ } else if (file_len - done >= CHUNK_SIZE &&
+ len != CHUNK_SIZE) {
+ LOG(LOG_ERROR, "Problem reading file\n");
+ free(buf);
+ free(d);
+ fclose(fp);
+ return false;
+ }
+
+ /* Parse chunk */
+ ok = parse_chunk(&ctx, buf, len, d);
+ if (!ok) {
+ free(buf);
+ free(d);
+ fclose(fp);
+ return false;
+ }
+ LOG(LOG_DEBUG, "Parsed %zu bytes\n", done + len);
+ }
+
+ fclose(fp);
+
+ if (ctx.state != BEFORE_ID) {
+ LOG(LOG_ERROR, "Unexpected end of file\n");
+ free(buf);
+ free(d);
+ return false;
+ }
+
+ LOG(LOG_INFO, "Parsing complete:\n");
+ count = 0;
+ for (i = 0; i < 4; i++) {
+ LOG(LOG_INFO, " %s: %i gylphs\n", labels[i], ctx.count[i]);
+ count += ctx.count[i];
+ }
+
+ LOG(LOG_RESULT, " Total %i gylphs "
+ "(of which %i unique, %i codepoints, %i duplicates)\n",
+ count, d->glyphs, ctx.codepoints,
+ count - d->glyphs - ctx.codepoints);
+
+ free(buf);
+
+ *data = d;
+ return true;
+}
+
+static void log_usage(const char *argv0)
+{
+ level = LOG_INFO;
+ LOG(LOG_INFO,
+ "Usage:\n"
+ "\t%s [options] <in_file> <out_file>\n"
+ "\n"
+ "Options:\n"
+ "\t--help -h Display this text\n"
+ "\t--quiet -q Don't show warnings\n"
+ "\t--verbose -v Verbose output\n"
+ "\t--debug -d Full debug output\n",
+ argv0);
+}
+
+int main(int argc, char** argv)
+{
+ const char *in_path = NULL;
+ const char *out_path = NULL;
+ char *header_path = NULL;
+ struct font_data *data;
+ bool ok;
+ int i;
+ int opt;
+
+ level = LOG_RESULT;
+
+ /* Handle program arguments */
+ struct option long_options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "debug", no_argument, NULL, 'd' },
+ { "header", required_argument, NULL, 'H' },
+ };
+
+ while ((opt = getopt_long(argc, argv, "hqvdH:", long_options, NULL)) != -1) {
+ switch (opt) {
+ case 'q':
+ level = LOG_WARNING;
+ break;
+
+ case 'v':
+ level = LOG_INFO;
+ break;
+
+ case 'd':
+ level = LOG_DEBUG;
+ break;
+
+ case 'H':
+ header_path = strdup(optarg);
+ break;
+
+ case 'h':
+ log_usage(argv[0]);
+ free(header_path);
+ return EXIT_SUCCESS;
+
+ default:
+ log_usage(argv[0]);
+ free(header_path);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if ((argc - optind) < 2) {
+ log_usage(argv[0]);
+ free(header_path);
+ return EXIT_FAILURE;
+ }
+
+ in_path = argv[optind];
+ out_path = argv[optind + 1];
+
+ LOG(LOG_DEBUG, "Using input path: \"%s\"\n", in_path);
+ LOG(LOG_DEBUG, "Using output path: \"%s\"\n", out_path);
+
+ ok = load_font(in_path, &data);
+ if (!ok) {
+ free_table();
+ free(header_path);
+ return EXIT_FAILURE;
+ }
+
+ ok = generate_font_source(out_path, data);
+ if (ok && (header_path != NULL)) {
+ ok = generate_font_header(header_path, data);
+ }
+ free(header_path);
+ free_table();
+ for (i = 0; i < 4; i++) {
+ free(data->sections[i]);
+ }
+ free(data);
+ if (!ok) {
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/frontends/framebuffer/convert_image.c b/frontends/framebuffer/convert_image.c
new file mode 100644
index 000000000..838a90703
--- /dev/null
+++ b/frontends/framebuffer/convert_image.c
@@ -0,0 +1,315 @@
+/*
+ * Copyright 2009 Daniel Silverstone <dsilvers@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include <errno.h>
+#include <stdio.h>
+#include <png.h>
+#include <stdlib.h>
+
+#if PNG_LIBPNG_VER < 10209
+#define png_set_expand_gray_1_2_4_to_8(png) png_set_gray_1_2_4_to_8(png)
+#endif
+
+static png_structp png;
+static png_infop info;
+static int interlace;
+static size_t rowbytes;
+static int raw_width, raw_height;
+static int rowstride;
+static unsigned char *bitmap_data;
+static bool is_cursor = true;
+static int raw_hot_x, raw_hot_y;
+
+#define WIDTH (is_cursor?raw_width-1:raw_width)
+#define HEIGHT (is_cursor?raw_height-1:raw_height)
+
+#define HOT_X (is_cursor?raw_hot_x-1:0)
+#define HOT_Y (is_cursor?raw_hot_y-1:0)
+
+#define REAL(v) (is_cursor?v+1:v)
+
+#define PPIX_AT(x,y) ((bitmap_data + (rowstride * y)) + (x * 4))
+
+#define R_OFF 2
+#define G_OFF 1
+#define B_OFF 0
+#define A_OFF 3
+
+#define R_AT(x,y) *(PPIX_AT(x,y) + R_OFF)
+#define G_AT(x,y) *(PPIX_AT(x,y) + G_OFF)
+#define B_AT(x,y) *(PPIX_AT(x,y) + B_OFF)
+#define A_AT(x,y) *(PPIX_AT(x,y) + A_OFF)
+
+static void info_callback(png_structp png, png_infop info);
+static void row_callback(png_structp png, png_bytep new_row,
+ png_uint_32 row_num, int pass);
+static void end_callback(png_structp png, png_infop info);
+
+
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: fb_convert_image input.png output.inc varname\n");
+}
+
+static void info_callback(png_structp png, png_infop info);
+static void row_callback(png_structp png, png_bytep new_row,
+ png_uint_32 row_num, int pass);
+static void end_callback(png_structp png, png_infop info);
+
+
+static void
+detect_hotspot(void)
+{
+ int i;
+ int greenpixels = 0;
+
+ for (i = 0; i < raw_width; ++i) {
+ if (A_AT(i, 0) == 255) {
+ if (G_AT(i, 0) == 255) {
+ greenpixels++;
+ raw_hot_x = i;
+ }
+ if ((B_AT(i, 0) != 0) || (R_AT(i, 0) != 0)) {
+ is_cursor = false;
+ return;
+ }
+ } else if (A_AT(i, 0) != 0) {
+ is_cursor = false;
+ return;
+ }
+ }
+ if (greenpixels != 1) {
+ is_cursor = false;
+ return;
+ }
+
+ for (i = 0; i < raw_height; ++i) {
+ if (A_AT(0, i) == 255) {
+ if (G_AT(0, i) == 255) {
+ greenpixels++;
+ raw_hot_y = i;
+ }
+ if ((B_AT(0, i) != 0) || (R_AT(0, i) != 0)) {
+ is_cursor = false;
+ return;
+ }
+ } else if (A_AT(0, i) != 0) {
+ is_cursor = false;
+ return;
+ }
+ }
+ if (greenpixels != 2) {
+ is_cursor = false;
+ return;
+ }
+ printf(" Pointer detected. Adjusted hotspot at %d, %d (0-based)\n",
+ raw_hot_x - 1, raw_hot_y - 1);
+}
+
+int
+main(int argc, char **argv)
+{
+ FILE *f;
+ unsigned char buffer[1024];
+ int br;
+ int x, y, c;
+
+ if (argc != 4) {
+ usage();
+ return 1;
+ }
+
+ printf(" CONVERT: %s (%s)\n", argv[1], argv[3]);
+
+ png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
+ info = png_create_info_struct(png);
+
+ png_set_progressive_read_fn(png, NULL, info_callback, row_callback, end_callback);
+
+ f = fopen(argv[1], "rb");
+ if (f == NULL) {
+ printf(" Unable to open %s\n", argv[1]);
+ return 1;
+ }
+
+ do {
+ br = fread(buffer, 1, 1024, f);
+ if (br > 0) {
+ png_process_data(png, info, buffer, br);
+ }
+ } while (br > 0);
+
+ if (br < 0) {
+ printf("Error reading input: %s\n", strerror(errno));
+ fclose(f);
+ return 1;
+ }
+
+ fclose(f);
+
+ detect_hotspot();
+
+ f = fopen(argv[2], "w");
+ if (f == NULL) {
+ printf(" Unable to open %s\n", argv[2]);
+ return 2;
+ }
+
+ fprintf(f, "/* This file is auto-generated from %s\n", argv[1]);
+ fprintf(f, " *\n * Do not edit this file directly.\n */\n\n");
+ fprintf(f, "#include <sys/types.h>\n\n");
+ fprintf(f, "#include <stdint.h>\n\n");
+ fprintf(f, "#include <stdbool.h>\n\n");
+ fprintf(f, "#include <libnsfb.h>\n\n");
+ fprintf(f, "#include \"desktop/plot_style.h\"\n");
+ fprintf(f, "#include \"framebuffer/gui.h\"\n");
+ fprintf(f, "#include \"framebuffer/fbtk.h\"\n\n");
+
+ fprintf(f, "static uint8_t %s_pixdata[] = {\n", argv[3]);
+ for (y = 0; y < HEIGHT; ++y) {
+ unsigned char *rowptr = bitmap_data + (rowstride * y);
+ if (is_cursor) {
+ /* If it's a cursor, skip one row and one column */
+ rowptr += rowstride + 4;
+ }
+ fprintf(f, "\t");
+ for (x = 0; x < WIDTH; ++x) {
+ for (c = 0; c < 4; ++c) {
+ unsigned char b = *rowptr++;
+ fprintf(f, "0x%02x, ", b);
+ }
+ }
+ fprintf(f, "\n");
+ }
+ fprintf(f, "};\n\n");
+
+ fprintf(f, "struct fbtk_bitmap %s = {\n", argv[3]);
+ fprintf(f, "\t.width\t\t= %d,\n", WIDTH);
+ fprintf(f, "\t.height\t\t= %d,\n", HEIGHT);
+ fprintf(f, "\t.hot_x\t\t= %d,\n", HOT_X);
+ fprintf(f, "\t.hot_y\t\t= %d,\n", HOT_Y);
+ fprintf(f, "\t.pixdata\t= %s_pixdata,\n", argv[3]);
+
+ fprintf(f, "};\n\n");
+ fclose(f);
+
+ return 0;
+}
+
+static void
+info_callback(png_structp png, png_infop info)
+{
+ int bit_depth, color_type, interlace, intent;
+ double gamma;
+ unsigned long width, height;
+
+ /* Read the PNG details */
+ png_get_IHDR(png, info, &width, &height, &bit_depth,
+ &color_type, &interlace, 0, 0);
+
+ /* Set up our transformations */
+ if (color_type == PNG_COLOR_TYPE_PALETTE)
+ png_set_palette_to_rgb(png);
+ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
+ png_set_expand_gray_1_2_4_to_8(png);
+ if (png_get_valid(png, info, PNG_INFO_tRNS))
+ png_set_tRNS_to_alpha(png);
+ if (bit_depth == 16)
+ png_set_strip_16(png);
+ if (color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb(png);
+ if (!(color_type & PNG_COLOR_MASK_ALPHA))
+ png_set_filler(png, 0xff, PNG_FILLER_AFTER);
+ /* gamma correction - we use 2.2 as our screen gamma
+ * this appears to be correct (at least in respect to !Browse)
+ * see http://www.w3.org/Graphics/PNG/all_seven.html for a test case
+ */
+ if (png_get_sRGB(png, info, &intent))
+ png_set_gamma(png, 2.2, 0.45455);
+ else {
+ if (png_get_gAMA(png, info, &gamma))
+ png_set_gamma(png, 2.2, gamma);
+ else
+ png_set_gamma(png, 2.2, 0.45455);
+ }
+
+
+ png_read_update_info(png, info);
+
+ rowbytes = png_get_rowbytes(png, info);
+ interlace = (interlace == PNG_INTERLACE_ADAM7);
+ raw_width = width;
+ raw_height = height;
+
+ rowstride = raw_width * 4;
+ bitmap_data = malloc(rowstride * raw_height);
+}
+
+static unsigned int interlace_start[8] = {0, 16, 0, 8, 0, 4, 0};
+static unsigned int interlace_step[8] = {28, 28, 12, 12, 4, 4, 0};
+static unsigned int interlace_row_start[8] = {0, 0, 4, 0, 2, 0, 1};
+static unsigned int interlace_row_step[8] = {8, 8, 8, 4, 4, 2, 2};
+
+static void
+row_callback(png_structp png, png_bytep new_row,
+ png_uint_32 row_num, int pass)
+{
+ unsigned long i, j;
+ unsigned int start, step;
+ unsigned char *row = bitmap_data + (rowstride * row_num);
+
+ if (new_row == 0)
+ return;
+
+ if (interlace) {
+ start = interlace_start[pass];
+ step = interlace_step[pass];
+ row_num = interlace_row_start[pass] +
+ interlace_row_step[pass] * row_num;
+
+ /* Copy the data to our current row taking interlacing
+ * into consideration */
+ row = bitmap_data + (rowstride * row_num);
+ for (j = 0, i = start; i < rowbytes; i += step) {
+ row[i++] = new_row[j++];
+ row[i++] = new_row[j++];
+ row[i++] = new_row[j++];
+ row[i++] = new_row[j++];
+ }
+ } else {
+ memcpy(row, new_row, rowbytes);
+ }
+}
+
+static void
+end_callback(png_structp png, png_infop info)
+{
+}
+
+
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */
+
diff --git a/frontends/framebuffer/fb_search.c b/frontends/framebuffer/fb_search.c
new file mode 100644
index 000000000..19fefa8b2
--- /dev/null
+++ b/frontends/framebuffer/fb_search.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include <string.h>
+
+#include "utils/log.h"
+
+/* callback functions for search implementation */
+static void gui_search_set_status(bool found, void *p);
+static void gui_search_set_hourglass(bool active, void *p);
+static void gui_search_add_recent(const char *string, void *p);
+static void gui_search_set_forward_state(bool active, void *p);
+static void gui_search_set_back_state(bool active, void *p);
+
+/**
+* Change the displayed search status.
+* \param found search pattern matched in text
+* \param p the pointer sent to search_verify_new() / search_create_context()
+*/
+void gui_search_set_status(bool found, void *p)
+{
+}
+
+/**
+* display hourglass while searching
+* \param active start/stop indicator
+* \param p the pointer sent to search_verify_new() / search_create_context()
+*/
+void gui_search_set_hourglass(bool active, void *p)
+{
+}
+
+/**
+* add search string to recent searches list
+* \param string search pattern
+* \param p the pointer sent to search_verify_new() / search_create_context()
+*/
+void gui_search_add_recent(const char *string, void *p)
+{
+}
+
+/**
+* activate search forwards button in gui
+* \param active activate/inactivate
+* \param p the pointer sent to search_verify_new() / search_create_context()
+*/
+void gui_search_set_forward_state(bool active, void *p)
+{
+}
+
+/**
+* activate search forwards button in gui
+* \param active activate/inactivate
+* \param p the pointer sent to search_verify_new() / search_create_context()
+*/
+void gui_search_set_back_state(bool active, void *p)
+{
+}
diff --git a/frontends/framebuffer/fbtk.h b/frontends/framebuffer/fbtk.h
new file mode 100644
index 000000000..6ad06edff
--- /dev/null
+++ b/frontends/framebuffer/fbtk.h
@@ -0,0 +1,616 @@
+/*
+ * Copyright 2008,2010 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_FB_FBTK_H
+#define NETSURF_FB_FBTK_H
+
+#ifdef FBTK_LOGGING
+#define FBTK_LOG(x) LOG(x)
+#else
+#define FBTK_LOG(x)
+#endif
+
+#define FB_SCROLL_COLOUR 0xFFAAAAAA
+#define FB_FRAME_COLOUR 0xFFDDDDDD
+#define FB_COLOUR_BLACK 0xFF000000
+#define FB_COLOUR_WHITE 0xFFFFFFFF
+
+#define FBTK_WIDGET_PADDING 30 /**< percentage of widget size used for padding */
+#define FBTK_DPI 90 /**< screen DPI */
+
+typedef struct fbtk_widget_s fbtk_widget_t;
+
+/** Widget Callback type */
+typedef enum fbtk_callback_type {
+ FBTK_CBT_START = 0,
+ FBTK_CBT_SCROLLX,
+ FBTK_CBT_SCROLLY,
+ FBTK_CBT_CLICK,
+ FBTK_CBT_INPUT,
+ FBTK_CBT_POINTERMOVE,
+ FBTK_CBT_POINTERLEAVE,
+ FBTK_CBT_POINTERENTER,
+ FBTK_CBT_REDRAW,
+ FBTK_CBT_DESTROY,
+ FBTK_CBT_USER,
+ FBTK_CBT_STRIP_FOCUS,
+ FBTK_CBT_END,
+} fbtk_callback_type;
+
+/** widget callback information */
+typedef struct fbtk_callback_info {
+ enum fbtk_callback_type type;
+ void *context;
+ nsfb_event_t *event;
+ int x;
+ int y;
+ char *text;
+ fbtk_widget_t *widget;
+} fbtk_callback_info;
+
+/** framebuffer toolkit bitmaps */
+struct fbtk_bitmap {
+ int width;
+ int height;
+ uint8_t *pixdata;
+ bool opaque;
+
+ /* The following two are only used for cursors */
+ int hot_x;
+ int hot_y;
+};
+
+/** Key modifier status */
+typedef enum fbtk_modifier_type {
+ FBTK_MOD_CLEAR = 0,
+ FBTK_MOD_LSHIFT = (1 << 0),
+ FBTK_MOD_RSHIFT = (1 << 1),
+ FBTK_MOD_LCTRL = (1 << 2),
+ FBTK_MOD_RCTRL = (1 << 3)
+} fbtk_modifier_type;
+
+typedef int (*fbtk_callback)(fbtk_widget_t *widget, fbtk_callback_info *cbi);
+
+/* enter pressed on writable icon */
+typedef int (*fbtk_enter_t)(void *pw, char *text);
+
+
+/************************ Core ****************************/
+
+
+/**
+ * Initialise widget toolkit.
+ *
+ * Initialises widget toolkit against a framebuffer.
+ *
+ * @param fb The underlying framebuffer.
+ * @return The root widget handle.
+ */
+fbtk_widget_t *fbtk_init(nsfb_t *fb);
+
+/**
+ * Retrieve the framebuffer library handle from toolkit widget.
+ *
+ * @param widget A fbtk widget.
+ * @return The underlying framebuffer.
+ */
+nsfb_t *fbtk_get_nsfb(fbtk_widget_t *widget);
+
+/** Perform any pending widget redraws.
+ *
+ * @param widget A fbtk widget.
+ */
+int fbtk_redraw(fbtk_widget_t *widget);
+
+/** Determine if there are any redraws pending for a widget.
+ *
+ * Mainly used by clients on the root widget to determine if they need
+ * to call ::fbtk_redraw
+ *
+ * @param widget to check.
+ */
+bool fbtk_get_redraw_pending(fbtk_widget_t *widget);
+
+/** clip a bounding box to a widgets area.
+ */
+bool fbtk_clip_to_widget(fbtk_widget_t *widget, bbox_t * restrict box);
+
+/** clip one bounding box to another.
+ */
+bool fbtk_clip_rect(const bbox_t * restrict clip, bbox_t * restrict box);
+
+/***************** Callback processing ********************/
+
+/** Helper function to allow simple calling of callbacks with parameters.
+ *
+ * @param widget The fbtk widget to post the callback to.
+ * @param cbt The type of callback to post
+ * @param ... Parameters appropriate for the callback type.
+ */
+int fbtk_post_callback(fbtk_widget_t *widget, fbtk_callback_type cbt, ...);
+
+/** Set a callback handler.
+ *
+ * Set a callback handler and the pointer to pass for a widget.
+ *
+ * @param widget The widget to set the handler for.
+ * @param cbt The type of callback to set.
+ * @param cb The callback.
+ * @param pw The private pointer to pass when calling the callback.
+ * @return The previous callback handler for the type or NULL.
+ */
+fbtk_callback fbtk_set_handler(fbtk_widget_t *widget, fbtk_callback_type cbt, fbtk_callback cb, void *pw);
+
+/** Get a callback handler.
+ */
+fbtk_callback fbtk_get_handler(fbtk_widget_t *widget, fbtk_callback_type cbt);
+
+
+/******************* Event processing **********************/
+
+/** Retrive events from the framebuffer input.
+ *
+ * Obtain events from the framebuffer input system with a
+ * timeout. Some events may be used by the toolkit instead of being
+ * returned to the caller.
+ *
+ * @param root An fbtk widget.
+ * @param event an event structure to update.
+ * @param timeout The number of miliseconds to wait for an event. 0
+ * means do not wait and -1 means wait foreevr.
+ * @return wether \a event has been updated.
+ */
+bool fbtk_event(fbtk_widget_t *root, nsfb_event_t *event, int timeout);
+
+/** Insert mouse button press into toolkit.
+ */
+void fbtk_click(fbtk_widget_t *widget, nsfb_event_t *event);
+
+/** Insert input into toolkit.
+ */
+void fbtk_input(fbtk_widget_t *widget, nsfb_event_t *event);
+
+/** Move pointer.
+ *
+ * Move the pointer cursor to a given location.
+ *
+ * @param widget any tookit widget.
+ * @param x movement in horizontal plane.
+ * @param y movement in vertical plane.
+ * @param relative Wheter the /a x and /a y should be considered relative to
+ * current pointer position.
+ */
+void fbtk_warp_pointer(fbtk_widget_t *widget, int x, int y, bool relative);
+
+/** Toggle pointer grab.
+ *
+ * Toggles the movement grab for a widget.
+ *
+ * @param widget The widget trying to grab the movement.
+ * @return true if the grab was ok, false if the grab failed (already grabbed).
+ */
+bool fbtk_tgrab_pointer(fbtk_widget_t *widget);
+
+/** Convert a framebuffer keycode to ucs4.
+ *
+ * Character mapping between keycode with modifier state and ucs-4.
+ */
+int fbtk_keycode_to_ucs4(int code, fbtk_modifier_type mods);
+
+
+/******************* Widget Information **********************/
+
+/** Obtain the widget at a point on screen.
+ *
+ * @param widget any tookit widget.
+ * @param x location in horizontal plane.
+ * @param y location in vertical plane.
+ * @return widget or NULL.
+ */
+fbtk_widget_t *fbtk_get_widget_at(fbtk_widget_t *widget, int x, int y);
+
+/** Get a widget's absolute horizontal screen co-ordinate.
+ *
+ * @param widget The widget to inspect.
+ * @return The absolute screen co-ordinate.
+ */
+int fbtk_get_absx(fbtk_widget_t *widget);
+
+/** Get a widget's absolute vertical screen co-ordinate.
+ *
+ * @param widget The widget to inspect.
+ * @return The absolute screen co-ordinate.
+ */
+int fbtk_get_absy(fbtk_widget_t *widget);
+
+/**
+ * Get a widget's width.
+ *
+ * @param widget The widget to inspect.
+ * @return The widget width.
+ */
+int fbtk_get_width(fbtk_widget_t *widget);
+
+/**
+ * Get a widget's height.
+ *
+ * @param widget The widget to inspect.
+ * @return The widget height.
+ */
+int fbtk_get_height(fbtk_widget_t *widget);
+
+/**
+ * Get a widget's bounding box in absolute screen co-ordinates.
+ *
+ * @param widget The widget to inspect.
+ * @param bbox The bounding box structure to update.
+ * @return If the \a bbox parameter has been updated.
+ */
+bool fbtk_get_bbox(fbtk_widget_t *widget, struct nsfb_bbox_s *bbox);
+
+/**
+ * Get a widget caret pos, if it owns caret.
+ *
+ * @param widget The widget to inspect.
+ * @param x If widget has caret, returns x-coord of caret within widget
+ * @param y If widget has caret, returns y-coord of caret within widget
+ * @param height If widget has caret, returns caret height
+ * @return true iff widget has caret
+ */
+bool fbtk_get_caret(fbtk_widget_t *widget, int *x, int *y, int *height);
+
+
+/******************* Widget Manipulation **********************/
+
+/**
+ * Change the widget's position and size. (Doesn't redraw)
+ *
+ */
+bool fbtk_set_pos_and_size(fbtk_widget_t *widget, int x, int y, int width, int height);
+
+/**
+ * Set caret owner and position
+ *
+ * @param widget widget to give caret to, or ensure caret is released from
+ * @param set true: caret to be set for widget, false: caret to be released
+ * @param x x-coordinate of caret top
+ * @param y y-coordinate of caret top
+ * @param height height of caret
+ * @param remove_caret callback when caret is removed.
+ */
+void fbtk_set_caret(fbtk_widget_t *widget, bool set, int x, int y, int height,
+ void (*remove_caret)(fbtk_widget_t *widget));
+
+/**
+ * Map a widget and request it is redrawn.
+ */
+int fbtk_set_mapping(fbtk_widget_t *widget, bool mapped);
+
+/**
+ * Set the z order of a widget.
+ */
+int fbtk_set_zorder(fbtk_widget_t *widget, int z);
+
+/**
+ * Indicate a widget should be redrawn.
+ */
+void fbtk_request_redraw(fbtk_widget_t *widget);
+
+/**
+ * Destroy a widget and all its descendants.
+ *
+ * Removes a widget from the hierachy and frees it and all its children.
+ *
+ * @param widget The widget to destroy.
+ * @return 0 on success or -1 on error.
+ */
+int fbtk_destroy_widget(fbtk_widget_t *widget);
+
+
+
+/********************************* Widgets *********************************/
+
+
+/**
+ * Create a window widget.
+ *
+ * @param parent The parent window or the root widget for a top level window.
+ * @param x The x location relative to the parent window.
+ * @param y the y location relative to the parent window.
+ * @param width The width of the window. 0 indicates parents width should be
+ * used. Negative value indicates parents width less the value
+ * should be used. The width is limited to lie within the parent
+ * window.
+ * @param height The height of the window limited in a similar way to the
+ * /a width.
+ * @param bg The background colour.
+ * @return new window widget handle or NULL on error.
+ */
+fbtk_widget_t *fbtk_create_window(fbtk_widget_t *parent, int x, int y, int width, int height, colour bg);
+
+
+
+/**
+ * Create a filled rectangle
+ *
+ * Create a widget which is a filled rectangle, usually used for backgrounds.
+ *
+ * @param window The window to add the filled area widget to.
+ * @param x X coordinate of widget.
+ * @param y Y coordinate of widget.
+ * @param width Width of the widget
+ * @param height Height of the widget
+ * @param c widget colour
+ * @return new widget handle or NULL on error.
+ */
+fbtk_widget_t *
+fbtk_create_fill(fbtk_widget_t *window, int x, int y, int width, int height, colour c);
+
+
+/**
+ * Create a horizontal scroll widget
+ *
+ * Create a horizontal scroll widget.
+ *
+ * @param window The window to add the filled area widget to.
+ * @param x X coordinate of widget.
+ * @param y Y coordinate of widget.
+ * @param width Width of the widget
+ * @param height Height of the widget
+ * @param bg background colour
+ * @param fg foreground colour
+ * @param callback Called on scroll
+ * @param context context passed to callback.
+ * @return new widget handle or NULL on error.
+ */
+fbtk_widget_t *
+fbtk_create_hscroll(fbtk_widget_t *window, int x, int y, int width, int height, colour fg, colour bg, fbtk_callback callback, void *context);
+
+/**
+ * Create a vertical scroll widget
+ *
+ * Create a vertical scroll widget.
+ *
+ * @param window The window to add the filled area widget to.
+ * @param x X coordinate of widget.
+ * @param y Y coordinate of widget.
+ * @param width Width of the widget
+ * @param height Height of the widget
+ * @param bg background colour
+ * @param fg foreground colour
+ * @param callback Called on scroll
+ * @param context context passed to callback.
+ * @return new widget handle or NULL on error.
+ */
+fbtk_widget_t *
+fbtk_create_vscroll(fbtk_widget_t *window, int x, int y, int width, int height, colour fg, colour bg, fbtk_callback callback, void *context);
+
+/**
+ * Set scoll widget parameters
+ *
+ * @param widget The widget to set the parameters for.
+ * @param min The minimum range value.
+ * @param max The maximum range value.
+ * @param thumb The size of the slider.
+ * @param page The amout to scroll for a page.
+ * @return true if the scroll parameter was set else false.
+ */
+bool fbtk_set_scroll_parameters(fbtk_widget_t *widget, int min, int max, int thumb, int page);
+
+/**
+ * set scroll widget position.
+ *
+ * @param widget The widget to set the position on.
+ * @param pos The position to set
+ * @return true if the scroll parameter was set else false.
+ */
+bool fbtk_set_scroll_position(fbtk_widget_t *widget, int pos);
+
+
+/**
+ * Move and/or resize a horizontal scroll widget
+ *
+ * @param scrollh the horizontal scroll widget
+ * @param x new x pos
+ * @param y new y pos
+ * @param width new width
+ * @param height new height
+ */
+void fbtk_reposition_hscroll(fbtk_widget_t *scrollh,
+ int x, int y, int width, int height);
+
+/**
+ * Move and/or resize a vertical scroll widget
+ *
+ * @param scrollv the vertical scroll widget
+ * @param x new x pos
+ * @param y new y pos
+ * @param width new width
+ * @param height new height
+ */
+void fbtk_reposition_vscroll(fbtk_widget_t *scrollv,
+ int x, int y, int width, int height);
+
+
+/**
+ * Create a user widget.
+ *
+ * Create a widget which is to be handled entirely by the calling application.
+ *
+ * @param window The window to add the user widget to.
+ * @param x X coordinate of widget.
+ * @param y Y coordinate of widget.
+ * @param width Width of the widget
+ * @param height Height of the widget
+ * @param pw The private pointer which can be read using ::fbtk_get_userpw
+ * @return new widget handle or NULL on error.
+ */
+fbtk_widget_t *fbtk_create_user(fbtk_widget_t *window, int x, int y, int width, int height, void *pw);
+
+
+/**
+ * Get the user context from a widget
+ *
+ * @param widget The widget to get the context from.
+ * @return The context or NULL.
+ */
+void *fbtk_get_userpw(fbtk_widget_t *widget);
+
+
+/**
+ * Create a bitmap widget.
+ *
+ * Create a widget which shows a bitmap.
+ *
+ * @param window The window to add the bitmap widget to.
+ * @param x X coordinate of widget.
+ * @param y Y coordinate of widget.
+ * @param width Width of the widget
+ * @param height Height of the widget
+ * @param c background colour
+ * @param image The bitmap to put in the widget
+ * @return new widget handle or NULL on error.
+ */
+fbtk_widget_t *fbtk_create_bitmap(fbtk_widget_t *window, int x, int y, int width, int height, colour c, struct fbtk_bitmap *image);
+
+
+/**
+ * Change the bitmap in a widget.
+ *
+ * @param widget The widget to get the context from.
+ * @param image The bitmap to put in the widget
+ */
+void fbtk_set_bitmap(fbtk_widget_t *widget, struct fbtk_bitmap *image);
+
+
+/**
+ * Create a button widget with an image.
+ *
+ * Helper function which creates a bitmap widget and associate a handler for
+ * when it is clicked.
+ *
+ * @param window The window to add the button widget to.
+ * @param x X coordinate of widget.
+ * @param y Y coordinate of widget.
+ * @param width Width of the widget
+ * @param height Height of the widget
+ * @param c background colour
+ * @param image The bitmap to put in the widget
+ * @param click The callback upon a click
+ * @param pw The context tp pass to the callback
+ * @return new widget handle or NULL on error.
+ */
+fbtk_widget_t *fbtk_create_button(fbtk_widget_t *window, int x, int y, int width, int height, colour c, struct fbtk_bitmap *image, fbtk_callback click, void *pw);
+
+
+/**
+ * Create a text widget.
+ *
+ * @param window The window to add the text widget to.
+ * @param x X coordinate of widget.
+ * @param y Y coordinate of widget.
+ * @param width Width of the widget
+ * @param height Height of the widget
+ * @param bg background colour
+ * @param fg foreground colour
+ * @param outline widget will have a border.
+ * @return new widget handle or NULL on error.
+ */
+fbtk_widget_t *fbtk_create_text(fbtk_widget_t *window, int x, int y, int width, int height, colour bg, colour fg, bool outline);
+
+
+/**
+ * Create a button with text.
+ *
+ * @param window The window to add the text widget to.
+ * @param x X coordinate of widget.
+ * @param y Y coordinate of widget.
+ * @param width Width of the widget
+ * @param height Height of the widget
+ * @param bg background colour
+ * @param fg foreground colour
+ * @param click The callback upon a click
+ * @param pw The context tp pass to the callback
+ * @return new widget handle or NULL on error.
+ */
+fbtk_widget_t *fbtk_create_text_button(fbtk_widget_t *window, int x, int y, int width, int height, colour bg, colour fg, fbtk_callback click, void *pw);
+
+
+/**
+ * Create a writable text widget.
+ *
+ * Helper function which creates a text widget and configures an input handler
+ * to create a writable text field. This call is equivalent to calling
+ * ::fbtk_create_text followed by ::fbtk_writable_text
+ *
+ * @param window The window to add the text widget to.
+ * @param x X coordinate of widget.
+ * @param y Y coordinate of widget.
+ * @param width Width of the widget
+ * @param height Height of the widget
+ * @param bg background colour
+ * @param fg foreground colour
+ * @param outline widget will have a border.
+ * @param enter Callback when enter is pressed in widget.
+ * @param pw Context pointer passed to entry callback.
+ * @return new widget handle or NULL on error.
+ */
+fbtk_widget_t *fbtk_create_writable_text(fbtk_widget_t *window, int x, int y, int width, int height, colour bg, colour fg, bool outline, fbtk_enter_t enter, void *pw);
+
+
+/**
+ * Alter a text widget to be writable.
+ *
+ * @param widget Text widget.
+ * @param enter The routine to call when enter is pressed.
+ * @param pw The context to pass to the enter callback routine.
+ */
+void fbtk_writable_text(fbtk_widget_t *widget, fbtk_enter_t enter, void *pw);
+
+
+/**
+ * Change the text of a text widget.
+ *
+ * @param widget Text widget.
+ * @param text The new UTF-8 text to put in the widget.
+ */
+void fbtk_set_text(fbtk_widget_t *widget, const char *text);
+
+
+/**
+ * Give widget input focus.
+ *
+ * @param widget Widget to be given input focus.
+ */
+void fbtk_set_focus(fbtk_widget_t *widget);
+
+
+/**
+ * enable the on screen keyboard for input
+ *
+ * @param widget Widget to be given input focus.
+ */
+void fbtk_enable_oskb(fbtk_widget_t *widget);
+
+
+/**
+ * show the osk.
+ */
+void map_osk(void);
+
+#endif
diff --git a/frontends/framebuffer/fbtk/bitmap.c b/frontends/framebuffer/fbtk/bitmap.c
new file mode 100644
index 000000000..1f147be00
--- /dev/null
+++ b/frontends/framebuffer/fbtk/bitmap.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2010 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * Framebuffer windowing toolkit bitmaped image widget
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include <stdlib.h>
+
+#include <libnsfb.h>
+#include <libnsfb_plot.h>
+
+#include "desktop/browser.h"
+
+#include "framebuffer/gui.h"
+#include "framebuffer/fbtk.h"
+#include "framebuffer/image_data.h"
+
+#include "widget.h"
+
+static int
+fb_redraw_bitmap(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ nsfb_bbox_t bbox;
+ nsfb_bbox_t rect;
+ nsfb_t *nsfb;
+
+ nsfb = fbtk_get_nsfb(widget);
+
+ fbtk_get_bbox(widget, &bbox);
+
+ rect = bbox;
+
+ nsfb_claim(nsfb, &bbox);
+
+ /* clear background */
+ if ((widget->bg & 0xFF000000) != 0) {
+ /* transparent polygon filling isnt working so fake it */
+ nsfb_plot_rectangle_fill(nsfb, &bbox, widget->bg);
+ }
+
+ /* plot the image */
+ nsfb_plot_bitmap(nsfb,
+ &rect,
+ (nsfb_colour_t *)widget->u.bitmap.bitmap->pixdata,
+ widget->u.bitmap.bitmap->width,
+ widget->u.bitmap.bitmap->height,
+ widget->u.bitmap.bitmap->width,
+ !widget->u.bitmap.bitmap->opaque);
+
+ nsfb_update(nsfb, &bbox);
+
+ return 0;
+}
+
+/* exported function documented in fbtk.h */
+void
+fbtk_set_bitmap(fbtk_widget_t *widget, struct fbtk_bitmap *image)
+{
+ if ((widget == NULL) || (widget->type != FB_WIDGET_TYPE_BITMAP))
+ return;
+
+ widget->u.bitmap.bitmap = image;
+
+ fbtk_request_redraw(widget);
+}
+
+/* exported function documented in fbtk.h */
+fbtk_widget_t *
+fbtk_create_bitmap(fbtk_widget_t *parent,
+ int x,
+ int y,
+ int width,
+ int height,
+ colour c,
+ struct fbtk_bitmap *image)
+{
+ fbtk_widget_t *neww;
+
+ neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_BITMAP, x, y, width, height);
+
+ neww->bg = c;
+ neww->mapped = true;
+ neww->u.bitmap.bitmap = image;
+
+ fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_bitmap, NULL);
+
+ return neww;
+}
+
+/* exported function documented in fbtk.h */
+fbtk_widget_t *
+fbtk_create_button(fbtk_widget_t *parent,
+ int x,
+ int y,
+ int width,
+ int height,
+ colour c,
+ struct fbtk_bitmap *image,
+ fbtk_callback click,
+ void *pw)
+{
+ fbtk_widget_t *neww;
+
+ neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_BITMAP, x, y, width, height);
+
+ neww->bg = c;
+ neww->mapped = true;
+ neww->u.bitmap.bitmap = image;
+
+ fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_bitmap, NULL);
+ fbtk_set_handler(neww, FBTK_CBT_CLICK, click, pw);
+ fbtk_set_handler(neww, FBTK_CBT_POINTERENTER, fbtk_set_ptr, &hand_image);
+
+ return neww;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */
diff --git a/frontends/framebuffer/fbtk/event.c b/frontends/framebuffer/fbtk/event.c
new file mode 100644
index 000000000..c0894921e
--- /dev/null
+++ b/frontends/framebuffer/fbtk/event.c
@@ -0,0 +1,349 @@
+/*
+ * Copyright 2010 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * Framebuffer windowing toolkit event processing.
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <sys/types.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include <libnsfb.h>
+#include <libnsfb_plot.h>
+#include <libnsfb_plot_util.h>
+#include <libnsfb_event.h>
+#include <libnsfb_cursor.h>
+
+#include "utils/utils.h"
+#include "utils/log.h"
+#include "desktop/browser.h"
+#include "desktop/textinput.h"
+
+#include "framebuffer/gui.h"
+#include "framebuffer/fbtk.h"
+#include "framebuffer/image_data.h"
+
+#include "widget.h"
+
+/* exported function documented in fbtk.h */
+void
+fbtk_input(fbtk_widget_t *root, nsfb_event_t *event)
+{
+ fbtk_widget_t *input;
+
+ root = fbtk_get_root_widget(root);
+
+ /* obtain widget with input focus */
+ input = root->u.root.input;
+ if (input == NULL) {
+ LOG("No widget has input focus.");
+ return; /* no widget with input */
+ }
+
+ fbtk_post_callback(input, FBTK_CBT_INPUT, event);
+}
+
+/* exported function documented in fbtk.h */
+void
+fbtk_click(fbtk_widget_t *widget, nsfb_event_t *event)
+{
+ fbtk_widget_t *root;
+ fbtk_widget_t *clicked;
+ nsfb_bbox_t cloc;
+ int x, y;
+
+ /* ensure we have the root widget */
+ root = fbtk_get_root_widget(widget);
+
+ nsfb_cursor_loc_get(root->u.root.fb, &cloc);
+
+ clicked = fbtk_get_widget_at(root, cloc.x0, cloc.y0);
+
+ if (clicked == NULL)
+ return;
+
+ if (fbtk_get_handler(clicked, FBTK_CBT_INPUT) != NULL) {
+ fbtk_set_focus(clicked);
+ }
+
+ x = fbtk_get_absx(clicked);
+ y = fbtk_get_absy(clicked);
+
+ LOG("clicked %p at %d,%d", clicked, x, y);
+
+ /* post the click */
+ fbtk_post_callback(clicked, FBTK_CBT_CLICK, event, cloc.x0 - x, cloc.y0 - y);
+}
+
+/* exported function documented in fbtk.h */
+bool
+fbtk_tgrab_pointer(fbtk_widget_t *widget)
+{
+ fbtk_widget_t *root;
+
+ /* ensure we have the root widget */
+ root = fbtk_get_root_widget(widget);
+
+ if (root->u.root.grabbed == widget) {
+ /* release pointer grab */
+ root->u.root.grabbed = NULL;
+ return true;
+ } else if (root->u.root.grabbed == NULL) {
+ /* set pointer grab */
+ root->u.root.grabbed = widget;
+ return true;
+ }
+ /* pointer was already grabbed */
+ return false;
+}
+
+/* exported function documented in fbtk.h */
+void
+fbtk_warp_pointer(fbtk_widget_t *widget, int x, int y, bool relative)
+{
+ fbtk_widget_t *root;
+ fbtk_widget_t *moved;
+ nsfb_bbox_t cloc;
+
+ /* ensure we have the root widget */
+ root = fbtk_get_root_widget(widget);
+
+ if (relative) {
+ nsfb_cursor_loc_get(root->u.root.fb, &cloc);
+ cloc.x0 += x;
+ cloc.y0 += y;
+ } else {
+ cloc.x0 = x;
+ cloc.y0 = y;
+ }
+
+ /* ensure cursor location lies within the root widget */
+ if (cloc.x0 < root->x)
+ cloc.x0 = root->x;
+ if (cloc.x0 >= (root->x + root->width))
+ cloc.x0 = (root->x + root->width) - 1;
+ if (cloc.y0 < root->y)
+ cloc.y0 = root->y;
+ if (cloc.y0 >= (root->y + root->height))
+ cloc.y0 = (root->y + root->height) - 1;
+
+ if (root->u.root.grabbed == NULL) {
+ /* update the pointer cursor */
+ nsfb_cursor_loc_set(root->u.root.fb, &cloc);
+
+ moved = fbtk_get_widget_at(root, cloc.x0, cloc.y0);
+
+ x = fbtk_get_absx(moved);
+ y = fbtk_get_absy(moved);
+
+ /* post enter and leaving messages */
+ if (moved != root->u.root.prev) {
+ fbtk_post_callback(root->u.root.prev, FBTK_CBT_POINTERLEAVE);
+ root->u.root.prev = moved;
+ fbtk_post_callback(root->u.root.prev, FBTK_CBT_POINTERENTER);
+ }
+ } else {
+ /* pointer movement has been grabbed by a widget */
+ moved = root->u.root.grabbed;
+
+ /* ensure pointer remains within widget boundary */
+ x = fbtk_get_absx(moved);
+ y = fbtk_get_absy(moved);
+
+ if (cloc.x0 < x)
+ cloc.x0 = x;
+ if (cloc.y0 < y)
+ cloc.y0 = y;
+ if (cloc.x0 > (x + moved->width))
+ cloc.x0 = (x + moved->width);
+ if (cloc.y0 > (y + moved->height))
+ cloc.y0 = (y + moved->height);
+
+ /* update the pointer cursor */
+ nsfb_cursor_loc_set(root->u.root.fb, &cloc);
+ }
+
+ /* post the movement */
+ fbtk_post_callback(moved, FBTK_CBT_POINTERMOVE, cloc.x0 - x, cloc.y0 - y);
+
+}
+
+/* exported function documented in fbtk.h */
+bool
+fbtk_event(fbtk_widget_t *root, nsfb_event_t *event, int timeout)
+{
+ nsfb_bbox_t cloc;
+ bool unused = false; /* is the event available */
+ bool move_pointer = false; /* whether pointer move events occured */
+
+ /* ensure we have the root widget */
+ root = fbtk_get_root_widget(root);
+
+ do {
+ if (nsfb_event(root->u.root.fb, event, timeout) == false) {
+ if (move_pointer)
+ fbtk_warp_pointer(root, cloc.x0, cloc.y0,
+ false);
+ return false;
+ }
+
+ if (move_pointer && event->type != NSFB_EVENT_MOVE_RELATIVE &&
+ event->type != NSFB_EVENT_MOVE_ABSOLUTE) {
+ /* Flush the movements */
+ fbtk_warp_pointer(root, cloc.x0, cloc.y0, false);
+
+ } else if (!move_pointer &&
+ event->type == NSFB_EVENT_MOVE_RELATIVE) {
+ /* Get current pointer coords */
+ nsfb_cursor_loc_get(root->u.root.fb, &cloc);
+ }
+
+ switch (event->type) {
+ case NSFB_EVENT_KEY_DOWN:
+ case NSFB_EVENT_KEY_UP:
+ if ((event->value.keycode >= NSFB_KEY_MOUSE_1) &&
+ (event->value.keycode <= NSFB_KEY_MOUSE_5)) {
+ fbtk_click(root, event);
+ } else {
+ fbtk_input(root, event);
+ }
+ break;
+
+ case NSFB_EVENT_CONTROL:
+ unused = true;
+ break;
+
+ case NSFB_EVENT_MOVE_RELATIVE:
+ /* Consecutive move events are consolidated into a
+ * single pointer warp */
+ move_pointer = true;
+ cloc.x0 += event->value.vector.x;
+ cloc.y0 += event->value.vector.y;
+ timeout = 0;
+ break;
+
+ case NSFB_EVENT_MOVE_ABSOLUTE:
+ /* Consecutive move events are consolidated into a
+ * single pointer warp */
+ move_pointer = true;
+ cloc.x0 = event->value.vector.x;
+ cloc.y0 = event->value.vector.y;
+ timeout = 0;
+ break;
+
+ case NSFB_EVENT_RESIZE:
+ /* Try to resize framebuffer */
+ gui_resize(root,
+ event->value.resize.w,
+ event->value.resize.h);
+ break;
+
+ default:
+ break;
+ }
+ } while (event->type == NSFB_EVENT_MOVE_RELATIVE ||
+ event->type == NSFB_EVENT_MOVE_ABSOLUTE);
+ return unused;
+}
+
+static int keymap[] = {
+ /* 0 1 2 3 4 5 6 7 8 9 */
+ -1, -1, -1, -1, -1, -1, -1, -1, 8, 9, /* 0 - 9 */
+ -1, -1, -1, 13, -1, -1, -1, -1, -1, -1, /* 10 - 19 */
+ -1, -1, -1, -1, -1, -1, -1, 27, -1, -1, /* 20 - 29 */
+ -1, -1, ' ', '!', '"', '#', '$', -1, '&','\'', /* 30 - 39 */
+ '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', /* 40 - 49 */
+ '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', /* 50 - 59 */
+ '<', '=', '>', '?', '@', -1, -1, -1, -1, -1, /* 60 - 69 */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 70 - 79 */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 - 89 */
+ -1, '[','\\', ']', '~', '_', '`', 'a', 'b', 'c', /* 90 - 99 */
+ 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', /* 100 - 109 */
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* 110 - 119 */
+ 'x', 'y', 'z', -1, -1, -1, -1, -1, -1, -1, /* 120 - 129 */
+};
+
+static int sh_keymap[] = {
+ /* 0 1 2 3 4 5 6 7 8 9 */
+ -1, -1, -1, -1, -1, -1, -1, -1, 8, 9, /* 0 - 9 */
+ -1, -1, -1, 13, -1, -1, -1, -1, -1, -1, /* 10 - 19 */
+ -1, -1, -1, -1, -1, -1, -1, 27, -1, -1, /* 20 - 29 */
+ -1, -1, ' ', '!', '"', '~', '$', -1, '&', '@', /* 30 - 39 */
+ '(', ')', '*', '+', '<', '_', '>', '?', ')', '!', /* 40 - 49 */
+ '"', 243, '$', '%', '^', '&', '*', '(', ';', ':', /* 50 - 59 */
+ '<', '+', '>', '?', '@', -1, -1, -1, -1, -1, /* 60 - 69 */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 70 - 79 */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 - 89 */
+ -1, '{', '|', '}', '~', '_', 254, 'A', 'B', 'C', /* 90 - 99 */
+ 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', /* 100 - 109 */
+ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* 110 - 119 */
+ 'X', 'Y', 'Z', -1, -1, -1, -1, -1, -1, -1, /* 120 - 129 */
+};
+
+
+/* exported function documented in fbtk.h */
+int
+fbtk_keycode_to_ucs4(int code, fbtk_modifier_type mods)
+{
+ int ucs4 = -1;
+
+ if (mods & FBTK_MOD_LSHIFT || mods & FBTK_MOD_RSHIFT) {
+ if ((code >= 0) && (code < (int) NOF_ELEMENTS(sh_keymap)))
+ ucs4 = sh_keymap[code];
+
+ } else if (mods == FBTK_MOD_CLEAR) {
+ if ((code >= 0) && (code < (int) NOF_ELEMENTS(keymap)))
+ ucs4 = keymap[code];
+
+ } else if (mods & FBTK_MOD_LCTRL || mods & FBTK_MOD_RCTRL) {
+ switch (code) {
+ case NSFB_KEY_a:
+ ucs4 = NS_KEY_SELECT_ALL;
+ break;
+
+ case NSFB_KEY_c:
+ ucs4 = NS_KEY_COPY_SELECTION;
+ break;
+
+ case NSFB_KEY_u:
+ ucs4 = NS_KEY_DELETE_LINE;
+ break;
+
+ case NSFB_KEY_v:
+ ucs4 = NS_KEY_PASTE;
+ break;
+
+ case NSFB_KEY_x:
+ ucs4 = NS_KEY_CUT_SELECTION;
+ break;
+
+ case NSFB_KEY_z:
+ ucs4 = NS_KEY_CLEAR_SELECTION;
+ break;
+ default:
+ break;
+ }
+ }
+ return ucs4;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */
diff --git a/frontends/framebuffer/fbtk/fbtk.c b/frontends/framebuffer/fbtk/fbtk.c
new file mode 100644
index 000000000..db1c1f1a9
--- /dev/null
+++ b/frontends/framebuffer/fbtk/fbtk.c
@@ -0,0 +1,830 @@
+/*
+ * Copyright 2008,2010 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * Framebuffer windowing toolkit core.
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdarg.h>
+
+#include <libnsfb.h>
+#include <libnsfb_plot.h>
+#include <libnsfb_plot_util.h>
+#include <libnsfb_event.h>
+#include <libnsfb_cursor.h>
+
+#include "utils/utils.h"
+#include "utils/log.h"
+#include "desktop/browser.h"
+#include "desktop/plotters.h"
+
+#include "framebuffer/gui.h"
+#include "framebuffer/fbtk.h"
+#include "framebuffer/image_data.h"
+
+#include "widget.h"
+
+#ifdef FBTK_LOGGING
+
+/* tree dump debug, also example of depth first tree walk */
+static void
+dump_tk_tree(fbtk_widget_t *widget)
+{
+ widget = fbtk_get_root_widget(widget);
+ int indent = 0;
+
+ while (widget != NULL) {
+ LOG("%*s%p", indent, "", widget);
+ if (widget->first_child != NULL) {
+ widget = widget->first_child;
+ indent += 6;
+ } else if (widget->next != NULL) {
+ widget = widget->next;
+ } else {
+ while ((widget->parent != NULL) &&
+ (widget->parent->next == NULL)) {
+ widget = widget->parent;
+ indent -= 6;
+ }
+ if (widget->parent != NULL) {
+ indent -= 6;
+ widget = widget->parent->next;
+ } else {
+ widget = NULL;
+ }
+ }
+ }
+}
+
+#endif
+
+/* exported function documented in fbtk.h */
+void
+fbtk_request_redraw(fbtk_widget_t *widget)
+{
+ fbtk_widget_t *cwidget;
+ fbtk_widget_t *pwidget;
+
+ assert(widget != NULL);
+
+ /* if widget not mapped do not try to redraw it */
+ pwidget = widget;
+ while (pwidget != NULL) {
+ if (pwidget->mapped == false)
+ return;
+ pwidget = pwidget->parent;
+ }
+
+ widget->redraw.needed = true;
+ widget->redraw.x = 0;
+ widget->redraw.y = 0;
+ widget->redraw.width = widget->width;
+ widget->redraw.height = widget->height;
+
+#ifdef FBTK_LOGGING
+ LOG("redrawing %p %d,%d %d,%d", widget, widget->redraw.x, widget->redraw.y, widget->redraw.width, widget->redraw.height);
+#endif
+
+ cwidget = widget->last_child;
+ while (cwidget != NULL) {
+ fbtk_request_redraw(cwidget);
+ cwidget = cwidget->prev;
+ }
+
+ while (widget->parent != NULL) {
+ widget = widget->parent;
+ widget->redraw.child = true;
+ }
+}
+
+
+
+/* exported function documented in fbtk.h */
+int
+fbtk_set_mapping(fbtk_widget_t *widget, bool map)
+{
+ LOG("setting mapping on %p to %d", widget, map);
+ widget->mapped = map;
+ if (map) {
+ fbtk_request_redraw(widget);
+ } else {
+ fbtk_request_redraw(widget->parent);
+ }
+ return 0;
+}
+
+/** swap the widget given with the next sibling.
+ *
+ * Swap a sibling widget with the next deepest in the hierachy
+ */
+static void
+swap_siblings(fbtk_widget_t *lw)
+{
+ fbtk_widget_t *rw = lw->next; /* the widget to swap lw with */
+ fbtk_widget_t *before;
+ fbtk_widget_t *after;
+
+ assert(rw != NULL);
+
+ LOG("Swapping %p with %p", lw, rw);
+ before = lw->prev;
+ after = rw->next;
+
+ if (before == NULL) {
+ /* left widget is currently the first child */
+ lw->parent->first_child = rw;
+ } else {
+ before->next = rw;
+ }
+ rw->prev = before;
+ rw->next = lw;
+
+ if (after == NULL) {
+ /* right widget is currently the last child */
+ rw->parent->last_child = lw;
+ } else {
+ after->prev = lw;
+ }
+ lw->next = after;
+ lw->prev = rw;
+}
+
+
+
+/* exported function documented in fbtk.h */
+int
+fbtk_set_zorder(fbtk_widget_t *widget, int z)
+{
+ while (z != 0) {
+ if (z < 0) {
+ if (widget->prev == NULL)
+ break; /* cannot go any shallower */
+
+ /* swap with previous entry */
+ swap_siblings(widget->prev);
+
+ z++;
+ } else {
+ if (widget->next == NULL)
+ break; /* cannot go any deeper */
+
+ /* swap with subsequent entry */
+ swap_siblings(widget);
+
+ z--;
+ }
+ }
+
+ return z;
+}
+
+
+/* exported function documented in fbtk.h */
+bool
+fbtk_set_pos_and_size(fbtk_widget_t *widget,
+ int x, int y,
+ int width, int height)
+{
+ if (widget->parent != NULL) {
+ fbtk_widget_t *parent = widget->parent;
+
+ /* make new window fit inside parent */
+ if (width == 0) {
+ width = parent->width - x;
+ } else if (width < 0) {
+ width = parent->width + width - x;
+ }
+ if ((width + x) > parent->width) {
+ width = parent->width - x;
+ }
+
+ if (height == 0) {
+ height = parent->height - y;
+ } else if (height < 0) {
+ height = parent->height + height - y;
+ }
+ if ((height + y) > parent->height) {
+ height = parent->height - y;
+ }
+ }
+
+ if ((widget->x != x) ||
+ (widget->y != y) ||
+ (widget->width != width) ||
+ (widget->height != height)) {
+ widget->x = x;
+ widget->y = y;
+ widget->width = width;
+ widget->height = height;
+ return true;
+ }
+ return false;
+}
+
+
+/* exported function docuemnted in fbtk.h */
+void
+fbtk_set_caret(fbtk_widget_t *widget, bool set,
+ int x, int y, int height,
+ void (*remove_caret)(fbtk_widget_t *widget))
+{
+ fbtk_widget_t *root;
+
+ assert(widget != NULL);
+ root = fbtk_get_root_widget(widget);
+
+ if (root->u.root.caret.owner != NULL &&
+ root->u.root.caret.remove_cb != NULL)
+ root->u.root.caret.remove_cb(widget);
+
+ if (set) {
+ assert(remove_caret != NULL);
+
+ root->u.root.caret.owner = widget;
+ root->u.root.caret.x = x;
+ root->u.root.caret.y = y;
+ root->u.root.caret.height = height;
+ root->u.root.caret.remove_cb = remove_caret;
+
+ } else {
+ root->u.root.caret.owner = NULL;
+ root->u.root.caret.remove_cb = NULL;
+ }
+}
+
+/* exported function documented in fbtk.h */
+int
+fbtk_destroy_widget(fbtk_widget_t *widget)
+{
+ fbtk_widget_t *parent;
+ int ret = 0;
+
+ ret = fbtk_post_callback(widget, FBTK_CBT_DESTROY);
+
+ while (widget->first_child != NULL) {
+ fbtk_destroy_widget(widget->first_child);
+ }
+
+ parent = widget->parent;
+ if (parent != NULL) {
+
+ /* unlink from siblings */
+ if (widget->prev != NULL) {
+ widget->prev->next = widget->next;
+ } else {
+ /* must be the first widget, unlink from parent */
+ parent->first_child = widget->next;
+ }
+ if (widget->next != NULL) {
+ widget->next->prev = widget->prev;
+ } else {
+ /* must be the last widget, unlink from parent */
+ parent->last_child = widget->prev;
+ }
+
+ free(widget);
+ }
+
+ return ret;
+}
+
+/* region coverage flags. */
+enum {
+ POINT_LEFTOF_REGION = 1,
+ POINT_RIGHTOF_REGION = 2,
+ POINT_ABOVE_REGION = 4,
+ POINT_BELOW_REGION = 8,
+};
+
+/* Computes where a point lies in respect to an area. */
+#define REGION(x,y,cx1,cx2,cy1,cy2) \
+ (( (y) > (cy2) ? POINT_BELOW_REGION : 0) | \
+ ( (y) < (cy1) ? POINT_ABOVE_REGION : 0) | \
+ ( (x) > (cx2) ? POINT_RIGHTOF_REGION : 0) | \
+ ( (x) < (cx1) ? POINT_LEFTOF_REGION : 0) )
+
+/* swap two integers */
+#define SWAP(a, b) do { int t; t=(a); (a)=(b); (b)=t; } while(0)
+
+/* exported function documented in fbtk.h */
+bool
+fbtk_clip_rect(const bbox_t * restrict clip, bbox_t * restrict box)
+{
+ uint8_t region1;
+ uint8_t region2;
+
+ /* ensure co-ordinates are in ascending order */
+ if (box->x1 < box->x0)
+ SWAP(box->x0, box->x1);
+ if (box->y1 < box->y0)
+ SWAP(box->y0, box->y1);
+
+ region1 = REGION(box->x0, box->y0, clip->x0, clip->x1 - 1, clip->y0, clip->y1 - 1);
+ region2 = REGION(box->x1, box->y1, clip->x0, clip->x1 - 1, clip->y0, clip->y1 - 1);
+
+ /* area lies entirely outside the clipping rectangle */
+ if ((region1 | region2) && (region1 & region2))
+ return false;
+
+ if (box->x0 < clip->x0)
+ box->x0 = clip->x0;
+ if (box->x0 > clip->x1)
+ box->x0 = clip->x1;
+
+ if (box->x1 < clip->x0)
+ box->x1 = clip->x0;
+ if (box->x1 > clip->x1)
+ box->x1 = clip->x1;
+
+ if (box->y0 < clip->y0)
+ box->y0 = clip->y0;
+ if (box->y0 > clip->y1)
+ box->y0 = clip->y1;
+
+ if (box->y1 < clip->y0)
+ box->y1 = clip->y0;
+ if (box->y1 > clip->y1)
+ box->y1 = clip->y1;
+
+ return true;
+}
+
+/* exported function documented in fbtk.h */
+bool
+fbtk_clip_to_widget(fbtk_widget_t *widget, bbox_t * restrict box)
+{
+ bbox_t wbox;
+ wbox.x0 = 0;
+ wbox.y0 = 0;
+ wbox.x1 = widget->width;
+ wbox.y1 = widget->height;
+ return fbtk_clip_rect(&wbox, box);
+}
+
+
+
+/* internally exported function documented in widget.h */
+int
+fbtk_set_ptr(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ fbtk_widget_t *root = fbtk_get_root_widget(widget);
+ struct fbtk_bitmap *bm = cbi->context;
+
+ nsfb_cursor_set(root->u.root.fb,
+ (nsfb_colour_t *)bm->pixdata,
+ bm->width,
+ bm->height,
+ bm->width,
+ bm->hot_x,
+ bm->hot_y);
+
+ return 0;
+}
+
+
+
+/* internally exported function documented in widget.h */
+fbtk_widget_t *
+fbtk_get_root_widget(fbtk_widget_t *widget)
+{
+ while (widget->parent != NULL)
+ widget = widget->parent;
+
+ /* check root widget was found */
+ if (widget->type != FB_WIDGET_TYPE_ROOT) {
+ LOG("Widget with null parent that is not the root widget!");
+ return NULL;
+ }
+
+ return widget;
+}
+
+
+/* exported function documented in fbtk.h */
+int
+fbtk_get_absx(fbtk_widget_t *widget)
+{
+ int x = widget->x;
+
+ while (widget->parent != NULL) {
+ widget = widget->parent;
+ x += widget->x;
+ }
+
+ return x;
+}
+
+/* exported function documented in fbtk.h */
+int
+fbtk_get_absy(fbtk_widget_t *widget)
+{
+ int y = widget->y;
+
+ while (widget->parent != NULL) {
+ widget = widget->parent;
+ y += widget->y;
+ }
+
+ return y;
+}
+
+/* exported function documented in fbtk.h */
+int
+fbtk_get_height(fbtk_widget_t *widget)
+{
+ return widget->height;
+}
+
+/* exported function documented in fbtk.h */
+int
+fbtk_get_width(fbtk_widget_t *widget)
+{
+ return widget->width;
+}
+
+/* exported function documented in fbtk.h */
+bool
+fbtk_get_bbox(fbtk_widget_t *widget, nsfb_bbox_t *bbox)
+{
+ bbox->x0 = widget->x;
+ bbox->y0 = widget->y;
+ bbox->x1 = widget->x + widget->width;
+ bbox->y1 = widget->y + widget->height;
+
+ widget = widget->parent;
+ while (widget != NULL) {
+ bbox->x0 += widget->x;
+ bbox->y0 += widget->y;
+ bbox->x1 += widget->x;
+ bbox->y1 += widget->y;
+ widget = widget->parent;
+ }
+
+ return true;
+}
+
+bool
+fbtk_get_caret(fbtk_widget_t *widget, int *x, int *y, int *height)
+{
+ fbtk_widget_t *root = fbtk_get_root_widget(widget);
+
+ if (root->u.root.caret.owner == widget) {
+ *x = root->u.root.caret.x;
+ *y = root->u.root.caret.y;
+ *height = root->u.root.caret.height;
+
+ return true;
+
+ } else {
+ *x = 0;
+ *y = 0;
+ *height = 0;
+
+ return false;
+ }
+}
+
+/* exported function documented in fbtk.h */
+fbtk_widget_t *
+fbtk_get_widget_at(fbtk_widget_t *nwid, int x, int y)
+{
+ fbtk_widget_t *widget = NULL; /* found widget */
+
+ /* require the root widget to start */
+ nwid = fbtk_get_root_widget(nwid);
+
+ while (nwid != NULL) {
+ if ((nwid->mapped) &&
+ (x >= nwid->x) &&
+ (y >= nwid->y) &&
+ (x < (nwid->x + nwid->width)) &&
+ (y < (nwid->y + nwid->height))) {
+ widget = nwid;
+ x -= nwid->x;
+ y -= nwid->y;
+ nwid = nwid->first_child;
+ } else {
+ nwid = nwid->next;
+ }
+ }
+
+ return widget;
+}
+
+
+
+
+/* internally exported function documented in widget.h */
+fbtk_widget_t *
+fbtk_widget_new(fbtk_widget_t *parent,
+ enum fbtk_widgettype_e type,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ fbtk_widget_t *neww; /* new widget */
+
+ if (parent == NULL)
+ return NULL;
+
+ neww = calloc(1, sizeof(fbtk_widget_t));
+ if (neww == NULL)
+ return NULL;
+
+#ifdef FBTK_LOGGING
+ LOG("creating %p %d,%d %d,%d", neww, x, y, width, height);
+#endif
+
+ /* make new window fit inside parent */
+ if (width == 0) {
+ width = parent->width - x;
+ } else if (width < 0) {
+ width = parent->width + width - x;
+ }
+ if ((width + x) > parent->width) {
+ width = parent->width - x;
+ }
+
+ if (height == 0) {
+ height = parent->height - y;
+ } else if (height < 0) {
+ height = parent->height + height - y;
+ }
+ if ((height + y) > parent->height) {
+ height = parent->height - y;
+ }
+
+#ifdef FBTK_LOGGING
+ LOG("using %p %d,%d %d,%d", neww, x, y, width, height);
+#endif
+ /* set values */
+ neww->type = type;
+ neww->x = x;
+ neww->y = y;
+ neww->width = width;
+ neww->height = height;
+
+ /* insert into widget heiarchy */
+
+ neww->parent = parent;
+
+ if (parent->first_child == NULL) {
+ /* no child widgets yet */
+ parent->last_child = neww;
+ } else {
+ /* add new widget to front of sibling chain */
+ neww->next = parent->first_child;
+ neww->next->prev = neww;
+ }
+ parent->first_child = neww;
+
+ return neww;
+}
+
+/* exported function documented in fbtk.h */
+bool
+fbtk_get_redraw_pending(fbtk_widget_t *widget)
+{
+ fbtk_widget_t *root;
+
+ /* ensure we have the root widget */
+ root = fbtk_get_root_widget(widget);
+
+ return root->redraw.needed | root->redraw.child;
+}
+
+/** Perform a depth-first tree-walk, calling the redraw callback of the widgets in turn.
+ *
+ * This function makes no decisions of its own and simply walks the
+ * widget tree depth first calling widgets redraw callbacks if flagged
+ * to do so.
+ * The tree search is optimised with a flag to indicate wether the
+ * children of a node should be considered.
+ */
+static int
+do_redraw(nsfb_t *nsfb, fbtk_widget_t *widget)
+{
+ nsfb_bbox_t plot_ctx;
+ fbtk_widget_t *cwidget; /* child widget */
+
+ /* check if the widget requires redrawing */
+ if (widget->redraw.needed == true) {
+ plot_ctx.x0 = fbtk_get_absx(widget) + widget->redraw.x;
+ plot_ctx.y0 = fbtk_get_absy(widget) + widget->redraw.y;
+ plot_ctx.x1 = plot_ctx.x0 + widget->redraw.width;
+ plot_ctx.y1 = plot_ctx.y0 + widget->redraw.height;
+
+#ifdef FBTK_LOGGING
+ LOG("clipping %p %d,%d %d,%d", widget, plot_ctx.x0, plot_ctx.y0, plot_ctx.x1, plot_ctx.y1);
+#endif
+ if (nsfb_plot_set_clip(nsfb, &plot_ctx) == true) {
+ fbtk_post_callback(widget, FBTK_CBT_REDRAW);
+ }
+ widget->redraw.needed = false;
+ }
+
+ /* walk the widgets children if child flag is set */
+ if (widget->redraw.child) {
+ cwidget = widget->last_child;
+ while (cwidget != NULL) {
+ do_redraw(nsfb, cwidget);
+ cwidget = cwidget->prev;
+ }
+ widget->redraw.child = false;
+ }
+
+ return 1;
+}
+
+/* exported function documented in fbtk.h */
+int
+fbtk_redraw(fbtk_widget_t *widget)
+{
+ fbtk_widget_t *root;
+
+ /* ensure we have the root widget */
+ root = fbtk_get_root_widget(widget);
+
+ return do_redraw(root->u.root.fb, root);
+}
+
+/* exported function documented in fbtk.h */
+fbtk_callback
+fbtk_get_handler(fbtk_widget_t *widget, fbtk_callback_type cbt)
+{
+ if ((cbt <= FBTK_CBT_START) || (cbt >= FBTK_CBT_END)) {
+ /* type out of range, no way to report error so return NULL */
+ return NULL;
+ }
+
+ return widget->callback[cbt];
+}
+
+/* exported function documented in fbtk.h */
+fbtk_callback
+fbtk_set_handler(fbtk_widget_t *widget,
+ fbtk_callback_type cbt,
+ fbtk_callback cb,
+ void *context)
+{
+ fbtk_callback prevcb;
+
+ if ((cbt <= FBTK_CBT_START) || (cbt >= FBTK_CBT_END)) {
+ /* type out of range, no way to report error so return NULL */
+ return NULL;
+ }
+
+ prevcb = widget->callback[cbt];
+
+ widget->callback[cbt] = cb;
+ widget->callback_context[cbt] = context;
+
+ return prevcb;
+}
+
+/* exported function docuemnted in fbtk.h */
+int
+fbtk_post_callback(fbtk_widget_t *widget, fbtk_callback_type cbt, ...)
+{
+ fbtk_callback_info cbi;
+ int ret = 0;
+ va_list ap;
+
+ if (widget == NULL)
+ return -1;
+ /* if the widget is not mapped do not attempt to post any
+ * events to it
+ */
+ if (widget->mapped == false)
+ return ret;
+
+ if (widget->callback[cbt] != NULL) {
+ cbi.type = cbt;
+ cbi.context = widget->callback_context[cbt];
+
+ va_start(ap, cbt);
+
+ switch (cbt) {
+ case FBTK_CBT_SCROLLX:
+ cbi.x = va_arg(ap,int);
+ break;
+
+ case FBTK_CBT_SCROLLY:
+ cbi.y = va_arg(ap,int);
+ break;
+
+ case FBTK_CBT_CLICK:
+ cbi.event = va_arg(ap, void *);
+ cbi.x = va_arg(ap, int);
+ cbi.y = va_arg(ap, int);
+ break;
+
+ case FBTK_CBT_INPUT:
+ cbi.event = va_arg(ap, void *);
+ break;
+
+ case FBTK_CBT_POINTERMOVE:
+ cbi.x = va_arg(ap, int);
+ cbi.y = va_arg(ap, int);
+ break;
+
+ case FBTK_CBT_REDRAW:
+ break;
+
+ case FBTK_CBT_USER:
+ break;
+
+ case FBTK_CBT_STRIP_FOCUS:
+ break;
+
+ default:
+ break;
+ }
+ va_end(ap);
+
+ ret = (widget->callback[cbt])(widget, &cbi);
+ }
+
+ return ret;
+}
+
+/* exported function docuemnted in fbtk.h */
+void
+fbtk_set_focus(fbtk_widget_t *widget)
+{
+ fbtk_widget_t *root;
+
+ /* ensure we have the root widget */
+ root = fbtk_get_root_widget(widget);
+
+ if (root->u.root.input != NULL &&
+ root->u.root.input != widget) {
+ /* inform previous holder of focus that it's being stripped
+ * of focus */
+ fbtk_post_callback(root->u.root.input, FBTK_CBT_STRIP_FOCUS);
+ }
+
+ root->u.root.input = widget;
+}
+
+
+
+/* exported function docuemnted in fbtk.h */
+nsfb_t *
+fbtk_get_nsfb(fbtk_widget_t *widget)
+{
+ fbtk_widget_t *root;
+
+ /* ensure we have the root widget */
+ root = fbtk_get_root_widget(widget);
+
+ return root->u.root.fb;
+}
+
+/* exported function docuemnted in fbtk.h */
+fbtk_widget_t *
+fbtk_init(nsfb_t *fb)
+{
+ fbtk_widget_t *root;
+
+ /* create and configure root widget */
+ root = calloc(1, sizeof(fbtk_widget_t));
+ if (root == NULL)
+ return NULL;
+
+ root->type = FB_WIDGET_TYPE_ROOT;
+ root->u.root.fb = fb;
+ root->u.root.caret.owner = NULL;
+
+ nsfb_get_geometry(fb, &root->width, &root->height, NULL);
+
+ root->mapped = true;
+
+ return root;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */
diff --git a/frontends/framebuffer/fbtk/fill.c b/frontends/framebuffer/fbtk/fill.c
new file mode 100644
index 000000000..07397b2df
--- /dev/null
+++ b/frontends/framebuffer/fbtk/fill.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2010 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * Framebuffer windowing toolkit filled area widget
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include <stdlib.h>
+
+#include <libnsfb.h>
+#include <libnsfb_plot.h>
+
+#include "desktop/browser.h"
+
+#include "framebuffer/gui.h"
+#include "framebuffer/fbtk.h"
+
+#include "widget.h"
+
+static int
+fb_redraw_fill(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ nsfb_bbox_t bbox;
+ nsfb_t *nsfb;
+
+ nsfb = fbtk_get_nsfb(widget);
+
+ fbtk_get_bbox(widget, &bbox);
+
+ nsfb_claim(nsfb, &bbox);
+
+ /* clear background */
+ if ((widget->bg & 0xFF000000) != 0) {
+ /* transparent polygon filling isnt working so fake it */
+ nsfb_plot_rectangle_fill(nsfb, &bbox, widget->bg);
+ }
+
+ nsfb_update(nsfb, &bbox);
+
+ return 0;
+}
+
+/* exported function documented in fbtk.h */
+fbtk_widget_t *
+fbtk_create_fill(fbtk_widget_t *parent,
+ int x,
+ int y,
+ int width,
+ int height,
+ colour c)
+{
+ fbtk_widget_t *neww;
+
+ neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_FILL, x, y, width, height);
+ neww->bg = c;
+ neww->mapped = true;
+
+ fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_fill, NULL);
+
+ return neww;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */
diff --git a/frontends/framebuffer/fbtk/osk.c b/frontends/framebuffer/fbtk/osk.c
new file mode 100644
index 000000000..1d57f157f
--- /dev/null
+++ b/frontends/framebuffer/fbtk/osk.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2010 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * Framebuffer windowing toolkit on screen keyboard.
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include <limits.h>
+
+#include <libnsfb.h>
+#include <libnsfb_plot.h>
+#include <libnsfb_event.h>
+#include <libnsfb_cursor.h>
+
+#include "utils/log.h"
+#include "desktop/browser.h"
+
+#include "framebuffer/gui.h"
+#include "framebuffer/fbtk.h"
+#include "framebuffer/image_data.h"
+
+#include "widget.h"
+
+struct kbd_button_s {
+ int x;
+ int y;
+ int w;
+ int h;
+ const char *t;
+ enum nsfb_key_code_e keycode;
+};
+
+#define KEYCOUNT 58
+
+static struct kbd_button_s kbdbase[KEYCOUNT] = {
+ { 0, 0, 20, 15, "`", NSFB_KEY_BACKQUOTE},
+ { 20, 0, 20, 15, "1", NSFB_KEY_1},
+ { 40, 0, 20, 15, "2", NSFB_KEY_2},
+ { 60, 0, 20, 15, "3", NSFB_KEY_3},
+ { 80, 0, 20, 15, "4", NSFB_KEY_4},
+ { 100, 0, 20, 15, "5", NSFB_KEY_5},
+ { 120, 0, 20, 15, "6", NSFB_KEY_6},
+ { 140, 0, 20, 15, "7", NSFB_KEY_7},
+ { 160, 0, 20, 15, "8", NSFB_KEY_8},
+ { 180, 0, 20, 15, "9", NSFB_KEY_9},
+ { 200, 0, 20, 15, "0", NSFB_KEY_0},
+ { 220, 0, 20, 15, "-", NSFB_KEY_MINUS},
+ { 240, 0, 20, 15, "=", NSFB_KEY_EQUALS},
+ { 260, 0, 40, 15, "\xe2\x8c\xab", NSFB_KEY_BACKSPACE},
+ { 0, 15, 30, 15, "\xe2\x86\xb9", NSFB_KEY_TAB},
+ { 30, 15, 20, 15, "q", NSFB_KEY_q},
+ { 50, 15, 20, 15, "w", NSFB_KEY_w},
+ { 70, 15, 20, 15, "e", NSFB_KEY_e},
+ { 90, 15, 20, 15, "r", NSFB_KEY_r},
+ { 110, 15, 20, 15, "t", NSFB_KEY_t},
+ { 130, 15, 20, 15, "y", NSFB_KEY_y},
+ { 150, 15, 20, 15, "u", NSFB_KEY_u},
+ { 170, 15, 20, 15, "i", NSFB_KEY_i},
+ { 190, 15, 20, 15, "o", NSFB_KEY_o},
+ { 210, 15, 20, 15, "p", NSFB_KEY_p},
+ { 230, 15, 20, 15, "[", NSFB_KEY_LEFTBRACKET},
+ { 250, 15, 20, 15, "]", NSFB_KEY_RIGHTBRACKET},
+ { 275, 15, 25, 30, "\xe2\x8f\x8e", NSFB_KEY_RETURN},
+ { 35, 30, 20, 15, "a", NSFB_KEY_a},
+ { 55, 30, 20, 15, "s", NSFB_KEY_s},
+ { 75, 30, 20, 15, "d", NSFB_KEY_d},
+ { 95, 30, 20, 15, "f", NSFB_KEY_f},
+ { 115, 30, 20, 15, "g", NSFB_KEY_g},
+ { 135, 30, 20, 15, "h", NSFB_KEY_h},
+ { 155, 30, 20, 15, "j", NSFB_KEY_j},
+ { 175, 30, 20, 15, "k", NSFB_KEY_k},
+ { 195, 30, 20, 15, "l", NSFB_KEY_l},
+ { 215, 30, 20, 15, ";", NSFB_KEY_SEMICOLON},
+ { 235, 30, 20, 15, "'", NSFB_KEY_l},
+ { 255, 30, 20, 15, "#", NSFB_KEY_HASH},
+ { 0, 45, 25, 15, "\xe2\x87\xa7", NSFB_KEY_LSHIFT},
+ { 25, 45, 20, 15, "\\", NSFB_KEY_SLASH},
+ { 45, 45, 20, 15, "z", NSFB_KEY_z},
+ { 65, 45, 20, 15, "x", NSFB_KEY_x},
+ { 85, 45, 20, 15, "c", NSFB_KEY_c},
+ { 105, 45, 20, 15, "v", NSFB_KEY_v},
+ { 125, 45, 20, 15, "b", NSFB_KEY_b},
+ { 145, 45, 20, 15, "n", NSFB_KEY_n},
+ { 165, 45, 20, 15, "m", NSFB_KEY_m},
+ { 185, 45, 20, 15, ",", NSFB_KEY_COMMA},
+ { 205, 45, 20, 15, ".", NSFB_KEY_PERIOD},
+ { 225, 45, 20, 15, "/", NSFB_KEY_BACKSLASH},
+ { 245, 45, 55, 15, "\xe2\x87\xa7", NSFB_KEY_RSHIFT},
+ { 40, 67, 185, 15, "", NSFB_KEY_SPACE},
+ { 250, 60, 20, 15, "\xe2\x96\xb2", NSFB_KEY_UP},
+ { 230, 67, 20, 15, "\xe2\x97\x80", NSFB_KEY_LEFT},
+ { 270, 67, 20, 15, "\xe2\x96\xb6", NSFB_KEY_RIGHT},
+ { 250, 75, 20, 15, "\xe2\x96\xbc", NSFB_KEY_DOWN},
+};
+
+static fbtk_widget_t *osk;
+
+static int
+osk_close(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ if (cbi->event->type != NSFB_EVENT_KEY_UP)
+ return 0;
+
+ fbtk_set_mapping(osk, false);
+
+ return 0;
+}
+
+static int
+osk_click(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ nsfb_event_t event;
+ struct kbd_button_s *kbd_button = cbi->context;
+
+ event.type = cbi->event->type;
+ event.value.keycode = kbd_button->keycode;
+ fbtk_input(widget, &event);
+
+ return 0;
+}
+
+/* exported function documented in fbtk.h */
+void
+fbtk_enable_oskb(fbtk_widget_t *fbtk)
+{
+ fbtk_widget_t *widget;
+ int kloop;
+ int maxx = 0;
+ int maxy = 0;
+ int ww;
+ int wh;
+ fbtk_widget_t *root = fbtk_get_root_widget(fbtk);
+ int furniture_width = 18;
+
+ for (kloop=0; kloop < KEYCOUNT; kloop++) {
+ if ((kbdbase[kloop].x + kbdbase[kloop].w) > maxx)
+ maxx=kbdbase[kloop].x + kbdbase[kloop].w;
+ if ((kbdbase[kloop].y + kbdbase[kloop].h) > maxy)
+ maxy=kbdbase[kloop].y + kbdbase[kloop].h;
+ }
+
+ ww = fbtk_get_width(root);
+
+ /* scale window height apropriately */
+ wh = (maxy * ww) / maxx;
+
+ osk = fbtk_create_window(root, 0, fbtk_get_height(root) - wh, 0, wh, 0xff202020);
+
+ for (kloop=0; kloop < KEYCOUNT; kloop++) {
+ widget = fbtk_create_text_button(osk,
+ (kbdbase[kloop].x * ww) / maxx,
+ (kbdbase[kloop].y * ww) / maxx,
+ (kbdbase[kloop].w * ww) / maxx,
+ (kbdbase[kloop].h *ww) / maxx,
+ FB_FRAME_COLOUR,
+ FB_COLOUR_BLACK,
+ osk_click,
+ &kbdbase[kloop]);
+ fbtk_set_text(widget, kbdbase[kloop].t);
+ }
+
+ widget = fbtk_create_button(osk,
+ fbtk_get_width(osk) - furniture_width,
+ fbtk_get_height(osk) - furniture_width,
+ furniture_width,
+ furniture_width,
+ FB_FRAME_COLOUR,
+ &osk_image,
+ osk_close,
+ NULL);
+}
+
+/* exported function documented in fbtk.h */
+void
+map_osk(void)
+{
+ fbtk_set_zorder(osk, INT_MIN);
+ fbtk_set_mapping(osk, true);
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */
diff --git a/frontends/framebuffer/fbtk/scroll.c b/frontends/framebuffer/fbtk/scroll.c
new file mode 100644
index 000000000..c86c8a6de
--- /dev/null
+++ b/frontends/framebuffer/fbtk/scroll.c
@@ -0,0 +1,589 @@
+/*
+ * Copyright 2010 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * Framebuffer windowing toolkit scrollbar widgets
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <assert.h>
+
+#include <stdbool.h>
+
+#include <libnsfb.h>
+#include <libnsfb_plot.h>
+#include <libnsfb_event.h>
+#include <libnsfb_cursor.h>
+
+#include "utils/log.h"
+#include "desktop/browser.h"
+
+#include "framebuffer/gui.h"
+#include "framebuffer/fbtk.h"
+#include "framebuffer/image_data.h"
+
+#include "widget.h"
+
+/* Vertical scroll widget */
+
+static int
+vscroll_redraw(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ int vscroll;
+ int vpos;
+
+ nsfb_bbox_t bbox;
+ nsfb_bbox_t rect;
+ fbtk_widget_t *root = fbtk_get_root_widget(widget);
+
+ fbtk_get_bbox(widget, &bbox);
+
+ nsfb_claim(root->u.root.fb, &bbox);
+
+ rect = bbox;
+
+ /* background */
+ nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg);
+
+ /* scroll well */
+ rect.x0 = bbox.x0 + 2;
+ rect.y0 = bbox.y0 + 1;
+ rect.x1 = bbox.x1 - 3;
+ rect.y1 = bbox.y1 - 2;
+
+ nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->fg);
+ nsfb_plot_rectangle(root->u.root.fb, &rect, 1, 0xFF999999, false, false);
+
+ /* scroll bar */
+ if ((widget->u.scroll.maximum - widget->u.scroll.minimum) > 0) {
+ vscroll = ((widget->height - 4) * widget->u.scroll.thumb) /
+ (widget->u.scroll.maximum - widget->u.scroll.minimum) ;
+ vpos = ((widget->height - 4) * widget->u.scroll.position) /
+ (widget->u.scroll.maximum - widget->u.scroll.minimum) ;
+ } else {
+ vscroll = (widget->height - 4);
+ vpos = 0;
+ }
+
+ rect.x0 = bbox.x0 + 5;
+ rect.y0 = bbox.y0 + 3 + vpos;
+ rect.x1 = bbox.x0 + widget->width - 5;
+ rect.y1 = bbox.y0 + vscroll + vpos;
+
+ nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg);
+
+ nsfb_update(root->u.root.fb, &bbox);
+
+ return 0;
+}
+
+static int
+vscroll_drag(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ int newpos;
+ fbtk_widget_t *scrollw = cbi->context;
+
+ newpos = ((widget->u.scroll.drag_position +
+ (cbi->y - widget->u.scroll.drag)) *
+ (widget->u.scroll.maximum - widget->u.scroll.minimum)) /
+ (widget->height - 4);
+
+ if (newpos < scrollw->u.scroll.minimum)
+ newpos = scrollw->u.scroll.minimum;
+
+ if (newpos > (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb ))
+ newpos = (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb);
+
+ if (newpos == scrollw->u.scroll.position)
+ return 0;
+
+ return fbtk_post_callback(widget, FBTK_CBT_SCROLLY, newpos);
+}
+
+static int
+vscrollu_click(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ int newpos;
+ fbtk_widget_t *scrollw = cbi->context;
+
+ if (cbi->event->type != NSFB_EVENT_KEY_DOWN)
+ return 0;
+
+ newpos = scrollw->u.scroll.position - scrollw->u.scroll.page;
+ if (newpos < scrollw->u.scroll.minimum)
+ newpos = scrollw->u.scroll.minimum;
+
+ if (newpos == scrollw->u.scroll.position)
+ return 0;
+
+ return fbtk_post_callback(scrollw, FBTK_CBT_SCROLLY, newpos);
+}
+
+static int
+vscrolld_click(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ int newpos;
+ fbtk_widget_t *scrollw = cbi->context;
+
+ if (cbi->event->type != NSFB_EVENT_KEY_DOWN)
+ return 0;
+
+ newpos = scrollw->u.scroll.position + scrollw->u.scroll.page;
+ if (newpos > (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb ))
+ newpos = (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb);
+
+ if (newpos == scrollw->u.scroll.position)
+ return 0;
+
+ return fbtk_post_callback(scrollw, FBTK_CBT_SCROLLY, newpos);
+}
+
+static int
+vscrollarea_click(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ int vscroll;
+ int vpos;
+ int newpos;
+ int ret = 0;
+
+ if (cbi->event->type != NSFB_EVENT_KEY_DOWN) {
+ /* end all drags, just in case */
+ if (fbtk_set_handler(widget, FBTK_CBT_POINTERMOVE, NULL, NULL) != NULL)
+ fbtk_tgrab_pointer(widget);
+ return 0;
+ }
+
+ switch (cbi->event->value.keycode) {
+
+ case NSFB_KEY_MOUSE_4:
+ /* scroll up */
+ newpos = widget->u.scroll.position - widget->u.scroll.page;
+ if (newpos < widget->u.scroll.minimum)
+ newpos = widget->u.scroll.minimum;
+ ret = fbtk_post_callback(cbi->context, FBTK_CBT_SCROLLY, newpos);
+ break;
+
+ case NSFB_KEY_MOUSE_5:
+ /* scroll down */
+ newpos = widget->u.scroll.position + widget->u.scroll.page;
+ if (newpos > widget->u.scroll.maximum)
+ newpos = widget->u.scroll.maximum;
+ ret = fbtk_post_callback(cbi->context, FBTK_CBT_SCROLLY, newpos);
+ break;
+
+ default:
+
+ if ((widget->u.scroll.maximum - widget->u.scroll.minimum) > 0) {
+ vscroll = ((widget->height - 4) * widget->u.scroll.thumb) /
+ (widget->u.scroll.maximum - widget->u.scroll.minimum) ;
+ vpos = ((widget->height - 4) * widget->u.scroll.position) /
+ (widget->u.scroll.maximum - widget->u.scroll.minimum) ;
+ } else {
+ vscroll = (widget->height - 4);
+ vpos = 0;
+ }
+
+ if (cbi->y < vpos) {
+ /* above bar */
+ newpos = widget->u.scroll.position - widget->u.scroll.thumb;
+ if (newpos < widget->u.scroll.minimum)
+ newpos = widget->u.scroll.minimum;
+ ret = fbtk_post_callback(cbi->context, FBTK_CBT_SCROLLY, newpos);
+ } else if (cbi->y > (vpos + vscroll)) {
+ /* below bar */
+ newpos = widget->u.scroll.position + widget->u.scroll.thumb;
+ if (newpos > widget->u.scroll.maximum)
+ newpos = widget->u.scroll.maximum;
+ ret = fbtk_post_callback(cbi->context, FBTK_CBT_SCROLLY, newpos);
+ } else {
+ /* on bar - start drag */
+ widget->u.scroll.drag = cbi->y;
+ widget->u.scroll.drag_position = vpos;
+ fbtk_set_handler(widget, FBTK_CBT_POINTERMOVE, vscroll_drag, widget);
+ fbtk_tgrab_pointer(widget);
+ }
+ }
+ return ret;
+}
+
+
+/* exported function documented in fbtk.h */
+fbtk_widget_t *
+fbtk_create_vscroll(fbtk_widget_t *parent,
+ int x,
+ int y,
+ int width,
+ int height,
+ colour fg,
+ colour bg,
+ fbtk_callback callback,
+ void *context)
+{
+ fbtk_widget_t *neww;
+
+ neww = fbtk_widget_new(parent,
+ FB_WIDGET_TYPE_VSCROLL,
+ x,
+ y + scrollu.height,
+ width,
+ height - scrollu.height - scrolld.height);
+
+ neww->fg = fg;
+ neww->bg = bg;
+ neww->mapped = true;
+
+ fbtk_set_handler(neww, FBTK_CBT_REDRAW, vscroll_redraw, NULL);
+
+ fbtk_set_handler(neww, FBTK_CBT_CLICK, vscrollarea_click, neww);
+
+ fbtk_set_handler(neww, FBTK_CBT_SCROLLY, callback, context);
+
+ neww->u.scroll.btnul = fbtk_create_button(parent,
+ x,
+ y,
+ width,
+ scrollu.height,
+ fg,
+ &scrollu,
+ vscrollu_click,
+ neww);
+
+ neww->u.scroll.btndr = fbtk_create_button(parent,
+ x,
+ y + height - scrolld.height,
+ width,
+ scrolld.height,
+ fg,
+ &scrolld,
+ vscrolld_click,
+ neww);
+
+
+ return neww;
+}
+
+
+/* exported function documented in fbtk.h */
+void
+fbtk_reposition_vscroll(fbtk_widget_t *vscroll,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ assert(vscroll->type == FB_WIDGET_TYPE_VSCROLL);
+
+ fbtk_set_pos_and_size(vscroll, x, y + scrollu.height,
+ width, height - scrollu.height - scrolld.height);
+ fbtk_set_pos_and_size(vscroll->u.scroll.btnul,
+ x, y, width, scrollu.height);
+ fbtk_set_pos_and_size(vscroll->u.scroll.btndr,
+ x, y + height - scrolld.height,
+ width, scrolld.height);
+}
+
+/* Horizontal scroll widget */
+
+static int
+hscroll_redraw(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ int hscroll;
+ int hpos;
+ nsfb_bbox_t bbox;
+ nsfb_bbox_t rect;
+ fbtk_widget_t *root = fbtk_get_root_widget(widget);
+
+ fbtk_get_bbox(widget, &bbox);
+
+ nsfb_claim(root->u.root.fb, &bbox);
+
+ rect = bbox;
+
+ /* background */
+ nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg);
+
+ /* scroll well */
+ rect.x0 = bbox.x0 + 1;
+ rect.y0 = bbox.y0 + 2;
+ rect.x1 = bbox.x1 - 2;
+ rect.y1 = bbox.y1 - 3;
+ nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->fg);
+
+ /* scroll well outline */
+ nsfb_plot_rectangle(root->u.root.fb, &rect, 1, 0xFF999999, false, false);
+
+ if ((widget->u.scroll.maximum - widget->u.scroll.minimum) > 0) {
+ hscroll = ((widget->width - 4) * widget->u.scroll.thumb) /
+ (widget->u.scroll.maximum - widget->u.scroll.minimum) ;
+ hpos = ((widget->width - 4) * widget->u.scroll.position) /
+ (widget->u.scroll.maximum - widget->u.scroll.minimum) ;
+ } else {
+ hscroll = (widget->width - 4);
+ hpos = 0;
+ }
+
+ LOG("hscroll %d", hscroll);
+
+ rect.x0 = bbox.x0 + 3 + hpos;
+ rect.y0 = bbox.y0 + 5;
+ rect.x1 = bbox.x0 + hscroll + hpos;
+ rect.y1 = bbox.y0 + widget->height - 5;
+
+ nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg);
+
+ nsfb_update(root->u.root.fb, &bbox);
+
+ return 0;
+}
+
+static int
+hscrolll_click(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ int newpos;
+ fbtk_widget_t *scrollw = cbi->context;
+
+ if (cbi->event->type != NSFB_EVENT_KEY_DOWN)
+ return 0;
+
+ newpos = scrollw->u.scroll.position - scrollw->u.scroll.page;
+ if (newpos < scrollw->u.scroll.minimum)
+ newpos = scrollw->u.scroll.minimum;
+
+ if (newpos == scrollw->u.scroll.position) {
+ LOG("horiz scroll was the same %d", newpos);
+ return 0;
+ }
+
+ return fbtk_post_callback(scrollw, FBTK_CBT_SCROLLX, newpos);
+}
+
+static int
+hscrollr_click(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ int newpos;
+ fbtk_widget_t *scrollw = cbi->context;
+
+ if (cbi->event->type != NSFB_EVENT_KEY_DOWN)
+ return 0;
+
+ newpos = scrollw->u.scroll.position + scrollw->u.scroll.page;
+ if (newpos > (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb ))
+ newpos = (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb);
+
+ if (newpos == scrollw->u.scroll.position)
+ return 0;
+
+ return fbtk_post_callback(scrollw, FBTK_CBT_SCROLLX, newpos);
+}
+
+static int
+hscroll_drag(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ int newpos;
+ fbtk_widget_t *scrollw = cbi->context;
+
+ newpos = ((widget->u.scroll.drag_position +
+ (cbi->x - widget->u.scroll.drag)) *
+ (widget->u.scroll.maximum - widget->u.scroll.minimum)) /
+ (widget->width - 4);
+
+ if (newpos < scrollw->u.scroll.minimum)
+ newpos = scrollw->u.scroll.minimum;
+
+ if (newpos > (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb ))
+ newpos = (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb);
+
+ if (newpos == scrollw->u.scroll.position)
+ return 0;
+
+ return fbtk_post_callback(widget, FBTK_CBT_SCROLLX, newpos);
+}
+
+static int
+hscrollarea_click(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ int hscroll;
+ int hpos;
+ int newpos;
+ int ret = 0;
+
+ if (cbi->event->type != NSFB_EVENT_KEY_DOWN) {
+ /* end all drags, just in case */
+ if (fbtk_set_handler(widget, FBTK_CBT_POINTERMOVE, NULL, NULL) != NULL)
+ fbtk_tgrab_pointer(widget);
+ return 0;
+ }
+
+ if ((widget->u.scroll.maximum - widget->u.scroll.minimum) > 0) {
+ hscroll = ((widget->width - 4) * widget->u.scroll.thumb) /
+ (widget->u.scroll.maximum - widget->u.scroll.minimum) ;
+ hpos = ((widget->width - 4) * widget->u.scroll.position) /
+ (widget->u.scroll.maximum - widget->u.scroll.minimum) ;
+ } else {
+ hscroll = (widget->width - 4);
+ hpos = 0;
+ }
+
+ if (cbi->x < hpos) {
+ /* left of bar */
+ newpos = widget->u.scroll.position - widget->u.scroll.page;
+ if (newpos < widget->u.scroll.minimum)
+ newpos = widget->u.scroll.minimum;
+ ret = fbtk_post_callback(cbi->context, FBTK_CBT_SCROLLX, newpos);
+ } else if (cbi->x > (hpos + hscroll)) {
+ /* right of bar */
+ newpos = widget->u.scroll.position + widget->u.scroll.page;
+ if (newpos > widget->u.scroll.maximum)
+ newpos = widget->u.scroll.maximum;
+ ret = fbtk_post_callback(cbi->context, FBTK_CBT_SCROLLX, newpos);
+ } else {
+ /* on bar - start drag */
+ widget->u.scroll.drag = cbi->x;
+ widget->u.scroll.drag_position = hpos;
+ fbtk_set_handler(widget, FBTK_CBT_POINTERMOVE, hscroll_drag, widget);
+ fbtk_tgrab_pointer(widget);
+ }
+ return ret;
+}
+
+/* exported function documented in fbtk.h */
+fbtk_widget_t *
+fbtk_create_hscroll(fbtk_widget_t *parent,
+ int x,
+ int y,
+ int width,
+ int height,
+ colour fg,
+ colour bg,
+ fbtk_callback callback,
+ void *context)
+{
+ fbtk_widget_t *neww;
+
+ neww = fbtk_widget_new(parent,
+ FB_WIDGET_TYPE_HSCROLL,
+ x + scrolll.width,
+ y,
+ width - scrolll.width - scrollr.width,
+ height);
+
+ neww->fg = fg;
+ neww->bg = bg;
+ neww->mapped = true;
+
+ fbtk_set_handler(neww, FBTK_CBT_REDRAW, hscroll_redraw, NULL);
+ fbtk_set_handler(neww, FBTK_CBT_CLICK, hscrollarea_click, neww);
+ fbtk_set_handler(neww, FBTK_CBT_SCROLLX, callback, context);
+
+ neww->u.scroll.btnul = fbtk_create_button(parent,
+ x,
+ y,
+ scrolll.width,
+ height,
+ fg,
+ &scrolll,
+ hscrolll_click,
+ neww);
+
+ neww->u.scroll.btndr = fbtk_create_button(parent,
+ x + width - scrollr.width,
+ y,
+ scrollr.width,
+ height,
+ fg,
+ &scrollr,
+ hscrollr_click,
+ neww);
+
+ return neww;
+}
+
+/* exported function documented in fbtk.h */
+void
+fbtk_reposition_hscroll(fbtk_widget_t *scrollh,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ assert(scrollh->type == FB_WIDGET_TYPE_HSCROLL);
+
+ fbtk_set_pos_and_size(scrollh, x + scrolll.width, y,
+ width - scrolll.width - scrollr.width, height);
+ fbtk_set_pos_and_size(scrollh->u.scroll.btnul,
+ x, y, scrolll.width, height);
+ fbtk_set_pos_and_size(scrollh->u.scroll.btndr,
+ x + width - scrollr.width, y,
+ scrollr.width, height);
+}
+
+
+/* exported function documented in fbtk.h */
+bool
+fbtk_set_scroll_parameters(fbtk_widget_t *widget,
+ int min,
+ int max,
+ int thumb,
+ int page)
+{
+ if (widget == NULL)
+ return false;
+
+ if ((widget->type != FB_WIDGET_TYPE_HSCROLL) &&
+ (widget->type != FB_WIDGET_TYPE_VSCROLL))
+ return false;
+
+ widget->u.scroll.minimum = min;
+ widget->u.scroll.maximum = max;
+ widget->u.scroll.thumb = thumb;
+ widget->u.scroll.page = page;
+
+ if (widget->u.scroll.position > max)
+ widget->u.scroll.position = max;
+ if (widget->u.scroll.position < min)
+ widget->u.scroll.position = min;
+
+ fbtk_request_redraw(widget);
+
+ return true;
+}
+
+/* exported function documented in fbtk.h */
+bool
+fbtk_set_scroll_position(fbtk_widget_t *widget, int position)
+{
+ if (widget == NULL)
+ return false;
+
+ if ((widget->type != FB_WIDGET_TYPE_HSCROLL) &&
+ (widget->type != FB_WIDGET_TYPE_VSCROLL))
+ return false;
+
+ if ((position < widget->u.scroll.minimum) ||
+ (position > widget->u.scroll.maximum))
+ return false;
+
+ widget->u.scroll.position = position;
+
+ fbtk_request_redraw(widget);
+
+ return true;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */
diff --git a/frontends/framebuffer/fbtk/text.c b/frontends/framebuffer/fbtk/text.c
new file mode 100644
index 000000000..258e9dff9
--- /dev/null
+++ b/frontends/framebuffer/fbtk/text.c
@@ -0,0 +1,640 @@
+/*
+ * Copyright 2010 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * Framebuffer windowing toolkit scrollbar widgets.
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libnsfb.h>
+#include <libnsfb_plot.h>
+#include <libnsfb_plot_util.h>
+#include <libnsfb_event.h>
+
+#include "utils/log.h"
+#include "desktop/browser.h"
+#include "desktop/plotters.h"
+
+#include "framebuffer/gui.h"
+#include "framebuffer/fbtk.h"
+#include "framebuffer/font.h"
+#include "framebuffer/framebuffer.h"
+#include "framebuffer/image_data.h"
+
+#include "widget.h"
+
+//#define TEXT_WIDGET_BORDER 3 /**< The pixel border round a text widget. */
+
+/* Lighten a colour by taking seven eights of each channel's intensity
+ * and adding a full eighth
+ */
+#define brighten_colour(c1) \
+ (((((7 * ((c1 >> 16) & 0xff)) >> 3) + 32) << 16) | \
+ ((((7 * ((c1 >> 8) & 0xff)) >> 3) + 32) << 8) | \
+ ((((7 * (c1 & 0xff)) >> 3) + 32) << 0))
+
+/* Convert pixels to points, assuming a DPI of 90 */
+#define px_to_pt(x) (((x) * 72) / FBTK_DPI)
+
+/* Get a font style for a text input */
+static inline void
+fb_text_font_style(fbtk_widget_t *widget, int *font_height, int *padding,
+ plot_font_style_t *font_style)
+{
+ if (widget->u.text.outline)
+ *padding = 1;
+ else
+ *padding = 0;
+
+#ifdef FB_USE_FREETYPE
+ *padding += widget->height / 6;
+ *font_height = widget->height - *padding - *padding;
+#else
+ *font_height = FB_FONT_HEIGHT;
+ *padding = (widget->height - *padding - *font_height) / 2;
+#endif
+
+ font_style->family = PLOT_FONT_FAMILY_SANS_SERIF;
+ font_style->size = px_to_pt(*font_height * FONT_SIZE_SCALE);
+ font_style->weight = 400;
+ font_style->flags = FONTF_NONE;
+ font_style->background = widget->bg;
+ font_style->foreground = widget->fg;
+}
+
+/** Text redraw callback.
+ *
+ * Called when a text widget requires redrawing.
+ *
+ * @param widget The widget to be redrawn.
+ * @param cbi The callback parameters.
+ * @return The callback result.
+ */
+static int
+fb_redraw_text(fbtk_widget_t *widget, fbtk_callback_info *cbi )
+{
+ nsfb_bbox_t bbox;
+ nsfb_bbox_t rect;
+ fbtk_widget_t *root;
+ plot_font_style_t font_style;
+ int caret_x, caret_y, caret_h;
+ int fh;
+ int padding;
+ int scroll = 0;
+ bool caret = false;
+
+ fb_text_font_style(widget, &fh, &padding, &font_style);
+
+ if (fbtk_get_caret(widget, &caret_x, &caret_y, &caret_h)) {
+ caret = true;
+ }
+
+ root = fbtk_get_root_widget(widget);
+
+ fbtk_get_bbox(widget, &bbox);
+
+ rect = bbox;
+
+ nsfb_claim(root->u.root.fb, &bbox);
+
+ /* clear background */
+ if ((widget->bg & 0xFF000000) != 0) {
+ /* transparent polygon filling isnt working so fake it */
+ nsfb_plot_rectangle_fill(root->u.root.fb, &bbox, widget->bg);
+ }
+
+ /* widget can have a single pixel outline border */
+ if (widget->u.text.outline) {
+ rect.x1--;
+ rect.y1--;
+ nsfb_plot_rectangle(root->u.root.fb, &rect, 1,
+ 0x00000000, false, false);
+ }
+
+ if (widget->u.text.text != NULL) {
+ int x = bbox.x0 + padding;
+ int y = bbox.y0 + ((fh * 3 + 2) / 4) + padding;
+
+#ifdef FB_USE_FREETYPE
+ /* Freetype renders text higher */
+ y += 1;
+#endif
+
+ if (caret && widget->width - padding - padding < caret_x) {
+ scroll = (widget->width - padding - padding) - caret_x;
+ x += scroll;
+ }
+
+ /* Call the fb text plotting, baseline is 3/4 down the font */
+ fb_plotters.text(x, y, widget->u.text.text,
+ widget->u.text.len, &font_style);
+ }
+
+ if (caret) {
+ /* This widget has caret, so render it */
+ nsfb_t *nsfb = fbtk_get_nsfb(widget);
+ nsfb_bbox_t line;
+ nsfb_plot_pen_t pen;
+
+ line.x0 = bbox.x0 + caret_x + scroll;
+ line.y0 = bbox.y0 + caret_y;
+ line.x1 = bbox.x0 + caret_x + scroll;
+ line.y1 = bbox.y0 + caret_y + caret_h;
+
+ pen.stroke_type = NFSB_PLOT_OPTYPE_SOLID;
+ pen.stroke_width = 1;
+ pen.stroke_colour = 0xFF0000FF;
+
+ nsfb_plot_line(nsfb, &line, &pen);
+ }
+
+ nsfb_update(root->u.root.fb, &bbox);
+
+ return 0;
+}
+
+/** Text destroy callback.
+ *
+ * Called when a text widget is destroyed.
+ *
+ * @param widget The widget being destroyed.
+ * @param cbi The callback parameters.
+ * @return The callback result.
+ */
+static int fb_destroy_text(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ if ((widget == NULL) || (widget->type != FB_WIDGET_TYPE_TEXT)) {
+ return 0;
+ }
+
+ if (widget->u.text.text != NULL) {
+ free(widget->u.text.text);
+ }
+
+ return 0;
+}
+
+/** Text button redraw callback.
+ *
+ * Called when a text widget requires redrawing.
+ *
+ * @param widget The widget to be redrawn.
+ * @param cbi The callback parameters.
+ * @return The callback result.
+ */
+static int
+fb_redraw_text_button(fbtk_widget_t *widget, fbtk_callback_info *cbi )
+{
+ nsfb_bbox_t bbox;
+ nsfb_bbox_t rect;
+ nsfb_bbox_t line;
+ nsfb_plot_pen_t pen;
+ plot_font_style_t font_style;
+ int fh;
+ int border;
+ fbtk_widget_t *root = fbtk_get_root_widget(widget);
+
+ fb_text_font_style(widget, &fh, &border, &font_style);
+
+ pen.stroke_type = NFSB_PLOT_OPTYPE_SOLID;
+ pen.stroke_width = 1;
+ pen.stroke_colour = brighten_colour(widget->bg);
+
+ fbtk_get_bbox(widget, &bbox);
+
+ rect = bbox;
+ rect.x1--;
+ rect.y1--;
+
+ nsfb_claim(root->u.root.fb, &bbox);
+
+ /* clear background */
+ if ((widget->bg & 0xFF000000) != 0) {
+ /* transparent polygon filling isnt working so fake it */
+ nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg);
+ }
+
+ if (widget->u.text.outline) {
+ line.x0 = rect.x0;
+ line.y0 = rect.y0;
+ line.x1 = rect.x0;
+ line.y1 = rect.y1;
+ nsfb_plot_line(root->u.root.fb, &line, &pen);
+ line.x0 = rect.x0;
+ line.y0 = rect.y0;
+ line.x1 = rect.x1;
+ line.y1 = rect.y0;
+ nsfb_plot_line(root->u.root.fb, &line, &pen);
+ pen.stroke_colour = darken_colour(widget->bg);
+ line.x0 = rect.x0;
+ line.y0 = rect.y1;
+ line.x1 = rect.x1;
+ line.y1 = rect.y1;
+ nsfb_plot_line(root->u.root.fb, &line, &pen);
+ line.x0 = rect.x1;
+ line.y0 = rect.y0;
+ line.x1 = rect.x1;
+ line.y1 = rect.y1;
+ nsfb_plot_line(root->u.root.fb, &line, &pen);
+ }
+
+ if (widget->u.text.text != NULL) {
+ /* Call the fb text plotting, baseline is 3/4 down the font */
+ fb_plotters.text(bbox.x0 + border,
+ bbox.y0 + ((fh * 3) / 4) + border,
+ widget->u.text.text,
+ widget->u.text.len,
+ &font_style);
+ }
+
+ nsfb_update(root->u.root.fb, &bbox);
+
+ return 0;
+}
+
+static void
+fb_text_input_remove_caret_cb(fbtk_widget_t *widget)
+{
+ int c_x, c_y, c_h;
+
+ if (fbtk_get_caret(widget, &c_x, &c_y, &c_h)) {
+ fbtk_request_redraw(widget);
+ }
+}
+
+/** Routine called when text events occour in writeable widget.
+ *
+ * @param widget The widget reciving input events.
+ * @param cbi The callback parameters.
+ * @return The callback result.
+ */
+static int
+text_input(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ int value;
+ static fbtk_modifier_type modifier = FBTK_MOD_CLEAR;
+ char *temp;
+ plot_font_style_t font_style;
+ int fh;
+ int border;
+ bool caret_moved = false;
+
+ fb_text_font_style(widget, &fh, &border, &font_style);
+
+ if (cbi->event == NULL) {
+ /* gain focus */
+ if (widget->u.text.text == NULL)
+ widget->u.text.text = calloc(1,1);
+
+ return 0;
+ }
+
+ value = cbi->event->value.keycode;
+
+ if (cbi->event->type != NSFB_EVENT_KEY_DOWN) {
+ switch (value) {
+ case NSFB_KEY_RSHIFT:
+ modifier &= ~FBTK_MOD_RSHIFT;
+ break;
+
+ case NSFB_KEY_LSHIFT:
+ modifier &= ~FBTK_MOD_LSHIFT;
+ break;
+
+ case NSFB_KEY_RCTRL:
+ modifier &= ~FBTK_MOD_RCTRL;
+ break;
+
+ case NSFB_KEY_LCTRL:
+ modifier &= ~FBTK_MOD_LCTRL;
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+ }
+
+ switch (value) {
+ case NSFB_KEY_BACKSPACE:
+ if (widget->u.text.idx <= 0)
+ break;
+ memmove(widget->u.text.text + widget->u.text.idx - 1,
+ widget->u.text.text + widget->u.text.idx,
+ widget->u.text.len - widget->u.text.idx);
+ widget->u.text.idx--;
+ widget->u.text.len--;
+ widget->u.text.text[widget->u.text.len] = 0;
+
+ fb_font_width(&font_style, widget->u.text.text,
+ widget->u.text.len, &widget->u.text.width);
+
+ caret_moved = true;
+ break;
+
+ case NSFB_KEY_RETURN:
+ widget->u.text.enter(widget->u.text.pw, widget->u.text.text);
+ break;
+
+ case NSFB_KEY_RIGHT:
+ if (widget->u.text.idx < widget->u.text.len) {
+ if (modifier == FBTK_MOD_CLEAR)
+ widget->u.text.idx++;
+ else
+ widget->u.text.idx = widget->u.text.len;
+
+ caret_moved = true;
+ }
+ break;
+
+ case NSFB_KEY_LEFT:
+ if (widget->u.text.idx > 0) {
+ if (modifier == FBTK_MOD_CLEAR)
+ widget->u.text.idx--;
+ else
+ widget->u.text.idx = 0;
+
+ caret_moved = true;
+ }
+ break;
+
+ case NSFB_KEY_PAGEUP:
+ case NSFB_KEY_PAGEDOWN:
+ case NSFB_KEY_UP:
+ case NSFB_KEY_DOWN:
+ /* Not handling any of these correctly yet, but avoid putting
+ * charcters in the text widget when they're pressed. */
+ break;
+
+ case NSFB_KEY_RSHIFT:
+ modifier |= FBTK_MOD_RSHIFT;
+ break;
+
+ case NSFB_KEY_LSHIFT:
+ modifier |= FBTK_MOD_LSHIFT;
+ break;
+
+ case NSFB_KEY_RCTRL:
+ modifier |= FBTK_MOD_RCTRL;
+ break;
+
+ case NSFB_KEY_LCTRL:
+ modifier |= FBTK_MOD_LCTRL;
+ break;
+
+ default:
+ if (modifier & FBTK_MOD_LCTRL || modifier & FBTK_MOD_RCTRL) {
+ /* CTRL pressed, don't enter any text */
+ if (value == NSFB_KEY_u) {
+ /* CTRL+U: clear writable */
+ widget->u.text.idx = 0;
+ widget->u.text.len = 0;
+ widget->u.text.text[widget->u.text.len] = '\0';
+ widget->u.text.width = 0;
+ caret_moved = true;
+ }
+ break;
+ }
+
+ /* allow for new character and null */
+ temp = realloc(widget->u.text.text, widget->u.text.len + 2);
+ if (temp == NULL) {
+ break;
+ }
+
+ widget->u.text.text = temp;
+ memmove(widget->u.text.text + widget->u.text.idx + 1,
+ widget->u.text.text + widget->u.text.idx,
+ widget->u.text.len - widget->u.text.idx);
+ widget->u.text.text[widget->u.text.idx] =
+ fbtk_keycode_to_ucs4(value, modifier);
+ widget->u.text.idx++;
+ widget->u.text.len++;
+ widget->u.text.text[widget->u.text.len] = '\0';
+
+ fb_font_width(&font_style, widget->u.text.text,
+ widget->u.text.len, &widget->u.text.width);
+ caret_moved = true;
+ break;
+ }
+
+ if (caret_moved) {
+ fb_font_width(&font_style, widget->u.text.text,
+ widget->u.text.idx, &widget->u.text.idx_offset);
+ fbtk_set_caret(widget, true,
+ widget->u.text.idx_offset + border,
+ border,
+ widget->height - border - border,
+ fb_text_input_remove_caret_cb);
+ }
+
+ fbtk_request_redraw(widget);
+
+ return 0;
+}
+
+/** Routine called when click events occour in writeable widget.
+ *
+ * @param widget The widget reciving click events.
+ * @param cbi The callback parameters.
+ * @return The callback result.
+ */
+static int
+text_input_click(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ plot_font_style_t font_style;
+ int fh;
+ int border;
+ size_t idx;
+
+ fb_text_font_style(widget, &fh, &border, &font_style);
+
+ widget->u.text.idx = widget->u.text.len;
+
+ fb_font_position(&font_style, widget->u.text.text,
+ widget->u.text.len, cbi->x - border,
+ &idx,
+ &widget->u.text.idx_offset);
+ widget->u.text.idx = idx;
+ fbtk_set_caret(widget, true,
+ widget->u.text.idx_offset + border,
+ border,
+ widget->height - border - border,
+ fb_text_input_remove_caret_cb);
+
+ fbtk_request_redraw(widget);
+
+ return 0;
+}
+
+/** Routine called when "stripped of focus" event occours for writeable widget.
+ *
+ * @param widget The widget reciving "stripped of focus" event.
+ * @param cbi The callback parameters.
+ * @return The callback result.
+ */
+static int
+text_input_strip_focus(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ fbtk_set_caret(widget, false, 0, 0, 0, NULL);
+
+ return 0;
+}
+
+/* exported function documented in fbtk.h */
+void
+fbtk_writable_text(fbtk_widget_t *widget, fbtk_enter_t enter, void *pw)
+{
+ widget->u.text.enter = enter;
+ widget->u.text.pw = pw;
+
+ fbtk_set_handler(widget, FBTK_CBT_INPUT, text_input, widget);
+}
+
+/* exported function documented in fbtk.h */
+void
+fbtk_set_text(fbtk_widget_t *widget, const char *text)
+{
+ plot_font_style_t font_style;
+ int c_x, c_y, c_h;
+ int fh;
+ int border;
+
+ if ((widget == NULL) || (widget->type != FB_WIDGET_TYPE_TEXT))
+ return;
+ if (widget->u.text.text != NULL) {
+ if (strcmp(widget->u.text.text, text) == 0)
+ return; /* text is being set to the same thing */
+ free(widget->u.text.text);
+ }
+ widget->u.text.text = strdup(text);
+ widget->u.text.len = strlen(text);
+ widget->u.text.idx = widget->u.text.len;
+
+
+ fb_text_font_style(widget, &fh, &border, &font_style);
+ fb_font_width(&font_style, widget->u.text.text,
+ widget->u.text.len, &widget->u.text.width);
+ fb_font_width(&font_style, widget->u.text.text,
+ widget->u.text.idx, &widget->u.text.idx_offset);
+
+ if (fbtk_get_caret(widget, &c_x, &c_y, &c_h)) {
+ /* Widget has caret; move it to end of new string */
+ fbtk_set_caret(widget, true,
+ widget->u.text.idx_offset + border,
+ border,
+ widget->height - border - border,
+ fb_text_input_remove_caret_cb);
+ }
+
+ fbtk_request_redraw(widget);
+}
+
+/* exported function documented in fbtk.h */
+fbtk_widget_t *
+fbtk_create_text(fbtk_widget_t *parent,
+ int x,
+ int y,
+ int width,
+ int height,
+ colour bg,
+ colour fg,
+ bool outline)
+{
+ fbtk_widget_t *neww;
+
+ neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_TEXT, x, y, width, height);
+ neww->fg = fg;
+ neww->bg = bg;
+ neww->mapped = true;
+ neww->u.text.outline = outline;
+
+ fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_text, NULL);
+ fbtk_set_handler(neww, FBTK_CBT_DESTROY, fb_destroy_text, NULL);
+
+ return neww;
+}
+
+/* exported function documented in fbtk.h */
+fbtk_widget_t *
+fbtk_create_writable_text(fbtk_widget_t *parent,
+ int x,
+ int y,
+ int width,
+ int height,
+ colour bg,
+ colour fg,
+ bool outline,
+ fbtk_enter_t enter,
+ void *pw)
+{
+ fbtk_widget_t *neww;
+
+ neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_TEXT, x, y, width, height);
+ neww->fg = fg;
+ neww->bg = bg;
+ neww->mapped = true;
+
+ neww->u.text.outline = outline;
+ neww->u.text.enter = enter;
+ neww->u.text.pw = pw;
+
+ fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_text, NULL);
+ fbtk_set_handler(neww, FBTK_CBT_DESTROY, fb_destroy_text, NULL);
+ fbtk_set_handler(neww, FBTK_CBT_CLICK, text_input_click, pw);
+ fbtk_set_handler(neww, FBTK_CBT_STRIP_FOCUS, text_input_strip_focus, NULL);
+ fbtk_set_handler(neww, FBTK_CBT_INPUT, text_input, neww);
+
+ return neww;
+}
+
+/* exported function documented in fbtk.h */
+fbtk_widget_t *
+fbtk_create_text_button(fbtk_widget_t *parent,
+ int x,
+ int y,
+ int width,
+ int height,
+ colour bg,
+ colour fg,
+ fbtk_callback click,
+ void *pw)
+{
+ fbtk_widget_t *neww;
+
+ neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_TEXT, x, y, width, height);
+ neww->fg = fg;
+ neww->bg = bg;
+ neww->mapped = true;
+
+ neww->u.text.outline = true;
+
+ fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_text_button, NULL);
+ fbtk_set_handler(neww, FBTK_CBT_DESTROY, fb_destroy_text, NULL);
+ fbtk_set_handler(neww, FBTK_CBT_CLICK, click, pw);
+ fbtk_set_handler(neww, FBTK_CBT_POINTERENTER, fbtk_set_ptr, &hand_image);
+
+ return neww;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */
diff --git a/frontends/framebuffer/fbtk/user.c b/frontends/framebuffer/fbtk/user.c
new file mode 100644
index 000000000..2b9cc8768
--- /dev/null
+++ b/frontends/framebuffer/fbtk/user.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2010 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * Framebuffer windowing toolkit user widget.
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <stdbool.h>
+#include <libnsfb.h>
+
+#include "desktop/plot_style.h"
+#include "framebuffer/gui.h"
+#include "framebuffer/fbtk.h"
+
+#include "widget.h"
+
+/* exported function documented in fbtk.h */
+void *
+fbtk_get_userpw(fbtk_widget_t *widget)
+{
+ if ((widget == NULL) ||
+ (widget->type != FB_WIDGET_TYPE_USER))
+ return NULL;
+
+ return widget->u.user.pw;
+}
+
+/* exported function documented in fbtk.h */
+fbtk_widget_t *
+fbtk_create_user(fbtk_widget_t *parent,
+ int x,
+ int y,
+ int width,
+ int height,
+ void *pw)
+{
+ fbtk_widget_t *neww;
+
+ neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_USER, x, y, width, height);
+ neww->u.user.pw = pw;
+ neww->mapped = true;
+
+ return neww;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */
diff --git a/frontends/framebuffer/fbtk/widget.h b/frontends/framebuffer/fbtk/widget.h
new file mode 100644
index 000000000..5622723ee
--- /dev/null
+++ b/frontends/framebuffer/fbtk/widget.h
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2010 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_FB_FBTK_WIDGET_H
+#define NETSURF_FB_FBTK_WIDGET_H
+
+#include <stdbool.h>
+
+enum fbtk_widgettype_e {
+ FB_WIDGET_TYPE_ROOT = 0,
+ FB_WIDGET_TYPE_WINDOW,
+ FB_WIDGET_TYPE_BITMAP,
+ FB_WIDGET_TYPE_FILL,
+ FB_WIDGET_TYPE_TEXT,
+ FB_WIDGET_TYPE_HSCROLL,
+ FB_WIDGET_TYPE_VSCROLL,
+ FB_WIDGET_TYPE_USER,
+};
+
+
+/** Widget description.
+ *
+ * A widget is an entry in a tree structure which represents a
+ * rectangular area with co-ordinates relative to its parent widget.
+ * This area has a distinct set of callback operations for handling
+ * events which occour within its boundries. A widget may have an
+ * arbitrary number of child widgets. The order within the tree
+ * determines a widgets z order.
+ *
+ * ---
+ * A
+ * |
+ * +----------+
+ * +--->| Button 3 |
+ * | +----------+
+ * | | A
+ * | V |
+ * | +----------+
+ * | | Button 2 |
+ * | +----------+
+ * | | A
+ * | V |
+ * | +----------+
+ * | | Button 1 |
+ * | +----------+
+ * | | A
+ * | V |
+ * --- | +----------+
+ * A | +->| Filled |
+ * | | | +----------+
+ * +----------+ | | |
+ * +---->| |-+ | V
+ * | | Window 1 | | --- ---
+ * | | |---+ A
+ * | +----------+ |
+ * | | A +----------+ ---
+ * | | | +--->| Button 2 | A
+ * | | | | +----------+ |
+ * | | | | | A +-------------+
+ * | | | | | | +--->| Button Up |
+ * | | | | | | | +-------------+
+ * | | | | | | | | A
+ * | | | | | | | V |
+ * | | | | | | | +-------------+
+ * | | | | | | | | Button Down |
+ * | | | | | | | +-------------+
+ * | | | | | | | | A
+ * | | | | | | | V |
+ * | | | | | | | +-------------+
+ * | | | | | | | +->| Scroller |
+ * | | | | V | | | +-------------+
+ * | | | | +----------+ | | |
+ * | | | | | |-+ | V
+ * | | | | | V Scroll | | ---
+ * | | | | | |---+
+ * | | | | +----------+
+ * | | | | | A
+ * | | | | V |
+ * | | | | +----------+
+ * | | | | +->| Button 1 |
+ * | | | | | +----------+
+ * | +----------+ | | |
+ * | | |-+ | V
+ * | | Window 2 | | ---
+ * | | |---+
+ * | +----------+
+ * | | A
+ * | V |
+ * | +------------+
+ * --- | | Background |
+ * A | +->| Bitmap |
+ * | | | +------------+
+ * +------+ | | |
+ * | |-+ | V
+ * | Root | | ---
+ * | |---+
+ * +------+
+ * |
+ * V
+ * ---
+ *
+ * Every widget is contained within this generic wrapper. The
+ * integrated union provides for data specific to a widget type.
+ */
+struct fbtk_widget_s {
+ struct fbtk_widget_s *next; /* next lower z ordered widget in tree */
+ struct fbtk_widget_s *prev; /* next higher z ordered widget in tree */
+
+ struct fbtk_widget_s *parent; /* parent widget */
+
+ struct fbtk_widget_s *first_child; /* first child widget */
+ struct fbtk_widget_s *last_child; /* last child widget */
+
+ /* flags */
+ bool mapped; /**< The widget is mapped/visible . */
+
+ /* Generic properties */
+ int x;
+ int y;
+ int width;
+ int height;
+ colour bg;
+ colour fg;
+
+ /* event callback handlers */
+ fbtk_callback callback[FBTK_CBT_END];
+ void *callback_context[FBTK_CBT_END];
+
+ /* widget redraw */
+ struct {
+ bool child; /* A child of this widget requires redrawing */
+ bool needed; /* the widget requires redrawing */
+ int x;
+ int y;
+ int width;
+ int height;
+ } redraw;
+
+ enum fbtk_widgettype_e type; /**< The type of the widget */
+
+
+ union {
+ /* toolkit base handle */
+ struct {
+ nsfb_t *fb;
+ struct fbtk_widget_s *prev; /* previous widget pointer wasin */
+ struct fbtk_widget_s *grabbed; /* widget that has grabbed pointer movement. */
+ struct fbtk_widget_s *input;
+
+ /* caret */
+ struct {
+ struct fbtk_widget_s *owner; /* widget / NULL */
+ int x; /* relative to owner */
+ int y; /* relative to owner */
+ int height;
+ void (*remove_cb)(fbtk_widget_t *widget);
+ } caret;
+ } root;
+
+ /* bitmap */
+ struct {
+ struct fbtk_bitmap *bitmap;
+ } bitmap;
+
+ /* text */
+ struct {
+ char* text;
+ bool outline;
+ fbtk_enter_t enter;
+ void *pw;
+ int idx; /* caret pos in text */
+ int len; /* text length */
+ int width; /* text width in px */
+ int idx_offset; /* caret pos in pixels */
+ } text;
+
+ /* application driven widget */
+ struct {
+ void *pw; /* private data for user widget */
+ } user;
+
+ struct {
+ int minimum; /* lowest value of scrollbar */
+ int maximum; /* highest value of scrollbar */
+ int thumb; /* size of bar representing a page */
+ int page; /* amount to page document */
+ int position; /* position of bar */
+ int drag; /* offset to start of drag */
+ int drag_position; /* indicator bar pos at drag start */
+ struct fbtk_widget_s *btnul; /* scroll button up/left */
+ struct fbtk_widget_s *btndr; /* scroll button down/right*/
+ } scroll;
+
+ } u;
+};
+
+
+/* These functions are not considered part of the public API but are
+ * not static as they are used by the higher level widget provision
+ * routines
+ */
+
+
+/** creates a new widget and insert it into to hierachy.
+ *
+ * The widget is set to defaults of false, 0 or NULL.
+ *
+ * @param parent The parent widget. The new widget will be added with
+ * the shallowest z order relative to its siblings.
+ * @param type The type of the widget.
+ * @param x The x co-ordinate relative to the parent widget.
+ * @param y The y co-ordinate relative to the parent widget.
+ * @param width the widgets width. This will be clipped to the parent, if
+ * the value is 0 the largest extent which can fit within the parent
+ * is used, if the value is negative the largest value that will fit
+ * within the parent less the value given will be used.
+ * @param height the widgets width. This will be clipped to the parent, if
+ * the value is 0 the largest extent which can fit within the parent
+ * is used, if the value is negative the largest value that will fit
+ * within the parent less the value given will be used.
+ */
+fbtk_widget_t *fbtk_widget_new(fbtk_widget_t *parent, enum fbtk_widgettype_e type, int x, int y, int width, int height);
+
+/** find the root widget from any widget in the toolkit hierarchy.
+ *
+ * @param widget Any widget.
+ * @return The root widget or NULL if \a widget was not valid.
+ */
+fbtk_widget_t *fbtk_get_root_widget(fbtk_widget_t *widget);
+
+/** set pointer to bitmap in context.
+ *
+ * widget helper callback to set cursor image to the bitmap passed in
+ * the callbacks private data.
+ */
+int fbtk_set_ptr(fbtk_widget_t *widget, fbtk_callback_info *cbi);
+
+#endif
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */
diff --git a/frontends/framebuffer/fbtk/window.c b/frontends/framebuffer/fbtk/window.c
new file mode 100644
index 000000000..787dac720
--- /dev/null
+++ b/frontends/framebuffer/fbtk/window.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2010 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * Framebuffer windowing toolkit window widget.
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include <stdlib.h>
+
+#include <libnsfb.h>
+#include <libnsfb_plot.h>
+
+#include "desktop/browser.h"
+
+#include "framebuffer/gui.h"
+#include "framebuffer/fbtk.h"
+
+#include "widget.h"
+
+/** Window redraw callback.
+ *
+ * Called when a window requires redrawing.
+ *
+ * @param widget The widget to be redrawn.
+ * @param cbi The callback parameters.
+ * @return The callback result.
+ */
+static int
+fb_redraw_window(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ nsfb_bbox_t bbox;
+ nsfb_t *nsfb;
+
+ if ((widget->bg & 0xFF000000) == 0)
+ return 0;
+
+ nsfb = fbtk_get_nsfb(widget);
+
+ fbtk_get_bbox(widget, &bbox);
+
+ nsfb_claim(nsfb, &bbox);
+
+ nsfb_plot_rectangle_fill(nsfb, &bbox, widget->bg);
+
+ nsfb_update(nsfb, &bbox);
+
+ return 0;
+}
+
+/* exported function documented in fbtk.h */
+fbtk_widget_t *
+fbtk_create_window(fbtk_widget_t *parent,
+ int x,
+ int y,
+ int width,
+ int height,
+ colour bg)
+{
+ fbtk_widget_t *neww;
+
+ if (parent == NULL)
+ return NULL;
+
+ neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_WINDOW, x, y, width, height);
+
+ neww->bg = bg;
+
+ fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_window, NULL);
+
+ return neww;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */
diff --git a/frontends/framebuffer/fetch.c b/frontends/framebuffer/fetch.c
new file mode 100644
index 000000000..24920fbbb
--- /dev/null
+++ b/frontends/framebuffer/fetch.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Interfaces for fetch table.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "utils/nsurl.h"
+#include "utils/log.h"
+#include "utils/filepath.h"
+#include "utils/file.h"
+#include "desktop/gui_fetch.h"
+
+#include "framebuffer/findfile.h"
+#include "framebuffer/fetch.h"
+
+
+/**
+ * Translate resource to full url.
+ *
+ * Transforms a resource: path into a full URL. The returned URL
+ * is used as the target for a redirect. The caller takes ownership of
+ * the returned nsurl including unrefing it when finished with it.
+ *
+ * \param path The path of the resource to locate.
+ * \return A string containing the full URL of the target object or
+ * NULL if no suitable resource can be found.
+ */
+static nsurl *get_resource_url(const char *path)
+{
+ char buf[PATH_MAX];
+ nsurl *url = NULL;
+
+ if (strcmp(path, "favicon.ico") == 0)
+ path = "favicon.png";
+
+ netsurf_path_to_nsurl(filepath_sfind(respaths, buf, path), &url);
+
+ return url;
+}
+
+/**
+ * filetype -- determine the MIME type of a local file
+ */
+static const char *fetch_filetype(const char *unix_path)
+{
+ int l;
+ LOG("unix path %s", unix_path);
+ l = strlen(unix_path);
+ if (2 < l && strcasecmp(unix_path + l - 3, "css") == 0)
+ return "text/css";
+ if (2 < l && strcasecmp(unix_path + l - 3, "f79") == 0)
+ return "text/css";
+ if (2 < l && strcasecmp(unix_path + l - 3, "jpg") == 0)
+ return "image/jpeg";
+ if (3 < l && strcasecmp(unix_path + l - 4, "jpeg") == 0)
+ return "image/jpeg";
+ if (2 < l && strcasecmp(unix_path + l - 3, "gif") == 0)
+ return "image/gif";
+ if (2 < l && strcasecmp(unix_path + l - 3, "png") == 0)
+ return "image/png";
+ if (2 < l && strcasecmp(unix_path + l - 3, "b60") == 0)
+ return "image/png";
+ if (2 < l && strcasecmp(unix_path + l - 3, "jng") == 0)
+ return "image/jng";
+ if (2 < l && strcasecmp(unix_path + l - 3, "svg") == 0)
+ return "image/svg";
+ return "text/html";
+}
+
+/* table for fetch operations */
+static struct gui_fetch_table fetch_table = {
+ .filetype = fetch_filetype,
+
+ .get_resource_url = get_resource_url,
+};
+
+struct gui_fetch_table *framebuffer_fetch_table = &fetch_table;
diff --git a/frontends/framebuffer/fetch.h b/frontends/framebuffer/fetch.h
new file mode 100644
index 000000000..718b08300
--- /dev/null
+++ b/frontends/framebuffer/fetch.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_FB_FETCH_H
+#define NETSURF_FB_FETCH_H
+
+struct gui_fetch_table *framebuffer_fetch_table;
+
+#endif
diff --git a/frontends/framebuffer/findfile.c b/frontends/framebuffer/findfile.c
new file mode 100644
index 000000000..67312f452
--- /dev/null
+++ b/frontends/framebuffer/findfile.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2008 Daniel Silverstone <dsilvers@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdio.h>
+
+#include "utils/filepath.h"
+
+#include "framebuffer/findfile.h"
+
+char **respaths; /** resource search path vector */
+
+/** Create an array of valid paths to search for resources.
+ *
+ * The idea is that all the complex path computation to find resources
+ * is performed here, once, rather than every time a resource is
+ * searched for.
+ */
+char **
+fb_init_resource(const char *resource_path)
+{
+ char **pathv; /* resource path string vector */
+ char **respath; /* resource paths vector */
+ const char *lang = NULL;
+
+ pathv = filepath_path_to_strvec(resource_path);
+
+ respath = filepath_generate(pathv, &lang);
+
+ filepath_free_strvec(pathv);
+
+ return respath;
+}
+
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 8
+ * End:
+ */
+
diff --git a/frontends/framebuffer/findfile.h b/frontends/framebuffer/findfile.h
new file mode 100644
index 000000000..1f3db6eb1
--- /dev/null
+++ b/frontends/framebuffer/findfile.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2008 Daniel Silverstone <dsilvers@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_FB_FINDFILE_H
+#define NETSURF_FB_FINDFILE_H
+
+extern char **respaths;
+
+/** Create an array of valid paths to search for resources.
+ *
+ * The idea is that all the complex path computation to find resources
+ * is performed here, once, rather than every time a resource is
+ * searched for.
+ */
+char **fb_init_resource(const char *resource_path);
+
+#endif /* NETSURF_FB_FINDFILE_H */
diff --git a/frontends/framebuffer/font.h b/frontends/framebuffer/font.h
new file mode 100644
index 000000000..722a604e4
--- /dev/null
+++ b/frontends/framebuffer/font.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_FB_FONT_H
+#define NETSURF_FB_FONT_H
+
+extern struct gui_layout_table *framebuffer_layout_table;
+extern struct gui_utf8_table *framebuffer_utf8_table;
+
+/**
+ * Initialise framebuffer font handling.
+ */
+bool fb_font_init(void);
+
+/**
+ * Finalise framebuffer font handling.
+ */
+bool fb_font_finalise(void);
+
+/**
+ * Find the position in a string where an x coordinate falls.
+ *
+ * \param[in] fstyle style for this text
+ * \param[in] string UTF-8 string to measure
+ * \param[in] length length of string, in bytes
+ * \param[in] x coordinate to search for
+ * \param[out] char_offset updated to offset in string of actual_x, [0..length]
+ * \param[out] actual_x updated to x coordinate of character closest to x
+ * \return NSERROR_OK and char_offset and actual_x updated or
+ * appropriate error code on faliure
+ */
+nserror fb_font_position(const struct plot_font_style *fstyle, const char *string, size_t length, int x, size_t *char_offset, int *actual_x);
+
+/**
+ * Measure the width of a string.
+ *
+ * \param[in] fstyle plot style for this text
+ * \param[in] string UTF-8 string to measure
+ * \param[in] length length of string, in bytes
+ * \param[out] width updated to width of string[0..length)
+ * \return NSERROR_OK and width updated or appropriate error code on faliure
+ */
+nserror fb_font_width(const struct plot_font_style *fstyle, const char *string, size_t length, int *width);
+
+
+#ifdef FB_USE_FREETYPE
+#include "framebuffer/font_freetype.h"
+#else
+#include "framebuffer/font_internal.h"
+#endif
+
+#endif /* NETSURF_FB_FONT_H */
+
diff --git a/frontends/framebuffer/font_freetype.c b/frontends/framebuffer/font_freetype.c
new file mode 100644
index 000000000..7756ae77b
--- /dev/null
+++ b/frontends/framebuffer/font_freetype.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ * 2008 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <inttypes.h>
+#include <assert.h>
+
+#include <ft2build.h>
+#include FT_CACHE_H
+
+#include "utils/filepath.h"
+#include "utils/utf8.h"
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "desktop/gui_utf8.h"
+#include "desktop/gui_layout.h"
+#include "desktop/browser.h"
+
+#include "framebuffer/gui.h"
+#include "framebuffer/font.h"
+#include "framebuffer/findfile.h"
+
+/* glyph cache minimum size */
+#define CACHE_MIN_SIZE (100 * 1024)
+
+#define BOLD_WEIGHT 700
+
+static FT_Library library;
+static FTC_Manager ft_cmanager;
+static FTC_CMapCache ft_cmap_cache ;
+static FTC_ImageCache ft_image_cache;
+
+int ft_load_type;
+
+/* cache manager faceID data to create freetype faceid on demand */
+typedef struct fb_faceid_s {
+ char *fontfile; /* path to font */
+ int index; /* index of font */
+ int cidx; /* character map index for unicode */
+} fb_faceid_t;
+
+
+enum fb_face_e {
+ FB_FACE_SANS_SERIF = 0,
+ FB_FACE_SANS_SERIF_BOLD,
+ FB_FACE_SANS_SERIF_ITALIC,
+ FB_FACE_SANS_SERIF_ITALIC_BOLD,
+ FB_FACE_SERIF,
+ FB_FACE_SERIF_BOLD,
+ FB_FACE_MONOSPACE,
+ FB_FACE_MONOSPACE_BOLD,
+ FB_FACE_CURSIVE,
+ FB_FACE_FANTASY,
+ FB_FACE_COUNT
+};
+
+/* defines for accesing the faces */
+#define FB_FACE_DEFAULT 0
+
+static fb_faceid_t *fb_faces[FB_FACE_COUNT];
+
+/**
+ * map cache manager handle to face id
+ */
+static FT_Error
+ft_face_requester(FTC_FaceID face_id,
+ FT_Library library,
+ FT_Pointer request_data,
+ FT_Face *face )
+{
+ FT_Error error;
+ fb_faceid_t *fb_face = (fb_faceid_t *)face_id;
+ int cidx;
+
+ error = FT_New_Face(library, fb_face->fontfile, fb_face->index, face);
+ if (error) {
+ LOG("Could not find font (code %d)", error);
+ } else {
+
+ error = FT_Select_Charmap(*face, FT_ENCODING_UNICODE);
+ if (error) {
+ LOG("Could not select charmap (code %d)", error);
+ } else {
+ for (cidx = 0; cidx < (*face)->num_charmaps; cidx++) {
+ if ((*face)->charmap == (*face)->charmaps[cidx]) {
+ fb_face->cidx = cidx;
+ break;
+ }
+ }
+ }
+ }
+ LOG("Loaded face from %s", fb_face->fontfile);
+
+ return error;
+}
+
+/**
+ * create new framebuffer face and cause it to be loaded to check its ok
+ */
+static fb_faceid_t *
+fb_new_face(const char *option, const char *resname, const char *fontname)
+{
+ fb_faceid_t *newf;
+ FT_Error error;
+ FT_Face aface;
+ char buf[PATH_MAX];
+
+ newf = calloc(1, sizeof(fb_faceid_t));
+
+ if (option != NULL) {
+ newf->fontfile = strdup(option);
+ } else {
+ filepath_sfind(respaths, buf, fontname);
+ newf->fontfile = strdup(buf);
+ }
+
+ error = FTC_Manager_LookupFace(ft_cmanager, (FTC_FaceID)newf, &aface);
+ if (error) {
+ LOG("Could not find font face %s (code %d)", fontname, error);
+ free(newf->fontfile);
+ free(newf);
+ newf = NULL;
+ }
+
+ return newf;
+}
+
+/* exported interface documented in framebuffer/font.h */
+bool fb_font_init(void)
+{
+ FT_Error error;
+ FT_ULong max_cache_size;
+ FT_UInt max_faces = 6;
+ fb_faceid_t *fb_face;
+
+ /* freetype library initialise */
+ error = FT_Init_FreeType( &library );
+ if (error) {
+ LOG("Freetype could not initialised (code %d)", error);
+ return false;
+ }
+
+ /* set the Glyph cache size up */
+ max_cache_size = nsoption_int(fb_font_cachesize) * 1024;
+
+ if (max_cache_size < CACHE_MIN_SIZE) {
+ max_cache_size = CACHE_MIN_SIZE;
+ }
+
+ /* cache manager initialise */
+ error = FTC_Manager_New(library,
+ max_faces,
+ 0,
+ max_cache_size,
+ ft_face_requester,
+ NULL,
+ &ft_cmanager);
+ if (error) {
+ LOG("Freetype could not initialise cache manager (code %d)", error);
+ FT_Done_FreeType(library);
+ return false;
+ }
+
+ error = FTC_CMapCache_New(ft_cmanager, &ft_cmap_cache);
+
+ error = FTC_ImageCache_New(ft_cmanager, &ft_image_cache);
+
+ /* need to obtain the generic font faces */
+
+ /* Start with the sans serif font */
+ fb_face = fb_new_face(nsoption_charp(fb_face_sans_serif),
+ "sans_serif.ttf",
+ NETSURF_FB_FONT_SANS_SERIF);
+ if (fb_face == NULL) {
+ /* The sans serif font is the default and must be found. */
+ LOG("Could not find the default font");
+ FTC_Manager_Done(ft_cmanager);
+ FT_Done_FreeType(library);
+ return false;
+ } else {
+ fb_faces[FB_FACE_SANS_SERIF] = fb_face;
+ }
+
+ /* Bold sans serif face */
+ fb_face = fb_new_face(nsoption_charp(fb_face_sans_serif_bold),
+ "sans_serif_bold.ttf",
+ NETSURF_FB_FONT_SANS_SERIF_BOLD);
+ if (fb_face == NULL) {
+ /* seperate bold face unavailabe use the normal weight version */
+ fb_faces[FB_FACE_SANS_SERIF_BOLD] = fb_faces[FB_FACE_SANS_SERIF];
+ } else {
+ fb_faces[FB_FACE_SANS_SERIF_BOLD] = fb_face;
+ }
+
+ /* Italic sans serif face */
+ fb_face = fb_new_face(nsoption_charp(fb_face_sans_serif_italic),
+ "sans_serif_italic.ttf",
+ NETSURF_FB_FONT_SANS_SERIF_ITALIC);
+ if (fb_face == NULL) {
+ /* seperate italic face unavailabe use the normal weight version */
+ fb_faces[FB_FACE_SANS_SERIF_ITALIC] = fb_faces[FB_FACE_SANS_SERIF];
+ } else {
+ fb_faces[FB_FACE_SANS_SERIF_ITALIC] = fb_face;
+ }
+
+ /* Bold italic sans serif face */
+ fb_face = fb_new_face(nsoption_charp(fb_face_sans_serif_italic_bold),
+ "sans_serif_italic_bold.ttf",
+ NETSURF_FB_FONT_SANS_SERIF_ITALIC_BOLD);
+ if (fb_face == NULL) {
+ /* seperate italic face unavailabe use the normal weight version */
+ fb_faces[FB_FACE_SANS_SERIF_ITALIC_BOLD] = fb_faces[FB_FACE_SANS_SERIF];
+ } else {
+ fb_faces[FB_FACE_SANS_SERIF_ITALIC_BOLD] = fb_face;
+ }
+
+ /* serif face */
+ fb_face = fb_new_face(nsoption_charp(fb_face_serif),
+ "serif.ttf",
+ NETSURF_FB_FONT_SERIF);
+ if (fb_face == NULL) {
+ /* serif face unavailabe use the default */
+ fb_faces[FB_FACE_SERIF] = fb_faces[FB_FACE_SANS_SERIF];
+ } else {
+ fb_faces[FB_FACE_SERIF] = fb_face;
+ }
+
+ /* bold serif face*/
+ fb_face = fb_new_face(nsoption_charp(fb_face_serif_bold),
+ "serif_bold.ttf",
+ NETSURF_FB_FONT_SERIF_BOLD);
+ if (fb_face == NULL) {
+ /* bold serif face unavailabe use the normal weight */
+ fb_faces[FB_FACE_SERIF_BOLD] = fb_faces[FB_FACE_SERIF];
+ } else {
+ fb_faces[FB_FACE_SERIF_BOLD] = fb_face;
+ }
+
+
+ /* monospace face */
+ fb_face = fb_new_face(nsoption_charp(fb_face_monospace),
+ "monospace.ttf",
+ NETSURF_FB_FONT_MONOSPACE);
+ if (fb_face == NULL) {
+ /* serif face unavailabe use the default */
+ fb_faces[FB_FACE_MONOSPACE] = fb_faces[FB_FACE_SANS_SERIF];
+ } else {
+ fb_faces[FB_FACE_MONOSPACE] = fb_face;
+ }
+
+ /* bold monospace face*/
+ fb_face = fb_new_face(nsoption_charp(fb_face_monospace_bold),
+ "monospace_bold.ttf",
+ NETSURF_FB_FONT_MONOSPACE_BOLD);
+ if (fb_face == NULL) {
+ /* bold serif face unavailabe use the normal weight */
+ fb_faces[FB_FACE_MONOSPACE_BOLD] = fb_faces[FB_FACE_MONOSPACE];
+ } else {
+ fb_faces[FB_FACE_MONOSPACE_BOLD] = fb_face;
+ }
+
+ /* cursive face */
+ fb_face = fb_new_face(nsoption_charp(fb_face_cursive),
+ "cursive.ttf",
+ NETSURF_FB_FONT_CURSIVE);
+ if (fb_face == NULL) {
+ /* cursive face unavailabe use the default */
+ fb_faces[FB_FACE_CURSIVE] = fb_faces[FB_FACE_SANS_SERIF];
+ } else {
+ fb_faces[FB_FACE_CURSIVE] = fb_face;
+ }
+
+ /* fantasy face */
+ fb_face = fb_new_face(nsoption_charp(fb_face_fantasy),
+ "fantasy.ttf",
+ NETSURF_FB_FONT_FANTASY);
+ if (fb_face == NULL) {
+ /* fantasy face unavailabe use the default */
+ fb_faces[FB_FACE_FANTASY] = fb_faces[FB_FACE_SANS_SERIF];
+ } else {
+ fb_faces[FB_FACE_FANTASY] = fb_face;
+ }
+
+
+ /* set the default render mode */
+ if (nsoption_bool(fb_font_monochrome) == true)
+ ft_load_type = FT_LOAD_MONOCHROME; /* faster but less pretty */
+ else
+ ft_load_type = 0;
+
+ return true;
+}
+
+/* exported interface documented in framebuffer/font.h */
+bool fb_font_finalise(void)
+{
+ int i, j;
+
+ FTC_Manager_Done(ft_cmanager);
+ FT_Done_FreeType(library);
+
+ for (i = 0; i < FB_FACE_COUNT; i++) {
+ if (fb_faces[i] == NULL)
+ continue;
+
+ /* Unset any faces that duplicate this one */
+ for (j = i + 1; j < FB_FACE_COUNT; j++) {
+ if (fb_faces[i] == fb_faces[j])
+ fb_faces[j] = NULL;
+ }
+
+ free(fb_faces[i]->fontfile);
+ free(fb_faces[i]);
+
+ fb_faces[i] = NULL;
+ }
+
+ return true;
+}
+
+/**
+ * fill freetype scalar
+ */
+static void fb_fill_scalar(const plot_font_style_t *fstyle, FTC_Scaler srec)
+{
+ int selected_face = FB_FACE_DEFAULT;
+
+ switch (fstyle->family) {
+
+ case PLOT_FONT_FAMILY_SERIF:
+ if (fstyle->weight >= BOLD_WEIGHT) {
+ selected_face = FB_FACE_SERIF_BOLD;
+ } else {
+ selected_face = FB_FACE_SERIF;
+ }
+ break;
+
+ case PLOT_FONT_FAMILY_MONOSPACE:
+ if (fstyle->weight >= BOLD_WEIGHT) {
+ selected_face = FB_FACE_MONOSPACE_BOLD;
+ } else {
+ selected_face = FB_FACE_MONOSPACE;
+ }
+ break;
+
+ case PLOT_FONT_FAMILY_CURSIVE:
+ selected_face = FB_FACE_CURSIVE;
+ break;
+
+ case PLOT_FONT_FAMILY_FANTASY:
+ selected_face = FB_FACE_FANTASY;
+ break;
+
+ case PLOT_FONT_FAMILY_SANS_SERIF:
+ default:
+ if ((fstyle->flags & FONTF_ITALIC) ||
+ (fstyle->flags & FONTF_OBLIQUE)) {
+ if (fstyle->weight >= BOLD_WEIGHT) {
+ selected_face = FB_FACE_SANS_SERIF_ITALIC_BOLD;
+ } else {
+ selected_face = FB_FACE_SANS_SERIF_ITALIC;
+ }
+ } else {
+ if (fstyle->weight >= BOLD_WEIGHT) {
+ selected_face = FB_FACE_SANS_SERIF_BOLD;
+ } else {
+ selected_face = FB_FACE_SANS_SERIF;
+ }
+ }
+ }
+
+ srec->face_id = (FTC_FaceID)fb_faces[selected_face];
+
+ srec->width = srec->height = (fstyle->size * 64) / FONT_SIZE_SCALE;
+ srec->pixel = 0;
+
+ srec->x_res = srec->y_res = browser_get_dpi();
+}
+
+/* exported interface documented in framebuffer/freetype_font.h */
+FT_Glyph fb_getglyph(const plot_font_style_t *fstyle, uint32_t ucs4)
+{
+ FT_UInt glyph_index;
+ FTC_ScalerRec srec;
+ FT_Glyph glyph;
+ FT_Error error;
+ fb_faceid_t *fb_face;
+
+ fb_fill_scalar(fstyle, &srec);
+
+ fb_face = (fb_faceid_t *)srec.face_id;
+
+ glyph_index = FTC_CMapCache_Lookup(ft_cmap_cache, srec.face_id,
+ fb_face->cidx, ucs4);
+
+ error = FTC_ImageCache_LookupScaler(ft_image_cache,
+ &srec,
+ FT_LOAD_RENDER |
+ FT_LOAD_FORCE_AUTOHINT |
+ ft_load_type,
+ glyph_index,
+ &glyph,
+ NULL);
+ if (error != 0)
+ return NULL;
+
+ return glyph;
+}
+
+
+/* exported interface documented in framebuffer/freetype_font.h */
+nserror
+fb_font_width(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int *width)
+{
+ uint32_t ucs4;
+ size_t nxtchr = 0;
+ FT_Glyph glyph;
+
+ *width = 0;
+ while (nxtchr < length) {
+ ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr);
+ nxtchr = utf8_next(string, length, nxtchr);
+
+ glyph = fb_getglyph(fstyle, ucs4);
+ if (glyph == NULL)
+ continue;
+
+ *width += glyph->advance.x >> 16;
+ }
+
+ return true;
+}
+
+
+/* exported interface documented in framebuffer/freetype_font.h */
+nserror
+fb_font_position(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x)
+{
+ uint32_t ucs4;
+ size_t nxtchr = 0;
+ FT_Glyph glyph;
+ int prev_x = 0;
+
+ *actual_x = 0;
+ while (nxtchr < length) {
+ ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr);
+
+ glyph = fb_getglyph(fstyle, ucs4);
+ if (glyph == NULL)
+ continue;
+
+ *actual_x += glyph->advance.x >> 16;
+ if (*actual_x > x)
+ break;
+
+ prev_x = *actual_x;
+ nxtchr = utf8_next(string, length, nxtchr);
+ }
+
+ /* choose nearest of previous and last x */
+ if (abs(*actual_x - x) > abs(prev_x - x))
+ *actual_x = prev_x;
+
+ *char_offset = nxtchr;
+ return true;
+}
+
+
+/**
+ * Find where to split a string to make it fit a width.
+ *
+ * \param fstyle style for this text
+ * \param string UTF-8 string to measure
+ * \param length length of string, in bytes
+ * \param x width available
+ * \param char_offset updated to offset in string of actual_x, [1..length]
+ * \param actual_x updated to x coordinate of character closest to x
+ * \return true on success, false on error and error reported
+ *
+ * On exit, char_offset indicates first character after split point.
+ *
+ * Note: char_offset of 0 should never be returned.
+ *
+ * Returns:
+ * char_offset giving split point closest to x, where actual_x <= x
+ * else
+ * char_offset giving split point closest to x, where actual_x > x
+ *
+ * Returning char_offset == length means no split possible
+ */
+static nserror
+fb_font_split(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x)
+{
+ uint32_t ucs4;
+ size_t nxtchr = 0;
+ int last_space_x = 0;
+ int last_space_idx = 0;
+ FT_Glyph glyph;
+
+ *actual_x = 0;
+ while (nxtchr < length) {
+ ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr);
+
+ glyph = fb_getglyph(fstyle, ucs4);
+ if (glyph == NULL)
+ continue;
+
+ if (ucs4 == 0x20) {
+ last_space_x = *actual_x;
+ last_space_idx = nxtchr;
+ }
+
+ *actual_x += glyph->advance.x >> 16;
+ if (*actual_x > x && last_space_idx != 0) {
+ /* string has exceeded available width and we've
+ * found a space; return previous space */
+ *actual_x = last_space_x;
+ *char_offset = last_space_idx;
+ return true;
+ }
+
+ nxtchr = utf8_next(string, length, nxtchr);
+ }
+
+ *char_offset = nxtchr;
+
+ return true;
+}
+
+static struct gui_layout_table layout_table = {
+ .width = fb_font_width,
+ .position = fb_font_position,
+ .split = fb_font_split,
+};
+
+struct gui_layout_table *framebuffer_layout_table = &layout_table;
+
+
+struct gui_utf8_table *framebuffer_utf8_table = NULL;
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */
diff --git a/frontends/framebuffer/font_freetype.h b/frontends/framebuffer/font_freetype.h
new file mode 100644
index 000000000..cbc6d82c8
--- /dev/null
+++ b/frontends/framebuffer/font_freetype.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_FB_FONT_FREETYPE_H
+#define NETSURF_FB_FONT_FREETYPE_H
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+
+extern int ft_load_type;
+
+FT_Glyph fb_getglyph(const plot_font_style_t *fstyle, uint32_t ucs4);
+
+#endif /* NETSURF_FB_FONT_FREETYPE_H */
diff --git a/frontends/framebuffer/font_internal.c b/frontends/framebuffer/font_internal.c
new file mode 100644
index 000000000..7578e641a
--- /dev/null
+++ b/frontends/framebuffer/font_internal.c
@@ -0,0 +1,491 @@
+/*
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ * 2008 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <inttypes.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "utils/nsoption.h"
+#include "utils/utf8.h"
+#include "desktop/gui_utf8.h"
+#include "desktop/gui_layout.h"
+
+#include "framebuffer/gui.h"
+#include "framebuffer/font.h"
+
+#include <font-ns-sans.h>
+
+#define GLYPH_LEN 16
+
+uint8_t code_point[GLYPH_LEN];
+uint8_t glyph_x2[GLYPH_LEN * 4];
+
+#define SEVEN_SET ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | \
+ (1 << 4) | (1 << 5) | (1 << 6))
+
+#define THREE_SSS ((1 << 0) | (1 << 1) | (1 << 2))
+#define THREE_S_S ((1 << 0) | (1 << 2))
+#define THREE__SS ((1 << 0) | (1 << 1) )
+#define THREE_SS_ ( (1 << 1) | (1 << 2))
+#define THREE_S__ (1 << 2)
+#define THREE__S_ (1 << 1)
+#define THREE___S (1 << 0)
+
+uint8_t frag[16][5] = {
+ { THREE_SSS,
+ THREE_S_S,
+ THREE_S_S,
+ THREE_S_S,
+ THREE_SSS },
+
+ { THREE__S_,
+ THREE_SS_,
+ THREE__S_,
+ THREE__S_,
+ THREE_SSS },
+
+ { THREE_SS_,
+ THREE___S,
+ THREE__S_,
+ THREE_S__,
+ THREE_SSS },
+
+ { THREE_SS_,
+ THREE___S,
+ THREE_SS_,
+ THREE___S,
+ THREE_SS_ },
+
+ { THREE_S_S,
+ THREE_S_S,
+ THREE_SSS,
+ THREE___S,
+ THREE___S },
+
+ { THREE_SSS,
+ THREE_S__,
+ THREE_SSS,
+ THREE___S,
+ THREE_SSS },
+
+ { THREE__SS,
+ THREE_S__,
+ THREE_SSS,
+ THREE_S_S,
+ THREE_SSS },
+
+ { THREE_SSS,
+ THREE___S,
+ THREE__S_,
+ THREE__S_,
+ THREE__S_ },
+
+ { THREE_SSS,
+ THREE_S_S,
+ THREE_SSS,
+ THREE_S_S,
+ THREE_SSS },
+
+ { THREE_SSS,
+ THREE_S_S,
+ THREE_SSS,
+ THREE___S,
+ THREE___S },
+
+ { THREE__S_,
+ THREE_S_S,
+ THREE_SSS,
+ THREE_S_S,
+ THREE_S_S },
+
+ { THREE_SS_,
+ THREE_S_S,
+ THREE_SS_,
+ THREE_S_S,
+ THREE_SS_ },
+
+ { THREE__S_,
+ THREE_S_S,
+ THREE_S__,
+ THREE_S_S,
+ THREE__S_ },
+
+ { THREE_SS_,
+ THREE_S_S,
+ THREE_S_S,
+ THREE_S_S,
+ THREE_SS_ },
+
+ { THREE_SSS,
+ THREE_S__,
+ THREE_SS_,
+ THREE_S__,
+ THREE_SSS },
+
+ { THREE_SSS,
+ THREE_S__,
+ THREE_SS_,
+ THREE_S__,
+ THREE_S__ }
+};
+
+static uint8_t * get_codepoint(uint32_t id, bool italic)
+{
+ int shift = 0;
+ int l;
+ int r;
+
+ if (!italic)
+ shift = 1;
+
+ l = (id >> 12);
+ r = 0xf & (id >> 8);
+
+ code_point[ 0] = SEVEN_SET << shift;
+
+ code_point[ 2] = (frag[l][0] << (4 + shift)) | (frag[r][0] << shift);
+ code_point[ 3] = (frag[l][1] << (4 + shift)) | (frag[r][1] << shift);
+ code_point[ 4] = (frag[l][2] << (4 + shift)) | (frag[r][2] << shift);
+ code_point[ 5] = (frag[l][3] << (4 + shift)) | (frag[r][3] << shift);
+ code_point[ 6] = (frag[l][4] << (4 + shift)) | (frag[r][4] << shift);
+
+ shift = 1;
+
+ l = 0xf & (id >> 4);
+ r = 0xf & id;
+
+ code_point[ 8] = (frag[l][0] << (4 + shift)) | (frag[r][0] << shift);
+ code_point[ 9] = (frag[l][1] << (4 + shift)) | (frag[r][1] << shift);
+ code_point[10] = (frag[l][2] << (4 + shift)) | (frag[r][2] << shift);
+ code_point[11] = (frag[l][3] << (4 + shift)) | (frag[r][3] << shift);
+ code_point[12] = (frag[l][4] << (4 + shift)) | (frag[r][4] << shift);
+
+ code_point[14] = SEVEN_SET << shift;
+
+ return (uint8_t *)code_point;
+}
+
+
+bool fb_font_init(void)
+{
+ return true;
+}
+
+bool fb_font_finalise(void)
+{
+ return true;
+}
+
+enum fb_font_style
+fb_get_font_style(const plot_font_style_t *fstyle)
+{
+ enum fb_font_style style = FB_REGULAR;
+
+ if (fstyle->weight >= 700)
+ style |= FB_BOLD;
+ if ((fstyle->flags & FONTF_ITALIC) || (fstyle->flags & FONTF_OBLIQUE))
+ style |= FB_ITALIC;
+
+ return style;
+}
+
+int
+fb_get_font_size(const plot_font_style_t *fstyle)
+{
+ int size = fstyle->size * 10 /
+ (((nsoption_int(font_min_size) * 3 +
+ nsoption_int(font_size)) / 4) * FONT_SIZE_SCALE);
+ if (size > 2)
+ size = 2;
+ else if (size <= 0)
+ size = 1;
+
+ return size;
+}
+
+/** Lookup table to scale 4 bits to 8 bits, so e.g. 0101 --> 00110011 */
+const uint8_t glyph_lut[16] = {
+ 0x00, 0x03, 0x0c, 0x0f,
+ 0x30, 0x33, 0x3c, 0x3f,
+ 0xc0, 0xc3, 0xcc, 0xcf,
+ 0xf0, 0xf3, 0xfc, 0xff
+};
+
+static const uint8_t *
+glyph_scale_2(const uint8_t *glyph_data)
+{
+ const uint8_t *glyph_max = glyph_data + GLYPH_LEN;
+ uint8_t *pos = glyph_x2;
+
+ do {
+ *pos++ = glyph_lut[*glyph_data >> 4];
+ *pos++ = glyph_lut[*glyph_data & 0xf];
+ *pos++ = glyph_lut[*glyph_data >> 4];
+ *pos++ = glyph_lut[*glyph_data & 0xf];
+ } while (++glyph_data < glyph_max);
+
+ return glyph_x2;
+}
+
+const uint8_t *
+fb_get_glyph(uint32_t ucs4, enum fb_font_style style, int scale)
+{
+ const uint8_t *glyph_data;
+ unsigned int section;
+ unsigned int offset;
+ uint16_t g_offset;
+
+ /* Internal font has no glyphs beyond U+FFFF and there isn't
+ * space to render a >4 digit codepoint; just show replacement
+ * character. */
+ if (ucs4 > 0xffff)
+ ucs4 = 0xfffd;
+
+ switch (style) {
+ case FB_BOLD_ITALIC:
+ section = fb_bold_italic_section_table[ucs4 / 256];
+ if (section != 0 || ucs4 / 256 == 0) {
+ offset = section * 256 + (ucs4 & 0xff);
+ g_offset = fb_bold_italic_sections[offset] * 16;
+ if (g_offset != 0) {
+ glyph_data = &font_glyph_data[g_offset];
+ break;
+ }
+ }
+ case FB_BOLD:
+ section = fb_bold_section_table[ucs4 / 256];
+ if (section != 0 || ucs4 / 256 == 0) {
+ offset = section * 256 + (ucs4 & 0xff);
+ g_offset = fb_bold_sections[offset] * 16;
+ if (g_offset != 0) {
+ glyph_data = &font_glyph_data[g_offset];
+ break;
+ }
+ }
+ case FB_ITALIC:
+ section = fb_italic_section_table[ucs4 / 256];
+ if (section != 0 || ucs4 / 256 == 0) {
+ offset = section * 256 + (ucs4 & 0xff);
+ g_offset = fb_italic_sections[offset] * 16;
+ if (g_offset != 0) {
+ glyph_data = &font_glyph_data[g_offset];
+ break;
+ }
+ }
+ case FB_REGULAR:
+ section = fb_regular_section_table[ucs4 / 256];
+ if (section != 0 || ucs4 / 256 == 0) {
+ offset = section * 256 + (ucs4 & 0xff);
+ g_offset = fb_regular_sections[offset] * 16;
+ if (g_offset != 0) {
+ glyph_data = &font_glyph_data[g_offset];
+ break;
+ }
+ }
+ default:
+ glyph_data = get_codepoint(ucs4, style & FB_ITALIC);
+ break;
+ }
+
+ switch (scale) {
+ case 1:
+ break;
+ case 2:
+ glyph_data = glyph_scale_2(glyph_data);
+ break;
+ default:
+ assert(scale >= 1 && scale <= 2);
+ break;
+ }
+
+ return glyph_data;
+}
+
+static nserror utf8_to_local(const char *string,
+ size_t len,
+ char **result)
+{
+ return utf8_to_enc(string, "CP1252", len, result);
+
+}
+
+static nserror utf8_from_local(const char *string,
+ size_t len,
+ char **result)
+{
+ *result = malloc(len + 1);
+ if (*result == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ memcpy(*result, string, len);
+
+ (*result)[len] = '\0';
+
+ return NSERROR_OK;
+}
+
+
+/* exported interface documented in framebuffer/freetype_font.h */
+nserror
+fb_font_width(const plot_font_style_t *fstyle,
+ const char *string,
+ size_t length,
+ int *width)
+{
+ size_t nxtchr = 0;
+
+ *width = 0;
+ while (nxtchr < length) {
+ uint32_t ucs4;
+ ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr);
+ if (codepoint_displayable(ucs4)) {
+ *width += FB_FONT_WIDTH;
+ }
+
+ nxtchr = utf8_next(string, length, nxtchr);
+ }
+
+ *width *= fb_get_font_size(fstyle);
+ return true;
+}
+
+
+/* exported interface documented in framebuffer/freetype_font.h */
+nserror
+fb_font_position(const plot_font_style_t *fstyle,
+ const char *string,
+ size_t length,
+ int x,
+ size_t *char_offset,
+ int *actual_x)
+{
+ const int width = fb_get_font_size(fstyle) * FB_FONT_WIDTH;
+ size_t nxtchr = 0;
+ int x_pos = 0;
+
+ while (nxtchr < length) {
+ uint32_t ucs4;
+ if (abs(x_pos - x) <= (width / 2))
+ break;
+
+ ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr);
+ if (codepoint_displayable(ucs4)) {
+ x_pos += width;
+ }
+
+ nxtchr = utf8_next(string, length, nxtchr);
+ }
+
+ *actual_x = x_pos;
+
+ *char_offset = nxtchr;
+ return true;
+}
+
+
+/**
+ * Find where to split a string to make it fit a width.
+ *
+ * \param fstyle style for this text
+ * \param string UTF-8 string to measure
+ * \param length length of string, in bytes
+ * \param x width available
+ * \param char_offset updated to offset in string of actual_x, [1..length]
+ * \param actual_x updated to x coordinate of character closest to x
+ * \return true on success, false on error and error reported
+ *
+ * On exit, char_offset indicates first character after split point.
+ *
+ * Note: char_offset of 0 should never be returned.
+ *
+ * Returns:
+ * char_offset giving split point closest to x, where actual_x <= x
+ * else
+ * char_offset giving split point closest to x, where actual_x > x
+ *
+ * Returning char_offset == length means no split possible
+ */
+static nserror
+fb_font_split(const plot_font_style_t *fstyle,
+ const char *string,
+ size_t length,
+ int x,
+ size_t *char_offset,
+ int *actual_x)
+{
+ const int width = fb_get_font_size(fstyle) * FB_FONT_WIDTH;
+ size_t nxtchr = 0;
+ int last_space_x = 0;
+ int last_space_idx = 0;
+
+ *actual_x = 0;
+ while (nxtchr < length) {
+ uint32_t ucs4;
+
+ if (string[nxtchr] == ' ') {
+ last_space_x = *actual_x;
+ last_space_idx = nxtchr;
+ }
+
+ ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr);
+ if (codepoint_displayable(ucs4)) {
+ *actual_x += width;
+ }
+
+ if (*actual_x > x && last_space_idx != 0) {
+ /* string has exceeded available width and we've
+ * found a space; return previous space */
+ *actual_x = last_space_x;
+ *char_offset = last_space_idx;
+ return true;
+ }
+
+ nxtchr = utf8_next(string, length, nxtchr);
+ }
+
+ *char_offset = nxtchr;
+
+ return true;
+}
+
+
+static struct gui_layout_table layout_table = {
+ .width = fb_font_width,
+ .position = fb_font_position,
+ .split = fb_font_split,
+};
+
+struct gui_layout_table *framebuffer_layout_table = &layout_table;
+
+
+static struct gui_utf8_table utf8_table = {
+ .utf8_to_local = utf8_to_local,
+ .local_to_utf8 = utf8_from_local,
+};
+
+struct gui_utf8_table *framebuffer_utf8_table = &utf8_table;
+
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */
diff --git a/frontends/framebuffer/font_internal.h b/frontends/framebuffer/font_internal.h
new file mode 100644
index 000000000..f25df8de6
--- /dev/null
+++ b/frontends/framebuffer/font_internal.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_FB_FONT_INTERNAL_H
+#define NETSURF_FB_FONT_INTERNAL_H
+
+#include <stdbool.h>
+
+struct fb_font_desc {
+ const char *name;
+ int width, height, pitch;
+};
+
+#define FB_FONT_WIDTH 8
+#define FB_FONT_HEIGHT 16
+#define FB_FONT_PITCH 8
+
+enum fb_font_style {
+ FB_REGULAR = 0,
+ FB_ITALIC = (1 << 0),
+ FB_BOLD = (1 << 1),
+ FB_BOLD_ITALIC = (FB_ITALIC | FB_BOLD)
+};
+
+enum fb_font_style fb_get_font_style(const plot_font_style_t *fstyle);
+int fb_get_font_size(const plot_font_style_t *fstyle);
+
+#define codepoint_displayable(u) \
+ (!(u >= 0x200b && u <= 0x200f))
+
+const uint8_t *fb_get_glyph(uint32_t ucs4, enum fb_font_style style, int scale);
+
+#endif /* NETSURF_FB_FONT_INTERNAL_H */
+
diff --git a/frontends/framebuffer/framebuffer.c b/frontends/framebuffer/framebuffer.c
new file mode 100644
index 000000000..57dfecb4d
--- /dev/null
+++ b/frontends/framebuffer/framebuffer.c
@@ -0,0 +1,467 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * Framebuffer interface
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libnsfb.h>
+#include <libnsfb_plot.h>
+#include <libnsfb_event.h>
+#include <libnsfb_cursor.h>
+
+#include "utils/utils.h"
+#include "utils/log.h"
+#include "utils/utf8.h"
+#include "desktop/browser.h"
+#include "desktop/plotters.h"
+#include "image/bitmap.h"
+
+#include "framebuffer/gui.h"
+#include "framebuffer/fbtk.h"
+#include "framebuffer/framebuffer.h"
+#include "framebuffer/font.h"
+#include "framebuffer/bitmap.h"
+
+/* netsurf framebuffer library handle */
+static nsfb_t *nsfb;
+
+
+static bool
+framebuffer_plot_disc(int x, int y, int radius, const plot_style_t *style)
+{
+ nsfb_bbox_t ellipse;
+ ellipse.x0 = x - radius;
+ ellipse.y0 = y - radius;
+ ellipse.x1 = x + radius;
+ ellipse.y1 = y + radius;
+
+ if (style->fill_type != PLOT_OP_TYPE_NONE) {
+ nsfb_plot_ellipse_fill(nsfb, &ellipse, style->fill_colour);
+ }
+
+ if (style->stroke_type != PLOT_OP_TYPE_NONE) {
+ nsfb_plot_ellipse(nsfb, &ellipse, style->stroke_colour);
+ }
+ return true;
+}
+
+static bool
+framebuffer_plot_arc(int x, int y, int radius, int angle1, int angle2, const plot_style_t *style)
+{
+ return nsfb_plot_arc(nsfb, x, y, radius, angle1, angle2, style->fill_colour);
+}
+
+static bool
+framebuffer_plot_polygon(const int *p, unsigned int n, const plot_style_t *style)
+{
+ return nsfb_plot_polygon(nsfb, p, n, style->fill_colour);
+}
+
+
+#ifdef FB_USE_FREETYPE
+static bool
+framebuffer_plot_text(int x, int y, const char *text, size_t length,
+ const plot_font_style_t *fstyle)
+{
+ uint32_t ucs4;
+ size_t nxtchr = 0;
+ FT_Glyph glyph;
+ FT_BitmapGlyph bglyph;
+ nsfb_bbox_t loc;
+
+ while (nxtchr < length) {
+ ucs4 = utf8_to_ucs4(text + nxtchr, length - nxtchr);
+ nxtchr = utf8_next(text, length, nxtchr);
+
+ glyph = fb_getglyph(fstyle, ucs4);
+ if (glyph == NULL)
+ continue;
+
+ if (glyph->format == FT_GLYPH_FORMAT_BITMAP) {
+ bglyph = (FT_BitmapGlyph)glyph;
+
+ loc.x0 = x + bglyph->left;
+ loc.y0 = y - bglyph->top;
+ loc.x1 = loc.x0 + bglyph->bitmap.width;
+ loc.y1 = loc.y0 + bglyph->bitmap.rows;
+
+ /* now, draw to our target surface */
+ if (bglyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
+ nsfb_plot_glyph1(nsfb,
+ &loc,
+ bglyph->bitmap.buffer,
+ bglyph->bitmap.pitch,
+ fstyle->foreground);
+ } else {
+ nsfb_plot_glyph8(nsfb,
+ &loc,
+ bglyph->bitmap.buffer,
+ bglyph->bitmap.pitch,
+ fstyle->foreground);
+ }
+ }
+ x += glyph->advance.x >> 16;
+
+ }
+ return true;
+
+}
+#else
+static bool framebuffer_plot_text(int x, int y, const char *text, size_t length,
+ const plot_font_style_t *fstyle)
+{
+ enum fb_font_style style = fb_get_font_style(fstyle);
+ int size = fb_get_font_size(fstyle);
+ const uint8_t *chrp;
+ size_t nxtchr = 0;
+ nsfb_bbox_t loc;
+ uint32_t ucs4;
+ int p = FB_FONT_PITCH * size;
+ int w = FB_FONT_WIDTH * size;
+ int h = FB_FONT_HEIGHT * size;
+
+ y -= ((h * 3) / 4);
+ /* the coord is the bottom-left of the pixels offset by 1 to make
+ * it work since fb coords are the top-left of pixels */
+ y += 1;
+
+ while (nxtchr < length) {
+ ucs4 = utf8_to_ucs4(text + nxtchr, length - nxtchr);
+ nxtchr = utf8_next(text, length, nxtchr);
+
+ if (!codepoint_displayable(ucs4))
+ continue;
+
+ loc.x0 = x;
+ loc.y0 = y;
+ loc.x1 = loc.x0 + w;
+ loc.y1 = loc.y0 + h;
+
+ chrp = fb_get_glyph(ucs4, style, size);
+ nsfb_plot_glyph1(nsfb, &loc, chrp, p, fstyle->foreground);
+
+ x += w;
+
+ }
+
+ return true;
+}
+#endif
+
+
+static bool
+framebuffer_plot_bitmap(int x, int y,
+ int width, int height,
+ struct bitmap *bitmap, colour bg,
+ bitmap_flags_t flags)
+{
+ nsfb_bbox_t loc;
+ nsfb_bbox_t clipbox;
+ bool repeat_x = (flags & BITMAPF_REPEAT_X);
+ bool repeat_y = (flags & BITMAPF_REPEAT_Y);
+ int bmwidth;
+ int bmheight;
+ int bmstride;
+ enum nsfb_format_e bmformat;
+ unsigned char *bmptr;
+ nsfb_t *bm = (nsfb_t *)bitmap;
+
+ /* x and y define coordinate of top left of of the initial explicitly
+ * placed tile. The width and height are the image scaling and the
+ * bounding box defines the extent of the repeat (which may go in all
+ * four directions from the initial tile).
+ */
+
+ if (!(repeat_x || repeat_y)) {
+ /* Not repeating at all, so just plot it */
+ loc.x0 = x;
+ loc.y0 = y;
+ loc.x1 = loc.x0 + width;
+ loc.y1 = loc.y0 + height;
+
+ return nsfb_plot_copy(bm, NULL, nsfb, &loc);
+ }
+
+ nsfb_plot_get_clip(nsfb, &clipbox);
+ nsfb_get_geometry(bm, &bmwidth, &bmheight, &bmformat);
+ nsfb_get_buffer(bm, &bmptr, &bmstride);
+
+ /* Optimise tiled plots of 1x1 bitmaps by replacing with a flat fill
+ * of the area. Can only be done when image is fully opaque. */
+ if ((bmwidth == 1) && (bmheight == 1)) {
+ if ((*(nsfb_colour_t *)bmptr & 0xff000000) != 0) {
+ return nsfb_plot_rectangle_fill(nsfb, &clipbox,
+ *(nsfb_colour_t *)bmptr);
+ }
+ }
+
+ /* Optimise tiled plots of bitmaps scaled to 1x1 by replacing with
+ * a flat fill of the area. Can only be done when image is fully
+ * opaque. */
+ if ((width == 1) && (height == 1)) {
+ if (framebuffer_bitmap_get_opaque(bm)) {
+ /** TODO: Currently using top left pixel. Maybe centre
+ * pixel or average value would be better. */
+ return nsfb_plot_rectangle_fill(nsfb, &clipbox,
+ *(nsfb_colour_t *)bmptr);
+ }
+ }
+
+ /* get left most tile position */
+ if (repeat_x)
+ for (; x > clipbox.x0; x -= width);
+
+ /* get top most tile position */
+ if (repeat_y)
+ for (; y > clipbox.y0; y -= height);
+
+ /* set up top left tile location */
+ loc.x0 = x;
+ loc.y0 = y;
+ loc.x1 = loc.x0 + width;
+ loc.y1 = loc.y0 + height;
+
+ /* plot tiling across and down to extents */
+ nsfb_plot_bitmap_tiles(nsfb, &loc,
+ repeat_x ? ((clipbox.x1 - x) + width - 1) / width : 1,
+ repeat_y ? ((clipbox.y1 - y) + height - 1) / height : 1,
+ (nsfb_colour_t *)bmptr, bmwidth, bmheight,
+ bmstride * 8 / 32, bmformat == NSFB_FMT_ABGR8888);
+
+ return true;
+}
+
+static bool
+framebuffer_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ nsfb_bbox_t rect;
+ bool dotted = false;
+ bool dashed = false;
+
+ rect.x0 = x0;
+ rect.y0 = y0;
+ rect.x1 = x1;
+ rect.y1 = y1;
+
+ if (style->fill_type != PLOT_OP_TYPE_NONE) {
+ nsfb_plot_rectangle_fill(nsfb, &rect, style->fill_colour);
+ }
+
+ if (style->stroke_type != PLOT_OP_TYPE_NONE) {
+ if (style->stroke_type == PLOT_OP_TYPE_DOT)
+ dotted = true;
+
+ if (style->stroke_type == PLOT_OP_TYPE_DASH)
+ dashed = true;
+
+ nsfb_plot_rectangle(nsfb, &rect, style->stroke_width, style->stroke_colour, dotted, dashed);
+ }
+
+ return true;
+}
+
+static bool
+framebuffer_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ nsfb_bbox_t rect;
+ nsfb_plot_pen_t pen;
+
+ rect.x0 = x0;
+ rect.y0 = y0;
+ rect.x1 = x1;
+ rect.y1 = y1;
+
+ if (style->stroke_type != PLOT_OP_TYPE_NONE) {
+
+ if (style->stroke_type == PLOT_OP_TYPE_DOT) {
+ pen.stroke_type = NFSB_PLOT_OPTYPE_PATTERN;
+ pen.stroke_pattern = 0xAAAAAAAA;
+ } else if (style->stroke_type == PLOT_OP_TYPE_DASH) {
+ pen.stroke_type = NFSB_PLOT_OPTYPE_PATTERN;
+ pen.stroke_pattern = 0xF0F0F0F0;
+ } else {
+ pen.stroke_type = NFSB_PLOT_OPTYPE_SOLID;
+ }
+
+ pen.stroke_colour = style->stroke_colour;
+ pen.stroke_width = style->stroke_width;
+ nsfb_plot_line(nsfb, &rect, &pen);
+ }
+
+ return true;
+}
+
+
+static bool
+framebuffer_plot_path(const float *p,
+ unsigned int n,
+ colour fill,
+ float width,
+ colour c,
+ const float transform[6])
+{
+ LOG("path unimplemented");
+ return true;
+}
+
+static bool
+framebuffer_plot_clip(const struct rect *clip)
+{
+ nsfb_bbox_t nsfb_clip;
+ nsfb_clip.x0 = clip->x0;
+ nsfb_clip.y0 = clip->y0;
+ nsfb_clip.x1 = clip->x1;
+ nsfb_clip.y1 = clip->y1;
+
+ return nsfb_plot_set_clip(nsfb, &nsfb_clip);
+}
+
+const struct plotter_table fb_plotters = {
+ .clip = framebuffer_plot_clip,
+ .arc = framebuffer_plot_arc,
+ .disc = framebuffer_plot_disc,
+ .line = framebuffer_plot_line,
+ .rectangle = framebuffer_plot_rectangle,
+ .polygon = framebuffer_plot_polygon,
+ .path = framebuffer_plot_path,
+ .bitmap = framebuffer_plot_bitmap,
+ .text = framebuffer_plot_text,
+ .option_knockout = true,
+};
+
+
+static bool framebuffer_format_from_bpp(int bpp, enum nsfb_format_e *fmt)
+{
+ switch (bpp) {
+ case 32:
+ *fmt = NSFB_FMT_XRGB8888;
+ break;
+
+ case 24:
+ *fmt = NSFB_FMT_RGB888;
+ break;
+
+ case 16:
+ *fmt = NSFB_FMT_RGB565;
+ break;
+
+ case 8:
+ *fmt = NSFB_FMT_I8;
+ break;
+
+ case 4:
+ *fmt = NSFB_FMT_I4;
+ break;
+
+ case 1:
+ *fmt = NSFB_FMT_I1;
+ break;
+
+ default:
+ LOG("Bad bits per pixel (%d)\n", bpp);
+ return false;
+ }
+
+ return true;
+}
+
+
+
+nsfb_t *
+framebuffer_initialise(const char *fename, int width, int height, int bpp)
+{
+ enum nsfb_type_e fbtype;
+ enum nsfb_format_e fbfmt;
+
+ /* bpp is a proxy for the framebuffer format */
+ if (framebuffer_format_from_bpp(bpp, &fbfmt) == false) {
+ return NULL;
+ }
+
+ fbtype = nsfb_type_from_name(fename);
+ if (fbtype == NSFB_SURFACE_NONE) {
+ LOG("The %s surface is not available from libnsfb\n", fename);
+ return NULL;
+ }
+
+ nsfb = nsfb_new(fbtype);
+ if (nsfb == NULL) {
+ LOG("Unable to create %s fb surface\n", fename);
+ return NULL;
+ }
+
+ if (nsfb_set_geometry(nsfb, width, height, fbfmt) == -1) {
+ LOG("Unable to set surface geometry\n");
+ nsfb_free(nsfb);
+ return NULL;
+ }
+
+ nsfb_cursor_init(nsfb);
+
+ if (nsfb_init(nsfb) == -1) {
+ LOG("Unable to initialise nsfb surface\n");
+ nsfb_free(nsfb);
+ return NULL;
+ }
+
+ return nsfb;
+
+}
+
+bool
+framebuffer_resize(nsfb_t *nsfb, int width, int height, int bpp)
+{
+ enum nsfb_format_e fbfmt;
+
+ /* bpp is a proxy for the framebuffer format */
+ if (framebuffer_format_from_bpp(bpp, &fbfmt) == false) {
+ return false;
+ }
+
+ if (nsfb_set_geometry(nsfb, width, height, fbfmt) == -1) {
+ LOG("Unable to change surface geometry\n");
+ return false;
+ }
+
+ return true;
+
+}
+
+void
+framebuffer_finalise(void)
+{
+ nsfb_free(nsfb);
+}
+
+bool
+framebuffer_set_cursor(struct fbtk_bitmap *bm)
+{
+ return nsfb_cursor_set(nsfb, (nsfb_colour_t *)bm->pixdata, bm->width, bm->height, bm->width, bm->hot_x, bm->hot_y);
+}
+
+nsfb_t *framebuffer_set_surface(nsfb_t *new_nsfb)
+{
+ nsfb_t *old_nsfb;
+ old_nsfb = nsfb;
+ nsfb = new_nsfb;
+ return old_nsfb;
+}
diff --git a/frontends/framebuffer/framebuffer.h b/frontends/framebuffer/framebuffer.h
new file mode 100644
index 000000000..d99049f52
--- /dev/null
+++ b/frontends/framebuffer/framebuffer.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * framebuffer interface.
+ */
+
+#ifndef NETSURF_FB_FRAMEBUFFER_H
+#define NETSURF_FB_FRAMEBUFFER_H
+
+extern const struct plotter_table fb_plotters;
+
+nsfb_t *framebuffer_initialise(const char *fename, int width, int height, int bpp);
+bool framebuffer_resize(nsfb_t *nsfb, int width, int height, int bpp);
+void framebuffer_finalise(void);
+bool framebuffer_set_cursor(struct fbtk_bitmap *bm);
+
+/** Set framebuffer surface to render into
+ *
+ * @return return old surface
+ */
+nsfb_t *framebuffer_set_surface(nsfb_t *new_nsfb);
+
+#endif
diff --git a/frontends/framebuffer/gui.c b/frontends/framebuffer/gui.c
new file mode 100644
index 000000000..b0b98c546
--- /dev/null
+++ b/frontends/framebuffer/gui.c
@@ -0,0 +1,2226 @@
+/*
+ * Copyright 2008, 2014 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdint.h>
+#include <limits.h>
+#include <getopt.h>
+#include <assert.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <nsutils/time.h>
+
+#include <libnsfb.h>
+#include <libnsfb_plot.h>
+#include <libnsfb_event.h>
+
+#include "utils/utils.h"
+#include "utils/nsoption.h"
+#include "utils/filepath.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "desktop/browser.h"
+#include "desktop/textinput.h"
+#include "desktop/browser_history.h"
+#include "desktop/plotters.h"
+#include "desktop/gui_window.h"
+#include "desktop/gui_misc.h"
+#include "desktop/netsurf.h"
+#include "content/urldb.h"
+#include "content/fetch.h"
+
+#include "framebuffer/gui.h"
+#include "framebuffer/fbtk.h"
+#include "framebuffer/framebuffer.h"
+#include "framebuffer/schedule.h"
+#include "framebuffer/findfile.h"
+#include "framebuffer/image_data.h"
+#include "framebuffer/font.h"
+#include "framebuffer/clipboard.h"
+#include "framebuffer/fetch.h"
+#include "framebuffer/bitmap.h"
+
+
+#define NSFB_TOOLBAR_DEFAULT_LAYOUT "blfsrutc"
+
+fbtk_widget_t *fbtk;
+
+static bool fb_complete = false;
+
+struct gui_window *input_window = NULL;
+struct gui_window *search_current_window;
+struct gui_window *window_list = NULL;
+
+/* private data for browser user widget */
+struct browser_widget_s {
+ struct browser_window *bw; /**< The browser window connected to this gui window */
+ int scrollx, scrolly; /**< scroll offsets. */
+
+ /* Pending window redraw state. */
+ bool redraw_required; /**< flag indicating the foreground loop
+ * needs to redraw the browser widget.
+ */
+ bbox_t redraw_box; /**< Area requiring redraw. */
+ bool pan_required; /**< flag indicating the foreground loop
+ * needs to pan the window.
+ */
+ int panx, pany; /**< Panning required. */
+};
+
+static struct gui_drag {
+ enum state {
+ GUI_DRAG_NONE,
+ GUI_DRAG_PRESSED,
+ GUI_DRAG_DRAG
+ } state;
+ int button;
+ int x;
+ int y;
+ bool grabbed_pointer;
+} gui_drag;
+
+
+/**
+ * Cause an abnormal program termination.
+ *
+ * \note This never returns and is intended to terminate without any cleanup.
+ *
+ * \param error The message to display to the user.
+ */
+static void die(const char *error)
+{
+ fprintf(stderr, "%s\n", error);
+ exit(1);
+}
+
+
+/**
+ * Warn the user of an event.
+ *
+ * \param[in] message A warning looked up in the message translation table
+ * \param[in] detail Additional text to be displayed or NULL.
+ * \return NSERROR_OK on success or error code if there was a
+ * faliure displaying the message to the user.
+ */
+static nserror fb_warn_user(const char *warning, const char *detail)
+{
+ LOG("%s %s", warning, detail);
+ return NSERROR_OK;
+}
+
+/* queue a redraw operation, co-ordinates are relative to the window */
+static void
+fb_queue_redraw(struct fbtk_widget_s *widget, int x0, int y0, int x1, int y1)
+{
+ struct browser_widget_s *bwidget = fbtk_get_userpw(widget);
+
+ bwidget->redraw_box.x0 = min(bwidget->redraw_box.x0, x0);
+ bwidget->redraw_box.y0 = min(bwidget->redraw_box.y0, y0);
+ bwidget->redraw_box.x1 = max(bwidget->redraw_box.x1, x1);
+ bwidget->redraw_box.y1 = max(bwidget->redraw_box.y1, y1);
+
+ if (fbtk_clip_to_widget(widget, &bwidget->redraw_box)) {
+ bwidget->redraw_required = true;
+ fbtk_request_redraw(widget);
+ } else {
+ bwidget->redraw_box.y0 = bwidget->redraw_box.x0 = INT_MAX;
+ bwidget->redraw_box.y1 = bwidget->redraw_box.x1 = -(INT_MAX);
+ bwidget->redraw_required = false;
+ }
+}
+
+/* queue a window scroll */
+static void
+widget_scroll_y(struct gui_window *gw, int y, bool abs)
+{
+ struct browser_widget_s *bwidget = fbtk_get_userpw(gw->browser);
+ int content_width, content_height;
+ int height;
+
+ LOG("window scroll");
+ if (abs) {
+ bwidget->pany = y - bwidget->scrolly;
+ } else {
+ bwidget->pany += y;
+ }
+
+ browser_window_get_extents(gw->bw, true,
+ &content_width, &content_height);
+
+ height = fbtk_get_height(gw->browser);
+
+ /* dont pan off the top */
+ if ((bwidget->scrolly + bwidget->pany) < 0)
+ bwidget->pany = -bwidget->scrolly;
+
+ /* do not pan off the bottom of the content */
+ if ((bwidget->scrolly + bwidget->pany) > (content_height - height))
+ bwidget->pany = (content_height - height) - bwidget->scrolly;
+
+ if (bwidget->pany == 0)
+ return;
+
+ bwidget->pan_required = true;
+
+ fbtk_request_redraw(gw->browser);
+
+ fbtk_set_scroll_position(gw->vscroll, bwidget->scrolly + bwidget->pany);
+}
+
+/* queue a window scroll */
+static void
+widget_scroll_x(struct gui_window *gw, int x, bool abs)
+{
+ struct browser_widget_s *bwidget = fbtk_get_userpw(gw->browser);
+ int content_width, content_height;
+ int width;
+
+ if (abs) {
+ bwidget->panx = x - bwidget->scrollx;
+ } else {
+ bwidget->panx += x;
+ }
+
+ browser_window_get_extents(gw->bw, true,
+ &content_width, &content_height);
+
+ width = fbtk_get_width(gw->browser);
+
+ /* dont pan off the left */
+ if ((bwidget->scrollx + bwidget->panx) < 0)
+ bwidget->panx = - bwidget->scrollx;
+
+ /* do not pan off the right of the content */
+ if ((bwidget->scrollx + bwidget->panx) > (content_width - width))
+ bwidget->panx = (content_width - width) - bwidget->scrollx;
+
+ if (bwidget->panx == 0)
+ return;
+
+ bwidget->pan_required = true;
+
+ fbtk_request_redraw(gw->browser);
+
+ fbtk_set_scroll_position(gw->hscroll, bwidget->scrollx + bwidget->panx);
+}
+
+static void
+fb_pan(fbtk_widget_t *widget,
+ struct browser_widget_s *bwidget,
+ struct browser_window *bw)
+{
+ int x;
+ int y;
+ int width;
+ int height;
+ nsfb_bbox_t srcbox;
+ nsfb_bbox_t dstbox;
+
+ nsfb_t *nsfb = fbtk_get_nsfb(widget);
+
+ height = fbtk_get_height(widget);
+ width = fbtk_get_width(widget);
+
+ LOG("panning %d, %d", bwidget->panx, bwidget->pany);
+
+ x = fbtk_get_absx(widget);
+ y = fbtk_get_absy(widget);
+
+ /* if the pan exceeds the viewport size just redraw the whole area */
+ if (bwidget->pany >= height || bwidget->pany <= -height ||
+ bwidget->panx >= width || bwidget->panx <= -width) {
+
+ bwidget->scrolly += bwidget->pany;
+ bwidget->scrollx += bwidget->panx;
+ fb_queue_redraw(widget, 0, 0, width, height);
+
+ /* ensure we don't try to scroll again */
+ bwidget->panx = 0;
+ bwidget->pany = 0;
+ bwidget->pan_required = false;
+ return;
+ }
+
+ if (bwidget->pany < 0) {
+ /* pan up by less then viewport height */
+ srcbox.x0 = x;
+ srcbox.y0 = y;
+ srcbox.x1 = srcbox.x0 + width;
+ srcbox.y1 = srcbox.y0 + height + bwidget->pany;
+
+ dstbox.x0 = x;
+ dstbox.y0 = y - bwidget->pany;
+ dstbox.x1 = dstbox.x0 + width;
+ dstbox.y1 = dstbox.y0 + height + bwidget->pany;
+
+ /* move part that remains visible up */
+ nsfb_plot_copy(nsfb, &srcbox, nsfb, &dstbox);
+
+ /* redraw newly exposed area */
+ bwidget->scrolly += bwidget->pany;
+ fb_queue_redraw(widget, 0, 0, width, - bwidget->pany);
+
+ } else if (bwidget->pany > 0) {
+ /* pan down by less then viewport height */
+ srcbox.x0 = x;
+ srcbox.y0 = y + bwidget->pany;
+ srcbox.x1 = srcbox.x0 + width;
+ srcbox.y1 = srcbox.y0 + height - bwidget->pany;
+
+ dstbox.x0 = x;
+ dstbox.y0 = y;
+ dstbox.x1 = dstbox.x0 + width;
+ dstbox.y1 = dstbox.y0 + height - bwidget->pany;
+
+ /* move part that remains visible down */
+ nsfb_plot_copy(nsfb, &srcbox, nsfb, &dstbox);
+
+ /* redraw newly exposed area */
+ bwidget->scrolly += bwidget->pany;
+ fb_queue_redraw(widget, 0, height - bwidget->pany,
+ width, height);
+ }
+
+ if (bwidget->panx < 0) {
+ /* pan left by less then viewport width */
+ srcbox.x0 = x;
+ srcbox.y0 = y;
+ srcbox.x1 = srcbox.x0 + width + bwidget->panx;
+ srcbox.y1 = srcbox.y0 + height;
+
+ dstbox.x0 = x - bwidget->panx;
+ dstbox.y0 = y;
+ dstbox.x1 = dstbox.x0 + width + bwidget->panx;
+ dstbox.y1 = dstbox.y0 + height;
+
+ /* move part that remains visible left */
+ nsfb_plot_copy(nsfb, &srcbox, nsfb, &dstbox);
+
+ /* redraw newly exposed area */
+ bwidget->scrollx += bwidget->panx;
+ fb_queue_redraw(widget, 0, 0, -bwidget->panx, height);
+
+ } else if (bwidget->panx > 0) {
+ /* pan right by less then viewport width */
+ srcbox.x0 = x + bwidget->panx;
+ srcbox.y0 = y;
+ srcbox.x1 = srcbox.x0 + width - bwidget->panx;
+ srcbox.y1 = srcbox.y0 + height;
+
+ dstbox.x0 = x;
+ dstbox.y0 = y;
+ dstbox.x1 = dstbox.x0 + width - bwidget->panx;
+ dstbox.y1 = dstbox.y0 + height;
+
+ /* move part that remains visible right */
+ nsfb_plot_copy(nsfb, &srcbox, nsfb, &dstbox);
+
+ /* redraw newly exposed area */
+ bwidget->scrollx += bwidget->panx;
+ fb_queue_redraw(widget, width - bwidget->panx, 0,
+ width, height);
+ }
+
+ bwidget->pan_required = false;
+ bwidget->panx = 0;
+ bwidget->pany = 0;
+}
+
+static void
+fb_redraw(fbtk_widget_t *widget,
+ struct browser_widget_s *bwidget,
+ struct browser_window *bw)
+{
+ int x;
+ int y;
+ int caret_x, caret_y, caret_h;
+ struct rect clip;
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &fb_plotters
+ };
+ nsfb_t *nsfb = fbtk_get_nsfb(widget);
+ float scale = browser_window_get_scale(bw);
+
+ x = fbtk_get_absx(widget);
+ y = fbtk_get_absy(widget);
+
+ /* adjust clipping co-ordinates according to window location */
+ bwidget->redraw_box.y0 += y;
+ bwidget->redraw_box.y1 += y;
+ bwidget->redraw_box.x0 += x;
+ bwidget->redraw_box.x1 += x;
+
+ nsfb_claim(nsfb, &bwidget->redraw_box);
+
+ /* redraw bounding box is relative to window */
+ clip.x0 = bwidget->redraw_box.x0;
+ clip.y0 = bwidget->redraw_box.y0;
+ clip.x1 = bwidget->redraw_box.x1;
+ clip.y1 = bwidget->redraw_box.y1;
+
+ browser_window_redraw(bw,
+ (x - bwidget->scrollx) / scale,
+ (y - bwidget->scrolly) / scale,
+ &clip, &ctx);
+
+ if (fbtk_get_caret(widget, &caret_x, &caret_y, &caret_h)) {
+ /* This widget has caret, so render it */
+ nsfb_bbox_t line;
+ nsfb_plot_pen_t pen;
+
+ line.x0 = x - bwidget->scrollx + caret_x;
+ line.y0 = y - bwidget->scrolly + caret_y;
+ line.x1 = x - bwidget->scrollx + caret_x;
+ line.y1 = y - bwidget->scrolly + caret_y + caret_h;
+
+ pen.stroke_type = NFSB_PLOT_OPTYPE_SOLID;
+ pen.stroke_width = 1;
+ pen.stroke_colour = 0xFF0000FF;
+
+ nsfb_plot_line(nsfb, &line, &pen);
+ }
+
+ nsfb_update(fbtk_get_nsfb(widget), &bwidget->redraw_box);
+
+ bwidget->redraw_box.y0 = bwidget->redraw_box.x0 = INT_MAX;
+ bwidget->redraw_box.y1 = bwidget->redraw_box.x1 = INT_MIN;
+ bwidget->redraw_required = false;
+}
+
+static int
+fb_browser_window_redraw(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ struct gui_window *gw = cbi->context;
+ struct browser_widget_s *bwidget;
+
+ bwidget = fbtk_get_userpw(widget);
+ if (bwidget == NULL) {
+ LOG("browser widget from widget %p was null", widget);
+ return -1;
+ }
+
+ if (bwidget->pan_required) {
+ fb_pan(widget, bwidget, gw->bw);
+ }
+
+ if (bwidget->redraw_required) {
+ fb_redraw(widget, bwidget, gw->bw);
+ } else {
+ bwidget->redraw_box.x0 = 0;
+ bwidget->redraw_box.y0 = 0;
+ bwidget->redraw_box.x1 = fbtk_get_width(widget);
+ bwidget->redraw_box.y1 = fbtk_get_height(widget);
+ fb_redraw(widget, bwidget, gw->bw);
+ }
+ return 0;
+}
+
+static int fb_browser_window_destroy(fbtk_widget_t *widget,
+ fbtk_callback_info *cbi)
+{
+ struct browser_widget_s *browser_widget;
+
+ if (widget == NULL) {
+ return 0;
+ }
+
+ /* Free private data */
+ browser_widget = fbtk_get_userpw(widget);
+ free(browser_widget);
+
+ return 0;
+}
+
+
+static const char *fename;
+static int febpp;
+static int fewidth;
+static int feheight;
+static const char *feurl;
+
+static bool
+process_cmdline(int argc, char** argv)
+{
+ int opt;
+ int option_index;
+ static struct option long_options[] = {
+ {0, 0, 0, 0 }
+ }; /* no long options */
+
+ LOG("argc %d, argv %p", argc, argv);
+
+ fename = "sdl";
+ febpp = 32;
+
+ fewidth = nsoption_int(window_width);
+ if (fewidth <= 0) {
+ fewidth = 800;
+ }
+ feheight = nsoption_int(window_height);
+ if (feheight <= 0) {
+ feheight = 600;
+ }
+
+ if ((nsoption_charp(homepage_url) != NULL) &&
+ (nsoption_charp(homepage_url)[0] != '\0')) {
+ feurl = nsoption_charp(homepage_url);
+ } else {
+ feurl = NETSURF_HOMEPAGE;
+ }
+
+ while((opt = getopt_long(argc, argv, "f:b:w:h:",
+ long_options, &option_index)) != -1) {
+ switch (opt) {
+ case 'f':
+ fename = optarg;
+ break;
+
+ case 'b':
+ febpp = atoi(optarg);
+ break;
+
+ case 'w':
+ fewidth = atoi(optarg);
+ break;
+
+ case 'h':
+ feheight = atoi(optarg);
+ break;
+
+ default:
+ fprintf(stderr,
+ "Usage: %s [-f frontend] [-b bpp] url\n",
+ argv[0]);
+ return false;
+ }
+ }
+
+ if (optind < argc) {
+ feurl = argv[optind];
+ }
+
+ return true;
+}
+
+/**
+ * Set option defaults for framebuffer frontend
+ *
+ * @param defaults The option table to update.
+ * @return error status.
+ */
+static nserror set_defaults(struct nsoption_s *defaults)
+{
+ /* Set defaults for absent option strings */
+ nsoption_setnull_charp(cookie_file, strdup("~/.netsurf/Cookies"));
+ nsoption_setnull_charp(cookie_jar, strdup("~/.netsurf/Cookies"));
+
+ if (nsoption_charp(cookie_file) == NULL ||
+ nsoption_charp(cookie_jar) == NULL) {
+ LOG("Failed initialising cookie options");
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ /* set system colours for framebuffer ui */
+ nsoption_set_colour(sys_colour_ActiveBorder, 0x00000000);
+ nsoption_set_colour(sys_colour_ActiveCaption, 0x00ddddcc);
+ nsoption_set_colour(sys_colour_AppWorkspace, 0x00eeeeee);
+ nsoption_set_colour(sys_colour_Background, 0x00aa0000);
+ nsoption_set_colour(sys_colour_ButtonFace, 0x00dddddd);
+ nsoption_set_colour(sys_colour_ButtonHighlight, 0x00cccccc);
+ nsoption_set_colour(sys_colour_ButtonShadow, 0x00bbbbbb);
+ nsoption_set_colour(sys_colour_ButtonText, 0x00000000);
+ nsoption_set_colour(sys_colour_CaptionText, 0x00000000);
+ nsoption_set_colour(sys_colour_GrayText, 0x00777777);
+ nsoption_set_colour(sys_colour_Highlight, 0x00ee0000);
+ nsoption_set_colour(sys_colour_HighlightText, 0x00000000);
+ nsoption_set_colour(sys_colour_InactiveBorder, 0x00000000);
+ nsoption_set_colour(sys_colour_InactiveCaption, 0x00ffffff);
+ nsoption_set_colour(sys_colour_InactiveCaptionText, 0x00cccccc);
+ nsoption_set_colour(sys_colour_InfoBackground, 0x00aaaaaa);
+ nsoption_set_colour(sys_colour_InfoText, 0x00000000);
+ nsoption_set_colour(sys_colour_Menu, 0x00aaaaaa);
+ nsoption_set_colour(sys_colour_MenuText, 0x00000000);
+ nsoption_set_colour(sys_colour_Scrollbar, 0x00aaaaaa);
+ nsoption_set_colour(sys_colour_ThreeDDarkShadow, 0x00555555);
+ nsoption_set_colour(sys_colour_ThreeDFace, 0x00dddddd);
+ nsoption_set_colour(sys_colour_ThreeDHighlight, 0x00aaaaaa);
+ nsoption_set_colour(sys_colour_ThreeDLightShadow, 0x00999999);
+ nsoption_set_colour(sys_colour_ThreeDShadow, 0x00777777);
+ nsoption_set_colour(sys_colour_Window, 0x00aaaaaa);
+ nsoption_set_colour(sys_colour_WindowFrame, 0x00000000);
+ nsoption_set_colour(sys_colour_WindowText, 0x00000000);
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Ensures output logging stream is correctly configured
+ */
+static bool nslog_stream_configure(FILE *fptr)
+{
+ /* set log stream to be non-buffering */
+ setbuf(fptr, NULL);
+
+ return true;
+}
+
+static void framebuffer_run(void)
+{
+ nsfb_event_t event;
+ int timeout; /* timeout in miliseconds */
+
+ while (fb_complete != true) {
+ /* run the scheduler and discover how long to wait for
+ * the next event.
+ */
+ timeout = schedule_run();
+
+ /* if redraws are pending do not wait for event,
+ * return immediately
+ */
+ if (fbtk_get_redraw_pending(fbtk))
+ timeout = 0;
+
+ if (fbtk_event(fbtk, &event, timeout)) {
+ if ((event.type == NSFB_EVENT_CONTROL) &&
+ (event.value.controlcode == NSFB_CONTROL_QUIT))
+ fb_complete = true;
+ }
+
+ fbtk_redraw(fbtk);
+ }
+}
+
+static void gui_quit(void)
+{
+ LOG("gui_quit");
+
+ urldb_save_cookies(nsoption_charp(cookie_jar));
+
+ framebuffer_finalise();
+}
+
+/* called back when click in browser window */
+static int
+fb_browser_window_click(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ struct gui_window *gw = cbi->context;
+ struct browser_widget_s *bwidget = fbtk_get_userpw(widget);
+ browser_mouse_state mouse;
+ float scale = browser_window_get_scale(gw->bw);
+ int x = (cbi->x + bwidget->scrollx) / scale;
+ int y = (cbi->y + bwidget->scrolly) / scale;
+ uint64_t time_now;
+ static struct {
+ enum { CLICK_SINGLE, CLICK_DOUBLE, CLICK_TRIPLE } type;
+ uint64_t time;
+ } last_click;
+
+ if (cbi->event->type != NSFB_EVENT_KEY_DOWN &&
+ cbi->event->type != NSFB_EVENT_KEY_UP)
+ return 0;
+
+ LOG("browser window clicked at %d,%d", cbi->x, cbi->y);
+
+ switch (cbi->event->type) {
+ case NSFB_EVENT_KEY_DOWN:
+ switch (cbi->event->value.keycode) {
+ case NSFB_KEY_MOUSE_1:
+ browser_window_mouse_click(gw->bw,
+ BROWSER_MOUSE_PRESS_1, x, y);
+ gui_drag.state = GUI_DRAG_PRESSED;
+ gui_drag.button = 1;
+ gui_drag.x = x;
+ gui_drag.y = y;
+ break;
+
+ case NSFB_KEY_MOUSE_3:
+ browser_window_mouse_click(gw->bw,
+ BROWSER_MOUSE_PRESS_2, x, y);
+ gui_drag.state = GUI_DRAG_PRESSED;
+ gui_drag.button = 2;
+ gui_drag.x = x;
+ gui_drag.y = y;
+ break;
+
+ case NSFB_KEY_MOUSE_4:
+ /* scroll up */
+ if (browser_window_scroll_at_point(gw->bw, x, y,
+ 0, -100) == false)
+ widget_scroll_y(gw, -100, false);
+ break;
+
+ case NSFB_KEY_MOUSE_5:
+ /* scroll down */
+ if (browser_window_scroll_at_point(gw->bw, x, y,
+ 0, 100) == false)
+ widget_scroll_y(gw, 100, false);
+ break;
+
+ default:
+ break;
+
+ }
+
+ break;
+ case NSFB_EVENT_KEY_UP:
+
+ mouse = 0;
+ nsu_getmonotonic_ms(&time_now);
+
+ switch (cbi->event->value.keycode) {
+ case NSFB_KEY_MOUSE_1:
+ if (gui_drag.state == GUI_DRAG_DRAG) {
+ /* End of a drag, rather than click */
+
+ if (gui_drag.grabbed_pointer) {
+ /* need to ungrab pointer */
+ fbtk_tgrab_pointer(widget);
+ gui_drag.grabbed_pointer = false;
+ }
+
+ gui_drag.state = GUI_DRAG_NONE;
+
+ /* Tell core */
+ browser_window_mouse_track(gw->bw, 0, x, y);
+ break;
+ }
+ /* This is a click;
+ * clear PRESSED state and pass to core */
+ gui_drag.state = GUI_DRAG_NONE;
+ mouse = BROWSER_MOUSE_CLICK_1;
+ break;
+
+ case NSFB_KEY_MOUSE_3:
+ if (gui_drag.state == GUI_DRAG_DRAG) {
+ /* End of a drag, rather than click */
+ gui_drag.state = GUI_DRAG_NONE;
+
+ if (gui_drag.grabbed_pointer) {
+ /* need to ungrab pointer */
+ fbtk_tgrab_pointer(widget);
+ gui_drag.grabbed_pointer = false;
+ }
+
+ /* Tell core */
+ browser_window_mouse_track(gw->bw, 0, x, y);
+ break;
+ }
+ /* This is a click;
+ * clear PRESSED state and pass to core */
+ gui_drag.state = GUI_DRAG_NONE;
+ mouse = BROWSER_MOUSE_CLICK_2;
+ break;
+
+ default:
+ break;
+
+ }
+
+ /* Determine if it's a double or triple click, allowing
+ * 0.5 seconds (500ms) between clicks
+ */
+ if ((time_now < (last_click.time + 500)) &&
+ (cbi->event->value.keycode != NSFB_KEY_MOUSE_4) &&
+ (cbi->event->value.keycode != NSFB_KEY_MOUSE_5)) {
+ if (last_click.type == CLICK_SINGLE) {
+ /* Set double click */
+ mouse |= BROWSER_MOUSE_DOUBLE_CLICK;
+ last_click.type = CLICK_DOUBLE;
+
+ } else if (last_click.type == CLICK_DOUBLE) {
+ /* Set triple click */
+ mouse |= BROWSER_MOUSE_TRIPLE_CLICK;
+ last_click.type = CLICK_TRIPLE;
+ } else {
+ /* Set normal click */
+ last_click.type = CLICK_SINGLE;
+ }
+ } else {
+ last_click.type = CLICK_SINGLE;
+ }
+
+ if (mouse) {
+ browser_window_mouse_click(gw->bw, mouse, x, y);
+ }
+
+ last_click.time = time_now;
+
+ break;
+ default:
+ break;
+
+ }
+ return 1;
+}
+
+/* called back when movement in browser window */
+static int
+fb_browser_window_move(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ browser_mouse_state mouse = 0;
+ struct gui_window *gw = cbi->context;
+ struct browser_widget_s *bwidget = fbtk_get_userpw(widget);
+ float scale = browser_window_get_scale(gw->bw);
+ int x = (cbi->x + bwidget->scrollx) / scale;
+ int y = (cbi->y + bwidget->scrolly) / scale;
+
+ if (gui_drag.state == GUI_DRAG_PRESSED &&
+ (abs(x - gui_drag.x) > 5 ||
+ abs(y - gui_drag.y) > 5)) {
+ /* Drag started */
+ if (gui_drag.button == 1) {
+ browser_window_mouse_click(gw->bw,
+ BROWSER_MOUSE_DRAG_1,
+ gui_drag.x, gui_drag.y);
+ } else {
+ browser_window_mouse_click(gw->bw,
+ BROWSER_MOUSE_DRAG_2,
+ gui_drag.x, gui_drag.y);
+ }
+ gui_drag.grabbed_pointer = fbtk_tgrab_pointer(widget);
+ gui_drag.state = GUI_DRAG_DRAG;
+ }
+
+ if (gui_drag.state == GUI_DRAG_DRAG) {
+ /* set up mouse state */
+ mouse |= BROWSER_MOUSE_DRAG_ON;
+
+ if (gui_drag.button == 1)
+ mouse |= BROWSER_MOUSE_HOLDING_1;
+ else
+ mouse |= BROWSER_MOUSE_HOLDING_2;
+ }
+
+ browser_window_mouse_track(gw->bw, mouse, x, y);
+
+ return 0;
+}
+
+
+static int
+fb_browser_window_input(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ struct gui_window *gw = cbi->context;
+ static fbtk_modifier_type modifier = FBTK_MOD_CLEAR;
+ int ucs4 = -1;
+
+ LOG("got value %d", cbi->event->value.keycode);
+
+ switch (cbi->event->type) {
+ case NSFB_EVENT_KEY_DOWN:
+ switch (cbi->event->value.keycode) {
+
+ case NSFB_KEY_DELETE:
+ browser_window_key_press(gw->bw, NS_KEY_DELETE_RIGHT);
+ break;
+
+ case NSFB_KEY_PAGEUP:
+ if (browser_window_key_press(gw->bw,
+ NS_KEY_PAGE_UP) == false)
+ widget_scroll_y(gw, -fbtk_get_height(
+ gw->browser), false);
+ break;
+
+ case NSFB_KEY_PAGEDOWN:
+ if (browser_window_key_press(gw->bw,
+ NS_KEY_PAGE_DOWN) == false)
+ widget_scroll_y(gw, fbtk_get_height(
+ gw->browser), false);
+ break;
+
+ case NSFB_KEY_RIGHT:
+ if (modifier & FBTK_MOD_RCTRL ||
+ modifier & FBTK_MOD_LCTRL) {
+ /* CTRL held */
+ if (browser_window_key_press(gw->bw,
+ NS_KEY_LINE_END) == false)
+ widget_scroll_x(gw, INT_MAX, true);
+
+ } else if (modifier & FBTK_MOD_RSHIFT ||
+ modifier & FBTK_MOD_LSHIFT) {
+ /* SHIFT held */
+ if (browser_window_key_press(gw->bw,
+ NS_KEY_WORD_RIGHT) == false)
+ widget_scroll_x(gw, fbtk_get_width(
+ gw->browser), false);
+
+ } else {
+ /* no modifier */
+ if (browser_window_key_press(gw->bw,
+ NS_KEY_RIGHT) == false)
+ widget_scroll_x(gw, 100, false);
+ }
+ break;
+
+ case NSFB_KEY_LEFT:
+ if (modifier & FBTK_MOD_RCTRL ||
+ modifier & FBTK_MOD_LCTRL) {
+ /* CTRL held */
+ if (browser_window_key_press(gw->bw,
+ NS_KEY_LINE_START) == false)
+ widget_scroll_x(gw, 0, true);
+
+ } else if (modifier & FBTK_MOD_RSHIFT ||
+ modifier & FBTK_MOD_LSHIFT) {
+ /* SHIFT held */
+ if (browser_window_key_press(gw->bw,
+ NS_KEY_WORD_LEFT) == false)
+ widget_scroll_x(gw, -fbtk_get_width(
+ gw->browser), false);
+
+ } else {
+ /* no modifier */
+ if (browser_window_key_press(gw->bw,
+ NS_KEY_LEFT) == false)
+ widget_scroll_x(gw, -100, false);
+ }
+ break;
+
+ case NSFB_KEY_UP:
+ if (browser_window_key_press(gw->bw,
+ NS_KEY_UP) == false)
+ widget_scroll_y(gw, -100, false);
+ break;
+
+ case NSFB_KEY_DOWN:
+ if (browser_window_key_press(gw->bw,
+ NS_KEY_DOWN) == false)
+ widget_scroll_y(gw, 100, false);
+ break;
+
+ case NSFB_KEY_RSHIFT:
+ modifier |= FBTK_MOD_RSHIFT;
+ break;
+
+ case NSFB_KEY_LSHIFT:
+ modifier |= FBTK_MOD_LSHIFT;
+ break;
+
+ case NSFB_KEY_RCTRL:
+ modifier |= FBTK_MOD_RCTRL;
+ break;
+
+ case NSFB_KEY_LCTRL:
+ modifier |= FBTK_MOD_LCTRL;
+ break;
+
+ case NSFB_KEY_y:
+ case NSFB_KEY_z:
+ if (cbi->event->value.keycode == NSFB_KEY_z &&
+ (modifier & FBTK_MOD_RCTRL ||
+ modifier & FBTK_MOD_LCTRL) &&
+ (modifier & FBTK_MOD_RSHIFT ||
+ modifier & FBTK_MOD_LSHIFT)) {
+ /* Z pressed with CTRL and SHIFT held */
+ browser_window_key_press(gw->bw, NS_KEY_REDO);
+ break;
+
+ } else if (cbi->event->value.keycode == NSFB_KEY_z &&
+ (modifier & FBTK_MOD_RCTRL ||
+ modifier & FBTK_MOD_LCTRL)) {
+ /* Z pressed with CTRL held */
+ browser_window_key_press(gw->bw, NS_KEY_UNDO);
+ break;
+
+ } else if (cbi->event->value.keycode == NSFB_KEY_y &&
+ (modifier & FBTK_MOD_RCTRL ||
+ modifier & FBTK_MOD_LCTRL)) {
+ /* Y pressed with CTRL held */
+ browser_window_key_press(gw->bw, NS_KEY_REDO);
+ break;
+ }
+ /* Z or Y pressed but not undo or redo;
+ * Fall through to default handling */
+
+ default:
+ ucs4 = fbtk_keycode_to_ucs4(cbi->event->value.keycode,
+ modifier);
+ if (ucs4 != -1)
+ browser_window_key_press(gw->bw, ucs4);
+ break;
+ }
+ break;
+
+ case NSFB_EVENT_KEY_UP:
+ switch (cbi->event->value.keycode) {
+ case NSFB_KEY_RSHIFT:
+ modifier &= ~FBTK_MOD_RSHIFT;
+ break;
+
+ case NSFB_KEY_LSHIFT:
+ modifier &= ~FBTK_MOD_LSHIFT;
+ break;
+
+ case NSFB_KEY_RCTRL:
+ modifier &= ~FBTK_MOD_RCTRL;
+ break;
+
+ case NSFB_KEY_LCTRL:
+ modifier &= ~FBTK_MOD_LCTRL;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void
+fb_update_back_forward(struct gui_window *gw)
+{
+ struct browser_window *bw = gw->bw;
+
+ fbtk_set_bitmap(gw->back,
+ (browser_window_back_available(bw)) ?
+ &left_arrow : &left_arrow_g);
+ fbtk_set_bitmap(gw->forward,
+ (browser_window_forward_available(bw)) ?
+ &right_arrow : &right_arrow_g);
+}
+
+/* left icon click routine */
+static int
+fb_leftarrow_click(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ struct gui_window *gw = cbi->context;
+ struct browser_window *bw = gw->bw;
+
+ if (cbi->event->type != NSFB_EVENT_KEY_UP)
+ return 0;
+
+ if (browser_window_back_available(bw))
+ browser_window_history_back(bw, false);
+
+ fb_update_back_forward(gw);
+
+ return 1;
+}
+
+/* right arrow icon click routine */
+static int
+fb_rightarrow_click(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ struct gui_window *gw = cbi->context;
+ struct browser_window *bw = gw->bw;
+
+ if (cbi->event->type != NSFB_EVENT_KEY_UP)
+ return 0;
+
+ if (browser_window_forward_available(bw))
+ browser_window_history_forward(bw, false);
+
+ fb_update_back_forward(gw);
+ return 1;
+
+}
+
+/* reload icon click routine */
+static int
+fb_reload_click(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ struct browser_window *bw = cbi->context;
+
+ if (cbi->event->type != NSFB_EVENT_KEY_UP)
+ return 0;
+
+ browser_window_reload(bw, true);
+ return 1;
+}
+
+/* stop icon click routine */
+static int
+fb_stop_click(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ struct browser_window *bw = cbi->context;
+
+ if (cbi->event->type != NSFB_EVENT_KEY_UP)
+ return 0;
+
+ browser_window_stop(bw);
+ return 0;
+}
+
+static int
+fb_osk_click(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+
+ if (cbi->event->type != NSFB_EVENT_KEY_UP)
+ return 0;
+
+ map_osk();
+
+ return 0;
+}
+
+/* close browser window icon click routine */
+static int
+fb_close_click(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ if (cbi->event->type != NSFB_EVENT_KEY_UP)
+ return 0;
+
+ fb_complete = true;
+
+ return 0;
+}
+
+static int
+fb_scroll_callback(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ struct gui_window *gw = cbi->context;
+
+ switch (cbi->type) {
+ case FBTK_CBT_SCROLLY:
+ widget_scroll_y(gw, cbi->y, true);
+ break;
+
+ case FBTK_CBT_SCROLLX:
+ widget_scroll_x(gw, cbi->x, true);
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int
+fb_url_enter(void *pw, char *text)
+{
+ struct browser_window *bw = pw;
+ nsurl *url;
+ nserror error;
+
+ error = nsurl_create(text, &url);
+ if (error != NSERROR_OK) {
+ fb_warn_user(messages_get_errorcode(error), 0);
+ } else {
+ browser_window_navigate(bw, url, NULL, BW_NAVIGATE_HISTORY,
+ NULL, NULL, NULL);
+ nsurl_unref(url);
+ }
+
+ return 0;
+}
+
+static int
+fb_url_move(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ framebuffer_set_cursor(&caret_image);
+ return 0;
+}
+
+static int
+set_ptr_default_move(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ framebuffer_set_cursor(&pointer_image);
+ return 0;
+}
+
+static int
+fb_localhistory_btn_clik(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ struct gui_window *gw = cbi->context;
+
+ if (cbi->event->type != NSFB_EVENT_KEY_UP)
+ return 0;
+
+ fb_localhistory_map(gw->localhistory);
+
+ return 0;
+}
+
+
+/** Create a toolbar window and populate it with buttons.
+ *
+ * The toolbar layout uses a character to define buttons type and position:
+ * b - back
+ * l - local history
+ * f - forward
+ * s - stop
+ * r - refresh
+ * u - url bar expands to fit remaining space
+ * t - throbber/activity indicator
+ * c - close the current window
+ *
+ * The default layout is "blfsrut" there should be no more than a
+ * single url bar entry or behaviour will be undefined.
+ *
+ * @param gw Parent window
+ * @param toolbar_height The height in pixels of the toolbar
+ * @param padding The padding in pixels round each element of the toolbar
+ * @param frame_col Frame colour.
+ * @param toolbar_layout A string defining which buttons and controls
+ * should be added to the toolbar. May be empty
+ * string to disable the bar..
+ *
+ */
+static fbtk_widget_t *
+create_toolbar(struct gui_window *gw,
+ int toolbar_height,
+ int padding,
+ colour frame_col,
+ const char *toolbar_layout)
+{
+ fbtk_widget_t *toolbar;
+ fbtk_widget_t *widget;
+
+ int xpos; /* The position of the next widget. */
+ int xlhs = 0; /* extent of the left hand side widgets */
+ int xdir = 1; /* the direction of movement + or - 1 */
+ const char *itmtype; /* type of the next item */
+
+ if (toolbar_layout == NULL) {
+ toolbar_layout = NSFB_TOOLBAR_DEFAULT_LAYOUT;
+ }
+
+ LOG("Using toolbar layout %s", toolbar_layout);
+
+ itmtype = toolbar_layout;
+
+ /* check for the toolbar being disabled */
+ if ((*itmtype == 0) || (*itmtype == 'q')) {
+ return NULL;
+ }
+
+ toolbar = fbtk_create_window(gw->window, 0, 0, 0,
+ toolbar_height,
+ frame_col);
+
+ if (toolbar == NULL) {
+ return NULL;
+ }
+
+ fbtk_set_handler(toolbar,
+ FBTK_CBT_POINTERENTER,
+ set_ptr_default_move,
+ NULL);
+
+
+ xpos = padding;
+
+ /* loop proceeds creating widget on the left hand side until
+ * it runs out of layout or encounters a url bar declaration
+ * wherupon it works backwards from the end of the layout
+ * untill the space left is for the url bar
+ */
+ while ((itmtype >= toolbar_layout) &&
+ (*itmtype != 0) &&
+ (xdir !=0)) {
+
+ LOG("toolbar adding %c", *itmtype);
+
+
+ switch (*itmtype) {
+
+ case 'b': /* back */
+ widget = fbtk_create_button(toolbar,
+ (xdir == 1) ? xpos :
+ xpos - left_arrow.width,
+ padding,
+ left_arrow.width,
+ -padding,
+ frame_col,
+ &left_arrow,
+ fb_leftarrow_click,
+ gw);
+ gw->back = widget; /* keep reference */
+ break;
+
+ case 'l': /* local history */
+ widget = fbtk_create_button(toolbar,
+ (xdir == 1) ? xpos :
+ xpos - history_image.width,
+ padding,
+ history_image.width,
+ -padding,
+ frame_col,
+ &history_image,
+ fb_localhistory_btn_clik,
+ gw);
+ gw->history = widget;
+ break;
+
+ case 'f': /* forward */
+ widget = fbtk_create_button(toolbar,
+ (xdir == 1)?xpos :
+ xpos - right_arrow.width,
+ padding,
+ right_arrow.width,
+ -padding,
+ frame_col,
+ &right_arrow,
+ fb_rightarrow_click,
+ gw);
+ gw->forward = widget;
+ break;
+
+ case 'c': /* close the current window */
+ widget = fbtk_create_button(toolbar,
+ (xdir == 1)?xpos :
+ xpos - stop_image_g.width,
+ padding,
+ stop_image_g.width,
+ -padding,
+ frame_col,
+ &stop_image_g,
+ fb_close_click,
+ gw->bw);
+ gw->close = widget;
+ break;
+
+ case 's': /* stop */
+ widget = fbtk_create_button(toolbar,
+ (xdir == 1)?xpos :
+ xpos - stop_image.width,
+ padding,
+ stop_image.width,
+ -padding,
+ frame_col,
+ &stop_image,
+ fb_stop_click,
+ gw->bw);
+ gw->stop = widget;
+ break;
+
+ case 'r': /* reload */
+ widget = fbtk_create_button(toolbar,
+ (xdir == 1)?xpos :
+ xpos - reload.width,
+ padding,
+ reload.width,
+ -padding,
+ frame_col,
+ &reload,
+ fb_reload_click,
+ gw->bw);
+ gw->reload = widget;
+ break;
+
+ case 't': /* throbber/activity indicator */
+ widget = fbtk_create_bitmap(toolbar,
+ (xdir == 1)?xpos :
+ xpos - throbber0.width,
+ padding,
+ throbber0.width,
+ -padding,
+ frame_col,
+ &throbber0);
+ gw->throbber = widget;
+ break;
+
+
+ case 'u': /* url bar*/
+ if (xdir == -1) {
+ /* met the u going backwards add url
+ * now we know available extent
+ */
+
+ widget = fbtk_create_writable_text(toolbar,
+ xlhs,
+ padding,
+ xpos - xlhs,
+ -padding,
+ FB_COLOUR_WHITE,
+ FB_COLOUR_BLACK,
+ true,
+ fb_url_enter,
+ gw->bw);
+
+ fbtk_set_handler(widget,
+ FBTK_CBT_POINTERENTER,
+ fb_url_move, gw->bw);
+
+ gw->url = widget; /* keep reference */
+
+ /* toolbar is complete */
+ xdir = 0;
+ break;
+ }
+ /* met url going forwards, note position and
+ * reverse direction
+ */
+ itmtype = toolbar_layout + strlen(toolbar_layout);
+ xdir = -1;
+ xlhs = xpos;
+ xpos = (2 * fbtk_get_width(toolbar));
+ widget = toolbar;
+ break;
+
+ default:
+ widget = NULL;
+ xdir = 0;
+ LOG("Unknown element %c in toolbar layout", *itmtype);
+ break;
+
+ }
+
+ if (widget != NULL) {
+ xpos += (xdir * (fbtk_get_width(widget) + padding));
+ }
+
+ LOG("xpos is %d", xpos);
+
+ itmtype += xdir;
+ }
+
+ fbtk_set_mapping(toolbar, true);
+
+ return toolbar;
+}
+
+
+/** Resize a toolbar.
+ *
+ * @param gw Parent window
+ * @param toolbar_height The height in pixels of the toolbar
+ * @param padding The padding in pixels round each element of the toolbar
+ * @param toolbar_layout A string defining which buttons and controls
+ * should be added to the toolbar. May be empty
+ * string to disable the bar.
+ */
+static void
+resize_toolbar(struct gui_window *gw,
+ int toolbar_height,
+ int padding,
+ const char *toolbar_layout)
+{
+ fbtk_widget_t *widget;
+
+ int xpos; /* The position of the next widget. */
+ int xlhs = 0; /* extent of the left hand side widgets */
+ int xdir = 1; /* the direction of movement + or - 1 */
+ const char *itmtype; /* type of the next item */
+ int x = 0, y = 0, w = 0, h = 0;
+
+ if (gw->toolbar == NULL) {
+ return;
+ }
+
+ if (toolbar_layout == NULL) {
+ toolbar_layout = NSFB_TOOLBAR_DEFAULT_LAYOUT;
+ }
+
+ itmtype = toolbar_layout;
+
+ if (*itmtype == 0) {
+ return;
+ }
+
+ fbtk_set_pos_and_size(gw->toolbar, 0, 0, 0, toolbar_height);
+
+ xpos = padding;
+
+ /* loop proceeds creating widget on the left hand side until
+ * it runs out of layout or encounters a url bar declaration
+ * wherupon it works backwards from the end of the layout
+ * untill the space left is for the url bar
+ */
+ while (itmtype >= toolbar_layout && xdir != 0) {
+
+ switch (*itmtype) {
+ case 'b': /* back */
+ widget = gw->back;
+ x = (xdir == 1) ? xpos : xpos - left_arrow.width;
+ y = padding;
+ w = left_arrow.width;
+ h = -padding;
+ break;
+
+ case 'l': /* local history */
+ widget = gw->history;
+ x = (xdir == 1) ? xpos : xpos - history_image.width;
+ y = padding;
+ w = history_image.width;
+ h = -padding;
+ break;
+
+ case 'f': /* forward */
+ widget = gw->forward;
+ x = (xdir == 1) ? xpos : xpos - right_arrow.width;
+ y = padding;
+ w = right_arrow.width;
+ h = -padding;
+ break;
+
+ case 'c': /* close the current window */
+ widget = gw->close;
+ x = (xdir == 1) ? xpos : xpos - stop_image_g.width;
+ y = padding;
+ w = stop_image_g.width;
+ h = -padding;
+ break;
+
+ case 's': /* stop */
+ widget = gw->stop;
+ x = (xdir == 1) ? xpos : xpos - stop_image.width;
+ y = padding;
+ w = stop_image.width;
+ h = -padding;
+ break;
+
+ case 'r': /* reload */
+ widget = gw->reload;
+ x = (xdir == 1) ? xpos : xpos - reload.width;
+ y = padding;
+ w = reload.width;
+ h = -padding;
+ break;
+
+ case 't': /* throbber/activity indicator */
+ widget = gw->throbber;
+ x = (xdir == 1) ? xpos : xpos - throbber0.width;
+ y = padding;
+ w = throbber0.width;
+ h = -padding;
+ break;
+
+
+ case 'u': /* url bar*/
+ if (xdir == -1) {
+ /* met the u going backwards add url
+ * now we know available extent
+ */
+ widget = gw->url;
+ x = xlhs;
+ y = padding;
+ w = xpos - xlhs;
+ h = -padding;
+
+ /* toolbar is complete */
+ xdir = 0;
+ break;
+ }
+ /* met url going forwards, note position and
+ * reverse direction
+ */
+ itmtype = toolbar_layout + strlen(toolbar_layout);
+ xdir = -1;
+ xlhs = xpos;
+ w = fbtk_get_width(gw->toolbar);
+ xpos = 2 * w;
+ widget = gw->toolbar;
+ break;
+
+ default:
+ widget = NULL;
+ break;
+
+ }
+
+ if (widget != NULL) {
+ if (widget != gw->toolbar)
+ fbtk_set_pos_and_size(widget, x, y, w, h);
+ xpos += xdir * (w + padding);
+ }
+
+ itmtype += xdir;
+ }
+}
+
+/** Routine called when "stripped of focus" event occours for browser widget.
+ *
+ * @param widget The widget reciving "stripped of focus" event.
+ * @param cbi The callback parameters.
+ * @return The callback result.
+ */
+static int
+fb_browser_window_strip_focus(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ fbtk_set_caret(widget, false, 0, 0, 0, NULL);
+
+ return 0;
+}
+
+static void
+create_browser_widget(struct gui_window *gw, int toolbar_height, int furniture_width)
+{
+ struct browser_widget_s *browser_widget;
+ browser_widget = calloc(1, sizeof(struct browser_widget_s));
+
+ gw->browser = fbtk_create_user(gw->window,
+ 0,
+ toolbar_height,
+ -furniture_width,
+ -furniture_width,
+ browser_widget);
+
+ fbtk_set_handler(gw->browser, FBTK_CBT_REDRAW, fb_browser_window_redraw, gw);
+ fbtk_set_handler(gw->browser, FBTK_CBT_DESTROY, fb_browser_window_destroy, gw);
+ fbtk_set_handler(gw->browser, FBTK_CBT_INPUT, fb_browser_window_input, gw);
+ fbtk_set_handler(gw->browser, FBTK_CBT_CLICK, fb_browser_window_click, gw);
+ fbtk_set_handler(gw->browser, FBTK_CBT_STRIP_FOCUS, fb_browser_window_strip_focus, gw);
+ fbtk_set_handler(gw->browser, FBTK_CBT_POINTERMOVE, fb_browser_window_move, gw);
+}
+
+static void
+resize_browser_widget(struct gui_window *gw, int x, int y,
+ int width, int height)
+{
+ fbtk_set_pos_and_size(gw->browser, x, y, width, height);
+ browser_window_reformat(gw->bw, false, width, height);
+}
+
+static void
+create_normal_browser_window(struct gui_window *gw, int furniture_width)
+{
+ fbtk_widget_t *widget;
+ fbtk_widget_t *toolbar;
+ int statusbar_width = 0;
+ int toolbar_height = nsoption_int(fb_toolbar_size);
+
+ LOG("Normal window");
+
+ gw->window = fbtk_create_window(fbtk, 0, 0, 0, 0, 0);
+
+ statusbar_width = nsoption_int(toolbar_status_size) *
+ fbtk_get_width(gw->window) / 10000;
+
+ /* toolbar */
+ toolbar = create_toolbar(gw,
+ toolbar_height,
+ 2,
+ FB_FRAME_COLOUR,
+ nsoption_charp(fb_toolbar_layout));
+ gw->toolbar = toolbar;
+
+ /* set the actually created toolbar height */
+ if (toolbar != NULL) {
+ toolbar_height = fbtk_get_height(toolbar);
+ } else {
+ toolbar_height = 0;
+ }
+
+ /* status bar */
+ gw->status = fbtk_create_text(gw->window,
+ 0,
+ fbtk_get_height(gw->window) - furniture_width,
+ statusbar_width, furniture_width,
+ FB_FRAME_COLOUR, FB_COLOUR_BLACK,
+ false);
+ fbtk_set_handler(gw->status, FBTK_CBT_POINTERENTER, set_ptr_default_move, NULL);
+
+ LOG("status bar %p at %d,%d", gw->status, fbtk_get_absx(gw->status), fbtk_get_absy(gw->status));
+
+ /* create horizontal scrollbar */
+ gw->hscroll = fbtk_create_hscroll(gw->window,
+ statusbar_width,
+ fbtk_get_height(gw->window) - furniture_width,
+ fbtk_get_width(gw->window) - statusbar_width - furniture_width,
+ furniture_width,
+ FB_SCROLL_COLOUR,
+ FB_FRAME_COLOUR,
+ fb_scroll_callback,
+ gw);
+
+ /* fill bottom right area */
+
+ if (nsoption_bool(fb_osk) == true) {
+ widget = fbtk_create_text_button(gw->window,
+ fbtk_get_width(gw->window) - furniture_width,
+ fbtk_get_height(gw->window) - furniture_width,
+ furniture_width,
+ furniture_width,
+ FB_FRAME_COLOUR, FB_COLOUR_BLACK,
+ fb_osk_click,
+ NULL);
+ widget = fbtk_create_button(gw->window,
+ fbtk_get_width(gw->window) - furniture_width,
+ fbtk_get_height(gw->window) - furniture_width,
+ furniture_width,
+ furniture_width,
+ FB_FRAME_COLOUR,
+ &osk_image,
+ fb_osk_click,
+ NULL);
+ } else {
+ widget = fbtk_create_fill(gw->window,
+ fbtk_get_width(gw->window) - furniture_width,
+ fbtk_get_height(gw->window) - furniture_width,
+ furniture_width,
+ furniture_width,
+ FB_FRAME_COLOUR);
+
+ fbtk_set_handler(widget, FBTK_CBT_POINTERENTER, set_ptr_default_move, NULL);
+ }
+
+ gw->bottom_right = widget;
+
+ /* create vertical scrollbar */
+ gw->vscroll = fbtk_create_vscroll(gw->window,
+ fbtk_get_width(gw->window) - furniture_width,
+ toolbar_height,
+ furniture_width,
+ fbtk_get_height(gw->window) - toolbar_height - furniture_width,
+ FB_SCROLL_COLOUR,
+ FB_FRAME_COLOUR,
+ fb_scroll_callback,
+ gw);
+
+ /* browser widget */
+ create_browser_widget(gw, toolbar_height, nsoption_int(fb_furniture_size));
+
+ /* Give browser_window's user widget input focus */
+ fbtk_set_focus(gw->browser);
+}
+
+static void
+resize_normal_browser_window(struct gui_window *gw, int furniture_width)
+{
+ bool resized;
+ int width, height;
+ int statusbar_width;
+ int toolbar_height = fbtk_get_height(gw->toolbar);
+
+ /* Resize the main window widget */
+ resized = fbtk_set_pos_and_size(gw->window, 0, 0, 0, 0);
+ if (!resized)
+ return;
+
+ width = fbtk_get_width(gw->window);
+ height = fbtk_get_height(gw->window);
+ statusbar_width = nsoption_int(toolbar_status_size) * width / 10000;
+
+ resize_toolbar(gw, toolbar_height, 2,
+ nsoption_charp(fb_toolbar_layout));
+ fbtk_set_pos_and_size(gw->status,
+ 0, height - furniture_width,
+ statusbar_width, furniture_width);
+ fbtk_reposition_hscroll(gw->hscroll,
+ statusbar_width, height - furniture_width,
+ width - statusbar_width - furniture_width,
+ furniture_width);
+ fbtk_set_pos_and_size(gw->bottom_right,
+ width - furniture_width, height - furniture_width,
+ furniture_width, furniture_width);
+ fbtk_reposition_vscroll(gw->vscroll,
+ width - furniture_width,
+ toolbar_height, furniture_width,
+ height - toolbar_height - furniture_width);
+ resize_browser_widget(gw,
+ 0, toolbar_height,
+ width - furniture_width,
+ height - furniture_width - toolbar_height);
+}
+
+static void gui_window_add_to_window_list(struct gui_window *gw)
+{
+ gw->next = NULL;
+ gw->prev = NULL;
+
+ if (window_list == NULL) {
+ window_list = gw;
+ } else {
+ window_list->prev = gw;
+ gw->next = window_list;
+ window_list = gw;
+ }
+}
+
+static void gui_window_remove_from_window_list(struct gui_window *gw)
+{
+ struct gui_window *list;
+
+ for (list = window_list; list != NULL; list = list->next) {
+ if (list != gw)
+ continue;
+
+ if (list == window_list) {
+ window_list = list->next;
+ if (window_list != NULL)
+ window_list->prev = NULL;
+ } else {
+ list->prev->next = list->next;
+ if (list->next != NULL) {
+ list->next->prev = list->prev;
+ }
+ }
+ break;
+ }
+}
+
+
+static struct gui_window *
+gui_window_create(struct browser_window *bw,
+ struct gui_window *existing,
+ gui_window_create_flags flags)
+{
+ struct gui_window *gw;
+
+ gw = calloc(1, sizeof(struct gui_window));
+
+ if (gw == NULL)
+ return NULL;
+
+ /* associate the gui window with the underlying browser window
+ */
+ gw->bw = bw;
+
+ create_normal_browser_window(gw, nsoption_int(fb_furniture_size));
+ gw->localhistory = fb_create_localhistory(bw, fbtk, nsoption_int(fb_furniture_size));
+
+ /* map and request redraw of gui window */
+ fbtk_set_mapping(gw->window, true);
+
+ /* Add it to the window list */
+ gui_window_add_to_window_list(gw);
+
+ return gw;
+}
+
+static void
+gui_window_destroy(struct gui_window *gw)
+{
+ gui_window_remove_from_window_list(gw);
+
+ fbtk_destroy_widget(gw->window);
+
+ free(gw);
+}
+
+static void
+gui_window_redraw_window(struct gui_window *g)
+{
+ fb_queue_redraw(g->browser, 0, 0, fbtk_get_width(g->browser), fbtk_get_height(g->browser) );
+}
+
+static void
+gui_window_update_box(struct gui_window *g, const struct rect *rect)
+{
+ struct browser_widget_s *bwidget = fbtk_get_userpw(g->browser);
+ fb_queue_redraw(g->browser,
+ rect->x0 - bwidget->scrollx,
+ rect->y0 - bwidget->scrolly,
+ rect->x1 - bwidget->scrollx,
+ rect->y1 - bwidget->scrolly);
+}
+
+static bool
+gui_window_get_scroll(struct gui_window *g, int *sx, int *sy)
+{
+ struct browser_widget_s *bwidget = fbtk_get_userpw(g->browser);
+ float scale = browser_window_get_scale(g->bw);
+
+ *sx = bwidget->scrollx / scale;
+ *sy = bwidget->scrolly / scale;
+
+ return true;
+}
+
+static void
+gui_window_set_scroll(struct gui_window *gw, int sx, int sy)
+{
+ struct browser_widget_s *bwidget = fbtk_get_userpw(gw->browser);
+ float scale = browser_window_get_scale(gw->bw);
+
+ assert(bwidget);
+
+ widget_scroll_x(gw, sx * scale, true);
+ widget_scroll_y(gw, sy * scale, true);
+}
+
+
+static void
+gui_window_get_dimensions(struct gui_window *g,
+ int *width,
+ int *height,
+ bool scaled)
+{
+ float scale = browser_window_get_scale(g->bw);
+
+ *width = fbtk_get_width(g->browser);
+ *height = fbtk_get_height(g->browser);
+
+ if (scaled) {
+ *width /= scale;
+ *height /= scale;
+ }
+}
+
+static void
+gui_window_update_extent(struct gui_window *gw)
+{
+ int w, h;
+ browser_window_get_extents(gw->bw, true, &w, &h);
+
+ fbtk_set_scroll_parameters(gw->hscroll, 0, w,
+ fbtk_get_width(gw->browser), 100);
+
+ fbtk_set_scroll_parameters(gw->vscroll, 0, h,
+ fbtk_get_height(gw->browser), 100);
+}
+
+static void
+gui_window_set_status(struct gui_window *g, const char *text)
+{
+ fbtk_set_text(g->status, text);
+}
+
+static void
+gui_window_set_pointer(struct gui_window *g, gui_pointer_shape shape)
+{
+ switch (shape) {
+ case GUI_POINTER_POINT:
+ framebuffer_set_cursor(&hand_image);
+ break;
+
+ case GUI_POINTER_CARET:
+ framebuffer_set_cursor(&caret_image);
+ break;
+
+ case GUI_POINTER_MENU:
+ framebuffer_set_cursor(&menu_image);
+ break;
+
+ case GUI_POINTER_PROGRESS:
+ framebuffer_set_cursor(&progress_image);
+ break;
+
+ case GUI_POINTER_MOVE:
+ framebuffer_set_cursor(&move_image);
+ break;
+
+ default:
+ framebuffer_set_cursor(&pointer_image);
+ break;
+ }
+}
+
+static nserror
+gui_window_set_url(struct gui_window *g, nsurl *url)
+{
+ fbtk_set_text(g->url, nsurl_access(url));
+ return NSERROR_OK;
+}
+
+static void
+throbber_advance(void *pw)
+{
+ struct gui_window *g = pw;
+ struct fbtk_bitmap *image;
+
+ switch (g->throbber_index) {
+ case 0:
+ image = &throbber1;
+ g->throbber_index = 1;
+ break;
+
+ case 1:
+ image = &throbber2;
+ g->throbber_index = 2;
+ break;
+
+ case 2:
+ image = &throbber3;
+ g->throbber_index = 3;
+ break;
+
+ case 3:
+ image = &throbber4;
+ g->throbber_index = 4;
+ break;
+
+ case 4:
+ image = &throbber5;
+ g->throbber_index = 5;
+ break;
+
+ case 5:
+ image = &throbber6;
+ g->throbber_index = 6;
+ break;
+
+ case 6:
+ image = &throbber7;
+ g->throbber_index = 7;
+ break;
+
+ case 7:
+ image = &throbber8;
+ g->throbber_index = 0;
+ break;
+
+ default:
+ return;
+ }
+
+ if (g->throbber_index >= 0) {
+ fbtk_set_bitmap(g->throbber, image);
+ framebuffer_schedule(100, throbber_advance, g);
+ }
+}
+
+static void
+gui_window_start_throbber(struct gui_window *g)
+{
+ g->throbber_index = 0;
+ framebuffer_schedule(100, throbber_advance, g);
+}
+
+static void
+gui_window_stop_throbber(struct gui_window *gw)
+{
+ gw->throbber_index = -1;
+ fbtk_set_bitmap(gw->throbber, &throbber0);
+
+ fb_update_back_forward(gw);
+
+}
+
+static void
+gui_window_remove_caret_cb(fbtk_widget_t *widget)
+{
+ struct browser_widget_s *bwidget = fbtk_get_userpw(widget);
+ int c_x, c_y, c_h;
+
+ if (fbtk_get_caret(widget, &c_x, &c_y, &c_h)) {
+ /* browser window already had caret:
+ * redraw its area to remove it first */
+ fb_queue_redraw(widget,
+ c_x - bwidget->scrollx,
+ c_y - bwidget->scrolly,
+ c_x + 1 - bwidget->scrollx,
+ c_y + c_h - bwidget->scrolly);
+ }
+}
+
+static void
+gui_window_place_caret(struct gui_window *g, int x, int y, int height,
+ const struct rect *clip)
+{
+ struct browser_widget_s *bwidget = fbtk_get_userpw(g->browser);
+
+ /* set new pos */
+ fbtk_set_caret(g->browser, true, x, y, height,
+ gui_window_remove_caret_cb);
+
+ /* redraw new caret pos */
+ fb_queue_redraw(g->browser,
+ x - bwidget->scrollx,
+ y - bwidget->scrolly,
+ x + 1 - bwidget->scrollx,
+ y + height - bwidget->scrolly);
+}
+
+static void
+gui_window_remove_caret(struct gui_window *g)
+{
+ int c_x, c_y, c_h;
+
+ if (fbtk_get_caret(g->browser, &c_x, &c_y, &c_h)) {
+ /* browser window owns the caret, so can remove it */
+ fbtk_set_caret(g->browser, false, 0, 0, 0, NULL);
+ }
+}
+
+static void framebuffer_window_reformat(struct gui_window *gw)
+{
+ /** @todo if we ever do zooming reformat should be implemented */
+ LOG("window:%p", gw);
+
+ /*
+ browser_window_reformat(gw->bw, false, width, height);
+ */
+}
+
+static struct gui_window_table framebuffer_window_table = {
+ .create = gui_window_create,
+ .destroy = gui_window_destroy,
+ .redraw = gui_window_redraw_window,
+ .update = gui_window_update_box,
+ .get_scroll = gui_window_get_scroll,
+ .set_scroll = gui_window_set_scroll,
+ .get_dimensions = gui_window_get_dimensions,
+ .update_extent = gui_window_update_extent,
+ .reformat = framebuffer_window_reformat,
+
+ .set_url = gui_window_set_url,
+ .set_status = gui_window_set_status,
+ .set_pointer = gui_window_set_pointer,
+ .place_caret = gui_window_place_caret,
+ .remove_caret = gui_window_remove_caret,
+ .start_throbber = gui_window_start_throbber,
+ .stop_throbber = gui_window_stop_throbber,
+};
+
+
+static struct gui_misc_table framebuffer_misc_table = {
+ .schedule = framebuffer_schedule,
+ .warning = fb_warn_user,
+
+ .quit = gui_quit,
+};
+
+/** Entry point from OS.
+ *
+ * /param argc The number of arguments in the string vector.
+ * /param argv The argument string vector.
+ * /return The return code to the OS
+ */
+int
+main(int argc, char** argv)
+{
+ struct browser_window *bw;
+ char *options;
+ char *messages;
+ nsurl *url;
+ nserror ret;
+ nsfb_t *nsfb;
+ struct netsurf_table framebuffer_table = {
+ .misc = &framebuffer_misc_table,
+ .window = &framebuffer_window_table,
+ .clipboard = framebuffer_clipboard_table,
+ .fetch = framebuffer_fetch_table,
+ .utf8 = framebuffer_utf8_table,
+ .bitmap = framebuffer_bitmap_table,
+ .layout = framebuffer_layout_table,
+ };
+
+ ret = netsurf_register(&framebuffer_table);
+ if (ret != NSERROR_OK) {
+ die("NetSurf operation table failed registration");
+ }
+
+ respaths = fb_init_resource(NETSURF_FB_RESPATH":"NETSURF_FB_FONTPATH);
+
+ /* initialise logging. Not fatal if it fails but not much we
+ * can do about it either.
+ */
+ nslog_init(nslog_stream_configure, &argc, argv);
+
+ /* user options setup */
+ ret = nsoption_init(set_defaults, &nsoptions, &nsoptions_default);
+ if (ret != NSERROR_OK) {
+ die("Options failed to initialise");
+ }
+ options = filepath_find(respaths, "Choices");
+ nsoption_read(options, nsoptions);
+ free(options);
+ nsoption_commandline(&argc, argv, nsoptions);
+
+ /* message init */
+ messages = filepath_find(respaths, "Messages");
+ ret = messages_add_from_file(messages);
+ free(messages);
+ if (ret != NSERROR_OK) {
+ fprintf(stderr, "Message translations failed to load\n");
+ }
+
+ /* common initialisation */
+ ret = netsurf_init(NULL);
+ if (ret != NSERROR_OK) {
+ die("NetSurf failed to initialise");
+ }
+
+ /* Override, since we have no support for non-core SELECT menu */
+ nsoption_set_bool(core_select_menu, true);
+
+ if (process_cmdline(argc,argv) != true)
+ die("unable to process command line.\n");
+
+ nsfb = framebuffer_initialise(fename, fewidth, feheight, febpp);
+ if (nsfb == NULL)
+ die("Unable to initialise framebuffer");
+
+ framebuffer_set_cursor(&pointer_image);
+
+ if (fb_font_init() == false)
+ die("Unable to initialise the font system");
+
+ fbtk = fbtk_init(nsfb);
+
+ fbtk_enable_oskb(fbtk);
+
+ urldb_load_cookies(nsoption_charp(cookie_file));
+
+ /* create an initial browser window */
+
+ LOG("calling browser_window_create");
+
+ ret = nsurl_create(feurl, &url);
+ if (ret == NSERROR_OK) {
+ ret = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ &bw);
+ nsurl_unref(url);
+ }
+ if (ret != NSERROR_OK) {
+ fb_warn_user(messages_get_errorcode(ret), 0);
+ } else {
+ framebuffer_run();
+
+ browser_window_destroy(bw);
+ }
+
+ netsurf_exit();
+
+ if (fb_font_finalise() == false)
+ LOG("Font finalisation failed.");
+
+ /* finalise options */
+ nsoption_finalise(nsoptions, nsoptions_default);
+
+ return 0;
+}
+
+void gui_resize(fbtk_widget_t *root, int width, int height)
+{
+ struct gui_window *gw;
+ nsfb_t *nsfb = fbtk_get_nsfb(root);
+
+ /* Enforce a minimum */
+ if (width < 300)
+ width = 300;
+ if (height < 200)
+ height = 200;
+
+ if (framebuffer_resize(nsfb, width, height, febpp) == false) {
+ return;
+ }
+
+ fbtk_set_pos_and_size(root, 0, 0, width, height);
+
+ fewidth = width;
+ feheight = height;
+
+ for (gw = window_list; gw != NULL; gw = gw->next) {
+ resize_normal_browser_window(gw,
+ nsoption_int(fb_furniture_size));
+ }
+
+ fbtk_request_redraw(root);
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */
diff --git a/frontends/framebuffer/gui.h b/frontends/framebuffer/gui.h
new file mode 100644
index 000000000..0de1add69
--- /dev/null
+++ b/frontends/framebuffer/gui.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_FB_GUI_H
+#define NETSURF_FB_GUI_H
+
+
+struct fbtk_widget_s;
+
+typedef struct fb_cursor_s fb_cursor_t;
+
+/* bounding box */
+typedef struct nsfb_bbox_s bbox_t;
+
+struct gui_localhistory {
+ struct browser_window *bw;
+
+ struct fbtk_widget_s *window;
+ struct fbtk_widget_s *hscroll;
+ struct fbtk_widget_s *vscroll;
+ struct fbtk_widget_s *history;
+
+ int scrollx, scrolly; /**< scroll offsets. */
+};
+
+struct gui_window {
+ struct browser_window *bw;
+
+ struct fbtk_widget_s *window;
+ struct fbtk_widget_s *back;
+ struct fbtk_widget_s *forward;
+ struct fbtk_widget_s *history;
+ struct fbtk_widget_s *stop;
+ struct fbtk_widget_s *reload;
+ struct fbtk_widget_s *close;
+ struct fbtk_widget_s *url;
+ struct fbtk_widget_s *status;
+ struct fbtk_widget_s *throbber;
+ struct fbtk_widget_s *hscroll;
+ struct fbtk_widget_s *vscroll;
+ struct fbtk_widget_s *browser;
+ struct fbtk_widget_s *toolbar;
+ struct fbtk_widget_s *bottom_right;
+
+ int throbber_index;
+
+ struct gui_localhistory *localhistory;
+
+ struct gui_window *next;
+ struct gui_window *prev;
+};
+
+
+extern struct gui_window *window_list;
+
+struct gui_localhistory *fb_create_localhistory(struct browser_window *bw,
+ struct fbtk_widget_s *parent, int furniture_width);
+void fb_localhistory_map(struct gui_localhistory * glh);
+
+void gui_resize(struct fbtk_widget_s *root, int width, int height);
+
+
+#endif /* NETSURF_FB_GUI_H */
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */
diff --git a/frontends/framebuffer/image_data.h b/frontends/framebuffer/image_data.h
new file mode 100644
index 000000000..cf349f59a
--- /dev/null
+++ b/frontends/framebuffer/image_data.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 FB_IMAGE_DATA
+#define FB_IMAGE_DATA
+
+#include "framebuffer/fbtk.h"
+
+extern struct fbtk_bitmap left_arrow;
+extern struct fbtk_bitmap right_arrow;
+extern struct fbtk_bitmap reload;
+extern struct fbtk_bitmap stop_image;
+extern struct fbtk_bitmap history_image;
+
+extern struct fbtk_bitmap left_arrow_g;
+extern struct fbtk_bitmap right_arrow_g;
+extern struct fbtk_bitmap reload_g;
+extern struct fbtk_bitmap stop_image_g;
+extern struct fbtk_bitmap history_image_g;
+
+extern struct fbtk_bitmap scrolll;
+extern struct fbtk_bitmap scrollr;
+extern struct fbtk_bitmap scrollu;
+extern struct fbtk_bitmap scrolld;
+
+extern struct fbtk_bitmap osk_image;
+
+extern struct fbtk_bitmap pointer_image;
+extern struct fbtk_bitmap hand_image;
+extern struct fbtk_bitmap caret_image;
+extern struct fbtk_bitmap menu_image;
+extern struct fbtk_bitmap move_image;
+extern struct fbtk_bitmap progress_image;
+
+extern struct fbtk_bitmap throbber0;
+extern struct fbtk_bitmap throbber1;
+extern struct fbtk_bitmap throbber2;
+extern struct fbtk_bitmap throbber3;
+extern struct fbtk_bitmap throbber4;
+extern struct fbtk_bitmap throbber5;
+extern struct fbtk_bitmap throbber6;
+extern struct fbtk_bitmap throbber7;
+extern struct fbtk_bitmap throbber8;
+
+#endif /* FB_IMAGE_DATA */
diff --git a/frontends/framebuffer/localhistory.c b/frontends/framebuffer/localhistory.c
new file mode 100644
index 000000000..1b2eb9a60
--- /dev/null
+++ b/frontends/framebuffer/localhistory.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2010 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include <libnsfb.h>
+#include <libnsfb_plot.h>
+#include <libnsfb_event.h>
+
+#include "desktop/browser_history.h"
+#include "desktop/plotters.h"
+
+#include "framebuffer/gui.h"
+#include "framebuffer/fbtk.h"
+#include "framebuffer/framebuffer.h"
+
+static int
+localhistory_redraw(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ struct gui_localhistory *glh = cbi->context;
+ nsfb_bbox_t rbox;
+
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &fb_plotters
+ };
+
+ rbox.x0 = fbtk_get_absx(widget);
+ rbox.y0 = fbtk_get_absy(widget);
+
+ rbox.x1 = rbox.x0 + fbtk_get_width(widget);
+ rbox.y1 = rbox.y0 + fbtk_get_height(widget);
+
+ nsfb_claim(fbtk_get_nsfb(widget), &rbox);
+
+ nsfb_plot_rectangle_fill(fbtk_get_nsfb(widget), &rbox, 0xffffffff);
+
+ browser_window_history_redraw_rectangle(glh->bw,
+ glh->scrollx,
+ glh->scrolly,
+ fbtk_get_width(widget) + glh->scrollx,
+ fbtk_get_height(widget) + glh->scrolly,
+ 0, 0, &ctx);
+
+ nsfb_update(fbtk_get_nsfb(widget), &rbox);
+
+ return 0;
+}
+
+static int
+localhistory_click(fbtk_widget_t *widget, fbtk_callback_info *cbi)
+{
+ struct gui_localhistory *glh = cbi->context;
+
+ if (cbi->event->type != NSFB_EVENT_KEY_UP)
+ return 0;
+
+ browser_window_history_click(glh->bw, cbi->x, cbi->y, false);
+
+ fbtk_set_mapping(glh->window, false);
+
+ return 1;
+}
+
+struct gui_localhistory *
+fb_create_localhistory(struct browser_window *bw,
+ fbtk_widget_t *parent,
+ int furniture_width)
+{
+ struct gui_localhistory *glh;
+ glh = calloc(1, sizeof(struct gui_localhistory));
+
+ if (glh == NULL)
+ return NULL;
+
+ glh->bw = bw;
+
+ /* container window */
+ glh->window = fbtk_create_window(parent, 0, 0, 0, 0, 0);
+
+ glh->history = fbtk_create_user(glh->window, 0, 0, -furniture_width, -furniture_width, glh);
+
+ fbtk_set_handler(glh->history, FBTK_CBT_REDRAW, localhistory_redraw, glh);
+ fbtk_set_handler(glh->history, FBTK_CBT_CLICK, localhistory_click, glh);
+ /*
+ fbtk_set_handler(gw->localhistory, FBTK_CBT_INPUT, fb_browser_window_input, gw);
+ fbtk_set_handler(gw->localhistory, FBTK_CBT_POINTERMOVE, fb_browser_window_move, bw);
+ */
+
+ /* create horizontal scrollbar */
+ glh->hscroll = fbtk_create_hscroll(glh->window,
+ 0,
+ fbtk_get_height(glh->window) - furniture_width,
+ fbtk_get_width(glh->window) - furniture_width,
+ furniture_width,
+ FB_SCROLL_COLOUR,
+ FB_FRAME_COLOUR,
+ NULL,
+ NULL);
+
+ glh->vscroll = fbtk_create_vscroll(glh->window,
+ fbtk_get_width(glh->window) - furniture_width,
+ 0,
+ furniture_width,
+ fbtk_get_height(glh->window) - furniture_width,
+ FB_SCROLL_COLOUR,
+ FB_FRAME_COLOUR,
+ NULL,
+ NULL);
+
+ fbtk_create_fill(glh->window,
+ fbtk_get_width(glh->window) - furniture_width,
+ fbtk_get_height(glh->window) - furniture_width,
+ furniture_width,
+ furniture_width,
+ FB_FRAME_COLOUR);
+
+ return glh;
+}
+
+void
+fb_localhistory_map(struct gui_localhistory * glh)
+{
+ fbtk_set_zorder(glh->window, INT_MIN);
+ fbtk_set_mapping(glh->window, true);
+}
diff --git a/frontends/framebuffer/options.h b/frontends/framebuffer/options.h
new file mode 100644
index 000000000..eee6f4bc6
--- /dev/null
+++ b/frontends/framebuffer/options.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_FRAMEBUFFER_OPTIONS_H_
+#define _NETSURF_FRAMEBUFFER_OPTIONS_H_
+
+/* currently nothing here */
+
+#endif
+
+/***** surface options *****/
+
+NSOPTION_INTEGER(fb_depth, 32)
+NSOPTION_INTEGER(fb_refresh, 70)
+NSOPTION_STRING(fb_device, NULL)
+NSOPTION_STRING(fb_input_devpath, NULL)
+NSOPTION_STRING(fb_input_glob, NULL)
+
+/***** toolkit options *****/
+
+/** toolkit furniture size */
+NSOPTION_INTEGER(fb_furniture_size, 18)
+/** toolbar furniture size */
+NSOPTION_INTEGER(fb_toolbar_size, 30)
+/** toolbar layout */
+NSOPTION_STRING(fb_toolbar_layout, NULL)
+/** enable on screen keyboard */
+NSOPTION_BOOL(fb_osk, false)
+
+/***** font options *****/
+
+/** render all fonts monochrome */
+NSOPTION_BOOL(fb_font_monochrome, false)
+/** size of font glyph cache in kilobytes. */
+NSOPTION_INTEGER(fb_font_cachesize, 2048)
+
+/* Font face paths. These are treated as absolute paths if they start
+ * with a / otherwise the compile time resource path is searched.
+ */
+NSOPTION_STRING(fb_face_sans_serif, NULL)
+NSOPTION_STRING(fb_face_sans_serif_bold, NULL)
+NSOPTION_STRING(fb_face_sans_serif_italic, NULL)
+NSOPTION_STRING(fb_face_sans_serif_italic_bold, NULL)
+NSOPTION_STRING(fb_face_serif, NULL)
+NSOPTION_STRING(fb_face_serif_bold, NULL)
+NSOPTION_STRING(fb_face_monospace, NULL)
+NSOPTION_STRING(fb_face_monospace_bold, NULL)
+NSOPTION_STRING(fb_face_cursive, NULL)
+NSOPTION_STRING(fb_face_fantasy, NULL)
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */
diff --git a/frontends/framebuffer/res/Messages b/frontends/framebuffer/res/Messages
new file mode 120000
index 000000000..72c9eff90
--- /dev/null
+++ b/frontends/framebuffer/res/Messages
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/en/Messages \ No newline at end of file
diff --git a/frontends/framebuffer/res/adblock.css b/frontends/framebuffer/res/adblock.css
new file mode 120000
index 000000000..ff2485622
--- /dev/null
+++ b/frontends/framebuffer/res/adblock.css
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/AdBlock,f79 \ No newline at end of file
diff --git a/frontends/framebuffer/res/credits.html b/frontends/framebuffer/res/credits.html
new file mode 120000
index 000000000..1ba17392b
--- /dev/null
+++ b/frontends/framebuffer/res/credits.html
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/en/credits.html,faf \ No newline at end of file
diff --git a/frontends/framebuffer/res/default.css b/frontends/framebuffer/res/default.css
new file mode 120000
index 000000000..a8579eb7c
--- /dev/null
+++ b/frontends/framebuffer/res/default.css
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/CSS,f79 \ No newline at end of file
diff --git a/frontends/framebuffer/res/favicon.png b/frontends/framebuffer/res/favicon.png
new file mode 120000
index 000000000..5a8b3433c
--- /dev/null
+++ b/frontends/framebuffer/res/favicon.png
@@ -0,0 +1 @@
+../../../resources/favicon.png \ No newline at end of file
diff --git a/frontends/framebuffer/res/fonts/glyph_data b/frontends/framebuffer/res/fonts/glyph_data
new file mode 100644
index 000000000..e02756646
--- /dev/null
+++ b/frontends/framebuffer/res/fonts/glyph_data
@@ -0,0 +1,9508 @@
+* Licensed under the MIT License,
+* http://www.opensource.org/licenses/mit-license.php
+*
+* Copyright Tim Tyler
+* Copyright Michael Drake <tlsa@netsurf-browser.org>
+*
+* This font is based on the "Default" font with Zap 1.47 [TEST], which
+* was created for Zap by Tim Tyler. It was converted to a plain text
+* format and many glyphs added for use in the NetSurf project.
+*
+* Plain text font data:
+* http://git.netsurf-browser.org/art.git/plain/fonts/netsurf/glyph_data
+*
+* Zap: http://zap.tartarus.org/
+* NetSurf: http://www.netsurf-browser.org/
+*
+* Thanks to Tim Tyler for the original font and his permission to use it.
+* Thanks to James Aylett for helping track down Tim.
+* Thanks to Christian Ludlam for helping with Zap font info.
+*
+* Please contact Michael Drake if you want to contribute gylphs to
+* this font.
+
+-----------------------------------------------------
+ Regular Italic Bold Bold &
+ Italic
+-----------------------------------------------------
+U+0000 - NULL <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ ###.###. ###.###. ###.###. ###.###.
+ #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#.
+ #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#.
+ #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#.
+ ###.###. ###.###. ###.###. ###.###.
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+0001 - START OF HEADING <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ ###..#.. ###..#.. ###..#.. ###..#..
+ #.#.##.. #.#.##.. #.#.##.. #.#.##..
+ #.#..#.. #.#..#.. #.#..#.. #.#..#..
+ #.#..#.. #.#..#.. #.#..#.. #.#..#..
+ ###.###. ###.###. ###.###. ###.###.
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+0002 - START OF TEXT <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ ###.##.. ###.##.. ###.##.. ###.##..
+ #.#...#. #.#...#. #.#...#. #.#...#.
+ #.#..#.. #.#..#.. #.#..#.. #.#..#..
+ #.#.#... #.#.#... #.#.#... #.#.#...
+ ###.###. ###.###. ###.###. ###.###.
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+0003 - END OF TEXT <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ ###.##.. ###.##.. ###.##.. ###.##..
+ #.#...#. #.#...#. #.#...#. #.#...#.
+ #.#.##.. #.#.##.. #.#.##.. #.#.##..
+ #.#...#. #.#...#. #.#...#. #.#...#.
+ ###.##.. ###.##.. ###.##.. ###.##..
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+0004 - END OF TRANSMISSION <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ ###.#.#. ###.#.#. ###.#.#. ###.#.#.
+ #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#.
+ #.#.###. #.#.###. #.#.###. #.#.###.
+ #.#...#. #.#...#. #.#...#. #.#...#.
+ ###...#. ###...#. ###...#. ###...#.
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+0005 - ENQUIRY <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ ###.###. ###.###. ###.###. ###.###.
+ #.#.#... #.#.#... #.#.#... #.#.#...
+ #.#.###. #.#.###. #.#.###. #.#.###.
+ #.#...#. #.#...#. #.#...#. #.#...#.
+ ###.###. ###.###. ###.###. ###.###.
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+0006 - ACKNOWLEDGE <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ ###..##. ###..##. ###..##. ###..##.
+ #.#.#... #.#.#... #.#.#... #.#.#...
+ #.#.###. #.#.###. #.#.###. #.#.###.
+ #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#.
+ ###.###. ###.###. ###.###. ###.###.
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+0007 - BELL <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ ###.###. ###.###. ###.###. ###.###.
+ #.#...#. #.#...#. #.#...#. #.#...#.
+ #.#..#.. #.#..#.. #.#..#.. #.#..#..
+ #.#..#.. #.#..#.. #.#..#.. #.#..#..
+ ###..#.. ###..#.. ###..#.. ###..#..
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+0008 - BACKSPACE <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ ###.###. ###.###. ###.###. ###.###.
+ #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#.
+ #.#.###. #.#.###. #.#.###. #.#.###.
+ #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#.
+ ###.###. ###.###. ###.###. ###.###.
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+0009 - CHARACTER TABULATION <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ ###.###. ###.###. ###.###. ###.###.
+ #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#.
+ #.#.###. #.#.###. #.#.###. #.#.###.
+ #.#...#. #.#...#. #.#...#. #.#...#.
+ ###...#. ###...#. ###...#. ###...#.
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+000A - LINE FEED (LF) <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ ###..#.. ###..#.. ###..#.. ###..#..
+ #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#.
+ #.#.###. #.#.###. #.#.###. #.#.###.
+ #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#.
+ ###.#.#. ###.#.#. ###.#.#. ###.#.#.
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+000B - LINE TABULATION <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ ###.##.. ###.##.. ###.##.. ###.##..
+ #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#.
+ #.#.##.. #.#.##.. #.#.##.. #.#.##..
+ #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#.
+ ###.##.. ###.##.. ###.##.. ###.##..
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+000C - FORM FEED (FF) <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ ###..#.. ###..#.. ###..#.. ###..#..
+ #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#.
+ #.#.#... #.#.#... #.#.#... #.#.#...
+ #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#.
+ ###..#.. ###..#.. ###..#.. ###..#..
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+000D - CARRIAGE RETURN (CR) <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ ###.##.. ###.##.. ###.##.. ###.##..
+ #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#.
+ #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#.
+ #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#.
+ ###.##.. ###.##.. ###.##.. ###.##..
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+000E - SHIFT OUT <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ ###.###. ###.###. ###.###. ###.###.
+ #.#.#... #.#.#... #.#.#... #.#.#...
+ #.#.##.. #.#.##.. #.#.##.. #.#.##..
+ #.#.#... #.#.#... #.#.#... #.#.#...
+ ###.###. ###.###. ###.###. ###.###.
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+000F - SHIFT IN <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ ###.###. ###.###. ###.###. ###.###.
+ #.#.#... #.#.#... #.#.#... #.#.#...
+ #.#.##.. #.#.##.. #.#.##.. #.#.##..
+ #.#.#... #.#.#... #.#.#... #.#.#...
+ ###.#... ###.#... ###.#... ###.#...
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+0010 - DATA LINK ESCAPE <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ .#..###. .#..###. .#..###. .#..###.
+ ##..#.#. ##..#.#. ##..#.#. ##..#.#.
+ .#..#.#. .#..#.#. .#..#.#. .#..#.#.
+ .#..#.#. .#..#.#. .#..#.#. .#..#.#.
+ ###.###. ###.###. ###.###. ###.###.
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+0011 - DEVICE CONTROL ONE <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ .#...#.. .#...#.. .#...#.. .#...#..
+ ##..##.. ##..##.. ##..##.. ##..##..
+ .#...#.. .#...#.. .#...#.. .#...#..
+ .#...#.. .#...#.. .#...#.. .#...#..
+ ###.###. ###.###. ###.###. ###.###.
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+0012 - DEVICE CONTROL TWO <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ .#..##.. .#..##.. .#..##.. .#..##..
+ ##....#. ##....#. ##....#. ##....#.
+ .#...#.. .#...#.. .#...#.. .#...#..
+ .#..#... .#..#... .#..#... .#..#...
+ ###.###. ###.###. ###.###. ###.###.
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+0013 - DEVICE CONTROL THREE <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ .#..##.. .#..##.. .#..##.. .#..##..
+ ##....#. ##....#. ##....#. ##....#.
+ .#..##.. .#..##.. .#..##.. .#..##..
+ .#....#. .#....#. .#....#. .#....#.
+ ###.##.. ###.##.. ###.##.. ###.##..
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+0014 - DEVICE CONTROL FOUR <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ .#..#.#. .#..#.#. .#..#.#. .#..#.#.
+ ##..#.#. ##..#.#. ##..#.#. ##..#.#.
+ .#..###. .#..###. .#..###. .#..###.
+ .#....#. .#....#. .#....#. .#....#.
+ ###...#. ###...#. ###...#. ###...#.
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+0015 - NEGATIVE ACKNOWLEDGE <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ .#..###. .#..###. .#..###. .#..###.
+ ##..#... ##..#... ##..#... ##..#...
+ .#..###. .#..###. .#..###. .#..###.
+ .#....#. .#....#. .#....#. .#....#.
+ ###.###. ###.###. ###.###. ###.###.
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+0016 - SYNCHRONOUS IDLE <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ .#...##. .#...##. .#...##. .#...##.
+ ##..#... ##..#... ##..#... ##..#...
+ .#..###. .#..###. .#..###. .#..###.
+ .#..#.#. .#..#.#. .#..#.#. .#..#.#.
+ ###.###. ###.###. ###.###. ###.###.
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+0017 - END OF TRANSMISSION BLOCK <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ .#..###. .#..###. .#..###. .#..###.
+ ##....#. ##....#. ##....#. ##....#.
+ .#...#.. .#...#.. .#...#.. .#...#..
+ .#...#.. .#...#.. .#...#.. .#...#..
+ ###..#.. ###..#.. ###..#.. ###..#..
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+0018 - CANCEL <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ .#..###. .#..###. .#..###. .#..###.
+ ##..#.#. ##..#.#. ##..#.#. ##..#.#.
+ .#..###. .#..###. .#..###. .#..###.
+ .#..#.#. .#..#.#. .#..#.#. .#..#.#.
+ ###.###. ###.###. ###.###. ###.###.
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+0019 - END OF MEDIUM <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ .#..###. .#..###. .#..###. .#..###.
+ ##..#.#. ##..#.#. ##..#.#. ##..#.#.
+ .#..###. .#..###. .#..###. .#..###.
+ .#....#. .#....#. .#....#. .#....#.
+ ###...#. ###...#. ###...#. ###...#.
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+001A - SUBSTITUTE <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ .#...#.. .#...#.. .#...#.. .#...#..
+ ##..#.#. ##..#.#. ##..#.#. ##..#.#.
+ .#..###. .#..###. .#..###. .#..###.
+ .#..#.#. .#..#.#. .#..#.#. .#..#.#.
+ ###.#.#. ###.#.#. ###.#.#. ###.#.#.
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+001B - ESCAPE <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ .#..##.. .#..##.. .#..##.. .#..##..
+ ##..#.#. ##..#.#. ##..#.#. ##..#.#.
+ .#..##.. .#..##.. .#..##.. .#..##..
+ .#..#.#. .#..#.#. .#..#.#. .#..#.#.
+ ###.##.. ###.##.. ###.##.. ###.##..
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+001C - INFORMATION SEPARATOR FOUR <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ .#...#.. .#...#.. .#...#.. .#...#..
+ ##..#.#. ##..#.#. ##..#.#. ##..#.#.
+ .#..#... .#..#... .#..#... .#..#...
+ .#..#.#. .#..#.#. .#..#.#. .#..#.#.
+ ###..#.. ###..#.. ###..#.. ###..#..
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+001D - INFORMATION SEPARATOR THREE <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ .#..##.. .#..##.. .#..##.. .#..##..
+ ##..#.#. ##..#.#. ##..#.#. ##..#.#.
+ .#..#.#. .#..#.#. .#..#.#. .#..#.#.
+ .#..#.#. .#..#.#. .#..#.#. .#..#.#.
+ ###.##.. ###.##.. ###.##.. ###.##..
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+001E - INFORMATION SEPARATOR TWO <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ .#..###. .#..###. .#..###. .#..###.
+ ##..#... ##..#... ##..#... ##..#...
+ .#..##.. .#..##.. .#..##.. .#..##..
+ .#..#... .#..#... .#..#... .#..#...
+ ###.###. ###.###. ###.###. ###.###.
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+001F - INFORMATION SEPARATOR ONE <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ .#..###. .#..###. .#..###. .#..###.
+ ##..#... ##..#... ##..#... ##..#...
+ .#..##.. .#..##.. .#..##.. .#..##..
+ .#..#... .#..#... .#..#... .#..#...
+ ###.#... ###.#... ###.#... ###.#...
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+0020 - SPACE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0021 - EXCLAMATION MARK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ...##... .....##. ...###.. ....###.
+ ...##... .....##. ...###.. ....###.
+ ...##... .....##. ...###.. ....###.
+ ...##... .....##. ...###.. ....###.
+ ...##... ....##.. ...###.. ...###..
+ ...##... ....##.. ...###.. ...###..
+ ...##... ....##.. ...###.. ...###..
+ ...##... ....##.. ...###.. ...###..
+ ...##... ...##... ........ ........
+ ........ ........ ...###.. ..###...
+ ...##... ...##... ...###.. ..###...
+ ...##... ...##... ...###.. ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0022 - QUOTATION MARK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .##.##.. ...##.## ###.###. .###.###
+ .##.##.. ...##.## ###.###. .###.###
+ .##.##.. ..##.##. ###.###. .###.###
+ .##.##.. ..##.##. ###.###. .###.###
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0023 - NUMBER SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .##.##.. ...##.## .##.##.. ..##.##.
+ .##.##.. ...##.## .##.##.. ..##.##.
+ .##.##.. ...##.## #######. .#######
+ #######. .####### #######. .#######
+ #######. .####### #######. .#######
+ .##.##.. ..##.##. .##.##.. ..##.##.
+ .##.##.. ..##.##. .##.##.. .##.##..
+ #######. ######## #######. #######.
+ #######. ######## #######. #######.
+ .##.##.. .##.##.. #######. #######.
+ .##.##.. .##.##.. .##.##.. .##.##..
+ .##.##.. .##.##.. .##.##.. .##.##..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0024 - DOLLAR SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ...#.... .....#.. ...#.... ....#...
+ .#####.. ..#####. .#####.. ..#####.
+ #######. .####### #######. .#######
+ ##.#.##. .##.#.## ####.##. .####.##
+ ##.#.... .##.#... ####.... .####...
+ ####.... .####... #####... .#####..
+ .#####.. ..####.. .#####.. .#####..
+ ...####. ...####. ..#####. ..#####.
+ ...#.##. ...#.##. ...####. ...####.
+ ##.#.##. ##.#.##. ##.####. ##.####.
+ #######. #######. #######. #######.
+ .#####.. .#####.. .#####.. .#####..
+ ...#.... ..#..... ...#.... ...#....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0025 - PERCENT SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ###..... .###....
+ ##...##. ##...##. ###..##. .###..##
+ ##..###. ##..###. ###.###. .###.###
+ ...###.. ...###.. ...###.. ....###.
+ ..###... ..###... ..###... ..####..
+ .###.... .###.... .###.... .###....
+ ###..##. ###..##. ###.###. ###.###.
+ ##...##. ##...##. ##..###. ##..###.
+ ........ ........ ....###. ....###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0026 - AMPERSAND
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .###.... ...###.. .###.... ...###..
+ #####... ..#####. #####... ..#####.
+ ##.##... ..##.##. #####... .######.
+ ##.##... ..##.##. #####... .#####..
+ #####... ..####.. #####... .#####..
+ .###..#. ..###..# .###.##. ..###.##
+ #######. .####### #######. #######.
+ ##.####. .##.###. #######. #######.
+ ##..##.. ##..##.. ###.##.. ###.##..
+ ##..##.. ##..##.. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .####.#. .####.#. .###.##. .###.#..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0027 - APOSTROPHE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... .....##. ..###... ...###..
+ ...##... .....##. ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0028 - LEFT PARENTHESIS
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ .....##. .....##. .....##. ......##
+ ....###. ....###. ....##.. .....##.
+ ...###.. ...###.. ...###.. ....###.
+ ...##... ..###... ..###... ...###..
+ ..###... ..##.... ..###... ...###..
+ ..##.... .##..... .###.... ..###...
+ ..##.... .##..... .###.... ..###...
+ ..##.... ##...... .###.... .###....
+ ..##.... ##...... .###.... .###....
+ ..###... ##...... .###.... .###....
+ ...##... ##...... ..###... ..###...
+ ...###.. ##...... ..###... ..###...
+ ....###. ###..... ...###.. ...###..
+ .....##. .###.... ....##.. ....##..
+ ........ ..##.... .....##. .....##.
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0029 - RIGHT PARENTHESIS
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ .##..... ...##... .##..... .##.....
+ .###.... ...###.. ..##.... ..##....
+ ..###... ....###. ..###... ..###...
+ ...##... .....##. ...###.. ...###..
+ ...###.. .....##. ...###.. ...###..
+ ....##.. .....##. ....###. ....###.
+ ....##.. .....##. ....###. ....###.
+ ....##.. .....##. ....###. ....###.
+ ....##.. ....##.. ....###. ...###..
+ ...###.. ....##.. ....###. ...###..
+ ...##... ...##... ...###.. ..###...
+ ..###... ..###... ...###.. ..###...
+ .###.... .###.... ..###... .###....
+ .##..... ###..... ..##.... .##.....
+ ........ ##...... .##..... ##......
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+002A - ASTERISK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ...#.... ....#...
+ .##.##.. ..##.##. .#.#.#.. ..#.#.#.
+ .##.##.. ..##.##. #######. .#######
+ ..###... ...###.. .#####.. ..#####.
+ #######. .####### ..###... ...##...
+ #######. #######. .#####.. .#####..
+ ..###... ..###... #######. #######.
+ .##.##.. .##.##.. .#.#.#.. .#.#.#..
+ .##.##.. .##.##.. ...#.... ...#....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+002B - PLUS SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... ........ ........ ........
+ ...##... ...##... ..###... ...###..
+ .######. ...##... ..###... ...###..
+ .######. .######. #######. .######.
+ ...##... ######.. #######. .######.
+ ...##... ..##.... ..###... ..###...
+ ........ ..##.... ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+002C - COMMA
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... ...##... ..###... ..###...
+ ..###... ..###... .####... .####...
+ ..##.... .###.... .###.... .###....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+002D - HYPHEN-MINUS
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .######. .######. ........ ........
+ .######. ######.. #######. #######.
+ ........ ........ #######. #######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+002E - FULL STOP
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+002F - SOLIDUS
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .....##. .....##. ....###. .....###
+ ....###. ....###. ...####. ....####
+ ...###.. ...###.. ..####.. ..####..
+ ..###... ..###... .####... .####...
+ .###.... .###.... ####.... ####....
+ ###..... ###..... ###..... ###.....
+ ##...... ##...... ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0030 - DIGIT ZERO
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .#####.. ...####. ..###... ...###..
+ #######. ..###### .#####.. ..#####.
+ ##...##. ..##..## ###.###. .###.###
+ ##..###. ..##..## ###.###. .###.###
+ ##..###. .##..### ###.###. .###.###
+ ##.#.##. .##.###. ###.###. .###.###
+ ##.#.##. .###.##. ###.###. ###.###.
+ ###..##. ###..##. ###.###. ###.###.
+ ###..##. ##..##.. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ #######. ######.. .#####.. .#####..
+ .#####.. .####... ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0031 - DIGIT ONE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ...##... .....##. ...##... ....##..
+ ..###... ....###. ..###... ...###..
+ .####... ...####. .####... ..####..
+ .####... ..####.. .####... ..####..
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0032 - DIGIT TWO
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .#####.. ...####. .#####.. ..#####.
+ #######. ..###### #######. .#######
+ ##...##. ..##..## ###.###. .###.###
+ .....##. ......## ....###. .....###
+ .....##. .....### ....###. .....###
+ ..#####. ...####. .######. ..#####.
+ .#####.. .#####.. ######.. .#####..
+ ###..... .##..... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ #######. ######.. #######. #######.
+ #######. ######.. #######. #######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0033 - DIGIT THREE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .#####.. ...####. .#####.. ..#####.
+ #######. ..###### #######. .#######
+ ##...##. ..##..## ###.###. .###.###
+ .....##. ......## ....###. .....###
+ .....##. .....##. ....###. .....###
+ ..####.. ...####. ..####.. ...####.
+ ..####.. ...####. ..####.. ..####..
+ .....##. .....##. ....###. ....###.
+ .....##. .....##. ....###. ....###.
+ ##...##. ##..##.. ###.###. ###.###.
+ #######. ######.. #######. #######.
+ .#####.. .####... .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0034 - DIGIT FOUR
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .....#.. .......# .....#.. ......#.
+ ....##.. ......## ....##.. .....##.
+ ...###.. .....### ...###.. ....###.
+ ..####.. ....#### ..####.. ...####.
+ .#####.. ...####. .#####.. ..#####.
+ ###.##.. ..##.##. .##.##.. .###.##.
+ #######. .####### ###.##.. ###.##..
+ #######. .####### #######. #######.
+ ....##.. ....##.. #######. #######.
+ ....##.. ....##.. ...###.. ...###..
+ ....##.. ...##... ...###.. ...###..
+ ....##.. ...##... ...###.. ...###..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0035 - DIGIT FIVE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. ...##### #######. .#######
+ #######. ...##### #######. .#######
+ ##...... ..##.... ###..... .###....
+ ##...... ..##.... ###..... .###....
+ ##...... .#####.. ######.. .#####..
+ ######.. .######. #######. .######.
+ #######. .....##. ....###. ....###.
+ .....##. .....##. ....###. ....###.
+ .....##. .....##. ....###. ....###.
+ ##...##. ##..###. ###.###. ###.###.
+ #######. ######.. #######. #######.
+ .#####.. .####... .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0036 - DIGIT SIX
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..####.. ....#### .#####.. ..#####.
+ .#####.. ...##### ######.. .######.
+ ###..... ..###... ###..... .###....
+ ##...... ..##.... ###..... .###....
+ ##...... .##..... ######.. .#####..
+ ######.. .#####.. #######. .######.
+ #######. .######. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .#####.. .#####.. .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0037 - DIGIT SEVEN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. ..###### #######. .#######
+ #######. .####### #######. .#######
+ ##...##. .##...## ....###. .....###
+ ....###. ......## ....###. .....###
+ ....##.. .....##. ...###.. ....###.
+ ...###.. .....##. ...###.. ....###.
+ ...##... ....##.. ...###.. ...###..
+ ..###... ....##.. ..###... ..###...
+ ..##.... ...##... ..###... ..###...
+ ..##.... ...##... ..###... ..###...
+ ..##.... ..##.... ..###... ..###...
+ ..##.... ..##.... ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0038 - DIGIT EIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .#####.. ...####. .#####.. ..#####.
+ #######. ..###### #######. .#######
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## #######. .#######
+ .#####.. ...####. .#####.. ..#####.
+ .#####.. .#####.. #######. .######.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .#####.. .#####.. .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0039 - DIGIT NINE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .#####.. ...####. .#####.. ..#####.
+ #######. ..###### #######. .#######
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.###
+ #######. .######. #######. .#######
+ .######. ..#####. .######. ..#####.
+ .....##. .....##. ....###. ....###.
+ .....##. ....##.. ....###. ....###.
+ ....###. ...###.. ....###. ....###.
+ .#####.. #####... .#####.. .#####..
+ .####... ####.... .####... .####...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+003A - COLON
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... ........ ........ ........
+ ...##... ...##... ..###... ..###...
+ ........ ...##... ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+003B - SEMICOLON
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... ........ ...##... ...##...
+ ...##... ...##... ..###... ..###...
+ ..###... ..###... ..###... ..###...
+ ..##.... ..##.... ..##.... ..##....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+003C - LESS-THAN SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .....##. .....##. ........ ........
+ ....###. ....###. .....##. ......##
+ ...###.. ...###.. ....###. .....###
+ ..###... ..###... ...###.. ....###.
+ .###.... .###.... ..###... ...###..
+ .###.... .###.... .###.... .###....
+ ..###... .###.... .###.... .###....
+ ...###.. ..###... ..###... ..###...
+ ....###. ...###.. ...###.. ...###..
+ .....##. ....##.. ....###. ....###.
+ ........ ........ .....##. .....##.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+003D - EQUALS SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .######. ..#####. ........ ........
+ .######. .#####.. #######. .#######
+ ........ ........ #######. .#######
+ ........ ........ ........ ........
+ .######. .#####.. ........ ........
+ .######. #####... #######. #######.
+ ........ ........ #######. #######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+003E - GREATER-THAN SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .##..... ...##... ........ ........
+ .###.... ...##... .##..... ..##....
+ ..###... ....##.. .###.... ..###...
+ ...###.. ....##.. ..###... ...###..
+ ....###. .....##. ...###.. ....###.
+ ....###. ....###. ....###. ....###.
+ ...###.. ...###.. ....###. ....###.
+ ..###... ..###... ...###.. ...###..
+ .###.... .###.... ..###... ..###...
+ .##..... .##..... .###.... .###....
+ ........ ........ .##..... .##.....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+003F - QUESTION MARK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .#####.. ...####. .#####.. ..#####.
+ #######. ..###### #######. .#######
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ....###. .....###
+ .....##. ......## ....###. .....###
+ ...####. .....### ..#####. ...####.
+ ..####.. ....###. .#####.. .#####..
+ ..##.... ...###.. .###.... .###....
+ ..##.... ...##... ........ ........
+ ........ ........ .###.... .###....
+ ..##.... ..##.... .###.... .###....
+ ..##.... ..##.... ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0040 - COMMERCIAL AT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .#####.. ...####. .#####.. ..#####.
+ #######. ..###### #######. .#######
+ ##...##. ..##..## ###.###. .###.###
+ ##..###. .##...## ###.###. .###.###
+ ##.####. .##.#### ###.###. .###.###
+ ##.#.##. .##.#.## #######. .#######
+ ##.#.##. .##.#.## #######. #######.
+ ##.####. ##..#### ###.##.. ###.##..
+ ##..##.. ##..###. ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ ######.. ######.. ######.. ######..
+ .#####.. .#####.. .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0041 - LATIN CAPITAL LETTER A
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .#####.. ...####. .#####.. ..#####.
+ #######. ..###### #######. .#######
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. .##..##. ###.###. .###.###
+ #######. .######. ###.###. .###.##.
+ #######. .######. #######. #######.
+ ##...##. .##..##. #######. #######.
+ ##...##. ##..##.. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0042 - LATIN CAPITAL LETTER B
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ######.. ..#####. ######.. .######.
+ #######. ..###### #######. .#######
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.###
+ ######.. .######. ######.. .######.
+ ######.. .#####.. ######.. ######..
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ ######.. ######.. ######.. ######..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0043 - LATIN CAPITAL LETTER C
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .#####.. ...####. .#####.. ..#####.
+ #######. ..###### #######. .#######
+ ##...##. ..##..## ###.###. .###.###
+ ##...... ..##.... ###.###. .###.###
+ ##...... .##..... ###..... .###....
+ ##...... .##..... ###..... .###....
+ ##...... .##..... ###..... ###.....
+ ##...... .##..... ###..... ###.....
+ ##...... ##...... ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .#####.. .#####.. .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0044 - LATIN CAPITAL LETTER D
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #####... ..#####. #####... .#####..
+ ######.. ..###### ######.. .######.
+ ##..###. ..##..## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.##.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##....## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##..###. ##..###. ###.###. ###.###.
+ ######.. ######.. ######.. ######..
+ #####... #####... #####... #####...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0045 - LATIN CAPITAL LETTER E
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. ..###### #######. .#######
+ #######. ..###### #######. .#######
+ ##...... ..##.... ###..... .###....
+ ##...... ..##.... ###..... .###....
+ ##...... .##..... ###..... .###....
+ #####... .#####.. #####... .#####..
+ #####... .#####.. #####... #####...
+ ##...... .##..... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ #######. #######. #######. #######.
+ #######. #######. #######. #######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0046 - LATIN CAPITAL LETTER F
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. ..###### #######. .#######
+ #######. ..###### #######. .#######
+ ##...... ..##.... ###..... .###....
+ ##...... ..##.... ###..... .###....
+ ##...... .##..... ###..... .###....
+ #####... .#####.. #####... .#####..
+ #####... .#####.. #####... #####...
+ ##...... .##..... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0047 - LATIN CAPITAL LETTER G
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .#####.. ...####. .#####.. ..#####.
+ #######. ..###### #######. .#######
+ ##...##. ..##..## ###.###. .###.###
+ ##...... ..##.... ###.###. .###.###
+ ##...... .##..... ###..... .###....
+ ##.####. .##..... ###..... .###....
+ ##.####. .##.###. #######. #######.
+ ##...##. .##.###. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##..###. ###.###. ###.###.
+ #######. ######.. #######. #######.
+ .#####.. .####... .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0048 - LATIN CAPITAL LETTER H
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##..##. ###.###. .###.###
+ #######. .######. #######. .######.
+ #######. .######. #######. #######.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0049 - LATIN CAPITAL LETTER I
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .######. ..###### #######. .#######
+ .######. ..###### #######. .#######
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ...##... ...##... ..###... ...###..
+ ...##... ...##... ..###... ...###..
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ .######. ######.. #######. #######.
+ .######. ######.. #######. #######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+004A - LATIN CAPITAL LETTER J
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ....##.. ......## ....###. .....###
+ ....##.. ......## ....###. .....###
+ ....##.. ......## ....###. .....###
+ ....##.. ......## ....###. .....###
+ ....##.. .....##. ....###. .....###
+ ....##.. .....##. ....###. .....##.
+ ....##.. .....##. ....###. ....###.
+ ....##.. .....##. ....###. ....###.
+ ....##.. ....##.. ###.###. ###.###.
+ ##..##.. ##..##.. ###.###. ###.###.
+ ######.. ######.. #######. #######.
+ .####... .####... .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+004B - LATIN CAPITAL LETTER K
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ##...... ..##.... ###..... .###....
+ ##...##. ..##.... ###..##. .###....
+ ##..###. ..##..## ###.###. .###..#.
+ ##.###.. ..##.##. #######. .###.###
+ #####... .#####.. ######.. .#######
+ ####.... .####... #####... .######.
+ ###..... .###.... ####.... #####...
+ ####.... .####... #####... #####...
+ #####... #####... ######.. ######..
+ ##.###.. ##.###.. #######. #######.
+ ##..###. ##..###. ###.###. ###.###.
+ ##...##. ##...##. ###..##. ###..##.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+004C - LATIN CAPITAL LETTER L
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ##...... ..##.... ###..... .###....
+ ##...... ..##.... ###..... .###....
+ ##...... ..##.... ###..... .###....
+ ##...... ..##.... ###..... .###....
+ ##...... .##..... ###..... .###....
+ ##...... .##..... ###..... .###....
+ ##...... .##..... ###..... ###.....
+ ##...... .##..... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ #######. #######. #######. #######.
+ #######. #######. #######. #######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+004D - LATIN CAPITAL LETTER M
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ##...##. ..##..## ##...##. .##...##
+ ##...##. ..##..## ###.###. .###.###
+ ###.###. ..###### #######. .#######
+ #######. ..###### #######. .#######
+ #######. .####### #######. .#######
+ #######. .##.#.## #######. .######.
+ ##.#.##. .##..##. ###.###. ###.###.
+ ##.#.##. .##..##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+004E - LATIN CAPITAL LETTER N
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ##...##. ..##..## ##..###. .##..###
+ ##...##. ..##..## ###.###. .###.###
+ ###..##. ..###.## ###.###. .###.###
+ ###..##. ..###.## #######. .#######
+ ####.##. .####.## #######. .#######
+ ####.##. .######. #######. .######.
+ ##.####. .##.###. #######. #######.
+ ##.####. .##.###. #######. #######.
+ ##..###. ##..###. #######. #######.
+ ##..###. ##..##.. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ##...##. ##..##.. ###..##. ###..##.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+004F - LATIN CAPITAL LETTER O
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .#####.. ...####. .#####.. ..#####.
+ #######. ..###### #######. .#######
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##..##. ###.###. .###.##.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ #######. ######.. #######. #######.
+ .#####.. .####... .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0050 - LATIN CAPITAL LETTER P
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ######.. ..#####. ######.. .######.
+ #######. ..###### #######. .#######
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. .##..### ###.###. .###.###
+ #######. .######. ###.###. .###.###
+ ######.. .#####.. #######. #######.
+ ##...... .##..... ######.. ######..
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0051 - LATIN CAPITAL LETTER Q
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .#####.. ...####. .#####.. ..#####.
+ #######. ..###### #######. .#######
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##..##. ###.###. .###.##.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##.#.##. ####.##. #######. #######.
+ ##.####. ##.###.. #######. #######.
+ #######. #####... #######. #######.
+ .#####.. .#####.. .######. .######.
+ .....##. ....##.. .....##. .....##.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0052 - LATIN CAPITAL LETTER R
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ######.. ..#####. ######.. .######.
+ #######. ..###### #######. .#######
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.###
+ ######.. .######. #######. .######.
+ ######.. .#####.. ######.. ######..
+ ##...##. .##.###. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0053 - LATIN CAPITAL LETTER S
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .#####.. ...####. .#####.. ..#####.
+ #######. ..###### #######. .#######
+ ##...##. ..##..## ###.###. .###.###
+ ##...... .##..... ###..... .###....
+ ##...... .##..... ###..... .###....
+ ######.. .#####.. ######.. .#####..
+ .######. ..#####. .######. ..#####.
+ .....##. .....##. ....###. ....###.
+ .....##. .....##. ....###. ....###.
+ ##...##. ##..###. ###.###. ###.###.
+ #######. ######.. #######. #######.
+ .#####.. .####... .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0054 - LATIN CAPITAL LETTER T
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .######. .####### #######. .#######
+ .######. .####### #######. .#######
+ ...##... ...##... ..###... ...###..
+ ...##... ...##... ..###... ...###..
+ ...##... ..##.... ..###... ...###..
+ ...##... ..##.... ..###... ...###..
+ ...##... ..##.... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ ...##... .##..... ..###... ..###...
+ ...##... .##..... ..###... ..###...
+ ...##... .##..... ..###... ..###...
+ ...##... .##..... ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0055 - LATIN CAPITAL LETTER U
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##..##. ###.###. .###.##.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ #######. ######.. #######. #######.
+ .#####.. .#####.. .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0056 - LATIN CAPITAL LETTER V
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.##.
+ ###.###. .##..##. ###.###. ###.###.
+ .##.##.. .##..##. .##.##.. .##.##..
+ .#####.. .##.##.. .#####.. .#####..
+ ..###... ..###... ..###... ..###...
+ ..###... ..###... ..###... ..###...
+ ...#.... ...#.... ...#.... ...#....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0057 - LATIN CAPITAL LETTER W
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.##.
+ ##.#.##. .##.#.## ###.###. ###.###.
+ ##.#.##. .##.#.## #######. #######.
+ #######. #######. #######. #######.
+ #######. #######. #######. #######.
+ ###.###. ###.###. ###.###. ###.###.
+ .#...#.. .#...#.. .#...#.. .#...#..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0058 - LATIN CAPITAL LETTER X
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.###
+ ###.###. .###.### ###.###. .###.###
+ .#####.. ..#####. .#####.. ..#####.
+ ..###... ...###.. ..###... ...###..
+ ..###... ..###... ..###... ..###...
+ .#####.. .#####.. .#####.. .#####..
+ ###.###. ###.###. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0059 - LATIN CAPITAL LETTER Y
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ##...##. .##..##. ###.###. .###.###
+ ##...##. .##..##. ###.###. .###.###
+ ##...##. .##..##. ###.###. .###.###
+ ##...##. .##..##. ###.###. .###.###
+ ###.###. ..####.. ###.###. .###.###
+ .#####.. ..####.. .#####.. ..#####.
+ ..###... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ ...##... .##..... ..###... ..###...
+ ...##... .##..... ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+005A - LATIN CAPITAL LETTER Z
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ #######. .####### #######. .#######
+ ....###. ......## ....###. .....###
+ ....##.. .....### ....###. .....###
+ ...###.. ....###. ...###.. ....###.
+ ...##... ....##.. ..###... ...###..
+ ..###... ...##... ..###... ..###...
+ ..##.... ..###... .###.... .###....
+ .###.... .###.... ###..... ###.....
+ .##..... ###..... ###..... ###.....
+ #######. #######. #######. #######.
+ #######. #######. #######. #######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+005B - LEFT SQUARE BRACKET
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..#####. ....#### ..#####. ...#####
+ ..#####. ....#### ..#####. ...#####
+ ..##.... ....##.. ..###... ...###..
+ ..##.... ....##.. ..###... ...###..
+ ..##.... ...##... ..###... ...###..
+ ..##.... ...##... ..###... ...###..
+ ..##.... ...##... ..###... ..###...
+ ..##.... ...##... ..###... ..###...
+ ..##.... ..##.... ..###... ..###...
+ ..##.... ..##.... ..###... ..###...
+ ..#####. ..#####. ..#####. ..#####.
+ ..#####. ..#####. ..#####. ..#####.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+005C - REVERSE SOLIDUS
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ .#...... ..#.....
+ ##...... ##...... ###..... .###....
+ ###..... ###..... ####.... .####...
+ .###.... .###.... .####... ..###...
+ ..###... ..###... ..####.. ...###..
+ ...###.. ...###.. ...####. ...####.
+ ....###. ....###. ....###. ....###.
+ .....##. .....##. .....#.. .....#..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+005D - RIGHT SQUARE BRACKET
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .#####.. ...##### ######.. .######.
+ .#####.. ...##### ######.. .######.
+ ....##.. ......## ...###.. ....###.
+ ....##.. ......## ...###.. ....###.
+ ....##.. .....##. ...###.. ....###.
+ ....##.. .....##. ...###.. ....###.
+ ....##.. .....##. ...###.. ...###..
+ ....##.. .....##. ...###.. ...###..
+ ....##.. ....##.. ...###.. ...###..
+ ....##.. ....##.. ...###.. ...###..
+ .#####.. .#####.. ######.. ######..
+ .#####.. .#####.. ######.. ######..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+005E - CIRCUMFLEX ACCENT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ...#.... .....#.. ...#.... ....#...
+ ..###... ....###. ..###... ...###..
+ .#####.. ...##### .#####.. ..#####.
+ ###.###. ..###.## ###.###. .###.###
+ ##...##. .##...## ##...##. .##...##
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+005F - LOW LINE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ######## #######. ######## ########
+ ######## #######. ######## ########
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0060 - GRAVE ACCENT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ..##.... ........ ........ ........
+ ..##.... ....##.. ..##.... ...##...
+ ...##... ....###. ..###... ...###..
+ ....#... ....##.. ...##... ....##..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0061 - LATIN SMALL LETTER A
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .#####.. ..####.. .#####.. ..####..
+ .######. ..#####. .######. ..#####.
+ .....##. .....##. ....###. ....###.
+ .######. .######. .######. ..#####.
+ #######. ######.. #######. .######.
+ ##...##. ##..##.. ###.###. ###.###.
+ #######. ######.. #######. #######.
+ .######. .#####.. .######. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0062 - LATIN SMALL LETTER B
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ##...... ...##... ###..... ..###...
+ ##...... ...##... ###..... ..###...
+ ##...... ..##.... ###..... ..###...
+ ##...... ..##.... ###..... .###....
+ ######.. ..#####. ######.. .#####..
+ #######. .####### #######. .######.
+ ##...##. .##...## ###.###. .##.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. ######..
+ ######.. ######.. ######.. #####...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0063 - LATIN SMALL LETTER C
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .#####.. ..####.. .#####.. ..####..
+ #######. .######. #######. .######.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ ##...##. ##..##.. ###.###. ###.###.
+ #######. ######.. #######. #######.
+ .#####.. .####... .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0064 - LATIN SMALL LETTER D
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .....##. ......## ....###. .....###
+ .....##. ......## ....###. .....###
+ .....##. ......## ....###. .....###
+ .....##. .....##. ....###. .....###
+ .######. ..#####. .######. ..######
+ #######. .######. #######. .######.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ #######. ######.. #######. #######.
+ .######. .#####.. .######. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0065 - LATIN SMALL LETTER E
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .#####.. ..####.. .#####.. ..####..
+ #######. .######. #######. .######.
+ ##...##. .##..##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ #######. ######.. #######. ######..
+ ##...... ##...... ###..... ###.....
+ #######. ######.. #######. ######..
+ .######. .#####.. .######. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0066 - LATIN SMALL LETTER F
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ...####. .....### ..#####. ...#####
+ ..#####. ....#### .######. ..######
+ ..##.... ....##.. .###.... ..###...
+ ..##.... ...##... .###.... ..###...
+ ..##.... .######. .###.... .###....
+ .####... .######. #####... .#####..
+ .####... ..##.... #####... .#####..
+ ..##.... ..##.... .###.... .###....
+ ..##.... ..##.... .###.... ###.....
+ ..##.... .##..... .###.... ###.....
+ ..##.... .##..... .###.... ###.....
+ ..##.... .##..... .###.... ###.....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0067 - LATIN SMALL LETTER G
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .######. ..###### .######. ..#####.
+ #######. .####### #######. .######.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .######. .######. .######. .#####..
+ .....##. ....##.. ....###. ...###..
+ .######. ######.. .######. ######..
+ .#####.. #####... .#####.. #####...
+-----------------------------------------------------
+U+0068 - LATIN SMALL LETTER H
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ##...... ..##.... ###..... .###....
+ ##...... ..##.... ###..... .###....
+ ##...... ..##.... ###..... .###....
+ ##.###.. .##.###. ######.. .#####..
+ #######. .####### #######. .######.
+ ###..##. .###..## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0069 - LATIN SMALL LETTER I
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... .....##. ..###... ....###.
+ ...##... .....##. ..###... ....###.
+ ........ ........ ........ ........
+ ..###... ...###.. .####... ...####.
+ ..###... ..####.. .####... ..#####.
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ..####.. .#####.. .#####.. .#####..
+ ..####.. .#####.. .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+006A - LATIN SMALL LETTER J
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ....##.. ......## ...###.. ....###.
+ ....##.. ......## ...###.. ....###.
+ ........ ........ ........ ........
+ ...###.. ....###. ..####.. ...####.
+ ...###.. ...####. ..####.. ..#####.
+ ....##.. .....##. ...###.. ....###.
+ ....##.. .....##. ...###.. ...###..
+ ....##.. ....##.. ...###.. ...###..
+ ....##.. ....##.. ...###.. ...###..
+ ....##.. ....##.. ...###.. ...###..
+ ....##.. ....##.. ...###.. ..###...
+ ....##.. ...##... ...###.. ..###...
+ .#####.. #####... .#####.. #####...
+ .####... ####.... .####... ####....
+-----------------------------------------------------
+U+006B - LATIN SMALL LETTER K
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ##...... ..##.... ###..... ..###...
+ ##...... ..##.... ###..... ..###...
+ ##...... ..##.... ###..... ..###...
+ ##...... ..##..## ###..##. ..###.#.
+ ##...##. .##..### ###.###. .#######
+ ##..###. .##.###. #######. .######.
+ ##.###.. .#####.. ######.. .#####..
+ #####... .#####.. #####... .###....
+ #####... ##.###.. ######.. #####...
+ ##.###.. ##..###. #######. ######..
+ ##..###. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###..##. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+006C - LATIN SMALL LETTER L
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..###... ....###. .####... ..####..
+ ..###... ....###. .####... ..####..
+ ...##... .....##. ..###... ...###..
+ ...##... .....##. ..###... ...###..
+ ...##... ....##.. ..###... ..###...
+ ...##... ....##.. ..###... ..###...
+ ...##... ....##.. ..###... ..###...
+ ...##... ....##.. ..###... ..###...
+ ...##... ...##... ..###... .###....
+ ...##... ...##... ..###... .###....
+ ...##... ...##... ..###... .###....
+ ...##... ...##... ..###... .###....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+006D - LATIN SMALL LETTER M
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ###.##.. .###.##. ###.##.. .###.#..
+ #######. .####### #######. .######.
+ #######. .####### #######. #######.
+ ##.#.##. .##.#.## #######. #######.
+ ##.#.##. ##.#.##. #######. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+006E - LATIN SMALL LETTER N
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ##.###.. .##.##.. ######.. .#####..
+ #######. .######. #######. .######.
+ ###..##. .###.##. ###.###. ###.###.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+006F - LATIN SMALL LETTER O
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .#####.. ..####.. .#####.. ..####..
+ #######. .######. #######. .######.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ #######. ######.. #######. ######..
+ .#####.. .####... .#####.. .####...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0070 - LATIN SMALL LETTER P
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ######.. ..#####. ######.. .#####..
+ #######. ..###### #######. .######.
+ ##...##. ..##..## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. .##..##. ###.###. ###.###.
+ #######. #######. #######. ######..
+ ######.. ######.. ######.. #####...
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+-----------------------------------------------------
+U+0071 - LATIN SMALL LETTER Q
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .######. ..###### .######. ..#####.
+ #######. .####### #######. .######.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. ######..
+ .######. .#####.. .######. .#####..
+ .....##. ....##.. ....###. ...###..
+ .....### ....###. ....#### ...####.
+ .....### ....###. ....#### ...####.
+-----------------------------------------------------
+U+0072 - LATIN SMALL LETTER R
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ##.####. .##.#### ##.####. .###.##.
+ #######. .####### #######. .######.
+ ###..... .###.... ###..... .###....
+ ##...... .##..... ###..... .###....
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0073 - LATIN SMALL LETTER S
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .######. ..###### .######. ...#####
+ #######. .####### #######. ..#####.
+ ##...... .##..... ###..... .###....
+ ######.. .#####.. ######.. .#####..
+ .#####.. ..#####. .######. ..#####.
+ .....##. .....##. ....###. ....###.
+ #######. #######. #######. .#####..
+ ######.. ######.. ######.. #####...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0074 - LATIN SMALL LETTER T
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ...#.... .....##. ...#.... ..###...
+ ..##.... .....##. ..##.... ..###...
+ ..##.... ....##.. .###.... ..###...
+ ..##.... ....##.. .###.... .###....
+ .#####.. ..###### ######.. .#####..
+ ######.. ..###### ######.. .#####..
+ ..##.... ...##... .###.... .###....
+ ..##.... ...##... .###.... ###.....
+ ..##.... ..##.... .###.... ###.....
+ ..##.... ..##.... .###.... ###.....
+ ..#####. ..#####. .######. ######..
+ ...####. ...###.. ..#####. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0075 - LATIN SMALL LETTER U
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.##.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. .##..##. ###.###. ###.###.
+ ##..###. ##..##.. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .###.##. .###.##. .######. .######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0076 - LATIN SMALL LETTER V
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ##...##. .##..##. ###.###. .###.###
+ ##...##. .##..##. ###.###. .###.##.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ###.###. ##..##.. ###.###. ###.###.
+ .#####.. #####... .#####.. .#####..
+ ..###... .###.... ..###... ..###...
+ ...#.... ..#..... ...#.... ...#....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0077 - LATIN SMALL LETTER W
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.##.
+ ##...##. .##...## ###.###. ###.###.
+ ##.#.##. .##.#.## #######. #######.
+ ##.#.##. ##.####. #######. #######.
+ #######. #######. #######. #######.
+ #######. #######. ###.###. ###.###.
+ .##.##.. .##.##.. .#...#.. .#...#..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0078 - LATIN SMALL LETTER X
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.##.
+ ###.###. .###.### ###.###. .######.
+ .#####.. ..#####. .#####.. ..###...
+ .#####.. .#####.. .#####.. .#####..
+ ###.###. ###.###. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0079 - LATIN SMALL LETTER Y
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.##.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. #######. #######.
+ #######. #######. .######. .######.
+ .######. .######. ....###. ...###..
+ .....##. ....##.. ....###. ...###..
+ #######. ######.. #######. ######..
+ ######.. #####... ######.. #####...
+-----------------------------------------------------
+U+007A - LATIN SMALL LETTER Z
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ #######. .####### #######. .######.
+ ....###. .....### ...####. ...####.
+ ..####.. ...####. ..####.. ..####..
+ .####... .####... .####... .####...
+ ###..... ###..... ####.... ####....
+ #######. #######. #######. #######.
+ #######. #######. #######. #######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+007B - LEFT CURLY BRACKET
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ....###. .....### ...####. ....####
+ ...####. ....###. ..#####. ...#####
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ...##... ...##... ..###... ...###..
+ .###.... .###.... ####.... .####...
+ .###.... .###.... ####.... ####....
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ ...####. ..###... ..#####. ..#####.
+ ....###. ...###.. ...####. ...####.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+007C - VERTICAL LINE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##... .....##. ..###... ...###..
+ ...##... .....##. ..###... ...###..
+ ...##... .....##. ..###... ...###..
+ ...##... .....##. ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+007D - RIGHT CURLY BRACKET
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .###.... ..###... ####.... .####...
+ .####... ...###.. #####... .#####..
+ ...##... ....##.. ..###... ...###..
+ ...##... ...##... ..###... ...###..
+ ...##... ...##... ..###... ...###..
+ ....###. ....###. ...####. ....####
+ ....###. ....###. ...####. ...####.
+ ...##... ...##... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ .####... .###.... #####... #####...
+ .###.... ###..... ####.... ####....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+007E - TILDE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .###.##. ...###.# .##..##. ..##..##
+ #######. ..###### #######. .#######
+ ##.###.. ..##.### #######. .#######
+ ........ ........ ##..##.. .##..##.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+007F - DELETE <CONTROL>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ ........ ........ ........ ........
+ ###.###. .###.### ###.###. .###.###
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ #.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
+ ###.###. .###.### ###.###. .###.###
+ ........ ........ ........ ........
+ ###.###. ###.###. ###.###. ###.###.
+ ..#.#... ..#.#... ..#.#... ..#.#...
+ .#..##.. .#..##.. .#..##.. .#..##..
+ .#..#... .#..#... .#..#... .#..#...
+ .#..#... .#..#... .#..#... .#..#...
+ ........ ........ ........ ........
+ #######. #######. #######. #######.
+-----------------------------------------------------
+U+00A0 - NO-BREAK SPACE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00A1 - INVERTED EXCLAMATION MARK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ........ ........ ........ ........
+ ...##... ....##.. ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00A2 - CENT SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...#.... .....#.. ...#.... ....#...
+ ...#.... .....#.. ...#.... ....#...
+ .#####.. ..#####. .#####.. ..#####.
+ #######. .####### #######. .#######
+ ##.#.##. .##.#.## ##.#.##. ##.#.##.
+ ##.#.... .##.#... ##.#.... ##.#....
+ ##.#.... ##.#.... ##.#.... ##.#....
+ ##.#.##. ##.#.##. ##.#.##. ##.#.##.
+ #######. #######. #######. #######.
+ .#####.. .#####.. .#####.. .#####..
+ ...#.... ..#..... ...#.... ...#....
+ ...#.... ..#..... ...#.... ...#....
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00A3 - POUND SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..####.. ....#### ..####.. ...####.
+ .######. ...##### .######. ..######
+ .##..##. ...##..# .###.##. ..###.##
+ .##..... ...##... .###.... ..###...
+ .##..... ..##.... ######.. .######.
+ #####... .#####.. ######.. .######.
+ #####... .#####.. ######.. ######..
+ .##..... ..##.... .###.... .###....
+ .##..... .##..... .###.##. .###.##.
+ ###..##. ###..##. #######. #######.
+ #######. #######. #######. #######.
+ ##.###.. ##.###.. ##.###.. ##.###..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00A4 - CURRENCY SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .##..##. ...##..# ###.###. .###.###
+ .##..##. ..##..## ###.###. .###.###
+ ..####.. ...####. .#####.. ..#####.
+ .######. ..###### #######. #######.
+ .##..##. ..##..## #######. #######.
+ .##..##. .##..##. #######. #######.
+ .######. .######. #######. #######.
+ ..####.. ..####.. .#####.. .#####..
+ .##..##. .##..##. ###.###. ###.###.
+ .##..##. ##..##.. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00A5 - YEN SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .##..##. ...##..# ###.###. .###.###
+ .##..##. ...##..# ###.###. .###.###
+ .##..##. ...##..# ###.###. .###.###
+ ..####.. ...####. .#####.. ..#####.
+ ...##... ....##.. ..###... ...###..
+ .######. ..###### #######. #######.
+ ...##... ....##.. ..###... ..###...
+ .######. .######. #######. #######.
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00A6 - BROKEN BAR
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... .....##. ..###... ...###..
+ ...##... .....##. ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00A7 - SECTION SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..####.. .....### ..####.. ...####.
+ .#####.. ....###. .#####.. ..#####.
+ .##..... ...##... .#####.. ..#####.
+ .##..... ...##... .###.... ..###...
+ ..####.. ..####.. ..####.. ...####.
+ .######. .######. .######. ..######
+ .##..##. .##..##. .##..##. .##..##.
+ .######. .######. .######. .######.
+ ..####.. ..####.. ..####.. ..####..
+ .....##. ...##... ....###. ....###.
+ .....##. ...##... ..#####. ..#####.
+ ..#####. .###.... ..#####. ..#####.
+ ..####.. ###..... ..####.. ..####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00A8 - DIAERESIS
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ###.###. .###.###
+ .##..##. ..##..## ###.###. .###.###
+ .##..##. .##..##. ###.###. .###.###
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00A9 - COPYRIGHT SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..####.. ....###. ..####.. ...####.
+ .######. ...##### .######. ..######
+ ##....## ..#....# ##....## .##....#
+ #..##..# ..#....# #..##..# .#..##.#
+ #.####.# .#..##.# #.####.# .#.###.#
+ #.#..#.# .#.#...# #.#..#.# .#.#...#
+ #.#....# .#.#...# #.#....# #.#....#
+ #.#..#.# #..#...# #.#..#.# #.#..#.#
+ #.####.# #.#....# #.####.# #.####.#
+ #..##..# #..##.#. #..##..# #..##..#
+ ##....## #.....#. ##....## ##....##
+ .######. .#####.. .######. .######.
+ ..####.. ..###... ..####.. ..####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00AA - FEMININE ORDINAL INDICATOR
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .###.... ...###.. .####... ..####..
+ ....#... ......#. ....##.. .....##.
+ .####... ...####. .#####.. ..#####.
+ #...#... ..#...#. ##..##.. .##..##.
+ .####... ..####.. .#####.. ..#####.
+ ........ ........ ........ ........
+ #####... .#####.. ######.. ######..
+ #####... .#####.. ######.. ######..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00AB - LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ..##.##. ...##.## ..#####. ..#####.
+ .##.##.. ..##.##. .#####.. .#####..
+ ##.##... ##.##... #####... #####...
+ ##.##... ##.##... #####... #####...
+ .##.##.. .##.##.. .#####.. .#####..
+ ..##.##. ..##.##. ..#####. ..#####.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00AC - NOT SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .######. ...####. .######. ..######
+ .######. ..#####. .######. ..######
+ .....##. .....##. .######. ..######
+ ......#. ....##.. .....##. ......##
+ ........ ........ ......#. .......#
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00AD - SOFT HYPHEN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .######. ..###### .######. .######.
+ ........ ........ .######. .######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00AE - REGISTERED SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..####.. ....###. ..####.. ...####.
+ .######. ...##### .######. ..######
+ ##....## ..#....# ##....## .##....#
+ #.###..# .#..#..# #.###..# .#.##..#
+ #.####.# .#.#.#.# #.####.# .#.###.#
+ #.#..#.# .#.#.#.# #.#..#.# .#.#.#.#
+ #.###..# .#.##..# #.###..# #.###..#
+ #.###..# #..##..# #.###..# #.###..#
+ #.#..#.# #.#..#.# #.#..#.# #.#..#.#
+ #.#..#.# #.#..#.# #.#..#.# #.#..#.#
+ ##....## #.....#. ##....## ##....##
+ .######. .######. .######. .######.
+ ..####.. .####... ..####.. ..####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00AF - MACRON
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .######. ...##### #######. .#######
+ ........ ........ #######. .#######
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00B0 - DEGREE SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .####... ...####. .####... ..####..
+ ######.. ..###### ######.. .######.
+ ##..##.. ..##..## ##..##.. .##..##.
+ ######.. ..###### ######.. .######.
+ .####... ..####.. .####... ..####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00B1 - PLUS-MINUS SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ..###... ...###..
+ ...##... .....##. ..###... ...###..
+ ...##... .....##. #######. .#######
+ .######. ..###### #######. .#######
+ .######. ..###### #######. .#######
+ ...##... ....##.. ..###... ..###...
+ ...##... ....##.. ..###... ..###...
+ ........ ........ ........ ........
+ .######. .######. #######. #######.
+ .######. .######. #######. #######.
+ ........ ........ #######. #######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00B2 - SUPERSCRIPT TWO
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ####.... ..####.. ####.... .####...
+ #####... ..#####. #####... .#####..
+ ...##... .....##. ...##... ....##..
+ .####... ..####.. .####... ..####..
+ ####.... .####... ####.... .####...
+ ##...... .##..... ##...... ##......
+ #####... .#####.. #####... #####...
+ #####... .#####.. #####... #####...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00B3 - SUPERSCRIPT THREE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ####.... ..####.. ####.... .####...
+ #####... ..#####. #####... .#####..
+ ...##... .....##. ...##... ....##..
+ ..##.... ...##... ..##.... ...##...
+ ...##... ....##.. ...##... ....##..
+ #####... .#####.. #####... #####...
+ ####.... .####... ####.... ####....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00B4 - ACUTE ACCENT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... .....##. ..###... ...###..
+ ..##.... ....##.. .###.... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00B5 - MICRO SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .##..##. ..##..## ###.###. .###.###
+ .##..##. ..##..## ###.###. ###.###.
+ .##..##. ..##..## ###.###. ###.###.
+ .##..##. .##..##. ###.###. ###.###.
+ .##..##. .##..##. ###.###. ###.###.
+ .######. .######. #######. #######.
+ .#####.. .#####.. ######.. ######..
+ .##..... ##...... ###..... ###.....
+ .##..... ##...... ###..... ###.....
+ ##...... #....... ##...... ##......
+-----------------------------------------------------
+U+00B6 - PILCROW SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .####### ..###### .####### ..######
+ ##.##.## .##.#### ##.##.## .##.##.#
+ ##.##.## ##.##.## ##.##.## .##.##.#
+ ##.##.## ##.##.## ##.##.## .##.##.#
+ .####.## .####.## .####.## .####.#.
+ ...##.## ...##.## ...##.## ..##.##.
+ ...##.## ..##.##. ...##.## ..##.##.
+ ...##.## ..##.##. ...##.## ..##.##.
+ ...##.## ..##.##. ...##.## ..##.##.
+ ...##.## ..##.##. ...##.## ..##.##.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00B7 - MIDDLE DOT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ..###... ...###..
+ ...##... ....##.. ..###... ..###...
+ ...##... ....##.. ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00B8 - CEDILLA
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... ...##... ...##... ...##...
+ ...###.. ...###.. ...###.. ...###..
+ ....##.. ....##.. ....##.. ....##..
+ ...###.. ...###.. ...###.. ...###..
+ ...##... ..##.... ...##... ...##...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00B9 - SUPERSCRIPT ONE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .##..... ...##... .##..... ..##....
+ ###..... ..###... ###..... .###....
+ .##..... ...##... .##..... ..##....
+ .##..... ..##.... .##..... ..##....
+ .##..... ..##.... .##..... ..##....
+ ####.... .####... ####.... ####....
+ ####.... .####... ####.... ####....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00BA - MASCULINE ORDINAL INDICATOR
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .###.... ....##.. .###.... ..###...
+ #####... ...####. #####... .#####..
+ #...#... ..#...#. #...#... .#...#..
+ #####... ..####.. #####... .#####..
+ .###.... ...##... .###.... ..###...
+ ........ ........ ........ ........
+ #####... ..####.. #####... #####...
+ #####... .####... #####... #####...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00BB - RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ##.##... .##.##.. ##.##... ##.##...
+ .##.##.. ..##.##. .##.##.. .##.##..
+ ..##.##. ..##.##. ..##.##. ..##.##.
+ ..##.##. ..##.##. ..##.##. ..##.##.
+ .##.##.. .##.##.. .##.##.. .##.##..
+ ##.##... ##.##... ##.##... ##.##...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00BC - VULGAR FRACTION ONE QUARTER
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .#...... ...#.... .##..... ..##....
+ ##...... ..##.... ###..... .###....
+ .#....#. ...#.... .##...#. ..##...#
+ .#...##. ..#...## .##..##. ..##..##
+ ###.##.. .###.##. #######. .#######
+ ...##... ....##.. ...###.. ...###..
+ ..##.... ...##... ..###... ..###...
+ .##..#.. .##..#.. .###.##. .###.##.
+ ##..##.. ##..##.. ###.###. ###.###.
+ #..#.#.. #..#.#.. ##.#.##. ##.#.##.
+ ..#####. ..#####. ..#####. ..#####.
+ .....#.. ....#... ..#####. ..#####.
+ .....#.. ....#... .....##. .....##.
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00BD - VULGAR FRACTION ONE HALF
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .#...... ...#.... .##..... ..##....
+ ##...... ..##.... ###..... .###....
+ .#....#. ...#.... .##...#. ..##...#
+ .#...##. ..#...## .##..##. ..##..##
+ ###.##.. .###.##. #######. .#######
+ ...##... ....##.. ...###.. ...###..
+ ..##.... ...##... ..###... ..###...
+ .##..... .##..... .###.... .###....
+ ##.###.. ##.###.. ######.. ######..
+ #.....#. #.....#. ##...##. ##...##.
+ ....##.. ...###.. ....##.. ....##..
+ ...#.... ..#..... ...##... ...##...
+ ...####. ..####.. ...####. ...####.
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00BE - VULGAR FRACTION THREE QUARTERS
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ###..... ..###... ###..... .###....
+ ...#.... .....#.. ..##.... ...##...
+ .##...#. ...##... .##...#. ..##...#
+ ...#.##. ....#.## ..##.##. ...##.##
+ ###.##.. .###.##. ###.###. .###.###
+ ...##... ....##.. ...###.. ...###..
+ ..##.... ...##... ..###.#. ..###.#.
+ .##..#.. .##..#.. .###.##. .###.##.
+ ##..##.. ##..##.. ###.###. ###.###.
+ #..#.##. #..#.##. ##.#.##. ##.#.##.
+ ..#####. ..#####. ..#####. ..#####.
+ .....#.. ....#... ..#####. ..#####.
+ .....#.. ....#... .....##. .....##.
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00BF - INVERTED QUESTION MARK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ...###.. ....###.
+ ...##... .....##. ...###.. ....###.
+ ...##... .....##. ...###.. ....###.
+ ........ ........ ........ ........
+ ...##... ....##.. ...###.. ....###.
+ ...##... ....##.. ...###.. ...###..
+ .####... ..####.. .#####.. .#####..
+ ####.... .###.... #####... #####...
+ ##...... ##...... ###..... ###.....
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. ######.. #######. #######.
+ .#####.. .####... .#####.. .#####..
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00C0 - LATIN CAPITAL LETTER A WITH GRAVE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .##..... ...##... ###..... .###....
+ ..##.... ....##.. .###.... ..###...
+ ........ ........ ........ ........
+ ...#.... .....#.. ...#.... ....#...
+ ..###... ...###.. ..###... ...###..
+ .##.##.. ..##.##. .#####.. ..#####.
+ ##...##. .##...## ###.###. ###.###.
+ #######. .####### #######. #######.
+ #######. #######. #######. #######.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00C1 - LATIN CAPITAL LETTER A WITH ACUTE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ....##.. ......## ....###. .....###
+ ...##... .....##. ...###.. ....###.
+ ........ ........ ........ ........
+ ...#.... .....#.. ...#.... ....#...
+ ..###... ...###.. ..###... ...###..
+ .##.##.. ..##.##. .#####.. ..#####.
+ ##...##. .##...## ###.###. ###.###.
+ #######. .####### #######. #######.
+ #######. #######. #######. #######.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00C2 - LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..###... ....###. ..###... ...###..
+ .##.##.. ...##.## .##.##.. ..##.##.
+ .#...#.. ...#...# ##...##. .##...##
+ ...#.... .....#.. ...#.... ....#...
+ ..###... ...###.. ..###... ...###..
+ .##.##.. ..##.##. .#####.. ..#####.
+ ##...##. .##...## ###.###. ###.###.
+ #######. .####### #######. #######.
+ #######. #######. #######. #######.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00C3 - LATIN CAPITAL LETTER A WITH TILDE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .###.##. ...###.# .###.##. ..###.##
+ ##.###.. ..##.### ##.###.. .##.###.
+ ........ ........ ........ ........
+ ...#.... .....#.. ...#.... ....#...
+ ..###... ...###.. ..###... ...###..
+ .##.##.. ..##.##. .#####.. ..#####.
+ ##...##. .##...## ###.###. ###.###.
+ #######. .####### #######. #######.
+ #######. #######. #######. #######.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00C4 - LATIN CAPITAL LETTER A WITH DIAERESIS
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ .##.##.. ........ ........ ........
+ .##.##.. ...##.## ###.###. .###.###
+ ........ ...##.## ###.###. .###.###
+ ...#.... .....#.. ...#.... ....#...
+ ..###... ....###. ..###... ...###..
+ .##.##.. ..##.##. .#####.. ..#####.
+ ##...##. .##...## ###.###. .###.##.
+ #######. .####### #######. #######.
+ #######. .####### #######. #######.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00C5 - LATIN CAPITAL LETTER A WITH RING ABOVE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..###... ....###. ..###... ...###..
+ .#...#.. ...#...# .##.##.. ..##.##.
+ ..###... ....###. ..###... ...###..
+ ..###... ....###. ..###... ...###..
+ .##.##.. ..##.##. .#####.. ..#####.
+ ##...##. .##...## ###.###. .###.##.
+ #######. .####### #######. #######.
+ #######. .####### #######. #######.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00C6 - LATIN CAPITAL LETTER AE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ...##### .....### ...##### ....####
+ ..###### ....#### ..###### ...#####
+ .#####.. ...##### .####### ..######
+ ###.##.. ..###.## #######. .#######
+ ##..##.. .##..##. ###.###. .###.###
+ #######. .####### ######## .#######
+ #######. .####### ######## ########
+ ##..##.. .##..##. #######. #######.
+ ##..##.. ##..##.. ###.###. ###.###.
+ ##..##.. ##..##.. ###.#### ###.####
+ ##..#### ##..#### ###.#### ###.####
+ ##..#### ##..#### ###.#### ###.####
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00C7 - LATIN CAPITAL LETTER C WITH CEDILLA
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .#####.. ...####. .#####.. ..#####.
+ #######. ..###### #######. .#######
+ ##...##. ..##..## ###.###. .###.###
+ ##...... ..##.... ###..... .###....
+ ##...... .##..... ###..... .###....
+ ##...... .##..... ###..... .###....
+ ##...... .##..... ###..... ###.....
+ ##...... .##..... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .#####.. .#####.. .#####.. .#####..
+ ....###. ...###.. ....###. ....###.
+ .##..##. ##..##.. .##.###. .##.###.
+ ..####.. .####... .#####.. .#####..
+-----------------------------------------------------
+U+00C8 - LATIN CAPITAL LETTER E WITH GRAVE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..##.... ....##.. .###.... ..###...
+ ...##... .....##. ..###... ...###..
+ ........ ........ ........ ........
+ #######. ..###### #######. .#######
+ #######. .####### #######. .#######
+ ##...... .##..... ###..... .###....
+ ####.... .####... ###..... ###.....
+ ####.... .####... #####... #####...
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ #######. #######. #######. #######.
+ #######. #######. #######. #######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00C9 - LATIN CAPITAL LETTER E WITH ACUTE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ....##.. ......## ...###.. ....###.
+ ...##... .....##. ..###... ...###..
+ ........ ........ ........ ........
+ #######. ..###### #######. .#######
+ #######. .####### #######. .#######
+ ##...... .##..... ###..... .###....
+ ####.... .####... ###..... ###.....
+ ####.... .####... #####... #####...
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ #######. #######. #######. #######.
+ #######. #######. #######. #######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00CA - LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..###... ....###. .#####.. ..#####.
+ .##.##.. ...##.## ###.###. .###.###
+ ........ ........ ........ ........
+ #######. ..###### #######. .#######
+ #######. .####### #######. .#######
+ ##...... .##..... ###..... .###....
+ ####.... .####... ###..... ###.....
+ ####.... .####... #####... #####...
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ #######. #######. #######. #######.
+ #######. #######. #######. #######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00CB - LATIN CAPITAL LETTER E WITH DIAERESIS
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ .##.##.. ...##.## ###.###. .###.###
+ .##.##.. ...##.## ###.###. .###.###
+ ........ ........ ........ ........
+ #######. ..###### #######. .#######
+ #######. ..###### #######. .#######
+ ##...... .##..... ###..... .###....
+ ##...... .##..... ###..... .###....
+ ####.... .####... #####... #####...
+ ####.... .####... #####... #####...
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ #######. #######. #######. #######.
+ #######. #######. #######. #######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00CC - LATIN CAPITAL LETTER I WITH GRAVE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..##.... ....##.. .###.... ..###...
+ ...##... .....##. ..###... ...###..
+ ........ ........ ........ ........
+ ...##... ...##### ..###... ...###..
+ ...##... ...##### ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ..###...
+ ...##... ....##.. ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... .#####.. ..###... ..###...
+ ...##... .#####.. ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00CD - LATIN CAPITAL LETTER I WITH ACUTE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ....##.. ......## ...###.. ....###.
+ ...##... .....##. ..###... ...###..
+ ........ ........ ........ ........
+ ...##... ...##### ..###... ...###..
+ ...##... ...##### ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ..###...
+ ...##... ....##.. ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... .#####.. ..###... ..###...
+ ...##... .#####.. ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00CE - LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ...##... .....##. ..###... ...###..
+ ..#..#.. ....#..# .##.##.. ..##.##.
+ ...##... ...##### ..###... ...###..
+ ...##... ...##### ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ..###...
+ ...##... ....##.. ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... .#####.. ..###... ..###...
+ ...##... .#####.. ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00CF - LATIN CAPITAL LETTER I WITH DIAERESIS
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .##..##. ...##.## ###.###. .###.###
+ .##..##. ...##.## ###.###. .###.###
+ ........ ........ ........ ........
+ ...##... ...##### ..###... ...###..
+ ...##... ...##### ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ..###...
+ ...##... ....##.. ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... .#####.. ..###... ..###...
+ ...##... .#####.. ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00D0 - LATIN CAPITAL LETTER ETH
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .####... ...####. #####... .#####..
+ .#####.. ...##### ######.. .######.
+ .##.###. ...##.## ###.###. .###.###
+ .##..##. ..##..## ###.###. .###.###
+ .##..##. ..##..## ###.###. .###.##.
+ ####.##. .####.## #######. #######.
+ .##..##. ..##..## ###.###. ###.###.
+ .##..##. .##..##. ###.###. ###.###.
+ .##.###. .##.###. ###.###. ###.###.
+ .#####.. .#####.. ######.. ######..
+ .####... .####... #####... #####...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00D1 - LATIN CAPITAL LETTER N WITH TILDE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .###.##. ...###.# .###.##. ..###.##
+ ##.###.. ..##.### ##.###.. .##.###.
+ ........ ........ ........ ........
+ ##...##. ..##...# ###.###. .###.###
+ ##...##. .##...## ###.###. .###.###
+ ###..##. .###..## ###.###. .###.##.
+ ####.##. .####.## ####.##. ####.##.
+ #######. .####### #######. #######.
+ ##.####. ##.####. ##.####. ##.####.
+ ##..###. ##..###. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00D2 - LATIN CAPITAL LETTER O WITH GRAVE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..##.... ....##.. .###.... ..###...
+ ...##... .....##. ..###... ...###..
+ ........ ........ ........ ........
+ .#####.. ..#####. .#####.. ..#####.
+ #######. .####### #######. .#######
+ ##...##. .##...## ###.###. .###.##.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .#####.. .#####.. .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00D3 - LATIN CAPITAL LETTER O WITH ACUTE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ....##.. ......## ...###.. ....###.
+ ...##... .....##. ..###... ...###..
+ ........ ........ ........ ........
+ .#####.. ..#####. .#####.. ..#####.
+ #######. .####### #######. .#######
+ ##...##. .##...## ###.###. .###.##.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .#####.. .#####.. .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00D4 - LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..###... ....###. .#####.. ..#####.
+ .##.##.. ...##.## ###.###. .###.###
+ ........ ........ ........ ........
+ .#####.. ..#####. .#####.. ..#####.
+ #######. .####### #######. .#######
+ ##...##. .##...## ###.###. .###.##.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .#####.. .#####.. .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00D5 - LATIN CAPITAL LETTER O WITH TILDE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .###.##. ...###.# .###.##. ..###.##
+ ##.###.. ..##.### ##.###.. .##.###.
+ ........ ........ ........ ........
+ .#####.. ..#####. .#####.. ..#####.
+ #######. .####### #######. .#######
+ ##...##. .##...## ###.###. .###.##.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .#####.. .#####.. .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00D6 - LATIN CAPITAL LETTER O WITH DIAERESIS
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ###.###. .###.###
+ .##.##.. ...##.## ###.###. .###.###
+ ........ ...##.## ........ ........
+ .#####.. ........ .#####.. ..#####.
+ #######. ..#####. #######. .#######
+ ##...##. .####### ###.###. .###.###
+ ##...##. .##...## ###.###. .###.##.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .#####.. .#####.. .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00D7 - MULTIPLICATION SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ##...##. .##...## ##...##. .##...##
+ ###.###. .###.### ###.###. .###.##.
+ .#####.. ..#####. .#####.. .#####..
+ ..###... ...###.. ..###... ..###...
+ ..###... ..###... ..###... ..###...
+ .#####.. .#####.. .#####.. .#####..
+ ###.###. ###.###. ###.###. ###.###.
+ ##...##. ##...##. ##...##. ##...##.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00D8 - LATIN CAPITAL LETTER O WITH STROKE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .######. ...###.# .####.#. ..####.#
+ #######. ..#####. ######.. .######.
+ ##...##. ..##.### ###.###. .###.###
+ ##...##. .##.#.## ###.###. .###.###
+ ##..###. .##.#.## ###.###. .###.###
+ ##..###. .##.#.## #######. .######.
+ ##.#.##. ##.#.##. #######. #######.
+ ##.#.##. ##.#.##. #######. #######.
+ ###..##. ##.#.##. ###.###. ###.###.
+ ###..##. ###.###. ###.###. ###.###.
+ #######. .#####.. #######. #######.
+ .#####.. #.###... .#####.. .#####..
+ #....... ........ #....... #.......
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00D9 - LATIN CAPITAL LETTER U WITH GRAVE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..##.... ....##.. .###.... ..###...
+ ...##... .....##. ..###... ...###..
+ ........ ........ ........ ........
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.##.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##..###. ###.###. ###.###.
+ #######. ######.. #######. #######.
+ .#####.. .####... .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00DA - LATIN CAPITAL LETTER U WITH ACUTE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ....##.. ......## ...###.. ....###.
+ ...##... .....##. ..###... ...###..
+ ........ ........ ........ ........
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.##.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##..###. ###.###. ###.###.
+ #######. ######.. #######. #######.
+ .#####.. .####... .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00DB - LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..###... ....###. .#####.. ..#####.
+ .##.##.. ...##.## ###.###. .###.###
+ ........ ........ ........ ........
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.##.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##..###. ###.###. ###.###.
+ #######. ######.. #######. #######.
+ .#####.. .####... .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00DC - LATIN CAPITAL LETTER U WITH DIAERESIS
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ .##.##.. ...##.## ###.###. .###.###
+ .##.##.. ...##.## ###.###. .###.###
+ ........ ........ ........ ........
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.##.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##..###. ###.###. ###.###.
+ #######. ######.. #######. #######.
+ .#####.. .####... .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00DD - LATIN CAPITAL LETTER Y WITH ACUTE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ....##.. .....##. ...###.. ....###.
+ ...##... ....##.. ..###... ...###..
+ ........ ........ ........ ........
+ .##..##. ..##..## ###.###. .###.###
+ .##..##. ..##..## ###.###. .###.###
+ .##..##. .##..##. ###.###. .###.##.
+ .######. .##.###. ###.###. ###.###.
+ ..####.. .#####.. .#####.. .#####..
+ ...##... ..###... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ ...##... .##..... ..###... ..###...
+ ...##... .##..... ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00DE - LATIN CAPITAL LETTER THORN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .##..... ...##... ###..... .###....
+ .##..... ...##... ###..... .###....
+ .#####.. ...##### ######.. .######.
+ .######. ..###### #######. .#######
+ .##..##. ..##..## ###.###. .###.##.
+ .##..##. ..##..## ###.###. ###.###.
+ .######. ..###### ###.###. ###.###.
+ .#####.. .#####.. #######. #######.
+ .##..... .##..... ######.. ######..
+ .##..... .##..... ###..... ###.....
+ .##..... .##..... ###..... ###.....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00DF - LATIN SMALL LETTER SHARP S
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..####.. ....###. .#####.. ..#####.
+ .######. ...##### #######. .#######
+ .##..##. ...##.## ###.###. .###.###
+ .##..##. ...##.## ###.###. .###.###
+ .##..##. ..##..## ###.###. .###.###
+ .######. ..###### #######. .######.
+ .#####.. ..#####. ######.. ######..
+ .##..##. ..##..## ###.###. ###.###.
+ .##..##. .##..##. ###.###. ###.###.
+ .##..##. .##..##. ###.###. ###.###.
+ .######. .######. #######. #######.
+ .#####.. .#####.. ######.. ######..
+ ###..... ##...... ###..... ###.....
+ ##...... #....... ##...... ##......
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00E0 - LATIN SMALL LETTER A WITH GRAVE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ..##.... ....##.. .###.... .###....
+ ...##... .....##. ..###... ..###...
+ ........ ........ ........ ........
+ .######. ..###### .#####.. ..####..
+ #######. .####### .######. ..#####.
+ ##...##. .##...## ....###. ....###.
+ ##...##. .##...## .######. ..#####.
+ ##..###. ##..###. #######. .######.
+ ##..###. ##..###. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .###.##. .###.##. .######. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00E1 - LATIN SMALL LETTER A WITH ACUTE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... .....##. ...###.. ...###..
+ ..##.... ....##.. ..###... ..###...
+ ........ ........ ........ ........
+ .######. ..###### .#####.. ..####..
+ #######. .####### .######. ..#####.
+ ##...##. .##...## ....###. ....###.
+ ##...##. .##...## .######. ..#####.
+ ##...##. ##...##. #######. .######.
+ ##..###. ##..###. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .###.##. .###.##. .######. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00E2 - LATIN SMALL LETTER A WITH CIRCUMFLEX
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ...#.... .....#.. ..###... ..###...
+ ..###... ....###. .#####.. .#####..
+ .##.##.. ...##.## ###.###. ###.###.
+ ........ ........ ........ ........
+ .######. ..###### .#####.. ..####..
+ #######. .####### .######. ..#####.
+ ##...##. .##...## ....###. ....###.
+ ##...##. .##...## .######. ..#####.
+ ##...##. ##...##. #######. .######.
+ ##..###. ##..###. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .###.##. .###.##. .######. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00E3 - LATIN SMALL LETTER A WITH TILDE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .###.##. ...###.# .###.##. .###.##.
+ ##.###.. ..##.### ##.###.. ##.###..
+ ........ ........ ........ ........
+ .######. ..###### .#####.. ..####..
+ #######. .####### .######. ..#####.
+ ##...##. .##...## ....###. ....###.
+ ##...##. .##...## .######. ..#####.
+ ##...##. ##...##. #######. .######.
+ ##..###. ##..###. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .###.##. .###.##. .######. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00E4 - LATIN SMALL LETTER A WITH DIAERESIS
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .##.##.. ...##.## ###.###. ###.###.
+ .##.##.. ...##.## ###.###. ###.###.
+ ........ ........ ........ ........
+ .######. ..###### .#####.. ..####..
+ #######. .####### .######. ..#####.
+ ##...##. .##...## ....###. ....###.
+ ##...##. .##...## .######. ..#####.
+ ##...##. ##...##. #######. .######.
+ ##..###. ##..###. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .###.##. .###.##. .######. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00E5 - LATIN SMALL LETTER A WITH RING ABOVE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..###... ....###. ..###... ..###...
+ .##.##.. ...##.## .##.##.. .##.##..
+ ..###... ....###. ..###... ..###...
+ ........ ........ ........ ........
+ .######. ..#####. .#####.. ..####..
+ #######. .####### .######. ..#####.
+ ##...##. .##...## ....###. ....###.
+ ##...##. .##...## .######. ..#####.
+ ##...##. ##...##. #######. .######.
+ ##..###. ##..###. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .###.##. .###.##. .######. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00E6 - LATIN SMALL LETTER AE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ .###.#.. ..###.#.
+ .###.#.. ..###.#. .######. ..######
+ .######. ..###### .######. ..######
+ ...##.#. ....##.# ...##.#. ...##.#.
+ ..#####. ...##### .######. .######.
+ .######. .######. #######. #######.
+ ##.##... ##.##... ##.##... ##.##...
+ #######. #######. #######. #######.
+ .#####.. .#####.. .######. .######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00E7 - LATIN SMALL LETTER C WITH CEDILLA
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .#####.. ..#####. .#####.. ..#####.
+ #######. .####### #######. #######.
+ ##...##. .##...## ###.###. ###.###.
+ ##...... ##...... ###..... ###.....
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .#####.. .#####.. .#####.. .#####..
+ ...##... ..##.... ...###.. ...###..
+ .#####.. #####... .######. .######.
+ .####... ####.... .#####.. .#####..
+-----------------------------------------------------
+U+00E8 - LATIN SMALL LETTER E WITH GRAVE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ..##.... ....##.. .###.... ..###...
+ ...##... .....##. ..###... ...###..
+ ........ ........ ........ ........
+ .#####.. ..#####. .#####.. ..#####.
+ #######. .####### #######. .#######
+ ##...##. .##...## ###.###. ###.###.
+ #######. .####### #######. #######.
+ #######. #######. #######. #######.
+ ##...... ##...... ###..... ###.....
+ #######. #######. #######. #######.
+ .######. .######. .######. .######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00E9 - LATIN SMALL LETTER E WITH ACUTE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... .....##. ..###... ...###..
+ ..##.... ....##.. .###.... ..###...
+ ........ ........ ........ ........
+ .#####.. ..#####. .#####.. ..#####.
+ #######. .####### #######. .#######
+ ##...##. .##...## ###.###. ###.###.
+ #######. .####### #######. #######.
+ #######. #######. #######. #######.
+ ##...... ##...... ###..... ###.....
+ #######. #######. #######. #######.
+ .######. .######. .######. .######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0118 - LATIN CAPITAL LETTER E WITH OGONEK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ #######. ..###### #######. .#######
+ #######. ..###### #######. .#######
+ ##...... ..##.... ###..... .###....
+ ##...... ..##.... ###..... .###....
+ ##...... .##..... ###..... .###....
+ #####... .#####.. #####... .#####..
+ #####... .#####.. #####... #####...
+ ##...... .##..... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ #######. #######. #######. #######.
+ #######. #######. #######. #######.
+ ....##.. ...##... ....###. ...###..
+ ...##... .###.... ..###... .###....
+ ....###. ..###... ...####. .####...
+-----------------------------------------------------
+U+0119 - LATIN SMALL LETTER E WITH OGONEK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .#####.. ..####.. .#####.. ..####..
+ #######. .######. #######. .######.
+ ##...##. .##..##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ #######. ######.. #######. ######..
+ ##...... ##...... ###..... ###.....
+ #######. ######.. #######. ######..
+ .######. .#####.. .######. .#####..
+ ....##.. ...##... ....###. ...###..
+ ...##... .###.... ..###... .###....
+ ....###. ..###... ...####. .####...
+-----------------------------------------------------
+U+00EA - LATIN SMALL LETTER E WITH CIRCUMFLEX
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ...#.... .....#.. ..###... ...###..
+ ..###... ....###. .#####.. ..#####.
+ .##.##.. ...##.## ###.###. .###.###
+ ........ ........ ........ ........
+ .#####.. ..#####. .#####.. ..#####.
+ #######. .####### #######. .#######
+ ##...##. .##...## ###.###. ###.###.
+ #######. .####### #######. #######.
+ #######. #######. #######. #######.
+ ##...... ##...... ###..... ###.....
+ #######. #######. #######. #######.
+ .######. .######. .######. .######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00EB - LATIN SMALL LETTER E WITH DIAERESIS
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .##.##.. ...##.## ###.###. .###.###
+ .##.##.. ...##.## ###.###. .###.###
+ ........ ........ ........ ........
+ .#####.. ..#####. .#####.. ..#####.
+ #######. .####### #######. .#######
+ ##...##. .##...## ###.###. ###.###.
+ #######. .####### #######. #######.
+ #######. #######. #######. #######.
+ ##...... ##...... ###..... ###.....
+ #######. #######. #######. #######.
+ .######. .######. .######. .######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00EC - LATIN SMALL LETTER I WITH GRAVE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ..##.... ....##.. .###.... ..###...
+ ...##... .....##. ..###... ...###..
+ ........ ........ ........ ........
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ..###...
+ ...##... ....##.. ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00ED - LATIN SMALL LETTER I WITH ACUTE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ....##.. ......## ...###.. ....###.
+ ...##... .....##. ..###... ...###..
+ ........ ........ ........ ........
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ..###...
+ ...##... ....##.. ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00EE - LATIN SMALL LETTER I WITH CIRCUMFLEX
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ...##... .....##. ..###... ...###..
+ ..####.. ....#### .#####.. ..#####.
+ .##..##. ...##..# ###.###. .###.###
+ ........ ........ ........ ........
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ..###...
+ ...##... ....##.. ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00EF - LATIN SMALL LETTER I WITH DIAERESIS
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .##..##. ...##.## ###.###. .###.###
+ .##..##. ...##.## ###.###. .###.###
+ ........ ........ ........ ........
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ..###...
+ ...##... ....##.. ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00F0 - LATIN SMALL LETTER ETH
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .##.##.. ...##.## ###.##.. .###.##.
+ .#####.. ...##### .#####.. ..#####.
+ #####... ..#####. #####... .#####..
+ ##.###.. ..##.### ######.. .######.
+ .#####.. ..#####. .######. ..######
+ #######. .####### #######. .######.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .#####.. .#####.. .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00F1 - LATIN SMALL LETTER N WITH TILDE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .###.##. ...###.# .###.##. ..###.##
+ #######. ..###### #######. .#######
+ ##.###.. ..##.### ##.###.. .##.###.
+ ........ ........ ........ ........
+ ######.. .######. ######.. .######.
+ #######. .####### #######. .#######
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00F2 - LATIN SMALL LETTER O WITH GRAVE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .##..... ...##... ###..... .###....
+ ..##.... ....##.. .###.... ..###...
+ ........ ........ ........ ........
+ .#####.. ..#####. .#####.. ..####..
+ #######. .####### #######. .######.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. ######..
+ .#####.. .#####.. .#####.. .####...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00F3 - LATIN SMALL LETTER O WITH ACUTE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... .....##. ..###... ...###..
+ ..##.... ....##.. .###.... ..###...
+ ........ ........ ........ ........
+ .#####.. ..#####. .#####.. ..####..
+ #######. .####### #######. .######.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. ######..
+ .#####.. .#####.. .#####.. .####...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00F4 - LATIN SMALL LETTER O WITH CIRCUMFLEX
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ...#.... .....#.. ..###... ...###..
+ ..###... ....###. .#####.. ..#####.
+ .##.##.. ...##.## ###.###. .###.###
+ ........ ........ ........ ........
+ .#####.. ..#####. .#####.. ..####..
+ #######. .####### #######. .######.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. ######..
+ .#####.. .#####.. .#####.. .####...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00F5 - LATIN SMALL LETTER O WITH TILDE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .###.##. ...###.# .###.##. ..###.##
+ #######. ..###### #######. .#######
+ ##.###.. ..##.### ##.###.. .##.###.
+ ........ ........ ........ ........
+ .#####.. ..#####. .#####.. ..####..
+ #######. .####### #######. .######.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. ######..
+ .#####.. .#####.. .#####.. .####...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00F6 - LATIN SMALL LETTER O WITH DIAERESIS
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .##.##.. ...##.## ###.###. .###.###
+ .##.##.. ...##.## ###.###. .###.###
+ ........ ........ ........ ........
+ .#####.. ..#####. .#####.. ..####..
+ #######. .####### #######. .######.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. ######..
+ .#####.. .#####.. .#####.. .####...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00F7 - DIVISION SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ........ ........ ........ ........
+ .######. ..###### #######. #######.
+ .######. .######. #######. #######.
+ ........ ........ ........ ........
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00F8 - LATIN SMALL LETTER O WITH STROKE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ......#. ........ ......#. .......#
+ .#####.. ..#####. .#####.. ..#####.
+ #######. .####### #######. .######.
+ ##..###. .##..### ###.###. ###.###.
+ ##.#.##. .##.#.## ###.###. ###.###.
+ ##.#.##. ##.#.##. ###.###. ###.###.
+ ###..##. ###..##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .#####.. .#####.. .#####.. .#####..
+ #....... ........ #....... #.......
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00F9 - LATIN SMALL LETTER U WITH GRAVE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .##..... ...##... ###..... .###....
+ ..##.... ....##.. .###.... ..###...
+ ........ ........ ........ ........
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.##.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .######. .######. .######. .######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00FA - LATIN SMALL LETTER U WITH ACUTE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... .....##. ..###... ...###..
+ ..##.... ....##.. .###.... ..###...
+ ........ ........ ........ ........
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.##.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .######. .######. .######. .######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00FB - LATIN SMALL LETTER U WITH CIRCUMFLEX
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ...#.... .....#.. ..###... ...###..
+ ..###... ....###. .#####.. ..#####.
+ .##.##.. ...##.## ###.###. .###.###
+ ........ ........ ........ ........
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.##.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .######. .######. .######. .######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00FC - LATIN SMALL LETTER U WITH DIAERESIS
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .##.##.. ...##.## ###.###. .###.###
+ .##.##.. ...##.## ###.###. .###.###
+ ........ ........ ........ ........
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.##.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .######. .######. .######. .######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+00FD - LATIN SMALL LETTER Y WITH ACUTE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ....##.. ......## ...###.. ....###.
+ ...##... .....##. ..###... ...###..
+ ........ ........ ........ ........
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.##.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .######. .######. .######. .######.
+ .....##. ....##.. ....###. ....###.
+ ######.. #####... #######. #######.
+ #####... ####.... ######.. ######..
+-----------------------------------------------------
+U+00FE - LATIN SMALL LETTER THORN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .##..... ...##... ###..... .###....
+ .##..... ...##... ###..... .###....
+ .##..... ...##... ######.. .######.
+ .#####.. ..#####. #######. .#######
+ .######. ..###### ###.###. .###.##.
+ .##..##. ..##..## ###.###. ###.###.
+ .##..##. ..##..## ###.###. ###.###.
+ .##..##. .##..##. ###.###. ###.###.
+ .##..##. .##..##. ###.###. ###.###.
+ .######. .######. ###.###. ###.###.
+ .#####.. .#####.. #######. #######.
+ .##..... ##...... ######.. ######..
+ .##..... ##...... ###..... ###.....
+ .##..... ##...... ###..... ###.....
+-----------------------------------------------------
+U+00FF - LATIN SMALL LETTER Y WITH DIAERESIS
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .##.##.. ........ ###.###. .###.###
+ .##.##.. ...##.## ###.###. .###.###
+ ........ ........ ........ ........
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.##.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .######. .######. .######. .######.
+ .....##. ....##.. ....###. ....###.
+ #######. #####... #######. #######.
+ ######.. ####.... ######.. ######..
+-----------------------------------------------------
+U+0102 - LATIN CAPITAL LETTER A WITH BREVE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ##...##. .##...## ###.###. .###.###
+ #######. .######. #######. .######.
+ ........ ........ ........ ........
+ ...#.... .....#.. ...#.... ....#...
+ ..###... ...###.. ..###... ...###..
+ .##.##.. ..##.##. .#####.. ..#####.
+ ##...##. .##...## ###.###. ###.###.
+ #######. .####### #######. #######.
+ #######. #######. #######. #######.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0103 - LATIN SMALL LETTER A WITH BREVE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##..##. ###.###. .#######
+ .#####.. ..####.. .#####.. ..####..
+ ........ ........ ........ ........
+ .######. ..###### .#####.. ..####..
+ #######. .####### .######. ..#####.
+ ##...##. .##...## ....###. ....###.
+ ##...##. .##...## .######. ..#####.
+ ##...##. ##...##. #######. .######.
+ ##..###. ##..###. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .###.##. .###.##. .######. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0104 - LATIN CAPITAL LETTER A WITH OGONEK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .#####.. ...####. .#####.. ..#####.
+ #######. ..###### #######. .#######
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. ..##..## ###.###. .###.###
+ ##...##. .##..##. ###.###. .###.###
+ #######. .######. ###.###. .###.##.
+ #######. .######. #######. #######.
+ ##...##. .##..##. #######. #######.
+ ##...##. ##..##.. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ....##.. ...##... ....###. ...###..
+ ...##... .###.... ..###... .###....
+ ....###. ..###... ...####. .####...
+-----------------------------------------------------
+U+0105 - LATIN SMALL LETTER A WITH OGONEK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .######. ..###### .#####.. ..####..
+ #######. .####### .######. ..#####.
+ ##...##. .##...## ....###. ....###.
+ ##...##. .##...## .######. ..#####.
+ ##...##. ##...##. #######. .######.
+ ##..###. ##..###. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .###.##. .###.##. .######. .#####..
+ ....##.. ...##... ....###. ...###..
+ ...##... .###.... ..###... .###....
+ ....###. ..###... ...####. .####...
+-----------------------------------------------------
+U+0106 - LATIN CAPITAL LETTER C WITH ACUTE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ....##.. ......## ...###.. ....###.
+ ...##... .....##. ..###... ...###..
+ ........ ........ ........ ........
+ .#####.. ...####. .#####.. ..#####.
+ #######. ..###### #######. .#######
+ ##...##. ..##..## ###.###. .###.###
+ ##...... .##..... ###.###. .###.###
+ ##...... .##..... ###..... .###....
+ ##...... .##..... ###..... ###.....
+ ##...... .##..... ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .#####.. .#####.. .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0107 - LATIN SMALL LETTER C WITH ACUTE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ....##.. ......## ...###.. ....###.
+ ...##... .....##. ..###... ...###..
+ ........ ........ ........ ........
+ .#####.. ..####.. .#####.. ..####..
+ #######. .######. #######. .######.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ ##...##. ##..##.. ###.###. ###.###.
+ #######. ######.. #######. #######.
+ .#####.. .####... .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+010C - LATIN CAPITAL LETTER C WITH CARON
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ##...##. .##...## ###.###. .###.###
+ .#####.. ..#####. .#####.. ..#####.
+ ........ ........ ........ ........
+ .#####.. ...####. .#####.. ..#####.
+ #######. ..###### #######. .#######
+ ##...##. ..##..## ###.###. .###.###
+ ##...... .##..... ###.###. .###.###
+ ##...... .##..... ###..... .###....
+ ##...... .##..... ###..... ###.....
+ ##...... .##..... ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ #######. #######. #######. #######.
+ .#####.. .#####.. .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+010D - LATIN SMALL LETTER C WITH CARON
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ##...##. .##...## ###.###. .###.###
+ .##.##.. ..##.##. .#####.. ..#####.
+ ..###... ...###.. ..###... ...###..
+ ........ ........ ........ ........
+ .#####.. ..####.. .#####.. ..####..
+ #######. .######. #######. .######.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ ##...##. ##..##.. ###.###. ###.###.
+ #######. ######.. #######. #######.
+ .#####.. .####... .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0141 - LATIN CAPITAL LETTER L WITH STROKE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .##..... ...##... .###.... ..###...
+ .##..... ...##... .###.... ..###...
+ .##..... ...##... .###.... ..###.#.
+ .##.#... ...##.#. .###.##. ..#####.
+ .####... ..#####. .######. ..####..
+ .###.... ..####.. .#####.. ..###...
+ ###..... .###.... ####.... ####....
+ ###..... .###.... ####.... ####....
+ .##..... .##..... .###.... .###....
+ .##..... .##..... .###.... .###....
+ .######. .######. .######. .######.
+ .######. .######. .######. .######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0142 - LATIN SMALL LETTER L WITH STROKE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..###... ....###. .####... ..####..
+ ..###... ....###. .####... ..####..
+ ...##... .....##. ..###... ...###.#
+ ...##.#. .....### ..#####. ...#####
+ ...####. ....#### ..#####. ..#####.
+ ..####.. ...####. .#####.. .#####..
+ .####... ..####.. .####... #####...
+ .#.##... ..#.##.. .####... #.###...
+ ...##... ...##... ..###... .###....
+ ...##... ...##... ..###... .###....
+ ...##... ...##... ..###... .###....
+ ...##... ...##... ..###... .###....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0143 - LATIN CAPITAL LETTER N WITH ACUTE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ....##.. ......## ...###.. ....###.
+ ...##... .....##. ..###... ...###..
+ ........ ........ ........ ........
+ ##...##. ..##...# ###.###. .###.###
+ ##...##. .##...## ###.###. .###.###
+ ###..##. .###..## ###.###. .###.##.
+ ####.##. .####.## ####.##. ####.##.
+ #######. .####### #######. #######.
+ ##.####. ##.####. ##.####. ##.####.
+ ##..###. ##..###. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0144 - LATIN SMALL LETTER N WITH ACUTE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ....##.. ......## ...###.. ....###.
+ ...##... .....##. ..###... ...###..
+ ........ ........ ........ ........
+ ######.. .######. ######.. .######.
+ #######. .####### #######. .#######
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+014D - LATIN SMALL LETTER O WITH MACRON
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .######. ...##### #######. ..######
+ .######. ..#####. #######. .######.
+ ........ ........ ........ ........
+ .#####.. ..####.. .#####.. ..####..
+ #######. .######. #######. .######.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. .##..##. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ ##...##. ##..##.. ###.###. ###.###.
+ #######. ######.. #######. ######..
+ .#####.. .####... .#####.. .####...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0152 - LATIN CAPITAL LIGATURE OE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .####### ...##### .####### ..######
+ ######## ..###### ######## .#######
+ ##..##.. ..##..## ###.###. .###.###
+ ##..##.. ..##..## ###.###. .###.###
+ ##..##.. .##..##. ###.###. .###.###
+ ##..#### .##..### ###.#### .###.###
+ ##..#### .##..### ###.#### ###.####
+ ##..##.. .##..##. ###.###. ###.###.
+ ##..##.. ##..##.. ###.###. ###.###.
+ ##..##.. ##..##.. ###.###. ###.###.
+ ######## ######## ######## ########
+ .####### .####### .####### .#######
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0153 - LATIN SMALL LIGATURE OE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .##.##.. ..##.##. .##.##.. ..##.##.
+ #######. .####### #######. .#######
+ ##.##.#. .##.##.# #####.#. #####.#.
+ ##.####. .##.#### ##.####. ##.####.
+ ##.####. ##.####. ##.####. ##.####.
+ ##.##... ##.##... ######.. ######..
+ #######. #######. #######. #######.
+ .##.###. .##.###. .##.###. .##.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+015A - LATIN CAPITAL LETTER S WITH ACUTE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ....##.. ......## ...###.. ....###.
+ ...##... .....##. ..###... ...###..
+ ........ ........ ........ ........
+ .#####.. ...####. .#####.. ..#####.
+ #######. ..###### #######. .#######
+ ##...##. ..##..## ###.###. .###.###
+ ##...... .##..... ###..... .###....
+ ######.. .#####.. ######.. .#####..
+ .######. ..#####. .######. ..#####.
+ .....##. .....##. ....###. ....###.
+ ##...##. ##..###. ###.###. ###.###.
+ #######. ######.. #######. #######.
+ .#####.. .####... .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+015B - LATIN SMALL LETTER S WITH ACUTE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... .....##. ...###.. ...###..
+ ..##.... ....##.. ..###... ..###...
+ ........ ........ ........ ........
+ .######. ..###### .######. ...#####
+ #######. .####### #######. ..#####.
+ ##...... .##..... ###..... .###....
+ ######.. .#####.. ######.. .#####..
+ .#####.. ..#####. .######. ..#####.
+ .....##. .....##. ....###. ....###.
+ #######. #######. #######. .#####..
+ ######.. ######.. ######.. #####...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0160 LATIN CAPITAL LETTER S WITH CARON
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ##...##. .##...## ###.###. .###.###
+ .#####.. ..#####. .#####.. ..#####.
+ ........ ........ ........ ........
+ .#####.. ...####. .#####.. ..#####.
+ #######. ..###### #######. .#######
+ ##...##. ..##..## ###.###. .###.###
+ ##...... .##..... ###..... .###....
+ ######.. .#####.. ######.. .#####..
+ .######. ..#####. .######. ..#####.
+ .....##. .....##. ....###. ....###.
+ ##...##. ##..###. ###.###. ###.###.
+ #######. ######.. #######. #######.
+ .#####.. .####... .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0161 - LATIN SMALL LETTER S WITH CARON
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ##...##. .##...## ###.###. .###.###
+ .##.##.. ..##.##. .#####.. ..#####.
+ ..###... ...###.. ..###... ...###..
+ ........ ........ ........ ........
+ .######. ..###### .######. ...#####
+ #######. .####### #######. ..#####.
+ ##...... .##..... ###..... .###....
+ ######.. .#####.. ######.. .#####..
+ .#####.. ..#####. .######. ..#####.
+ .....##. .....##. ....###. ....###.
+ #######. #######. #######. .#####..
+ ######.. ######.. ######.. #####...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0174 - LATIN CAPITAL LETTER W WITH CIRCUMFLEX
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..###... ........ ........ ........
+ .##.##.. ....###. .#####.. ..#####.
+ ##...##. ...##.## ###.###. .###.###
+ ........ ........ ........ ........
+ ##...##. ..##...# ###.###. .###.###
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##...## ###.###. ###.###.
+ ##.#.##. .##.#.## #######. #######.
+ ##.#.##. ##.#.##. #######. #######.
+ #######. #######. #######. #######.
+ ###.###. ###.###. ###.###. ###.###.
+ .#...#.. .#...#.. .#...#.. .#...#..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0175 - LATIN SMALL LETTER W WITH CIRCUMFLEX
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ...#.... ....#...
+ ...#.... .....#.. ..###... ...###..
+ ..###... ....###. .#####.. ..#####.
+ .##.##.. ...##.## ###.###. .###.###
+ ........ ........ ........ ........
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##.#.##. ##.#.##. #######. #######.
+ ##.#.##. ##.#.##. #######. #######.
+ #######. #######. #######. #######.
+ .##.##.. .##.##.. .##.##.. .##.##..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0176 - LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##... ........ ........ ........
+ ..####.. ....#### .#####.. ..#####.
+ .##..##. ...##..# ###.###. .###.###
+ ........ ........ ........ ........
+ .##..##. ...##.## ###.###. .###.###
+ .##..##. ..##..## ###.###. .###.###
+ .##..##. ..##..## ###.###. .###.###
+ ..####.. ...####. .#####.. .#####..
+ ...##... ....##.. ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0177 - LATIN SMALL LETTER Y WITH CIRCUMFLEX
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ...#.... ....#...
+ ...#.... .....#.. ..###... ...###..
+ ..###... ....###. .#####.. ..#####.
+ .##.##.. ...##.## ###.###. .###.###
+ ........ ........ ........ ........
+ ##...##. .##...## ###.###. .###.###
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. .##...## ###.###. ###.###.
+ ##...##. ##...##. ###.###. ###.###.
+ ##...##. ##...##. #######. #######.
+ #######. ##...##. #######. #######.
+ .######. .######. .######. .######.
+ .....##. ....##.. .....##. .....##.
+ #######. #####... #######. #######.
+ ######.. ####.... ######.. ######..
+-----------------------------------------------------
+U+0178 - LATIN CAPITAL LETTER Y WITH DIAERESIS
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .##.##.. ..##..## ###.###. ..###.##
+ ........ ........ ........ ........
+ ##...##. .##..##. ###.###. .###.###
+ ##...##. .##..##. ###.###. .###.###
+ ##...##. .##..##. ###.###. .###.###
+ ###.###. ..####.. ###.###. .###.###
+ .#####.. ..####.. .#####.. ..#####.
+ ..###... ...##... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ ...##... .##..... ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0179 - LATIN CAPITAL LETTER Z WITH ACUTE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ....##.. ......## ...###.. ....###.
+ ...##... .....##. ..###... ...###..
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ #######. .####### #######. .#######
+ .....##. ......## ....###. .....###
+ ....##.. .....### ...####. ....###.
+ ...##... ....###. ..####.. ...###..
+ ..##.... ...##... .####... ..###...
+ .##..... .###.... ####.... .###....
+ ##...... ###..... ###..... ###.....
+ #######. #######. #######. #######.
+ #######. #######. #######. #######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+017A - LATIN SMALL LETTER Z WITH ACUTE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... .....##. ...###.. ...###..
+ ..##.... ....##.. ..###... ..###...
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ #######. .####### #######. .######.
+ ....###. .....### ...####. ...####.
+ ..####.. ...####. ..####.. ..####..
+ .####... .####... .####... .####...
+ ###..... ###..... ####.... ####....
+ #######. #######. #######. #######.
+ #######. #######. #######. #######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+017B - LATIN CAPITAL LETTER Z WITH DOT ABOVE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##... ....##.. ...##... ....###.
+ ...##... ....##.. ...##... ....###.
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ #######. .####### #######. .#######
+ .....##. ......## ....###. .....###
+ ....##.. .....### ...####. ....###.
+ ...##... ....###. ..####.. ...###..
+ ..##.... ...##... .####... ..###...
+ .##..... .###.... ####.... .###....
+ ##...... ###..... ###..... ###.....
+ #######. #######. #######. #######.
+ #######. #######. #######. #######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+017C - LATIN SMALL LETTER Z WITH DOT ABOVE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... ....##.. ...##... ....###.
+ ...##... ....##.. ...##... ....###.
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ #######. .####### #######. .######.
+ ....###. .....### ...####. ...####.
+ ..####.. ...####. ..####.. ..####..
+ .####... .####... .####... .####...
+ ###..... ###..... ####.... ####....
+ #######. #######. #######. #######.
+ #######. #######. #######. #######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+017D - LATIN CAPITAL LETTER Z WITH CARON
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ##...##. .##...## ###.###. .###.###
+ .#####.. ..#####. .#####.. ..#####.
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ #######. .####### #######. .#######
+ .....##. ......## ....###. .....###
+ ....##.. .....### ...####. ....###.
+ ...##... ....###. ..####.. ...###..
+ ..##.... ...##... .####... ..###...
+ .##..... .###.... ####.... .###....
+ ##...... ###..... ###..... ###.....
+ #######. #######. #######. #######.
+ #######. #######. #######. #######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+017E - LATIN SMALL LETTER Z WITH CARON
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ##...##. .##...## ###.###. .###.###
+ .##.##.. ..##.##. .#####.. ..#####.
+ ..###... ...###.. ..###... ...###..
+ ........ ........ ........ ........
+ #######. .####### #######. .#######
+ #######. .####### #######. .######.
+ ....###. .....### ...####. ...####.
+ ..####.. ...####. ..####.. ..####..
+ .####... .####... .####... .####...
+ ###..... ###..... ####.... ####....
+ #######. #######. #######. #######.
+ #######. #######. #######. #######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+0192 - LATIN SMALL LETTER F WITH HOOK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ...####. .....### ..#####. ...#####
+ ..#####. ....#### .######. ..######
+ ..##.... ....##.. .###.... ..###...
+ ..##.... ...##... .###.... ..###...
+ ..##.... .######. .###.... .###....
+ .####... .######. #####... .#####..
+ .####... ..##.... #####... .#####..
+ ..##.... ..##.... .###.... .###....
+ ..##.... ..##.... .###.... ###.....
+ ..##.... .##..... .###.... ###.....
+ ..##.... .##..... .###.... ###.....
+ ..##.... .##..... .###.... ###.....
+ ####.... ##...... ####.... ##......
+ .##..... #....... ###..... ##......
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+02C6 - MODIFIER LETTER CIRCUMFLEX ACCENT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ .#####.. ..#####. .#####.. ..#####.
+ ##...##. .##...## ###.###. .###.###
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+02DC - SMALL TILDE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..##..#. ...##..# .####.## ..###.##
+ .#..##.. ..#..##. ##.####. .##.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+1EC6 - LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..###... ....###. .#####.. ..#####.
+ .##.##.. ...##.## ###.###. .###.###
+ ........ ........ ........ ........
+ #######. ..###### #######. .#######
+ #######. .####### #######. .#######
+ ##...... .##..... ###..... .###....
+ ####.... .####... ###..... ###.....
+ ####.... .####... #####... #####...
+ ##...... ##...... ###..... ###.....
+ ##...... ##...... ###..... ###.....
+ #######. #######. #######. #######.
+ #######. #######. #######. #######.
+ ........ ........ ........ ........
+ ...##... ..##.... ...##... ..###...
+ ...##... ..##.... ...##... ..###...
+-----------------------------------------------------
+U+1EC7 - LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ...#.... .....#.. ..###... ...###..
+ ..###... ....###. .#####.. ..#####.
+ .##.##.. ...##.## ###.###. .###.###
+ ........ ........ ........ ........
+ .#####.. ..#####. .#####.. ..#####.
+ #######. .####### #######. .#######
+ ##...##. .##...## ###.###. ###.###.
+ #######. .####### #######. #######.
+ #######. #######. #######. #######.
+ ##...... ##...... ###..... ###.....
+ #######. #######. #######. #######.
+ .######. .######. .######. .######.
+ ........ ........ ........ ........
+ ...##... ..##.... ...##... ..###...
+ ...##... ..##.... ...##... ..###...
+-----------------------------------------------------
+U+2013 - EN DASH
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ..####.. ...####. ..####.. ..####..
+ ........ ........ ..####.. ..####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+2014 - EM DASH
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ #######. .####### #######. #######.
+ ........ ........ #######. #######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+2018 - LEFT SINGLE QUOTATION MARK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...#.... .....#.. ...##... ....##..
+ ..#..... ....#... ..##.... ...##...
+ ..##.... ...#.... ..##.... ...##...
+ ..##.... ...##... ..##.... ...##...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+2019 - RIGHT SINGLE QUOTATION MARK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ..##.... ....##.. ..##.... ...##...
+ ..##.... .....#.. ..##.... ...##...
+ ...#.... ....#... ..##.... ...##...
+ ..#..... ...#.... .##..... ..##....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+201A - SINGLE LOW-9 QUOTATION MARK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ....#... ...#.... ...##... ..##....
+ ...#.... ..#..... ..##.... .##.....
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+201C - LEFT DOUBLE QUOTATION MARK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ..#..#.. ....#..# ..##.##. ...##.##
+ .#..#... ...#..#. .##.##.. ..##.##.
+ .##.##.. ...##.## .##.##.. ..##.##.
+ .##.##.. ..##.##. .##.##.. ..##.##.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+201D - RIGHT DOUBLE QUOTATION MARK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ..##.##. ...##.## ..##.##. ...##.##
+ ..##.##. ...##.## ..##.##. ...##.##
+ ...#..#. ....#..# ..##.##. ...##.##
+ ..#..#.. ...#..#. .##.##.. ..##.##.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+201E - DOUBLE LOW-9 QUATATION MARK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .##..##. .##..##. ###.###. ###.###.
+ .##..##. .##..##. ###.###. ###.###.
+ ..#...#. .#...#.. .##..##. ##..##..
+ .#...#.. #...#... ##..##.. #..##...
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+2020 - DAGGER
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ...##... .....##. ..###... ...###..
+ ...##... .....##. ..###... ...###..
+ ...##... .....##. ..###... ...###..
+ .######. ...##### ..###... ...###..
+ .######. ..###### #######. .#######
+ ...##... ....##.. #######. .#######
+ ...##... ....##.. ..###... ..###...
+ ...##... ....##.. ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+2021 - DOUBLE DAGGER
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ...##... .....##. ..###... ...###..
+ ...##... .....##. ..###... ...###..
+ ...##... .....##. ..###... ...###..
+ .######. ...##### #######. .#######
+ .######. ..###### #######. .#######
+ ...##... ....##.. ..###... ...###..
+ .######. ..###### #######. #######.
+ .######. ..###### #######. #######.
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ...##... ..##.... ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+2022 - BULLET
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ..####.. ...####. ..####.. ...####.
+ .######. ..###### .######. .######.
+ .######. ..###### .######. .######.
+ .######. .######. .######. .######.
+ .######. .######. .######. .######.
+ ..####.. ..####.. ..####.. ..####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+2023 - TRIANGULAR BULLET
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .##..... ...##... ........ ..##....
+ .###.... ...##... .##..... ..###...
+ .####... ...###.. .###.... ..####..
+ .#####.. ..####.. .####... ..#####.
+ .######. ..#####. .#####.. ..######
+ .######. ..#####. .######. .#######
+ .#####.. ..####.. .######. .######.
+ .####... .####... .#####.. .#####..
+ .###.... .###.... .####... .####...
+ .##..... .##..... .###.... .###....
+ ........ ........ .##..... .##.....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+2026 - HORIZONTAL ELLIPSIS
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ##.##.## ##.##.##
+ ##.##.## ##.##.## ##.##.## ##.##.##
+ ##.##.## ##.##.## ##.##.## ##.##.##
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+2030 - PER MILLE SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ##....#. .##....# ##...##. .##...##
+ ##...##. .##...## ##..###. .##..###
+ ....##.. .....##. ...###.. ...###..
+ ...##... ....##.. ..###... ..###...
+ ..##.... ..##.... .###.... .###....
+ .##..... .##..... ###..... ###.....
+ ##.##.## ##.##.## ##.##.## ##.##.##
+ #..##.## #..##.## ...##.## ...##.##
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+2031 - PER TEN THOUSAND SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ##....#. .##....# ##...##. .##...##
+ ##...##. .##...## ##..###. .##..###
+ ....##.. .....##. ...###.. ...###..
+ ...##... ....##.. ..###... ..###...
+ ..##.... ..##.... .###.... .###....
+ .##..... .##..... ###..... ###.....
+ ##.#.#.# ##.#.#.# ##.#.#.# ##.#.#.#
+ #..#.#.# #..#.#.# ...#.#.# ...#.#.#
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+2039 - SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ .....##. ......##
+ ....##.. .....##. ....###. .....###
+ ...###.. ....###. ...###.. ...###..
+ ..###... ...###.. ..###... ..###...
+ ..##.... ..##.... .###.... .###....
+ ..###... ..###... ..###... ..###...
+ ...###.. ...###.. ...###.. ...###..
+ ....##.. ....##.. ....###. ....###.
+ ........ ........ .....##. .....##.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+203A - SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ .##..... ..##....
+ ..##.... ...##... .###.... ..###...
+ ..###... ...###.. ..###... ..###...
+ ...###.. ....###. ...###.. ...###..
+ ....##.. ....##.. ....###. ....###.
+ ...###.. ...###.. ...###.. ...###..
+ ..###... ..###... ..###... ..###...
+ ..##.... ..##.... .###.... .###....
+ ........ ........ .##..... .##.....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+20AC - EURO SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ...##... .....##. ..###... .....##.
+ ..####.. ....#### .#####.. ...#####
+ .##..##. ...##.## ###.###. ..###.##
+ .##..... ...##... ###..... .###....
+ ######.. .######. ######.. #######.
+ .##..... ..##.... ###..... .###....
+ #####... .#####.. #####... #####...
+ .##..... .##..... ###..... ###.....
+ .##..... .##..... ###..... ###.....
+ .##..##. .##.##.. ###.###. ###.###.
+ ..####.. ..####.. .#####.. ######..
+ ...##... ..##.... ..###... .###....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+2122 - TRADE MARK SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ####...# #####.## ####...# .####..#
+ #####.## ######## #####.## .#######
+ .#.##### .#.#.#.# .#.##### ..#.####
+ .#.#.#.# #.#...#. .#.#.#.# ..#.#..#
+ .#.#...# #.#...#. .#.#...# ..#.#..#
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+2190 - LEFTWARDS ARROW
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... ........ ........ ........
+ ..##.... .....#.. ...##... ....##..
+ .##..... ...##... ..##.... ...##...
+ #######. ..##.... .######. ..######
+ #######. .####### #######. #######.
+ .##..... .####### .######. .######.
+ ..##.... .##..... ..##.... ..##....
+ ...##... ..##.... ...##... ...##...
+ ........ ...##... ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+2191 - UPWARDS ARROW
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##... ....##.. ...#.... ....#...
+ ..####.. ...####. ..###... ...###..
+ .######. ..#####. .#####.. .#####..
+ ##.##.## .#..##.# #######. #######.
+ #..##..# #..##..# #.###.#. #.###.#.
+ ...##... ...##... ..###... ..###...
+ ...##... ...##... ..###... ..###...
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+2192 - RIGHTWARDS ARROW
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ..##.... ........ ........ ........
+ ...##... ....##.. ..##.... ...##...
+ ....##.. ....##.. ...##... ....##..
+ #######. .....##. ######.. .######.
+ #######. .####### #######. #######.
+ ....##.. .####### ######.. ######..
+ ...##... ....##.. ...##... ...##...
+ ..##.... ...##... ..##.... ..##....
+ ........ ..##.... ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+2193 - DOWNWARDS ARROW
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ ...##... ....##.. ..###... ...###..
+ #..##..# .#..##.. #.###.#. #.###.#.
+ ##.##.## .##.##.# #######. #######.
+ .######. .######. .#####.. .#####..
+ ..####.. ..####.. ..###... ..###...
+ ...##... ...##... ...#.... ...#....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+21E6 - LEFTWARDS WHITE ARROW
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...#.... ....##.. ...#.... ....#...
+ ..##.... ...##... ..##.... ...##...
+ .######. ..###### .######. ..#####.
+ #######. .####### #######. #######.
+ ##...##. .##...## #######. #######.
+ #######. #######. #######. #######.
+ .######. .######. .######. .######.
+ ..##.... ..##.... ..##.... ..##....
+ ...#.... ...#.... ...#.... ...#....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+21E7 - UPWARDS WHITE ARROW
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...#.... ....#... ...#.... ....#...
+ ..###... ...###.. ..###... ...###..
+ .##.##.. ..##.##. .#####.. ..#####.
+ ##...##. .##...#. #######. .######.
+ ###.###. ##....## #######. #######.
+ ###.###. ###.#### #######. #######.
+ ..#.#... .##.##.. .#####.. .#####..
+ ..#.#... ##.##... .#####.. .#####..
+ ..###... #####... .#####.. .#####..
+ ..###... #####... .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+21E8 - RIGHTWARDS WHITE ARROW
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...#.... .....#.. ...#.... ....#...
+ ...##... ....##.. ...##... ....##..
+ ######.. .######. ######.. .######.
+ #######. .####### #######. #######.
+ ##...##. .##...## #######. #######.
+ #######. #######. #######. #######.
+ ######.. ######.. ######.. ######..
+ ...##... ...##... ...##... ...##...
+ ...#.... ...#.... ...#.... ...#....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+21E9 - DOWNWARDS WHITE ARROW
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .#####.. ...##### .#####.. ..#####.
+ .#####.. ...##### .#####.. ..#####.
+ .##.##.. ...##.## .#####.. ..#####.
+ .##.##.. ..##.##. .#####.. .#####..
+ ###.###. ..##.##. #######. #######.
+ ###.###. ###...## #######. #######.
+ ##...##. ##...##. #######. #######.
+ .##.##.. .##.##.. .#####.. .#####..
+ ..###... ..###... ..###... ..###...
+ ...#.... ..#..... ...#.... ...#....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+2212 - MINUS SIGN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .#####.. ..#####. .#####.. .#####..
+ ........ ........ .#####.. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+23CE - RETURN SYMBOL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ...##.## ........ .....### ......##
+ ..##..## ....#... ...#.### ....#.##
+ .##...## ...##.## ..##.### ...##.##
+ ######## ..##..## .####### ..######
+ #######. .####### ######## ########
+ .##..... .####### .######. .######.
+ ..##.... .##..... ..##.... ..##....
+ ...##... ..##.... ...#.... ...#....
+ ........ ...##... ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+240C - SYMBOL FOR FORM FEED
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ####.... ..####.. ####.... .####...
+ ####.... ..####.. ####.... .####...
+ ##..#### .##..### ##..#### .##..###
+ ###.#### .###.### ###.#### .###.###
+ ###.##.. .###.##. ###.##.. .###.##.
+ ##..###. ##...### ##..###. ##..###.
+ ##..###. ##..#### ##..###. ##..###.
+ ##..##.. ##..##.. ##..##.. ##..##..
+ ....##.. ....##.. ....##.. ....##..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+2500 - BOX DRAWINGS LIGHT HORIZONTAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ########
+ ########
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2501 - BOX DRAWINGS HEAVY HORIZONTAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ########
+ ########
+ ########
+ ########
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2502 - BOX DRAWINGS LIGHT VERTICAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+2503 - BOX DRAWINGS HEAVY VERTICAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+2504 - BOX DRAWINGS LIGHT TRIPLE DASH HORIZONTAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ #.#..#.#
+ #.#..#.#
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2505 - BOX DRAWINGS HEAVY TRIPLE DASH HORIZONTAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ #.#..#.#
+ #.#..#.#
+ #.#..#.#
+ #.#..#.#
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2506 - BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ........
+ ........
+ ...##...
+ ...##...
+ ...##...
+ ........
+ ........
+ ...##...
+ ...##...
+ ...##...
+ ........
+ ........
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+2507 - BOX DRAWINGS HEAVY TRIPLE DASH VERTICAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ........
+ ........
+ ..####..
+ ..####..
+ ..####..
+ ........
+ ........
+ ..####..
+ ..####..
+ ..####..
+ ........
+ ........
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+2508 - BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ #.#.#.#.
+ #.#.#.#.
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2509 - BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZONTAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ #.#.#.#.
+ #.#.#.#.
+ #.#.#.#.
+ #.#.#.#.
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+250A - BOX DRAWINGS LIGHT QUADRUPLE DASH VERTICAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ...##...
+ ...##...
+ ........
+ ........
+ ...##...
+ ...##...
+ ........
+ ........
+ ...##...
+ ...##...
+ ........
+ ........
+ ...##...
+ ...##...
+ ........
+-----------------------------------------------------
+U+250B - BOX DRAWINGS HEAVY QUADRUPLE DASH VERTICAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ..####..
+ ..####..
+ ........
+ ........
+ ..####..
+ ..####..
+ ........
+ ........
+ ..####..
+ ..####..
+ ........
+ ........
+ ..####..
+ ..####..
+ ........
+-----------------------------------------------------
+U+250C - BOX DRAWINGS LIGHT DOWN AND RIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ...#####
+ ...#####
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+250D - BOX DRAWINGS DOWN LIGHT AND RIGHT HEAVY
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ...#####
+ ...#####
+ ...#####
+ ...#####
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+250E - BOX DRAWINGS DOWN HEAVY AND RIGHT LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ..######
+ ..######
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+250F - BOX DRAWINGS HEAVY DOWN AND RIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ..######
+ ..######
+ ..######
+ ..######
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+2510 - BOX DRAWINGS LIGHT DOWN AND LEFT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ #####...
+ #####...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+2511 - BOX DRAWINGS DOWN LIGHT AND LEFT HEAVY
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ #####...
+ #####...
+ #####...
+ #####...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+2512 - BOX DRAWINGS DOWN HEAVY AND LEFT LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ######..
+ ######..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+2513 - BOX DRAWINGS HEAVY DOWN AND LEFT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ######..
+ ######..
+ ######..
+ ######..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+2514 - BOX DRAWINGS LIGHT UP AND RIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...#####
+ ...#####
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2515 - BOX DRAWINGS UP LIGHT AND RIGHT HEAVY
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...#####
+ ...#####
+ ...#####
+ ...#####
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2516 - BOX DRAWINGS UP HEAVY AND RIGHT LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..######
+ ..######
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2517 - BOX DRAWINGS HEAVY UP AND RIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..######
+ ..######
+ ..######
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2518 - BOX DRAWINGS LIGHT UP AND LEFT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ #####...
+ #####...
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2519 - BOX DRAWINGS UP LIGHT AND LEFT HEAVY
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ #####...
+ #####...
+ #####...
+ #####...
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+251A - BOX DRAWINGS UP HEAVY AND LEFT LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ######..
+ ######..
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+251B - BOX DRAWINGS HEAVY UP AND LEFT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ######..
+ ######..
+ ######..
+ ######..
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+251C - BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...#####
+ ...#####
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+251D - BOX DRAWINGS VERTICAL LIGHT AND RIGHT HEAVY
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...#####
+ ...#####
+ ...#####
+ ...#####
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+251E - BOX DRAWINGS UP HEAVY AND RIGHT DOWN LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..######
+ ..######
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+251F - BOX DRAWINGS DOWN HEAVY AND RIGHT UP LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ..######
+ ..######
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+2520 - BOX DRAWINGS VERTICAL HEAVY AND RIGHT LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..######
+ ..######
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+2521 - BOX DRAWINGS DOWN LIGHT AND RIGHT UP HEAVY
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..######
+ ..######
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+2522 - BOX DRAWINGS UP LIGHT AND RIGHT DOWN HEAVY
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ..######
+ ..######
+ ..######
+ ..######
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+2523 - BOX DRAWINGS HEAVY VERTICAL AND RIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..######
+ ..######
+ ..######
+ ..######
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+2524 - BOX DRAWINGS LIGHT VERTICAL AND LEFT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ #####...
+ #####...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+2525 - BOX DRAWINGS VERTICAL LIGHT AND LEFT HEAVY
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ #####...
+ #####...
+ #####...
+ #####...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+2526 - BOX DRAWINGS UP HEAVY AND LEFT DOWN LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ######..
+ ######..
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+2527 - BOX DRAWINGS DOWN HEAVY AND LEFT UP LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ######..
+ ######..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+2528 - BOX DRAWINGS VERTICAL HEAVY AND LEFT LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ######..
+ ######..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+2529 - BOX DRAWINGS DOWN LIGHT AND LEFT UP HEAVY
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ######..
+ ######..
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+252A - BOX DRAWINGS UP LIGHT AND LEFT DOWN HEAVY
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ######..
+ ######..
+ ######..
+ ######..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+252B - BOX DRAWINGS HEAVY VERTICAL AND LEFT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ######..
+ ######..
+ ######..
+ ######..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+252C - BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ########
+ ########
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+252D - BOX DRAWINGS LEFT HEAVY AND RIGHT DOWN LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ #####...
+ ########
+ ########
+ #####...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+252E - BOX DRAWINGS RIGHT HEAVY AND LEFT DOWN LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ...#####
+ ########
+ ########
+ ...#####
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+252F - BOX DRAWINGS DOWN LIGHT AND HORIZONTAL HEAVY
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ########
+ ########
+ ########
+ ########
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+2530 - BOX DRAWINGS DOWN HEAVY AND HORIZONTAL LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ########
+ ########
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+2531 - BOX DRAWINGS RIGHT LIGHT AND LEFT DOWN HEAVY
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ######..
+ ########
+ ########
+ ######..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+2532 - BOX DRAWINGS LEFT LIGHT AND RIGHT DOWN HEAVY
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ..######
+ ########
+ ########
+ ..######
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+2533 - BOX DRAWINGS HEAVY DOWN AND HORIZONTAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ########
+ ########
+ ########
+ ########
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+2534 - BOX DRAWINGS LIGHT UP AND HORIZONTAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ########
+ ########
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2535 - BOX DRAWINGS LEFT HEAVY AND RIGHT UP LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ #####...
+ ########
+ ########
+ #####...
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2536 - BOX DRAWINGS RIGHT HEAVY AND LEFT UP LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...#####
+ ########
+ ########
+ ...#####
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2537 - BOX DRAWINGS UP LIGHT AND HORIZONTAL HEAVY
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ########
+ ########
+ ########
+ ########
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2538 - BOX DRAWINGS UP HEAVY AND HORIZONTAL LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ########
+ ########
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2539 - BOX DRAWINGS RIGHT LIGHT AND LEFT UP HEAVY
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ######..
+ ########
+ ########
+ ######..
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+253A - BOX DRAWINGS LEFT LIGHT AND RIGHT UP HEAVY
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..######
+ ########
+ ########
+ ..######
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+253B - BOX DRAWINGS HEAVY UP AND HORIZONTAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ########
+ ########
+ ########
+ ########
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+253C - BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ########
+ ########
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+253D - BOX DRAWINGS LEFT HEAVY AND RIGHT VERTICAL LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ #####...
+ ########
+ ########
+ #####...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+253E - BOX DRAWINGS RIGHT HEAVY AND LEFT VERTICAL LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...#####
+ ########
+ ########
+ ...#####
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+253F - BOX DRAWINGS VERTICAL LIGHT AND HORIZONTAL HEAVY
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ########
+ ########
+ ########
+ ########
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+2540 - BOX DRAWINGS UP HEAVY AND DOWN HORIZONTAL LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ########
+ ########
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+2541 - BOX DRAWINGS DOWN HEAVY AND UP HORIZONTAL LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ########
+ ########
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+2542 - BOX DRAWINGS VERTICAL HEAVY AND HORIZONTAL LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ########
+ ########
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+2543 - BOX DRAWINGS LEFT UP HEAVY AND RIGHT DOWN LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ######..
+ ########
+ ########
+ ######..
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+2544 - BOX DRAWINGS RIGHT UP HEAVY AND LEFT DOWN LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..######
+ ########
+ ########
+ ..######
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+2545 - BOX DRAWINGS LEFT DOWN HEAVY AND RIGHT UP LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ######..
+ ########
+ ########
+ ######..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+2546 - BOX DRAWINGS RIGHT DOWN HEAVY AND LEFT UP LIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ..######
+ ########
+ ########
+ ..######
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+2547 - BOX DRAWINGS DOWN LIGHT AND UP HORIZONTAL HEAVY
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ########
+ ########
+ ########
+ ########
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+2548 - BOX DRAWINGS UP LIGHT AND DOWN HORIZONTAL HEAVY
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ########
+ ########
+ ########
+ ########
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+2549 - BOX DRAWINGS RIGHT LIGHT AND LEFT VERTICAL HEAVY
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ######..
+ ########
+ ########
+ ######..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+254A - BOX DRAWINGS LEFT LIGHT AND RIGHT VERTICAL HEAVY
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..######
+ ########
+ ########
+ ..######
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+254B - BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ########
+ ########
+ ########
+ ########
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+254C - BOX DRAWINGS LIGHT DOUBLE DASH HORIZONTAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ .##..##.
+ .##..##.
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+254D - BOX DRAWINGS HEAVY DOUBLE DASH HORIZONTAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ .##..##.
+ .##..##.
+ .##..##.
+ .##..##.
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+254E - BOX DRAWINGS LIGHT DOUBLE DASH VERTICAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ........
+ ........
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ........
+ ........
+-----------------------------------------------------
+U+254F - BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ........
+ ........
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ........
+ ........
+-----------------------------------------------------
+U+2550 - BOX DRAWINGS DOUBLE HORIZONTAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ########
+ ........
+ ........
+ ########
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2551 - BOX DRAWINGS DOUBLE VERTICAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+-----------------------------------------------------
+U+2552 - BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ...#####
+ ...##...
+ ...##...
+ ...#####
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+2553 - BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ..######
+ ..######
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+-----------------------------------------------------
+U+2554 - BOX DRAWINGS DOUBLE DOWN AND RIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ..######
+ ..#.....
+ ..#.....
+ ..#..###
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+-----------------------------------------------------
+U+2555 - BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ #####...
+ ...##...
+ ...##...
+ #####...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+2556 - BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ######..
+ ######..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+-----------------------------------------------------
+U+2557 - BOX DRAWINGS DOUBLE DOWN AND LEFT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ######..
+ .....#..
+ .....#..
+ ###..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+-----------------------------------------------------
+U+2558 - BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...#####
+ ...##...
+ ...##...
+ ...#####
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2559 - BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..######
+ ..######
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+255A - BOX DRAWINGS DOUBLE UP AND RIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..###
+ ..#.....
+ ..#.....
+ ..######
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+255B - BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ #####...
+ ...##...
+ ...##...
+ #####...
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+255C - BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ######..
+ ######..
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+255D - BOX DRAWINGS DOUBLE UP AND LEFT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ###..#..
+ .....#..
+ .....#..
+ ######..
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+255E - BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...#####
+ ...##...
+ ...##...
+ ...#####
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+255F - BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..###
+ ..#..###
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+-----------------------------------------------------
+U+2560 - BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..###
+ ..#.....
+ ..#.....
+ ..#..###
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+-----------------------------------------------------
+U+2561 - BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ #####...
+ ...##...
+ ...##...
+ #####...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+2562 - BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ###..#..
+ ###..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+-----------------------------------------------------
+U+2563 - BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ###..#..
+ .....#..
+ .....#..
+ ###..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+-----------------------------------------------------
+U+2564 - BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ########
+ ........
+ ........
+ ########
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+2565 - BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ########
+ ########
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+-----------------------------------------------------
+U+2566 - BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ########
+ ........
+ ........
+ ###..###
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+-----------------------------------------------------
+U+2567 - BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ########
+ ........
+ ........
+ ########
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2568 - BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ########
+ ########
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2569 - BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ###..###
+ ........
+ ........
+ ########
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+256A - BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ########
+ ...##...
+ ...##...
+ ########
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+256B - BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ########
+ ########
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+-----------------------------------------------------
+U+256C - BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ###..###
+ ........
+ ........
+ ###..###
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+-----------------------------------------------------
+U+256D - BOX DRAWINGS LIGHT ARC DOWN AND RIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ......##
+ ....####
+ ....##..
+ ....##..
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+256E - BOX DRAWINGS LIGHT ARC DOWN AND LEFT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ##......
+ ####....
+ ..##....
+ ..##....
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+256F - BOX DRAWINGS LIGHT ARC UP AND LEFT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ..##....
+ ..##....
+ ####....
+ ##......
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2570 - BOX DRAWINGS LIGHT ARC UP AND RIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ....##..
+ ....##..
+ ....####
+ ......##
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2571 - BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ .......#
+ ......##
+ ......##
+ .....##.
+ .....##.
+ ....##..
+ ....##..
+ ...##...
+ ...##...
+ ..##....
+ ..##....
+ .##.....
+ .##.....
+ ##......
+ ##......
+ #.......
+-----------------------------------------------------
+U+2572 - BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ #.......
+ ##......
+ ##......
+ .##.....
+ .##.....
+ ..##....
+ ..##....
+ ...##...
+ ...##...
+ ....##..
+ ....##..
+ .....##.
+ .....##.
+ ......##
+ ......##
+ .......#
+-----------------------------------------------------
+U+2573 - BOX DRAWINGS LIGHT DIAGONAL CROSS
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ #......#
+ ##....##
+ ##....##
+ .##..##.
+ .##..##.
+ ..####..
+ ..####..
+ ...##...
+ ...##...
+ ..####..
+ ..####..
+ .##..##.
+ .##..##.
+ ##....##
+ ##....##
+ #......#
+-----------------------------------------------------
+U+2574 - BOX DRAWINGS LIGHT LEFT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ####....
+ ####....
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2575 - BOX DRAWINGS LIGHT UP
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2576 - BOX DRAWINGS LIGHT RIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ....####
+ ....####
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2577 - BOX DRAWINGS LIGHT DOWN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+2578 - BOX DRAWINGS HEAVY LEFT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ####....
+ ####....
+ ####....
+ ####....
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2579 - BOX DRAWINGS HEAVY UP
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+257A - BOX DRAWINGS HEAVY RIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ....####
+ ....####
+ ....####
+ ....####
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+257B - BOX DRAWINGS HEAVY DOWN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+257C - BOX DRAWINGS LIGHT LEFT AND HEAVY RIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ....####
+ ########
+ ########
+ ....####
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+257D - BOX DRAWINGS LIGHT UP AND HEAVY DOWN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+-----------------------------------------------------
+U+257E - BOX DRAWINGS HEAVY LEFT AND LIGHT RIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ####....
+ ########
+ ########
+ ####....
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+257F - BOX DRAWINGS HEAVY UP AND LIGHT DOWN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ..####..
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+ ...##...
+-----------------------------------------------------
+U+2580 - UPPER HALF BLOCK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2581 - LOWER ONE EIGHTH BLOCK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ########
+ ########
+-----------------------------------------------------
+U+2582 - LOWER ONE QUARTER BLOCK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ########
+ ########
+ ########
+ ########
+-----------------------------------------------------
+U+2583 - LOWER THREE EIGHTHS BLOCK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+-----------------------------------------------------
+U+2584 - LOWER HALF BLOCK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+-----------------------------------------------------
+U+2585 - LOWER FIVE EIGHTHS BLOCK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+-----------------------------------------------------
+U+2586 - LOWER THREE QUARTERS BLOCK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+-----------------------------------------------------
+U+2587 - LOWER SEVEN EIGHTHS BLOCK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+-----------------------------------------------------
+U+2588 - FULL BLOCK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+-----------------------------------------------------
+U+2589 - LEFT SEVEN EIGHTHS BLOCK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ #######.
+ #######.
+ #######.
+ #######.
+ #######.
+ #######.
+ #######.
+ #######.
+ #######.
+ #######.
+ #######.
+ #######.
+ #######.
+ #######.
+ #######.
+ #######.
+-----------------------------------------------------
+U+258A - LEFT THREE QUARTERS BLOCK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ######..
+ ######..
+ ######..
+ ######..
+ ######..
+ ######..
+ ######..
+ ######..
+ ######..
+ ######..
+ ######..
+ ######..
+ ######..
+ ######..
+ ######..
+ ######..
+-----------------------------------------------------
+U+258B - LEFT FIVE EIGHTHS BLOCK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ #####...
+ #####...
+ #####...
+ #####...
+ #####...
+ #####...
+ #####...
+ #####...
+ #####...
+ #####...
+ #####...
+ #####...
+ #####...
+ #####...
+ #####...
+ #####...
+-----------------------------------------------------
+U+258C - LEFT HALF BLOCK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+-----------------------------------------------------
+U+258D - LEFT THREE EIGHTHS BLOCK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ###.....
+ ###.....
+ ###.....
+ ###.....
+ ###.....
+ ###.....
+ ###.....
+ ###.....
+ ###.....
+ ###.....
+ ###.....
+ ###.....
+ ###.....
+ ###.....
+ ###.....
+ ###.....
+-----------------------------------------------------
+U+258E - LEFT ONE QUARTER BLOCK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ##......
+ ##......
+ ##......
+ ##......
+ ##......
+ ##......
+ ##......
+ ##......
+ ##......
+ ##......
+ ##......
+ ##......
+ ##......
+ ##......
+ ##......
+ ##......
+-----------------------------------------------------
+U+258F - LEFT ONE EIGHTH BLOCK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ #.......
+ #.......
+ #.......
+ #.......
+ #.......
+ #.......
+ #.......
+ #.......
+ #.......
+ #.......
+ #.......
+ #.......
+ #.......
+ #.......
+ #.......
+ #.......
+-----------------------------------------------------
+U+2590 - RIGHT HALF BLOCK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+-----------------------------------------------------
+U+2591 - LIGHT SHADE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ..#...#.
+ #...#...
+ ..#...#.
+ #...#...
+ ..#...#.
+ #...#...
+ ..#...#.
+ #...#...
+ ..#...#.
+ #...#...
+ ..#...#.
+ #...#...
+ ..#...#.
+ #...#...
+ ..#...#.
+ #...#...
+-----------------------------------------------------
+U+2592 - MEDIUM SHADE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ .#.#.#.#
+ #.#.#.#.
+ .#.#.#.#
+ #.#.#.#.
+ .#.#.#.#
+ #.#.#.#.
+ .#.#.#.#
+ #.#.#.#.
+ .#.#.#.#
+ #.#.#.#.
+ .#.#.#.#
+ #.#.#.#.
+ .#.#.#.#
+ #.#.#.#.
+ .#.#.#.#
+ #.#.#.#.
+-----------------------------------------------------
+U+2593 - DARK SHADE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ##.###.#
+ .###.###
+ ##.###.#
+ .###.###
+ ##.###.#
+ .###.###
+ ##.###.#
+ .###.###
+ ##.###.#
+ .###.###
+ ##.###.#
+ .###.###
+ ##.###.#
+ .###.###
+ ##.###.#
+ .###.###
+-----------------------------------------------------
+U+2594 - UPPER ONE EIGHTH BLOCK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ########
+ ########
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2595 - RIGHT ONE EIGHTH BLOCK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ .......#
+ .......#
+ .......#
+ .......#
+ .......#
+ .......#
+ .......#
+ .......#
+ .......#
+ .......#
+ .......#
+ .......#
+ .......#
+ .......#
+ .......#
+ .......#
+-----------------------------------------------------
+U+2596 - QUADRANT LOWER LEFT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+-----------------------------------------------------
+U+2597 - QUADRANT LOWER RIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+-----------------------------------------------------
+U+2598 - QUADRANT UPPER LEFT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2599 - QUADRANT UPPER LEFT AND LOWER LEFT AND LOWER RIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+-----------------------------------------------------
+U+259A - QUADRANT UPPER LEFT AND LOWER RIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+-----------------------------------------------------
+U+259B - QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER LEFT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+-----------------------------------------------------
+U+259C - QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER RIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+-----------------------------------------------------
+U+259D - QUADRANT UPPER RIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+259E - QUADRANT UPPER RIGHT AND LOWER LEFT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+ ####....
+-----------------------------------------------------
+U+259F - QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ....####
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+ ########
+-----------------------------------------------------
+U+25F0 - WHITE SQUARE WITH UPPER LEFT QUADRANT
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ #######. ..###### #######. .#######
+ #######. ..##.### #######. .#######
+ ##.#.##. ..##.### ##.#.##. .##.#.##
+ ##.#.##. .##.#.## ##.#.##. .##.#.##
+ ##.#.##. .##.#.## ##.#.##. .##.#.#.
+ ####.##. .####.## ####.##. ####.##.
+ ####.##. .####.## ####.##. ####.##.
+ ##...##. ##...##. ##...##. ##...##.
+ ##...##. ##...##. ##...##. ##...##.
+ ##...##. ##...##. ##...##. ##...##.
+ #######. #######. #######. #######.
+ #######. ######.. #######. #######.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+25AA - BLACK SMALL SQUARE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ..####.. ...####. ..#####. ..#####.
+ ..####.. ...####. ..#####. ..#####.
+ ..####.. ..####.. ..#####. .#####..
+ ..####.. ..####.. ..#####. .#####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+25B2 - BLACK UP-POINTING TRIANGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ...#....
+ ...#....
+ ..###...
+ ..###...
+ .#####..
+ .#####..
+ #######.
+ #######.
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+25B3 - WHITE UP-POINTING TRIANGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ...#....
+ ...#....
+ ..###...
+ ..#.#...
+ .##.##..
+ .#...#..
+ ##...##.
+ #######.
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+25B4 - BLACK UP-POINTING SMALL TRIANGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ...#....
+ ...#....
+ ..###...
+ ..###...
+ .#####..
+ .#####..
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+25B5 - WHITE UP-POINTING SMALL TRIANGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ ...#....
+ ...#....
+ ..###...
+ ..#.#...
+ .##.##..
+ .#####..
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+25B6 - BLACK RIGHT-POINTING TRIANGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ #.......
+ ###.....
+ #####...
+ #######.
+ #######.
+ #####...
+ ###.....
+ #.......
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+25B7 - WHITE RIGHT-POINTING TRIANGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ #.......
+ ###.....
+ #.###...
+ #....##.
+ #....##.
+ #.###...
+ ###.....
+ #.......
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+25B8 - BLACK RIGHT-POINTING SMALL TRIANGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ .#......
+ .###....
+ .#####..
+ .#####..
+ .###....
+ .#......
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+25B9 - WHITE RIGHT-POINTING SMALL TRIANGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ .#......
+ .###....
+ .#..##..
+ .#..##..
+ .###....
+ .#......
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+25BA - BLACK RIGHT-POINTING POINTER
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ #.......
+ ###.....
+ #####...
+ #######.
+ #######.
+ #####...
+ ###.....
+ #.......
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+25BB - WHITE RIGHT-POINTING POINTER
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ #.......
+ ###.....
+ #.###...
+ #....##.
+ #....##.
+ #.###...
+ ###.....
+ #.......
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+25BC - BLACK DOWN-POINTING TRIANGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ #######.
+ #######.
+ .#####..
+ .#####..
+ ..###...
+ ..###...
+ ...#....
+ ...#....
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+25BD - WHITE DOWN-POINTING TRIANGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ #######.
+ ##...##.
+ .#...#..
+ .##.##..
+ ..#.#...
+ ..###...
+ ...#....
+ ...#....
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+25BE - BLACK DOWN-POINTING SMALL TRIANGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ .#####..
+ .#####..
+ ..###...
+ ..###...
+ ...#....
+ ...#....
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+25BF - WHITE DOWN-POINTING SMALL TRIANGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ .#####..
+ .##.##..
+ ..#.#...
+ ..###...
+ ...#....
+ ...#....
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+25C0 - BLACK LEFT-POINTING TRIANGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ......#.
+ ....###.
+ ..#####.
+ #######.
+ #######.
+ ..#####.
+ ....###.
+ ......#.
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+25C1 - WHITE LEFT-POINTING TRIANGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ......#.
+ ....###.
+ ..###.#.
+ ##....#.
+ ##....#.
+ ..###.#.
+ ....###.
+ ......#.
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+25C2 - BLACK LEFT-POINTING SMALL TRIANGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ .....#..
+ ...###..
+ .#####..
+ .#####..
+ ...###..
+ .....#..
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+25C3 - WHITE LEFT-POINTING SMALL TRIANGLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ .....#..
+ ...###..
+ .##..#..
+ .##..#..
+ ...###..
+ .....#..
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+25C4 - BLACK LEFT-POINTING POINTER
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ......#.
+ ....###.
+ ..#####.
+ #######.
+ #######.
+ ..#####.
+ ....###.
+ ......#.
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+25C5 - WHITE LEFT-POINTING POINTER
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ......#.
+ ....###.
+ ..###.#.
+ ##....#.
+ ##....#.
+ ..###.#.
+ ....###.
+ ......#.
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+25CB - WHITE CIRCLE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ..####.. ...####. ..####.. ...####.
+ .######. ..###### .######. .######.
+ .##..##. ..##..## .##..##. .###..#.
+ .##..##. .##..##. .##..##. .##..##.
+ .######. .######. .######. .######.
+ ..####.. ..####.. ..####.. ..####..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+2603 - SNOWMAN
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ..###...
+ .#####..
+ ..#.#...
+ .#...#..
+ ..#.#...
+ .#.#.#..
+ #.....#.
+ #.....#.
+ #.....#.
+ #.....#.
+ .#...#..
+ ..###...
+ ........
+ ........
+-----------------------------------------------------
+U+263A - WHITE SMILING FACE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ..###...
+ .#...#..
+ #.....#.
+ #.#.#.#.
+ #.....#.
+ #.###.#.
+ #..#..#.
+ .#...#..
+ ..###...
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+263B - BLACK SMILING FACE
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ..###...
+ .#####..
+ #######.
+ ##.#.##.
+ #######.
+ #.###.#.
+ ##...##.
+ .#####..
+ ..###...
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+2713 - CHECK MARK
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .....##. .......# .....##. ......##
+ .....##. .......# ....###. .....###
+ ....##.. ......## ....###. .....###
+ ....##.. .....##. ...###.. ....###.
+ ....##.. .....##. ...###.. ....###.
+ ...##... ....##.. ..###... ..###...
+ ...##... ....##.. #####... #####...
+ ##.##... ##.##... #####... #####...
+ ####.... ####.... ####.... ####....
+ .###.... .###.... .###.... .###....
+ ..#..... ..#..... ..#..... ..#.....
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+2717 - BALLOT X
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ .##.##.. ...##.#. .##.##.. ..##.##.
+ #######. ..###### #######. .#######
+ ##.#.##. .##.#.## ##.#.##. .##.#.##
+ ##...##. .##...## ###.###. .###.###
+ .##.##.. ..##.##. .##.##.. .##.##..
+ .##.##.. ..####.. .##.##.. .##.##..
+ .##.##.. .##.##.. .##.##.. .##.##..
+ ##...##. ##...##. ###.###. ###.###.
+ ##.#.##. ##.#.##. ##.#.##. ##.#.##.
+ #######. #######. #######. #######.
+ .##.##.. .##.##.. .##.##.. .##.##..
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+2721 - STAR OF DAVID
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ...#....
+ ...#....
+ ..#.#...
+ #######.
+ .##.##..
+ .##.##..
+ #######.
+ ..#.#...
+ ...#....
+ ...#....
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+3007 - IDEOGRAPHIC NUMBER ZERO
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ..###...
+ .#...#..
+ .#...#..
+ #.....#.
+ #.....#.
+ #.....#.
+ #.....#.
+ .#...#..
+ .#...#..
+ ..###...
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+4E00 - <CJK Ideograph, First>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ........
+ ........
+ ........
+ ........
+ #######.
+ #######.
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+4E03 - <CJK Ideograph, Seventh>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ..#.....
+ ..#.....
+ ..#...#.
+ ..####..
+ ###.....
+ ..#.....
+ ..#.....
+ ..#...#.
+ ..#...#.
+ ..####..
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+4E07 - <CJK Ideograph, Ten Thousanth>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ #######.
+ ..#.....
+ ..#.....
+ ..####..
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ .#...#..
+ .#...#..
+ #..##...
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+4E09 - <CJK Ideograph, Third>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ .#####..
+ .#####..
+ ........
+ ........
+ ..###...
+ ..###...
+ ........
+ ........
+ #######.
+ #######.
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+4E5D - <CJK Ideograph, Ninth>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ..#.....
+ ..#.....
+ ######..
+ ..#..#..
+ ..#.#...
+ ..#.#...
+ ..#.#...
+ .#..#.#.
+ .#..#.#.
+ #...##..
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+4E8C - <CJK Ideograph, Second>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ .#####..
+ .#####..
+ ........
+ ........
+ ........
+ ........
+ ........
+ ........
+ #######.
+ #######.
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+4E94 - <CJK Ideograph, Fifth>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ #######.
+ ....#...
+ ...#....
+ ...#....
+ .######.
+ ...#..#.
+ ..#..#..
+ ..#..#..
+ ..#..#..
+ #######.
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+4EBF - <Hundred million, many>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ..#.###.
+ ..#...#.
+ .#...#..
+ .#...#..
+ ##..#...
+ .#..#...
+ .#.#....
+ .#.#..#.
+ .#.####.
+ .#......
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+516B - <CJK Ideograph, Eighth>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ...##...
+ ..#.#...
+ ..#.#...
+ ..#.#...
+ .#...#..
+ .#...#..
+ .#...#..
+ .#...#..
+ .#...#..
+ #.....#.
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+516D - <CJK Ideograph, Sixth>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ...#....
+ ...#....
+ ...#....
+ #######.
+ ........
+ ..#.#...
+ ..#..#..
+ ..#..#..
+ .#....#.
+ #.....#.
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+5341 - <CJK Ideograph, Tenth>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ...#....
+ ...#....
+ ...#....
+ ...#....
+ #######.
+ ...#....
+ ...#....
+ ...#....
+ ...#....
+ ...#....
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+5343 - <CJK Ideograph, Thousandth>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ....###.
+ ####....
+ ...#....
+ ...#....
+ #######.
+ ...#....
+ ...#....
+ ...#....
+ ...#....
+ ...#....
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+56DB - <CJK Ideograph, Fourth>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ #######.
+ #######.
+ #.#.#.#.
+ #.#.#.#.
+ #.#.#.#.
+ ##..###.
+ ##....#.
+ #.....#.
+ #######.
+ #.....#.
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+767E - <CJK Ideograph, Hundredth>
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ #######.
+ ...#....
+ .#####..
+ .#...#..
+ .#...#..
+ .#####..
+ .#...#..
+ .#...#..
+ .#####..
+ .#...#..
+ ........
+ ........
+ ........
+ ........
+ ........
+-----------------------------------------------------
+U+FB01 - LATIN SMALL LIGATURE FI
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..####.. ....###. .#####.. ..#####.
+ .######. ...##### #######. .#######
+ .##..##. ...##.## ###.###. .###.###
+ .##..##. ...##.## ###.###. .###.###
+ .##..... ..##.... ###..... .###....
+ #######. .####### #######. .#######
+ #######. .####### #######. #######.
+ .##..##. ..##..## ###.###. ###.###.
+ .##..##. .##..##. ###.###. ###.###.
+ .##..##. .##..##. ###.###. ###.###.
+ .##..##. .##..##. ###.###. ###.###.
+ .##..##. .##..##. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+FB02 - LATIN SMALL LIGATURE FL
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........ ........ ........ ........
+ ..#####. ...##### .######. ..######
+ .######. ..###### #######. .#######
+ .##..##. ..##..## ###.###. .###.###
+ .##..##. ..##.##. ###.###. .###.###
+ .##..##. .##..##. ###.###. .###.###
+ ####.##. ####.##. #######. .#######
+ ####.##. ####.##. #######. #######.
+ .##..##. .##..##. ###.###. ###.###.
+ .##..##. ##..##.. ###.###. ###.###.
+ .##..##. ##..##.. ###.###. ###.###.
+ .##..##. ##..##.. ###.###. ###.###.
+ .##..##. ##..##.. ###.###. ###.###.
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+ ........ ........ ........ ........
+-----------------------------------------------------
+U+FFFC - OBJECT REPLACEMENT CHARACTER
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ #..##..#
+ ..#..#..
+ #.#..#.#
+ ...##...
+ #......#
+ ..###...
+ #.#..#.#
+ ..###...
+ #.#..#.#
+ ..###...
+ #......#
+ ..####..
+ #...#..#
+ ....#...
+ #.#.#..#
+ ...#....
+-----------------------------------------------------
+U+FFFD - REPLACEMENT CHARACTER
+- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ........
+ ...#....
+ ..###...
+ ..###...
+ .##.##..
+ .#.#.#..
+ ####.##.
+ ###.###.
+ .##.##..
+ .#####..
+ ..#.#...
+ ..###...
+ ...#....
+ ........
+ ........
+ ........
+-----------------------------------------------------
diff --git a/frontends/framebuffer/res/icons/back.png b/frontends/framebuffer/res/icons/back.png
new file mode 100644
index 000000000..654a2928d
--- /dev/null
+++ b/frontends/framebuffer/res/icons/back.png
Binary files differ
diff --git a/frontends/framebuffer/res/icons/back_g.png b/frontends/framebuffer/res/icons/back_g.png
new file mode 100644
index 000000000..394c1cb30
--- /dev/null
+++ b/frontends/framebuffer/res/icons/back_g.png
Binary files differ
diff --git a/frontends/framebuffer/res/icons/forward.png b/frontends/framebuffer/res/icons/forward.png
new file mode 100644
index 000000000..73929f438
--- /dev/null
+++ b/frontends/framebuffer/res/icons/forward.png
Binary files differ
diff --git a/frontends/framebuffer/res/icons/forward_g.png b/frontends/framebuffer/res/icons/forward_g.png
new file mode 100644
index 000000000..3278f4110
--- /dev/null
+++ b/frontends/framebuffer/res/icons/forward_g.png
Binary files differ
diff --git a/frontends/framebuffer/res/icons/history.png b/frontends/framebuffer/res/icons/history.png
new file mode 100644
index 000000000..b124db7bf
--- /dev/null
+++ b/frontends/framebuffer/res/icons/history.png
Binary files differ
diff --git a/frontends/framebuffer/res/icons/history_g.png b/frontends/framebuffer/res/icons/history_g.png
new file mode 100644
index 000000000..1597d54e7
--- /dev/null
+++ b/frontends/framebuffer/res/icons/history_g.png
Binary files differ
diff --git a/frontends/framebuffer/res/icons/home.png b/frontends/framebuffer/res/icons/home.png
new file mode 100644
index 000000000..c13ba6cab
--- /dev/null
+++ b/frontends/framebuffer/res/icons/home.png
Binary files differ
diff --git a/frontends/framebuffer/res/icons/home_g.png b/frontends/framebuffer/res/icons/home_g.png
new file mode 100644
index 000000000..eea22a541
--- /dev/null
+++ b/frontends/framebuffer/res/icons/home_g.png
Binary files differ
diff --git a/frontends/framebuffer/res/icons/osk.png b/frontends/framebuffer/res/icons/osk.png
new file mode 100644
index 000000000..1e64fed01
--- /dev/null
+++ b/frontends/framebuffer/res/icons/osk.png
Binary files differ
diff --git a/frontends/framebuffer/res/icons/reload.png b/frontends/framebuffer/res/icons/reload.png
new file mode 100644
index 000000000..7609f816c
--- /dev/null
+++ b/frontends/framebuffer/res/icons/reload.png
Binary files differ
diff --git a/frontends/framebuffer/res/icons/reload_g.png b/frontends/framebuffer/res/icons/reload_g.png
new file mode 100644
index 000000000..65ad90402
--- /dev/null
+++ b/frontends/framebuffer/res/icons/reload_g.png
Binary files differ
diff --git a/frontends/framebuffer/res/icons/scrolld.png b/frontends/framebuffer/res/icons/scrolld.png
new file mode 100644
index 000000000..197720775
--- /dev/null
+++ b/frontends/framebuffer/res/icons/scrolld.png
Binary files differ
diff --git a/frontends/framebuffer/res/icons/scrolll.png b/frontends/framebuffer/res/icons/scrolll.png
new file mode 100644
index 000000000..83f8609b2
--- /dev/null
+++ b/frontends/framebuffer/res/icons/scrolll.png
Binary files differ
diff --git a/frontends/framebuffer/res/icons/scrollr.png b/frontends/framebuffer/res/icons/scrollr.png
new file mode 100644
index 000000000..669e758ba
--- /dev/null
+++ b/frontends/framebuffer/res/icons/scrollr.png
Binary files differ
diff --git a/frontends/framebuffer/res/icons/scrollu.png b/frontends/framebuffer/res/icons/scrollu.png
new file mode 100644
index 000000000..f635c2bdd
--- /dev/null
+++ b/frontends/framebuffer/res/icons/scrollu.png
Binary files differ
diff --git a/frontends/framebuffer/res/icons/stop.png b/frontends/framebuffer/res/icons/stop.png
new file mode 100644
index 000000000..c62005134
--- /dev/null
+++ b/frontends/framebuffer/res/icons/stop.png
Binary files differ
diff --git a/frontends/framebuffer/res/icons/stop_g.png b/frontends/framebuffer/res/icons/stop_g.png
new file mode 100644
index 000000000..5c36c7d8d
--- /dev/null
+++ b/frontends/framebuffer/res/icons/stop_g.png
Binary files differ
diff --git a/frontends/framebuffer/res/internal.css b/frontends/framebuffer/res/internal.css
new file mode 120000
index 000000000..17f9f1504
--- /dev/null
+++ b/frontends/framebuffer/res/internal.css
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/internal.css,f79 \ No newline at end of file
diff --git a/frontends/framebuffer/res/licence.html b/frontends/framebuffer/res/licence.html
new file mode 120000
index 000000000..147dd6db2
--- /dev/null
+++ b/frontends/framebuffer/res/licence.html
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/en/licence.html,faf \ No newline at end of file
diff --git a/frontends/framebuffer/res/maps.html b/frontends/framebuffer/res/maps.html
new file mode 120000
index 000000000..28362130a
--- /dev/null
+++ b/frontends/framebuffer/res/maps.html
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/en/welcome.html,faf \ No newline at end of file
diff --git a/frontends/framebuffer/res/netsurf.png b/frontends/framebuffer/res/netsurf.png
new file mode 120000
index 000000000..905512c25
--- /dev/null
+++ b/frontends/framebuffer/res/netsurf.png
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/netsurf.png,b60 \ No newline at end of file
diff --git a/frontends/framebuffer/res/pointers/caret.png b/frontends/framebuffer/res/pointers/caret.png
new file mode 100644
index 000000000..dff246599
--- /dev/null
+++ b/frontends/framebuffer/res/pointers/caret.png
Binary files differ
diff --git a/frontends/framebuffer/res/pointers/cross.png b/frontends/framebuffer/res/pointers/cross.png
new file mode 100644
index 000000000..90464dc5f
--- /dev/null
+++ b/frontends/framebuffer/res/pointers/cross.png
Binary files differ
diff --git a/frontends/framebuffer/res/pointers/default.png b/frontends/framebuffer/res/pointers/default.png
new file mode 100644
index 000000000..479a8e854
--- /dev/null
+++ b/frontends/framebuffer/res/pointers/default.png
Binary files differ
diff --git a/frontends/framebuffer/res/pointers/help.png b/frontends/framebuffer/res/pointers/help.png
new file mode 100644
index 000000000..36c1a02a1
--- /dev/null
+++ b/frontends/framebuffer/res/pointers/help.png
Binary files differ
diff --git a/frontends/framebuffer/res/pointers/left-right.png b/frontends/framebuffer/res/pointers/left-right.png
new file mode 100644
index 000000000..ec139caa7
--- /dev/null
+++ b/frontends/framebuffer/res/pointers/left-right.png
Binary files differ
diff --git a/frontends/framebuffer/res/pointers/lu-rd.png b/frontends/framebuffer/res/pointers/lu-rd.png
new file mode 100644
index 000000000..0ad5dc150
--- /dev/null
+++ b/frontends/framebuffer/res/pointers/lu-rd.png
Binary files differ
diff --git a/frontends/framebuffer/res/pointers/menu.png b/frontends/framebuffer/res/pointers/menu.png
new file mode 100644
index 000000000..5c0254df7
--- /dev/null
+++ b/frontends/framebuffer/res/pointers/menu.png
Binary files differ
diff --git a/frontends/framebuffer/res/pointers/move.png b/frontends/framebuffer/res/pointers/move.png
new file mode 100644
index 000000000..e467c0e2b
--- /dev/null
+++ b/frontends/framebuffer/res/pointers/move.png
Binary files differ
diff --git a/frontends/framebuffer/res/pointers/no_drop.png b/frontends/framebuffer/res/pointers/no_drop.png
new file mode 100644
index 000000000..e66874456
--- /dev/null
+++ b/frontends/framebuffer/res/pointers/no_drop.png
Binary files differ
diff --git a/frontends/framebuffer/res/pointers/not_allowed.png b/frontends/framebuffer/res/pointers/not_allowed.png
new file mode 100644
index 000000000..224f4e413
--- /dev/null
+++ b/frontends/framebuffer/res/pointers/not_allowed.png
Binary files differ
diff --git a/frontends/framebuffer/res/pointers/point.png b/frontends/framebuffer/res/pointers/point.png
new file mode 100644
index 000000000..b57244328
--- /dev/null
+++ b/frontends/framebuffer/res/pointers/point.png
Binary files differ
diff --git a/frontends/framebuffer/res/pointers/progress.png b/frontends/framebuffer/res/pointers/progress.png
new file mode 100644
index 000000000..41cfa37e7
--- /dev/null
+++ b/frontends/framebuffer/res/pointers/progress.png
Binary files differ
diff --git a/frontends/framebuffer/res/pointers/ru-ld.png b/frontends/framebuffer/res/pointers/ru-ld.png
new file mode 100644
index 000000000..77265fc03
--- /dev/null
+++ b/frontends/framebuffer/res/pointers/ru-ld.png
Binary files differ
diff --git a/frontends/framebuffer/res/pointers/up-down.png b/frontends/framebuffer/res/pointers/up-down.png
new file mode 100644
index 000000000..acb840e45
--- /dev/null
+++ b/frontends/framebuffer/res/pointers/up-down.png
Binary files differ
diff --git a/frontends/framebuffer/res/pointers/wait.png b/frontends/framebuffer/res/pointers/wait.png
new file mode 100644
index 000000000..6f9a0654a
--- /dev/null
+++ b/frontends/framebuffer/res/pointers/wait.png
Binary files differ
diff --git a/frontends/framebuffer/res/quirks.css b/frontends/framebuffer/res/quirks.css
new file mode 120000
index 000000000..88aabe48c
--- /dev/null
+++ b/frontends/framebuffer/res/quirks.css
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/Quirks,f79 \ No newline at end of file
diff --git a/frontends/framebuffer/res/throbber b/frontends/framebuffer/res/throbber
new file mode 120000
index 000000000..ccb7ff5d8
--- /dev/null
+++ b/frontends/framebuffer/res/throbber
@@ -0,0 +1 @@
+../../gtk/res/throbber \ No newline at end of file
diff --git a/frontends/framebuffer/res/welcome.html b/frontends/framebuffer/res/welcome.html
new file mode 120000
index 000000000..28362130a
--- /dev/null
+++ b/frontends/framebuffer/res/welcome.html
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/en/welcome.html,faf \ No newline at end of file
diff --git a/frontends/framebuffer/schedule.c b/frontends/framebuffer/schedule.c
new file mode 100644
index 000000000..581ad72f1
--- /dev/null
+++ b/frontends/framebuffer/schedule.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <time.h>
+#include <stdlib.h>
+
+#include "utils/sys_time.h"
+#include "utils/log.h"
+
+#include "framebuffer/schedule.h"
+
+#ifdef DEBUG_SCHEDULER
+#define SRLOG(x...) LOG(x)
+#else
+#define SRLOG(x...) ((void) 0)
+#endif
+
+/* linked list of scheduled callbacks */
+static struct nscallback *schedule_list = NULL;
+
+/**
+ * scheduled callback.
+ */
+struct nscallback
+{
+ struct nscallback *next;
+ struct timeval tv;
+ void (*callback)(void *p);
+ void *p;
+};
+
+/**
+ * Unschedule a callback.
+ *
+ * \param callback callback function
+ * \param p user parameter, passed to callback function
+ *
+ * All scheduled callbacks matching both callback and p are removed.
+ */
+static nserror schedule_remove(void (*callback)(void *p), void *p)
+{
+ struct nscallback *cur_nscb;
+ struct nscallback *prev_nscb;
+ struct nscallback *unlnk_nscb;
+
+ /* check there is something on the list to remove */
+ if (schedule_list == NULL) {
+ return NSERROR_OK;
+ }
+
+ SRLOG("removing %p, %p", callback, p);
+
+ cur_nscb = schedule_list;
+ prev_nscb = NULL;
+
+ while (cur_nscb != NULL) {
+ if ((cur_nscb->callback == callback) &&
+ (cur_nscb->p == p)) {
+ /* item to remove */
+
+ SRLOG("callback entry %p removing %p(%p)",
+ cur_nscb, cur_nscb->callback, cur_nscb->p);
+
+ /* remove callback */
+ unlnk_nscb = cur_nscb;
+ cur_nscb = unlnk_nscb->next;
+
+ if (prev_nscb == NULL) {
+ schedule_list = cur_nscb;
+ } else {
+ prev_nscb->next = cur_nscb;
+ }
+ free (unlnk_nscb);
+ } else {
+ /* move to next element */
+ prev_nscb = cur_nscb;
+ cur_nscb = prev_nscb->next;
+ }
+ }
+
+ return NSERROR_OK;
+}
+
+/* exported function documented in framebuffer/schedule.h */
+nserror framebuffer_schedule(int tival, void (*callback)(void *p), void *p)
+{
+ struct nscallback *nscb;
+ struct timeval tv;
+ nserror ret;
+
+ /* ensure uniqueness of the callback and context */
+ ret = schedule_remove(callback, p);
+ if ((tival < 0) || (ret != NSERROR_OK)) {
+ return ret;
+ }
+
+ SRLOG("Adding %p(%p) in %d", callback, p, tival);
+
+ tv.tv_sec = tival / 1000; /* miliseconds to seconds */
+ tv.tv_usec = (tival % 1000) * 1000; /* remainder to microseconds */
+
+ nscb = calloc(1, sizeof(struct nscallback));
+
+ gettimeofday(&nscb->tv, NULL);
+ timeradd(&nscb->tv, &tv, &nscb->tv);
+
+ nscb->callback = callback;
+ nscb->p = p;
+
+ /* add to list front */
+ nscb->next = schedule_list;
+ schedule_list = nscb;
+
+ return NSERROR_OK;
+}
+
+/* exported function documented in framebuffer/schedule.h */
+int schedule_run(void)
+{
+ struct timeval tv;
+ struct timeval nexttime;
+ struct timeval rettime;
+ struct nscallback *cur_nscb;
+ struct nscallback *prev_nscb;
+ struct nscallback *unlnk_nscb;
+
+ if (schedule_list == NULL)
+ return -1;
+
+ /* reset enumeration to the start of the list */
+ cur_nscb = schedule_list;
+ prev_nscb = NULL;
+ nexttime = cur_nscb->tv;
+
+ gettimeofday(&tv, NULL);
+
+ while (cur_nscb != NULL) {
+ if (timercmp(&tv, &cur_nscb->tv, >)) {
+ /* scheduled time */
+
+ /* remove callback */
+ unlnk_nscb = cur_nscb;
+
+ if (prev_nscb == NULL) {
+ schedule_list = unlnk_nscb->next;
+ } else {
+ prev_nscb->next = unlnk_nscb->next;
+ }
+
+ unlnk_nscb->callback(unlnk_nscb->p);
+
+ free(unlnk_nscb);
+
+ /* need to deal with callback modifying the list. */
+ if (schedule_list == NULL)
+ return -1; /* no more callbacks scheduled */
+
+ /* reset enumeration to the start of the list */
+ cur_nscb = schedule_list;
+ prev_nscb = NULL;
+ nexttime = cur_nscb->tv;
+ } else {
+ /* if the time to the event is sooner than the
+ * currently recorded soonest event record it
+ */
+ if (timercmp(&nexttime, &cur_nscb->tv, >)) {
+ nexttime = cur_nscb->tv;
+ }
+ /* move to next element */
+ prev_nscb = cur_nscb;
+ cur_nscb = prev_nscb->next;
+ }
+ }
+
+ /* make rettime relative to now */
+ timersub(&nexttime, &tv, &rettime);
+
+ SRLOG("returning time to next event as %ldms",(rettime.tv_sec * 1000) + (rettime.tv_usec / 1000));
+
+ /* return next event time in milliseconds (24days max wait) */
+ return (rettime.tv_sec * 1000) + (rettime.tv_usec / 1000);
+}
+
+void list_schedule(void)
+{
+ struct timeval tv;
+ struct nscallback *cur_nscb;
+
+ gettimeofday(&tv, NULL);
+
+ LOG("schedule list at %ld:%ld", tv.tv_sec, tv.tv_usec);
+
+ cur_nscb = schedule_list;
+
+ while (cur_nscb != NULL) {
+ LOG("Schedule %p at %ld:%ld", cur_nscb, cur_nscb->tv.tv_sec, cur_nscb->tv.tv_usec);
+ cur_nscb = cur_nscb->next;
+ }
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */
diff --git a/frontends/framebuffer/schedule.h b/frontends/framebuffer/schedule.h
new file mode 100644
index 000000000..4e94da68e
--- /dev/null
+++ b/frontends/framebuffer/schedule.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 FRAMEBUFFER_SCHEDULE_H
+#define FRAMEBUFFER_SCHEDULE_H
+
+/**
+ * Schedule a callback.
+ *
+ * \param tival interval before the callback should be made in ms
+ * \param callback callback function
+ * \param p user parameter, passed to callback function
+ *
+ * The callback function will be called as soon as possible after t ms have
+ * passed.
+ */
+
+nserror framebuffer_schedule(int tival, void (*callback)(void *p), void *p);
+
+/**
+ * Process scheduled callbacks up to current time.
+ *
+ * @return The number of milliseconds untill the next scheduled event
+ * or -1 for no event.
+ */
+int schedule_run(void);
+
+void list_schedule(void);
+
+#endif
diff --git a/frontends/gtk/Makefile b/frontends/gtk/Makefile
new file mode 100644
index 000000000..7f8ffc16a
--- /dev/null
+++ b/frontends/gtk/Makefile
@@ -0,0 +1,205 @@
+#
+# Makefile for NetSurf GTK target
+#
+# This file is part of NetSurf
+#
+# ----------------------------------------------------------------------------
+# GTK flag setup (using pkg-config)
+# ----------------------------------------------------------------------------
+
+# define additional CFLAGS and LDFLAGS requirements for pkg-configed libs here
+NETSURF_FEATURE_RSVG_CFLAGS := -DWITH_RSVG
+NETSURF_FEATURE_VIDEO_CFLAGS := -DWITH_VIDEO
+
+$(eval $(call pkg_config_find_and_add_enabled,RSVG,librsvg-2.0,SVG))
+$(eval $(call pkg_config_find_and_add_enabled,VIDEO,gstreamer-0.10,Video))
+
+# GTK and GLIB flags to disable depricated usage
+GTKDEPFLAGS := -DG_DISABLE_SINGLE_INCLUDES \
+ -DG_DISABLE_DEPRECATED \
+ -DGTK_DISABLE_SINGLE_INCLUDES \
+ -DGTK_MULTIHEAD_SAFE \
+ -DPANGO_DISABLE_DEPRECATED
+
+# later editions of gtk 2 deprecate interfaces we rely upon for cursors
+# -DGDK_PIXBUF_DISABLE_DEPRECATED
+
+# libsexy currently means we cannot enable this
+# -DGDK_DISABLE_DEPRECATED
+
+# gtk3 is depricating interfaces we use a lot
+ifeq ($(NETSURF_GTK_MAJOR),2)
+GTKDEPFLAGS += -DGTK_DISABLE_DEPRECATED
+endif
+
+
+GTKCFLAGS := -std=c99 -Dgtk -Dnsgtk -g \
+ $(GTKDEPFLAGS) \
+ -D_BSD_SOURCE \
+ -D_DEFAULT_SOURCE \
+ -D_XOPEN_SOURCE=600 \
+ -D_POSIX_C_SOURCE=200809L \
+ -D_NETBSD_SOURCE \
+ -DGTK_RESPATH=\"$(NETSURF_GTK_RESOURCES)\"
+
+# non optional pkg-configed libs
+$(eval $(call pkg_config_find_and_add,gtk+-$(NETSURF_GTK_MAJOR).0,GTK-$(NETSURF_GTK_MAJOR)))
+$(eval $(call pkg_config_find_and_add,gthread-2.0,GThread2))
+$(eval $(call pkg_config_find_and_add,gmodule-2.0,GModule2))
+
+
+CFLAGS += $(GTKCFLAGS)
+LDFLAGS += -lm
+
+# ---------------------------------------------------------------------------
+# Target setup
+# ---------------------------------------------------------------------------
+
+# Path to GTK resources
+NSGTK_RESOURCES_DIR := $(FRONTEND_RESOURCES_DIR)
+
+# The gtk binary target
+EXETARGET := nsgtk
+
+# The filter and target for split messages
+MESSAGES_FILTER=gtk
+MESSAGES_TARGET=$(NSGTK_RESOURCES_DIR)
+
+# ---------------------------------------------------------------------------
+# Windows flag setup
+# ---------------------------------------------------------------------------
+
+ifeq ($(HOST),Windows_NT)
+ CFLAGS += -U__STRICT_ANSI__
+endif
+
+# ----------------------------------------------------------------------------
+# Builtin resource handling
+# ----------------------------------------------------------------------------
+
+# builtin resource sources
+S_RESOURCE :=
+
+# Glib prior to 2.32 does not have GResource handling.
+#
+# This uses pkg-config to check for the minimum required version for
+# this feature in a way similar to the pkg_config_find_and_add_enabled
+# macro. Note we check for gmodule-2.0 which is a specific part of
+# glib we require.
+#
+# It would be nice if we could check for this functionality rather
+# than "knowing" the version but there does not appear to be a simple
+# way to implement that.
+#
+NETSURF_FEATURE_GRESOURCE_AVAILABLE := $(shell $(PKG_CONFIG) --atleast-version=2.32 gmodule-2.0 && echo yes)
+ifneq (,$(filter $(NETSURF_USE_GRESOURCE),AUTO YES))
+ifeq ($(NETSURF_FEATURE_GRESOURCE_AVAILABLE),yes)
+
+# Gresource use has been enabled
+NETSURF_FEATURE_GRESOURCE_ENABLED := yes
+
+#resource compiler tool
+GLIB_COMPILE_RESOURCES := glib-compile-resources
+CFLAGS += -DWITH_GRESOURCE
+
+NETSURF_GRESOURCE_XML := $(NSGTK_RESOURCES_DIR)/netsurf.gresource.xml
+MESSAGES_GRESOURCE_XML := $(NSGTK_RESOURCES_DIR)/messages.gresource.xml
+
+# generate the netsurf gresource source files
+$(OBJROOT)/netsurf_gresource.c: $(NETSURF_GRESOURCE_XML) $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir $(NSGTK_RESOURCES_DIR) --generate-dependencies $(NETSURF_GRESOURCE_XML))
+ $(VQ)echo "GRESORCE: $<"
+ $(Q)$(GLIB_COMPILE_RESOURCES) --generate-source --sourcedir $(NSGTK_RESOURCES_DIR) --target=$@ $<
+
+S_RESOURCE += $(OBJROOT)/netsurf_gresource.c
+
+# generate the messages gresource source file
+$(OBJROOT)/messages_gresource.c: $(MESSAGES_GRESOURCE_XML) $(addsuffix /Messages,$(addprefix $(MESSAGES_TARGET)/,$(MESSAGES_LANGUAGES)))
+ $(VQ)echo "GRESORCE: $<"
+ $(Q)$(GLIB_COMPILE_RESOURCES) --generate-source --sourcedir $(NSGTK_RESOURCES_DIR) --target=$@ $<
+
+S_RESOURCE += $(OBJROOT)/messages_gresource.c
+
+
+endif
+endif
+
+# Build pixbufs as inlines if enabled
+ifneq (,$(filter $(NETSURF_USE_INLINE_PIXBUF),AUTO YES))
+ifneq ($(NETSURF_FEATURE_GRESOURCE_ENABLED),yes)
+
+CFLAGS += -DWITH_BUILTIN_PIXBUF
+
+GTK_IMAGE_favicon := favicon.png
+GTK_IMAGE_netsurf := netsurf.xpm
+GTK_IMAGE_menu_cursor := menu_cursor.png
+
+# 1: input file
+# 2: output file
+# 3: bitmap name
+define convert_image
+
+# add converted pixbuf to builtin resource sources
+S_RESOURCE += $(2)
+
+$(2): $(1)
+ $(VQ)echo " INLINE: ${3}"
+ $(Q)echo "#include <gdk-pixbuf/gdk-pixdata.h>" > $(2)
+ $(Q)gdk-pixbuf-csource --extern --raw --name=$(3) $(1) >> $(2) || \
+ ( rm -f $(2) && false )
+
+endef
+
+$(eval $(foreach V,$(filter GTK_IMAGE_%,$(.VARIABLES)),$(call convert_image,$(addprefix $(NSGTK_RESOURCES_DIR)/,$($(V))),$(OBJROOT)/$(patsubst GTK_IMAGE_%,%,$(V)).c,$(patsubst GTK_IMAGE_%,%,$(V))_pixdata)))
+endif
+endif
+
+# ----------------------------------------------------------------------------
+# Source file setup
+# ----------------------------------------------------------------------------
+
+# S_FRONTEND are sources purely for the GTK frontend
+S_FRONTEND := gui.c schedule.c layout_pango.c bitmap.c plotters.c \
+ treeview.c scaffolding.c gdk.c completion.c login.c throbber.c \
+ selection.c history.c window.c fetch.c download.c menu.c \
+ print.c search.c tabs.c toolbar.c gettext.c \
+ compat.c cookies.c hotlist.c viewdata.c viewsource.c \
+ preferences.c about.c ssl_cert.c resources.c
+
+# This is the final source build list
+# Note this is deliberately *not* expanded here as common and image
+# are not yet available
+SOURCES = $(S_COMMON) $(S_IMAGE) $(S_BROWSER) $(S_RESOURCE) $(S_FRONTEND)
+
+# ----------------------------------------------------------------------------
+# Install target
+# ----------------------------------------------------------------------------
+
+GTK_RESOURCES_LIST := \
+ languages SearchEngines toolbarIndices ca-bundle.txt \
+ default.css adblock.css quirks.css internal.css \
+ credits.html licence.html welcome.html maps.html Messages \
+ default.ico favicon.png netsurf.png netsurf.xpm netsurf-16x16.xpm \
+ arrow_down_8x32.png
+
+GTK_RESOURCES_LIST := \
+ $(addprefix $(NSGTK_RESOURCES_DIR)/, $(GTK_RESOURCES_LIST)) \
+ $(wildcard $(NSGTK_RESOURCES_DIR)/*.gtk$(NETSURF_GTK_MAJOR).ui)
+
+# translations with more than just Messages files
+GTK_TRANSLATIONS_HTML := de en fr it ja nl
+
+install-gtk:
+ $(Q)mkdir -p $(DESTDIR)$(NETSURF_GTK_BIN)
+ $(Q)install nsgtk $(DESTDIR)$(NETSURF_GTK_BIN)netsurf
+ $(Q)mkdir -p $(DESTDIR)$(NETSURF_GTK_RESOURCES)icons
+ $(Q)install -m 0644 $(NSGTK_RESOURCES_DIR)/icons/*.png $(DESTDIR)$(NETSURF_GTK_RESOURCES)/icons
+ $(Q)mkdir -p $(DESTDIR)$(NETSURF_GTK_RESOURCES)throbber
+ $(Q)install -m 0644 $(NSGTK_RESOURCES_DIR)/throbber/*.png $(DESTDIR)$(NETSURF_GTK_RESOURCES)/throbber
+ $(Q)tar -c -h -C $(NSGTK_RESOURCES_DIR) -f - $(GTK_TRANSLATIONS_HTML) | tar -xv -C $(DESTDIR)$(NETSURF_GTK_RESOURCES) -f -
+ $(Q)install -m 0644 $(GTK_RESOURCES_LIST) $(DESTDIR)$(NETSURF_GTK_RESOURCES)
+
+# ----------------------------------------------------------------------------
+# Package target
+# ----------------------------------------------------------------------------
+
+package-gtk:
diff --git a/frontends/gtk/Makefile.defaults b/frontends/gtk/Makefile.defaults
new file mode 100644
index 000000000..fc352a020
--- /dev/null
+++ b/frontends/gtk/Makefile.defaults
@@ -0,0 +1,42 @@
+# ----------------------------------------------------------------------------
+# GTK-specific options
+# ----------------------------------------------------------------------------
+
+# Where to search for NetSurf's resources after looking in ~/.netsurf and
+# $NETSURFRES. It must have a trailing /
+NETSURF_GTK_RESOURCES := $(PREFIX)/share/netsurf/:./frontends/gtk/res/
+
+# Where to install the netsurf binary
+NETSURF_GTK_BIN := $(PREFIX)/bin/
+
+# Enable NetSurf's use of librsvg in conjunction with Cairo to display SVGs
+# Valid options: YES, NO, AUTO
+NETSURF_USE_RSVG := AUTO
+
+# Enable NetSurf's use of libsvgtiny for displaying SVGs
+# Valid options: YES, NO, AUTO
+NETSURF_USE_NSSVG := AUTO
+
+# Enable NetSurf's use of librosprite for displaying RISC OS Sprites
+# Valid options: YES, NO, AUTO
+NETSURF_USE_ROSPRITE := AUTO
+
+# Enable the use of GLib compiled in resource handling. This requires
+# GLib 2.32 or later
+# Valid options: YES, NO, AUTO
+NETSURF_USE_GRESOURCE := AUTO
+
+# Enable the use of compiled in inline pixbuf. This is depricated
+# since GLib 2.32. The automatic selection is disabled if GRESOURCE
+# handling is enabled
+# Valid options: YES, NO, AUTO
+NETSURF_USE_INLINE_PIXBUF := AUTO
+
+# Enable building the source object cache filesystem based backing store.
+NETSURF_FS_BACKING_STORE := YES
+
+# Set default GTK version to build for (2 or 3)
+NETSURF_GTK_MAJOR ?= 2
+
+# Optimisation levels
+CFLAGS += -O2
diff --git a/frontends/gtk/about.c b/frontends/gtk/about.c
new file mode 100644
index 000000000..d57afea7f
--- /dev/null
+++ b/frontends/gtk/about.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file gtk/about.c
+ *
+ * Implementation of gtk about dialog.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "utils/messages.h"
+#include "utils/nsoption.h"
+#include "utils/nsurl.h"
+#include "desktop/browser.h"
+#include "desktop/version.h"
+
+#include "gtk/warn.h"
+#include "gtk/compat.h"
+#include "gtk/about.h"
+
+#define ABOUT_RESPONSE_ID_LICENCE 1
+#define ABOUT_RESPONSE_ID_CREDITS 2
+
+
+/**
+ * Open a url and a browser window/tab
+ *
+ * \param url_text The text of the url to open
+ */
+static void about_open(const char *url_text)
+{
+ nsurl *url;
+ nserror ret;
+ enum browser_window_create_flags flags = BW_CREATE_HISTORY;
+
+ if (nsoption_bool(show_single_tab) == true) {
+ flags |= BW_CREATE_TAB;
+ }
+
+ ret = nsurl_create(url_text, &url);
+ if (ret == NSERROR_OK) {
+ ret = browser_window_create(flags, url, NULL, NULL, NULL);
+ nsurl_unref(url);
+ }
+
+ if (ret != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(ret), 0);
+ }
+}
+
+/**
+ * About dialog response handling.
+ *
+ * \param dialog The dialog widget
+ * \param response_id The response ID from the user clicking.
+ * \param user_data The value from the signal connection.
+ */
+static void
+nsgtk_about_dialog_response(GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ switch (response_id) {
+
+ case ABOUT_RESPONSE_ID_LICENCE:
+ about_open("about:credits");
+ break;
+
+ case ABOUT_RESPONSE_ID_CREDITS:
+ about_open("about:licence");
+ break;
+ }
+
+ /* close about dialog */
+ gtk_widget_destroy(GTK_WIDGET(dialog));
+}
+
+void nsgtk_about_dialog_init(GtkWindow *parent)
+{
+ GtkWidget *dialog, *vbox, *label;
+ gchar *name_string;
+ GList *pixbufs;
+
+ /* Create the dialog */
+ dialog = gtk_dialog_new_with_buttons("About NetSurf",
+ parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ "Licence", ABOUT_RESPONSE_ID_LICENCE,
+ "Credits", ABOUT_RESPONSE_ID_CREDITS,
+ "Close", GTK_RESPONSE_CANCEL,
+ NULL, NULL);
+
+ vbox = nsgtk_vbox_new(FALSE, 8);
+
+ gtk_box_pack_start(GTK_BOX(nsgtk_dialog_get_content_area(GTK_DIALOG(dialog))), vbox, TRUE, TRUE, 0);
+
+ /* NetSurf icon */
+ pixbufs = gtk_window_get_default_icon_list();
+ if (pixbufs != NULL) {
+ GtkWidget *image;
+
+ image = nsgtk_image_new_from_pixbuf_icon(GDK_PIXBUF(g_list_nth_data(pixbufs, 0)),
+ GTK_ICON_SIZE_DIALOG);
+ g_list_free(pixbufs);
+
+ gtk_box_pack_start(GTK_BOX(vbox), image, FALSE, FALSE, 0);
+ }
+
+ /* version string */
+ label = gtk_label_new (NULL);
+ name_string = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"bold\">NetSurf %s</span>", netsurf_version);
+ gtk_label_set_markup (GTK_LABEL (label), name_string);
+ g_free(name_string);
+ gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+ label = gtk_label_new(messages_get("AboutDesc"));
+ gtk_label_set_selectable(GTK_LABEL (label), TRUE);
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+ label = gtk_label_new(messages_get("NetSurfCopyright"));
+ gtk_label_set_selectable(GTK_LABEL(label), TRUE);
+ gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
+ gtk_box_pack_start(GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+ /* Remove separator */
+ nsgtk_dialog_set_has_separator(GTK_DIALOG (dialog), FALSE);
+
+ /* Ensure that the dialog box response is processed. */
+ g_signal_connect_swapped(dialog,
+ "response",
+ G_CALLBACK(nsgtk_about_dialog_response),
+ dialog);
+
+ /* Add the label, and show everything we've added to the dialog. */
+ gtk_widget_show_all(dialog);
+}
diff --git a/frontends/gtk/about.h b/frontends/gtk/about.h
new file mode 100644
index 000000000..bf3c9f58d
--- /dev/null
+++ b/frontends/gtk/about.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2008 Rob Kendrick <rjek@rjek.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_GTK_ABOUT_H
+#define NETSURF_GTK_ABOUT_H
+
+void nsgtk_about_dialog_init(GtkWindow *parent);
+
+#endif
diff --git a/frontends/gtk/bitmap.c b/frontends/gtk/bitmap.c
new file mode 100644
index 000000000..f8dcf1d17
--- /dev/null
+++ b/frontends/gtk/bitmap.c
@@ -0,0 +1,540 @@
+/*
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * Generic bitmap handling (GDK / GTK+ implementation).
+ *
+ * This implements the interface given by desktop/bitmap.h using GdkPixbufs.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+#include <cairo.h>
+#include <gtk/gtk.h>
+
+#include "utils/utils.h"
+#include "utils/log.h"
+#include "content/content.h"
+#include "image/bitmap.h"
+#include "desktop/plotters.h"
+
+#include "gtk/scaffolding.h"
+#include "gtk/plotters.h"
+#include "gtk/bitmap.h"
+
+
+/**
+ * Create a bitmap.
+ *
+ * \param width width of image in pixels
+ * \param height width of image in pixels
+ * \param state a flag word indicating the initial state
+ * \return an opaque struct bitmap, or NULL on memory exhaustion
+ */
+static void *bitmap_create(int width, int height, unsigned int state)
+{
+ struct bitmap *gbitmap;
+
+ gbitmap = calloc(1, sizeof(struct bitmap));
+ if (gbitmap != NULL) {
+ if ((state & BITMAP_OPAQUE) != 0) {
+ gbitmap->surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
+ } else {
+ gbitmap->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
+ }
+
+ if (cairo_surface_status(gbitmap->surface) != CAIRO_STATUS_SUCCESS) {
+ cairo_surface_destroy(gbitmap->surface);
+ free(gbitmap);
+ gbitmap = NULL;
+ }
+ }
+
+ return gbitmap;
+}
+
+
+/**
+ * Sets whether a bitmap should be plotted opaque
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \param opaque whether the bitmap should be plotted opaque
+ */
+static void bitmap_set_opaque(void *vbitmap, bool opaque)
+{
+ struct bitmap *gbitmap = (struct bitmap *)vbitmap;
+ cairo_format_t fmt;
+ cairo_surface_t *nsurface = NULL;
+
+ assert(gbitmap);
+
+ fmt = cairo_image_surface_get_format(gbitmap->surface);
+ if (fmt == CAIRO_FORMAT_RGB24) {
+ if (opaque == false) {
+ /* opaque to transparent */
+ nsurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ cairo_image_surface_get_width(gbitmap->surface),
+ cairo_image_surface_get_height(gbitmap->surface));
+
+ }
+
+ } else {
+ if (opaque == true) {
+ /* transparent to opaque */
+ nsurface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
+ cairo_image_surface_get_width(gbitmap->surface),
+ cairo_image_surface_get_height(gbitmap->surface));
+
+ }
+ }
+
+ if (nsurface != NULL) {
+ if (cairo_surface_status(nsurface) != CAIRO_STATUS_SUCCESS) {
+ cairo_surface_destroy(nsurface);
+ } else {
+ memcpy(cairo_image_surface_get_data(nsurface),
+ cairo_image_surface_get_data(gbitmap->surface),
+ cairo_image_surface_get_stride(gbitmap->surface) * cairo_image_surface_get_height(gbitmap->surface));
+ cairo_surface_destroy(gbitmap->surface);
+ gbitmap->surface = nsurface;
+
+ cairo_surface_mark_dirty(gbitmap->surface);
+
+ }
+
+ }
+}
+
+
+/**
+ * Tests whether a bitmap has an opaque alpha channel
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \return whether the bitmap is opaque
+ */
+static bool bitmap_test_opaque(void *vbitmap)
+{
+ struct bitmap *gbitmap = (struct bitmap *)vbitmap;
+ unsigned char *pixels;
+ int pcount;
+ int ploop;
+
+ assert(gbitmap);
+
+ pixels = cairo_image_surface_get_data(gbitmap->surface);
+
+ pcount = cairo_image_surface_get_stride(gbitmap->surface) *
+ cairo_image_surface_get_height(gbitmap->surface);
+
+ for (ploop = 3; ploop < pcount; ploop += 4) {
+ if (pixels[ploop] != 0xff) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Gets whether a bitmap should be plotted opaque
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ */
+static bool bitmap_get_opaque(void *vbitmap)
+{
+ struct bitmap *gbitmap = (struct bitmap *)vbitmap;
+ cairo_format_t fmt;
+
+ assert(gbitmap);
+
+ fmt = cairo_image_surface_get_format(gbitmap->surface);
+ if (fmt == CAIRO_FORMAT_RGB24) {
+ return true;
+ }
+
+ return false;
+}
+
+
+/**
+ * Return a pointer to the pixel data in a bitmap.
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \return pointer to the pixel buffer
+ *
+ * The pixel data is packed as BITMAP_FORMAT, possibly with padding at the end
+ * of rows. The width of a row in bytes is given by bitmap_get_rowstride().
+ */
+static unsigned char *bitmap_get_buffer(void *vbitmap)
+{
+ struct bitmap *gbitmap = (struct bitmap *)vbitmap;
+ int pixel_loop;
+ int pixel_count;
+ uint8_t *pixels;
+ uint32_t t, r, g, b;
+ cairo_format_t fmt;
+
+ assert(gbitmap);
+
+ cairo_surface_flush(gbitmap->surface);
+ pixels = cairo_image_surface_get_data(gbitmap->surface);
+
+ if (!gbitmap->converted)
+ return pixels;
+
+ fmt = cairo_image_surface_get_format(gbitmap->surface);
+ pixel_count = cairo_image_surface_get_width(gbitmap->surface) *
+ cairo_image_surface_get_height(gbitmap->surface);
+
+ if (fmt == CAIRO_FORMAT_RGB24) {
+ /* Opaque image */
+ for (pixel_loop=0; pixel_loop < pixel_count; pixel_loop++) {
+ /* Cairo surface is ARGB, written in native endian */
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ b = pixels[4 * pixel_loop + 0];
+ g = pixels[4 * pixel_loop + 1];
+ r = pixels[4 * pixel_loop + 2];
+ t = pixels[4 * pixel_loop + 3];
+#else
+ t = pixels[4 * pixel_loop + 0];
+ r = pixels[4 * pixel_loop + 1];
+ g = pixels[4 * pixel_loop + 2];
+ b = pixels[4 * pixel_loop + 3];
+#endif
+
+ /* Core bitmaps always have a component order of rgba,
+ * regardless of system endianness */
+ pixels[4 * pixel_loop + 0] = r;
+ pixels[4 * pixel_loop + 1] = g;
+ pixels[4 * pixel_loop + 2] = b;
+ pixels[4 * pixel_loop + 3] = t;
+ }
+ } else {
+ /* Alpha image: de-multiply alpha */
+ for (pixel_loop=0; pixel_loop < pixel_count; pixel_loop++) {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ b = pixels[4 * pixel_loop + 0];
+ g = pixels[4 * pixel_loop + 1];
+ r = pixels[4 * pixel_loop + 2];
+ t = pixels[4 * pixel_loop + 3];
+#else
+ t = pixels[4 * pixel_loop + 0];
+ r = pixels[4 * pixel_loop + 1];
+ g = pixels[4 * pixel_loop + 2];
+ b = pixels[4 * pixel_loop + 3];
+#endif
+
+ if (t != 0) {
+ r = (r << 8) / t;
+ g = (g << 8) / t;
+ b = (b << 8) / t;
+
+ r = (r > 255) ? 255 : r;
+ g = (g > 255) ? 255 : g;
+ b = (b > 255) ? 255 : b;
+ } else {
+ r = g = b = 0;
+ }
+
+ pixels[4 * pixel_loop + 0] = r;
+ pixels[4 * pixel_loop + 1] = g;
+ pixels[4 * pixel_loop + 2] = b;
+ pixels[4 * pixel_loop + 3] = t;
+ }
+ }
+
+ gbitmap->converted = false;
+
+ return (unsigned char *) pixels;
+}
+
+
+/**
+ * Find the width of a pixel row in bytes.
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \return width of a pixel row in the bitmap
+ */
+static size_t bitmap_get_rowstride(void *vbitmap)
+{
+ struct bitmap *gbitmap = (struct bitmap *)vbitmap;
+ assert(gbitmap);
+
+ return cairo_image_surface_get_stride(gbitmap->surface);
+}
+
+
+/**
+ * Find the bytes per pixel of a bitmap
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \return bytes per pixel
+ */
+static size_t bitmap_get_bpp(void *vbitmap)
+{
+ struct bitmap *gbitmap = (struct bitmap *)vbitmap;
+ assert(gbitmap);
+
+ return 4;
+}
+
+
+
+/**
+ * Free a bitmap.
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ */
+static void bitmap_destroy(void *vbitmap)
+{
+ struct bitmap *gbitmap = (struct bitmap *)vbitmap;
+ assert(gbitmap);
+
+ if (gbitmap->surface != NULL) {
+ cairo_surface_destroy(gbitmap->surface);
+ }
+ if (gbitmap->scsurface != NULL) {
+ cairo_surface_destroy(gbitmap->scsurface);
+ }
+ free(gbitmap);
+}
+
+
+/**
+ * Save a bitmap in the platform's native format.
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \param path pathname for file
+ * \param flags modify the behaviour of the save
+ * \return true on success, false on error and error reported
+ */
+static bool bitmap_save(void *vbitmap, const char *path, unsigned flags)
+{
+ struct bitmap *gbitmap = (struct bitmap *)vbitmap;
+ assert(gbitmap);
+
+ return false;
+}
+
+
+/**
+ * The bitmap image has changed, so flush any persistant cache.
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ */
+static void bitmap_modified(void *vbitmap)
+{
+ struct bitmap *gbitmap = (struct bitmap *)vbitmap;
+ int pixel_loop;
+ int pixel_count;
+ uint8_t *pixels;
+ uint32_t t, r, g, b;
+ cairo_format_t fmt;
+
+ assert(gbitmap);
+
+ fmt = cairo_image_surface_get_format(gbitmap->surface);
+
+ pixel_count = cairo_image_surface_get_width(gbitmap->surface) *
+ cairo_image_surface_get_height(gbitmap->surface);
+ pixels = cairo_image_surface_get_data(gbitmap->surface);
+
+ if (gbitmap->converted) {
+ cairo_surface_mark_dirty(gbitmap->surface);
+ return;
+ }
+
+ if (fmt == CAIRO_FORMAT_RGB24) {
+ /* Opaque image */
+ for (pixel_loop=0; pixel_loop < pixel_count; pixel_loop++) {
+ /* Core bitmaps always have a component order of rgba,
+ * regardless of system endianness */
+ r = pixels[4 * pixel_loop + 0];
+ g = pixels[4 * pixel_loop + 1];
+ b = pixels[4 * pixel_loop + 2];
+ t = pixels[4 * pixel_loop + 3];
+
+ /* Cairo surface is ARGB, written in native endian */
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ pixels[4 * pixel_loop + 0] = b;
+ pixels[4 * pixel_loop + 1] = g;
+ pixels[4 * pixel_loop + 2] = r;
+ pixels[4 * pixel_loop + 3] = t;
+#else
+ pixels[4 * pixel_loop + 0] = t;
+ pixels[4 * pixel_loop + 1] = r;
+ pixels[4 * pixel_loop + 2] = g;
+ pixels[4 * pixel_loop + 3] = b;
+#endif
+ }
+ } else {
+ /* Alpha image: pre-multiply alpha */
+ for (pixel_loop=0; pixel_loop < pixel_count; pixel_loop++) {
+ r = pixels[4 * pixel_loop + 0];
+ g = pixels[4 * pixel_loop + 1];
+ b = pixels[4 * pixel_loop + 2];
+ t = pixels[4 * pixel_loop + 3];
+
+ if (t != 0) {
+ r = ((r * (t + 1)) >> 8) & 0xff;
+ g = ((g * (t + 1)) >> 8) & 0xff;
+ b = ((b * (t + 1)) >> 8) & 0xff;
+ } else {
+ r = g = b = 0;
+ }
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ pixels[4 * pixel_loop + 0] = b;
+ pixels[4 * pixel_loop + 1] = g;
+ pixels[4 * pixel_loop + 2] = r;
+ pixels[4 * pixel_loop + 3] = t;
+#else
+ pixels[4 * pixel_loop + 0] = t;
+ pixels[4 * pixel_loop + 1] = r;
+ pixels[4 * pixel_loop + 2] = g;
+ pixels[4 * pixel_loop + 3] = b;
+#endif
+ }
+ }
+
+ cairo_surface_mark_dirty(gbitmap->surface);
+
+ gbitmap->converted = true;
+}
+
+/* exported interface documented in gtk/bitmap.h */
+int nsgtk_bitmap_get_width(void *vbitmap)
+{
+ struct bitmap *gbitmap = (struct bitmap *)vbitmap;
+ assert(gbitmap);
+
+ return cairo_image_surface_get_width(gbitmap->surface);
+}
+
+/* exported interface documented in gtk/bitmap.h */
+int nsgtk_bitmap_get_height(void *vbitmap)
+{
+ struct bitmap *gbitmap = (struct bitmap *)vbitmap;
+ assert(gbitmap);
+
+ return cairo_image_surface_get_height(gbitmap->surface);
+}
+
+/**
+ * Render content into a bitmap.
+ *
+ * \param bitmap The bitmap to draw to
+ * \param content The content to render
+ * \return true on success and bitmap updated else false
+ */
+static nserror
+bitmap_render(struct bitmap *bitmap, struct hlcache_handle *content)
+{
+ cairo_surface_t *dsurface = bitmap->surface;
+ cairo_surface_t *surface;
+ cairo_t *old_cr;
+ gint dwidth, dheight;
+ int cwidth, cheight;
+ struct redraw_context ctx = {
+ .interactive = false,
+ .background_images = true,
+ .plot = &nsgtk_plotters
+ };
+
+ assert(content);
+ assert(bitmap);
+
+ dwidth = cairo_image_surface_get_width(dsurface);
+ dheight = cairo_image_surface_get_height(dsurface);
+
+ /* Calculate size of buffer to render the content into */
+ /* Get the width from the content width, unless it exceeds 1024,
+ * in which case we use 1024. This means we never create excessively
+ * large render buffers for huge contents, which would eat memory and
+ * cripple performance.
+ */
+ cwidth = min(max(content_get_width(content), dwidth), 1024);
+
+ /* The height is set in proportion with the width, according to the
+ * aspect ratio of the required thumbnail. */
+ cheight = ((cwidth * dheight) + (dwidth / 2)) / dwidth;
+
+ /* Create surface to render into */
+ surface = cairo_surface_create_similar(dsurface, CAIRO_CONTENT_COLOR_ALPHA, cwidth, cheight);
+
+ if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
+ cairo_surface_destroy(surface);
+ return false;
+ }
+
+ old_cr = current_cr;
+ current_cr = cairo_create(surface);
+
+ /* render the content */
+ content_scaled_redraw(content, cwidth, cheight, &ctx);
+
+ cairo_destroy(current_cr);
+ current_cr = old_cr;
+
+ cairo_t *cr = cairo_create(dsurface);
+
+ /* Scale *before* setting the source surface (1) */
+ cairo_scale (cr, (double)dwidth / cwidth, (double)dheight / cheight);
+ cairo_set_source_surface (cr, surface, 0, 0);
+
+ /* To avoid getting the edge pixels blended with 0 alpha,
+ * which would occur with the default EXTEND_NONE. Use
+ * EXTEND_PAD for 1.2 or newer (2)
+ */
+ cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REFLECT);
+
+ /* Replace the destination with the source instead of overlaying */
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+
+ /* Do the actual drawing */
+ cairo_paint(cr);
+
+ cairo_destroy(cr);
+
+ cairo_surface_destroy(surface);
+
+ return NSERROR_OK;
+}
+
+
+static struct gui_bitmap_table bitmap_table = {
+ .create = bitmap_create,
+ .destroy = bitmap_destroy,
+ .set_opaque = bitmap_set_opaque,
+ .get_opaque = bitmap_get_opaque,
+ .test_opaque = bitmap_test_opaque,
+ .get_buffer = bitmap_get_buffer,
+ .get_rowstride = bitmap_get_rowstride,
+ .get_width = nsgtk_bitmap_get_width,
+ .get_height = nsgtk_bitmap_get_height,
+ .get_bpp = bitmap_get_bpp,
+ .save = bitmap_save,
+ .modified = bitmap_modified,
+ .render = bitmap_render,
+};
+
+struct gui_bitmap_table *nsgtk_bitmap_table = &bitmap_table;
diff --git a/frontends/gtk/bitmap.h b/frontends/gtk/bitmap.h
new file mode 100644
index 000000000..0f46d19a8
--- /dev/null
+++ b/frontends/gtk/bitmap.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2006 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_GTK_BITMAP_H
+#define NS_GTK_BITMAP_H
+
+#include <cairo.h>
+
+extern struct gui_bitmap_table *nsgtk_bitmap_table;
+
+struct bitmap {
+ cairo_surface_t *surface; /* original cairo surface */
+ cairo_surface_t *scsurface; /* scaled surface */
+ bool converted; /** set if the surface data has been converted */
+};
+
+int nsgtk_bitmap_get_width(void *vbitmap);
+int nsgtk_bitmap_get_height(void *vbitmap);
+
+#endif /* NS_GTK_BITMAP_H */
diff --git a/frontends/gtk/compat.c b/frontends/gtk/compat.c
new file mode 100644
index 000000000..4c5524b0e
--- /dev/null
+++ b/frontends/gtk/compat.c
@@ -0,0 +1,640 @@
+/*
+ * Copyright 2010 Rob Kendrick <rjek@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * Compatibility functions for older GTK versions implementation
+ */
+
+#include <stdint.h>
+
+#include "gtk/compat.h"
+
+#ifdef _SEXY_ICON_ENTRY_H_
+#include "gtk/sexy_icon_entry.c"
+
+/*
+ * exported interface documented in gtk/compat.h
+ *
+ * Only required for the lib sexy interface before 2.16
+ */
+GtkStateType nsgtk_widget_get_state(GtkWidget *widget)
+{
+#if GTK_CHECK_VERSION(2,18,0)
+ return gtk_widget_get_state(widget);
+#else
+ return GTK_WIDGET_STATE(widget);
+#endif
+}
+
+
+#endif
+
+void nsgtk_widget_set_can_focus(GtkWidget *widget, gboolean can_focus)
+{
+#if GTK_CHECK_VERSION(2,22,0)
+ gtk_widget_set_can_focus(widget, can_focus);
+#else
+ if (can_focus == TRUE)
+ GTK_WIDGET_SET_FLAGS(widget, GTK_CAN_FOCUS);
+ else
+ GTK_WIDGET_UNSET_FLAGS(widget, GTK_CAN_FOCUS);
+#endif
+}
+
+gboolean nsgtk_widget_has_focus(GtkWidget *widget)
+{
+#if GTK_CHECK_VERSION(2,20,0)
+ return gtk_widget_has_focus(widget);
+#else
+ return GTK_WIDGET_HAS_FOCUS(widget);
+#endif
+}
+
+gboolean nsgtk_widget_get_visible(GtkWidget *widget)
+{
+#if GTK_CHECK_VERSION(2,20,0)
+ return gtk_widget_get_visible(widget);
+#else
+ return GTK_WIDGET_VISIBLE(widget);
+#endif
+}
+
+gboolean nsgtk_widget_get_realized(GtkWidget *widget)
+{
+#if GTK_CHECK_VERSION(2,20,0)
+ return gtk_widget_get_realized(widget);
+#else
+ return GTK_WIDGET_REALIZED(widget);
+#endif
+}
+
+gboolean nsgtk_widget_get_mapped(GtkWidget *widget)
+{
+#if GTK_CHECK_VERSION(2,20,0)
+ return gtk_widget_get_mapped(widget);
+#else
+ return GTK_WIDGET_MAPPED(widget);
+#endif
+}
+
+gboolean nsgtk_widget_is_drawable(GtkWidget *widget)
+{
+#if GTK_CHECK_VERSION(2,18,0)
+ return gtk_widget_is_drawable(widget);
+#else
+ return GTK_WIDGET_DRAWABLE(widget);
+#endif
+}
+
+void nsgtk_dialog_set_has_separator(GtkDialog *dialog, gboolean setting)
+{
+#if GTK_CHECK_VERSION(2,21,8)
+ /* Deprecated */
+#else
+ gtk_dialog_set_has_separator(dialog, setting);
+#endif
+}
+
+GtkWidget *nsgtk_combo_box_text_new(void)
+{
+#if GTK_CHECK_VERSION(2,24,0)
+ return gtk_combo_box_text_new();
+#else
+ return gtk_combo_box_new_text();
+#endif
+}
+
+void nsgtk_combo_box_text_append_text(GtkWidget *combo_box, const gchar *text)
+{
+#if GTK_CHECK_VERSION(2,24,0)
+ gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo_box), text);
+#else
+ gtk_combo_box_append_text(GTK_COMBO_BOX(combo_box), text);
+#endif
+
+}
+
+gchar *nsgtk_combo_box_text_get_active_text(GtkWidget *combo_box)
+{
+#if GTK_CHECK_VERSION(2,24,0)
+ return gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo_box));
+#else
+ return gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo_box));
+#endif
+}
+
+GtkWidget *nsgtk_entry_new(void)
+{
+#if GTK_CHECK_VERSION(2,16,0)
+ return gtk_entry_new();
+#else
+ return GTK_WIDGET(sexy_icon_entry_new());
+#endif
+}
+
+void nsgtk_entry_set_icon_from_pixbuf(GtkWidget *entry,
+ GtkEntryIconPosition icon_pos,
+ GdkPixbuf *pixbuf)
+{
+#if GTK_CHECK_VERSION(2,16,0)
+ gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(entry), icon_pos, pixbuf);
+#else
+ GtkImage *image = GTK_IMAGE(gtk_image_new_from_pixbuf(pixbuf));
+
+ if (image != NULL) {
+ sexy_icon_entry_set_icon(SEXY_ICON_ENTRY(entry),
+ (SexyIconEntryPosition)icon_pos,
+ image);
+
+ g_object_unref(image);
+ }
+
+#endif
+}
+
+
+/* exported interface documented in gtk/compat.h */
+void nsgtk_entry_set_icon_from_stock(GtkWidget *entry,
+ GtkEntryIconPosition icon_pos,
+ const gchar *id)
+{
+#ifdef NSGTK_USE_ICON_NAME
+ gtk_entry_set_icon_from_icon_name(GTK_ENTRY(entry), icon_pos, id);
+#else
+#if GTK_CHECK_VERSION(2,16,0)
+ gtk_entry_set_icon_from_stock(GTK_ENTRY(entry), icon_pos, id);
+#else
+ GtkImage *image = GTK_IMAGE(gtk_image_new_from_stock(id,
+ GTK_ICON_SIZE_LARGE_TOOLBAR));
+
+ if (image != NULL) {
+ sexy_icon_entry_set_icon(SEXY_ICON_ENTRY(entry),
+ (SexyIconEntryPosition)icon_pos,
+ image);
+ g_object_unref(image);
+ }
+#endif
+#endif
+}
+
+
+/* exported interface documented in gtk/compat.h */
+GtkWidget *nsgtk_image_new_from_stock(const gchar *id, GtkIconSize size)
+{
+#ifdef NSGTK_USE_ICON_NAME
+ return gtk_image_new_from_icon_name(id, size);
+#else
+ return gtk_image_new_from_stock(id, size);
+#endif
+}
+
+
+/* exported interface documented in gtk/compat.h */
+GtkWidget *nsgtk_button_new_from_stock(const gchar *stock_id)
+{
+#ifdef NSGTK_USE_ICON_NAME
+ return gtk_button_new_with_label(stock_id);
+#else
+ return gtk_button_new_from_stock(stock_id);
+#endif
+}
+
+/* exported interface documented in gtk/compat.h */
+gboolean nsgtk_stock_lookup(const gchar *stock_id, GtkStockItem *item)
+{
+#ifdef NSGTK_USE_ICON_NAME
+ return FALSE;
+#else
+ return gtk_stock_lookup(stock_id, item);
+#endif
+}
+
+
+void nsgtk_widget_override_background_color(GtkWidget *widget,
+ GtkStateFlags state,
+ uint16_t a,
+ uint16_t r,
+ uint16_t g,
+ uint16_t b)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkRGBA colour;
+ colour.alpha = (double)a / 0xffff;
+ colour.red = (double)r / 0xffff;
+ colour.green = (double)g / 0xffff;
+ colour.blue = (double)b / 0xffff;
+ gtk_widget_override_background_color(widget, state, &colour);
+#else
+ GdkColor colour;
+ colour.pixel = a;
+ colour.red = r;
+ colour.green = g;
+ colour.blue = b;
+ gtk_widget_modify_bg(widget, state, &colour );
+#endif
+}
+
+GtkAdjustment *nsgtk_layout_get_vadjustment(GtkLayout *layout)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ return gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(layout));
+#else
+ return gtk_layout_get_vadjustment(layout);
+#endif
+}
+
+GtkAdjustment *nsgtk_layout_get_hadjustment(GtkLayout *layout)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ return gtk_scrollable_get_hadjustment(GTK_SCROLLABLE(layout));
+#else
+ return gtk_layout_get_hadjustment(layout);
+#endif
+}
+
+static void nsgtk_layout_set_adjustment_step_increment(GtkAdjustment *adj,
+ int value)
+{
+#if GTK_CHECK_VERSION(2,14,0)
+ gtk_adjustment_set_step_increment(adj, value);
+#else
+ adj->step_increment = value;
+#endif
+}
+
+void nsgtk_layout_set_hadjustment(GtkLayout *layout, GtkAdjustment *adj)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_scrollable_set_hadjustment(GTK_SCROLLABLE(layout), adj);
+#else
+ gtk_layout_set_hadjustment(layout, adj);
+#endif
+ nsgtk_layout_set_adjustment_step_increment(adj, 8);
+}
+
+void nsgtk_layout_set_vadjustment(GtkLayout *layout, GtkAdjustment *adj)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_scrollable_set_vadjustment(GTK_SCROLLABLE(layout), adj);
+#else
+ gtk_layout_set_vadjustment(layout, adj);
+#endif
+ nsgtk_layout_set_adjustment_step_increment(adj, 8);
+}
+
+GtkWidget *nsgtk_hbox_new(gboolean homogeneous, gint spacing)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ return gtk_box_new(GTK_ORIENTATION_HORIZONTAL, spacing);
+#else
+ return gtk_hbox_new(homogeneous, spacing);
+#endif
+}
+
+GtkWidget *nsgtk_vbox_new(gboolean homogeneous, gint spacing)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ return gtk_box_new(GTK_ORIENTATION_VERTICAL, spacing);
+#else
+ return gtk_vbox_new(homogeneous, spacing);
+#endif
+}
+
+GtkStateFlags nsgtk_widget_get_state_flags(GtkWidget *widget)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ return gtk_widget_get_state_flags(widget);
+#else
+#if GTK_CHECK_VERSION(2,18,0)
+ return gtk_widget_get_state(widget);
+#else
+ return 0; /* FIXME */
+#endif
+#endif
+}
+
+GtkStyleContext *nsgtk_widget_get_style_context(GtkWidget *widget)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ return gtk_widget_get_style_context(widget);
+#else
+ return widget->style;
+#endif
+}
+
+const PangoFontDescription* nsgtk_style_context_get_font(GtkStyleContext *style,
+ GtkStateFlags state)
+{
+#if GTK_CHECK_VERSION(3,8,0)
+ const PangoFontDescription* fontdesc = NULL;
+ gtk_style_context_get(style, state, GTK_STYLE_PROPERTY_FONT, &fontdesc, NULL);
+ return fontdesc;
+#else
+#if GTK_CHECK_VERSION(3,0,0)
+ return gtk_style_context_get_font(style, state);
+#else
+ return style->font_desc;
+#endif
+#endif
+}
+
+gulong nsgtk_connect_draw_event(GtkWidget *widget,
+ GCallback callback,
+ gpointer g)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ return g_signal_connect(G_OBJECT(widget), "draw", callback, g);
+#else
+ return g_signal_connect(G_OBJECT(widget), "expose_event", callback, g);
+#endif
+}
+
+void nsgdk_cursor_unref(GdkCursor *cursor)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ g_object_unref(cursor);
+#else
+ gdk_cursor_unref(cursor);
+#endif
+}
+
+void nsgtk_widget_modify_font(GtkWidget *widget,
+ PangoFontDescription *font_desc)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+/* FIXME */
+ return;
+#else
+ gtk_widget_modify_font(widget, font_desc);
+#endif
+}
+
+GdkWindow *nsgtk_widget_get_window(GtkWidget *widget)
+{
+#if GTK_CHECK_VERSION(2,14,0)
+ return gtk_widget_get_window(widget);
+#else
+ return widget->window;
+#endif
+}
+
+GtkWidget *nsgtk_dialog_get_content_area(GtkDialog *dialog)
+{
+#if GTK_CHECK_VERSION(2,14,0)
+ return gtk_dialog_get_content_area(dialog);
+#else
+ return dialog->vbox;
+#endif
+}
+
+gboolean nsgtk_show_uri(GdkScreen *screen,
+ const gchar *uri,
+ guint32 timestamp,
+ GError **error)
+{
+#if GTK_CHECK_VERSION(2,14,0)
+ return gtk_show_uri(screen, uri, timestamp, error);
+#else
+ return FALSE; /* FIXME */
+#endif
+}
+
+GdkWindow *nsgtk_layout_get_bin_window(GtkLayout *layout)
+{
+#if GTK_CHECK_VERSION(2,14,0)
+ return gtk_layout_get_bin_window(layout);
+#else
+ return layout->bin_window;
+#endif
+}
+
+gdouble nsgtk_adjustment_get_step_increment(GtkAdjustment *adjustment)
+{
+#if GTK_CHECK_VERSION(2,14,0)
+ return gtk_adjustment_get_step_increment(adjustment);
+#else
+ return adjustment->step_increment;
+#endif
+}
+
+gdouble nsgtk_adjustment_get_upper(GtkAdjustment *adjustment)
+{
+#if GTK_CHECK_VERSION(2,14,0)
+ return gtk_adjustment_get_upper(adjustment);
+#else
+ return adjustment->upper;
+#endif
+}
+
+gdouble nsgtk_adjustment_get_lower(GtkAdjustment *adjustment)
+{
+#if GTK_CHECK_VERSION(2,14,0)
+ return gtk_adjustment_get_lower(adjustment);
+#else
+ return adjustment->lower;
+#endif
+}
+
+gdouble nsgtk_adjustment_get_page_increment(GtkAdjustment *adjustment)
+{
+#if GTK_CHECK_VERSION(2,14,0)
+ return gtk_adjustment_get_page_increment(adjustment);
+#else
+ return adjustment->page_increment;
+#endif
+}
+
+void nsgtk_widget_get_allocation(GtkWidget *widget, GtkAllocation *allocation)
+{
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_get_allocation(widget, allocation);
+#else
+ allocation->x = widget->allocation.x;
+ allocation->y = widget->allocation.y;
+ allocation->width = widget->allocation.width;
+ allocation->height = widget->allocation.height;
+#endif
+}
+
+/* exported interface documented in gtk/compat.h */
+GtkWidget *nsgtk_image_new_from_pixbuf_icon(GdkPixbuf *pixbuf, GtkIconSize size)
+{
+#if GTK_CHECK_VERSION(3,10,0)
+ return gtk_image_new_from_pixbuf(pixbuf);
+#else
+ GtkIconSet *icon_set;
+ GtkWidget *image;
+
+ icon_set = gtk_icon_set_new_from_pixbuf(pixbuf);
+
+ image = gtk_image_new_from_icon_set(icon_set, size);
+
+ gtk_icon_set_unref(icon_set);
+
+ return image;
+#endif
+}
+
+
+/* exported interface documented in gtk/compat.h */
+void nsgtk_window_set_opacity(GtkWindow *window, gdouble opacity)
+{
+#if GTK_CHECK_VERSION(3,8,0)
+ gtk_widget_set_opacity(GTK_WIDGET(window), opacity);
+#else
+ gtk_window_set_opacity(window, opacity);
+#endif
+}
+
+/* exported interface documented in gtk/compat.h */
+void nsgtk_scrolled_window_add_with_viewport(GtkScrolledWindow *window,
+ GtkWidget *child)
+{
+#if GTK_CHECK_VERSION(3,8,0)
+ gtk_container_add(GTK_CONTAINER(window), child);
+#else
+ gtk_scrolled_window_add_with_viewport(window, child);
+#endif
+}
+
+/* exported interface documented in gtk/compat.h */
+GtkWidget *nsgtk_image_menu_item_new_with_mnemonic(const gchar *label)
+{
+#if GTK_CHECK_VERSION(3,10,0)
+ return gtk_menu_item_new_with_mnemonic(label);
+#else
+ return gtk_image_menu_item_new_with_mnemonic(label);
+#endif
+}
+
+/* exported interface documented in gtk/compat.h */
+void nsgtk_image_menu_item_set_image(GtkWidget *image_menu_item, GtkWidget *image)
+{
+#if !GTK_CHECK_VERSION(3,10,0)
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(image_menu_item), image);
+#endif
+}
+
+/* exported interface documented in gtk/compat.h */
+gboolean nsgtk_icon_size_lookup_for_settings(GtkSettings *settings,
+ GtkIconSize size,
+ gint *width,
+ gint *height)
+{
+#if GTK_CHECK_VERSION(3,10,0)
+ return gtk_icon_size_lookup(size, width, height);
+#else
+ return gtk_icon_size_lookup_for_settings(settings, size, width, height);
+#endif
+}
+
+/* exported interface documented in gtk/compat.h */
+void nsgtk_widget_set_alignment(GtkWidget *widget, GtkAlign halign, GtkAlign valign)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_set_halign(widget, halign);
+ gtk_widget_set_valign(widget, valign);
+#else
+ gfloat x, y;
+ switch(halign) {
+ case GTK_ALIGN_START:
+ x = 0.0;
+ break;
+
+ case GTK_ALIGN_END:
+ x = 1.0;
+ break;
+
+ default:
+ x = 0.5;
+ break;
+ }
+
+ switch(valign) {
+ case GTK_ALIGN_START:
+ y = 0.0;
+ break;
+
+ case GTK_ALIGN_END:
+ y = 1.0;
+ break;
+
+ default:
+ y = 0.5;
+ break;
+ }
+
+ gtk_misc_set_alignment(GTK_MISC(widget), x, y);
+#endif
+}
+
+/* exported interface documented in gtk/compat.h */
+void nsgtk_widget_set_margins(GtkWidget *widget, gint hmargin, gint vmargin)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+#if GTK_CHECK_VERSION(3,12,0)
+ gtk_widget_set_margin_start(widget, hmargin);
+ gtk_widget_set_margin_end(widget, hmargin);
+#else
+ gtk_widget_set_margin_left(widget, hmargin);
+ gtk_widget_set_margin_right(widget, hmargin);
+#endif
+ gtk_widget_set_margin_top(widget, vmargin);
+ gtk_widget_set_margin_bottom(widget, vmargin);
+#else
+ gtk_misc_set_padding(GTK_MISC(widget), hmargin, vmargin);
+#endif
+}
+
+/* exported interface documented in gtk/compat.h */
+guint
+nsgtk_builder_add_from_resource(GtkBuilder *builder,
+ const gchar *resource_path,
+ GError **error)
+{
+ guint ret;
+
+#ifdef WITH_GRESOURCE
+#if GTK_CHECK_VERSION(3,4,0)
+ ret = gtk_builder_add_from_resource(builder, resource_path, error);
+#else
+ GBytes *data;
+ const gchar *buffer;
+ gsize buffer_length;
+
+ g_assert(error && *error == NULL);
+
+ data = g_resources_lookup_data(resource_path, 0, error);
+ if (data == NULL) {
+ return 0;
+ }
+
+ buffer_length = 0;
+ buffer = g_bytes_get_data(data, &buffer_length);
+ g_assert(buffer != NULL);
+
+ ret = gtk_builder_add_from_string(builder, buffer, buffer_length, error);
+
+ g_bytes_unref(data);
+#endif
+#else
+ ret = 0; /* return an error as GResource not supported before GLIB 2.32 */
+#endif
+ return ret;
+}
diff --git a/frontends/gtk/compat.h b/frontends/gtk/compat.h
new file mode 100644
index 000000000..9554b0cba
--- /dev/null
+++ b/frontends/gtk/compat.h
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2010 Rob Kendrick <rjek@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Compatibility functions for older GTK versions (interface)
+ */
+
+#ifndef NETSURF_GTK_COMPAT_H_
+#define NETSURF_GTK_COMPAT_H_
+
+#include <stdint.h>
+
+#include <gtk/gtk.h>
+
+/* gtk 3.10 depricated the use of stock names */
+#if GTK_CHECK_VERSION(3,10,0)
+#define NSGTK_USE_ICON_NAME
+#else
+#undef NSGTK_USE_ICON_NAME
+#endif
+
+/* icon names instead of stock */
+#ifdef NSGTK_USE_ICON_NAME
+#define NSGTK_STOCK_ADD "list-add"
+#define NSGTK_STOCK_CANCEL "_Cancel"
+#define NSGTK_STOCK_CLEAR "edit-clear"
+#define NSGTK_STOCK_CLOSE "window-close"
+#define NSGTK_STOCK_FIND "edit-find"
+#define NSGTK_STOCK_GO_BACK "go-previous"
+#define NSGTK_STOCK_GO_FORWARD "go-next"
+#define NSGTK_STOCK_HOME "go-home"
+#define NSGTK_STOCK_INFO "dialog-information"
+#define NSGTK_STOCK_REFRESH "view-refresh"
+#define NSGTK_STOCK_SAVE "document-save"
+#define NSGTK_STOCK_SAVE_AS "document-save-as"
+#define NSGTK_STOCK_STOP "process-stop"
+#define NSGTK_STOCK_OK "_OK"
+#define NSGTK_STOCK_OPEN "_Open"
+#else
+#define NSGTK_STOCK_ADD GTK_STOCK_ADD
+#define NSGTK_STOCK_CANCEL GTK_STOCK_CANCEL
+#define NSGTK_STOCK_CLEAR GTK_STOCK_CLEAR
+#define NSGTK_STOCK_CLOSE GTK_STOCK_CLOSE
+#define NSGTK_STOCK_FIND GTK_STOCK_FIND
+#define NSGTK_STOCK_GO_BACK GTK_STOCK_GO_BACK
+#define NSGTK_STOCK_GO_FORWARD GTK_STOCK_GO_FORWARD
+#define NSGTK_STOCK_HOME GTK_STOCK_HOME
+#define NSGTK_STOCK_INFO GTK_STOCK_INFO
+#define NSGTK_STOCK_REFRESH GTK_STOCK_REFRESH
+#define NSGTK_STOCK_SAVE GTK_STOCK_SAVE
+#define NSGTK_STOCK_SAVE_AS GTK_STOCK_SAVE_AS
+#define NSGTK_STOCK_STOP GTK_STOCK_STOP
+#define NSGTK_STOCK_OK GTK_STOCK_OK
+#define NSGTK_STOCK_OPEN GTK_STOCK_OPEN
+#endif
+
+/* widget alignment only available since 3.0 */
+#if !GTK_CHECK_VERSION(3,0,0)
+typedef enum {
+ GTK_ALIGN_FILL,
+ GTK_ALIGN_START,
+ GTK_ALIGN_END,
+ GTK_ALIGN_CENTER,
+ GTK_ALIGN_BASELINE
+} GtkAlign;
+#endif
+
+/**
+ * Set the alignment of a widget.
+ *
+ * sets both the horizontal and vertical alignement of a widget
+ *
+ * @note this type of alignemnt was not available prior to GTK 3.0 so
+ * we emulate it using gtk_misc_set_alignment.
+ *
+ * \param widget The widget to set alignent on.
+ * \param halign The horizontal alignment to set.
+ * \param valign The vertical alignment to set
+ */
+void nsgtk_widget_set_alignment(GtkWidget *widget, GtkAlign halign, GtkAlign valign);
+
+/**
+ * Set the margins of a widget
+ *
+ * Sets the margin all round a widget.
+ *
+ * @note this type of margin was not available prior to GTK 3.0 so
+ * we emulate it using gtk_misc_set_padding.
+ *
+ * \param widget The widget to set alignent on.
+ * \param hmargin The horizontal margin.
+ * \param vmargin The vertical margin.
+ */
+void nsgtk_widget_set_margins(GtkWidget *widget, gint hmargin, gint vmargin);
+
+void nsgtk_widget_set_can_focus(GtkWidget *widget, gboolean can_focus);
+gboolean nsgtk_widget_has_focus(GtkWidget *widget);
+gboolean nsgtk_widget_get_visible(GtkWidget *widget);
+gboolean nsgtk_widget_get_realized(GtkWidget *widget);
+gboolean nsgtk_widget_get_mapped(GtkWidget *widget);
+gboolean nsgtk_widget_is_drawable(GtkWidget *widget);
+void nsgtk_dialog_set_has_separator(GtkDialog *dialog, gboolean setting);
+GtkWidget *nsgtk_combo_box_text_new(void);
+void nsgtk_combo_box_text_append_text(GtkWidget *combo_box, const gchar *text);
+gchar *nsgtk_combo_box_text_get_active_text(GtkWidget *combo_box);
+
+/**
+ * creates a new image widget of an appropriate icon size from a pixbuf.
+ *
+ * \param pixbuf The pixbuf to use as a source.
+ * \param size The size of icon to create
+ * \return An image widget.
+ */
+GtkWidget *nsgtk_image_new_from_pixbuf_icon(GdkPixbuf *pixbuf, GtkIconSize size);
+
+/* GTK prior to 2.16 needs the sexy interface for icons */
+#if !GTK_CHECK_VERSION(2,16,0)
+
+#include "gtk/sexy_icon_entry.h"
+
+typedef enum {
+ GTK_ENTRY_ICON_PRIMARY = SEXY_ICON_ENTRY_PRIMARY,
+ GTK_ENTRY_ICON_SECONDARY = SEXY_ICON_ENTRY_SECONDARY
+} GtkEntryIconPosition;
+
+GtkStateType nsgtk_widget_get_state(GtkWidget *widget);
+
+#endif
+
+#if GTK_CHECK_VERSION (2, 90, 7)
+#define GDK_KEY(symbol) GDK_KEY_##symbol
+#else
+#include <gdk/gdkkeysyms.h>
+#define GDK_KEY(symbol) GDK_##symbol
+#endif
+
+#if !GTK_CHECK_VERSION(3,0,0)
+typedef GtkStateType GtkStateFlags;
+typedef GtkStyle GtkStyleContext;
+
+#if GTK_CHECK_VERSION(2,22,0)
+enum {
+ GTK_IN_DESTRUCTION = 1 << 0,
+};
+#define GTK_OBJECT_FLAGS(obj) (GTK_OBJECT (obj)->flags)
+#endif
+
+#define gtk_widget_in_destruction(widget) \
+ (GTK_OBJECT_FLAGS(GTK_OBJECT(widget)) & GTK_IN_DESTRUCTION)
+
+#endif
+
+
+/**
+ * Sets the icon shown in the entry at the specified position from a
+ * stock image.
+ *
+ * Compatability interface for original deprecated in GTK 3.10
+ *
+ * \param entry The entry widget to set the icon on.
+ * \param icon_pos The position of the icon.
+ * \param stock_id the name of the stock item.
+ */
+void nsgtk_entry_set_icon_from_stock(GtkWidget *entry, GtkEntryIconPosition icon_pos, const gchar *stock_id);
+
+/**
+ * Creates a GtkImage displaying a stock icon.
+ *
+ * Compatability interface for original deprecated in GTK 3.10
+ *
+ * \param stock_id the name of the stock item.
+ * \param size The size of icon to create.
+ * \return The created image widget or NULL on error
+ */
+GtkWidget *nsgtk_image_new_from_stock(const gchar *stock_id, GtkIconSize size);
+
+/**
+ * Creates a new GtkButton containing the image and text from a stock item.
+ *
+ * Compatability interface for original deprecated in GTK 3.10
+ *
+ * \param stock_id the name of the stock item
+ */
+GtkWidget *nsgtk_button_new_from_stock(const gchar *stock_id);
+
+/**
+ * Fills item with the registered values for stock_id.
+ *
+ * Compatability interface for original deprecated in GTK 3.10
+ *
+ * \param stock_id the name of the stock item.
+ * \param item The structure to update if the stock_id was known.
+ * \return TRUE if stock_id was known.
+ */
+gboolean nsgtk_stock_lookup(const gchar *stock_id, GtkStockItem *item);
+
+void nsgtk_window_set_opacity(GtkWindow *window, gdouble opacity);
+
+void nsgtk_scrolled_window_add_with_viewport(GtkScrolledWindow *window, GtkWidget *child);
+
+GtkWidget *nsgtk_entry_new(void);
+
+void nsgtk_entry_set_icon_from_pixbuf(GtkWidget *entry, GtkEntryIconPosition icon_pos, GdkPixbuf *pixbuf);
+
+void nsgtk_widget_override_background_color(GtkWidget *widget, GtkStateFlags state, uint16_t a, uint16_t r, uint16_t g, uint16_t b);
+GtkWidget* nsgtk_hbox_new(gboolean homogeneous, gint spacing);
+GtkWidget* nsgtk_vbox_new(gboolean homogeneous, gint spacing);
+GtkStateFlags nsgtk_widget_get_state_flags(GtkWidget *widget);
+GtkStyleContext* nsgtk_widget_get_style_context(GtkWidget *widget);
+const PangoFontDescription* nsgtk_style_context_get_font(GtkStyleContext *style, GtkStateFlags state);
+gulong nsgtk_connect_draw_event(GtkWidget *widget, GCallback callback, gpointer g);
+void nsgdk_cursor_unref(GdkCursor *cursor);
+void nsgtk_widget_modify_font(GtkWidget *widget, PangoFontDescription *font_desc);
+GdkWindow *nsgtk_widget_get_window(GtkWidget *widget);
+GtkWidget *nsgtk_dialog_get_content_area(GtkDialog *dialog);
+gboolean nsgtk_show_uri(GdkScreen *screen, const gchar *uri, guint32 timestamp, GError **error);
+GdkWindow *nsgtk_layout_get_bin_window(GtkLayout *layout);
+void nsgtk_widget_get_allocation(GtkWidget *widget, GtkAllocation *allocation);
+
+gboolean nsgtk_icon_size_lookup_for_settings (GtkSettings *settings, GtkIconSize size, gint *width, gint *height);
+
+GtkAdjustment *nsgtk_layout_get_vadjustment(GtkLayout *layout);
+GtkAdjustment *nsgtk_layout_get_hadjustment(GtkLayout *layout);
+void nsgtk_layout_set_hadjustment(GtkLayout *layout, GtkAdjustment *adj);
+void nsgtk_layout_set_vadjustment(GtkLayout *layout, GtkAdjustment *adj);
+gdouble nsgtk_adjustment_get_step_increment(GtkAdjustment *adjustment);
+gdouble nsgtk_adjustment_get_upper(GtkAdjustment *adjustment);
+gdouble nsgtk_adjustment_get_lower(GtkAdjustment *adjustment);
+gdouble nsgtk_adjustment_get_page_increment(GtkAdjustment *adjustment);
+
+/* menu compatability */
+
+/**
+ * Creates a new GtkImageMenuItem containing a label.
+ *
+ * Compatability interface for original deprecated in GTK 3.10.
+ * @note post 3.10 this creates a GtkMenuItem.
+ *
+ * \param label The text of the button, with an underscore in front of
+ * the mnemonic character.
+ * \return a new GtkMenuItem
+ */
+GtkWidget *nsgtk_image_menu_item_new_with_mnemonic(const gchar *label);
+
+/**
+ * Sets the image of image_menu_item to the given widget.
+ *
+ * Compatability interface for original deprecated in GTK 3.10.
+ * @note post 3.10 this is empty as menu creation generates GtkMenuItem.
+ *
+ * \param image_menu_item The image menu entry item.
+ * \param image The image to set.
+ */
+void nsgtk_image_menu_item_set_image(GtkWidget *image_menu_item, GtkWidget *image);
+
+
+/**
+ * Parses a resource file containing a GtkBuilder UI definition and
+ * merges it with the current contents of builder.
+ *
+ * Compatability interface as this did not exist prior to GTK 3.4
+ *
+ * GTK prior to 3.4 can have the resources in a GResource but
+ * gtk_builder cannot directly instantiate from them
+ *
+ * GTK 3.4 onwards can use gtk_builder_add_from_resource() to add
+ * directly from resources. The gtk_builder_new_ type operations
+ * cannot be used because they are only available post 3.10 and handle
+ * all errors by aborting the application
+ *
+ * @note prior to GLIB 2.32 resources did not exist and this wrapper
+ * returns the error code.
+ *
+ * \param builder a GtkBuilder
+ * \param resource_path the path of the resource file to parse
+ * \param error return location for an error, or NULL.
+ * \return A positive value on success, 0 if an error occurred.
+ */
+guint nsgtk_builder_add_from_resource(GtkBuilder *builder, const gchar *resource_path, GError **error);
+
+#endif /* NETSURF_GTK_COMPAT_H */
diff --git a/frontends/gtk/completion.c b/frontends/gtk/completion.c
new file mode 100644
index 000000000..6dde728b3
--- /dev/null
+++ b/frontends/gtk/completion.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * Implementation of url entry completion.
+ */
+
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/nsoption.h"
+#include "content/urldb.h"
+#include "desktop/searchweb.h"
+#include "desktop/browser.h"
+
+#include "gtk/warn.h"
+#include "gtk/window.h"
+#include "gtk/completion.h"
+
+GtkListStore *nsgtk_completion_list;
+
+/**
+ * completion row matcher
+ */
+static gboolean nsgtk_completion_match(GtkEntryCompletion *completion,
+ const gchar *key,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ /* the completion list is modified to only contain valid
+ * entries so this simply returns TRUE to indicate all rows
+ * are in the list should be shown.
+ */
+ return TRUE;
+
+}
+
+
+/**
+ * callback for each entry to add to completion list
+ */
+static bool
+nsgtk_completion_udb_callback(nsurl *url, const struct url_data *data)
+{
+ GtkTreeIter iter;
+
+ if (data->visits != 0) {
+ gtk_list_store_append(nsgtk_completion_list, &iter);
+ gtk_list_store_set(nsgtk_completion_list, &iter, 0,
+ nsurl_access(url), -1);
+ }
+ return true;
+}
+
+/**
+ * event handler for when a completion suggestion is selected.
+ */
+static gboolean
+nsgtk_completion_match_select(GtkEntryCompletion *widget,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ GValue value = {0, };
+ struct nsgtk_scaffolding *g = user_data;
+ struct browser_window *bw = nsgtk_get_browser_window(nsgtk_scaffolding_top_level(g));
+ nserror ret;
+ nsurl *url;
+
+ gtk_tree_model_get_value(model, iter, 0, &value);
+
+ ret = search_web_omni(g_value_get_string(&value),
+ SEARCH_WEB_OMNI_NONE,
+ &url);
+
+ g_value_unset(&value);
+
+ if (ret == NSERROR_OK) {
+ ret = browser_window_navigate(bw,
+ url, NULL, BW_NAVIGATE_HISTORY,
+ NULL, NULL, NULL);
+ nsurl_unref(url);
+ }
+ if (ret != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(ret), 0);
+ }
+
+ return TRUE;
+}
+
+/* exported interface documented in completion.h */
+void nsgtk_completion_init(void)
+{
+ nsgtk_completion_list = gtk_list_store_new(1, G_TYPE_STRING);
+
+}
+
+/* exported interface documented in completion.h */
+gboolean nsgtk_completion_update(GtkEntry *entry)
+{
+ gtk_list_store_clear(nsgtk_completion_list);
+
+ if (nsoption_bool(url_suggestion) == true) {
+ urldb_iterate_partial(gtk_entry_get_text(entry),
+ nsgtk_completion_udb_callback);
+ }
+
+ return TRUE;
+}
+
+/* exported interface documented in completion.h */
+GtkEntryCompletion *nsgtk_url_entry_completion_new(struct nsgtk_scaffolding *gs)
+{
+ GtkEntryCompletion *completion;
+
+ completion = gtk_entry_completion_new();
+ gtk_entry_completion_set_match_func(completion,
+ nsgtk_completion_match, NULL, NULL);
+
+ gtk_entry_completion_set_model(completion,
+ GTK_TREE_MODEL(nsgtk_completion_list));
+
+ gtk_entry_completion_set_text_column(completion, 0);
+
+ gtk_entry_completion_set_minimum_key_length(completion, 1);
+
+ /* enable popup for completion */
+ gtk_entry_completion_set_popup_completion(completion, TRUE);
+
+ /* when selected callback */
+ g_signal_connect(G_OBJECT(completion), "match-selected",
+ G_CALLBACK(nsgtk_completion_match_select), gs);
+
+ g_object_set(G_OBJECT(completion),
+ "popup-set-width", TRUE,
+ "popup-single-match", TRUE,
+ NULL);
+
+ return completion;
+}
diff --git a/frontends/gtk/completion.h b/frontends/gtk/completion.h
new file mode 100644
index 000000000..9a1db293d
--- /dev/null
+++ b/frontends/gtk/completion.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * Interface to url entry completion.
+ */
+
+#ifndef _NETSURF_GTK_COMPLETION_H_
+#define _NETSURF_GTK_COMPLETION_H_
+
+struct nsgtk_scaffolding;
+
+/**
+ * initialise completion list store
+ */
+void nsgtk_completion_init(void);
+
+/**
+ * update completion list store.
+ */
+gboolean nsgtk_completion_update(GtkEntry *entry);
+
+/**
+ * create a new entry completion on a scaffold.
+ *
+ * \param gs The scaffoliding which the url entry is in.
+ */
+GtkEntryCompletion *nsgtk_url_entry_completion_new(struct nsgtk_scaffolding *gs);
+
+#endif
diff --git a/frontends/gtk/cookies.c b/frontends/gtk/cookies.c
new file mode 100644
index 000000000..f8f989347
--- /dev/null
+++ b/frontends/gtk/cookies.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Cookies (implementation).
+ */
+
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "desktop/cookie_manager.h"
+#include "desktop/plot_style.h"
+#include "desktop/tree.h"
+#include "desktop/textinput.h"
+
+#include "gtk/cookies.h"
+#include "gtk/plotters.h"
+#include "gtk/scaffolding.h"
+#include "gtk/treeview.h"
+#include "gtk/resources.h"
+
+#define MENUPROTO(x) static gboolean nsgtk_on_##x##_activate( \
+ GtkMenuItem *widget, gpointer g)
+#define MENUEVENT(x) { #x, G_CALLBACK(nsgtk_on_##x##_activate) }
+#define MENUHANDLER(x) gboolean nsgtk_on_##x##_activate(GtkMenuItem *widget, \
+ gpointer g)
+
+struct menu_events {
+ const char *widget;
+ GCallback handler;
+};
+
+/* edit menu */
+MENUPROTO(delete_selected);
+MENUPROTO(delete_all);
+MENUPROTO(select_all);
+MENUPROTO(clear_selection);
+
+/* view menu*/
+MENUPROTO(expand_all);
+MENUPROTO(expand_domains);
+MENUPROTO(expand_cookies);
+MENUPROTO(collapse_all);
+MENUPROTO(collapse_domains);
+MENUPROTO(collapse_cookies);
+
+
+static struct menu_events menu_events[] = {
+
+ /* edit menu */
+ MENUEVENT(delete_selected),
+ MENUEVENT(delete_all),
+ MENUEVENT(select_all),
+ MENUEVENT(clear_selection),
+
+ /* view menu*/
+ MENUEVENT(expand_all),
+ MENUEVENT(expand_domains),
+ MENUEVENT(expand_cookies),
+ MENUEVENT(collapse_all),
+ MENUEVENT(collapse_domains),
+ MENUEVENT(collapse_cookies),
+
+ {NULL, NULL}
+};
+
+static struct nsgtk_treeview *cookies_treeview;
+static GtkBuilder *cookie_builder;
+GtkWindow *wndCookies;
+
+/**
+ * Connects menu events in the cookies window.
+ */
+static void nsgtk_cookies_init_menu(void)
+{
+ struct menu_events *event = menu_events;
+ GtkWidget *w;
+
+ while (event->widget != NULL) {
+ w = GTK_WIDGET(gtk_builder_get_object(cookie_builder, event->widget));
+ if (w == NULL) {
+ LOG("Unable to connect menu widget ""%s""", event->widget); } else {
+ g_signal_connect(G_OBJECT(w), "activate", event->handler, cookies_treeview);
+ }
+ event++;
+ }
+}
+
+/* exported interface documented in gtk/cookies.h */
+nserror nsgtk_cookies_init(void)
+{
+ GtkScrolledWindow *scrolled;
+ GtkDrawingArea *drawing_area;
+ nserror res;
+
+ res = nsgtk_builder_new_from_resname("cookies", &cookie_builder);
+ if (res != NSERROR_OK) {
+ LOG("Cookie UI builder init failed");
+ return res;
+ }
+
+ gtk_builder_connect_signals(cookie_builder, NULL);
+
+ wndCookies = GTK_WINDOW(gtk_builder_get_object(cookie_builder,
+ "wndCookies"));
+
+ scrolled = GTK_SCROLLED_WINDOW(gtk_builder_get_object(cookie_builder,
+ "cookiesScrolled"));
+
+ drawing_area = GTK_DRAWING_AREA(gtk_builder_get_object(cookie_builder,
+ "cookiesDrawingArea"));
+
+ cookies_treeview = nsgtk_treeview_create(TREE_COOKIES,
+ wndCookies,
+ scrolled,
+ drawing_area);
+ if (cookies_treeview == NULL) {
+ return NSERROR_INIT_FAILED;
+ }
+
+#define CONNECT(obj, sig, callback, ptr) \
+ g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr))
+
+ CONNECT(wndCookies, "delete_event", gtk_widget_hide_on_delete, NULL);
+ CONNECT(wndCookies, "hide", nsgtk_tree_window_hide, cookies_treeview);
+
+ nsgtk_cookies_init_menu();
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Destroys the cookies window and performs any other necessary cleanup actions.
+ */
+void nsgtk_cookies_destroy(void)
+{
+ /** \todo what about cookie_builder? */
+ nsgtk_treeview_destroy(cookies_treeview);
+}
+
+
+/* edit menu */
+MENUHANDLER(delete_selected)
+{
+ cookie_manager_keypress(NS_KEY_DELETE_LEFT);
+ return TRUE;
+}
+
+MENUHANDLER(delete_all)
+{
+ cookie_manager_keypress(NS_KEY_SELECT_ALL);
+ cookie_manager_keypress(NS_KEY_DELETE_LEFT);
+ return TRUE;
+}
+
+MENUHANDLER(select_all)
+{
+ cookie_manager_keypress(NS_KEY_SELECT_ALL);
+ return TRUE;
+}
+
+MENUHANDLER(clear_selection)
+{
+ cookie_manager_keypress(NS_KEY_CLEAR_SELECTION);
+ return TRUE;
+}
+
+/* view menu*/
+MENUHANDLER(expand_all)
+{
+ cookie_manager_expand(false);
+ return TRUE;
+}
+
+MENUHANDLER(expand_domains)
+{
+ cookie_manager_expand(true);
+ return TRUE;
+}
+
+MENUHANDLER(expand_cookies)
+{
+ cookie_manager_expand(false);
+ return TRUE;
+}
+
+MENUHANDLER(collapse_all)
+{
+ cookie_manager_contract(true);
+ return TRUE;
+}
+
+MENUHANDLER(collapse_domains)
+{
+ cookie_manager_contract(true);
+ return TRUE;
+}
+
+MENUHANDLER(collapse_cookies)
+{
+ cookie_manager_contract(false);
+ return TRUE;
+}
diff --git a/frontends/gtk/cookies.h b/frontends/gtk/cookies.h
new file mode 100644
index 000000000..2d5c56d52
--- /dev/null
+++ b/frontends/gtk/cookies.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Cookies (interface).
+ */
+
+#ifndef __NSGTK_COOKIES_H__
+#define __NSGTK_COOKIES_H__
+
+extern GtkWindow *wndCookies;
+
+/**
+ * Creates the window for the cookies tree.
+ *
+ * \return NSERROR_OK on success else appropriate error code on faliure.
+ */
+nserror nsgtk_cookies_init(void);
+
+void nsgtk_cookies_destroy(void);
+
+#endif /* __NSGTK_COOKIES_H__ */
diff --git a/frontends/gtk/download.c b/frontends/gtk/download.c
new file mode 100644
index 000000000..b7eea2584
--- /dev/null
+++ b/frontends/gtk/download.c
@@ -0,0 +1,865 @@
+/*
+ * Copyright 2008 Michael Lester <element3260@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <gtk/gtk.h>
+#include <glib/gstdio.h>
+
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "utils/nsurl.h"
+#include "utils/messages.h"
+#include "utils/nsoption.h"
+#include "utils/string.h"
+#include "desktop/download.h"
+#include "desktop/gui_download.h"
+
+#include "gtk/warn.h"
+#include "gtk/scaffolding.h"
+#include "gtk/window.h"
+#include "gtk/compat.h"
+#include "gtk/resources.h"
+#include "gtk/download.h"
+
+#define UPDATE_RATE 500 /* In milliseconds */
+
+struct download_context;
+
+enum {
+ NSGTK_DOWNLOAD_PROGRESS,
+ NSGTK_DOWNLOAD_INFO,
+ NSGTK_DOWNLOAD_REMAINING,
+ NSGTK_DOWNLOAD_SPEED,
+ NSGTK_DOWNLOAD_PULSE,
+ NSGTK_DOWNLOAD_STATUS,
+ NSGTK_DOWNLOAD,
+
+ NSGTK_DOWNLOAD_N_COLUMNS
+};
+
+typedef enum {
+ NSGTK_DOWNLOAD_NONE,
+ NSGTK_DOWNLOAD_WORKING,
+ NSGTK_DOWNLOAD_ERROR,
+ NSGTK_DOWNLOAD_COMPLETE,
+ NSGTK_DOWNLOAD_CANCELED
+} nsgtk_download_status;
+
+typedef enum {
+ NSGTK_DOWNLOAD_PAUSE = 1 << 0,
+ NSGTK_DOWNLOAD_RESUME = 1 << 1,
+ NSGTK_DOWNLOAD_CANCEL = 1 << 2,
+ NSGTK_DOWNLOAD_CLEAR = 1 << 3
+} nsgtk_download_actions;
+
+struct gui_download_window {
+ struct download_context *ctx;
+ nsgtk_download_actions sensitivity;
+ nsgtk_download_status status;
+
+ GString *name;
+ GString *time_left;
+ gint size_total;
+ gint size_downloaded;
+ gint progress;
+ gfloat time_remaining;
+ gfloat start_time;
+ gfloat speed;
+
+ GtkTreeRowReference *row;
+ GIOChannel *write;
+ GError *error;
+};
+
+typedef void (*nsgtk_download_selection_action)(struct gui_download_window *dl);
+
+static GtkWindow *nsgtk_download_window, *nsgtk_download_parent;
+static GtkProgressBar *nsgtk_download_progress_bar;
+
+static GtkTreeView *nsgtk_download_tree;
+static GtkListStore *nsgtk_download_store;
+static GtkTreeSelection *nsgtk_download_selection;
+static GtkTreeIter nsgtk_download_iter;
+
+static GTimer *nsgtk_downloads_timer;
+static GList *nsgtk_downloads_list;
+static GtkButton *nsgtk_download_button_pause;
+static GtkButton *nsgtk_download_button_clear;
+static GtkButton *nsgtk_download_button_cancel;
+static GtkButton *nsgtk_download_button_resume;
+static gint nsgtk_downloads_num_active;
+static const gchar* status_messages[] = { NULL, "gtkWorking", "gtkError",
+ "gtkComplete", "gtkCanceled" };
+
+
+
+static GtkTreeView* nsgtk_download_tree_view_new(GtkBuilder *gladeFile)
+{
+ GtkTreeView *treeview;
+ GtkCellRenderer *renderer;
+
+ treeview = GTK_TREE_VIEW(gtk_builder_get_object(gladeFile, "treeDownloads"));
+
+ /* Progress column */
+ renderer = gtk_cell_renderer_progress_new();
+ gtk_tree_view_insert_column_with_attributes (treeview, -1,
+ messages_get("gtkProgress"), renderer, "value",
+ NSGTK_DOWNLOAD_PROGRESS, "pulse", NSGTK_DOWNLOAD_PULSE,
+ "text", NSGTK_DOWNLOAD_STATUS, NULL);
+
+ /* Information column */
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(G_OBJECT(renderer), "wrap-mode", PANGO_WRAP_WORD_CHAR,
+ "wrap-width", 300, NULL);
+ gtk_tree_view_insert_column_with_attributes (treeview, -1,
+ messages_get("gtkDetails"), renderer, "text",
+ NSGTK_DOWNLOAD_INFO, NULL);
+ gtk_tree_view_column_set_expand(gtk_tree_view_get_column(treeview,
+ NSGTK_DOWNLOAD_INFO), TRUE);
+
+ /* Time remaining column */
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes (treeview, -1,
+ messages_get("gtkRemaining"), renderer, "text",
+ NSGTK_DOWNLOAD_REMAINING, NULL);
+
+ /* Speed column */
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes (treeview, -1,
+ messages_get("gtkSpeed"), renderer, "text",
+ NSGTK_DOWNLOAD_SPEED, NULL);
+
+ return treeview;
+}
+
+static gint
+nsgtk_download_sort(GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer userdata)
+{
+ struct gui_download_window *dl1, *dl2;
+
+ gtk_tree_model_get(model, a, NSGTK_DOWNLOAD, &dl1, -1);
+ gtk_tree_model_get(model, b, NSGTK_DOWNLOAD, &dl2, -1);
+
+ return dl1->status - dl2->status;
+}
+
+static void
+nsgtk_download_sensitivity_update_buttons(nsgtk_download_actions sensitivity)
+{
+ /* Glade seems to pack the buttons in an arbitrary order */
+ enum { PAUSE_BUTTON, CLEAR_BUTTON, CANCEL_BUTTON, RESUME_BUTTON };
+
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_download_button_pause),
+ sensitivity & NSGTK_DOWNLOAD_PAUSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_download_button_clear),
+ sensitivity & NSGTK_DOWNLOAD_CLEAR);
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_download_button_cancel),
+ sensitivity & NSGTK_DOWNLOAD_CANCEL);
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_download_button_resume),
+ sensitivity & NSGTK_DOWNLOAD_RESUME);
+}
+
+static void nsgtk_download_sensitivity_evaluate(GtkTreeSelection *selection)
+{
+ GtkTreeIter iter;
+ GList *rows;
+ gboolean selected = gtk_tree_selection_count_selected_rows(selection);
+ GtkTreeModel *model = GTK_TREE_MODEL(nsgtk_download_store);
+ nsgtk_download_actions sensitivity = 0;
+ struct gui_download_window *dl;
+
+ if (selected) {
+ rows = gtk_tree_selection_get_selected_rows(selection, &model);
+ while (rows != NULL) {
+ gtk_tree_model_get_iter(model, &iter,
+ (GtkTreePath*)rows->data);
+ gtk_tree_model_get(model, &iter, NSGTK_DOWNLOAD,
+ &dl, -1);
+ sensitivity |= dl->sensitivity;
+ rows = rows->next;
+ }
+ } else {
+ rows = nsgtk_downloads_list;
+ while (rows != NULL) {
+ dl = rows->data;
+ sensitivity |= (dl->sensitivity & NSGTK_DOWNLOAD_CLEAR);
+ rows = rows->next;
+ }
+ }
+
+
+ nsgtk_download_sensitivity_update_buttons(sensitivity);
+}
+
+static void nsgtk_download_do(nsgtk_download_selection_action action)
+{
+ GList *rows, *dls = NULL;
+ GtkTreeModel *model = GTK_TREE_MODEL(nsgtk_download_store);
+ gboolean selection_exists = gtk_tree_selection_count_selected_rows(
+ nsgtk_download_selection);
+
+ if (selection_exists) {
+ rows = gtk_tree_selection_get_selected_rows(
+ nsgtk_download_selection, &model);
+ while (rows != NULL) {
+ struct gui_download_window *dl;
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(
+ nsgtk_download_store),
+ &nsgtk_download_iter,
+ (GtkTreePath*)rows->data);
+ gtk_tree_model_get(GTK_TREE_MODEL(nsgtk_download_store),
+ &nsgtk_download_iter, NSGTK_DOWNLOAD,
+ &dl, -1);
+ dls = g_list_prepend(dls, dl);
+
+ rows = rows->next;
+ }
+ g_list_foreach(rows, (GFunc)gtk_tree_path_free, NULL);
+ g_list_foreach(rows, (GFunc)g_free, NULL);
+ g_list_free(rows);
+ } else
+ dls = g_list_copy(nsgtk_downloads_list);
+
+ g_list_foreach(dls, (GFunc)action, NULL);
+ g_list_free(dls);
+}
+
+static gchar* nsgtk_download_info_to_string(struct gui_download_window *dl)
+{
+ gchar *size_info = g_strdup_printf(messages_get("gtkSizeInfo"),
+ human_friendly_bytesize(dl->size_downloaded),
+ dl->size_total == 0 ? messages_get("gtkUnknownSize") :
+ human_friendly_bytesize(dl->size_total));
+
+ gchar *r;
+
+ if (dl->status != NSGTK_DOWNLOAD_ERROR)
+ r = g_strdup_printf("%s\n%s", dl->name->str, size_info);
+ else
+ r = g_strdup_printf("%s\n%s", dl->name->str,
+ dl->error->message);
+
+ g_free(size_info);
+
+ return r;
+}
+
+static gchar* nsgtk_download_time_to_string(gint seconds)
+{
+ gint hours, minutes;
+
+ if (seconds < 0)
+ return g_strdup("-");
+
+ hours = seconds / 3600;
+ seconds -= hours * 3600;
+ minutes = seconds / 60;
+ seconds -= minutes * 60;
+
+ if (hours > 0)
+ return g_strdup_printf("%u:%02u:%02u", hours, minutes,
+ seconds);
+ else
+ return g_strdup_printf("%u:%02u", minutes, seconds);
+}
+
+static void nsgtk_download_store_update_item (struct gui_download_window *dl)
+{
+ gchar *info = nsgtk_download_info_to_string(dl);
+ char *human = human_friendly_bytesize(dl->speed);
+ char speed[strlen(human) + SLEN("/s") + 1];
+ sprintf(speed, "%s/s", human);
+ gchar *time = nsgtk_download_time_to_string(dl->time_remaining);
+ gboolean pulse = dl->status == NSGTK_DOWNLOAD_WORKING;
+
+ /* Updates iter (which is needed to set and get data) with the dl row */
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(nsgtk_download_store),
+ &nsgtk_download_iter,
+ gtk_tree_row_reference_get_path(dl->row));
+
+ gtk_list_store_set(nsgtk_download_store, &nsgtk_download_iter,
+ NSGTK_DOWNLOAD_PULSE, pulse ? dl->progress : -1,
+ NSGTK_DOWNLOAD_PROGRESS, pulse ? 0 : dl->progress,
+ NSGTK_DOWNLOAD_INFO, info,
+ NSGTK_DOWNLOAD_SPEED, dl->speed == 0 ? "-" : speed,
+ NSGTK_DOWNLOAD_REMAINING, time,
+ NSGTK_DOWNLOAD, dl,
+ -1);
+
+ g_free(info);
+ g_free(time);
+}
+
+static gboolean nsgtk_download_update(gboolean force_update)
+{
+ /* Be sure we need to update */
+ if (!nsgtk_widget_get_visible(GTK_WIDGET(nsgtk_download_window)))
+ return TRUE;
+
+ GList *list;
+ gchar *text;
+ gboolean update, pulse_mode = FALSE;
+ gint downloaded = 0, total = 0, dls = 0;
+ gfloat percent, elapsed = g_timer_elapsed(nsgtk_downloads_timer, NULL);
+ nsgtk_downloads_num_active = 0;
+
+ for (list = nsgtk_downloads_list; list != NULL; list = list->next) {
+ struct gui_download_window *dl = list->data;
+ update = force_update;
+
+ switch (dl->status) {
+ case NSGTK_DOWNLOAD_WORKING:
+ pulse_mode = TRUE;
+
+ case NSGTK_DOWNLOAD_NONE:
+ dl->speed = dl->size_downloaded /
+ (elapsed - dl->start_time);
+ if (dl->status == NSGTK_DOWNLOAD_NONE) {
+ dl->time_remaining = (dl->size_total -
+ dl->size_downloaded)/
+ dl->speed;
+ dl->progress = (gfloat)
+ dl->size_downloaded /
+ dl->size_total * 100;
+ } else
+ dl->progress++;
+
+ nsgtk_downloads_num_active++;
+ update = TRUE;
+
+ case NSGTK_DOWNLOAD_COMPLETE:
+ downloaded += dl->size_downloaded;
+ total += dl->size_total;
+ dls++;
+
+ default:
+ ;//Do nothing
+
+ }
+ if (update)
+ nsgtk_download_store_update_item(dl);
+ }
+
+ if (pulse_mode) {
+ text = g_strdup_printf(
+ messages_get(nsgtk_downloads_num_active > 1 ?
+ "gtkProgressBarPulse" :
+ "gtkProgressBarPulseSingle"),
+ nsgtk_downloads_num_active);
+ gtk_progress_bar_pulse(nsgtk_download_progress_bar);
+ gtk_progress_bar_set_text(nsgtk_download_progress_bar, text);
+ } else {
+ percent = total != 0 ? (gfloat)downloaded / total : 0;
+ text = g_strdup_printf(messages_get("gtkProgressBar"),
+ floor(percent*100), dls);
+ gtk_progress_bar_set_fraction(nsgtk_download_progress_bar,
+ percent);
+ gtk_progress_bar_set_text(nsgtk_download_progress_bar, text);
+ }
+
+ g_free(text);
+
+ if (nsgtk_downloads_num_active == 0)
+ return FALSE; /* Returning FALSE here cancels the g_timeout */
+ else
+ return TRUE;
+}
+
+static void nsgtk_download_store_clear_item(struct gui_download_window *dl)
+{
+ if (dl->sensitivity & NSGTK_DOWNLOAD_CLEAR) {
+ nsgtk_downloads_list = g_list_remove(nsgtk_downloads_list, dl);
+
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(nsgtk_download_store),
+ &nsgtk_download_iter,
+ gtk_tree_row_reference_get_path(dl->row));
+ gtk_list_store_remove(nsgtk_download_store,
+ &nsgtk_download_iter);
+
+ download_context_destroy(dl->ctx);
+ g_string_free(dl->name, TRUE);
+ g_string_free(dl->time_left, TRUE);
+ g_free(dl);
+
+ nsgtk_download_sensitivity_evaluate(nsgtk_download_selection);
+ nsgtk_download_update(FALSE);
+ }
+}
+
+static void nsgtk_download_tree_view_row_activated(GtkTreeView *tree,
+ GtkTreePath *path, GtkTreeViewColumn *column, gpointer data)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ model = gtk_tree_view_get_model(tree);
+
+ if (gtk_tree_model_get_iter(model, &iter, path)) {
+ /* TODO: This will be a context action (pause, start, clear) */
+ nsgtk_download_do(nsgtk_download_store_clear_item);
+ }
+}
+
+static void nsgtk_download_change_sensitivity(struct gui_download_window *dl,
+ nsgtk_download_actions sensitivity)
+{
+ dl->sensitivity = sensitivity;
+ nsgtk_download_sensitivity_evaluate(nsgtk_download_selection);
+}
+
+static void nsgtk_download_change_status (
+ struct gui_download_window *dl, nsgtk_download_status status)
+{
+ dl->status = status;
+ if (status != NSGTK_DOWNLOAD_NONE) {
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(nsgtk_download_store),
+ &nsgtk_download_iter,
+ gtk_tree_row_reference_get_path(dl->row));
+
+ gtk_list_store_set(nsgtk_download_store, &nsgtk_download_iter,
+ NSGTK_DOWNLOAD_STATUS,
+ messages_get(status_messages[status]), -1);
+ }
+}
+
+static void nsgtk_download_store_cancel_item (struct gui_download_window *dl)
+{
+ if (dl->sensitivity & NSGTK_DOWNLOAD_CANCEL) {
+ dl->speed = 0;
+ dl->size_downloaded = 0;
+ dl->progress = 0;
+ dl->time_remaining = -1;
+ nsgtk_download_change_sensitivity(dl, NSGTK_DOWNLOAD_CLEAR);
+ nsgtk_download_change_status(dl, NSGTK_DOWNLOAD_CANCELED);
+
+ download_context_abort(dl->ctx);
+
+ g_unlink(download_context_get_filename(dl->ctx));
+
+ nsgtk_download_update(TRUE);
+ }
+}
+
+static gboolean nsgtk_download_hide(GtkWidget *window)
+{
+ gtk_widget_hide(window);
+ return TRUE;
+}
+
+/* exported interface documented in gtk/download.h */
+nserror nsgtk_download_init(void)
+{
+ GtkBuilder* builder;
+ nserror res;
+
+ res = nsgtk_builder_new_from_resname("downloads", &builder);
+ if (res != NSERROR_OK) {
+ LOG("Download UI builder init failed");
+ return res;
+ }
+
+ gtk_builder_connect_signals(builder, NULL);
+
+ nsgtk_download_button_pause = GTK_BUTTON(gtk_builder_get_object(builder, "buttonPause"));
+ nsgtk_download_button_clear = GTK_BUTTON(gtk_builder_get_object(builder, "buttonClear"));
+ nsgtk_download_button_cancel = GTK_BUTTON(gtk_builder_get_object(builder, "buttonCancel"));
+ nsgtk_download_button_resume = GTK_BUTTON(gtk_builder_get_object(builder, "buttonPlay"));
+
+ nsgtk_download_progress_bar = GTK_PROGRESS_BAR(gtk_builder_get_object(builder, "progressBar"));
+ nsgtk_download_window = GTK_WINDOW(gtk_builder_get_object(builder, "wndDownloads"));
+ nsgtk_download_parent = NULL;
+
+ gtk_window_set_transient_for(GTK_WINDOW(nsgtk_download_window),
+ nsgtk_download_parent);
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(nsgtk_download_window),
+ FALSE);
+
+ nsgtk_downloads_timer = g_timer_new();
+
+ nsgtk_download_tree = nsgtk_download_tree_view_new(builder);
+
+ nsgtk_download_store = gtk_list_store_new(NSGTK_DOWNLOAD_N_COLUMNS,
+ G_TYPE_INT, /* % complete */
+ G_TYPE_STRING, /* Description */
+ G_TYPE_STRING, /* Time remaining */
+ G_TYPE_STRING, /* Speed */
+ G_TYPE_INT, /* Pulse */
+ G_TYPE_STRING, /* Status */
+ G_TYPE_POINTER /* Download structure */
+ );
+
+
+ gtk_tree_view_set_model(nsgtk_download_tree,
+ GTK_TREE_MODEL(nsgtk_download_store));
+
+ gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(nsgtk_download_store),
+ NSGTK_DOWNLOAD_STATUS,
+ (GtkTreeIterCompareFunc) nsgtk_download_sort, NULL, NULL);
+ gtk_tree_sortable_set_sort_column_id(
+ GTK_TREE_SORTABLE(nsgtk_download_store),
+ NSGTK_DOWNLOAD_STATUS, GTK_SORT_ASCENDING);
+
+ g_object_unref(nsgtk_download_store);
+
+ nsgtk_download_selection =
+ gtk_tree_view_get_selection(nsgtk_download_tree);
+ gtk_tree_selection_set_mode(nsgtk_download_selection,
+ GTK_SELECTION_MULTIPLE);
+
+ g_signal_connect(G_OBJECT(nsgtk_download_selection), "changed",
+ G_CALLBACK(nsgtk_download_sensitivity_evaluate), NULL);
+ g_signal_connect(nsgtk_download_tree, "row-activated",
+ G_CALLBACK(nsgtk_download_tree_view_row_activated),
+ NULL);
+ g_signal_connect_swapped(gtk_builder_get_object(builder, "buttonClear"),
+ "clicked",
+ G_CALLBACK(nsgtk_download_do),
+ nsgtk_download_store_clear_item);
+ g_signal_connect_swapped(gtk_builder_get_object(builder, "buttonCancel"),
+ "clicked",
+ G_CALLBACK(nsgtk_download_do),
+ nsgtk_download_store_cancel_item);
+ g_signal_connect(G_OBJECT(nsgtk_download_window), "delete-event",
+ G_CALLBACK(nsgtk_download_hide), NULL);
+
+ return NSERROR_OK;
+}
+
+void nsgtk_download_destroy ()
+{
+ nsgtk_download_do(nsgtk_download_store_cancel_item);
+}
+
+bool nsgtk_check_for_downloads (GtkWindow *parent)
+{
+ if (nsgtk_downloads_num_active != 0) {
+ GtkWidget *dialog;
+ dialog = gtk_message_dialog_new_with_markup(parent,
+ GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_NONE,
+ "<big><b>%s</b></big>\n\n"
+ "<small>%s</small>", messages_get("gtkQuit"),
+ messages_get("gtkDownloadsRunning"));
+ gtk_dialog_add_buttons(GTK_DIALOG(dialog), "gtk-cancel",
+ GTK_RESPONSE_CANCEL, "gtk-quit",
+ GTK_RESPONSE_CLOSE, NULL);
+
+ gint response = gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+
+ if (response == GTK_RESPONSE_CANCEL)
+ return true;
+ }
+
+ return false;
+}
+
+void nsgtk_download_show(GtkWindow *parent)
+{
+ gtk_window_set_transient_for(nsgtk_download_window,
+ nsgtk_download_parent);
+ gtk_window_present(nsgtk_download_window);
+}
+
+static gchar* nsgtk_download_dialog_show (const gchar *filename, const gchar *domain,
+ const gchar *size)
+{
+ enum { GTK_RESPONSE_DOWNLOAD, GTK_RESPONSE_SAVE_AS };
+ GtkWidget *dialog;
+ char *destination = NULL;
+ gchar *message = g_strdup(messages_get("gtkStartDownload"));
+ gchar *info = g_strdup_printf(messages_get("gtkInfo"), filename,
+ domain, size);
+
+ dialog = gtk_message_dialog_new_with_markup(nsgtk_download_parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
+ "<span size=\"x-large\" weight=\"ultrabold\">%s</span>"
+ "\n\n<small>%s</small>",
+ message, info);
+
+ gtk_dialog_add_buttons(GTK_DIALOG(dialog), NSGTK_STOCK_SAVE,
+ GTK_RESPONSE_DOWNLOAD, NSGTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL, NSGTK_STOCK_SAVE_AS,
+ GTK_RESPONSE_SAVE_AS, NULL);
+
+ gint result = gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ g_free(message);
+ g_free(info);
+
+ switch (result) {
+ case GTK_RESPONSE_SAVE_AS: {
+ dialog = gtk_file_chooser_dialog_new
+ (messages_get("gtkSave"),
+ nsgtk_download_parent,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NSGTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+ gtk_file_chooser_set_current_name
+ (GTK_FILE_CHOOSER(dialog), filename);
+ gtk_file_chooser_set_current_folder
+ (GTK_FILE_CHOOSER(dialog),
+ nsoption_charp(downloads_directory));
+ gtk_file_chooser_set_do_overwrite_confirmation
+ (GTK_FILE_CHOOSER(dialog),
+ nsoption_bool(request_overwrite));
+
+ gint result = gtk_dialog_run(GTK_DIALOG(dialog));
+ if (result == GTK_RESPONSE_ACCEPT)
+ destination = gtk_file_chooser_get_filename
+ (GTK_FILE_CHOOSER(dialog));
+ gtk_widget_destroy(dialog);
+ break;
+ }
+ case GTK_RESPONSE_DOWNLOAD: {
+ destination = malloc(strlen(nsoption_charp(downloads_directory))
+ + strlen(filename) + SLEN("/") + 1);
+ if (destination == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ break;
+ }
+ sprintf(destination, "%s/%s",
+ nsoption_charp(downloads_directory), filename);
+ /* Test if file already exists and display overwrite
+ * confirmation if needed */
+ if (g_file_test(destination, G_FILE_TEST_EXISTS) &&
+ nsoption_bool(request_overwrite)) {
+ message = g_strdup_printf(messages_get(
+ "gtkOverwrite"), filename);
+ info = g_strdup_printf(messages_get(
+ "gtkOverwriteInfo"),
+ nsoption_charp(downloads_directory));
+
+ dialog = gtk_message_dialog_new_with_markup(
+ nsgtk_download_parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_CANCEL,
+ "<b>%s</b>",message);
+ gtk_message_dialog_format_secondary_markup(
+ GTK_MESSAGE_DIALOG(dialog),
+ "%s", info);
+
+ GtkWidget *button = gtk_dialog_add_button(
+ GTK_DIALOG(dialog),
+ "_Replace",
+ GTK_RESPONSE_DOWNLOAD);
+ gtk_button_set_image(GTK_BUTTON(button),
+ nsgtk_image_new_from_stock(
+ NSGTK_STOCK_SAVE,
+ GTK_ICON_SIZE_BUTTON));
+
+ gint result = gtk_dialog_run(GTK_DIALOG(dialog));
+ if (result == GTK_RESPONSE_CANCEL)
+ destination = NULL;
+
+ gtk_widget_destroy(dialog);
+ g_free(message);
+ g_free(info);
+ }
+ break;
+ }
+ }
+ return destination;
+}
+
+
+static gboolean nsgtk_download_handle_error (GError *error)
+{
+ if (error != NULL) {
+ GtkWidget*dialog;
+ gchar *message = g_strdup_printf(messages_get("gtkFileError"),
+ error->message);
+
+ dialog = gtk_message_dialog_new_with_markup
+ (nsgtk_download_parent,
+ GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ "<big><b>%s</b></big>\n\n"
+ "<small>%s</small>", messages_get("gtkFailed"),
+ message);
+
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void nsgtk_download_store_create_item (struct gui_download_window *dl)
+{
+ nsgtk_download_store_update_item(dl);
+ /* The iter has already been updated to this row */
+ gtk_list_store_set(nsgtk_download_store, &nsgtk_download_iter,
+ NSGTK_DOWNLOAD, dl, -1);
+}
+
+static struct gui_download_window *
+gui_download_window_create(download_context *ctx, struct gui_window *gui)
+{
+ nsurl *url = download_context_get_url(ctx);
+ unsigned long total_size = download_context_get_total_length(ctx);
+ gchar *domain;
+ gchar *destination;
+ gboolean unknown_size = total_size == 0;
+ const char *size = (total_size == 0 ?
+ messages_get("gtkUnknownSize") :
+ human_friendly_bytesize(total_size));
+
+ nsgtk_download_parent =
+ nsgtk_scaffolding_window(nsgtk_get_scaffold(gui));
+
+ struct gui_download_window *download = malloc(sizeof *download);
+ if (download == NULL) {
+ return NULL;
+ }
+
+ /* set the domain to the host component of the url if it exists */
+ if (nsurl_has_component(url, NSURL_HOST)) {
+ domain = g_strdup(lwc_string_data(nsurl_get_component(url, NSURL_HOST)));
+ } else {
+ domain = g_strdup(messages_get("gtkUnknownHost"));
+ }
+ if (domain == NULL) {
+ free(download);
+ return NULL;
+ }
+
+ /* show the dialog */
+ destination = nsgtk_download_dialog_show(
+ download_context_get_filename(ctx), domain, size);
+ if (destination == NULL) {
+ g_free(domain);
+ free(download);
+ return NULL;
+ }
+
+ /* Add the new row and store the reference to it (which keeps track of
+ * the tree changes) */
+ gtk_list_store_prepend(nsgtk_download_store, &nsgtk_download_iter);
+ download->row = gtk_tree_row_reference_new(
+ GTK_TREE_MODEL(nsgtk_download_store),
+ gtk_tree_model_get_path(
+ GTK_TREE_MODEL(nsgtk_download_store),
+ &nsgtk_download_iter));
+
+ download->ctx = ctx;
+ download->name = g_string_new(download_context_get_filename(ctx));
+ download->time_left = g_string_new("");
+ download->size_total = total_size;
+ download->size_downloaded = 0;
+ download->speed = 0;
+ download->start_time = g_timer_elapsed(nsgtk_downloads_timer, NULL);
+ download->time_remaining = -1;
+ download->status = NSGTK_DOWNLOAD_NONE;
+ download->progress = 0;
+ download->error = NULL;
+ download->write =
+ g_io_channel_new_file(destination, "w", &download->error);
+
+ if (nsgtk_download_handle_error(download->error)) {
+ g_string_free(download->name, TRUE);
+ g_string_free(download->time_left, TRUE);
+ free(download);
+ return NULL;
+ }
+ g_io_channel_set_encoding(download->write, NULL, &download->error);
+
+ nsgtk_download_change_sensitivity(download, NSGTK_DOWNLOAD_CANCEL);
+
+ nsgtk_download_store_create_item(download);
+ nsgtk_download_show(nsgtk_download_parent);
+
+ if (unknown_size)
+ nsgtk_download_change_status(download, NSGTK_DOWNLOAD_WORKING);
+
+ if (nsgtk_downloads_num_active == 0) {
+ g_timeout_add(UPDATE_RATE,
+ (GSourceFunc) nsgtk_download_update, FALSE);
+ }
+
+ nsgtk_downloads_list = g_list_prepend(nsgtk_downloads_list, download);
+
+ return download;
+}
+
+
+static nserror gui_download_window_data(struct gui_download_window *dw,
+ const char *data, unsigned int size)
+{
+ g_io_channel_write_chars(dw->write, data, size, NULL, &dw->error);
+ if (dw->error != NULL) {
+ dw->speed = 0;
+ dw->time_remaining = -1;
+
+ nsgtk_download_change_sensitivity(dw, NSGTK_DOWNLOAD_CLEAR);
+ nsgtk_download_change_status(dw, NSGTK_DOWNLOAD_ERROR);
+
+ nsgtk_download_update(TRUE);
+
+ gtk_window_present(nsgtk_download_window);
+
+ return NSERROR_SAVE_FAILED;
+ }
+ dw->size_downloaded += size;
+
+ return NSERROR_OK;
+}
+
+
+static void gui_download_window_error(struct gui_download_window *dw,
+ const char *error_msg)
+{
+}
+
+
+static void gui_download_window_done(struct gui_download_window *dw)
+{
+ g_io_channel_shutdown(dw->write, TRUE, &dw->error);
+ g_io_channel_unref(dw->write);
+
+ dw->speed = 0;
+ dw->time_remaining = -1;
+ dw->progress = 100;
+ dw->size_total = dw->size_downloaded;
+ nsgtk_download_change_sensitivity(dw, NSGTK_DOWNLOAD_CLEAR);
+ nsgtk_download_change_status(dw, NSGTK_DOWNLOAD_COMPLETE);
+
+ if (nsoption_bool(downloads_clear))
+ nsgtk_download_store_clear_item(dw);
+ else
+ nsgtk_download_update(TRUE);
+}
+
+
+static struct gui_download_table download_table = {
+ .create = gui_download_window_create,
+ .data = gui_download_window_data,
+ .error = gui_download_window_error,
+ .done = gui_download_window_done,
+};
+
+struct gui_download_table *nsgtk_download_table = &download_table;
diff --git a/frontends/gtk/download.h b/frontends/gtk/download.h
new file mode 100644
index 000000000..0b1097655
--- /dev/null
+++ b/frontends/gtk/download.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2008 Michael Lester <element3260@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 GTK_DOWNLOAD_H
+#define GTK_DOWNLOAD_H
+
+#include <gtk/gtk.h>
+
+/**
+ * download operation table for gtk
+ */
+struct gui_download_table *nsgtk_download_table;
+
+/**
+ * Initialise download window ready for use.
+ *
+ * \return NSERROR_OK on success else appropriate error code on faliure.
+ */
+nserror nsgtk_download_init(void);
+
+void nsgtk_download_destroy (void);
+bool nsgtk_check_for_downloads(GtkWindow *parent);
+void nsgtk_download_show(GtkWindow *parent);
+void nsgtk_download_add(gchar *url, gchar *destination);
+
+#endif
diff --git a/frontends/gtk/fetch.c b/frontends/gtk/fetch.c
new file mode 100644
index 000000000..06770b6b2
--- /dev/null
+++ b/frontends/gtk/fetch.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2007, 2014 Vincent Sanders <vince@netsurf-browser.org>
+ * Copyright 2007 Rob Kendrick <rjek@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <gtk/gtk.h>
+
+#include "utils/hashtable.h"
+#include "utils/log.h"
+#include "utils/filepath.h"
+#include "utils/file.h"
+#include "utils/nsurl.h"
+#include "desktop/gui_fetch.h"
+
+#include "gtk/gui.h"
+#include "gtk/resources.h"
+#include "gtk/fetch.h"
+
+#define HASH_SIZE 117
+#define MAX_LINE_LEN 256
+
+static struct hash_table *mime_hash = NULL;
+
+void gtk_fetch_filetype_init(const char *mimefile)
+{
+ struct stat statbuf;
+ FILE *fh = NULL;
+
+ mime_hash = hash_create(HASH_SIZE);
+
+ /* first, check to see if /etc/mime.types in preference */
+
+ if ((stat("/etc/mime.types", &statbuf) == 0) &&
+ S_ISREG(statbuf.st_mode)) {
+ mimefile = "/etc/mime.types";
+ }
+
+ fh = fopen(mimefile, "r");
+
+ /* Some OSes (mentioning no Solarises) have a worthlessly tiny
+ * /etc/mime.types that don't include essential things, so we
+ * pre-seed our hash with the essentials. These will get
+ * over-ridden if they are mentioned in the mime.types file.
+ */
+
+ hash_add(mime_hash, "css", "text/css");
+ hash_add(mime_hash, "htm", "text/html");
+ hash_add(mime_hash, "html", "text/html");
+ hash_add(mime_hash, "jpg", "image/jpeg");
+ hash_add(mime_hash, "jpeg", "image/jpeg");
+ hash_add(mime_hash, "gif", "image/gif");
+ hash_add(mime_hash, "png", "image/png");
+ hash_add(mime_hash, "jng", "image/jng");
+ hash_add(mime_hash, "mng", "image/mng");
+ hash_add(mime_hash, "webp", "image/webp");
+ hash_add(mime_hash, "spr", "image/x-riscos-sprite");
+
+ if (fh == NULL) {
+ LOG("Unable to open a mime.types file, so using a minimal one for you.");
+ return;
+ }
+
+ while (feof(fh) == 0) {
+ char line[MAX_LINE_LEN], *ptr, *type, *ext;
+
+ if (fgets(line, sizeof(line), fh) == NULL)
+ break;
+
+ if ((feof(fh) == 0) && line[0] != '#') {
+ ptr = line;
+
+ /* search for the first non-whitespace character */
+ while (isspace(*ptr)) {
+ ptr++;
+ }
+
+ /* is this line empty other than leading whitespace? */
+ if (*ptr == '\n' || *ptr == '\0') {
+ continue;
+ }
+
+ type = ptr;
+
+ /* search for the first non-whitespace char or NUL or
+ * NL */
+ while (*ptr && (!isspace(*ptr)) && *ptr != '\n') {
+ ptr++;
+ }
+
+ if (*ptr == '\0' || *ptr == '\n') {
+ /* this mimetype has no extensions - read next
+ * line.
+ */
+ continue;
+ }
+
+ *ptr++ = '\0';
+
+ /* search for the first non-whitespace character which
+ * will be the first filename extenion */
+ while (isspace(*ptr)) {
+ ptr++;
+ }
+
+ while (true) {
+ ext = ptr;
+
+ /* search for the first whitespace char or
+ * NUL or NL which is the end of the ext.
+ */
+ while (*ptr &&
+ (!isspace(*ptr)) &&
+ *ptr != '\n') {
+ ptr++;
+ }
+
+ if (*ptr == '\0' || *ptr == '\n') {
+ /* special case for last extension on
+ * the line
+ */
+ *ptr = '\0';
+ hash_add(mime_hash, ext, type);
+ break;
+ }
+
+ *ptr++ = '\0';
+ hash_add(mime_hash, ext, type);
+
+ /* search for the first non-whitespace char or
+ * NUL or NL, to find start of next ext.
+ */
+ while (*ptr &&
+ (isspace(*ptr)) &&
+ *ptr != '\n') {
+ ptr++;
+ }
+ }
+ }
+ }
+
+ fclose(fh);
+}
+
+void gtk_fetch_filetype_fin(void)
+{
+ hash_destroy(mime_hash);
+}
+
+const char *fetch_filetype(const char *unix_path)
+{
+ struct stat statbuf;
+ char *ext;
+ const char *ptr;
+ char *lowerchar;
+ const char *type;
+ int l;
+
+ /* stat the path to attempt to determine if the file is special */
+ if (stat(unix_path, &statbuf) == 0) {
+ /* stat suceeded so can check for directory */
+
+ if (S_ISDIR(statbuf.st_mode)) {
+ return "application/x-netsurf-directory";
+ }
+ }
+
+ l = strlen(unix_path);
+
+ /* Hacky RISC OS compatibility */
+ if ((3 < l) && (strcasecmp(unix_path + l - 4, ",f79") == 0)) {
+ return "text/css";
+ } else if ((3 < l) && (strcasecmp(unix_path + l - 4, ",faf") == 0)) {
+ return "text/html";
+ } else if ((3 < l) && (strcasecmp(unix_path + l - 4, ",b60") == 0)) {
+ return "image/png";
+ } else if ((3 < l) && (strcasecmp(unix_path + l - 4, ",ff9") == 0)) {
+ return "image/x-riscos-sprite";
+ }
+
+ if (strchr(unix_path, '.') == NULL) {
+ /* no extension anywhere! */
+ return "text/plain";
+ }
+
+ ptr = unix_path + strlen(unix_path);
+ while (*ptr != '.' && *ptr != '/') {
+ ptr--;
+ }
+
+ if (*ptr != '.') {
+ return "text/plain";
+ }
+
+ ext = strdup(ptr + 1); /* skip the . */
+
+ /* the hash table only contains lower-case versions - make sure this
+ * copy is lower case too.
+ */
+ lowerchar = ext;
+ while (*lowerchar) {
+ *lowerchar = tolower(*lowerchar);
+ lowerchar++;
+ }
+
+ type = hash_get(mime_hash, ext);
+ free(ext);
+
+ if (type == NULL) {
+ type = "text/plain";
+ }
+
+ return type;
+}
+
+
+static nsurl *nsgtk_get_resource_url(const char *path)
+{
+ char buf[PATH_MAX];
+ nsurl *url = NULL;
+
+ /* favicon.ico -> favicon.png */
+ if (strcmp(path, "favicon.ico") == 0) {
+ nsurl_create("resource:favicon.png", &url);
+ } else {
+ netsurf_path_to_nsurl(filepath_sfind(respaths, buf, path), &url);
+ }
+
+ return url;
+}
+
+static struct gui_fetch_table fetch_table = {
+ .filetype = fetch_filetype,
+
+ .get_resource_url = nsgtk_get_resource_url,
+ .get_resource_data = nsgtk_data_from_resname,
+};
+
+struct gui_fetch_table *nsgtk_fetch_table = &fetch_table;
diff --git a/frontends/gtk/fetch.h b/frontends/gtk/fetch.h
new file mode 100644
index 000000000..a095adbf9
--- /dev/null
+++ b/frontends/gtk/fetch.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_GTK_FETCH_H
+#define NETSURF_GTK_FETCH_H
+
+struct gui_fetch_table *nsgtk_fetch_table;
+
+void gtk_fetch_filetype_init(const char *mimefile);
+void gtk_fetch_filetype_fin(void);
+const char *fetch_filetype(const char *unix_path);
+
+#endif
diff --git a/frontends/gtk/gdk.c b/frontends/gtk/gdk.c
new file mode 100644
index 000000000..9ed90bd8e
--- /dev/null
+++ b/frontends/gtk/gdk.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2011 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <string.h>
+
+#include "utils/log.h"
+
+#include "gtk/gdk.h"
+
+static void
+convert_alpha(guchar *dest_data,
+ int dest_stride,
+ guchar *src_data,
+ int src_stride,
+ int width,
+ int height)
+{
+ int x, y;
+
+ for (y = 0; y < height; y++) {
+ guint32 *src = (guint32 *) src_data;
+
+ for (x = 0; x < width; x++) {
+ guint alpha = src[x] >> 24;
+
+ if (alpha == 0) {
+ dest_data[x * 4 + 0] = 0;
+ dest_data[x * 4 + 1] = 0;
+ dest_data[x * 4 + 2] = 0;
+ } else {
+ dest_data[x * 4 + 0] = (((src[x] & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
+ dest_data[x * 4 + 1] = (((src[x] & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
+ dest_data[x * 4 + 2] = (((src[x] & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha;
+ }
+ dest_data[x * 4 + 3] = alpha;
+ }
+
+ src_data += src_stride;
+ dest_data += dest_stride;
+ }
+}
+
+
+GdkPixbuf *
+nsgdk_pixbuf_get_from_surface(cairo_surface_t *surface, int scwidth, int scheight)
+{
+ int width, height; /* source width and height */
+ cairo_surface_t *scsurface; /* scaled surface */
+ cairo_t *cr; /* cairo context for scaled surface */
+ GdkPixbuf *pixbuf; /* The result pixel buffer */
+
+ /* create pixmap */
+ pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, scwidth, scheight);
+ if (pixbuf == NULL) {
+ return NULL;
+ }
+
+ memset(gdk_pixbuf_get_pixels(pixbuf),
+ 0xff,
+ gdk_pixbuf_get_rowstride(pixbuf) * scheight);
+
+ /* scale cairo surface into new surface the target size */
+ cairo_surface_flush(surface); /* ensure source surface is ready */
+
+ /* get source surface dimensions */
+ width = cairo_image_surface_get_width(surface);
+ height = cairo_image_surface_get_height(surface);
+
+ /* scaled surface always has an alpha chanel for ease */
+ scsurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, scwidth, scheight);
+ if (cairo_surface_status(scsurface) != CAIRO_STATUS_SUCCESS) {
+ cairo_surface_destroy(scsurface);
+ g_object_unref(pixbuf);
+ LOG("Surface creation failed");
+ return NULL;
+ }
+
+ cr = cairo_create(scsurface);
+
+ /* Scale *before* setting the source surface */
+ cairo_scale(cr, (double)scwidth / width, (double)scheight / height);
+ cairo_set_source_surface(cr, surface, 0, 0);
+
+ /* To avoid getting the edge pixels blended with 0
+ * alpha, which would occur with the default
+ * EXTEND_NONE. Use EXTEND_PAD for 1.2 or newer
+ */
+ cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REFLECT);
+
+ /* Replace the destination with the source instead of overlaying */
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+
+ /* Do the actual drawing */
+ cairo_paint(cr);
+
+ cairo_destroy(cr);
+
+ /* copy data from surface into pixmap */
+ convert_alpha(gdk_pixbuf_get_pixels(pixbuf),
+ gdk_pixbuf_get_rowstride(pixbuf),
+ cairo_image_surface_get_data(scsurface),
+ cairo_image_surface_get_stride(scsurface),
+ scwidth, scheight);
+
+ cairo_surface_destroy(scsurface);
+
+ return pixbuf;
+}
+
diff --git a/frontends/gtk/gdk.h b/frontends/gtk/gdk.h
new file mode 100644
index 000000000..2fcee07f1
--- /dev/null
+++ b/frontends/gtk/gdk.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2011 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * GDK support functions for missing interfaces
+ */
+
+#ifndef NETSURF_GTK_GDK_H_
+#define NETSURF_GTK_GDK_H_
+
+#include <gtk/gtk.h>
+
+/** obtain a pixbuf of the specified size from a cairo surface.
+ *
+ * This is the same as the GTK+ 3 gdk_pixbuf_get_from_surface but
+ * actually works and is available on gtk 2
+ */
+GdkPixbuf *nsgdk_pixbuf_get_from_surface(cairo_surface_t *surface, int width, int height);
+
+#endif /* NETSURF_GTK_GDK_H */
diff --git a/frontends/gtk/gettext.c b/frontends/gtk/gettext.c
new file mode 100644
index 000000000..a9f6f48be
--- /dev/null
+++ b/frontends/gtk/gettext.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2013 Vincent Sanders <vince@kyllikki.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Localised gettext message support (implementation).
+ *
+ * Wrappers for gettext to the internal native language message support.
+ */
+
+#include <stdlib.h>
+
+#include "utils/messages.h"
+#include "gtk/gettext.h"
+
+char *gettext(const char *msgid)
+{
+ return dcgettext(NULL, msgid, 0);
+}
+
+char *dgettext(const char *domainname, const char *msgid)
+{
+ return dcgettext(domainname, msgid, 0);
+}
+
+char *dcgettext(const char *domainname, const char *msgid, int category)
+{
+ return (void *)messages_get(msgid);
+}
diff --git a/frontends/gtk/gettext.h b/frontends/gtk/gettext.h
new file mode 100644
index 000000000..726ba356e
--- /dev/null
+++ b/frontends/gtk/gettext.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Localised message support (interface).
+ *
+ * Provide gettext interface to the utility localisation routines
+ */
+
+#ifndef _NETSURF_GTK_GETTEXT_MESSAGES_H_
+#define _NETSURF_GTK_GETTEXT_MESSAGES_H_
+
+char *gettext(const char *msgid);
+char *dgettext(const char *domainname, const char *msgid);
+char *dcgettext(const char *domainname, const char *msgid, int category);
+
+#endif
diff --git a/frontends/gtk/gui.c b/frontends/gtk/gui.c
new file mode 100644
index 000000000..e705918bc
--- /dev/null
+++ b/frontends/gtk/gui.c
@@ -0,0 +1,1161 @@
+/*
+ * Copyright 2004-2010 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2010-2016 Vincent Sanders <vince@netsurf-browser.org>
+ * Copyright 2004-2009 John-Mark Bell <jmb@netsurf-browser.org>
+ * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
+ * Copyright 2006-2009 Daniel Silverstone <dsilvers@netsurf-browser.org>
+ * Copyright 2006-2008 Rob Kendrick <rjek@netsurf-browser.org>
+ * Copyright 2008 John Tytgat <joty@netsurf-browser.org>
+ * Copyright 2008 Adam Blokus <adamblokus@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <gtk/gtk.h>
+
+#include "utils/filepath.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "utils/file.h"
+#include "utils/nsoption.h"
+#include "content/fetchers.h"
+#include "content/hlcache.h"
+#include "content/urldb.h"
+#include "content/backing_store.h"
+#include "desktop/browser.h"
+#include "desktop/save_complete.h"
+#include "desktop/save_pdf.h"
+#include "desktop/searchweb.h"
+#include "desktop/textinput.h"
+#include "desktop/tree.h"
+#include "desktop/gui_misc.h"
+#include "desktop/netsurf.h"
+
+#include "gtk/compat.h"
+#include "gtk/warn.h"
+#include "gtk/completion.h"
+#include "gtk/cookies.h"
+#include "gtk/download.h"
+#include "gtk/fetch.h"
+#include "gtk/gui.h"
+#include "gtk/history.h"
+#include "gtk/hotlist.h"
+#include "gtk/throbber.h"
+#include "gtk/treeview.h"
+#include "gtk/window.h"
+#include "gtk/schedule.h"
+#include "gtk/selection.h"
+#include "gtk/search.h"
+#include "gtk/ssl_cert.h"
+#include "gtk/bitmap.h"
+#include "gtk/resources.h"
+#include "gtk/login.h"
+#include "gtk/layout_pango.h"
+
+bool nsgtk_complete = false;
+
+char *toolbar_indices_file_location;
+
+char *nsgtk_config_home; /* exported global defined in gtk/gui.h */
+
+GdkPixbuf *favicon_pixbuf; /** favicon default pixbuf */
+GdkPixbuf *win_default_icon_pixbuf; /** default window icon pixbuf */
+GdkPixbuf *arrow_down_pixbuf; /** arrow down pixbuf */
+
+GtkBuilder *warning_builder;
+
+char **respaths; /** resource search path vector */
+
+/**
+ * Cause an abnormal program termination.
+ *
+ * \note This never returns and is intended to terminate without any cleanup.
+ *
+ * \param error The message to display to the user.
+ */
+static void die(const char * const error)
+{
+ fprintf(stderr, "%s", error);
+ exit(EXIT_FAILURE);
+}
+
+/**
+ * Create an array of valid paths to search for resources.
+ *
+ * The idea is that all the complex path computation to find resources
+ * is performed here, once, rather than every time a resource is
+ * searched for.
+ */
+static char **
+nsgtk_init_resource_path(const char *config_home)
+{
+ char *resource_path;
+ int resource_path_len;
+ const gchar * const *langv;
+ char **pathv; /* resource path string vector */
+ char **respath; /* resource paths vector */
+
+ if (config_home != NULL) {
+ resource_path_len = snprintf(NULL, 0,
+ "%s:${NETSURFRES}:%s",
+ config_home,
+ GTK_RESPATH);
+ resource_path = malloc(resource_path_len + 1);
+ if (resource_path == NULL) {
+ return NULL;
+ }
+ snprintf(resource_path, resource_path_len + 1,
+ "%s:${NETSURFRES}:%s",
+ config_home,
+ GTK_RESPATH);
+ } else {
+ resource_path_len = snprintf(NULL, 0,
+ "${NETSURFRES}:%s",
+ GTK_RESPATH);
+ resource_path = malloc(resource_path_len + 1);
+ if (resource_path == NULL) {
+ return NULL;
+ }
+ snprintf(resource_path,
+ resource_path_len + 1,
+ "${NETSURFRES}:%s",
+ GTK_RESPATH);
+ }
+
+ pathv = filepath_path_to_strvec(resource_path);
+
+ langv = g_get_language_names();
+
+ respath = filepath_generate(pathv, langv);
+
+ filepath_free_strvec(pathv);
+
+ free(resource_path);
+
+ return respath;
+}
+
+
+/**
+ * Set option defaults for gtk frontend.
+ *
+ * @param defaults The option table to update.
+ * @return error status.
+ */
+static nserror set_defaults(struct nsoption_s *defaults)
+{
+ char *fname;
+
+ /* cookie file default */
+ fname = NULL;
+ netsurf_mkpath(&fname, NULL, 2, nsgtk_config_home, "Cookies");
+ if (fname != NULL) {
+ nsoption_setnull_charp(cookie_file, fname);
+ }
+
+ /* cookie jar default */
+ fname = NULL;
+ netsurf_mkpath(&fname, NULL, 2, nsgtk_config_home, "Cookies");
+ if (fname != NULL) {
+ nsoption_setnull_charp(cookie_jar, fname);
+ }
+
+ /* url database default */
+ fname = NULL;
+ netsurf_mkpath(&fname, NULL, 2, nsgtk_config_home, "URLs");
+ if (fname != NULL) {
+ nsoption_setnull_charp(url_file, fname);
+ }
+
+ /* bookmark database default */
+ fname = NULL;
+ netsurf_mkpath(&fname, NULL, 2, nsgtk_config_home, "Hotlist");
+ if (fname != NULL) {
+ nsoption_setnull_charp(hotlist_path, fname);
+ }
+
+ /* download directory default */
+ fname = getenv("HOME");
+ if (fname != NULL) {
+ nsoption_setnull_charp(downloads_directory, strdup(fname));
+ }
+
+ /* default path to certificates */
+ nsoption_setnull_charp(ca_path, strdup("/etc/ssl/certs"));
+
+ if ((nsoption_charp(cookie_file) == NULL) ||
+ (nsoption_charp(cookie_jar) == NULL) ||
+ (nsoption_charp(url_file) == NULL) ||
+ (nsoption_charp(hotlist_path) == NULL) ||
+ (nsoption_charp(downloads_directory) == NULL) ||
+ (nsoption_charp(ca_path) == NULL)) {
+ LOG("Failed initialising default resource paths");
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ /* set default font names */
+ nsoption_set_charp(font_sans, strdup("Sans"));
+ nsoption_set_charp(font_serif, strdup("Serif"));
+ nsoption_set_charp(font_mono, strdup("Monospace"));
+ nsoption_set_charp(font_cursive, strdup("Serif"));
+ nsoption_set_charp(font_fantasy, strdup("Serif"));
+
+ return NSERROR_OK;
+}
+
+
+
+
+/**
+ * Initialize GTK interface.
+ */
+static nserror nsgtk_init(int argc, char** argv, char **respath)
+{
+ char buf[PATH_MAX];
+ char *resource_filename;
+ char *addr = NULL;
+ nsurl *url;
+ nserror error;
+
+ error = nsgtk_builder_new_from_resname("warning", &warning_builder);
+ if (error != NSERROR_OK) {
+ LOG("Unable to initialise warning dialog");
+ return error;
+ }
+
+ gtk_builder_connect_signals(warning_builder, NULL);
+
+ /* set default icon if its available */
+ error = nsgdk_pixbuf_new_from_resname("netsurf.xpm",
+ &win_default_icon_pixbuf);
+ if (error == NSERROR_OK) {
+ LOG("Seting default window icon");
+ gtk_window_set_default_icon(win_default_icon_pixbuf);
+ }
+
+ /* Search engine sources */
+ resource_filename = filepath_find(respath, "SearchEngines");
+ search_web_init(resource_filename);
+ if (resource_filename != NULL) {
+ LOG("Using '%s' as Search Engines file", resource_filename);
+ free(resource_filename);
+ }
+
+ /* Default favicon */
+ error = nsgdk_pixbuf_new_from_resname("favicon.png", &favicon_pixbuf);
+ if (error != NSERROR_OK) {
+ favicon_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
+ false, 8, 16, 16);
+ }
+
+ /* arrow down icon */
+ error = nsgdk_pixbuf_new_from_resname("arrow_down_8x32.png",
+ &arrow_down_pixbuf);
+ if (error != NSERROR_OK) {
+ arrow_down_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
+ false, 8, 8, 32);
+ }
+
+ /* Toolbar inicies file */
+ toolbar_indices_file_location = filepath_find(respath,
+ "toolbarIndices");
+ LOG("Using '%s' as custom toolbar settings file",
+ toolbar_indices_file_location);
+
+ /* initialise throbber */
+ error = nsgtk_throbber_init();
+ if (error != NSERROR_OK) {
+ LOG("Unable to initialise throbber.");
+ return error;
+ }
+
+ /* Initialise completions - cannot fail */
+ nsgtk_completion_init();
+
+ filepath_sfinddef(respath, buf, "mime.types", "/etc/");
+ gtk_fetch_filetype_init(buf);
+
+ save_complete_init();
+
+ urldb_load(nsoption_charp(url_file));
+ urldb_load_cookies(nsoption_charp(cookie_file));
+
+ /* The tree view system needs to know the screen's DPI, so we
+ * find that out here, rather than when we create a first browser
+ * window.
+ */
+ browser_set_dpi(gdk_screen_get_resolution(gdk_screen_get_default()));
+ LOG("Set CSS DPI to %d", browser_get_dpi());
+
+ /* Initialise top level UI elements */
+ error = nsgtk_history_init();
+ if (error != NSERROR_OK) {
+ LOG("Unable to initialise global history window.");
+ return error;
+ }
+
+ error = nsgtk_download_init();
+ if (error != NSERROR_OK) {
+ LOG("Unable to initialise download window.");
+ return error;
+ }
+
+ error = nsgtk_cookies_init();
+ if (error != NSERROR_OK) {
+ LOG("Unable to initialise cookies window.");
+ return error;
+ }
+
+ error = nsgtk_hotlist_init();
+ if (error != NSERROR_OK) {
+ LOG("Unable to initialise hotlist window.");
+ return error;
+ }
+
+ /* If there is a url specified on the command line use it */
+ if (argc > 1) {
+ struct stat fs;
+ if (stat(argv[1], &fs) == 0) {
+ size_t addrlen;
+ char *rp = realpath(argv[1], NULL);
+ assert(rp != NULL);
+
+ /* calculate file url length including terminator */
+ addrlen = SLEN("file://") + strlen(rp) + 1;
+ addr = malloc(addrlen);
+ assert(addr != NULL);
+ snprintf(addr, addrlen, "file://%s", rp);
+ free(rp);
+ } else {
+ addr = strdup(argv[1]);
+ }
+ }
+ if (addr != NULL) {
+ /* managed to set up based on local launch */
+ } else if (nsoption_charp(homepage_url) != NULL) {
+ addr = strdup(nsoption_charp(homepage_url));
+ } else {
+ addr = strdup(NETSURF_HOMEPAGE);
+ }
+
+ /* create an initial browser window */
+ error = nsurl_create(addr, &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+
+ free(addr);
+
+ return error;
+}
+
+
+
+/**
+ * Ensures output logging stream is correctly configured
+ */
+static bool nslog_stream_configure(FILE *fptr)
+{
+ /* set log stream to be non-buffering */
+ setbuf(fptr, NULL);
+
+ return true;
+}
+
+
+/**
+ * Run the gtk event loop.
+ *
+ * The same as the standard gtk_main loop except this ensures active
+ * FD are added to the gtk poll event set.
+ */
+static void nsgtk_main(void)
+{
+ fd_set read_fd_set, write_fd_set, exc_fd_set;
+ int max_fd;
+ GPollFD *fd_list[1000];
+ unsigned int fd_count;
+
+ while (!nsgtk_complete) {
+ max_fd = -1;
+ fd_count = 0;
+ FD_ZERO(&read_fd_set);
+ FD_ZERO(&write_fd_set);
+ FD_ZERO(&exc_fd_set);
+
+ fetcher_fdset(&read_fd_set, &write_fd_set, &exc_fd_set, &max_fd);
+ for (int i = 0; i <= max_fd; i++) {
+ if (FD_ISSET(i, &read_fd_set)) {
+ GPollFD *fd = malloc(sizeof *fd);
+ fd->fd = i;
+ fd->events = G_IO_IN | G_IO_HUP | G_IO_ERR;
+ g_main_context_add_poll(0, fd, 0);
+ fd_list[fd_count++] = fd;
+ }
+ if (FD_ISSET(i, &write_fd_set)) {
+ GPollFD *fd = malloc(sizeof *fd);
+ fd->fd = i;
+ fd->events = G_IO_OUT | G_IO_ERR;
+ g_main_context_add_poll(0, fd, 0);
+ fd_list[fd_count++] = fd;
+ }
+ if (FD_ISSET(i, &exc_fd_set)) {
+ GPollFD *fd = malloc(sizeof *fd);
+ fd->fd = i;
+ fd->events = G_IO_ERR;
+ g_main_context_add_poll(0, fd, 0);
+ fd_list[fd_count++] = fd;
+ }
+ }
+
+ schedule_run();
+
+ gtk_main_iteration();
+
+ for (unsigned int i = 0; i != fd_count; i++) {
+ g_main_context_remove_poll(0, fd_list[i]);
+ free(fd_list[i]);
+ }
+ }
+}
+
+
+static void gui_quit(void)
+{
+ LOG("Quitting GUI");
+
+ /* Ensure all scaffoldings are destroyed before we go into exit */
+ nsgtk_download_destroy();
+ urldb_save_cookies(nsoption_charp(cookie_jar));
+ urldb_save(nsoption_charp(url_file));
+ nsgtk_cookies_destroy();
+ nsgtk_history_destroy();
+ nsgtk_hotlist_destroy();
+
+ free(toolbar_indices_file_location);
+
+ free(nsgtk_config_home);
+
+ gtk_fetch_filetype_fin();
+}
+
+static nserror gui_launch_url(struct nsurl *url)
+{
+ gboolean ok;
+ GError *error = NULL;
+
+ ok = nsgtk_show_uri(NULL, nsurl_access(url), GDK_CURRENT_TIME, &error);
+ if (ok == TRUE) {
+ return NSERROR_OK;
+ }
+
+ if (error) {
+ nsgtk_warning(messages_get("URIOpenError"), error->message);
+ g_error_free(error);
+ }
+ return NSERROR_NO_FETCH_HANDLER;
+}
+
+/* exported function documented in gtk/warn.h */
+nserror nsgtk_warning(const char *warning, const char *detail)
+{
+ char buf[300]; /* 300 is the size the RISC OS GUI uses */
+ static GtkWindow *nsgtk_warning_window;
+ GtkLabel *WarningLabel;
+
+ LOG("%s %s", warning, detail ? detail : "");
+ fflush(stdout);
+
+ nsgtk_warning_window = GTK_WINDOW(gtk_builder_get_object(warning_builder, "wndWarning"));
+ WarningLabel = GTK_LABEL(gtk_builder_get_object(warning_builder,
+ "labelWarning"));
+
+ snprintf(buf, sizeof(buf), "%s %s", messages_get(warning),
+ detail ? detail : "");
+ buf[sizeof(buf) - 1] = 0;
+
+ gtk_label_set_text(WarningLabel, buf);
+
+ gtk_widget_show_all(GTK_WIDGET(nsgtk_warning_window));
+
+ return NSERROR_OK;
+}
+
+
+static void nsgtk_PDF_set_pass(GtkButton *w, gpointer data)
+{
+ char **owner_pass = ((void **)data)[0];
+ char **user_pass = ((void **)data)[1];
+ GtkWindow *wnd = ((void **)data)[2];
+ GtkBuilder *password_builder = ((void **)data)[3];
+ char *path = ((void **)data)[4];
+
+ char *op, *op1;
+ char *up, *up1;
+
+ op = strdup(gtk_entry_get_text(
+ GTK_ENTRY(gtk_builder_get_object(password_builder,
+ "entryPDFOwnerPassword"))));
+ op1 = strdup(gtk_entry_get_text(
+ GTK_ENTRY(gtk_builder_get_object(password_builder,
+ "entryPDFOwnerPassword1"))));
+ up = strdup(gtk_entry_get_text(
+ GTK_ENTRY(gtk_builder_get_object(password_builder,
+ "entryPDFUserPassword"))));
+ up1 = strdup(gtk_entry_get_text(
+ GTK_ENTRY(gtk_builder_get_object(password_builder,
+ "entryPDFUserPassword1"))));
+
+
+ if (op[0] == '\0') {
+ gtk_label_set_text(GTK_LABEL(gtk_builder_get_object(password_builder,
+ "labelInfo")),
+ "Owner password must be at least 1 character long:");
+ free(op);
+ free(up);
+ } else if (!strcmp(op, up)) {
+ gtk_label_set_text(GTK_LABEL(gtk_builder_get_object(password_builder,
+ "labelInfo")),
+ "User and owner passwords must be different:");
+ free(op);
+ free(up);
+ } else if (!strcmp(op, op1) && !strcmp(up, up1)) {
+
+ *owner_pass = op;
+ if (up[0] == '\0')
+ free(up);
+ else
+ *user_pass = up;
+
+ free(data);
+ gtk_widget_destroy(GTK_WIDGET(wnd));
+ g_object_unref(G_OBJECT(password_builder));
+
+ save_pdf(path);
+
+ free(path);
+ } else {
+ gtk_label_set_text(GTK_LABEL(gtk_builder_get_object(password_builder,
+ "labelInfo")), "Passwords not confirmed:");
+ free(op);
+ free(up);
+ }
+
+ free(op1);
+ free(up1);
+}
+
+static void nsgtk_PDF_no_pass(GtkButton *w, gpointer data)
+{
+ GtkWindow *wnd = ((void **)data)[2];
+ GtkBuilder *password_builder = ((void **)data)[3];
+ char *path = ((void **)data)[4];
+
+ free(data);
+
+ gtk_widget_destroy(GTK_WIDGET(wnd));
+ g_object_unref(G_OBJECT(password_builder));
+
+ save_pdf(path);
+
+ free(path);
+}
+
+static void nsgtk_pdf_password(char **owner_pass, char **user_pass, char *path)
+{
+ GtkButton *ok, *no;
+ GtkWindow *wnd;
+ void **data;
+ GtkBuilder *password_builder;
+ nserror res;
+
+ res = nsgtk_builder_new_from_resname("password", &password_builder);
+ if (res != NSERROR_OK) {
+ LOG("Password UI builder init failed");
+ return;
+ }
+
+ gtk_builder_connect_signals(password_builder, NULL);
+
+ wnd = GTK_WINDOW(gtk_builder_get_object(password_builder,
+ "wndPDFPassword"));
+
+ data = malloc(5 * sizeof(void *));
+
+ *owner_pass = NULL;
+ *user_pass = NULL;
+
+ data[0] = owner_pass;
+ data[1] = user_pass;
+ data[2] = wnd;
+ data[3] = password_builder;
+ data[4] = path;
+
+ ok = GTK_BUTTON(gtk_builder_get_object(password_builder,
+ "buttonPDFSetPassword"));
+ no = GTK_BUTTON(gtk_builder_get_object(password_builder,
+ "buttonPDFNoPassword"));
+
+ g_signal_connect(G_OBJECT(ok), "clicked",
+ G_CALLBACK(nsgtk_PDF_set_pass), (gpointer)data);
+ g_signal_connect(G_OBJECT(no), "clicked",
+ G_CALLBACK(nsgtk_PDF_no_pass), (gpointer)data);
+
+ gtk_widget_show(GTK_WIDGET(wnd));
+}
+
+
+uint32_t gtk_gui_gdkkey_to_nskey(GdkEventKey *key)
+{
+ /* this function will need to become much more complex to support
+ * everything that the RISC OS version does. But this will do for
+ * now. I hope.
+ */
+ switch (key->keyval) {
+
+ case GDK_KEY(Tab):
+ return NS_KEY_TAB;
+
+ case GDK_KEY(BackSpace):
+ if (key->state & GDK_SHIFT_MASK)
+ return NS_KEY_DELETE_LINE_START;
+ else
+ return NS_KEY_DELETE_LEFT;
+ case GDK_KEY(Delete):
+ if (key->state & GDK_SHIFT_MASK)
+ return NS_KEY_DELETE_LINE_END;
+ else
+ return NS_KEY_DELETE_RIGHT;
+ case GDK_KEY(Linefeed): return 13;
+ case GDK_KEY(Return): return 10;
+ case GDK_KEY(Left): return NS_KEY_LEFT;
+ case GDK_KEY(Right): return NS_KEY_RIGHT;
+ case GDK_KEY(Up): return NS_KEY_UP;
+ case GDK_KEY(Down): return NS_KEY_DOWN;
+ case GDK_KEY(Home):
+ if (key->state & GDK_CONTROL_MASK)
+ return NS_KEY_TEXT_START;
+ else
+ return NS_KEY_LINE_START;
+ case GDK_KEY(End):
+ if (key->state & GDK_CONTROL_MASK)
+ return NS_KEY_TEXT_END;
+ else
+ return NS_KEY_LINE_END;
+ case GDK_KEY(Page_Up):
+ return NS_KEY_PAGE_UP;
+ case GDK_KEY(Page_Down):
+ return NS_KEY_PAGE_DOWN;
+ case 'a':
+ if (key->state & GDK_CONTROL_MASK)
+ return NS_KEY_SELECT_ALL;
+ return gdk_keyval_to_unicode(key->keyval);
+ case 'u':
+ if (key->state & GDK_CONTROL_MASK)
+ return NS_KEY_DELETE_LINE;
+ return gdk_keyval_to_unicode(key->keyval);
+ case 'c':
+ if (key->state & GDK_CONTROL_MASK)
+ return NS_KEY_COPY_SELECTION;
+ return gdk_keyval_to_unicode(key->keyval);
+ case 'v':
+ if (key->state & GDK_CONTROL_MASK)
+ return NS_KEY_PASTE;
+ return gdk_keyval_to_unicode(key->keyval);
+ case 'x':
+ if (key->state & GDK_CONTROL_MASK)
+ return NS_KEY_CUT_SELECTION;
+ return gdk_keyval_to_unicode(key->keyval);
+ case 'Z':
+ case 'y':
+ if (key->state & GDK_CONTROL_MASK)
+ return NS_KEY_REDO;
+ return gdk_keyval_to_unicode(key->keyval);
+ case 'z':
+ if (key->state & GDK_CONTROL_MASK)
+ return NS_KEY_UNDO;
+ return gdk_keyval_to_unicode(key->keyval);
+ case GDK_KEY(Escape):
+ return NS_KEY_ESCAPE;
+
+ /* Modifiers - do nothing for now */
+ case GDK_KEY(Shift_L):
+ case GDK_KEY(Shift_R):
+ case GDK_KEY(Control_L):
+ case GDK_KEY(Control_R):
+ case GDK_KEY(Caps_Lock):
+ case GDK_KEY(Shift_Lock):
+ case GDK_KEY(Meta_L):
+ case GDK_KEY(Meta_R):
+ case GDK_KEY(Alt_L):
+ case GDK_KEY(Alt_R):
+ case GDK_KEY(Super_L):
+ case GDK_KEY(Super_R):
+ case GDK_KEY(Hyper_L):
+ case GDK_KEY(Hyper_R):
+ return 0;
+
+ default:
+ return gdk_keyval_to_unicode(key->keyval);
+ }
+}
+
+
+/**
+ * create directory name and check it is acessible and a directory.
+ */
+static nserror
+check_dirname(const char *path, const char *leaf, char **dirname_out)
+{
+ nserror ret;
+ char *dirname = NULL;
+ struct stat dirname_stat;
+
+ ret = netsurf_mkpath(&dirname, NULL, 2, path, leaf);
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+
+ /* ensure access is possible and the entry is actualy
+ * a directory.
+ */
+ if (stat(dirname, &dirname_stat) == 0) {
+ if (S_ISDIR(dirname_stat.st_mode)) {
+ if (access(dirname, R_OK | W_OK) == 0) {
+ *dirname_out = dirname;
+ return NSERROR_OK;
+ } else {
+ ret = NSERROR_PERMISSION;
+ }
+ } else {
+ ret = NSERROR_NOT_DIRECTORY;
+ }
+ } else {
+ ret = NSERROR_NOT_FOUND;
+ }
+
+ free(dirname);
+
+ return ret;
+}
+
+/**
+ * Get the path to the config directory.
+ *
+ * @param config_home_out Path to configuration directory.
+ * @return NSERROR_OK on sucess and \a config_home_out updated else error code.
+ */
+static nserror get_config_home(char **config_home_out)
+{
+ nserror ret;
+ char *home_dir;
+ char *xdg_config_dir;
+ char *config_home;
+
+ home_dir = getenv("HOME");
+
+ /* The old $HOME/.netsurf/ directory should be used if it
+ * exists and is accessible.
+ */
+ if (home_dir != NULL) {
+ ret = check_dirname(home_dir, ".netsurf", &config_home);
+ if (ret == NSERROR_OK) {
+ LOG("\"%s\"", config_home);
+ *config_home_out = config_home;
+ return ret;
+ }
+ }
+
+ /* $XDG_CONFIG_HOME defines the base directory
+ * relative to which user specific configuration files
+ * should be stored.
+ */
+ xdg_config_dir = getenv("XDG_CONFIG_HOME");
+
+ if ((xdg_config_dir == NULL) || (*xdg_config_dir == 0)) {
+ /* If $XDG_CONFIG_HOME is either not set or empty, a
+ * default equal to $HOME/.config should be used.
+ */
+
+ /** @todo the meaning of empty is never defined so I
+ * am assuming it is a zero length string but is it
+ * supposed to mean "whitespace" and if so what counts
+ * as whitespace? (are tabs etc. counted or should
+ * isspace() be used)
+ */
+
+ /* the HOME envvar is required */
+ if (home_dir == NULL) {
+ return NSERROR_NOT_DIRECTORY;
+ }
+
+ ret = check_dirname(home_dir, ".config/netsurf", &config_home);
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+ } else {
+ ret = check_dirname(xdg_config_dir, "netsurf", &config_home);
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+ }
+
+ LOG("\"%s\"", config_home);
+
+ *config_home_out = config_home;
+ return NSERROR_OK;
+}
+
+static nserror create_config_home(char **config_home_out)
+{
+ char *config_home = NULL;
+ char *home_dir;
+ char *xdg_config_dir;
+ nserror ret;
+
+ LOG("Attempting to create configuration directory");
+
+ /* $XDG_CONFIG_HOME defines the base directory
+ * relative to which user specific configuration files
+ * should be stored.
+ */
+ xdg_config_dir = getenv("XDG_CONFIG_HOME");
+
+ if ((xdg_config_dir == NULL) || (*xdg_config_dir == 0)) {
+ home_dir = getenv("HOME");
+
+ if ((home_dir == NULL) || (*home_dir == 0)) {
+ return NSERROR_NOT_DIRECTORY;
+ }
+
+ ret = netsurf_mkpath(&config_home, NULL, 4, home_dir, ".config","netsurf", "/");
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+ } else {
+ ret = netsurf_mkpath(&config_home, NULL, 3, xdg_config_dir, "netsurf", "/");
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+ }
+
+ /* ensure all elements of path exist (the trailing / is required) */
+ ret = netsurf_mkdir_all(config_home);
+ if (ret != NSERROR_OK) {
+ free(config_home);
+ return ret;
+ }
+
+ /* strip the trailing separator */
+ config_home[strlen(config_home) - 1] = 0;
+
+ LOG("\"%s\"", config_home);
+
+ *config_home_out = config_home;
+
+ return NSERROR_OK;
+}
+
+/**
+ * Get the path to the cache directory.
+ *
+ * @param cache_home_out Path to cache directory.
+ * @return NSERROR_OK on sucess and \a cache_home_out updated else error code.
+ */
+static nserror get_cache_home(char **cache_home_out)
+{
+ nserror ret;
+ char *xdg_cache_dir;
+ char *cache_home;
+ char *home_dir;
+
+ /* $XDG_CACHE_HOME defines the base directory relative to
+ * which user specific non-essential data files should be
+ * stored.
+ */
+ xdg_cache_dir = getenv("XDG_CACHE_HOME");
+
+ if ((xdg_cache_dir == NULL) || (*xdg_cache_dir == 0)) {
+ /* If $XDG_CACHE_HOME is either not set or empty, a
+ * default equal to $HOME/.cache should be used.
+ */
+
+ home_dir = getenv("HOME");
+
+ /* the HOME envvar is required */
+ if (home_dir == NULL) {
+ return NSERROR_NOT_DIRECTORY;
+ }
+
+ ret = check_dirname(home_dir, ".cache/netsurf", &cache_home);
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+ } else {
+ ret = check_dirname(xdg_cache_dir, "netsurf", &cache_home);
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+ }
+
+ LOG("\"%s\"", cache_home);
+
+ *cache_home_out = cache_home;
+ return NSERROR_OK;
+}
+
+static nserror create_cache_home(char **cache_home_out)
+{
+ char *cache_home = NULL;
+ char *home_dir;
+ char *xdg_cache_dir;
+ nserror ret;
+
+ LOG("Attempting to create configuration directory");
+
+ /* $XDG_CACHE_HOME defines the base directory
+ * relative to which user specific cache files
+ * should be stored.
+ */
+ xdg_cache_dir = getenv("XDG_CACHE_HOME");
+
+ if ((xdg_cache_dir == NULL) || (*xdg_cache_dir == 0)) {
+ home_dir = getenv("HOME");
+
+ if ((home_dir == NULL) || (*home_dir == 0)) {
+ return NSERROR_NOT_DIRECTORY;
+ }
+
+ ret = netsurf_mkpath(&cache_home, NULL, 4, home_dir, ".cache", "netsurf", "/");
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+ } else {
+ ret = netsurf_mkpath(&cache_home, NULL, 3, xdg_cache_dir, "netsurf", "/");
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+ }
+
+ /* ensure all elements of path exist (the trailing / is required) */
+ ret = netsurf_mkdir_all(cache_home);
+ if (ret != NSERROR_OK) {
+ free(cache_home);
+ return ret;
+ }
+
+ /* strip the trailing separator */
+ cache_home[strlen(cache_home) - 1] = 0;
+
+ LOG("\"%s\"", cache_home);
+
+ *cache_home_out = cache_home;
+
+ return NSERROR_OK;
+}
+
+static nserror nsgtk_option_init(int *pargc, char** argv)
+{
+ nserror ret;
+ char *choices = NULL;
+
+ /* user options setup */
+ ret = nsoption_init(set_defaults, &nsoptions, &nsoptions_default);
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+
+ /* Attempt to load the user choices */
+ ret = netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices");
+ if (ret == NSERROR_OK) {
+ nsoption_read(choices, nsoptions);
+ free(choices);
+ }
+
+ /* overide loaded options with those from commandline */
+ nsoption_commandline(pargc, argv, nsoptions);
+
+ /* ensure all options fall within sensible bounds */
+
+ /* Attempt to handle nonsense status bar widths. These may exist
+ * in people's Choices as the GTK front end used to abuse the
+ * status bar width option by using it for an absolute value in px.
+ * The GTK front end now correctly uses it as a proportion of window
+ * width. Here we assume that a value of less than 15% is wrong
+ * and set to the default two thirds. */
+ if (nsoption_int(toolbar_status_size) < 1500) {
+ nsoption_set_int(toolbar_status_size, 6667);
+ }
+
+ return NSERROR_OK;
+}
+
+static struct gui_misc_table nsgtk_misc_table = {
+ .schedule = nsgtk_schedule,
+ .warning = nsgtk_warning,
+
+ .quit = gui_quit,
+ .launch_url = gui_launch_url,
+ .cert_verify = gtk_cert_verify,
+ .login = gui_401login_open,
+ .pdf_password = nsgtk_pdf_password,
+};
+
+
+static nserror nsgtk_messages_init(char **respaths)
+{
+ const char *messages;
+ nserror ret;
+ const uint8_t *data;
+ size_t data_size;
+
+ ret = nsgtk_data_from_resname("Messages", &data, &data_size);
+ if (ret == NSERROR_OK) {
+ ret = messages_add_from_inline(data, data_size);
+ } else {
+ /* Obtain path to messages */
+ ret = nsgtk_path_from_resname("Messages", &messages);
+ if (ret == NSERROR_OK) {
+ ret = messages_add_from_file(messages);
+ }
+ }
+ return ret;
+}
+
+/**
+ * Main entry point from OS.
+ */
+int main(int argc, char** argv)
+{
+ char *cache_home = NULL;
+ nserror ret;
+ struct netsurf_table nsgtk_table = {
+ .misc = &nsgtk_misc_table,
+ .window = nsgtk_window_table,
+ .clipboard = nsgtk_clipboard_table,
+ .download = nsgtk_download_table,
+ .fetch = nsgtk_fetch_table,
+ .llcache = filesystem_llcache_table,
+ .search = nsgtk_search_table,
+ .search_web = nsgtk_search_web_table,
+ .bitmap = nsgtk_bitmap_table,
+ .layout = nsgtk_layout_table,
+ };
+
+ ret = netsurf_register(&nsgtk_table);
+ if (ret != NSERROR_OK) {
+ die("NetSurf operation table failed registration\n");
+ }
+
+ /* Locate the correct user configuration directory path */
+ ret = get_config_home(&nsgtk_config_home);
+ if (ret == NSERROR_NOT_FOUND) {
+ /* no config directory exists yet so try to create one */
+ ret = create_config_home(&nsgtk_config_home);
+ }
+ if (ret != NSERROR_OK) {
+ LOG("Unable to locate a configuration directory.");
+ nsgtk_config_home = NULL;
+ }
+
+ /* Initialise gtk */
+ gtk_init(&argc, &argv);
+
+ /* initialise logging. Not fatal if it fails but not much we
+ * can do about it either.
+ */
+ nslog_init(nslog_stream_configure, &argc, argv);
+
+ /* build the common resource path list */
+ respaths = nsgtk_init_resource_path(nsgtk_config_home);
+ if (respaths == NULL) {
+ fprintf(stderr, "Unable to locate resources\n");
+ return 1;
+ }
+
+ /* initialise the gtk resource handling */
+ ret = nsgtk_init_resources(respaths);
+ if (ret != NSERROR_OK) {
+ fprintf(stderr, "GTK resources failed to initialise (%s)\n",
+ messages_get_errorcode(ret));
+ return 1;
+ }
+
+ /* Initialise user options */
+ ret = nsgtk_option_init(&argc, argv);
+ if (ret != NSERROR_OK) {
+ fprintf(stderr, "Options failed to initialise (%s)\n",
+ messages_get_errorcode(ret));
+ return 1;
+ }
+
+ /* Initialise translated messages */
+ ret = nsgtk_messages_init(respaths);
+ if (ret != NSERROR_OK) {
+ fprintf(stderr, "Unable to load translated messages (%s)\n",
+ messages_get_errorcode(ret));
+ LOG("Unable to load translated messages");
+ /** \todo decide if message load faliure should be fatal */
+ }
+
+ /* Locate the correct user cache directory path */
+ ret = get_cache_home(&cache_home);
+ if (ret == NSERROR_NOT_FOUND) {
+ /* no cache directory exists yet so try to create one */
+ ret = create_cache_home(&cache_home);
+ }
+ if (ret != NSERROR_OK) {
+ LOG("Unable to locate a cache directory.");
+ }
+
+ /* core initialisation */
+ ret = netsurf_init(cache_home);
+ free(cache_home);
+ if (ret != NSERROR_OK) {
+ fprintf(stderr, "NetSurf core failed to initialise (%s)\n",
+ messages_get_errorcode(ret));
+ return 1;
+ }
+
+ /* run the browser */
+ ret = nsgtk_init(argc, argv, respaths);
+ if (ret != NSERROR_OK) {
+ fprintf(stderr, "NetSurf gtk initialise failed (%s)\n",
+ messages_get_errorcode(ret));
+ } else {
+ nsgtk_main();
+ }
+
+ /* common finalisation */
+ netsurf_exit();
+
+ /* finalise options */
+ nsoption_finalise(nsoptions, nsoptions_default);
+
+ return 0;
+}
diff --git a/frontends/gtk/gui.h b/frontends/gtk/gui.h
new file mode 100644
index 000000000..b6a6dc994
--- /dev/null
+++ b/frontends/gtk/gui.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 GTK_GUI_H
+#define GTK_GUI_H
+
+struct nsurl;
+
+/** toolbar arrangement file path. */
+extern char *toolbar_indices_file_location;
+
+/** Directory where all configuration files are held. */
+extern char *nsgtk_config_home;
+
+/** favicon default pixbuf */
+extern GdkPixbuf *favicon_pixbuf;
+
+/** arrow down pixbuf */
+extern GdkPixbuf *arrow_down_pixbuf;
+
+/** resource search path vector */
+extern char **respaths;
+
+/** input conversion. */
+uint32_t gtk_gui_gdkkey_to_nskey(GdkEventKey *eventkey);
+
+/** set when no windows remain open. */
+extern bool nsgtk_complete;
+
+#endif /* GTK_GUI_H */
diff --git a/frontends/gtk/history.c b/frontends/gtk/history.c
new file mode 100644
index 000000000..9c5c0b5e4
--- /dev/null
+++ b/frontends/gtk/history.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "desktop/global_history.h"
+#include "desktop/plot_style.h"
+#include "desktop/tree.h"
+#include "desktop/textinput.h"
+
+#include "gtk/plotters.h"
+#include "gtk/scaffolding.h"
+#include "gtk/treeview.h"
+#include "gtk/compat.h"
+#include "gtk/resources.h"
+#include "gtk/history.h"
+
+#define MENUPROTO(x) static gboolean nsgtk_on_##x##_activate( \
+ GtkMenuItem *widget, gpointer g)
+#define MENUEVENT(x) { #x, G_CALLBACK(nsgtk_on_##x##_activate) }
+#define MENUHANDLER(x) gboolean nsgtk_on_##x##_activate(GtkMenuItem *widget, \
+ gpointer g)
+
+struct menu_events {
+ const char *widget;
+ GCallback handler;
+};
+
+/* file menu*/
+MENUPROTO(export);
+
+/* edit menu */
+MENUPROTO(delete_selected);
+MENUPROTO(delete_all);
+MENUPROTO(select_all);
+MENUPROTO(clear_selection);
+
+/* view menu*/
+MENUPROTO(expand_all);
+MENUPROTO(expand_directories);
+MENUPROTO(expand_addresses);
+MENUPROTO(collapse_all);
+MENUPROTO(collapse_directories);
+MENUPROTO(collapse_addresses);
+
+MENUPROTO(launch);
+
+static struct menu_events menu_events[] = {
+
+ /* file menu*/
+ MENUEVENT(export),
+
+ /* edit menu */
+ MENUEVENT(delete_selected),
+ MENUEVENT(delete_all),
+ MENUEVENT(select_all),
+ MENUEVENT(clear_selection),
+
+ /* view menu*/
+ MENUEVENT(expand_all),
+ MENUEVENT(expand_directories),
+ MENUEVENT(expand_addresses),
+ MENUEVENT(collapse_all),
+ MENUEVENT(collapse_directories),
+ MENUEVENT(collapse_addresses),
+
+ MENUEVENT(launch),
+ {NULL, NULL}
+};
+
+static struct nsgtk_treeview *global_history_window;
+static GtkBuilder *history_builder;
+GtkWindow *wndHistory;
+
+/**
+ * Connects menu events in the global history window.
+ */
+static void nsgtk_history_init_menu(void)
+{
+ struct menu_events *event = menu_events;
+ GtkWidget *w;
+
+ while (event->widget != NULL) {
+ w = GTK_WIDGET(gtk_builder_get_object(history_builder,
+ event->widget));
+ if (w == NULL) {
+ LOG("Unable to connect menu widget ""%s""",
+ event->widget);
+ } else {
+ g_signal_connect(G_OBJECT(w),
+ "activate",
+ event->handler,
+ global_history_window);
+ }
+ event++;
+ }
+}
+
+/* exported interface, documented in gtk/history.h */
+nserror nsgtk_history_init(void)
+{
+ GtkWindow *window;
+ GtkScrolledWindow *scrolled;
+ GtkDrawingArea *drawing_area;
+ nserror res;
+
+ res = nsgtk_builder_new_from_resname("history", &history_builder);
+ if (res != NSERROR_OK) {
+ LOG("History UI builder init failed");
+ return res;
+ }
+ gtk_builder_connect_signals(history_builder, NULL);
+
+ wndHistory = GTK_WINDOW(gtk_builder_get_object(history_builder,
+ "wndHistory"));
+
+ window = wndHistory;
+
+ scrolled = GTK_SCROLLED_WINDOW(gtk_builder_get_object(history_builder,
+ "globalHistoryScrolled"));
+
+ drawing_area = GTK_DRAWING_AREA(gtk_builder_get_object(history_builder,
+ "globalHistoryDrawingArea"));
+
+ global_history_window = nsgtk_treeview_create(TREE_HISTORY,
+ window,
+ scrolled,
+ drawing_area);
+ if (global_history_window == NULL) {
+ return NSERROR_INIT_FAILED;
+ }
+
+#define CONNECT(obj, sig, callback, ptr) \
+ g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr))
+
+ CONNECT(window, "delete_event", gtk_widget_hide_on_delete, NULL);
+ CONNECT(window, "hide", nsgtk_tree_window_hide, global_history_window);
+
+ nsgtk_history_init_menu();
+
+ return NSERROR_OK;
+}
+
+
+
+
+/**
+ * Destroys the global history window and performs any other necessary cleanup
+ * actions.
+ */
+void nsgtk_history_destroy(void)
+{
+ /** \todo what about history_builder? */
+ nsgtk_treeview_destroy(global_history_window);
+}
+
+
+/* file menu */
+MENUHANDLER(export)
+{
+ GtkWidget *save_dialog;
+ save_dialog = gtk_file_chooser_dialog_new("Save File",
+ wndHistory,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NSGTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(save_dialog),
+ getenv("HOME") ? getenv("HOME") : "/");
+
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_dialog),
+ "history.html");
+
+ if (gtk_dialog_run(GTK_DIALOG(save_dialog)) == GTK_RESPONSE_ACCEPT) {
+ gchar *filename = gtk_file_chooser_get_filename(
+ GTK_FILE_CHOOSER(save_dialog));
+
+ global_history_export(filename, NULL);
+ g_free(filename);
+ }
+
+ gtk_widget_destroy(save_dialog);
+
+ return TRUE;
+}
+
+/* edit menu */
+MENUHANDLER(delete_selected)
+{
+ global_history_keypress(NS_KEY_DELETE_LEFT);
+ return TRUE;
+}
+
+MENUHANDLER(delete_all)
+{
+ global_history_keypress(NS_KEY_SELECT_ALL);
+ global_history_keypress(NS_KEY_DELETE_LEFT);
+ return TRUE;
+}
+
+MENUHANDLER(select_all)
+{
+ global_history_keypress(NS_KEY_SELECT_ALL);
+ return TRUE;
+}
+
+MENUHANDLER(clear_selection)
+{
+ global_history_keypress(NS_KEY_CLEAR_SELECTION);
+ return TRUE;
+}
+
+/* view menu*/
+MENUHANDLER(expand_all)
+{
+ global_history_expand(false);
+ return TRUE;
+}
+
+MENUHANDLER(expand_directories)
+{
+ global_history_expand(true);
+ return TRUE;
+}
+
+MENUHANDLER(expand_addresses)
+{
+ global_history_expand(false);
+ return TRUE;
+}
+
+MENUHANDLER(collapse_all)
+{
+ global_history_contract(true);
+ return TRUE;
+}
+
+MENUHANDLER(collapse_directories)
+{
+ global_history_contract(true);
+ return TRUE;
+}
+
+MENUHANDLER(collapse_addresses)
+{
+ global_history_contract(false);
+ return TRUE;
+}
+
+MENUHANDLER(launch)
+{
+ global_history_keypress(NS_KEY_CR);
+ return TRUE;
+}
diff --git a/frontends/gtk/history.h b/frontends/gtk/history.h
new file mode 100644
index 000000000..c0f7db2bd
--- /dev/null
+++ b/frontends/gtk/history.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 __NSGTK_HISTORY_H__
+#define __NSGTK_HISTORY_H__
+
+#include <gtk/gtk.h>
+
+extern GtkWindow *wndHistory;
+
+/**
+ * Creates the window for the global history tree.
+ *
+ * \return NSERROR_OK on sucess else appropriate error code.
+ */
+nserror nsgtk_history_init(void);
+
+/**
+ * Free global resources associated with the gtk history window.
+ */
+void nsgtk_history_destroy(void);
+
+#endif /* __NSGTK_HISTORY_H__ */
diff --git a/frontends/gtk/hotlist.c b/frontends/gtk/hotlist.c
new file mode 100644
index 000000000..06fd5cd69
--- /dev/null
+++ b/frontends/gtk/hotlist.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "desktop/hotlist.h"
+#include "desktop/tree.h"
+
+#include "gtk/plotters.h"
+#include "gtk/scaffolding.h"
+#include "gtk/treeview.h"
+#include "gtk/compat.h"
+#include "gtk/resources.h"
+#include "gtk/hotlist.h"
+
+#define MENUPROTO(x) static gboolean nsgtk_on_##x##_activate( \
+ GtkMenuItem *widget, gpointer g)
+#define MENUEVENT(x) { #x, G_CALLBACK(nsgtk_on_##x##_activate) }
+#define MENUHANDLER(x) gboolean nsgtk_on_##x##_activate(GtkMenuItem *widget, \
+ gpointer g)
+
+struct menu_events {
+ const char *widget;
+ GCallback handler;
+};
+
+
+/* file menu*/
+MENUPROTO(export);
+MENUPROTO(new_folder);
+MENUPROTO(new_entry);
+
+/* edit menu */
+MENUPROTO(edit_selected);
+MENUPROTO(delete_selected);
+MENUPROTO(select_all);
+MENUPROTO(clear_selection);
+
+/* view menu*/
+MENUPROTO(expand_all);
+MENUPROTO(expand_directories);
+MENUPROTO(expand_addresses);
+MENUPROTO(collapse_all);
+MENUPROTO(collapse_directories);
+MENUPROTO(collapse_addresses);
+
+MENUPROTO(launch);
+
+static struct menu_events menu_events[] = {
+
+ /* file menu*/
+ MENUEVENT(export),
+ MENUEVENT(new_folder),
+ MENUEVENT(new_entry),
+
+ /* edit menu */
+ MENUEVENT(edit_selected),
+ MENUEVENT(delete_selected),
+ MENUEVENT(select_all),
+ MENUEVENT(clear_selection),
+
+ /* view menu*/
+ MENUEVENT(expand_all),
+ MENUEVENT(expand_directories),
+ MENUEVENT(expand_addresses),
+ MENUEVENT(collapse_all),
+ MENUEVENT(collapse_directories),
+ MENUEVENT(collapse_addresses),
+
+ MENUEVENT(launch),
+ {NULL, NULL}
+};
+
+static struct nsgtk_treeview *hotlist_treeview;
+static GtkBuilder *hotlist_builder;
+GtkWindow *wndHotlist;
+
+/**
+ * Connects menu events in the hotlist window.
+ */
+static void nsgtk_hotlist_init_menu(void)
+{
+ struct menu_events *event = menu_events;
+ GtkWidget *w;
+
+ while (event->widget != NULL) {
+ w = GTK_WIDGET(gtk_builder_get_object(hotlist_builder, event->widget));
+ if (w == NULL) {
+ LOG("Unable to connect menu widget ""%s""", event->widget); } else {
+ g_signal_connect(G_OBJECT(w), "activate", event->handler, hotlist_treeview);
+ }
+ event++;
+ }
+}
+
+/* exported interface docuemnted in gtk/hotlist.h */
+nserror nsgtk_hotlist_init(void)
+{
+ GtkWindow *window;
+ GtkScrolledWindow *scrolled;
+ GtkDrawingArea *drawing_area;
+ nserror res;
+
+ res = nsgtk_builder_new_from_resname("hotlist", &hotlist_builder);
+ if (res != NSERROR_OK) {
+ LOG("Cookie UI builder init failed");
+ return res;
+ }
+
+ gtk_builder_connect_signals(hotlist_builder, NULL);
+
+ wndHotlist = GTK_WINDOW(gtk_builder_get_object(hotlist_builder, "wndHotlist"));
+ window = wndHotlist;
+
+ scrolled = GTK_SCROLLED_WINDOW(gtk_builder_get_object(hotlist_builder,
+ "hotlistScrolled"));
+
+ drawing_area = GTK_DRAWING_AREA(gtk_builder_get_object(hotlist_builder,
+ "hotlistDrawingArea"));
+
+
+ tree_hotlist_path = nsoption_charp(hotlist_path);
+ hotlist_treeview = nsgtk_treeview_create(TREE_HOTLIST, window,
+ scrolled, drawing_area);
+
+ if (hotlist_treeview == NULL) {
+ return NSERROR_INIT_FAILED;
+ }
+
+#define CONNECT(obj, sig, callback, ptr) \
+ g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr))
+
+ CONNECT(window, "delete_event", gtk_widget_hide_on_delete, NULL);
+ CONNECT(window, "hide", nsgtk_tree_window_hide, hotlist_treeview);
+
+ nsgtk_hotlist_init_menu();
+
+ return NSERROR_OK;
+}
+
+
+
+
+/**
+ * Destroys the hotlist window and performs any other necessary cleanup actions.
+ */
+void nsgtk_hotlist_destroy(void)
+{
+ /** \todo what about hotlist_builder? */
+ nsgtk_treeview_destroy(hotlist_treeview);
+}
+
+
+/* file menu*/
+MENUHANDLER(export)
+{
+ GtkWidget *save_dialog;
+ save_dialog = gtk_file_chooser_dialog_new("Save File",
+ wndHotlist,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NSGTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(save_dialog),
+ getenv("HOME") ? getenv("HOME") : "/");
+
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_dialog),
+ "hotlist.html");
+
+ if (gtk_dialog_run(GTK_DIALOG(save_dialog)) == GTK_RESPONSE_ACCEPT) {
+ gchar *filename = gtk_file_chooser_get_filename(
+ GTK_FILE_CHOOSER(save_dialog));
+
+ hotlist_export(filename, NULL);
+ g_free(filename);
+ }
+
+ gtk_widget_destroy(save_dialog);
+
+ return TRUE;
+}
+
+MENUHANDLER(new_folder)
+{
+ hotlist_add_folder(NULL, false, 0);
+ return TRUE;
+}
+
+MENUHANDLER(new_entry)
+{
+ hotlist_add_entry(NULL, NULL, false, 0);
+ return TRUE;
+}
+
+/* edit menu */
+MENUHANDLER(edit_selected)
+{
+ hotlist_edit_selection();
+ return TRUE;
+}
+
+MENUHANDLER(delete_selected)
+{
+ hotlist_keypress(NS_KEY_DELETE_LEFT);
+ return TRUE;
+}
+
+MENUHANDLER(select_all)
+{
+ hotlist_keypress(NS_KEY_SELECT_ALL);
+ return TRUE;
+}
+
+MENUHANDLER(clear_selection)
+{
+ hotlist_keypress(NS_KEY_CLEAR_SELECTION);
+ return TRUE;
+}
+
+/* view menu*/
+MENUHANDLER(expand_all)
+{
+ hotlist_expand(false);
+ return TRUE;
+}
+
+MENUHANDLER(expand_directories)
+{
+ hotlist_expand(true);
+ return TRUE;
+}
+
+MENUHANDLER(expand_addresses)
+{
+ hotlist_expand(false);
+ return TRUE;
+}
+
+MENUHANDLER(collapse_all)
+{
+ hotlist_contract(true);
+ return TRUE;
+}
+
+MENUHANDLER(collapse_directories)
+{
+ hotlist_contract(true);
+ return TRUE;
+}
+
+MENUHANDLER(collapse_addresses)
+{
+ hotlist_contract(false);
+ return TRUE;
+}
+
+MENUHANDLER(launch)
+{
+ hotlist_keypress(NS_KEY_CR);
+ return TRUE;
+}
diff --git a/frontends/gtk/hotlist.h b/frontends/gtk/hotlist.h
new file mode 100644
index 000000000..01e5a86c5
--- /dev/null
+++ b/frontends/gtk/hotlist.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * GTK hotlist (interface).
+ */
+
+#ifndef __NSGTK_HOTLIST_H__
+#define __NSGTK_HOTLIST_H__
+
+#include <gtk/gtk.h>
+
+extern GtkWindow *wndHotlist;
+
+/**
+ * Initialise the gtk specific hotlist (bookmarks) display.
+ *
+ * \return NSERROR_OK on success else appropriate error code on faliure.
+ */
+nserror nsgtk_hotlist_init(void);
+
+
+void nsgtk_hotlist_destroy(void);
+
+#endif /* __NSGTK_HOTLIST_H__ */
diff --git a/frontends/gtk/layout_pango.c b/frontends/gtk/layout_pango.c
new file mode 100644
index 000000000..49b629399
--- /dev/null
+++ b/frontends/gtk/layout_pango.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * GTK implementation of layout handling using pango.
+ *
+ * Pango is used handle and render fonts.
+ */
+
+
+#include <assert.h>
+#include <stdio.h>
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "desktop/gui_layout.h"
+
+#include "gtk/layout_pango.h"
+#include "gtk/plotters.h"
+
+static PangoContext *nsfont_pango_context = NULL;
+static PangoLayout *nsfont_pango_layout = NULL;
+
+static inline void nsfont_pango_check(void)
+{
+ if (nsfont_pango_context == NULL) {
+ LOG("Creating nsfont_pango_context.");
+ nsfont_pango_context = gdk_pango_context_get();
+ }
+
+ if (nsfont_pango_layout == NULL) {
+ LOG("Creating nsfont_pango_layout.");
+ nsfont_pango_layout = pango_layout_new(nsfont_pango_context);
+ }
+}
+
+/**
+ * Measure the width of a string.
+ *
+ * \param[in] fstyle plot style for this text
+ * \param[in] string UTF-8 string to measure
+ * \param[in] length length of string, in bytes
+ * \param[out] width updated to width of string[0..length)
+ * \return NSERROR_OK and width updated or appropriate error code on faliure
+ */
+static nserror
+nsfont_width(const plot_font_style_t *fstyle,
+ const char *string,
+ size_t length,
+ int *width)
+{
+ PangoFontDescription *desc;
+
+ if (length == 0) {
+ *width = 0;
+ return NSERROR_OK;
+ }
+
+ nsfont_pango_check();
+
+ desc = nsfont_style_to_description(fstyle);
+ pango_layout_set_font_description(nsfont_pango_layout, desc);
+ pango_font_description_free(desc);
+
+ pango_layout_set_text(nsfont_pango_layout, string, length);
+
+ pango_layout_get_pixel_size(nsfont_pango_layout, width, 0);
+
+ /* LOG("fstyle: %p string:\"%.*s\", length: %u, width: %dpx",
+ fstyle, length, string, length, *width);
+ */
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Find the position in a string where an x coordinate falls.
+ *
+ * \param[in] fstyle style for this text
+ * \param[in] string UTF-8 string to measure
+ * \param[in] length length of string, in bytes
+ * \param[in] x coordinate to search for
+ * \param[out] char_offset updated to offset in string of actual_x, [0..length]
+ * \param[out] actual_x updated to x coordinate of character closest to x
+ * \return NSERROR_OK and char_offset and actual_x updated or appropriate
+ * error code on faliure
+ */
+static nserror
+nsfont_position_in_string(const plot_font_style_t *fstyle,
+ const char *string,
+ size_t length,
+ int x,
+ size_t *char_offset,
+ int *actual_x)
+{
+ int index;
+ PangoFontDescription *desc;
+ PangoRectangle pos;
+
+ nsfont_pango_check();
+
+ desc = nsfont_style_to_description(fstyle);
+ pango_layout_set_font_description(nsfont_pango_layout, desc);
+ pango_font_description_free(desc);
+
+ pango_layout_set_text(nsfont_pango_layout, string, length);
+
+ if (pango_layout_xy_to_index(nsfont_pango_layout,
+ x * PANGO_SCALE,
+ 0, &index, 0) == FALSE) {
+ index = length;
+ }
+
+ pango_layout_index_to_pos(nsfont_pango_layout, index, &pos);
+
+ *char_offset = index;
+ *actual_x = PANGO_PIXELS(pos.x);
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Find where to split a string to make it fit a width.
+ *
+ * \param[in] fstyle style for this text
+ * \param[in] string UTF-8 string to measure
+ * \param[in] length length of string, in bytes
+ * \param[in] x width available
+ * \param[out] char_offset updated to offset in string of actual_x, [1..length]
+ * \param[out] actual_x updated to x coordinate of character closest to x
+ * \return NSERROR_OK or appropriate error code on faliure
+ *
+ * On exit, char_offset indicates first character after split point.
+ *
+ * \note char_offset of 0 must never be returned.
+ *
+ * Returns:
+ * char_offset giving split point closest to x, where actual_x <= x
+ * else
+ * char_offset giving split point closest to x, where actual_x > x
+ *
+ * Returning char_offset == length means no split possible
+ */
+static nserror
+nsfont_split(const plot_font_style_t *fstyle,
+ const char *string,
+ size_t length,
+ int x,
+ size_t *char_offset,
+ int *actual_x)
+{
+ int index = length;
+ PangoFontDescription *desc;
+ PangoContext *context;
+ PangoLayout *layout;
+ PangoLayoutLine *line;
+
+ context = gdk_pango_context_get();
+ layout = pango_layout_new(context);
+
+ desc = nsfont_style_to_description(fstyle);
+ pango_layout_set_font_description(layout, desc);
+ pango_font_description_free(desc);
+
+ pango_layout_set_text(layout, string, length);
+
+ /* Limit width of layout to the available width */
+ pango_layout_set_width(layout, x * PANGO_SCALE);
+
+ /* Request word wrapping */
+ pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
+
+ /* Prevent pango treating linebreak characters as line breaks */
+ pango_layout_set_single_paragraph_mode(layout, TRUE);
+
+ /* Obtain the second line of the layout (if there is one) */
+ line = pango_layout_get_line(layout, 1);
+ if (line != NULL) {
+ /* Pango split the text. The line's start_index indicates the
+ * start of the character after the line break. */
+ index = line->start_index;
+ }
+
+ g_object_unref(layout);
+ g_object_unref(context);
+
+ *char_offset = index;
+ /* Obtain the pixel offset of the split character */
+ nsfont_width(fstyle, string, index, actual_x);
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Render a string.
+ *
+ * \param x x coordinate
+ * \param y y coordinate
+ * \param string UTF-8 string to measure
+ * \param length length of string
+ * \param fstyle plot style for this text
+ * \return true on success, false on error and error reported
+ */
+bool nsfont_paint(int x, int y, const char *string, size_t length,
+ const plot_font_style_t *fstyle)
+{
+ PangoFontDescription *desc;
+ PangoLayout *layout;
+ PangoLayoutLine *line;
+
+ if (length == 0)
+ return true;
+
+ layout = pango_cairo_create_layout(current_cr);
+
+ desc = nsfont_style_to_description(fstyle);
+ pango_layout_set_font_description(layout, desc);
+ pango_font_description_free(desc);
+
+ pango_layout_set_text(layout, string, length);
+
+ line = pango_layout_get_line_readonly(layout, 0);
+ cairo_move_to(current_cr, x, y);
+ nsgtk_set_colour(fstyle->foreground);
+ pango_cairo_show_layout_line(current_cr, line);
+
+ g_object_unref(layout);
+
+ return true;
+}
+
+
+/* exported interface documented in gtk/layout_pango.h */
+PangoFontDescription *
+nsfont_style_to_description(const plot_font_style_t *fstyle)
+{
+ unsigned int size;
+ PangoFontDescription *desc;
+ PangoStyle style = PANGO_STYLE_NORMAL;
+
+ switch (fstyle->family) {
+ case PLOT_FONT_FAMILY_SERIF:
+ desc = pango_font_description_from_string(nsoption_charp(font_serif));
+ break;
+ case PLOT_FONT_FAMILY_MONOSPACE:
+ desc = pango_font_description_from_string(nsoption_charp(font_mono));
+ break;
+ case PLOT_FONT_FAMILY_CURSIVE:
+ desc = pango_font_description_from_string(nsoption_charp(font_cursive));
+ break;
+ case PLOT_FONT_FAMILY_FANTASY:
+ desc = pango_font_description_from_string(nsoption_charp(font_fantasy));
+ break;
+ case PLOT_FONT_FAMILY_SANS_SERIF:
+ default:
+ desc = pango_font_description_from_string(nsoption_charp(font_sans));
+ break;
+ }
+
+ size = (fstyle->size * PANGO_SCALE) / FONT_SIZE_SCALE;
+
+ if (fstyle->flags & FONTF_ITALIC)
+ style = PANGO_STYLE_ITALIC;
+ else if (fstyle->flags & FONTF_OBLIQUE)
+ style = PANGO_STYLE_OBLIQUE;
+
+ pango_font_description_set_style(desc, style);
+
+ pango_font_description_set_weight(desc, (PangoWeight) fstyle->weight);
+
+ pango_font_description_set_size(desc, size);
+
+ if (fstyle->flags & FONTF_SMALLCAPS) {
+ pango_font_description_set_variant(desc,
+ PANGO_VARIANT_SMALL_CAPS);
+ } else {
+ pango_font_description_set_variant(desc, PANGO_VARIANT_NORMAL);
+ }
+
+ return desc;
+}
+
+static struct gui_layout_table layout_table = {
+ .width = nsfont_width,
+ .position = nsfont_position_in_string,
+ .split = nsfont_split,
+};
+
+struct gui_layout_table *nsgtk_layout_table = &layout_table;
diff --git a/frontends/gtk/layout_pango.h b/frontends/gtk/layout_pango.h
new file mode 100644
index 000000000..137cebe68
--- /dev/null
+++ b/frontends/gtk/layout_pango.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * Interface to GTK layout handling using pango.
+ */
+
+#ifndef _NETSURF_GTK_LAYOUT_PANGO_H_
+#define _NETSURF_GTK_LAYOUT_PANGO_H_
+
+#include <stdbool.h>
+
+struct plot_font_style;
+
+extern struct gui_layout_table *nsgtk_layout_table;
+
+bool nsfont_paint(int x, int y, const char *string, size_t length, const struct plot_font_style *fstyle);
+
+/**
+ * Convert a plot style to a PangoFontDescription.
+ *
+ * \param fstyle plot style for this text
+ * \return A new Pango font description
+ */
+PangoFontDescription *nsfont_style_to_description(const struct plot_font_style *fstyle);
+
+#endif
diff --git a/frontends/gtk/login.c b/frontends/gtk/login.c
new file mode 100644
index 000000000..ee77052d7
--- /dev/null
+++ b/frontends/gtk/login.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "utils/nsurl.h"
+#include "content/urldb.h"
+
+#include "gtk/resources.h"
+#include "gtk/login.h"
+
+/** login window session data */
+struct session_401 {
+ nsurl *url; /**< URL being fetched */
+ lwc_string *host; /**< Host for user display */
+ char *realm; /**< Authentication realm */
+ nserror (*cb)(bool proceed, void *pw); /**< Continuation callback */
+ void *cbpw; /**< Continuation data */
+ GtkBuilder *x; /**< Our builder windows */
+ GtkWindow *wnd; /**< The login window itself */
+ GtkEntry *user; /**< Widget with username */
+ GtkEntry *pass; /**< Widget with password */
+};
+
+/**
+ * Destroy login window and free all associated resources
+ *
+ * \param session The login window session to destroy.
+ */
+static void destroy_login_window(struct session_401 *session)
+{
+ nsurl_unref(session->url);
+ lwc_string_unref(session->host);
+ free(session->realm);
+ gtk_widget_destroy(GTK_WIDGET(session->wnd));
+ g_object_unref(G_OBJECT(session->x));
+ free(session);
+}
+
+
+/**
+ * process next signal in entry widgets.
+ *
+ * \param w current widget
+ * \param data next widget
+ */
+static void nsgtk_login_next(GtkWidget *w, gpointer data)
+{
+ gtk_widget_grab_focus(GTK_WIDGET(data));
+}
+
+
+/**
+ * handler called when navigation is continued
+ *
+ * \param w current widget
+ * \param data login window session
+ */
+static void nsgtk_login_ok_clicked(GtkButton *w, gpointer data)
+{
+ /* close the window and destroy it, having continued the fetch
+ * assoicated with it.
+ */
+
+ struct session_401 *session = (struct session_401 *)data;
+ const gchar *user = gtk_entry_get_text(session->user);
+ const gchar *pass = gtk_entry_get_text(session->pass);
+ char *auth;
+
+ auth = malloc(strlen(user) + strlen(pass) + 2);
+ sprintf(auth, "%s:%s", user, pass);
+ urldb_set_auth_details(session->url, session->realm, auth);
+ free(auth);
+
+ session->cb(true, session->cbpw);
+
+ destroy_login_window(session);
+}
+
+
+/**
+ * handler called when navigation is cancelled
+ *
+ * \param w widget
+ * \param data login window session
+ */
+static void nsgtk_login_cancel_clicked(GtkButton *w, gpointer data)
+{
+ struct session_401 *session = (struct session_401 *) data;
+
+ session->cb(false, session->cbpw);
+
+ /* close and destroy the window */
+ destroy_login_window(session);
+}
+
+
+/**
+ * create a new instance of the login window
+ *
+ * creates login window and handles to all the widgets we're
+ * interested in.
+ *
+ * \param url The url causing the login.
+ * \param host the host being logged into
+ * \param realm realmm the login relates to
+ * \param cb callback when complete
+ * \param cbpw data to pass to callback
+ * \return NSERROR_OK on sucessful window creation or error code on faliure.
+ */
+static nserror
+create_login_window(nsurl *url,
+ lwc_string *host,
+ const char *realm,
+ nserror (*cb)(bool proceed, void *pw),
+ void *cbpw)
+{
+ struct session_401 *session;
+ GtkWindow *wnd;
+ GtkLabel *lhost, *lrealm;
+ GtkEntry *euser, *epass;
+ GtkButton *bok, *bcan;
+ GtkBuilder* builder;
+ nserror res;
+
+ session = calloc(1, sizeof(struct session_401));
+ if (session == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ res = nsgtk_builder_new_from_resname("login", &builder);
+ if (res != NSERROR_OK) {
+ free(session);
+ return res;
+ }
+
+ gtk_builder_connect_signals(builder, NULL);
+
+ wnd = GTK_WINDOW(gtk_builder_get_object(builder, "wndLogin"));
+ lhost = GTK_LABEL(gtk_builder_get_object(builder, "labelLoginHost"));
+ lrealm = GTK_LABEL(gtk_builder_get_object(builder, "labelLoginRealm"));
+ euser = GTK_ENTRY(gtk_builder_get_object(builder, "entryLoginUser"));
+ epass = GTK_ENTRY(gtk_builder_get_object(builder, "entryLoginPass"));
+ bok = GTK_BUTTON(gtk_builder_get_object(builder, "buttonLoginOK"));
+ bcan = GTK_BUTTON(gtk_builder_get_object(builder, "buttonLoginCan"));
+
+ /* create and fill in our session structure */
+ session->url = nsurl_ref(url);
+ session->host = lwc_string_ref(host);
+ session->realm = strdup(realm ? realm : "Secure Area");
+ session->cb = cb;
+ session->cbpw = cbpw;
+ session->x = builder;
+ session->wnd = wnd;
+ session->user = euser;
+ session->pass = epass;
+
+ /* fill in our new login window */
+
+ gtk_label_set_text(GTK_LABEL(lhost), lwc_string_data(host));
+ gtk_label_set_text(lrealm, realm);
+ gtk_entry_set_text(euser, "");
+ gtk_entry_set_text(epass, "");
+
+ /* attach signal handlers to the Login and Cancel buttons in our new
+ * window to call functions in this file to process the login
+ */
+ g_signal_connect(G_OBJECT(bok), "clicked",
+ G_CALLBACK(nsgtk_login_ok_clicked), (gpointer)session);
+ g_signal_connect(G_OBJECT(bcan), "clicked",
+ G_CALLBACK(nsgtk_login_cancel_clicked),
+ (gpointer)session);
+
+ /* attach signal handlers to the entry boxes such that pressing
+ * enter in one progresses the focus onto the next widget.
+ */
+
+ g_signal_connect(G_OBJECT(euser), "activate",
+ G_CALLBACK(nsgtk_login_next), (gpointer)epass);
+ g_signal_connect(G_OBJECT(epass), "activate",
+ G_CALLBACK(nsgtk_login_next), (gpointer)bok);
+
+ /* make sure the username entry box currently has the focus */
+ gtk_widget_grab_focus(GTK_WIDGET(euser));
+
+ /* finally, show the window */
+ gtk_widget_show(GTK_WIDGET(wnd));
+
+ return NSERROR_OK;
+}
+
+
+/* exported function documented in gtk/login.h */
+void gui_401login_open(nsurl *url,
+ const char *realm,
+ nserror (*cb)(bool proceed, void *pw),
+ void *cbpw)
+{
+ lwc_string *host;
+ nserror res;
+
+ host = nsurl_get_component(url, NSURL_HOST);
+ assert(host != NULL);
+
+ res = create_login_window(url, host, realm, cb, cbpw);
+ if (res != NSERROR_OK) {
+ LOG("Login init failed");
+
+ /* creating login failed so cancel navigation */
+ cb(false, cbpw);
+ }
+
+ lwc_string_unref(host);
+}
diff --git a/frontends/gtk/login.h b/frontends/gtk/login.h
new file mode 100644
index 000000000..00c29000c
--- /dev/null
+++ b/frontends/gtk/login.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Login interfaces.
+ */
+
+#ifndef __NSGTK_LOGIN_H__
+#define __NSGTK_LOGIN_H__
+
+/**
+ * login window request.
+ */
+extern void gui_401login_open(struct nsurl *url, const char *realm, nserror (*cb)(bool proceed, void *pw), void *cbpw);
+
+#endif
diff --git a/frontends/gtk/menu.c b/frontends/gtk/menu.c
new file mode 100644
index 000000000..a93ef9385
--- /dev/null
+++ b/frontends/gtk/menu.c
@@ -0,0 +1,585 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <stdbool.h>
+
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+
+#include "utils/messages.h"
+
+#include "gtk/compat.h"
+#include "gtk/menu.h"
+#include "gtk/warn.h"
+
+/**
+ * Adds image menu item to a menu.
+ *
+ * \param menu the menu to add the item to
+ * \param item_out a pointer to the item's location in the menu struct
+ * \param message the menu item I18n lookup value
+ * \param messageAccel the menu item accelerator I18n lookup value
+ * \param group the 'global' in a gtk sense accelerator group
+ * \return true if sucessful and \a item_out updated else false.
+ */
+
+static bool nsgtk_menu_add_image_item(GtkMenu *menu,
+ GtkWidget **item_out, const char *message,
+ const char *messageAccel, GtkAccelGroup *group)
+{
+ unsigned int key;
+ GdkModifierType mod;
+ GtkWidget *item;
+
+ item = nsgtk_image_menu_item_new_with_mnemonic(messages_get(message));
+ if (item == NULL) {
+ return false;
+ }
+
+ gtk_accelerator_parse(messages_get(messageAccel), &key, &mod);
+ if (key > 0) {
+ gtk_widget_add_accelerator(item, "activate", group, key, mod,
+ GTK_ACCEL_VISIBLE);
+ }
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+ gtk_widget_show(item);
+
+ *item_out = item;
+
+ return true;
+}
+
+#define NEW_MENU(n, m) \
+ n = malloc(sizeof(*n)); \
+ if (n == NULL) { \
+ return NULL; \
+ } \
+ n->m##_menu = GTK_MENU(gtk_menu_new())
+
+#define IMAGE_ITEM(p, q, r, s, t)\
+ nsgtk_menu_add_image_item(s->p##_menu, &(s->q##_menuitem), #r,\
+ #r "Accel", t)
+
+#define CHECK_ITEM(p, q, r, s)\
+ s->q##_menuitem = GTK_CHECK_MENU_ITEM(\
+ gtk_check_menu_item_new_with_mnemonic(\
+ messages_get(#r)));\
+ if ((s->q##_menuitem != NULL) && (s->p##_menu != NULL)) {\
+ gtk_menu_shell_append(GTK_MENU_SHELL(s->p##_menu),\
+ GTK_WIDGET(s->q##_menuitem));\
+ gtk_widget_show(GTK_WIDGET(s->q##_menuitem));\
+ }
+
+#define SET_SUBMENU(q, r) \
+ do { \
+ r->q##_submenu = nsgtk_menu_##q##_submenu(group); \
+ if ((r->q##_submenu != NULL) && \
+ (r->q##_submenu->q##_menu != NULL) && \
+ (r->q##_menuitem != NULL)) { \
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(r->q##_menuitem), \
+ GTK_WIDGET(r->q##_submenu->q##_menu)); \
+ } \
+ } while(0)
+
+#define ADD_NAMED_SEP(q, r, s) \
+ do { \
+ s->r##_separator = gtk_separator_menu_item_new(); \
+ if ((s->r##_separator != NULL) && (s->q##_menu != NULL)) { \
+ gtk_menu_shell_append(GTK_MENU_SHELL(s->q##_menu), s->r##_separator); \
+ gtk_widget_show(s->r##_separator); \
+ } \
+ } while(0)
+
+#define ADD_SEP(q, r) \
+ do { \
+ GtkWidget *w = gtk_separator_menu_item_new(); \
+ if ((w != NULL) && (r->q##_menu != NULL)) { \
+ gtk_menu_shell_append(GTK_MENU_SHELL(r->q##_menu), w); \
+ gtk_widget_show(w); \
+ } \
+ } while(0)
+
+#define ATTACH_PARENT(parent, msgname, menuv, group) \
+ do { \
+ /* create top level menu entry and attach to parent */ \
+ menuv = GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(messages_get(#msgname))); \
+ gtk_menu_shell_append(parent, GTK_WIDGET(menuv)); \
+ gtk_widget_show(GTK_WIDGET(menuv)); \
+ /* attach submenu to parent */ \
+ gtk_menu_item_set_submenu(menuv, GTK_WIDGET(menuv##_menu)); \
+ gtk_menu_set_accel_group(menuv##_menu, group); \
+ } while(0)
+
+/**
+* creates an export submenu
+* \param group the 'global' in a gtk sense accelerator reference
+*/
+
+static struct nsgtk_export_submenu *nsgtk_menu_export_submenu(GtkAccelGroup *group)
+{
+ struct nsgtk_export_submenu *ret = malloc(sizeof(struct
+ nsgtk_export_submenu));
+ if (ret == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ ret->export_menu = GTK_MENU(gtk_menu_new());
+ if (ret->export_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(ret);
+ return NULL;
+ }
+ IMAGE_ITEM(export, plaintext, gtkPlainText, ret, group);
+ IMAGE_ITEM(export, drawfile, gtkDrawFile, ret, group);
+ IMAGE_ITEM(export, postscript, gtkPostScript, ret, group);
+ IMAGE_ITEM(export, pdf, gtkPDF, ret, group);
+ return ret;
+}
+
+/**
+* creates a scaleview submenu
+* \param group the 'global' in a gtk sense accelerator reference
+*/
+
+static struct nsgtk_scaleview_submenu *nsgtk_menu_scaleview_submenu(
+ GtkAccelGroup *group)
+{
+ struct nsgtk_scaleview_submenu *ret =
+ malloc(sizeof(struct nsgtk_scaleview_submenu));
+ if (ret == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ ret->scaleview_menu = GTK_MENU(gtk_menu_new());
+ if (ret->scaleview_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(ret);
+ return NULL;
+ }
+ IMAGE_ITEM(scaleview, zoomplus, gtkZoomPlus, ret, group);
+ IMAGE_ITEM(scaleview, zoomnormal, gtkZoomNormal, ret, group);
+ IMAGE_ITEM(scaleview, zoomminus, gtkZoomMinus, ret, group);
+ return ret;
+}
+
+/**
+* creates a tab navigation submenu
+* \param group the 'global' in a gtk sense accelerator reference
+*/
+
+static struct nsgtk_tabs_submenu *nsgtk_menu_tabs_submenu(GtkAccelGroup *group)
+{
+ struct nsgtk_tabs_submenu *ret = malloc(sizeof(struct nsgtk_tabs_submenu));
+ if (ret == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ ret->tabs_menu = GTK_MENU(gtk_menu_new());
+ if (ret->tabs_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(ret);
+ return NULL;
+ }
+ IMAGE_ITEM(tabs, nexttab, gtkNextTab, ret, group);
+ IMAGE_ITEM(tabs, prevtab, gtkPrevTab, ret, group);
+ IMAGE_ITEM(tabs, closetab, gtkCloseTab, ret, group);
+
+ return ret;
+}
+
+/**
+* creates an images submenu
+* \param group the 'global' in a gtk sense accelerator reference
+*/
+
+static struct nsgtk_images_submenu *nsgtk_menu_images_submenu(GtkAccelGroup *group)
+{
+ struct nsgtk_images_submenu *ret =
+ malloc(sizeof(struct nsgtk_images_submenu));
+ if (ret == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ ret->images_menu = GTK_MENU(gtk_menu_new());
+ if (ret->images_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(ret);
+ return NULL;
+ }
+ CHECK_ITEM(images, foregroundimages, gtkForegroundImages, ret)
+ CHECK_ITEM(images, backgroundimages, gtkBackgroundImages, ret)
+ return ret;
+}
+
+/**
+* creates a toolbars submenu
+* \param group the 'global' in a gtk sense accelerator reference
+*/
+
+static struct nsgtk_toolbars_submenu *nsgtk_menu_toolbars_submenu(
+ GtkAccelGroup *group)
+{
+ struct nsgtk_toolbars_submenu *ret =
+ malloc(sizeof(struct nsgtk_toolbars_submenu));
+ if (ret == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ ret->toolbars_menu = GTK_MENU(gtk_menu_new());
+ if (ret->toolbars_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(ret);
+ return NULL;
+ }
+ CHECK_ITEM(toolbars, menubar, gtkMenuBar, ret)
+ if (ret->menubar_menuitem != NULL)
+ gtk_check_menu_item_set_active(ret->menubar_menuitem, TRUE);
+ CHECK_ITEM(toolbars, toolbar, gtkToolBar, ret)
+ if (ret->toolbar_menuitem != NULL)
+ gtk_check_menu_item_set_active(ret->toolbar_menuitem, TRUE);
+ return ret;
+}
+
+/**
+* creates a debugging submenu
+* \param group the 'global' in a gtk sense accelerator reference
+*/
+
+static struct nsgtk_developer_submenu *nsgtk_menu_developer_submenu(
+ GtkAccelGroup *group)
+{
+ struct nsgtk_developer_submenu *dmenu =
+ malloc(sizeof(struct nsgtk_developer_submenu));
+ if (dmenu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ dmenu->developer_menu = GTK_MENU(gtk_menu_new());
+ if (dmenu->developer_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(dmenu);
+ return NULL;
+ }
+
+ IMAGE_ITEM(developer, viewsource, gtkPageSource, dmenu, group);
+ IMAGE_ITEM(developer, toggledebugging, gtkToggleDebugging, dmenu, group);
+ IMAGE_ITEM(developer, debugboxtree, gtkDebugBoxTree, dmenu, group);
+ IMAGE_ITEM(developer, debugdomtree, gtkDebugDomTree, dmenu, group);
+
+ return dmenu;
+}
+
+/**
+ * creates the file menu
+ *
+ * \param group The gtk 'global' accelerator reference
+ * \return The new file menu or NULL on error
+ */
+static struct nsgtk_file_menu *nsgtk_menu_file_submenu(GtkAccelGroup *group)
+{
+ struct nsgtk_file_menu *fmenu;
+
+ fmenu = malloc(sizeof(struct nsgtk_file_menu));
+ if (fmenu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+
+ fmenu->file_menu = GTK_MENU(gtk_menu_new());
+ if (fmenu->file_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(fmenu);
+ return NULL;
+ }
+
+ IMAGE_ITEM(file, newwindow, gtkNewWindow, fmenu, group);
+ IMAGE_ITEM(file, newtab, gtkNewTab, fmenu, group);
+ IMAGE_ITEM(file, openfile, gtkOpenFile, fmenu, group);
+ IMAGE_ITEM(file, closewindow, gtkCloseWindow, fmenu, group);
+ ADD_SEP(file, fmenu);
+ IMAGE_ITEM(file, savepage, gtkSavePage, fmenu, group);
+ IMAGE_ITEM(file, export, gtkExport, fmenu, group);
+ ADD_SEP(file, fmenu);
+ IMAGE_ITEM(file, printpreview, gtkPrintPreview, fmenu, group);
+ IMAGE_ITEM(file, print, gtkPrint, fmenu, group);
+ ADD_SEP(file, fmenu);
+ IMAGE_ITEM(file, quit, gtkQuitMenu, fmenu, group);
+ SET_SUBMENU(export, fmenu);
+
+ return fmenu;
+}
+
+/**
+* creates an edit menu
+* \param group the 'global' in a gtk sense accelerator reference
+*/
+
+static struct nsgtk_edit_menu *nsgtk_menu_edit_submenu(GtkAccelGroup *group)
+{
+ struct nsgtk_edit_menu *ret = malloc(sizeof(struct nsgtk_edit_menu));
+ if (ret == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ ret->edit_menu = GTK_MENU(gtk_menu_new());
+ if (ret->edit_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(ret);
+ return NULL;
+ }
+ IMAGE_ITEM(edit, cut, gtkCut, ret, group);
+ IMAGE_ITEM(edit, copy, gtkCopy, ret, group);
+ IMAGE_ITEM(edit, paste, gtkPaste, ret, group);
+ IMAGE_ITEM(edit, delete, gtkDelete, ret, group);
+ ADD_SEP(edit, ret);
+ IMAGE_ITEM(edit, selectall, gtkSelectAll, ret, group);
+ ADD_SEP(edit, ret);
+ IMAGE_ITEM(edit, find, gtkFind, ret, group);
+ ADD_SEP(edit, ret);
+ IMAGE_ITEM(edit, preferences, gtkPreferences, ret, group);
+
+ return ret;
+}
+
+/**
+* creates a view menu
+* \param group the 'global' in a gtk sense accelerator reference
+*/
+
+static struct nsgtk_view_menu *nsgtk_menu_view_submenu(GtkAccelGroup *group)
+{
+ struct nsgtk_view_menu *ret = malloc(sizeof(struct nsgtk_view_menu));
+ if (ret == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ ret->view_menu = GTK_MENU(gtk_menu_new());
+ if (ret->view_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(ret);
+ return NULL;
+ }
+ IMAGE_ITEM(view, stop, gtkStop, ret, group);
+ IMAGE_ITEM(view, reload, gtkReload, ret, group);
+ ADD_SEP(view, ret);
+ IMAGE_ITEM(view, scaleview, gtkScaleView, ret, group);
+ IMAGE_ITEM(view, fullscreen, gtkFullScreen, ret, group);
+ ADD_SEP(view, ret);
+ IMAGE_ITEM(view, images, gtkImages, ret, group);
+ IMAGE_ITEM(view, toolbars, gtkToolbars, ret, group);
+ IMAGE_ITEM(view, tabs, gtkTabs, ret, group);
+ ADD_SEP(view, ret);
+ IMAGE_ITEM(view, savewindowsize, gtkSaveWindowSize, ret, group);
+ SET_SUBMENU(scaleview, ret);
+ SET_SUBMENU(images, ret);
+ SET_SUBMENU(toolbars, ret);
+ SET_SUBMENU(tabs, ret);
+
+
+ return ret;
+}
+
+/**
+* creates a nav menu
+* \param group the 'global' in a gtk sense accelerator reference
+*/
+
+static struct nsgtk_nav_menu *nsgtk_menu_nav_submenu(GtkAccelGroup *group)
+{
+ struct nsgtk_nav_menu *ret = malloc(sizeof(struct nsgtk_nav_menu));
+ if (ret == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ ret->nav_menu = GTK_MENU(gtk_menu_new());
+ if (ret->nav_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(ret);
+ return NULL;
+ }
+
+ IMAGE_ITEM(nav, back, gtkBack, ret, group);
+ IMAGE_ITEM(nav, forward, gtkForward, ret, group);
+ IMAGE_ITEM(nav, home, gtkHome, ret, group);
+ ADD_SEP(nav, ret);
+ IMAGE_ITEM(nav, localhistory, gtkLocalHistory, ret, group);
+ IMAGE_ITEM(nav, globalhistory, gtkGlobalHistory, ret, group);
+ ADD_SEP(nav, ret);
+ IMAGE_ITEM(nav, addbookmarks, gtkAddBookMarks, ret, group);
+ IMAGE_ITEM(nav, showbookmarks, gtkShowBookMarks, ret, group);
+ ADD_SEP(nav, ret);
+ IMAGE_ITEM(nav, openlocation, gtkOpenLocation, ret, group);
+
+
+ return ret;
+}
+
+/**
+ * creates the tools menu
+ * \param group the 'global' in a gtk sense accelerator reference
+ */
+static struct nsgtk_tools_menu *nsgtk_menu_tools_submenu(GtkAccelGroup *group)
+{
+ struct nsgtk_tools_menu *ret = malloc(sizeof(struct nsgtk_tools_menu));
+ if (ret == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ ret->tools_menu = GTK_MENU(gtk_menu_new());
+ if (ret->tools_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(ret);
+ return NULL;
+ }
+
+ IMAGE_ITEM(tools, downloads, gtkDownloads, ret, group);
+ IMAGE_ITEM(tools, showcookies, gtkShowCookies, ret, group);
+ IMAGE_ITEM(tools, developer, gtkDeveloper, ret, group);
+ SET_SUBMENU(developer, ret);
+
+ return ret;
+}
+
+/**
+ * creates a help menu
+ * \param group the 'global' in a gtk sense accelerator reference
+ */
+static struct nsgtk_help_menu *nsgtk_menu_help_submenu(GtkAccelGroup *group)
+{
+ struct nsgtk_help_menu *ret = malloc(sizeof(struct nsgtk_help_menu));
+ if (ret == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ ret->help_menu = GTK_MENU(gtk_menu_new());
+ if (ret->help_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(ret);
+ return NULL;
+ }
+ IMAGE_ITEM(help, contents, gtkContents, ret, group);
+ IMAGE_ITEM(help, guide, gtkGuide, ret, group);
+ IMAGE_ITEM(help, info, gtkUserInformation, ret, group);
+ ADD_SEP(help, ret);
+ IMAGE_ITEM(help, about, gtkAbout, ret, group);
+
+ return ret;
+}
+
+
+/**
+ * Generate menubar menus.
+ *
+ * Generate the main menu structure and attach it to a menubar widget.
+ */
+struct nsgtk_bar_submenu *
+nsgtk_menu_bar_create(GtkMenuShell *menubar, GtkAccelGroup *group)
+{
+ struct nsgtk_bar_submenu *nmenu;
+
+ nmenu = calloc(1, sizeof(struct nsgtk_bar_submenu));
+ if (nmenu == NULL) {
+ return NULL;
+ }
+
+ /* create sub menus */
+ nmenu->file_submenu = nsgtk_menu_file_submenu(group);
+ nmenu->edit_submenu = nsgtk_menu_edit_submenu(group);
+ nmenu->view_submenu = nsgtk_menu_view_submenu(group);
+ nmenu->nav_submenu = nsgtk_menu_nav_submenu(group);
+ nmenu->tools_submenu = nsgtk_menu_tools_submenu(group);
+ nmenu->help_submenu = nsgtk_menu_help_submenu(group);
+
+ if (menubar != NULL) {
+ nmenu->bar_menu = GTK_MENU_BAR(menubar);
+
+ /* attach menus to menubar */
+ ATTACH_PARENT(menubar, gtkFile, nmenu->file_submenu->file, group);
+ ATTACH_PARENT(menubar, gtkEdit, nmenu->edit_submenu->edit, group);
+ ATTACH_PARENT(menubar, gtkView, nmenu->view_submenu->view, group);
+ ATTACH_PARENT(menubar, gtkNavigate, nmenu->nav_submenu->nav, group);
+ ATTACH_PARENT(menubar, gtkTools, nmenu->tools_submenu->tools, group);
+ ATTACH_PARENT(menubar, gtkHelp, nmenu->help_submenu->help, group);
+ }
+
+ return nmenu;
+}
+
+/* exported function documented in gtk/menu.h */
+struct nsgtk_popup_menu *nsgtk_popup_menu_create(GtkAccelGroup *group)
+{
+ struct nsgtk_popup_menu *nmenu;
+
+ NEW_MENU(nmenu, popup);
+
+ IMAGE_ITEM(popup, file, gtkFile, nmenu, group);
+ SET_SUBMENU(file, nmenu);
+
+ IMAGE_ITEM(popup, edit, gtkEdit, nmenu, group);
+ SET_SUBMENU(edit, nmenu);
+
+ IMAGE_ITEM(popup, view, gtkView, nmenu, group);
+ SET_SUBMENU(view, nmenu);
+
+ IMAGE_ITEM(popup, nav, gtkNavigate, nmenu, group);
+ SET_SUBMENU(nav, nmenu);
+
+ IMAGE_ITEM(popup, tools, gtkTools, nmenu, group);
+ SET_SUBMENU(tools, nmenu);
+
+ IMAGE_ITEM(popup, help, gtkHelp, nmenu, group);
+ SET_SUBMENU(help, nmenu);
+
+ ADD_NAMED_SEP(popup, first, nmenu);
+
+ IMAGE_ITEM(popup, back, gtkBack, nmenu, group);
+ IMAGE_ITEM(popup, forward, gtkForward, nmenu, group);
+
+ ADD_NAMED_SEP(popup, third, nmenu);
+
+ IMAGE_ITEM(popup, stop, gtkStop, nmenu, group);
+ IMAGE_ITEM(popup, reload, gtkReload, nmenu, group);
+ IMAGE_ITEM(popup, cut, gtkCut, nmenu, group);
+ IMAGE_ITEM(popup, copy, gtkCopy, nmenu, group);
+ IMAGE_ITEM(popup, paste, gtkPaste, nmenu, group);
+ IMAGE_ITEM(popup, customize, gtkCustomize, nmenu, group);
+
+ return nmenu;
+}
+
+
+/* exported function documented in gtk/menu.h */
+struct nsgtk_link_menu *
+nsgtk_link_menu_create(GtkAccelGroup *group)
+{
+ struct nsgtk_link_menu *nmenu;
+
+ NEW_MENU(nmenu, link);
+
+ IMAGE_ITEM(link, opentab, gtkOpentab, nmenu, group);
+ IMAGE_ITEM(link, openwin, gtkOpenwin, nmenu, group);
+
+ ADD_SEP(link, nmenu);
+
+ IMAGE_ITEM(link, save, gtkSavelink, nmenu, group);
+ IMAGE_ITEM(link, bookmark, gtkBookmarklink, nmenu, group);
+ IMAGE_ITEM(link, copy, gtkCopylink, nmenu, group);
+
+ return nmenu;
+}
diff --git a/frontends/gtk/menu.h b/frontends/gtk/menu.h
new file mode 100644
index 000000000..5da5cb1b2
--- /dev/null
+++ b/frontends/gtk/menu.h
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_GTK_MENU_H_
+#define _NETSURF_GTK_MENU_H_
+
+#include <gtk/gtk.h>
+
+struct nsgtk_file_menu {
+ GtkMenuItem *file; /* File menu item on menubar */
+ GtkMenu *file_menu;
+ GtkWidget *newwindow_menuitem;
+ GtkWidget *newtab_menuitem;
+ GtkWidget *openfile_menuitem;
+ GtkWidget *closewindow_menuitem;
+ GtkWidget *savepage_menuitem;
+ GtkWidget *export_menuitem;
+ struct nsgtk_export_submenu *export_submenu;
+ GtkWidget *printpreview_menuitem;
+ GtkWidget *print_menuitem;
+ GtkWidget *quit_menuitem;
+};
+
+struct nsgtk_edit_menu {
+ GtkMenuItem *edit; /* Edit menu item on menubar */
+ GtkMenu *edit_menu;
+ GtkWidget *cut_menuitem;
+ GtkWidget *copy_menuitem;
+ GtkWidget *paste_menuitem;
+ GtkWidget *delete_menuitem;
+ GtkWidget *selectall_menuitem;
+ GtkWidget *find_menuitem;
+ GtkWidget *preferences_menuitem;
+};
+
+struct nsgtk_view_menu {
+ GtkMenuItem *view; /* View menu item on menubar */
+ GtkMenu *view_menu; /* gtk menu attached to menu item */
+ GtkWidget *stop_menuitem;
+ GtkWidget *reload_menuitem;
+ GtkWidget *scaleview_menuitem;
+ struct nsgtk_scaleview_submenu *scaleview_submenu;
+ GtkWidget *fullscreen_menuitem;
+ GtkWidget *images_menuitem;
+ struct nsgtk_images_submenu *images_submenu;
+ GtkWidget *toolbars_menuitem;
+ struct nsgtk_toolbars_submenu *toolbars_submenu;
+ GtkWidget *tabs_menuitem;
+ struct nsgtk_tabs_submenu *tabs_submenu;
+ GtkWidget *savewindowsize_menuitem;
+};
+
+struct nsgtk_nav_menu {
+ GtkMenuItem *nav; /* Nav menu item on menubar */
+ GtkMenu *nav_menu;
+ GtkWidget *back_menuitem;
+ GtkWidget *forward_menuitem;
+ GtkWidget *home_menuitem;
+ GtkWidget *localhistory_menuitem;
+ GtkWidget *globalhistory_menuitem;
+ GtkWidget *addbookmarks_menuitem;
+ GtkWidget *showbookmarks_menuitem;
+ GtkWidget *openlocation_menuitem;
+};
+
+struct nsgtk_tools_menu {
+ GtkMenuItem *tools; /* Tools menu item on menubar */
+ GtkMenu *tools_menu;
+
+ GtkWidget *showcookies_menuitem;
+ GtkWidget *downloads_menuitem;
+ GtkWidget *developer_menuitem;
+ struct nsgtk_developer_submenu *developer_submenu;
+};
+
+struct nsgtk_help_menu {
+ GtkMenuItem *help; /* Help menu item on menubar */
+ GtkMenu *help_menu;
+ GtkWidget *contents_menuitem;
+ GtkWidget *guide_menuitem;
+ GtkWidget *info_menuitem;
+ GtkWidget *about_menuitem;
+};
+
+
+struct nsgtk_export_submenu {
+ GtkMenu *export_menu;
+ GtkWidget *plaintext_menuitem;
+ GtkWidget *drawfile_menuitem;
+ GtkWidget *postscript_menuitem;
+ GtkWidget *pdf_menuitem;
+};
+
+struct nsgtk_scaleview_submenu {
+ GtkMenu *scaleview_menu;
+ GtkWidget *zoomplus_menuitem;
+ GtkWidget *zoomminus_menuitem;
+ GtkWidget *zoomnormal_menuitem;
+};
+
+struct nsgtk_tabs_submenu {
+ GtkMenu *tabs_menu;
+ GtkWidget *nexttab_menuitem;
+ GtkWidget *prevtab_menuitem;
+ GtkWidget *closetab_menuitem;
+};
+
+struct nsgtk_images_submenu {
+ GtkMenu *images_menu;
+ GtkCheckMenuItem *foregroundimages_menuitem;
+ GtkCheckMenuItem *backgroundimages_menuitem;
+};
+
+struct nsgtk_toolbars_submenu {
+ GtkMenu *toolbars_menu;
+ GtkCheckMenuItem *menubar_menuitem;
+ GtkCheckMenuItem *toolbar_menuitem;
+};
+
+struct nsgtk_developer_submenu {
+ GtkMenu *developer_menu;
+
+ GtkWidget *viewsource_menuitem;
+ GtkWidget *toggledebugging_menuitem;
+ GtkWidget *debugboxtree_menuitem;
+ GtkWidget *debugdomtree_menuitem;
+};
+
+
+struct nsgtk_bar_submenu {
+ GtkMenuBar *bar_menu;
+ struct nsgtk_file_menu *file_submenu;
+ struct nsgtk_edit_menu *edit_submenu;
+ struct nsgtk_view_menu *view_submenu;
+ struct nsgtk_nav_menu *nav_submenu;
+ struct nsgtk_tabs_submenu *tabs_submenu;
+ struct nsgtk_tools_menu *tools_submenu;
+ struct nsgtk_help_menu *help_submenu;
+};
+
+struct nsgtk_popup_menu {
+ GtkMenu *popup_menu;
+
+ GtkWidget *file_menuitem;
+ struct nsgtk_file_menu *file_submenu;
+
+ GtkWidget *edit_menuitem;
+ struct nsgtk_edit_menu *edit_submenu;
+
+ GtkWidget *view_menuitem;
+ struct nsgtk_view_menu *view_submenu;
+
+ GtkWidget *nav_menuitem;
+ struct nsgtk_nav_menu *nav_submenu;
+
+ GtkWidget *tabs_menuitem;
+ struct nsgtk_tabs_submenu *tabs_submenu;
+
+ GtkWidget *tools_menuitem;
+ struct nsgtk_tools_menu *tools_submenu;
+
+ GtkWidget *help_menuitem;
+ struct nsgtk_help_menu *help_submenu;
+
+ GtkWidget *first_separator;
+
+ /* navigation entries */
+ GtkWidget *back_menuitem;
+ GtkWidget *forward_menuitem;
+
+ GtkWidget *third_separator;
+
+ /* view entries */
+ GtkWidget *stop_menuitem;
+ GtkWidget *reload_menuitem;
+
+ GtkWidget *cut_menuitem;
+ GtkWidget *copy_menuitem;
+ GtkWidget *paste_menuitem;
+ GtkWidget *customize_menuitem;
+
+};
+
+struct nsgtk_link_menu {
+ GtkMenu *link_menu;
+
+ GtkWidget *opentab_menuitem;
+ GtkWidget *openwin_menuitem;
+
+ GtkWidget *save_menuitem;
+ GtkWidget *bookmark_menuitem;
+ GtkWidget *copy_menuitem;
+};
+
+/**
+ * Create main menu bar.
+ */
+struct nsgtk_bar_submenu *nsgtk_menu_bar_create(GtkMenuShell *menubar, GtkAccelGroup *group);
+
+/**
+ * Generate right click menu menu.
+ *
+ */
+struct nsgtk_popup_menu *nsgtk_popup_menu_create(GtkAccelGroup *group);
+
+/**
+ * Generate context sensitive popup menu for link.
+ *
+ */
+struct nsgtk_link_menu *nsgtk_link_menu_create(GtkAccelGroup *group);
+
+
+#endif
diff --git a/frontends/gtk/options.h b/frontends/gtk/options.h
new file mode 100644
index 000000000..ac642c153
--- /dev/null
+++ b/frontends/gtk/options.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_GTK_OPTIONS_H_
+#define _NETSURF_GTK_OPTIONS_H_
+
+/* currently nothing here */
+
+#endif
+
+/* High quality image scaling */
+NSOPTION_BOOL(render_resample, true)
+
+/* clear downloads */
+NSOPTION_BOOL(downloads_clear, false)
+
+/* prompt before overwriting downloads */
+NSOPTION_BOOL(request_overwrite, true)
+
+/* location to download files to */
+NSOPTION_STRING(downloads_directory, NULL)
+
+/* where to store URL database */
+NSOPTION_STRING(url_file, NULL)
+
+/* Always show tabs even if there is only one */
+NSOPTION_BOOL(show_single_tab, false)
+
+/* size of buttons */
+NSOPTION_INTEGER(button_type, 0)
+
+/* disallow popup windows */
+NSOPTION_BOOL(disable_popups, false)
+
+/* disable content plugins */
+NSOPTION_BOOL(disable_plugins, false)
+
+/* number of days to keep history data */
+NSOPTION_INTEGER(history_age, 0)
+
+/* show urls in local history browser */
+NSOPTION_BOOL(hover_urls, false)
+
+/* bring new tabs to front */
+NSOPTION_BOOL(focus_new, false)
+
+/* new tabs are blank instead of homepage */
+NSOPTION_BOOL(new_blank, false)
+
+/* path to save hotlist file */
+NSOPTION_STRING(hotlist_path, NULL)
+
+/* Developer information viewer display method */
+NSOPTION_INTEGER(developer_view, 0)
+
+/* where tabs are positioned */
+NSOPTION_INTEGER(position_tab, 0)
diff --git a/frontends/gtk/plotters.c b/frontends/gtk/plotters.c
new file mode 100644
index 000000000..1d8c19827
--- /dev/null
+++ b/frontends/gtk/plotters.c
@@ -0,0 +1,538 @@
+/*
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * GTK and Cairo plotter implementations.
+ *
+ * Uses Cairo drawing primitives to render browser output.
+ * \todo remove the use of the gdk structure for clipping
+ */
+
+#include <math.h>
+#include <assert.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "desktop/plotters.h"
+#include "utils/nsoption.h"
+
+#include "gtk/layout_pango.h"
+#include "gtk/plotters.h"
+#include "gtk/scaffolding.h"
+#include "gtk/bitmap.h"
+
+GtkWidget *current_widget;
+cairo_t *current_cr;
+
+static GdkRectangle cliprect;
+
+struct plotter_table plot;
+
+/** Set cairo context colour to nsgtk colour. */
+void nsgtk_set_colour(colour c)
+{
+ cairo_set_source_rgba(current_cr,
+ (c & 0xff) / 255.0,
+ ((c & 0xff00) >> 8) / 255.0,
+ ((c & 0xff0000) >> 16) / 255.0,
+ 1.0);
+}
+
+/** Set cairo context to solid plot operation. */
+static inline void nsgtk_set_solid(void)
+{
+ double dashes = 0;
+ cairo_set_dash(current_cr, &dashes, 0, 0);
+}
+
+/** Set cairo context to dotted plot operation. */
+static inline void nsgtk_set_dotted(void)
+{
+ double cdashes[] = { 1.0, 2.0 };
+ cairo_set_dash(current_cr, cdashes, 2, 0);
+}
+
+/** Set cairo context to dashed plot operation. */
+static inline void nsgtk_set_dashed(void)
+{
+ double cdashes[] = { 8.0, 2.0 };
+ cairo_set_dash(current_cr, cdashes, 2, 0);
+}
+
+/** Set clipping area for subsequent plot operations. */
+static bool nsgtk_plot_clip(const struct rect *clip)
+{
+ cairo_reset_clip(current_cr);
+ cairo_rectangle(current_cr, clip->x0, clip->y0,
+ clip->x1 - clip->x0, clip->y1 - clip->y0);
+ cairo_clip(current_cr);
+
+ cliprect.x = clip->x0;
+ cliprect.y = clip->y0;
+ cliprect.width = clip->x1 - clip->x0;
+ cliprect.height = clip->y1 - clip->y0;
+
+ return true;
+}
+
+
+static bool nsgtk_plot_arc(int x, int y, int radius, int angle1, int angle2, const plot_style_t *style)
+{
+ nsgtk_set_colour(style->fill_colour);
+ nsgtk_set_solid();
+
+ cairo_set_line_width(current_cr, 1);
+ cairo_arc(current_cr, x, y, radius,
+ (angle1 + 90) * (M_PI / 180),
+ (angle2 + 90) * (M_PI / 180));
+ cairo_stroke(current_cr);
+
+ return true;
+}
+
+static bool nsgtk_plot_disc(int x, int y, int radius, const plot_style_t *style)
+{
+ if (style->fill_type != PLOT_OP_TYPE_NONE) {
+ nsgtk_set_colour(style->fill_colour);
+ nsgtk_set_solid();
+ cairo_set_line_width(current_cr, 0);
+ cairo_arc(current_cr, x, y, radius, 0, M_PI * 2);
+ cairo_fill(current_cr);
+ cairo_stroke(current_cr);
+ }
+
+ if (style->stroke_type != PLOT_OP_TYPE_NONE) {
+ nsgtk_set_colour(style->stroke_colour);
+
+ switch (style->stroke_type) {
+ case PLOT_OP_TYPE_SOLID: /**< Solid colour */
+ default:
+ nsgtk_set_solid();
+ break;
+
+ case PLOT_OP_TYPE_DOT: /**< Doted plot */
+ nsgtk_set_dotted();
+ break;
+
+ case PLOT_OP_TYPE_DASH: /**< dashed plot */
+ nsgtk_set_dashed();
+ break;
+ }
+
+ if (style->stroke_width == 0)
+ cairo_set_line_width(current_cr, 1);
+ else
+ cairo_set_line_width(current_cr, style->stroke_width);
+
+ cairo_arc(current_cr, x, y, radius, 0, M_PI * 2);
+
+ cairo_stroke(current_cr);
+ }
+
+ return true;
+}
+
+static bool
+nsgtk_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ nsgtk_set_colour(style->stroke_colour);
+
+ switch (style->stroke_type) {
+ case PLOT_OP_TYPE_SOLID: /**< Solid colour */
+ default:
+ nsgtk_set_solid();
+ break;
+
+ case PLOT_OP_TYPE_DOT: /**< Doted plot */
+ nsgtk_set_dotted();
+ break;
+
+ case PLOT_OP_TYPE_DASH: /**< dashed plot */
+ nsgtk_set_dashed();
+ break;
+ }
+
+ if (style->stroke_type != PLOT_OP_TYPE_NONE) {
+ nsgtk_set_colour(style->stroke_colour);
+ }
+
+ if (style->stroke_width == 0)
+ cairo_set_line_width(current_cr, 1);
+ else
+ cairo_set_line_width(current_cr, style->stroke_width);
+
+ /* core expects horizontal and vertical lines to be on pixels, not
+ * between pixels */
+ cairo_move_to(current_cr, (x0 == x1) ? x0 + 0.5 : x0,
+ (y0 == y1) ? y0 + 0.5 : y0);
+ cairo_line_to(current_cr, (x0 == x1) ? x1 + 0.5 : x1,
+ (y0 == y1) ? y1 + 0.5 : y1);
+ cairo_stroke(current_cr);
+
+ return true;
+}
+
+/** Plot a caret.
+ *
+ * @note It is assumed that the plotters have been set up.
+ */
+void nsgtk_plot_caret(int x, int y, int h)
+{
+ nsgtk_set_solid(); /* solid line */
+ nsgtk_set_colour(0); /* black */
+ cairo_set_line_width(current_cr, 1); /* thin line */
+
+ /* core expects horizontal and vertical lines to be on pixels, not
+ * between pixels */
+ cairo_move_to(current_cr, x + 0.5, y);
+ cairo_line_to(current_cr, x + 0.5, y + h - 1);
+ cairo_stroke(current_cr);
+}
+
+static bool nsgtk_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ if (style->fill_type != PLOT_OP_TYPE_NONE) {
+ nsgtk_set_colour(style->fill_colour);
+ nsgtk_set_solid();
+
+ cairo_set_line_width(current_cr, 0);
+ cairo_rectangle(current_cr, x0, y0, x1 - x0, y1 - y0);
+ cairo_fill(current_cr);
+ cairo_stroke(current_cr);
+ }
+
+ if (style->stroke_type != PLOT_OP_TYPE_NONE) {
+ nsgtk_set_colour(style->stroke_colour);
+
+ switch (style->stroke_type) {
+ case PLOT_OP_TYPE_SOLID: /**< Solid colour */
+ default:
+ nsgtk_set_solid();
+ break;
+
+ case PLOT_OP_TYPE_DOT: /**< Doted plot */
+ nsgtk_set_dotted();
+ break;
+
+ case PLOT_OP_TYPE_DASH: /**< dashed plot */
+ nsgtk_set_dashed();
+ break;
+ }
+
+ if (style->stroke_width == 0)
+ cairo_set_line_width(current_cr, 1);
+ else
+ cairo_set_line_width(current_cr, style->stroke_width);
+
+ cairo_rectangle(current_cr, x0 + 0.5, y0 + 0.5, x1 - x0, y1 - y0);
+ cairo_stroke(current_cr);
+ }
+ return true;
+}
+
+static bool nsgtk_plot_polygon(const int *p, unsigned int n, const plot_style_t *style)
+{
+ unsigned int i;
+
+ nsgtk_set_colour(style->fill_colour);
+ nsgtk_set_solid();
+
+ cairo_set_line_width(current_cr, 0);
+ cairo_move_to(current_cr, p[0], p[1]);
+ for (i = 1; i != n; i++) {
+ cairo_line_to(current_cr, p[i * 2], p[i * 2 + 1]);
+ }
+ cairo_fill(current_cr);
+ cairo_stroke(current_cr);
+
+ return true;
+}
+
+
+
+
+static bool nsgtk_plot_text(int x, int y, const char *text, size_t length,
+ const struct plot_font_style *fstyle)
+{
+ return nsfont_paint(x, y, text, length, fstyle);
+}
+
+
+
+static bool nsgtk_plot_pixbuf(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg)
+{
+ int x0, y0, x1, y1;
+ int dsrcx, dsrcy, dwidth, dheight;
+ int bmwidth, bmheight;
+
+ cairo_surface_t *bmsurface = bitmap->surface;
+
+ /* Bail early if we can */
+ if (width == 0 || height == 0)
+ /* Nothing to plot */
+ return true;
+ if ((x > (cliprect.x + cliprect.width)) ||
+ ((x + width) < cliprect.x) ||
+ (y > (cliprect.y + cliprect.height)) ||
+ ((y + height) < cliprect.y)) {
+ /* Image completely outside clip region */
+ return true;
+ }
+
+ /* Get clip rectangle / image rectangle edge differences */
+ x0 = cliprect.x - x;
+ y0 = cliprect.y - y;
+ x1 = (x + width) - (cliprect.x + cliprect.width);
+ y1 = (y + height) - (cliprect.y + cliprect.height);
+
+ /* Set initial draw geometry */
+ dsrcx = x;
+ dsrcy = y;
+ dwidth = width;
+ dheight = height;
+
+ /* Manually clip draw coordinates to area of image to be rendered */
+ if (x0 > 0) {
+ /* Clip left */
+ dsrcx += x0;
+ dwidth -= x0;
+ }
+ if (y0 > 0) {
+ /* Clip top */
+ dsrcy += y0;
+ dheight -= y0;
+ }
+ if (x1 > 0) {
+ /* Clip right */
+ dwidth -= x1;
+ }
+ if (y1 > 0) {
+ /* Clip bottom */
+ dheight -= y1;
+ }
+
+ if (dwidth == 0 || dheight == 0)
+ /* Nothing to plot */
+ return true;
+
+ bmwidth = cairo_image_surface_get_width(bmsurface);
+ bmheight = cairo_image_surface_get_height(bmsurface);
+
+ /* Render the bitmap */
+ if ((bmwidth == width) && (bmheight == height)) {
+ /* Bitmap is not scaled */
+ /* Plot the bitmap */
+ cairo_set_source_surface(current_cr, bmsurface, x, y);
+ cairo_rectangle(current_cr, dsrcx, dsrcy, dwidth, dheight);
+ cairo_fill(current_cr);
+
+ } else {
+ /* Bitmap is scaled */
+ if ((bitmap->scsurface != NULL) &&
+ ((cairo_image_surface_get_width(bitmap->scsurface) != width) ||
+ (cairo_image_surface_get_height(bitmap->scsurface) != height))){
+ cairo_surface_destroy(bitmap->scsurface);
+ bitmap->scsurface = NULL;
+ }
+
+ if (bitmap->scsurface == NULL) {
+ bitmap->scsurface = cairo_surface_create_similar(bmsurface,CAIRO_CONTENT_COLOR_ALPHA, width, height);
+ cairo_t *cr = cairo_create(bitmap->scsurface);
+
+ /* Scale *before* setting the source surface (1) */
+ cairo_scale(cr, (double)width / bmwidth, (double)height / bmheight);
+ cairo_set_source_surface(cr, bmsurface, 0, 0);
+
+ /* To avoid getting the edge pixels blended with 0
+ * alpha, which would occur with the default
+ * EXTEND_NONE. Use EXTEND_PAD for 1.2 or newer (2)
+ */
+ cairo_pattern_set_extend(cairo_get_source(cr),
+ CAIRO_EXTEND_REFLECT);
+
+ /* Replace the destination with the source instead of
+ * overlaying
+ */
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+
+ /* Do the actual drawing */
+ cairo_paint(cr);
+
+ cairo_destroy(cr);
+
+ }
+ /* Plot the scaled bitmap */
+ cairo_set_source_surface(current_cr, bitmap->scsurface, x, y);
+ cairo_rectangle(current_cr, dsrcx, dsrcy, dwidth, dheight);
+ cairo_fill(current_cr);
+
+
+ }
+
+ return true;
+}
+
+static bool nsgtk_plot_bitmap(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg,
+ bitmap_flags_t flags)
+{
+ int doneheight = 0, donewidth = 0;
+ bool repeat_x = (flags & BITMAPF_REPEAT_X);
+ bool repeat_y = (flags & BITMAPF_REPEAT_Y);
+
+ /* Bail early if we can */
+ if (width == 0 || height == 0)
+ /* Nothing to plot */
+ return true;
+
+ if (!(repeat_x || repeat_y)) {
+ /* Not repeating at all, so just pass it on */
+ return nsgtk_plot_pixbuf(x, y, width, height, bitmap, bg);
+ }
+
+ if (y > cliprect.y) {
+ doneheight = (cliprect.y - height) + ((y - cliprect.y) % height);
+ } else {
+ doneheight = y;
+ }
+
+ while (doneheight < (cliprect.y + cliprect.height)) {
+ if (x > cliprect.x) {
+ donewidth = (cliprect.x - width) + ((x - cliprect.x) % width);
+ } else {
+ donewidth = x;
+ }
+
+ while (donewidth < (cliprect.x + cliprect.width)) {
+ nsgtk_plot_pixbuf(donewidth, doneheight,
+ width, height, bitmap, bg);
+ donewidth += width;
+ if (!repeat_x)
+ break;
+ }
+ doneheight += height;
+
+ if (!repeat_y)
+ break;
+ }
+
+ return true;
+}
+
+static bool nsgtk_plot_path(const float *p, unsigned int n, colour fill, float width,
+ colour c, const float transform[6])
+{
+ unsigned int i;
+ cairo_matrix_t old_ctm, n_ctm;
+
+ if (n == 0)
+ return true;
+
+ if (p[0] != PLOTTER_PATH_MOVE) {
+ LOG("Path does not start with move");
+ return false;
+ }
+
+
+ /* Save CTM */
+ cairo_get_matrix(current_cr, &old_ctm);
+
+ /* Set up line style and width */
+ cairo_set_line_width(current_cr, 1);
+ nsgtk_set_solid();
+
+ /* Load new CTM */
+ n_ctm.xx = transform[0];
+ n_ctm.yx = transform[1];
+ n_ctm.xy = transform[2];
+ n_ctm.yy = transform[3];
+ n_ctm.x0 = transform[4];
+ n_ctm.y0 = transform[5];
+
+ cairo_set_matrix(current_cr, &n_ctm);
+
+ /* Construct path */
+ for (i = 0; i < n; ) {
+ if (p[i] == PLOTTER_PATH_MOVE) {
+ cairo_move_to(current_cr, p[i+1], p[i+2]);
+ i += 3;
+ } else if (p[i] == PLOTTER_PATH_CLOSE) {
+ cairo_close_path(current_cr);
+ i++;
+ } else if (p[i] == PLOTTER_PATH_LINE) {
+ cairo_line_to(current_cr, p[i+1], p[i+2]);
+ i += 3;
+ } else if (p[i] == PLOTTER_PATH_BEZIER) {
+ cairo_curve_to(current_cr, p[i+1], p[i+2],
+ p[i+3], p[i+4],
+ p[i+5], p[i+6]);
+ i += 7;
+ } else {
+ LOG("bad path command %f", p[i]);
+ /* Reset matrix for safety */
+ cairo_set_matrix(current_cr, &old_ctm);
+ return false;
+ }
+ }
+
+ /* Restore original CTM */
+ cairo_set_matrix(current_cr, &old_ctm);
+
+ /* Now draw path */
+ if (fill != NS_TRANSPARENT) {
+ nsgtk_set_colour(fill);
+
+ if (c != NS_TRANSPARENT) {
+ /* Fill & Stroke */
+ cairo_fill_preserve(current_cr);
+ nsgtk_set_colour(c);
+ cairo_stroke(current_cr);
+ } else {
+ /* Fill only */
+ cairo_fill(current_cr);
+ }
+ } else if (c != NS_TRANSPARENT) {
+ /* Stroke only */
+ nsgtk_set_colour(c);
+ cairo_stroke(current_cr);
+ }
+
+ return true;
+}
+
+/** GTK plotter table */
+const struct plotter_table nsgtk_plotters = {
+ .clip = nsgtk_plot_clip,
+ .arc = nsgtk_plot_arc,
+ .disc = nsgtk_plot_disc,
+ .line = nsgtk_plot_line,
+ .rectangle = nsgtk_plot_rectangle,
+ .polygon = nsgtk_plot_polygon,
+ .path = nsgtk_plot_path,
+ .bitmap = nsgtk_plot_bitmap,
+ .text = nsgtk_plot_text,
+ .option_knockout = true
+};
+
+
+
diff --git a/frontends/gtk/plotters.h b/frontends/gtk/plotters.h
new file mode 100644
index 000000000..c88a8da0c
--- /dev/null
+++ b/frontends/gtk/plotters.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Target independent plotting (GDK / GTK+ interface).
+ */
+
+#ifndef NETSURF_GTK_PLOTTERS_H
+#define NETSURF_GTK_PLOTTERS_H 1
+
+#include <gtk/gtk.h>
+
+struct plotter_table;
+
+extern const struct plotter_table nsgtk_plotters;
+
+/* make sure this is NULL if no redraw is in progress */
+extern GtkWidget *current_widget;
+extern cairo_t *current_cr;
+
+void nsgtk_set_colour(colour c);
+void nsgtk_plot_caret(int x, int y, int h);
+
+#endif /* NETSURF_GTK_PLOTTERS_H */
+
diff --git a/frontends/gtk/preferences.c b/frontends/gtk/preferences.c
new file mode 100644
index 000000000..3efb9eddd
--- /dev/null
+++ b/frontends/gtk/preferences.c
@@ -0,0 +1,1030 @@
+/*
+ * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <stdint.h>
+#include <math.h>
+#include <string.h>
+
+#include "utils/messages.h"
+#include "utils/nsoption.h"
+#include "utils/file.h"
+#include "utils/log.h"
+#include "utils/nsurl.h"
+#include "desktop/browser.h"
+#include "desktop/searchweb.h"
+
+#include "gtk/compat.h"
+#include "gtk/window.h"
+#include "gtk/gui.h"
+#include "gtk/scaffolding.h"
+#include "gtk/resources.h"
+#include "gtk/preferences.h"
+
+/* private prefs */
+struct ppref {
+ /** dialog handle created when window first accessed */
+ GObject *dialog;
+
+ struct browser_window *bw;
+
+ /* widgets which are accessed from outside their own signal handlers */
+ GtkEntry *entryHomePageURL;
+ GtkEntry *entryProxyHost;
+ GtkEntry *entryProxyUser;
+ GtkEntry *entryProxyPassword;
+ GtkEntry *entryProxyNoproxy;
+ GtkSpinButton *spinProxyPort;
+
+ /* dynamic list stores */
+ GtkListStore *content_language;
+ GtkListStore *search_providers;
+};
+
+static struct ppref ppref;
+
+
+/* Set netsurf option based on toggle button state
+ *
+ * This works for any widget which subclasses togglebutton (checkbox,
+ * radiobutton etc.)
+ */
+#define TOGGLEBUTTON_SIGNALS(WIDGET, OPTION) \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_toggled(GtkToggleButton *togglebutton, \
+ struct ppref *priv); \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_toggled(GtkToggleButton *togglebutton, \
+ struct ppref *priv) \
+{ \
+ nsoption_set_bool(OPTION, \
+ gtk_toggle_button_get_active(togglebutton)); \
+} \
+ \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, \
+ struct ppref *priv); \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, \
+ struct ppref *priv) \
+{ \
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), \
+ nsoption_bool(OPTION)); \
+}
+
+#define SPINBUTTON_SIGNALS(WIDGET, OPTION, MULTIPLIER) \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_valuechanged(GtkSpinButton *spinbutton, \
+ struct ppref *priv); \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_valuechanged(GtkSpinButton *spinbutton, \
+ struct ppref *priv) \
+{ \
+ nsoption_set_int(OPTION, \
+ round(gtk_spin_button_get_value(spinbutton) * MULTIPLIER)); \
+} \
+ \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv); \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv) \
+{ \
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), \
+ ((gdouble)nsoption_int(OPTION)) / MULTIPLIER); \
+}
+
+#define SPINBUTTON_UINT_SIGNALS(WIDGET, OPTION, MULTIPLIER) \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_valuechanged(GtkSpinButton *spinbutton, \
+ struct ppref *priv); \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_valuechanged(GtkSpinButton *spinbutton, \
+ struct ppref *priv) \
+{ \
+ nsoption_set_uint(OPTION, \
+ round(gtk_spin_button_get_value(spinbutton) * MULTIPLIER)); \
+} \
+ \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv); \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv) \
+{ \
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), \
+ ((gdouble)nsoption_uint(OPTION)) / MULTIPLIER); \
+}
+
+#define ENTRY_SIGNALS(WIDGET, OPTION) \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_changed(GtkEditable *editable, struct ppref *priv); \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_changed(GtkEditable *editable, struct ppref *priv)\
+{ \
+ nsoption_set_charp(OPTION, \
+ strdup(gtk_entry_get_text(GTK_ENTRY(editable)))); \
+} \
+ \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv); \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv) \
+{ \
+ const char *OPTION; \
+ OPTION = nsoption_charp(OPTION); \
+ if (OPTION != NULL) { \
+ gtk_entry_set_text(GTK_ENTRY(widget), OPTION); \
+ } \
+}
+
+/* GTK module requires these to be exported symbols so these all need
+ * forward declaring to avoid warnings
+ */
+G_MODULE_EXPORT void nsgtk_preferences_comboProxyType_changed(GtkComboBox *combo, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboProxyType_realize(GtkWidget *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboboxLoadImages_changed(GtkComboBox *combo, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboboxLoadImages_realize(GtkWidget *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboDefault_changed(GtkComboBox *combo, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboDefault_realize(GtkWidget *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_fontPreview_clicked(GtkButton *button, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboboxLanguage_changed(GtkComboBox *combo, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboboxLanguage_realize(GtkWidget *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_checkShowSingleTab_toggled(GtkToggleButton *togglebutton, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_checkShowSingleTab_realize(GtkWidget *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboTabPosition_changed(GtkComboBox *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboTabPosition_realize(GtkWidget *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboDeveloperView_changed(GtkComboBox *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboDeveloperView_realize(GtkWidget *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboButtonType_changed(GtkComboBox *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboButtonType_realize(GtkWidget *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_setCurrentPage_clicked(GtkButton *button, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_setDefaultPage_clicked(GtkButton *button, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboSearch_changed(GtkComboBox *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboSearch_realize(GtkWidget *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_fileChooserDownloads_selectionchanged(GtkFileChooser *chooser, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_fileChooserDownloads_realize(GtkWidget *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_dialogPreferences_response(GtkDialog *dlg, gint resid);
+G_MODULE_EXPORT gboolean nsgtk_preferences_dialogPreferences_deleteevent(GtkDialog *dlg, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_dialogPreferences_destroy(GtkDialog *dlg, struct ppref *priv);
+
+
+/********* PDF **********/
+
+/* Appearance */
+
+/* no images in output */
+TOGGLEBUTTON_SIGNALS(checkSuppressImages, suppress_images)
+
+/* no background images */
+TOGGLEBUTTON_SIGNALS(checkRemoveBackgrounds, remove_backgrounds)
+
+/* scale to fit page */
+TOGGLEBUTTON_SIGNALS(checkFitPage, enable_loosening)
+
+/* port */
+SPINBUTTON_SIGNALS(spinExportScale, export_scale, 1.0)
+
+/* Margins */
+SPINBUTTON_SIGNALS(spinMarginTop, margin_top, 1.0)
+SPINBUTTON_SIGNALS(spinMarginBottom, margin_bottom, 1.0)
+SPINBUTTON_SIGNALS(spinMarginLeft, margin_left, 1.0)
+SPINBUTTON_SIGNALS(spinMarginRight, margin_right, 1.0)
+
+
+/* Generation */
+
+/* output is compressed */
+TOGGLEBUTTON_SIGNALS(checkCompressPDF, enable_PDF_compression)
+
+/* output has a password */
+TOGGLEBUTTON_SIGNALS(checkPasswordPDF, enable_PDF_password)
+
+/********* Network **********/
+
+/* HTTP proxy */
+static void set_proxy_widgets_sensitivity(int proxyval, struct ppref *priv)
+{
+ gboolean host;
+ gboolean port;
+ gboolean user;
+ gboolean pass;
+ gboolean noproxy;
+
+ switch (proxyval) {
+ case 0: /* no proxy */
+ host = FALSE;
+ port = FALSE;
+ user = FALSE;
+ pass = FALSE;
+ noproxy = FALSE;
+ break;
+
+ case 1: /* proxy with no auth */
+ host = TRUE;
+ port = TRUE;
+ user = FALSE;
+ pass = FALSE;
+ noproxy = TRUE;
+ break;
+
+ case 2: /* proxy with basic auth */
+ host = TRUE;
+ port = TRUE;
+ user = TRUE;
+ pass = TRUE;
+ noproxy = TRUE;
+ break;
+
+ case 3: /* proxy with ntlm auth */
+ host = TRUE;
+ port = TRUE;
+ user = TRUE;
+ pass = TRUE;
+ noproxy = TRUE;
+ break;
+
+ case 4: /* system proxy */
+ host = FALSE;
+ port = FALSE;
+ user = FALSE;
+ pass = FALSE;
+ noproxy = FALSE;
+ break;
+
+ default:
+ return;
+ }
+
+ gtk_widget_set_sensitive(GTK_WIDGET(priv->entryProxyHost), host);
+ gtk_widget_set_sensitive(GTK_WIDGET(priv->spinProxyPort), port);
+ gtk_widget_set_sensitive(GTK_WIDGET(priv->entryProxyUser), user);
+ gtk_widget_set_sensitive(GTK_WIDGET(priv->entryProxyPassword), pass);
+ gtk_widget_set_sensitive(GTK_WIDGET(priv->entryProxyNoproxy), noproxy);
+
+}
+
+G_MODULE_EXPORT void
+nsgtk_preferences_comboProxyType_changed(GtkComboBox *combo, struct ppref *priv)
+{
+ int proxy_sel;
+ proxy_sel = gtk_combo_box_get_active(combo);
+
+ switch (proxy_sel) {
+ case 0: /* no proxy */
+ nsoption_set_bool(http_proxy, false);
+ break;
+
+ case 1: /* proxy with no auth */
+ nsoption_set_bool(http_proxy, true);
+ nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NONE);
+ break;
+
+ case 2: /* proxy with basic auth */
+ nsoption_set_bool(http_proxy, true);
+ nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_BASIC);
+ break;
+
+ case 3: /* proxy with ntlm auth */
+ nsoption_set_bool(http_proxy, true);
+ nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NTLM);
+ break;
+
+ case 4: /* system proxy */
+ nsoption_set_bool(http_proxy, true);
+ nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NONE);
+ break;
+ }
+
+ set_proxy_widgets_sensitivity(proxy_sel, priv);
+}
+
+G_MODULE_EXPORT void
+nsgtk_preferences_comboProxyType_realize(GtkWidget *widget, struct ppref *priv)
+{
+ int proxytype = 0; /* no proxy by default */
+
+ if (nsoption_bool(http_proxy) == true) {
+ /* proxy type combo box starts with disabled, to allow
+ * for this the http_proxy option needs combining with
+ * the http_proxy_auth option
+ */
+ proxytype = nsoption_int(http_proxy_auth) + 1;
+ if (nsoption_charp(http_proxy_host) == NULL) {
+ /* set to use a proxy without a host, turn proxy off */
+ proxytype = 0;
+ } else if (((proxytype == 2) ||
+ (proxytype == 3)) &&
+ ((nsoption_charp(http_proxy_auth_user) == NULL) ||
+ (nsoption_charp(http_proxy_auth_pass) == NULL))) {
+ /* authentication selected with empty credentials, turn proxy off */
+ proxytype = 0;
+ }
+ }
+ gtk_combo_box_set_active(GTK_COMBO_BOX(widget), proxytype);
+
+ set_proxy_widgets_sensitivity(proxytype, priv);
+}
+
+/* host */
+ENTRY_SIGNALS(entryProxyHost, http_proxy_host)
+
+/* port */
+SPINBUTTON_SIGNALS(spinProxyPort, http_proxy_port, 1.0)
+
+/* user */
+ENTRY_SIGNALS(entryProxyUser, http_proxy_auth_user)
+
+/* password */
+ENTRY_SIGNALS(entryProxyPassword, http_proxy_auth_pass)
+
+/* no proxy */
+ENTRY_SIGNALS(entryProxyNoproxy, http_proxy_noproxy)
+
+/* Fetching */
+
+/* maximum fetchers */
+SPINBUTTON_SIGNALS(spinMaxFetchers, max_fetchers, 1.0)
+
+/* fetches per host */
+SPINBUTTON_SIGNALS(spinFetchesPerHost, max_fetchers_per_host, 1.0)
+
+/* cached connections */
+SPINBUTTON_SIGNALS(spinCachedConnections, max_cached_fetch_handles, 1.0)
+
+
+/********* Privacy **********/
+
+/* General */
+
+/* enable referral submission */
+TOGGLEBUTTON_SIGNALS(checkSendReferer, send_referer)
+
+/* send do not track */
+TOGGLEBUTTON_SIGNALS(checkSendDNT, do_not_track)
+
+/* History */
+
+/* local history shows url tooltips */
+TOGGLEBUTTON_SIGNALS(checkHoverURLs, hover_urls)
+
+/* remember browsing history */
+SPINBUTTON_SIGNALS(spinHistoryAge, history_age, 1.0)
+
+/* Cache */
+
+/* memory cache size */
+SPINBUTTON_SIGNALS(spinMemoryCacheSize, memory_cache_size, (1024*1024))
+
+/* disc cache size */
+SPINBUTTON_UINT_SIGNALS(spinDiscCacheSize, disc_cache_size, (1024*1024))
+
+
+/* disc cache age */
+SPINBUTTON_SIGNALS(spinDiscCacheAge, disc_cache_age, 1.0)
+
+
+/********* Content **********/
+
+/* Control */
+
+
+/* prevent popups */
+TOGGLEBUTTON_SIGNALS(checkDisablePopups, disable_popups)
+
+/* hide adverts */
+TOGGLEBUTTON_SIGNALS(checkHideAdverts, block_advertisements)
+
+/* enable javascript */
+TOGGLEBUTTON_SIGNALS(checkEnableJavascript, enable_javascript)
+
+/* disable plugins */
+TOGGLEBUTTON_SIGNALS(checkDisablePlugins, disable_plugins)
+
+/* high quality image scaling */
+TOGGLEBUTTON_SIGNALS(checkResampleImages, render_resample)
+
+/* load and display of images */
+G_MODULE_EXPORT void
+nsgtk_preferences_comboboxLoadImages_changed(GtkComboBox *combo,
+ struct ppref *priv)
+{
+ int img_sel;
+ /* get the row number for the selection */
+ img_sel = gtk_combo_box_get_active(combo);
+ switch (img_sel) {
+ case 0:
+ /* background and foreground */
+ nsoption_set_bool(foreground_images, true);
+ nsoption_set_bool(background_images, true);
+ break;
+
+ case 1:
+ /* foreground only */
+ nsoption_set_bool(foreground_images, true);
+ nsoption_set_bool(background_images, false);
+ break;
+
+ case 2:
+ /* background only */
+ nsoption_set_bool(foreground_images, false);
+ nsoption_set_bool(background_images, true);
+ break;
+
+ case 3:
+ /* no images */
+ nsoption_set_bool(foreground_images, false);
+ nsoption_set_bool(background_images, false);
+ break;
+ }
+}
+
+G_MODULE_EXPORT void
+nsgtk_preferences_comboboxLoadImages_realize(GtkWidget *widget,
+ struct ppref *priv)
+{
+ if (nsoption_bool(foreground_images)) {
+ if (nsoption_bool(background_images)) {
+ /* background and foreground */
+ gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 0);
+ } else {
+ /* foreground only */
+ gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 1);
+ }
+ } else {
+ if (nsoption_bool(background_images)) {
+ /* background only */
+ gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 2);
+ } else {
+ /* no images */
+ gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 3);
+ }
+ }
+}
+
+/* Animation */
+
+/* enable animation */
+TOGGLEBUTTON_SIGNALS(checkEnableAnimations, animate_images)
+
+/* frame time */
+SPINBUTTON_SIGNALS(spinAnimationSpeed, minimum_gif_delay, 100.0)
+
+/* Fonts */
+
+/* default font */
+G_MODULE_EXPORT void
+nsgtk_preferences_comboDefault_changed(GtkComboBox *combo, struct ppref *priv)
+{
+ int font_sel;
+ /* get the row number for the selection */
+ font_sel = gtk_combo_box_get_active(combo);
+ if ((font_sel >= 0) && (font_sel <= 4)) {
+ nsoption_set_int(font_default, font_sel);
+ }
+}
+
+G_MODULE_EXPORT void
+nsgtk_preferences_comboDefault_realize(GtkWidget *widget, struct ppref *priv)
+{
+ gtk_combo_box_set_active(GTK_COMBO_BOX(widget),
+ nsoption_int(font_default));
+}
+
+/* default font size */
+SPINBUTTON_SIGNALS(spinDefaultSize, font_size, 10.0)
+
+/* preview - actually reflow all views */
+G_MODULE_EXPORT void
+nsgtk_preferences_fontPreview_clicked(GtkButton *button, struct ppref *priv)
+{
+ nsgtk_reflow_all_windows();
+}
+
+
+/* Language */
+
+/* accept language */
+G_MODULE_EXPORT void
+nsgtk_preferences_comboboxLanguage_changed(GtkComboBox *combo,
+ struct ppref *priv)
+{
+ gchar *lang = NULL;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ /* Obtain currently selected item from combo box.
+ * If nothing is selected, do nothing.
+ */
+ if (gtk_combo_box_get_active_iter(combo, &iter)) {
+ /* Obtain data model from combo box. */
+ model = gtk_combo_box_get_model(combo);
+
+ /* Obtain string from model. */
+ gtk_tree_model_get(model, &iter, 0, &lang, -1);
+ }
+
+ if (lang != NULL) {
+ nsoption_set_charp(accept_language, strdup(lang));
+ g_free(lang);
+ }
+}
+
+/**
+ * populate language combo from data
+ */
+static nserror
+comboboxLanguage_add_from_data(GtkListStore *liststore,
+ GtkComboBox *combobox,
+ const char *accept_language,
+ const uint8_t *data,
+ size_t data_size)
+{
+ int active_language = -1;
+ GtkTreeIter iter;
+ int combo_row_count = 0;
+ const uint8_t *s;
+ const uint8_t *nl;
+ char buf[50];
+ int bufi = 0;
+
+ gtk_list_store_clear(liststore);
+ s = data;
+
+ while (s < (data + data_size)) {
+ /* find nl and copy buffer */
+ for (nl = s; nl < data + data_size; nl++) {
+ if ((*nl == '\n') || (bufi == (sizeof(buf) - 2))) {
+ buf[bufi] = 0; /* null terminate */
+ break;
+ }
+ buf[bufi++] = *nl;
+ }
+ if (bufi > 0) {
+ gtk_list_store_append(liststore, &iter);
+ gtk_list_store_set(liststore, &iter, 0, buf, -1 );
+
+ if (strcmp(buf, accept_language) == 0) {
+ active_language = combo_row_count;
+ }
+
+ combo_row_count++;
+ }
+ bufi = 0;
+ s = nl + 1; /* skip newline */
+ }
+
+ /* if configured language was not in list, add it */
+ if (active_language == -1) {
+ gtk_list_store_append(liststore, &iter);
+ gtk_list_store_set(liststore, &iter, 0, accept_language, -1);
+ active_language = combo_row_count;
+ }
+
+ gtk_combo_box_set_active(combobox, active_language);
+
+ return NSERROR_OK;
+}
+
+/**
+ * populate language combo from file
+ */
+static nserror
+comboboxLanguage_add_from_file(GtkListStore *liststore,
+ GtkComboBox *combobox,
+ const char *accept_language,
+ const char *file_location)
+{
+ int active_language = -1;
+ GtkTreeIter iter;
+ int combo_row_count = 0;
+ FILE *fp;
+ char buf[50];
+
+ fp = fopen(file_location, "r");
+ if (fp == NULL) {
+ return NSERROR_NOT_FOUND;
+ }
+
+ gtk_list_store_clear(liststore);
+
+ LOG("Used %s for languages", file_location);
+ while (fgets(buf, sizeof(buf), fp)) {
+ /* Ignore blank lines */
+ if (buf[0] == '\0')
+ continue;
+
+ /* Remove trailing \n */
+ buf[strlen(buf) - 1] = '\0';
+
+ gtk_list_store_append(liststore, &iter);
+ gtk_list_store_set(liststore, &iter, 0, buf, -1 );
+
+ if (strcmp(buf, accept_language) == 0) {
+ active_language = combo_row_count;
+ }
+
+ combo_row_count++;
+ }
+
+ /* if configured language was not in list, add it */
+ if (active_language == -1) {
+ gtk_list_store_append(liststore, &iter);
+ gtk_list_store_set(liststore, &iter, 0, accept_language, -1);
+ active_language = combo_row_count;
+ }
+
+ fclose(fp);
+
+ gtk_combo_box_set_active(combobox, active_language);
+
+ return NSERROR_OK;
+}
+
+/**
+ * Fill content language list store.
+ */
+G_MODULE_EXPORT void
+nsgtk_preferences_comboboxLanguage_realize(GtkWidget *widget,
+ struct ppref *priv)
+{
+ nserror res;
+ const uint8_t *data;
+ size_t data_size;
+ const char *languages_file;
+ const char *accept_language;
+
+ if (priv->content_language == NULL) {
+ LOG("content language list store unavailable");
+ return;
+ }
+
+ /* get current accept language */
+ accept_language = nsoption_charp(accept_language);
+ if (accept_language == NULL) {
+ accept_language = "en";
+ }
+
+ /* attempt to read languages from inline resource */
+ res = nsgtk_data_from_resname("languages", &data, &data_size);
+ if (res == NSERROR_OK) {
+ res = comboboxLanguage_add_from_data(priv->content_language,
+ GTK_COMBO_BOX(widget),
+ accept_language,
+ data,
+ data_size);
+ } else {
+ /* attempt to read languages from file */
+ res = nsgtk_path_from_resname("languages", &languages_file);
+ if (res == NSERROR_OK) {
+ res = comboboxLanguage_add_from_file(priv->content_language,
+ GTK_COMBO_BOX(widget),
+ accept_language,
+ languages_file);
+ }
+ }
+ if (res != NSERROR_OK) {
+ LOG("error populatiung languages combo");
+ }
+}
+
+
+/********* Apperance **********/
+
+/* Tabs */
+
+/* always show tab bar */
+G_MODULE_EXPORT void
+nsgtk_preferences_checkShowSingleTab_toggled(GtkToggleButton *togglebutton,
+ struct ppref *priv)
+{
+ nsoption_set_bool(show_single_tab,
+ gtk_toggle_button_get_active(togglebutton));
+ nsgtk_reflow_all_windows();
+}
+
+G_MODULE_EXPORT void
+nsgtk_preferences_checkShowSingleTab_realize(GtkWidget *widget,
+ struct ppref *priv)
+{
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
+ nsoption_bool(show_single_tab));
+}
+
+/* switch to newly opened tabs immediately */
+TOGGLEBUTTON_SIGNALS(checkFocusNew, focus_new)
+
+/* newly opened tabs are blank */
+TOGGLEBUTTON_SIGNALS(checkNewBlank, new_blank)
+
+/* tab position */
+G_MODULE_EXPORT void
+nsgtk_preferences_comboTabPosition_changed(GtkComboBox *widget,
+ struct ppref *priv)
+{
+ struct nsgtk_scaffolding *current;
+
+ /* set the option */
+ nsoption_set_int(position_tab, gtk_combo_box_get_active(widget));
+
+ /* update all notebooks in all scaffolds */
+ current = nsgtk_scaffolding_iterate(NULL);
+ while (current) {
+ nsgtk_scaffolding_reset_offset(current);
+
+ nsgtk_reflow_all_windows();
+
+ current = nsgtk_scaffolding_iterate(current);
+ }
+}
+
+G_MODULE_EXPORT void
+nsgtk_preferences_comboTabPosition_realize(GtkWidget *widget,
+ struct ppref *priv)
+{
+ gtk_combo_box_set_active(GTK_COMBO_BOX(widget),
+ nsoption_int(position_tab));
+}
+
+/* Tools */
+
+/* developer view opening */
+G_MODULE_EXPORT void
+nsgtk_preferences_comboDeveloperView_changed(GtkComboBox *widget,
+ struct ppref *priv)
+{
+ /* set the option */
+ nsoption_set_int(developer_view, gtk_combo_box_get_active(widget));
+}
+
+G_MODULE_EXPORT void
+nsgtk_preferences_comboDeveloperView_realize(GtkWidget *widget,
+ struct ppref *priv)
+{
+ gtk_combo_box_set_active(GTK_COMBO_BOX(widget),
+ nsoption_int(developer_view));
+}
+
+
+/* URLbar */
+
+/* show recently visited urls as you type */
+TOGGLEBUTTON_SIGNALS(checkDisplayRecentURLs, url_suggestion)
+
+/* Toolbar */
+
+/* button position */
+G_MODULE_EXPORT void
+nsgtk_preferences_comboButtonType_changed(GtkComboBox *widget,
+ struct ppref *priv)
+{
+ struct nsgtk_scaffolding *current;
+
+ nsoption_set_int(button_type, gtk_combo_box_get_active(widget) + 1);
+
+ current = nsgtk_scaffolding_iterate(NULL);
+ while (current != NULL) {
+ nsgtk_scaffolding_reset_offset(current);
+
+ nsgtk_scaffolding_toolbars(current, nsoption_int(button_type));
+
+ current = nsgtk_scaffolding_iterate(current);
+ }
+}
+
+G_MODULE_EXPORT void
+nsgtk_preferences_comboButtonType_realize(GtkWidget *widget,
+ struct ppref *priv)
+{
+ gtk_combo_box_set_active(GTK_COMBO_BOX(widget),
+ nsoption_int(button_type) - 1);
+}
+
+
+
+/************ Main ************/
+
+/* Startup */
+
+/* entry HomePageURL widget */
+ENTRY_SIGNALS(entryHomePageURL, homepage_url)
+
+/* put current page into homepage url */
+G_MODULE_EXPORT void
+nsgtk_preferences_setCurrentPage_clicked(GtkButton *button, struct ppref *priv)
+{
+ const gchar *url = nsurl_access(browser_window_get_url(priv->bw));
+
+ if (priv->entryHomePageURL != NULL) {
+ gtk_entry_set_text(GTK_ENTRY(priv->entryHomePageURL), url);
+ nsoption_set_charp(homepage_url, strdup(url));
+ }
+}
+
+/* put default page into homepage */
+G_MODULE_EXPORT void
+nsgtk_preferences_setDefaultPage_clicked(GtkButton *button, struct ppref *priv)
+{
+ const gchar *url = NETSURF_HOMEPAGE;
+
+ if (priv->entryHomePageURL != NULL) {
+ gtk_entry_set_text(GTK_ENTRY(priv->entryHomePageURL), url);
+ nsoption_set_charp(homepage_url, strdup(url));
+ }
+}
+
+/* Search */
+
+/* Url Search widget */
+TOGGLEBUTTON_SIGNALS(checkUrlSearch, search_url_bar)
+
+/* provider combo */
+G_MODULE_EXPORT void
+nsgtk_preferences_comboSearch_changed(GtkComboBox *widget, struct ppref *priv)
+{
+ int provider;
+
+ provider = gtk_combo_box_get_active(widget);
+
+ /* set the option */
+ nsoption_set_int(search_provider, provider);
+
+ /* set search provider */
+ search_web_select_provider(provider);
+}
+
+G_MODULE_EXPORT void
+nsgtk_preferences_comboSearch_realize(GtkWidget *widget, struct ppref *priv)
+{
+ int iter;
+ const char *name;
+ int provider = nsoption_int(search_provider);
+
+ if (priv->search_providers != NULL) {
+ gtk_list_store_clear(priv->search_providers);
+ for (iter = search_web_iterate_providers(0, &name);
+ iter != -1;
+ iter = search_web_iterate_providers(iter, &name)) {
+ gtk_list_store_insert_with_values(priv->search_providers,
+ NULL, -1,
+ 0, name, -1);
+ }
+ }
+
+ gtk_combo_box_set_active(GTK_COMBO_BOX(widget), provider);
+}
+
+
+/* Downloads */
+
+/* clear downloads */
+TOGGLEBUTTON_SIGNALS(checkClearDownloads, downloads_clear)
+
+/* request overwite */
+TOGGLEBUTTON_SIGNALS(checkRequestOverwrite, request_overwrite)
+
+/* download location
+ *
+ * note selection-changed is used instead of file-set as the returned
+ * filename when that signal are used is incorrect. Though this signal
+ * does update frequently often with the same data.
+ */
+G_MODULE_EXPORT void
+nsgtk_preferences_fileChooserDownloads_selectionchanged(GtkFileChooser *chooser,
+ struct ppref *priv)
+{
+ gchar *dir;
+ dir = gtk_file_chooser_get_filename(chooser);
+ nsoption_set_charp(downloads_directory, strdup(dir));
+ g_free(dir);
+}
+
+G_MODULE_EXPORT void
+nsgtk_preferences_fileChooserDownloads_realize(GtkWidget *widget,
+ struct ppref *priv)
+{
+ gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(widget),
+ nsoption_charp(downloads_directory));
+}
+
+
+/************* Dialog window ***********/
+
+/* dialog close and destroy events */
+G_MODULE_EXPORT void
+nsgtk_preferences_dialogPreferences_response(GtkDialog *dlg, gint resid)
+{
+ char *choices = NULL;
+
+ if (resid == GTK_RESPONSE_CLOSE) {
+ netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices");
+ if (choices != NULL) {
+ nsoption_write(choices, NULL, NULL);
+ free(choices);
+ }
+ gtk_widget_hide(GTK_WIDGET(dlg));
+ }
+}
+
+G_MODULE_EXPORT gboolean
+nsgtk_preferences_dialogPreferences_deleteevent(GtkDialog *dlg,
+ struct ppref *priv)
+{
+ char *choices = NULL;
+
+ netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices");
+ if (choices != NULL) {
+ nsoption_write(choices, NULL, NULL);
+ free(choices);
+ }
+
+ gtk_widget_hide(GTK_WIDGET(dlg));
+
+ /* Delt with it by hiding window, no need to destory widget by
+ * default.
+ */
+ return TRUE;
+}
+
+G_MODULE_EXPORT void
+nsgtk_preferences_dialogPreferences_destroy(GtkDialog *dlg, struct ppref *priv)
+{
+ char *choices = NULL;
+
+ netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices");
+ if (choices != NULL) {
+ nsoption_write(choices, NULL, NULL);
+ free(choices);
+ }
+}
+
+
+/* exported interface documented in gtk/preferences.h */
+GtkWidget* nsgtk_preferences(struct browser_window *bw, GtkWindow *parent)
+{
+ GtkBuilder *preferences_builder;
+ struct ppref *priv = &ppref;
+ nserror res;
+
+ priv->bw = bw; /* for setting "current" page */
+
+ /* memoised dialog creation */
+ if (priv->dialog != NULL) {
+ gtk_window_set_transient_for(GTK_WINDOW(priv->dialog), parent);
+ return GTK_WIDGET(priv->dialog);
+ }
+
+ res = nsgtk_builder_new_from_resname("options", &preferences_builder);
+ if (res != NSERROR_OK) {
+ LOG("Preferences UI builder init failed");
+ return NULL;
+ }
+
+ priv->dialog = gtk_builder_get_object(preferences_builder,
+ "dialogPreferences");
+ if (priv->dialog == NULL) {
+ LOG("Unable to get object for preferences dialog");
+ /* release builder as were done with it */
+ g_object_unref(G_OBJECT(preferences_builder));
+ return NULL;
+ }
+
+ /* need to explicitly obtain handles for some widgets enabling
+ * updates by other widget events
+ */
+#define GB(TYPE, NAME) GTK_##TYPE(gtk_builder_get_object(preferences_builder, #NAME))
+ priv->entryHomePageURL = GB(ENTRY, entryHomePageURL);
+ priv->content_language = GB(LIST_STORE, liststore_content_language);
+ priv->search_providers = GB(LIST_STORE, liststore_search_provider);
+ priv->entryProxyHost = GB(ENTRY, entryProxyHost);
+ priv->spinProxyPort = GB(SPIN_BUTTON, spinProxyPort);
+ priv->entryProxyUser = GB(ENTRY, entryProxyUser);
+ priv->entryProxyPassword = GB(ENTRY, entryProxyPassword);
+ priv->entryProxyNoproxy = GB(ENTRY, entryProxyNoproxy);
+#undef GB
+
+ /* connect all signals ready to use */
+ gtk_builder_connect_signals(preferences_builder, priv);
+
+ /* release builder as were done with it */
+ g_object_unref(G_OBJECT(preferences_builder));
+
+ /* mark dialog as transient on parent */
+ gtk_window_set_transient_for(GTK_WINDOW(priv->dialog), parent);
+
+ return GTK_WIDGET(priv->dialog);
+}
+
diff --git a/frontends/gtk/preferences.h b/frontends/gtk/preferences.h
new file mode 100644
index 000000000..9fe469e24
--- /dev/null
+++ b/frontends/gtk/preferences.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_GTK_PREFERENCES_H
+#define NETSURF_GTK_PREFERENCES_H
+
+#include <gtk/gtk.h>
+
+/** Initialise prefernces window
+ */
+GtkWidget* nsgtk_preferences(struct browser_window *bw, GtkWindow *parent);
+
+#endif
diff --git a/frontends/gtk/print.c b/frontends/gtk/print.c
new file mode 100644
index 000000000..a6e639996
--- /dev/null
+++ b/frontends/gtk/print.c
@@ -0,0 +1,613 @@
+/*
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2008 Adam Blokus <adamblokus@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+ /** \file
+ * GTK printing (implementation).
+ * All the functions and structures necessary for printing( signal handlers,
+ * plotters, printer) are here.
+ * Most of the plotters have been copied from the gtk_plotters.c file.
+ */
+
+#include "utils/config.h"
+
+#include <math.h>
+#include <assert.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "content/content.h"
+#include "content/hlcache.h"
+#include "utils/nsoption.h"
+#include "desktop/plotters.h"
+#include "desktop/print.h"
+#include "desktop/printer.h"
+
+#include "gtk/layout_pango.h"
+#include "gtk/bitmap.h"
+#include "gtk/print.h"
+#include "gtk/scaffolding.h"
+
+/* Globals */
+cairo_t *gtk_print_current_cr;
+static struct print_settings* settings;
+hlcache_handle *content_to_print;
+static GdkRectangle cliprect;
+
+static inline void nsgtk_print_set_colour(colour c)
+{
+ int r, g, b;
+
+ r = c & 0xff;
+ g = (c & 0xff00) >> 8;
+ b = (c & 0xff0000) >> 16;
+
+#ifdef FIXME
+ GdkColor colour;
+ colour.red = r | (r << 8);
+ colour.green = g | (g << 8);
+ colour.blue = b | (b << 8);
+ colour.pixel = (r << 16) | (g << 8) | b;
+ gdk_colormap_alloc_color(gdk_colormap_get_system(), &colour, true, true);
+#endif
+ cairo_set_source_rgba(gtk_print_current_cr, r / 255.0,
+ g / 255.0, b / 255.0, 1.0);
+}
+
+
+
+static bool gtk_print_font_paint(int x, int y,
+ const char *string, size_t length,
+ const plot_font_style_t *fstyle)
+{
+ PangoFontDescription *desc;
+ PangoLayout *layout;
+ gint size;
+ PangoLayoutLine *line;
+
+ if (length == 0)
+ return true;
+
+ desc = nsfont_style_to_description(fstyle);
+ size = (gint) ((double) pango_font_description_get_size(desc) *
+ settings->scale);
+
+ if (pango_font_description_get_size_is_absolute(desc))
+ pango_font_description_set_absolute_size(desc, size);
+ else
+ pango_font_description_set_size(desc, size);
+
+ layout = pango_cairo_create_layout(gtk_print_current_cr);
+
+ pango_layout_set_font_description(layout, desc);
+ pango_layout_set_text(layout, string, length);
+
+ line = pango_layout_get_line(layout, 0);
+
+ cairo_move_to(gtk_print_current_cr, x, y);
+ nsgtk_print_set_colour(fstyle->foreground);
+ pango_cairo_show_layout_line(gtk_print_current_cr, line);
+
+ g_object_unref(layout);
+ pango_font_description_free(desc);
+
+ return true;
+}
+
+
+/** Set cairo context to solid plot operation. */
+static inline void nsgtk_print_set_solid(void)
+{
+ double dashes = 0;
+ cairo_set_dash(gtk_print_current_cr, &dashes, 0, 0);
+}
+
+/** Set cairo context to dotted plot operation. */
+static inline void nsgtk_print_set_dotted(void)
+{
+ double cdashes[] = { 1.0, 2.0 };
+ cairo_set_dash(gtk_print_current_cr, cdashes, 1, 0);
+}
+
+/** Set cairo context to dashed plot operation. */
+static inline void nsgtk_print_set_dashed(void)
+{
+ double cdashes[] = { 8.0, 2.0 };
+ cairo_set_dash(gtk_print_current_cr, cdashes, 1, 0);
+}
+
+/** Set clipping area for subsequent plot operations. */
+static bool nsgtk_print_plot_clip(const struct rect *clip)
+{
+ LOG("Clipping. x0: %i ;\t y0: %i ;\t x1: %i ;\t y1: %i", clip->x0, clip->y0, clip->x1, clip->y1);
+
+ /* Normalize cllipping area - to prevent overflows.
+ * See comment in pdf_plot_fill. */
+ int clip_x0 = min(max(clip->x0, 0), settings->page_width);
+ int clip_y0 = min(max(clip->y0, 0), settings->page_height);
+ int clip_x1 = min(max(clip->x1, 0), settings->page_width);
+ int clip_y1 = min(max(clip->y1, 0), settings->page_height);
+
+ cairo_reset_clip(gtk_print_current_cr);
+ cairo_rectangle(gtk_print_current_cr, clip_x0, clip_y0,
+ clip_x1 - clip_x0, clip_y1 - clip_y0);
+ cairo_clip(gtk_print_current_cr);
+
+ cliprect.x = clip_x0;
+ cliprect.y = clip_y0;
+ cliprect.width = clip_x1 - clip_x0;
+ cliprect.height = clip_y1 - clip_y0;
+
+ return true;
+}
+
+static bool nsgtk_print_plot_arc(int x, int y, int radius, int angle1, int angle2, const plot_style_t *style)
+{
+ nsgtk_print_set_colour(style->fill_colour);
+ nsgtk_print_set_solid();
+
+ cairo_set_line_width(gtk_print_current_cr, 1);
+ cairo_arc(gtk_print_current_cr, x, y, radius,
+ (angle1 + 90) * (M_PI / 180),
+ (angle2 + 90) * (M_PI / 180));
+ cairo_stroke(gtk_print_current_cr);
+
+ return true;
+}
+
+static bool nsgtk_print_plot_disc(int x, int y, int radius, const plot_style_t *style)
+{
+ if (style->fill_type != PLOT_OP_TYPE_NONE) {
+ nsgtk_print_set_colour(style->fill_colour);
+ nsgtk_print_set_solid();
+ cairo_set_line_width(gtk_print_current_cr, 0);
+ cairo_arc(gtk_print_current_cr, x, y, radius, 0, M_PI * 2);
+ cairo_fill(gtk_print_current_cr);
+ cairo_stroke(gtk_print_current_cr);
+ }
+
+ if (style->stroke_type != PLOT_OP_TYPE_NONE) {
+ nsgtk_print_set_colour(style->stroke_colour);
+
+ switch (style->stroke_type) {
+ case PLOT_OP_TYPE_SOLID: /**< Solid colour */
+ default:
+ nsgtk_print_set_solid();
+ break;
+
+ case PLOT_OP_TYPE_DOT: /**< Doted plot */
+ nsgtk_print_set_dotted();
+ break;
+
+ case PLOT_OP_TYPE_DASH: /**< dashed plot */
+ nsgtk_print_set_dashed();
+ break;
+ }
+
+ if (style->stroke_width == 0)
+ cairo_set_line_width(gtk_print_current_cr, 1);
+ else
+ cairo_set_line_width(gtk_print_current_cr, style->stroke_width);
+
+ cairo_arc(gtk_print_current_cr, x, y, radius, 0, M_PI * 2);
+
+ cairo_stroke(gtk_print_current_cr);
+ }
+ return true;
+}
+
+static bool nsgtk_print_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ nsgtk_print_set_colour(style->stroke_colour);
+
+ switch (style->stroke_type) {
+ case PLOT_OP_TYPE_SOLID: /**< Solid colour */
+ default:
+ nsgtk_print_set_solid();
+ break;
+
+ case PLOT_OP_TYPE_DOT: /**< Doted plot */
+ nsgtk_print_set_dotted();
+ break;
+
+ case PLOT_OP_TYPE_DASH: /**< dashed plot */
+ nsgtk_print_set_dashed();
+ break;
+ }
+
+ if (style->stroke_width == 0)
+ cairo_set_line_width(gtk_print_current_cr, 1);
+ else
+ cairo_set_line_width(gtk_print_current_cr, style->stroke_width);
+
+ cairo_move_to(gtk_print_current_cr, x0 + 0.5, y0 + 0.5);
+ cairo_line_to(gtk_print_current_cr, x1 + 0.5, y1 + 0.5);
+ cairo_stroke(gtk_print_current_cr);
+
+ return true;
+}
+
+static bool nsgtk_print_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ LOG("x0: %i ;\t y0: %i ;\t x1: %i ;\t y1: %i", x0, y0, x1, y1);
+
+ if (style->fill_type != PLOT_OP_TYPE_NONE) {
+
+ nsgtk_print_set_colour(style->fill_colour);
+ nsgtk_print_set_solid();
+
+ /* Normalize boundaries of the area - to prevent overflows.
+ * See comment in pdf_plot_fill. */
+ x0 = min(max(x0, 0), settings->page_width);
+ y0 = min(max(y0, 0), settings->page_height);
+ x1 = min(max(x1, 0), settings->page_width);
+ y1 = min(max(y1, 0), settings->page_height);
+
+ cairo_set_line_width(gtk_print_current_cr, 0);
+ cairo_rectangle(gtk_print_current_cr, x0, y0, x1 - x0, y1 - y0);
+ cairo_fill(gtk_print_current_cr);
+ cairo_stroke(gtk_print_current_cr);
+ }
+
+ if (style->stroke_type != PLOT_OP_TYPE_NONE) {
+ nsgtk_print_set_colour(style->stroke_colour);
+
+ switch (style->stroke_type) {
+ case PLOT_OP_TYPE_SOLID: /**< Solid colour */
+ default:
+ nsgtk_print_set_solid();
+ break;
+
+ case PLOT_OP_TYPE_DOT: /**< Doted plot */
+ nsgtk_print_set_dotted();
+ break;
+
+ case PLOT_OP_TYPE_DASH: /**< dashed plot */
+ nsgtk_print_set_dashed();
+ break;
+ }
+
+ if (style->stroke_width == 0)
+ cairo_set_line_width(gtk_print_current_cr, 1);
+ else
+ cairo_set_line_width(gtk_print_current_cr, style->stroke_width);
+
+ cairo_rectangle(gtk_print_current_cr, x0, y0, x1 - x0, y1 - y0);
+ cairo_stroke(gtk_print_current_cr);
+ }
+
+ return true;
+}
+
+static bool nsgtk_print_plot_polygon(const int *p, unsigned int n, const plot_style_t *style)
+{
+ unsigned int i;
+
+ LOG("Plotting polygon.");
+
+ nsgtk_print_set_colour(style->fill_colour);
+ nsgtk_print_set_solid();
+
+ cairo_set_line_width(gtk_print_current_cr, 0);
+ cairo_move_to(gtk_print_current_cr, p[0], p[1]);
+
+ LOG("Starting line at: %i\t%i", p[0], p[1]);
+
+ for (i = 1; i != n; i++) {
+ cairo_line_to(gtk_print_current_cr, p[i * 2], p[i * 2 + 1]);
+ LOG("Drawing line to: %i\t%i", p[i * 2], p[i * 2 + 1]);
+ }
+
+ cairo_fill(gtk_print_current_cr);
+ cairo_stroke(gtk_print_current_cr);
+
+ return true;
+}
+
+
+static bool nsgtk_print_plot_path(const float *p, unsigned int n, colour fill,
+ float width, colour c, const float transform[6])
+{
+ /* Only the internal SVG renderer uses this plot call currently,
+ * and the GTK version uses librsvg. Thus, we ignore this complexity,
+ * and just return true obliviously. */
+
+ return true;
+}
+
+
+static bool nsgtk_print_plot_pixbuf(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg)
+{
+ int x0, y0, x1, y1;
+ int dsrcx, dsrcy, dwidth, dheight;
+ int bmwidth, bmheight;
+
+ cairo_surface_t *bmsurface = bitmap->surface;
+
+ /* Bail early if we can */
+ if (width == 0 || height == 0)
+ /* Nothing to plot */
+ return true;
+ if ((x > (cliprect.x + cliprect.width)) ||
+ ((x + width) < cliprect.x) ||
+ (y > (cliprect.y + cliprect.height)) ||
+ ((y + height) < cliprect.y)) {
+ /* Image completely outside clip region */
+ return true;
+ }
+
+ /* Get clip rectangle / image rectangle edge differences */
+ x0 = cliprect.x - x;
+ y0 = cliprect.y - y;
+ x1 = (x + width) - (cliprect.x + cliprect.width);
+ y1 = (y + height) - (cliprect.y + cliprect.height);
+
+ /* Set initial draw geometry */
+ dsrcx = x;
+ dsrcy = y;
+ dwidth = width;
+ dheight = height;
+
+ /* Manually clip draw coordinates to area of image to be rendered */
+ if (x0 > 0) {
+ /* Clip left */
+ dsrcx += x0;
+ dwidth -= x0;
+ }
+ if (y0 > 0) {
+ /* Clip top */
+ dsrcy += y0;
+ dheight -= y0;
+ }
+ if (x1 > 0) {
+ /* Clip right */
+ dwidth -= x1;
+ }
+ if (y1 > 0) {
+ /* Clip bottom */
+ dheight -= y1;
+ }
+
+ if (dwidth == 0 || dheight == 0)
+ /* Nothing to plot */
+ return true;
+
+ bmwidth = cairo_image_surface_get_width(bmsurface);
+ bmheight = cairo_image_surface_get_height(bmsurface);
+
+ /* Render the bitmap */
+ if ((bmwidth == width) && (bmheight == height)) {
+ /* Bitmap is not scaled */
+ /* Plot the bitmap */
+ cairo_set_source_surface(gtk_print_current_cr, bmsurface, x, y);
+ cairo_rectangle(gtk_print_current_cr, dsrcx, dsrcy, dwidth, dheight);
+ cairo_fill(gtk_print_current_cr);
+
+ } else {
+ /* Bitmap is scaled */
+ if ((bitmap->scsurface != NULL) &&
+ ((cairo_image_surface_get_width(bitmap->scsurface) != width) ||
+ (cairo_image_surface_get_height(bitmap->scsurface) != height))){
+ cairo_surface_destroy(bitmap->scsurface);
+ bitmap->scsurface = NULL;
+ }
+
+ if (bitmap->scsurface == NULL) {
+ bitmap->scsurface = cairo_surface_create_similar(bmsurface,CAIRO_CONTENT_COLOR_ALPHA, width, height);
+ cairo_t *cr = cairo_create(bitmap->scsurface);
+
+ /* Scale *before* setting the source surface (1) */
+ cairo_scale(cr, (double)width / bmwidth, (double)height / bmheight);
+ cairo_set_source_surface(cr, bmsurface, 0, 0);
+
+ /* To avoid getting the edge pixels blended with 0
+ * alpha, which would occur with the default
+ * EXTEND_NONE. Use EXTEND_PAD for 1.2 or newer (2)
+ */
+ cairo_pattern_set_extend(cairo_get_source(cr),
+ CAIRO_EXTEND_REFLECT);
+
+ /* Replace the destination with the source instead of
+ * overlaying
+ */
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+
+ /* Do the actual drawing */
+ cairo_paint(cr);
+
+ cairo_destroy(cr);
+
+ }
+ /* Plot the scaled bitmap */
+ cairo_set_source_surface(gtk_print_current_cr, bitmap->scsurface, x, y);
+ cairo_rectangle(gtk_print_current_cr, dsrcx, dsrcy, dwidth, dheight);
+ cairo_fill(gtk_print_current_cr);
+
+ }
+
+ return true;
+}
+
+
+static bool nsgtk_print_plot_bitmap(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg,
+ bitmap_flags_t flags)
+{
+ int doneheight = 0, donewidth = 0;
+ bool repeat_x = (flags & BITMAPF_REPEAT_X);
+ bool repeat_y = (flags & BITMAPF_REPEAT_Y);
+
+ if (!(repeat_x || repeat_y)) {
+ /* Not repeating at all, so just pass it on */
+ return nsgtk_print_plot_pixbuf(x, y, width, height, bitmap, bg);
+ }
+
+ width = nsgtk_bitmap_get_width(bitmap);
+ height = nsgtk_bitmap_get_height(bitmap);
+
+ /* Bail early if we can */
+ if (width == 0 || height == 0)
+ /* Nothing to plot */
+ return true;
+
+ if (y > cliprect.y) {
+ doneheight = (cliprect.y - height) + ((y - cliprect.y) % height);
+ } else {
+ doneheight = y;
+ }
+
+ while (doneheight < (cliprect.y + cliprect.height)) {
+ if (x > cliprect.x) {
+ donewidth = (cliprect.x - width) + ((x - cliprect.x) % width);
+ } else {
+ donewidth = x;
+ }
+
+ while (donewidth < (cliprect.x + cliprect.width)) {
+ nsgtk_print_plot_pixbuf(donewidth, doneheight,
+ width, height, bitmap, bg);
+ donewidth += width;
+ if (!repeat_x)
+ break;
+ }
+ doneheight += height;
+
+ if (!repeat_y)
+ break;
+ }
+
+ return true;
+}
+
+static bool nsgtk_print_plot_text(int x, int y, const char *text, size_t length,
+ const plot_font_style_t *fstyle)
+{
+ return gtk_print_font_paint(x, y, text, length, fstyle);
+}
+
+/** GTK print plotter table */
+static const struct plotter_table nsgtk_print_plotters = {
+ .clip = nsgtk_print_plot_clip,
+ .arc = nsgtk_print_plot_arc,
+ .disc = nsgtk_print_plot_disc,
+ .line = nsgtk_print_plot_line,
+ .rectangle = nsgtk_print_plot_rectangle,
+ .polygon = nsgtk_print_plot_polygon,
+ .path = nsgtk_print_plot_path,
+ .bitmap = nsgtk_print_plot_bitmap,
+ .text = nsgtk_print_plot_text,
+ .option_knockout = false,
+};
+
+static bool gtk_print_begin(struct print_settings* settings)
+{
+ return true;
+}
+
+static bool gtk_print_next_page(void)
+{
+ return true;
+}
+
+static void gtk_print_end(void)
+{
+}
+
+static const struct printer gtk_printer = {
+ &nsgtk_print_plotters,
+ gtk_print_begin,
+ gtk_print_next_page,
+ gtk_print_end
+};
+
+/**
+ * Handle the begin_print signal from the GtkPrintOperation
+ *
+ * \param operation the operation which emited the signal
+ * \param context the print context used to set up the pages
+ * \param user_data nothing in here
+ */
+void gtk_print_signal_begin_print (GtkPrintOperation *operation,
+ GtkPrintContext *context, gpointer user_data)
+{
+ int page_number;
+ double height_on_page, height_to_print;
+
+ LOG("Begin print");
+
+ settings = user_data;
+
+ settings->margins[MARGINTOP] = 0;
+ settings->margins[MARGINLEFT] = 0;
+ settings->margins[MARGINBOTTOM] = 0;
+ settings->margins[MARGINRIGHT] = 0;
+ settings->page_width = gtk_print_context_get_width(context);
+ settings->page_height = gtk_print_context_get_height(context);
+ settings->scale = 0.7; /* at 0.7 the pages look the best */
+ settings->font_func = nsgtk_layout_table;
+
+ if (print_set_up(content_to_print, &gtk_printer,
+ settings, &height_to_print) == false) {
+ gtk_print_operation_cancel(operation);
+
+ } else {
+
+ LOG("page_width: %f ;page_height: %f; content height: %lf", settings->page_width, settings->page_height, height_to_print);
+
+ height_on_page = settings->page_height;
+ height_on_page = height_on_page -
+ FIXTOFLT(FSUB(settings->margins[MARGINTOP],
+ settings->margins[MARGINBOTTOM]));
+ height_to_print *= settings->scale;
+
+ page_number = height_to_print / height_on_page;
+
+ if (height_to_print - page_number * height_on_page > 0)
+ page_number += 1;
+
+ gtk_print_operation_set_n_pages(operation, page_number);
+ }
+}
+
+/**
+ * Handle the draw_page signal from the GtkPrintOperation.
+ * This function changes only the cairo context to print on.
+ */
+void gtk_print_signal_draw_page(GtkPrintOperation *operation,
+ GtkPrintContext *context, gint page_nr, gpointer user_data)
+{
+ LOG("Draw Page");
+ gtk_print_current_cr = gtk_print_context_get_cairo_context(context);
+ print_draw_next_page(&gtk_printer, settings);
+}
+
+/**
+ * Handle the end_print signal from the GtkPrintOperation.
+ * This functions calls only the print_cleanup function from the print interface
+ */
+void gtk_print_signal_end_print(GtkPrintOperation *operation,
+ GtkPrintContext *context, gpointer user_data)
+{
+ LOG("End print");
+ print_cleanup(content_to_print, &gtk_printer, user_data);
+}
+
+
diff --git a/frontends/gtk/print.h b/frontends/gtk/print.h
new file mode 100644
index 000000000..d44fad31f
--- /dev/null
+++ b/frontends/gtk/print.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2008 Adam Blokus <adamblokus@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+ /** \file
+ * GTK printing (interface).
+ */
+
+#ifndef NETSURF_GTK_PRINT_PLOTTERS_H
+#define NETSURF_GTK_PRINT_PLOTTERS_H
+
+
+#include <gtk/gtk.h>
+
+struct hlcache_handle;
+
+extern cairo_t *gtk_print_current_cr;
+
+extern struct hlcache_handle *content_to_print;
+
+
+/*handlers for signals from the GTK print operation*/
+void gtk_print_signal_begin_print(GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ gpointer user_data);
+
+void gtk_print_signal_draw_page(GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ gint page_nr,
+ gpointer user_data);
+
+void gtk_print_signal_end_print(GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ gpointer user_data);
+
+#endif
diff --git a/frontends/gtk/res/Messages b/frontends/gtk/res/Messages
new file mode 120000
index 000000000..75bfdf53d
--- /dev/null
+++ b/frontends/gtk/res/Messages
@@ -0,0 +1 @@
+en/Messages \ No newline at end of file
diff --git a/frontends/gtk/res/SearchEngines b/frontends/gtk/res/SearchEngines
new file mode 120000
index 000000000..3dc819c2a
--- /dev/null
+++ b/frontends/gtk/res/SearchEngines
@@ -0,0 +1 @@
+../../../resources/SearchEngines \ No newline at end of file
diff --git a/frontends/gtk/res/adblock.css b/frontends/gtk/res/adblock.css
new file mode 120000
index 000000000..ff2485622
--- /dev/null
+++ b/frontends/gtk/res/adblock.css
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/AdBlock,f79 \ No newline at end of file
diff --git a/frontends/gtk/res/arrow_down_8x32.png b/frontends/gtk/res/arrow_down_8x32.png
new file mode 100644
index 000000000..475b4ff61
--- /dev/null
+++ b/frontends/gtk/res/arrow_down_8x32.png
Binary files differ
diff --git a/frontends/gtk/res/ca-bundle.txt b/frontends/gtk/res/ca-bundle.txt
new file mode 120000
index 000000000..0b0e416ad
--- /dev/null
+++ b/frontends/gtk/res/ca-bundle.txt
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/ca-bundle \ No newline at end of file
diff --git a/frontends/gtk/res/cookies.gtk2.ui b/frontends/gtk/res/cookies.gtk2.ui
new file mode 100644
index 000000000..86f15c765
--- /dev/null
+++ b/frontends/gtk/res/cookies.gtk2.ui
@@ -0,0 +1,174 @@
+<?xml version="1.0"?>
+<interface>
+ <!-- interface-requires gtk+ 2.6 -->
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkWindow" id="wndCookies">
+ <property name="title" translatable="yes">Cookies - NetSurf</property>
+ <property name="window_position">mouse</property>
+ <property name="default_width">600</property>
+ <property name="default_height">500</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="border_width">2</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="menuitem2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu2">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="delete_selected">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Delete</property>
+ <property name="use_underline">True</property>
+ <accelerator key="Delete" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="delete_all">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">D_elete all</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="select_all">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Select all</property>
+ <property name="use_underline">True</property>
+ <accelerator key="A" signal="activate" modifiers="GDK_MOD1_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="clear_selection">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Clear selection</property>
+ <property name="use_underline">True</property>
+ <accelerator key="U" signal="activate" modifiers="GDK_MOD1_MASK"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu4">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="expand">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Expand</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu5">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="expand_all">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_domains">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Domains</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_cookies">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Cookies</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Collapse</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu6">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="collapse_all">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_domains">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Domains</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_cookies">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Cookies</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="cookiesScrolled">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkViewport" id="cookiesViewport">
+ <property name="visible">True</property>
+ <property name="resize_mode">queue</property>
+ <child>
+ <object class="GtkDrawingArea" id="cookiesDrawingArea">
+ <property name="visible">True</property>
+ <property name="app_paintable">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/cookies.gtk3.ui b/frontends/gtk/res/cookies.gtk3.ui
new file mode 100644
index 000000000..44dcb80b8
--- /dev/null
+++ b/frontends/gtk/res/cookies.gtk3.ui
@@ -0,0 +1,206 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkWindow" id="wndCookies">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">NetSurf Cookies</property>
+ <property name="window_position">mouse</property>
+ <property name="default_width">600</property>
+ <property name="default_height">500</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="menuitem2">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="delete_selected">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Delete</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="delete_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">D_elete all</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="select_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Select all</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="clear_selection">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Clear selection</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem3">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="expand">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Expand</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="expand_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_domains">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Domains</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_cookies">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Cookies</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Collapse</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="collapse_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_domains">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Domains</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_cookies">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Cookies</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="cookiesScrolled">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkViewport" id="cookiesViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkDrawingArea" id="cookiesDrawingArea">
+ <property name="visible">True</property>
+ <property name="app_paintable">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/credits.html b/frontends/gtk/res/credits.html
new file mode 120000
index 000000000..ca85d3d27
--- /dev/null
+++ b/frontends/gtk/res/credits.html
@@ -0,0 +1 @@
+en/credits.html \ No newline at end of file
diff --git a/frontends/gtk/res/de/welcome.html b/frontends/gtk/res/de/welcome.html
new file mode 120000
index 000000000..98a53b215
--- /dev/null
+++ b/frontends/gtk/res/de/welcome.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/de/welcome.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/default.css b/frontends/gtk/res/default.css
new file mode 120000
index 000000000..a8579eb7c
--- /dev/null
+++ b/frontends/gtk/res/default.css
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/CSS,f79 \ No newline at end of file
diff --git a/frontends/gtk/res/default.ico b/frontends/gtk/res/default.ico
new file mode 100644
index 000000000..1cb432828
--- /dev/null
+++ b/frontends/gtk/res/default.ico
Binary files differ
diff --git a/frontends/gtk/res/downloads.gtk2.ui b/frontends/gtk/res/downloads.gtk2.ui
new file mode 100644
index 000000000..1e71328a4
--- /dev/null
+++ b/frontends/gtk/res/downloads.gtk2.ui
@@ -0,0 +1,175 @@
+<?xml version="1.0"?>
+<interface>
+ <!-- interface-requires gtk+ 2.12 -->
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkWindow" id="wndDownloads">
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="title" translatable="yes">Downloads - NetSurf</property>
+ <property name="window_position">mouse</property>
+ <property name="default_width">500</property>
+ <property name="default_height">300</property>
+ <property name="destroy_with_parent">True</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkTreeView" id="treeDownloads">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="search_column">2</property>
+ <property name="show_expanders">False</property>
+ <property name="rubber_banding">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator1">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="top_padding">2</property>
+ <property name="bottom_padding">2</property>
+ <property name="left_padding">2</property>
+ <property name="right_padding">2</property>
+ <child>
+ <object class="GtkProgressBar" id="progressBar">
+ <property name="visible">True</property>
+ <property name="show_text">True</property>
+ <property name="text" translatable="yes">0% of 0 files</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHButtonBox" id="hbuttonbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="buttonPause">
+ <property name="label">gtk-media-pause</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonPlay">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-media-play</property>
+ <property name="icon-size">2</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Resume</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonCancel">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonClear">
+ <property name="label">gtk-clear</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/downloads.gtk3.ui b/frontends/gtk/res/downloads.gtk3.ui
new file mode 100644
index 000000000..1e71328a4
--- /dev/null
+++ b/frontends/gtk/res/downloads.gtk3.ui
@@ -0,0 +1,175 @@
+<?xml version="1.0"?>
+<interface>
+ <!-- interface-requires gtk+ 2.12 -->
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkWindow" id="wndDownloads">
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="title" translatable="yes">Downloads - NetSurf</property>
+ <property name="window_position">mouse</property>
+ <property name="default_width">500</property>
+ <property name="default_height">300</property>
+ <property name="destroy_with_parent">True</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkTreeView" id="treeDownloads">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="search_column">2</property>
+ <property name="show_expanders">False</property>
+ <property name="rubber_banding">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator1">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="top_padding">2</property>
+ <property name="bottom_padding">2</property>
+ <property name="left_padding">2</property>
+ <property name="right_padding">2</property>
+ <child>
+ <object class="GtkProgressBar" id="progressBar">
+ <property name="visible">True</property>
+ <property name="show_text">True</property>
+ <property name="text" translatable="yes">0% of 0 files</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHButtonBox" id="hbuttonbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="buttonPause">
+ <property name="label">gtk-media-pause</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonPlay">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-media-play</property>
+ <property name="icon-size">2</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Resume</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonCancel">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonClear">
+ <property name="label">gtk-clear</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/en/credits.html b/frontends/gtk/res/en/credits.html
new file mode 120000
index 000000000..252516fd7
--- /dev/null
+++ b/frontends/gtk/res/en/credits.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/en/credits.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/en/licence.html b/frontends/gtk/res/en/licence.html
new file mode 120000
index 000000000..79f73669b
--- /dev/null
+++ b/frontends/gtk/res/en/licence.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/en/licence.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/en/maps.html b/frontends/gtk/res/en/maps.html
new file mode 120000
index 000000000..bb3ffcbe7
--- /dev/null
+++ b/frontends/gtk/res/en/maps.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/en/maps.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/en/welcome.html b/frontends/gtk/res/en/welcome.html
new file mode 120000
index 000000000..601099223
--- /dev/null
+++ b/frontends/gtk/res/en/welcome.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/en/welcome.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/favicon.png b/frontends/gtk/res/favicon.png
new file mode 120000
index 000000000..5a8b3433c
--- /dev/null
+++ b/frontends/gtk/res/favicon.png
@@ -0,0 +1 @@
+../../../resources/favicon.png \ No newline at end of file
diff --git a/frontends/gtk/res/history.gtk2.ui b/frontends/gtk/res/history.gtk2.ui
new file mode 100644
index 000000000..2b89ecb4b
--- /dev/null
+++ b/frontends/gtk/res/history.gtk2.ui
@@ -0,0 +1,242 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkWindow" id="wndHistory">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">NetSurf Global History</property>
+ <property name="window_position">center</property>
+ <property name="default_width">600</property>
+ <property name="default_height">500</property>
+ <property name="type_hint">utility</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="menuitem1">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="export">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Export</property>
+ <property name="use_underline">True</property>
+ <accelerator key="E" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem2">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="delete_selected">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Delete</property>
+ <property name="use_underline">True</property>
+ <accelerator key="Delete" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="delete_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">D_elete all</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="select_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Select all</property>
+ <property name="use_underline">True</property>
+ <accelerator key="A" signal="activate" modifiers="GDK_MOD1_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="clear_selection">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Clear selection</property>
+ <property name="use_underline">True</property>
+ <accelerator key="U" signal="activate" modifiers="GDK_MOD1_MASK"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem3">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="expand">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Expand</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="expand_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_directories">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Directories</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_addresses">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Add_resses</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Collapse</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="collapse_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_directories">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Directories</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_addresses">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Add_resses</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="launch">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Launch</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="globalHistoryScrolled">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkViewport" id="globalHistoryViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="resize_mode">queue</property>
+ <child>
+ <object class="GtkDrawingArea" id="globalHistoryDrawingArea">
+ <property name="visible">True</property>
+ <property name="app_paintable">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/history.gtk3.ui b/frontends/gtk/res/history.gtk3.ui
new file mode 100644
index 000000000..7fa598f1e
--- /dev/null
+++ b/frontends/gtk/res/history.gtk3.ui
@@ -0,0 +1,238 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkWindow" id="wndHistory">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">NetSurf Global History</property>
+ <property name="window_position">center</property>
+ <property name="default_width">600</property>
+ <property name="default_height">500</property>
+ <property name="type_hint">utility</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="menuitem1">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="export">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Export</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem2">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="delete_selected">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Delete</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="delete_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">D_elete all</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="select_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Select all</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="clear_selection">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Clear selection</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem3">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="expand">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Expand</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="expand_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_directories">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Directories</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_addresses">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Add_resses</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Collapse</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="collapse_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_directories">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Directories</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_addresses">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Add_resses</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="launch">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Launch</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="globalHistoryScrolled">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkViewport" id="globalHistoryViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkDrawingArea" id="globalHistoryDrawingArea">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/hotlist.gtk2.ui b/frontends/gtk/res/hotlist.gtk2.ui
new file mode 100644
index 000000000..af0fd5696
--- /dev/null
+++ b/frontends/gtk/res/hotlist.gtk2.ui
@@ -0,0 +1,217 @@
+<?xml version="1.0"?>
+<interface>
+ <!-- interface-requires gtk+ 2.6 -->
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkWindow" id="wndHotlist">
+ <property name="title" translatable="yes">Bookmarks - NetSurf</property>
+ <property name="window_position">mouse</property>
+ <property name="default_width">600</property>
+ <property name="default_height">500</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="border_width">2</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="menuitem1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="export">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Export</property>
+ <property name="use_underline">True</property>
+ <accelerator key="E" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="new_folder">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">New _folder</property>
+ <property name="use_underline">True</property>
+ <accelerator key="M" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="new_entry">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">New _entry</property>
+ <property name="use_underline">True</property>
+ <accelerator key="N" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu2">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="edit_selected">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="delete_selected">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Delete</property>
+ <property name="use_underline">True</property>
+ <accelerator key="Delete" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="select_all">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Select all</property>
+ <property name="use_underline">True</property>
+ <accelerator key="A" signal="activate" modifiers="GDK_MOD1_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="clear_selection">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Clear selection</property>
+ <property name="use_underline">True</property>
+ <accelerator key="U" signal="activate" modifiers="GDK_MOD1_MASK"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu4">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="expand">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Expand</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu5">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="expand_all">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_directories">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Directories</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_addresses">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Add_resses</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Collapse</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu6">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="collapse_all">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_directories">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Directories</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_addresses">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Add_resses</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="launch">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Launch</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="hotlistScrolled">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkViewport" id="hotlistViewport">
+ <property name="visible">True</property>
+ <property name="resize_mode">queue</property>
+ <child>
+ <object class="GtkDrawingArea" id="hotlistDrawingArea">
+ <property name="visible">True</property>
+ <property name="app_paintable">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/hotlist.gtk3.ui b/frontends/gtk/res/hotlist.gtk3.ui
new file mode 100644
index 000000000..b0e075c4b
--- /dev/null
+++ b/frontends/gtk/res/hotlist.gtk3.ui
@@ -0,0 +1,255 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkWindow" id="wndHotlist">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">NetSurf Bookmarks</property>
+ <property name="window_position">mouse</property>
+ <property name="default_width">600</property>
+ <property name="default_height">500</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="menuitem1">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="export">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Export</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="new_folder">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">New _folder</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="new_entry">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">New _entry</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem2">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="edit_selected">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="delete_selected">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Delete</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="select_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Select all</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="clear_selection">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Clear selection</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem3">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="expand">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Expand</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="expand_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_directories">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Directories</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_addresses">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Add_resses</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Collapse</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="collapse_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_directories">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Directories</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_addresses">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Add_resses</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="launch">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Launch</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="hotlistScrolled">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkViewport" id="hotlistViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkDrawingArea" id="hotlistDrawingArea">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/icons b/frontends/gtk/res/icons
new file mode 120000
index 000000000..187efd6f9
--- /dev/null
+++ b/frontends/gtk/res/icons
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/Icons/ \ No newline at end of file
diff --git a/frontends/gtk/res/internal.css b/frontends/gtk/res/internal.css
new file mode 120000
index 000000000..17f9f1504
--- /dev/null
+++ b/frontends/gtk/res/internal.css
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/internal.css,f79 \ No newline at end of file
diff --git a/frontends/gtk/res/it/credits.html b/frontends/gtk/res/it/credits.html
new file mode 120000
index 000000000..64b78982e
--- /dev/null
+++ b/frontends/gtk/res/it/credits.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/it/credits.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/it/licence.html b/frontends/gtk/res/it/licence.html
new file mode 120000
index 000000000..4abc825d3
--- /dev/null
+++ b/frontends/gtk/res/it/licence.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/it/licence.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/it/welcome.html b/frontends/gtk/res/it/welcome.html
new file mode 120000
index 000000000..59cef0551
--- /dev/null
+++ b/frontends/gtk/res/it/welcome.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/it/welcome.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/ja/welcome.html b/frontends/gtk/res/ja/welcome.html
new file mode 120000
index 000000000..a2556ee4e
--- /dev/null
+++ b/frontends/gtk/res/ja/welcome.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/ja/welcome.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/languages b/frontends/gtk/res/languages
new file mode 100644
index 000000000..4927e03a0
--- /dev/null
+++ b/frontends/gtk/res/languages
@@ -0,0 +1,261 @@
+aa
+ab
+ae
+af
+ak
+am
+an
+ar
+ar-ae
+ar-bh
+ar-dz
+ar-eg
+ar-iq
+ar-jo
+ar-kw
+ar-lb
+ar-ly
+ar-ma
+ar-om
+ar-qa
+ar-sa
+ar-sy
+ar-tn
+ar-ye
+as
+ast
+av
+az
+ba
+be
+bg
+bh
+bi
+bm
+bn
+bo
+br
+bs
+ca
+ce
+ch
+co
+cr
+cs
+cu
+cv
+cy
+da
+de
+de-at
+de-ch
+de-de
+de-li
+de-lu
+dv
+dz
+ee
+el
+en
+en-au
+en-bz
+en-ca
+en-gb
+en-ie
+en-jm
+en-nz
+en-ph
+en-tt
+en-us
+en-za
+en-zw
+eo
+es
+es-ar
+es-bo
+es-cl
+es-co
+es-cr
+es-do
+es-ec
+es-es
+es-gt
+es-hn
+es-mx
+es-ni
+es-pa
+es-pe
+es-pr
+es-py
+es-sv
+es-uy
+es-ve
+et
+eu
+fa
+fa-ir
+ff
+fi
+fj
+fo
+fr
+fr-be
+fr-ca
+fr-ch
+fr-fr
+fr-lu
+fr-mc
+fur
+fy
+ga
+gd
+gl
+gn
+gu
+gv
+ha
+he
+hi
+ho
+hsb
+hr
+ht
+hu
+hy
+hz
+ia
+id
+ie
+ig
+ii
+ik
+io
+is
+it
+it-ch
+iu
+ja
+jv
+ka
+kg
+ki
+kk
+kl
+km
+kn
+ko
+ko-kp
+ko-kr
+kok
+kr
+ks
+ku
+kv
+kw
+ky
+la
+lb
+lg
+li
+ln
+lo
+lt
+lu
+lv
+mg
+mh
+mi
+mk
+mk-mk
+ml
+mn
+mo
+mr
+ms
+mt
+my
+na
+nb
+nd
+ne
+ng
+nl
+nl-be
+nn
+no
+nr
+nso
+nv
+ny
+oc
+oj
+om
+or
+os
+pa
+pa-in
+pa-pk
+pi
+pl
+ps
+pt
+pt-br
+qu
+rm
+rn
+ro
+ro-mo
+ru
+ru-mo
+sa
+sc
+sd
+sg
+si
+sk
+sl
+so
+sq
+sr
+ss
+st
+su
+sv
+sv-fi
+sv-se
+sw
+ta
+te
+tg
+th
+ti
+tig
+tk
+tl
+tlh
+tn
+to
+tr
+ts
+tt
+tw
+ty
+ug
+uk
+ur
+uz
+ve
+vi
+vo
+wa
+wo
+xh
+yi
+yo
+za
+zh
+zh-cn
+zh-hk
+zh-sg
+zh-tw
+zu
diff --git a/frontends/gtk/res/licence.html b/frontends/gtk/res/licence.html
new file mode 120000
index 000000000..86f8c54bf
--- /dev/null
+++ b/frontends/gtk/res/licence.html
@@ -0,0 +1 @@
+en/licence.html \ No newline at end of file
diff --git a/frontends/gtk/res/login.gtk2.ui b/frontends/gtk/res/login.gtk2.ui
new file mode 100644
index 000000000..552b173ed
--- /dev/null
+++ b/frontends/gtk/res/login.gtk2.ui
@@ -0,0 +1,223 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+ <object class="GtkDialog" id="wndLogin">
+ <property name="title" translatable="yes">Site Authentication</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ALWAYS</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox2">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkHBox" id="hbox12">
+ <property name="visible">True</property>
+ <property name="border_width">3</property>
+ <child>
+ <object class="GtkImage" id="image3">
+ <property name="visible">True</property>
+ <property name="yalign">0.10000000149011612</property>
+ <property name="xpad">12</property>
+ <property name="icon_size">6</property>
+ <property name="icon_name">gtk-dialog-authentication</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="table5">
+ <property name="visible">True</property>
+ <property name="border_width">1</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">11</property>
+ <property name="row_spacing">10</property>
+ <child>
+ <object class="GtkLabel" id="labelLoginHost">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">moo.yoo.com</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label57">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Password</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label56">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Username</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label54">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Host</property>
+ </object>
+ <packing>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label55">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Realm</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="labelLoginRealm">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">my sekr3t area</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryLoginPass">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="visibility">False</property>
+ <property name="activates_default">True</property>
+ <property name="text" translatable="yes">opensesame</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryLoginUser">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="text" translatable="yes">sesame</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">1</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area2">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <object class="GtkButton" id="buttonLoginCan">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonLoginOK">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment14">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <child>
+ <object class="GtkHBox" id="hbox11">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="stock">gtk-ok</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label49">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Login</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">buttonLoginCan</action-widget>
+ <action-widget response="-5">buttonLoginOK</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/login.gtk3.ui b/frontends/gtk/res/login.gtk3.ui
new file mode 100644
index 000000000..552b173ed
--- /dev/null
+++ b/frontends/gtk/res/login.gtk3.ui
@@ -0,0 +1,223 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+ <object class="GtkDialog" id="wndLogin">
+ <property name="title" translatable="yes">Site Authentication</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ALWAYS</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox2">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkHBox" id="hbox12">
+ <property name="visible">True</property>
+ <property name="border_width">3</property>
+ <child>
+ <object class="GtkImage" id="image3">
+ <property name="visible">True</property>
+ <property name="yalign">0.10000000149011612</property>
+ <property name="xpad">12</property>
+ <property name="icon_size">6</property>
+ <property name="icon_name">gtk-dialog-authentication</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="table5">
+ <property name="visible">True</property>
+ <property name="border_width">1</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">11</property>
+ <property name="row_spacing">10</property>
+ <child>
+ <object class="GtkLabel" id="labelLoginHost">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">moo.yoo.com</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label57">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Password</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label56">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Username</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label54">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Host</property>
+ </object>
+ <packing>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label55">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Realm</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="labelLoginRealm">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">my sekr3t area</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryLoginPass">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="visibility">False</property>
+ <property name="activates_default">True</property>
+ <property name="text" translatable="yes">opensesame</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryLoginUser">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="text" translatable="yes">sesame</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">1</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area2">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <object class="GtkButton" id="buttonLoginCan">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonLoginOK">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment14">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <child>
+ <object class="GtkHBox" id="hbox11">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="stock">gtk-ok</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label49">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Login</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">buttonLoginCan</action-widget>
+ <action-widget response="-5">buttonLoginOK</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/maps.html b/frontends/gtk/res/maps.html
new file mode 120000
index 000000000..a32f725fb
--- /dev/null
+++ b/frontends/gtk/res/maps.html
@@ -0,0 +1 @@
+en/maps.html \ No newline at end of file
diff --git a/frontends/gtk/res/menu_cursor.png b/frontends/gtk/res/menu_cursor.png
new file mode 100644
index 000000000..ccbbbd2d4
--- /dev/null
+++ b/frontends/gtk/res/menu_cursor.png
Binary files differ
diff --git a/frontends/gtk/res/menu_cursor.xbm b/frontends/gtk/res/menu_cursor.xbm
new file mode 100644
index 000000000..1257ac1f4
--- /dev/null
+++ b/frontends/gtk/res/menu_cursor.xbm
@@ -0,0 +1,6 @@
+#define menu_cursor_width 16
+#define menu_cursor_height 16
+static char menu_cursor_bits[] = {
+ 0x00, 0x00, 0x80, 0x7F, 0x88, 0x40, 0x9E, 0x5E, 0x88, 0x40, 0x80, 0x56,
+ 0x80, 0x40, 0x80, 0x5A, 0x80, 0x40, 0x80, 0x5E, 0x80, 0x40, 0x80, 0x56,
+ 0x80, 0x40, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x00 };
diff --git a/frontends/gtk/res/menu_cursor_mask.xbm b/frontends/gtk/res/menu_cursor_mask.xbm
new file mode 100644
index 000000000..09789d51b
--- /dev/null
+++ b/frontends/gtk/res/menu_cursor_mask.xbm
@@ -0,0 +1,6 @@
+#define menu_cursor_mask_width 16
+#define menu_cursor_mask_height 16
+static char menu_cursor_mask_bits[] = {
+ 0xC0, 0xFF, 0xC8, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xC8, 0xFF,
+ 0xC0, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF,
+ 0xC0, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF, 0x00, 0x00 };
diff --git a/frontends/gtk/res/menu_cursor_mask.xpm b/frontends/gtk/res/menu_cursor_mask.xpm
new file mode 100644
index 000000000..985d46cc6
--- /dev/null
+++ b/frontends/gtk/res/menu_cursor_mask.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * menu_cursor_mask_xpm[] = {
+"16 16 3 1",
+" c None",
+". c #FFFFFF",
+"+ c #000000",
+" ..........",
+" . .++++++++.",
+"...+. .+......+.",
+".++++..+.++++.+.",
+"...+. .+......+.",
+" . .+.++.+.+.",
+" .+......+.",
+" .+.+.++.+.",
+" .+......+.",
+" .+.++++.+.",
+" .+......+.",
+" .+.++.+.+.",
+" .+......+.",
+" .++++++++.",
+" ..........",
+" "};
diff --git a/frontends/gtk/res/messages.gresource.xml b/frontends/gtk/res/messages.gresource.xml
new file mode 100644
index 000000000..684a10862
--- /dev/null
+++ b/frontends/gtk/res/messages.gresource.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/netsurf">
+ <file>Messages</file>
+ <file>nl/Messages</file>
+ <file>de/Messages</file>
+ <file>fr/Messages</file>
+ <file>it/Messages</file>
+ </gresource>
+</gresources>
diff --git a/frontends/gtk/res/netsurf-16x16.xpm b/frontends/gtk/res/netsurf-16x16.xpm
new file mode 100644
index 000000000..7880f7aae
--- /dev/null
+++ b/frontends/gtk/res/netsurf-16x16.xpm
@@ -0,0 +1,211 @@
+/* XPM */
+static char *netsurf___x__[] = {
+/* columns rows colors chars-per-pixel */
+"16 16 189 2",
+" c #02530B521130",
+". c #02C50E711619",
+"X c #02AF106B198E",
+"o c #02D913641E8A",
+"O c #04D913E91D9D",
+"+ c #042B14C01F86",
+"@ c #124A1BC61F1D",
+"# c #097F17941FC4",
+"$ c #03CE193E2759",
+"% c #04451CED2D68",
+"& c #0BDC1D8727E6",
+"* c #10D2247A2F43",
+"= c #069C27663D09",
+"- c #180C2AED33E3",
+"; c #1AEC2D37353A",
+": c #16F12D9C3927",
+"> c #20AD32C039B7",
+", c #0A2A2E04451D",
+"< c #0C1A306A47D4",
+"1 c #1D6E39A1482F",
+"2 c #060A341B5355",
+"3 c #0B773D945E4D",
+"4 c #20B93C4949C5",
+"5 c #07B03F0A6460",
+"6 c #2332407A4E57",
+"7 c #253040AD4CF8",
+"8 c #33854FAB5A6E",
+"9 c #15494CF97003",
+"0 c #19F855EA7B34",
+"q c #1E7F57237923",
+"w c #21344D986676",
+"e c #422B60856A1E",
+"r c #09DA58208D29",
+"t c #11AC562C82BA",
+"y c #0A6A5CB99496",
+"u c #18A265959712",
+"i c #17B8662E9805",
+"p c #11A66358990E",
+"a c #224A65BB8E98",
+"s c #342C75079947",
+"d c #3487766A9B6F",
+"f c #35B478C99F4B",
+"g c #0BFA6509A06D",
+"h c #0F9175B8BA3A",
+"j c #1AAA7C1DBB21",
+"k c #289374DBA2C1",
+"l c #200E76DFADCA",
+"z c #269D7B1FAF00",
+"x c #32E57D4FA875",
+"c c #299F805AB558",
+"v c #2DF28A1CC027",
+"b c #345A83F3B216",
+"n c #3151843FB58D",
+"m c #59C882138E75",
+"M c #5F5E8AAC9842",
+"N c #51998AF6A4C9",
+"B c #5F5A96AEACF5",
+"V c #43048E07B597",
+"C c #65129940AC1C",
+"Z c #6C1AA2A1B60E",
+"A c #7A87AEC9BD67",
+"S c #125084AAD039",
+"D c #163388A1D450",
+"F c #1BF88E3BD899",
+"G c #17BD8EB6DCB2",
+"H c #220A83D0C214",
+"J c #29678B9EC6ED",
+"K c #3F59A336DDC8",
+"L c #198792ECE247",
+"P c #11B295CDED75",
+"I c #132A98BFF1CB",
+"U c #25099D91EB08",
+"Y c #29299E0AE8BB",
+"T c #1412A137FE21",
+"R c #191EA60AFFFF",
+"E c #1C73A663FFFF",
+"W c #2E68A1C2E7B3",
+"Q c #2C05A14AEAC1",
+"! c #2E32A597F03F",
+"~ c #39BBA7F4E929",
+"^ c #23A0A4D1F872",
+"/ c #25C6A4E4F6A5",
+"( c #2965A4CCF3B1",
+") c #236CA4BAF891",
+"_ c #23A1A6A2FA96",
+"` c #2018A8FDFFFF",
+"' c #25F5AB89FFFF",
+"] c #2B0BACCDFECB",
+"[ c #2AEBAD69FFF4",
+"{ c #2E7EAC78FC88",
+"} c #37FCADAFF4EB",
+"| c #3B7FAF4EF4E9",
+" . c #303DAB56F8E3",
+".. c #3354B0E8FFFF",
+"X. c #3607B294FFCE",
+"o. c #3978B4A2FFF7",
+"O. c #414EA3E7DD4D",
+"+. c #5643A8B7D325",
+"@. c #52B4AADCD9F0",
+"#. c #60F0B145D82C",
+"$. c #6F40B9C2DA92",
+"%. c #71EFBCB5DD40",
+"&. c #482FB26FEEA5",
+"*. c #5E59B526E061",
+"=. c #413CB50EFA06",
+"-. c #40FAB7C7FF64",
+";. c #411FB840FF4A",
+":. c #43C4B9CAFFFA",
+">. c #4AB5BB61FFFF",
+",. c #4A0ABCF3FFFF",
+"<. c #4BB1BD9AFFB4",
+"1. c #4D02BE43FFE0",
+"2. c #542DBA39F2D5",
+"3. c #5682BD17F583",
+"4. c #6545BFABECE0",
+"5. c #7AD1C155DCC5",
+"6. c #512AC23DFFFF",
+"7. c #5584C279FF9A",
+"8. c #5642C44BFFFF",
+"9. c #57E0C1FCF97C",
+"0. c #5C81C618FFB3",
+"q. c #5F65C55DFFFF",
+"w. c #5FA0C7BFFFCB",
+"e. c #6BD0C37FEDBC",
+"r. c #6FB7C6C0EF7B",
+"t. c #7055C5DCEDC7",
+"y. c #7A3ECB76EEA8",
+"u. c #63C4C2A1F626",
+"i. c #64A9C5C0F7C3",
+"p. c #6B3AC4CBF902",
+"a. c #621EC925FFE9",
+"s. c #6557C9F1FE6D",
+"d. c #67BACC2FFFFE",
+"f. c #66B4CD66FFFF",
+"g. c #69E9CD27FFFC",
+"h. c #6E23CD02FFBA",
+"j. c #6E50CFA0FFFF",
+"k. c #7093CC80FFBB",
+"l. c #6E47D0E6FFFF",
+"z. c #7269D1B2FFFF",
+"x. c #73B7D323FFFF",
+"c. c #7383D389FFFF",
+"v. c #7F08D0B1FFA4",
+"b. c #7A4CD5C8FFFF",
+"n. c #7C1BD6A7FFFF",
+"m. c #7CBAD75AFFFF",
+"M. c #8CB9CF6EE5B6",
+"N. c #9678D359E3DC",
+"B. c #8911D5A7F360",
+"V. c #829DD7A7FFFF",
+"C. c #8092DA47FFFF",
+"Z. c #814DDA85FFFF",
+"A. c #839BDA7BFFFF",
+"S. c #85D6DB65FFFF",
+"D. c #86DFDAB4FFFD",
+"F. c #8EE1DDADFC14",
+"G. c #8CC8DC55FFFE",
+"H. c #894DDD3BFFFE",
+"J. c #89CCDDD9FFFF",
+"K. c #8AFCDE12FFFC",
+"L. c #8D41DF48FFF9",
+"P. c #8D5FE05DFFFF",
+"I. c #8FB7E05EFFEA",
+"U. c #9808DD11F202",
+"Y. c #A445E007FF52",
+"T. c #96ADE3E2FFB6",
+"R. c #9705E548FFFF",
+"E. c #9DB5E153FFFD",
+"W. c #9914E507FFB9",
+"Q. c #9A08E660FFF3",
+"!. c #9A40E858FFFF",
+"~. c #A7C7E568F44C",
+"^. c #A7C5E6F4F48B",
+"/. c #A3A6EB6AFFFB",
+"(. c #A652EC53FFFA",
+"). c #A408ED75FFFF",
+"_. c #B2C9E411FFBB",
+"`. c #B047E69AFFFB",
+"'. c #B8E2E8BFFFF5",
+"]. c #D6CBF231FFFF",
+"[. c #DE35F506FFFC",
+"{. c #E029F512FFFF",
+"}. c #E57AF80BFFFE",
+"|. c #E6C5F71DFFE9",
+" X c #E735F794FFF7",
+".X c #E895F7DEFFEE",
+"XX c #E9B0F91EFFFE",
+"oX c gray100",
+"OX c None",
+/* pixels */
+"OXOXOXOXOXOX; 7 6 : OXOXOXOXOXOX",
+"OXOXOXOX> C 5.y.r.*.V w OXOXOXOX",
+"OXOX@ e M ).R.J.m.l.V.u.f & OXOX",
+"OXOXm ^.~.(.W.K.D.`.[.].Y.k OXOX",
+"OX8 U.A N./.T.H.G.}.oXoX{.p.9 OX",
+"OXB !.Z M.Q.I.A.E.XXoXoX|.q.H O ",
+"- %.P.B.F.L.S.n.z.'. X.X_.>.U , ",
+"4 t.C.N $.Z.b.z.g.h.k.v.-...^ 3 ",
+"1 4.c.#.e.x.j.d.w.7.<.;.X.[ F = ",
+"* @.f.i.+.s.a.0.7.1.:.o.{ _ p . ",
+"OXd 9.3.s 2.8.6.,.=.} ! u G h X ",
+"OX# b &.x O.K n | z l / L I 5 OX",
+"OXOXq ~ v a c J .( ) E T y OXOX",
+"OXOXOX0 W Q Y ] ' ` R P r o OXOX",
+"OXOXOXOX< i j t D S g 2 OXOXOXOX",
+"OXOXOXOXOXOX+ % $ OXOXOXOXOXOX"
+};
diff --git a/frontends/gtk/res/netsurf-gtk.desktop b/frontends/gtk/res/netsurf-gtk.desktop
new file mode 100644
index 000000000..4c21d5537
--- /dev/null
+++ b/frontends/gtk/res/netsurf-gtk.desktop
@@ -0,0 +1,68 @@
+[Desktop Entry]
+Name=NetSurf Web Browser
+Name[ca]=Navegador web NetSurf
+Name[cs]=NetSurf Webový prohlížeÄ
+Name[es]=Navegador web NetSurf
+Name[fa]=مرورگر اینترنتی NetSurf
+Name[fi]=NetSurf-selain
+Name[fr]=Navigateur Web NetSurf
+Name[hu]=NetSurf webböngésző
+Name[it]=NetSurf Browser Web
+Name[ja]=NetSurf ウェブ・ブラウザ
+Name[ko]=NetSurf 웹 브ë¼ìš°ì €
+Name[nb]=NetSurf Nettleser
+Name[nl]=NetSurf webbrowser
+Name[nn]=NetSurf Nettlesar
+Name[no]=NetSurf Nettleser
+Name[pl]=PrzeglÄ…darka WWW NetSurf
+Name[pt]=NetSurf Navegador Web
+Name[pt_BR]=Navegador Web NetSurf
+Name[sk]=Internetový prehliadaÄ NetSurf
+Comment=Browse the World Wide Web
+Comment[ca]=Navegueu per el web
+Comment[cs]=Prohlížení stránek World Wide Webu
+Comment[de]=Im Internet surfen
+Comment[es]=Navegue por la web
+Comment[fa]=صÙحات شبکه جهانی اینترنت را مرور نمایید
+Comment[fi]=Selaa Internetin WWW-sivuja
+Comment[fr]=Navigue sur Internet
+Comment[hu]=A világháló böngészése
+Comment[it]=Esplora il web
+Comment[ja]=ウェブを閲覧ã—ã¾ã™
+Comment[ko]=ì›¹ì„ ëŒì•„ 다닙니다
+Comment[nb]=Surf på nettet
+Comment[nl]=Verken het internet
+Comment[nn]=Surf på nettet
+Comment[no]=Surf på nettet
+Comment[pl]=PrzeglÄ…danie stron WWW
+Comment[pt]=Navegue na Internet
+Comment[pt_BR]=Navegue na Internet
+Comment[sk]=Prehliadanie internetu
+GenericName=Web Browser
+GenericName[ca]=Navegador web
+GenericName[cs]=Webový prohlížeÄ
+GenericName[es]=Navegador web
+GenericName[fa]=مرورگر اینترنتی
+GenericName[fi]=WWW-selain
+GenericName[fr]=Navigateur Web
+GenericName[hu]=Webböngésző
+GenericName[it]=Browser Web
+GenericName[ja]=ウェブ・ブラウザ
+GenericName[ko]=웹 브ë¼ìš°ì €
+GenericName[nb]=Nettleser
+GenericName[nl]=Webbrowser
+GenericName[nn]=Nettlesar
+GenericName[no]=Nettleser
+GenericName[pl]=PrzeglÄ…darka WWW
+GenericName[pt]=Navegador Web
+GenericName[pt_BR]=Navegador Web
+GenericName[sk]=Internetový prehliadaÄ
+Exec=netsurf-gtk %u
+Terminal=false
+X-MultipleArgs=false
+Type=Application
+Icon=netsurf.png
+Categories=Network;
+MimeType=text/html;text/xml;application/xhtml+xml;application/xml;image/gif;image/jpeg;image/png
+StartupWMClass=NetSurf-bin
+StartupNotify=true
diff --git a/frontends/gtk/res/netsurf.gresource.xml b/frontends/gtk/res/netsurf.gresource.xml
new file mode 100644
index 000000000..c7626b053
--- /dev/null
+++ b/frontends/gtk/res/netsurf.gresource.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/netsurf">
+ <file>cookies.gtk2.ui</file>
+ <file>history.gtk3.ui</file>
+ <file>netsurf.gtk2.ui</file>
+ <file>password.gtk3.ui</file>
+ <file>toolbar.gtk2.ui</file>
+ <file>warning.gtk3.ui</file>
+ <file>cookies.gtk3.ui</file>
+ <file>hotlist.gtk2.ui</file>
+ <file>netsurf.gtk3.ui</file>
+ <file>ssl.gtk2.ui</file>
+ <file>toolbar.gtk3.ui</file>
+ <file>downloads.gtk2.ui</file>
+ <file>hotlist.gtk3.ui</file>
+ <file>options.gtk2.ui</file>
+ <file>ssl.gtk3.ui</file>
+ <file>viewdata.gtk2.ui</file>
+ <file>downloads.gtk3.ui</file>
+ <file>login.gtk2.ui</file>
+ <file>options.gtk3.ui</file>
+ <file>tabcontents.gtk2.ui</file>
+ <file>viewdata.gtk3.ui</file>
+ <file>history.gtk2.ui</file>
+ <file>login.gtk3.ui</file>
+ <file>password.gtk2.ui</file>
+ <file>tabcontents.gtk3.ui</file>
+ <file>warning.gtk2.ui</file>
+ <file preprocess="to-pixdata">favicon.png</file>
+ <file preprocess="to-pixdata">netsurf.xpm</file>
+ <file preprocess="to-pixdata">menu_cursor.png</file>
+ <file preprocess="to-pixdata">throbber/throbber0.png</file>
+ <file preprocess="to-pixdata">throbber/throbber1.png</file>
+ <file preprocess="to-pixdata">throbber/throbber2.png</file>
+ <file preprocess="to-pixdata">throbber/throbber3.png</file>
+ <file preprocess="to-pixdata">throbber/throbber4.png</file>
+ <file preprocess="to-pixdata">throbber/throbber5.png</file>
+ <file preprocess="to-pixdata">throbber/throbber6.png</file>
+ <file preprocess="to-pixdata">throbber/throbber7.png</file>
+ <file preprocess="to-pixdata">throbber/throbber8.png</file>
+ <file>credits.html</file>
+ <file>it/credits.html</file>
+ <file>nl/credits.html</file>
+ <file>licence.html</file>
+ <file>it/licence.html</file>
+ <file>nl/licence.html</file>
+ <file>welcome.html</file>
+ <file>de/welcome.html</file>
+ <file>it/welcome.html</file>
+ <file>ja/welcome.html</file>
+ <file>nl/welcome.html</file>
+ <file>maps.html</file>
+ <file>adblock.css</file>
+ <file>default.css</file>
+ <file>internal.css</file>
+ <file>quirks.css</file>
+ <file>netsurf.png</file>
+ <file>default.ico</file>
+ <file>arrow_down_8x32.png</file>
+ <file>icons/arrow-l.png</file>
+ <file>icons/content.png</file>
+ <file>icons/directory2.png</file>
+ <file>icons/directory.png</file>
+ <file>icons/hotlist-add.png</file>
+ <file>icons/hotlist-rmv.png</file>
+ <file>icons/search.png</file>
+ <file>languages</file>
+ </gresource>
+</gresources>
diff --git a/frontends/gtk/res/netsurf.gtk2.ui b/frontends/gtk/res/netsurf.gtk2.ui
new file mode 100644
index 000000000..68812b364
--- /dev/null
+++ b/frontends/gtk/res/netsurf.gtk2.ui
@@ -0,0 +1,212 @@
+<?xml version="1.0"?>
+<!--Generated with glade3 3.4.5 on Wed Apr 7 17:10:28 2010 -->
+<interface>
+ <object class="GtkAdjustment" id="adjustment1">
+ <property name="upper">100</property>
+ <property name="lower">0</property>
+ <property name="page_increment">10</property>
+ <property name="step_increment">26</property>
+ <property name="page_size">10</property>
+ <property name="value">0.5357142857142857</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment2">
+ <property name="upper">100</property>
+ <property name="lower">0</property>
+ <property name="page_increment">10</property>
+ <property name="step_increment">26</property>
+ <property name="page_size">10</property>
+ <property name="value">0</property>
+ </object>
+ <object class="GtkUIManager" id="uimanager1">
+ <ui>
+ <menubar name="menubar"/>
+ </ui>
+ </object>
+ <object class="GtkWindow" id="wndBrowser">
+ <property name="title" translatable="yes">NetSurf</property>
+ <property name="window_position">GTK_WIN_POS_CENTER</property>
+ <child>
+ <object class="GtkVBox" id="vbox14">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuBar" constructor="uimanager1" id="menubar">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="toolbar">
+ <property name="visible">True</property>
+ <property name="toolbar_style">GTK_TOOLBAR_BOTH_HORIZ</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="searchbar">
+ <property name="toolbar_style">GTK_TOOLBAR_BOTH</property>
+ <child>
+ <object class="GtkToolButton" id="closeSearchButton">
+ <property name="visible">True</property>
+ <property name="stock_id">gtk-close</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="searchLabelItem">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="searchlabel">
+ <property name="visible">True</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Match</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="toolSearch">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkEntry" id="searchEntry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="searchBackButton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Search _Back</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-go-back</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="searchForwardButton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Search _Forward</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-go-forward</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="checkAllSearchItem">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkCheckButton" id="checkAllSearch">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip-text" translatable="yes">show all matches</property>
+ <property name="label" translatable="yes">All </property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="caseSensItem">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkCheckButton" id="caseSensButton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip-text" translatable="yes">Match case when searching</property>
+ <property name="label" translatable="yes">Case</property>
+ <property name="relief">GTK_RELIEF_NONE</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkNotebook" id="notebook">
+ <property name="visible">True</property>
+ <property name="show_tabs">False</property>
+ <property name="show_border">False</property>
+ <property name="scrollable">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/netsurf.gtk3.ui b/frontends/gtk/res/netsurf.gtk3.ui
new file mode 100644
index 000000000..ce47c6370
--- /dev/null
+++ b/frontends/gtk/res/netsurf.gtk3.ui
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkWindow" id="wndBrowser">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="toolbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="toolbar_style">both</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="searchbar">
+ <property name="can_focus">False</property>
+ <property name="toolbar_style">both</property>
+ <child>
+ <object class="GtkToolButton" id="closeSearchButton">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="label" translatable="yes">gtk-close</property>
+ <property name="stock_id">gtk-close</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="searchLabelItem">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <child>
+ <object class="GtkLabel" id="searchlabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Match</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="toolSearch">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <child>
+ <object class="GtkEntry" id="searchEntry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">â—</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="searchBackButton">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="label" translatable="yes">Search _Back</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-go-back</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="searchForwardButton">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="label" translatable="yes">Search _Forward</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-go-forward</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="checkAllSearchItem">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <child>
+ <object class="GtkCheckButton" id="checkAllSearch">
+ <property name="label" translatable="yes">All</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="caseSensItem">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <child>
+ <object class="GtkCheckButton" id="caseSensButton">
+ <property name="label" translatable="yes">Case</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkNotebook" id="notebook">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="show_tabs">False</property>
+ <property name="show_border">False</property>
+ <property name="scrollable">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/netsurf.png b/frontends/gtk/res/netsurf.png
new file mode 120000
index 000000000..905512c25
--- /dev/null
+++ b/frontends/gtk/res/netsurf.png
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/netsurf.png,b60 \ No newline at end of file
diff --git a/frontends/gtk/res/netsurf.xpm b/frontends/gtk/res/netsurf.xpm
new file mode 100644
index 000000000..7061727d6
--- /dev/null
+++ b/frontends/gtk/res/netsurf.xpm
@@ -0,0 +1,317 @@
+/* XPM */
+static char *netsurf[] = {
+/* columns rows colors chars-per-pixel */
+"132 135 176 2",
+" c black",
+". c #010101",
+"X c #010202",
+"o c #030406",
+"O c #040A0D",
+"+ c #060E14",
+"@ c #08141A",
+"# c #081720",
+"$ c #091B27",
+"% c #0F2430",
+"& c #142E3D",
+"* c #143548",
+"= c #153C53",
+"- c #194662",
+"; c #1E516E",
+": c #1D577A",
+"> c #175B87",
+", c #1A6595",
+"< c #1E6B9C",
+"1 c #26709C",
+"2 c #33789E",
+"3 c #3188BD",
+"4 c #3E89B3",
+"5 c #1F92DD",
+"6 c #0E9CFD",
+"7 c #0E9DFF",
+"8 c #0F9EFF",
+"9 c #1A95E7",
+"0 c #1898ED",
+"q c #1299F4",
+"w c #119BF9",
+"e c #119FFF",
+"r c #14A0FF",
+"t c #15A1FF",
+"y c #17A2FF",
+"u c #18A3FF",
+"i c #1AA3FE",
+"p c #1BA4FE",
+"a c #1CA5FE",
+"s c #1EA5FE",
+"d c #1FA6FE",
+"f c #268AC9",
+"g c #218ED3",
+"h c #20A6FE",
+"j c #21A7FE",
+"k c #23A8FE",
+"l c #24A8FE",
+"z c #25A8FE",
+"x c #26A9FE",
+"c c #27AAFE",
+"v c #28AAFE",
+"b c #2AABFF",
+"n c #2BACFF",
+"m c #2CADFF",
+"M c #2EAEFF",
+"N c #2FAEFF",
+"B c #31AFFE",
+"V c #31AFFF",
+"C c #32B0FF",
+"Z c #34B1FF",
+"A c #35B2FF",
+"S c #36B2FF",
+"D c #37B2FF",
+"F c #3AB2FB",
+"G c #38B3FE",
+"H c #39B3FE",
+"J c #3AB3FC",
+"K c #3BB4FE",
+"L c #3CB5FE",
+"P c #3DB5FE",
+"I c #3EB6FF",
+"U c #3FB7FF",
+"Y c #4182A4",
+"T c #4789AB",
+"R c #43B7FB",
+"E c #40B7FF",
+"W c #44B6FA",
+"Q c #42B8FE",
+"! c #44B9FE",
+"~ c #46BAFF",
+"^ c #47BAFF",
+"/ c #48BBFF",
+"( c #49BCFF",
+") c #4ABCFF",
+"_ c #4CBDFF",
+"` c #4EBEFF",
+"' c #50BFFE",
+"] c #50BFFF",
+"[ c #51BFFF",
+"{ c #5ABFF7",
+"} c #52C0FE",
+"| c #54C1FE",
+" . c #56C2FF",
+".. c #57C3FF",
+"X. c #58C3FE",
+"o. c #58C3FF",
+"O. c #5CC1F8",
+"+. c #5DC4FD",
+"@. c #5CC5FE",
+"#. c #5DC5FE",
+"$. c #5DC6FF",
+"%. c #5EC6FF",
+"&. c #5FC7FF",
+"*. c #61C8FF",
+"=. c #62C8FF",
+"-. c #63C9FF",
+";. c #65CAFF",
+":. c #66CBFF",
+">. c #67CBFF",
+",. c #68CCFF",
+"<. c #69CDFF",
+"1. c #6BCDFF",
+"2. c #6CCEFF",
+"3. c #6DCEFF",
+"4. c #6FCFFF",
+"5. c #70D0FF",
+"6. c #72D1FF",
+"7. c #73D1FF",
+"8. c #74D2FF",
+"9. c #76D3FF",
+"0. c #77D3FF",
+"q. c #78D4FF",
+"w. c #79D5FF",
+"e. c #7AD5FF",
+"r. c #7CD6FF",
+"t. c #7ED7FF",
+"y. c #7FD8FF",
+"u. c #81CBE7",
+"i. c #82CBE7",
+"p. c #83CEEB",
+"a. c #81D4F7",
+"s. c #84D3F4",
+"d. c #80D7FE",
+"f. c #80D8FF",
+"g. c #84DAFF",
+"h. c #85DAFF",
+"j. c #86DBFF",
+"k. c #87DBFF",
+"l. c #89DCFF",
+"z. c #8ADDFF",
+"x. c #8BDEFF",
+"c. c #8DDEFF",
+"v. c #8EDEFF",
+"b. c #8FDFFF",
+"n. c #91E0FF",
+"m. c #92E1FF",
+"M. c #93E2FF",
+"N. c #95E3FF",
+"B. c #97E3FF",
+"V. c #98E4FF",
+"C. c #99E4FE",
+"Z. c #9BE5FE",
+"A. c #9CE6FE",
+"S. c #9EE6FF",
+"D. c #9FE7FF",
+"F. c #A1E8FF",
+"G. c #A3E9FF",
+"H. c #A5EBFF",
+"J. c #A6ECFF",
+"K. c #A8EDFF",
+"L. c #ABEEFF",
+"P. c #ADEFFF",
+"I. c #B0F0FF",
+"U. c #B3F1FF",
+"Y. c #B7F2FF",
+"T. c #C7F3FF",
+"R. c #DCF6FF",
+"E. c #EAF8FF",
+"W. c #F1FAFF",
+"Q. c #F8FCFF",
+"!. c #FCFEFF",
+"~. c #FDFEFF",
+"^. c #FEFEFF",
+"/. c #FEFFFF",
+"(. c gray100",
+"). c None",
+/* pixels */
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). % ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). O & ; 2 T O.O.a.r.e.e.e.q.9.9.4.{ { T 2 ; % O ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). @ - T r.l.k.k.k.y.t.t.t.t.r.e.e.q.9.8.8.8.8.4.4.1.1.1.{ T ; # ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). = Y p.v.x.x.s.v.k.k.k.a.g.t.y.t.r.e.e.q.9.8.8.8.2.4.4.4.4.,.,.;.;.;.O.4 = o ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). % 2 u.m.m.v.v.c.x.x.l.k.k.k.k.g.t.y.t.t.r.e.e.q.9.8.8.8.8.4.4.1.1.,.,.;.;.-.-.-.{ 2 & ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). O ; u.B.m.m.m.m.m.v.c.c.x.x.x.k.k.k.g.g.g.t.t.r.e.e.q.9.9.8.8.2.2.8.2.1.1.,.,.;.;.-.*.*.$.$.! : + ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). O : p.C.C.B.B.B.m.m.m.m.c.c.c.c.x.k.l.k.k.g.y.g.t.t.r.e.e.q.q.8.9.2.2.4.2.1.1.1.,.;.;.-.-.-.&.$.$.+.O.2 @ ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).). + 2 s.A.A.C.C.C.B.m.N.m.M.m.m.m.c.l.x.x.l.k.k.g.g.t.f.t.r.r.e.q.9.9.8.8.8.8.8.1.2.,.,.,.;.-.-.&.&.&.$.+... .| 2 @ ).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).). = p.F.F.A.A.A.C.C.C.C.N.N.m.m.m.m.v.v.c.x.l.l.g.k.g.t.f.f.t.r.e.e.q.9.8.8.8.2.8.2.2.1.,.,.,.;.-.&.-.&.+.+....... .` 1 O ).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).). ; A.F.F.A.A.A.A.C.C.C.N.N.N.m.m.m.v.c.x.x.l.k.k.g.g.f.f.t.r.e.e.q.9.q.8.8.8.2.2.2.1.,.,.;.;.-.-.&.&.$.+.$..... .| | W = ).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).). o T F.F.F.A.F.S.A.A.C.C.C.N.M.M.m.m.c.c.c.x.l.k.k.g.g.f.f.f.t.t.e.e.q.9.9.8.5.5.4.1.1.1.;.,.-.-.-.-.$.*.$.+.o. . . .| | ` T # ).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).). * & p.F.F.F.A.F.F.A.A.A.C.C.N.N.M.M.m.m.c.c.x.x.k.k.k.g.h.f.f.t.r.r.e.q.9.9.8.5.5.4.4.4.1.1.;.,.;.-.-.&.$.$.+. . . . .| | ` ` W ; ).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).). 2 A. % J.J.F.J.F.F.S.F.F.A.A.C.C.C.N.M.m.m.c.c.c.x.x.l.k.k.f.t.t.t.t.r.e.q.0.9.9.8.5.4.4.1.1.,.,.;.-.-.&.-.$.$.$.o.+... .| | ` ) ) ) 2 O ).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).). @ u.J.T ; J.J.J.F.J.F.F.F.A.A.D.C.N.C.N.M.M.m.m.m.x.x.x.l.k.k.k.k.f.f.t.r.r.q.0.9.5.5.5.5.4.4.1.,.,.;.;.-.-.&.$.$.+.o. ... .| | ` | [ ) ( 5 $ ).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).). & p.K.K.: T J.J.J.J.F.G.F.F.F.F.A.A.N.C.C.N.M.m.m.c.c.c.x.x.l.k.g.g.t.t.t.r.r.q.0.0.9.8.5.5.4.3.1.,.,.,.;.-.-.&.h.k.+.$.o. . . .| ` ` ` | ( ( W - ).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).). = F.K.P.K.& p.K.J.J.J.J.F.F.F.A.F.D.A.A.C.C.N.M.M.m.m.c.c.x.l.l.k.k.g.g.g.t.r.r.r.e.0.9.9.5.5.4.4.1.1.,.;.,.-.-.,.Q./.8.o.o.o. .[ ` | | ` ) ) ( ( ( ; ).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).). ; J.K.P.P.F. % K.P.K.J.J.J.G.G.G.F.A.D.A.A.C.C.N.N.M.m.m.m.v.x.x.l.k.g.g.t.y.t.t.r.q.0.0.9.7.9.5.4.4.2.1.,.,.;.;.-.F././.T.$.o.o. . .| ) ` ) ) ) ( ( ! ! , ).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).). : K.K.P.P.P.u. ; P.K.P.K.K.J.J.J.G.F.F.D.D.D.C.C.N.N.M.m.m.c.v.c.x.l.k.k.g.k.y.t.r.r.r.0.0.9.9.5.5.4.4.2.1.,.;.,.;.,.W./././.l.o. . . . .| | ` ) ) ( ( ^ ! Q 1 ).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).). Y K.K.P.P.P.I.Y T P.P.P.K.K.J.J.J.F.G.F.D.D.A.A.N.N.N.N.M.m.m.c.c.c.x.l.k.g.g.y.t.t.t.r.w.q.q.8.7.5.5.4.2.1.,.,.;.;.F././././.E.+. . . .| | ) ` ) ) ) ( / ! ! W 2 ).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).). 2 K.P.P.P.P.P.I.P.u.T ; % i.I.I.P.P.P.K.J.J.J.G.F.D.D.D.A.C.C.N.N.m.m.m.c.c.x.l.l.g.k.g.y.t.t.r.r.w.q.8.8.7.5.5.5.2.1.,.,.;.;.E./././././.P. . . .| | | ) ) ` ( 1.K.0.! E Q 1 ).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).). ; J.K.P.P.P.I.I.U.U.U.U.U.U.U.I.I.P.P.P.K.J.J.J.G.G.D.D.D.A.D.C.C.N.N.m.N.c.c.x.l.l.g.k.g.y.g.t.r.r.r.q.q.8.7.5.5.5.2.2.,.,.;.C./././././././.4.o.o.| | ) | $.C.R././.U.! ! U L > ).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).). * F.J.K.K.P.I.I.U.U.U.U.U.U.U.U.U.I.I.P.K.K.J.J.J.G.G.D.D.D.D.C.N.C.N.m.m.m.c.c.x.x.l.k.k.k.y.t.t.r.r.q.q.8.8.9.5.5.5.2.2.,.,.;.E./././././././.T.o.[ | .v.T./.!././.!.k.! E Q L L - ).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).). & C.J.K.K.P.P.I.I.U.U.U.Y.Y.Y.U.U.U.I.I.P.P.P.K.J.G.J.F.D.D.D.A.C.C.N.N.M.m.m.m.c.x.l.l.k.k.g.y.y.y.r.r.r.M.R.U.C.9.5.5.2.2.1.,.v.!././././././././.k.d.T././././././././.+.! ! E E L G * ).).).).).).).).).).).).).",
+").).).).).).).).).).).).). + p.J.J.K.P.P.P.I.I.U.U.Y.Y.Y.Y.Y.U.U.U.I.P.P.K.K.J.J.J.F.F.D.D.A.D.N.C.N.M.m.m.c.c.x.x.l.z.g.g.y.g.y.r.r.r.L./.!././.R.U.m.2.1.>.R./././././././././././././././././././.W.! ! E E E L L F $ ).).).).).).).).).).).).",
+").).).).).).).).).).).).). T G.J.J.K.K.P.P.I.I.U.U.Y.Y.Y.Y.Y.U.U.I.I.P.P.P.K.J.J.J.G.F.D.D.A.D.C.C.N.M.m.M.c.c.x.x.l.k.k.g.y.t.y.t.r.q.r.!./././././././.R.T././././././././././././././././././././.Y.! ! ! E L L L K g o ).).).).).).).).).).).).",
+").).).).).).).).).).).). ; J.G.J.J.K.K.P.P.I.I.U.U.U.Y.Y.Y.U.U.U.I.I.P.P.P.K.J.G.J.G.F.D.D.D.C.N.C.N.M.m.M.c.c.x.l.l.k.g.g.y.t.y.r.r.r.q.T.!././././././././.!././././././././././././././././././.Q.v.! ! E E E L L K G , ).).).).).).).).).).).",
+").).).).).).).).).).). & A.J.F.J.J.J.J.L.P.P.I.U.U.U.U.U.U.U.U.U.I.I.P.P.P.K.J.J.F.F.F.D.D.A.A.N.C.N.M.m.m.c.c.x.l.l.k.k.h.f.f.f.r.r.r.0.A.^././././././././././././././././././././././././././././.*.! ! E E E L L L G D * ).).).).).).).).).).",
+").).).).).).).).).).). O u.A.F.F.J.J.J.K.K.P.P.I.I.U.U.U.U.U.U.U.I.I.P.P.K.K.J.J.J.F.J.D.D.D.A.A.C.N.N.M.m.m.m.c.x.x.l.k.k.f.f.f.r.r.r.0.0.0.Q./././././././././././././././././././././././././././.W.! ! ! E E L L L P G D B @ ).).).).).).).).).).",
+").).).).).).).).).). Y F.D.F.J.J.J.J.K.L.P.P.P.I.I.I.U.U.U.U.I.I.I.P.P.K.K.J.J.J.G.F.D.D.D.A.C.C.C.N.m.m.m.c.c.x.l.l.k.k.h.f.f.r.r.r.e.0.9.T.^././././././././././././././././././././././././././.T./ ! ! ! E E L L G G D D 1 ).).).).).).).).).",
+").).).).).).).).).). $ N.D.D.F.F.G.J.J.K.K.L.L.P.P.I.I.I.I.I.I.I.I.P.P.P.K.J.J.J.G.G.F.D.D.A.D.C.N.N.M.N.m.m.c.c.x.x.l.k.f.h.f.f.f.r.e.e.e.9.n.^./././././././././././././././././././././././././.Q.K.! ! ! E E L L L G D D B V & ).).).).).).).).).",
+").).).).).).).).). T A.F.D.D.F.F.J.J.J.J.K.L.L.T : T i.J.I.I.P.P.P.K.K.K.J.J.G.G.G.F.D.D.A.C.C.N.N.M.m.m.m.v.v.l.x.k.k.g.g.t.f.f.r.e.e.e.9.9.W./././././././././././././././././././././././././.^.^.R.g.W E E P P P G G D D V g ).).).).).).).).",
+").).).).).).).).). ; C.A.A.D.D.F.F.F.J.J.J.K.K.L.& K.P.P.P.P.K.K.J.J.J.F.G.F.A.D.D.A.C.C.C.N.M.m.m.c.c.x.x.k.k.k.g.f.f.f.r.r.e.0.0.9.8.Y./././././././././././././././././././././././././././././.E.v.W P P G G G D V V N - ).).).).).).).).",
+").).).).).).).). o u.C.C.A.D.D.F.F.F.J.G.J.J.J.K.+ % P.K.K.P.K.K.K.J.J.G.F.F.F.F.D.A.D.C.C.N.M.M.m.m.m.l.x.x.l.k.k.g.f.f.f.r.r.e.9.9.9.8.h./././././././././././././././././././././././././././././././.W.n.^ P G G D D V V b + ).).).).).).).",
+").).).).).).).). ; B.C.C.A.A.D.D.D.F.F.G.J.G.K.A. = K.K.K.K.J.J.K.G.J.G.G.F.F.A.A.A.C.C.N.N.M.m.m.c.v.v.x.l.l.k.k.k.f.f.r.r.r.e.0.9.8.8.B.^./././././././././././././././././././././././././././././././.!.Q.F./ G D V V V N : ).).).).).).).",
+").).).).).).). p.B.N.C.C.D.A.D.D.F.F.G.G.G.J.r. ; J.J.J.J.J.J.J.F.G.G.F.F.S.F.A.C.C.C.N.N.M.m.N.c.x.x.z.z.z.g.g.g.g.t.t.r.r.q.0.0.h.R./././././././././././././././././././././././././././././././././././.Q./.2.A C C V N 0 O ).).).).).).",
+").).).).).).). ; N.N.N.N.C.C.D.D.D.D.F.F.G.G.K.i. 2 J.J.G.J.J.J.G.G.F.F.F.A.A.A.A.C.C.N.N.N.M.m.m.c.c.x.z.z.g.k.g.t.y.y.t.r.r.q.0.Y./././././././././././././././././././././././././././././././././././././.Q.E.&.A C C N N m ; ).).).).).).",
+").).).).).). r.m.B.B.N.C.C.A.A.D.D.D.F.F.G.G.Y X Y J.G.J.J.G.G.G.F.F.S.F.S.A.A.C.C.C.N.M.M.m.M.c.c.x.x.z.k.k.g.g.g.t.t.r.r.q.D.W././././././././././././././././././././././././././././././././././././.^./.G.U A A C C V N m 9 o ).).).).).",
+").).).).).). = n.m.m.m.N.C.C.C.A.A.D.D.A.F.G.G.Y X T G.G.G.F.G.F.F.F.A.F.S.A.A.A.C.C.N.N.M.m.m.c.c.c.x.z.z.k.g.g.g.t.t.t.r.k.R././././././././././././././././././././././././././././././././././././././.T.o.G K A C C V V N m N - ).).).).).",
+").).).).).). X X T n.n.n.n.N.N.C.C.C.A.A.A.F.A.F.A.: u.F.F.F.F.F.A.F.A.A.F.A.A.C.C.N.C.N.M.M.m.m.c.c.x.x.x.k.k.k.g.f.f.t.t.N./.!./././././././././././././././././././././././././././././././././././.Q.W.r.P G G A A D V V V N m b f ).).).).).",
+").).).).). X @ s.n.n.C.n.m.B.N.N.C.C.A.A.Z.F.S.F.; u.F.F.F.F.A.F.F.F.A.A.A.C.C.C.C.N.m.M.m.M.c.c.c.x.l.l.k.k.g.g.f.f.t.r.m.W././././././././././././././././././././././././././././././././././.^.^.G./ L P P G A A D V V N m m b b # ).).).).",
+").).).).). - n.b.M.n.n.m.m.B.m.C.B.C.C.A.A.A.F.= B.A.F.F.A.A.F.A.A.A.C.C.B.C.B.N.M.M.m.m.c.c.c.x.l.l.k.k.g.g.g.f.f.t.r.e.e.J.E././././././././././././././././././././././././././././././././.^.*.E E L L P G A A V V V N m N N b : ).).).).",
+").).).).). e.b.b.s.n.n.m.m.m.B.B.B.C.C.C.C.A.A.* F.F.A.A.A.A.A.A.C.C.B.C.C.m.B.M.M.m.m.c.m.c.x.x.x.l.k.k.g.t.t.f.t.e.e.e.e.9.9.A.R././././././././././././././././././././././././././././././.Q.r.E L L L P G K A A V N N m b b b g ).).).).",
+").).).). % x.l.x.c.c.c.M.m.m.M.M.N.N.C.C.C.C.C.C.D.D.A.A.A.A.A.A.C.A.C.C.C.N.C.N.N.M.M.M.m.m.c.m.l.c.x.x.k.k.k.g.g.g.t.t.t.r.e.e.q.9.8.8.8.m.R./././././././././././././././././././././././././././././.T.Q L L L K K D V V C V N m m b c c % ).).).",
+").).).). 2 l.x.x.x.c.c.c.m.m.m.M.M.N.N.N.C.N.C.N.C.C.C.C.C.C.C.C.C.C.C.C.C.C.N.N.M.m.m.m.m.m.c.v.v.c.l.k.k.k.g.g.y.y.t.t.r.e.e.q.9.9.8.8.4.2.4.l.T././././././././././././././././././././././././././././.) E L J A A D V C V N N m m b c c > ).).).",
+").).).). T k.k.x.x.x.c.c.c.m.m.m.M.M.M.N.N.N.N.C.C.C.C.C.N.C.C.N.C.N.C.N.N.N.M.M.M.m.m.m.c.m.c.c.x.x.l.k.k.k.g.y.g.t.t.r.e.e.e.q.9.8.8.2.8.4.4.1.1.J.!./././././././././././././././././././././././././.!.N.L L L J D A A C V N m m m b c c f ).).).",
+").).).). O a.k.k.l.l.x.x.c.c.c.m.m.m.m.M.N.m.N.N.N.N.N.N.B.B.N.C.N.N.N.N.M.M.M.M.m.m.m.m.c.v.v.l.x.l.l.k.k.g.g.g.y.y.t.t.r.e.e.q.8.8.8.8.8.2.4.1.1.,.T./././././././././././././././././././././././././././.R.L L L A D A C C V N m b b b c c k + ).).",
+").).). * g.k.g.k.l.x.l.x.c.c.c.c.m.m.m.m.m.m.M.M.M.M.M.N.N.N.m.B.M.M.M.M.M.m.m.m.m.c.c.c.x.x.x.x.l.k.k.k.g.g.g.f.t.t.r.r.e.q.9.9.8.8.8.2.4.4.4.1.,.W././././././././././././././././././././././././././././.;.K K A D A V V V N N b m b c c k * ).).",
+").).). 2 k.g.k.k.k.l.l.l.x.x.c.c.m.v.m.m.m.m.m.m.M.M.M.M.M.m.m.m.m.m.m.m.M.m.m.c.m.c.c.c.x.x.x.l.k.k.k.g.k.t.y.t.t.r.r.e.e.q.9.8.8.8.2.8.4.1.1.1.e././././././././././././././././././././././././././././.W.P.K A G D A V V V N m m b c c c k > ).).",
+").).). O.t.g.g.k.k.k.l.l.x.x.x.c.c.v.c.c.m.m.m.m.m.M.M.m.m.m.m.m.m.m.m.m.c.m.c.c.c.c.x.x.l.x.k.k.k.k.g.g.t.f.t.t.t.e.e.e.q.9.9.8.8.4.2.4.4.1.1.;.A.!./././././././././././././././././././.Q././././././././.Q.U K A A A V V V m m m b c c k k g ).).",
+").).). X a.f.t.t.g.g.k.k.k.l.x.l.x.l.v.v.c.c.c.c.m.c.c.M.m.m.m.m.m.c.m.c.c.c.c.c.c.x.x.x.x.l.k.g.k.g.g.g.t.g.t.t.r.r.e.e.q.q.9.8.8.8.4.4.4.1.4.;.1.T././././././././././././././././././././.C.U.W./.W././././.Q.g.K A A V V V N m b b b c c k k k o ).).",
+").).). $ d.f.f.f.f.h.h.k.k.k.k.l.x.z.x.x.x.x.x.c.c.c.c.c.c.c.c.m.c.c.c.c.c.c.x.x.x.x.x.k.l.k.k.k.g.g.g.g.t.y.t.r.e.e.e.q.9.9.8.8.8.8.2.8.1.1.,.,.,.W./././././././././.W.^./././././././.^.T./ ! ! *.C.T./.Q.E.!.T.D A A V V V N b N v v c c k k j $ ).).",
+").). = r.r.f.f.f.f.h.f.k.k.k.k.z.k.z.z.z.x.x.x.x.c.c.c.v.v.v.v.c.c.c.x.x.x.l.x.x.k.k.k.k.k.g.g.k.t.t.y.t.t.r.r.e.q.q.q.9.8.8.8.4.2.8.2.4.1.,.,.9./././././././.E.K.1.8.^./././././././.^.9./ / ! ! ! L Q 8.F.R.R.A A A V V N b N v N v c j k j j * ).",
+").). : r.r.t.t.f.f.f.f.g.g.k.k.k.k.x.k.l.x.x.l.x.x.x.x.x.x.l.x.x.x.x.l.l.x.l.l.z.k.g.k.k.g.g.g.g.t.t.t.r.r.r.r.q.q.9.q.8.8.8.4.2.2.2.2.1.,.,.,.A./././.!.Q.U.w.$. .o. .P./././././././.R./ / ! ! E E E U K K K J D V C C V N b b b v v c j k j d ; ).",
+").). Y e.r.r.t.t.f.f.f.t.g.g.g.k.g.k.k.k.l.l.l.Y ; ; = & s.x.l.l.l.l.l.l.l.k.k.g.k.k.g.g.k.t.t.y.y.t.t.r.r.r.w.q.q.8.8.8.8.8.2.4.8.1.1.,.,.,.;.T././.T.x.&.&.+.+. .o. .+.W././././././.d.! / W ! ! E L U K K A D D C V V V N N b b v c c j k j d , ).",
+").). T e.e.e.e.r.r.t.f.t.g.t.k.g.g.g.k.k.k.k.g.; r.l.l.l.h.k.k.k.k.k.k.k.g.g.g.t.y.y.g.t.t.r.r.r.r.q.q.q.8.8.8.8.4.2.8.4.4.1.,.>.1.;.;.9.A.;.*.&.$.$.$.o.o. . .[ k./././././.E.( / ! ! ! ! L E L L K K A A C C V N N m v v v v j c j d d f ).",
+").). { q.e.e.e.r.r.t.r.t.t.t.g.t.g.g.g.g.g.k.k.; i.h.h.k.k.k.k.k.g.g.g.g.g.g.y.t.g.y.t.t.r.r.w.w.w.q.q.q.8.8.8.4.4.4.4.4.1.1.,.1.>.;.;.*.*.*.*.$.$.+...o. . . . .[ T././././.v./ / ! ! E E E L K K K A A C C V V N m b N v v c z j j d d g ).",
+").). { 9.9.e.e.e.r.r.r.t.t.y.y.y.g.t.t.g.k.g.k.2 T h.h.h.h.f.k.g.g.g.t.t.y.g.t.t.t.t.r.r.r.r.w.q.q.q.8.8.8.8.8.2.4.4.1.1.1.,.,.;.;.;.-.-.*.$.$.+.+.+.o. . . . .[ [ ;./././.Q.) / / ! Q ! L L L U K K A A A C V V m m m v v c c j c j d d 9 ).",
+").). 5.8.9.9.q.e.e.e.r.r.r.t.t.y.y.t.g.t.t.t.t.T Y h.h.f.k.f.f.t.t.y.g.y.y.t.t.t.r.r.r.r.w.q.q.q.8.q.8.8.8.2.2.8.4.4.1.1.,.,.1.;.;.-.-.*.*.*.$.+.+.+.o. . . .[ [ [ [ K.!.!.B./ ! ! ! Q E E E L K A K A A V C V N m m b v c c z j j d d d 0 ).",
+").). 8.8.8.9.8.q.q.q.e.e.r.r.r.r.t.r.f.f.f.f.f.{ ; f.f.f.f.f.f.f.f.t.f.t.t.r.r.r.e.e.e.e.q.q.9.q.8.8.8.8.2.5.5.4.4.1.1.,.,.,.,.-.-.-.-.&.&.$.$.+....... . .| | ) | ) | E.!.[ / / ! Q Q E L L L K A K A A V V V N m b m v c c k k j d d p p ).",
+").). O 8.8.8.8.8.9.9.q.q.e.e.e.e.r.r.r.r.t.t.t.t.u. = f.f.f.t.f.f.t.t.t.t.r.r.r.e.e.e.e.q.q.9.9.8.8.8.8.8.8.8.2.2.1.1.1.,.,.;.;.;.-.-.&.-.&.$.$.$.+. ..... .| | | | ) ) ( ;.-.! ! ! ! Q E L E L L K A A C C V N N m m b b c c c k j d d d i i O ).",
+").). @ 2.4.8.8.8.8.9.9.9.q.q.e.e.e.e.e.r.r.r.r.r.r.O % t.t.t.t.r.r.r.r.r.r.r.e.e.e.e.q.q.9.9.9.8.8.8.8.8.2.2.2.5.2.1.1.,.,.;.,.;.-.-.-.&.&.$.$.$.+...+. . .| ` | ` ` | ) ) ( / / ! ! Q Q E E L L L A K A A C V V N m m m b c c k k j j d p p y + ).",
+").). @ 4.4.4.8.8.8.8.8.8.9.q.q.q.e.e.e.e.e.e.r.r.r.% + r.r.r.r.r.e.r.e.e.e.e.e.q.q.e.9.9.9.8.8.8.8.8.2.8.2.2.8.2.2.1.,.1.;.,.;.-.-.-.&.&.$.$.+.$. ... . . .| | ` ` ) ` ) ) / ( ! ! ! Q E L L L L G K A C C C V V N m b b c c c k k j d d p p i @ ).",
+").). O 4.4.4.2.8.8.8.8.8.8.9.9.9.9.q.q.q.e.e.e.e.e.* r.e.e.e.e.e.e.e.q.q.q.q.9.9.9.9.8.8.9.8.8.8.2.2.8.2.2.2.2.2.,.,.,.;.-.-.-.&.&.&.$.$.+.+..... . . .| | | | ) ) ` ( ( ( ! ! ! W E E E L L P G K A C C V N N m b m b c c k k d d d d p p y O ).",
+").). 4.1.4.8.2.2.8.8.8.8.8.8.8.9.8.9.9.q.q.q.q.e.: Y e.q.e.q.q.e.e.q.q.9.9.9.8.8.8.8.8.8.2.4.2.2.8.2.4.2.2.:.:.,.,.,.;.-.-.-.-.&.&.$.+.+....... . . .| [ ) | [ ) ) ( ( / / ! Q Q E L L L P G G A C C C V N m m b b c c c k k k d d p p y y ).",
+").). O.1.4.4.4.2.8.2.8.8.8.8.8.8.8.8.9.9.9.9.9.9.T O * : Y O.e.9.9.9.9.9.9.9.9.8.8.8.9.8.8.8.4.2.8.2.8.2.2.4.1.:.2.:.:.;.,.-.-.-.-.&.&.&.$.+.+......... .| | | ) | | ) ` ) ( ( ! ! ! Q E L L L P G G G A A C V V N m b m b c c k k k d d d p p p q ).",
+").). { 1.1.1.4.2.8.2.8.2.2.4.8.8.8.8.8.8.8.8.8.9.9.9.9.e.q.q.q.9.9.9.8.8.8.8.8.8.8.8.8.8.8.2.4.4.8.2.2.2.1.1.1.2.:.:.;.-.;.-.-.-.&.&.$.$.$.+...+... ... .| ) [ | ) ) ) ) ( ( ! ! ! ! Q E E P P G G D D C C C V N m m m b c c c k k d d d p p y 9 9 ).",
+").). { ,.,.1.1.2.2.2.2.2.8.2.8.2.8.8.2.9.8.8.8.8.8.8.8.9.9.9.6.9.6.6.9.8.8.8.8.2.8.8.2.4.4.8.2.2.2.2.2.2.,.,.,.,.;.;.;.;.;.;.&.&.&.$.$.$.$...........| | | ` | ) ) ) ) ( ^ ^ ! Q Q Q Q L E L L K A A V V V V V N m b b v c c c j j d d d i p w $ % ).",
+").). T ,.,.,.1.1.2.2.2.4.4.4.8.2.8.2.8.2.8.8.8.8.2.8.8.7.7.6.9.7.7.6.6.8.8.4.4.8.2.2.8.4.4.2.2.2.2.4.1.,.,.,.,.;.;.;.;.-.;.&.&.&.$.$.+.+...........| | ` | ` ` ` ` ( ( ( ( ^ ! ! Q Q L L L L L K A A D V V V V m m m b v c z j j j d d p p y = ).",
+").). Y ,.;.,.,.,.1.1.2.1.1.4.2.4.4.2.8.8.2.2.8.2.2.8.2.7.7.3.3.3.7.3.6.8.2.8.2.4.4.4.2.1.1.4.1.1.1.,.,.,.,.;.;.;.;.&.;.&.&.&.&.&.$.+.+......... .| | | | ` ` ` ) ( ( ( ^ ^ ! ! Q Q Q L L L G G K A V D V V V N N b b b c c j z j j d d p i - ).",
+").). : -.,.;.,.,.,.,.,.1.1.1.1.1.1.4.4.2.8.2.2.8.4.4.4.3.3.3.7.3.3.7.3.2.8.2.2.4.1.1.1.1.1.1.,.,.,.,.,.;.,.;.;.;.;.;.&.-.&.&.$.$.$.+. .......| | | | ` ` | ` ( ` ( ( ( ^ Q Q Q Q U U L L L G G A A A V V V N b b b b c c c c j j d d p p : ).",
+").). = -.;.;.,.,.,.,.,.,.,.,.,.1.1.4.1.2.2.2.2.2.8.2.2.4.4.4.4.7.3.6.3.2.2.2.2.2.1.1.1.,.,.,.,.,.;.,.-.;.;.-.;.;.&.&.&.&.&.$.$.$.+...+... . .| | | ` ` ` ` ` ` ( ( ^ ^ ! ! Q Q Q L U L P P D D D V V V V N m m b b c c z z j j d d d p - ).",
+").).). % -.-.-.;.-.-.,.,.,.,.,.,.,.,.1.1.2.2.2.2.2.2.2.2.2.2.2.1.1.1.3.1.2.1.1.1.1.,.,.,.,.,.,.;.;.,.;.;.-.;.-.&.&.;.&.&.$.$.$.+......... . .| | | ` ` | ` _ _ ( ( ^ ^ ^ ! Q Q Q L U L L P G D D D V V V V N m m m b c c z j c j d d i = ).).",
+").).). o $.&.-.-.-.-.;.;.,.-.,.,.,.,.,.,.,.,.2.:.2.,.:.2.1.1.1.1.,.1.1.,.,.,.,.,.,.,.,.,.,.-.,.,.;.-.-.-.;.&.-.&.&.&.$.$.$.+.+......... . .| | ` ` | ) ` _ _ ( ( ( ^ ^ Q Q Q U U U L K K G G D D V V V V N m m b b c c c z j j d d 0 & O o ).).",
+").).). g -.&.-.-.-.-.-.;.;.-.,.-.,.;.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.;.,.,.-.,.,.;.;.-.-.-.-.-.-.&.&.&.&.$.$.$.$.$....... . . .| | | ` | ) | ) ` ( ( ^ ^ ^ ! Q Q Q U L L K K K G D D B V V V V m m m b b c c k j j d d d - O , ).).",
+").).). 2 $.*.*.*.*.*.;.;.-.-.;.;.;.;.;.,.;.;.,.,.;.,.,.,.,.,.;.;.1.;.;.,.;.;.,.;.;.;.;.;.;.&.;.&.&.&.&.&.&.&.$.$.+.+.$........... .| | | [ [ | ) ) ) ) ) ) / / / ! ! E Q U U U K K K A A A C C V V N N N n N v c c c k c j d d p 0 $ + g , ).).",
+").).). * $.$.$.$.*.*.*.-.&.-.-.-.-.;.;.;.;.;.;.;.;.;.;.,.;.T : # 2 ;.;.;.;.;.;.-.-.-.-.&.;.&.;.&.;.&.&.&.&.$.+.+.+.| ........| .| | | | ) ) [ [ ) ( ( ) ( ! / ! ! E ! U U K K K K K A A A C C V N N m v n v v c c j k j d d p p i g O # 5 e = ).).",
+").).). + $.$.$.$.$.$.*.&.-.-.&.&.;.-.;.;.;.;.&.;.;.;.;.O.O $ -.-.-.-.-.-.-.-.-.&.&.;.&.&.&.&.&.$.$.$.+.$...+...........| | ` ` ` | | ) [ ) ) ) ( ( / ! ! ! Q ! E U L L L K A K A A V C V V N N b N v v v c j k j j d d p i y y < * 0 y e O ).).",
+").).).). T $.$.$.$.$.$.&.&.&.&.&.-.&.&.&.&.&.;.&.;.;.;.-.$ T -.-.-.-.&.&.-.&.-.&.&.&.$.$.$.$.$.+.+........... ...| | | | | ` | ) | ) ) ) ) ( ( / ! ! ! Q Q E L U L L K A K A A A C V V N m b N N v c c c j k j d d d i i y y y y e 8 g ).).).",
+").).).). > o.$.$.+.$.$.$.$.$.$.&.&.&.&.&.;.&.&.;.&.&.&.-.2 - -.&.-.&.&.&.&.&.$.$.$.$.$.$.+.+.+.| ............ .| | ) | ` | ` ) [ ) ) ) ( / ( ! ! ! ! Q Q Q L L L L K K K A A C V C V V N b b b v v c c k k j j d p p i y y y y y e e > ).).).",
+").).).). & o.o.o.o.$.+.+.$.$.$.$.$.$.&.$.&.$.$.&.&.&.&.$.{ o O.&.&.&.$.$.$.$.$.$.+.$.+.+.............| ....| | | | | | | ) | ) ) ) ) ) / ( ! ! ! Q Q Q E L E L L K A J A A A C C V N N b N b b v c z > 5 j d d p d p y y y y e e e 8 & ).).).",
+").).).).). W .o.o. .o...+.$.+.+.+.+.$.$.$.$.$.$.$.&.$.$.$.= 2 &.$.$.$.$.+.$.+.+.$.+.............| ....| | | | ) | | ` ) | ( ) ) ) / ( / ! ! ! Q Q Q L L L L L K K K A A C C C V V N m N b v c c , @ = j d d d i i i y y e y e e 5 ).).).",
+").).).).). : . . .o. ...........$.+.+.$.$.+.$.$.$.+.$.$.$.4 % $.+.+.+.+.$................. . .....| | ` | ` ` | ) ) | ) ` ) ( ( / / ! / ! ! Q Q Q Q L E L L L K A A A A C C V V N m m v v c > + g d d i i i y y y e e e 8 > ).).).).",
+").).).).). # | . . . . . .....+. ......... .$.+.$.+.$.+.+.+.@ 4 ....+...............| ...... .| | | | ` } } ` ) | ) ) ) ) ) / / / / ! ! ! Q Q Q L Q L K K K A K D V C C C V V N m m m 5 ; + * d p i y i i y y e e 8 8 $ ).).).).",
+").).).).).). 4 .| | . . . . .........+. .+............... .: * ..............| ......| | | | | | ` ` } ` ` _ ) ) ) ) ( ( ( / / ! ! ! Q Q Q E L L L L K K A K D D A V C V V N m m 3 = $ 0 p i i y e e e e e 8 g ).).).).).",
+").).).).).). - | | | | | | . . . . . .| .......... . ..... .W o 4 .. . . . . ...| | | | | ` ` ` ` ` } ` ` _ _ ) ) ) ) / / ( ! ! ! ! E W E E L L L L L L K A D A A V C N V N m m b % @ , i p i y y i y e e 8 8 8 - ).).).).).",
+").).).).).). W | ) [ [ | | | | | | . . . . . . . .| ... . .; O : R . . .| | | | | ` | ` | ` | | ` ` ` _ _ _ _ ) ( ( ( / / ! ! ! ! E ! E L E E L L L G A D D D A C C V N N m m b m , $ < d d p i y y y e i e e 8 7 9 O ).).).).).",
+").).).).).).). + 4 ) | | [ [ | | | | | | | | | | | | | | | | | W + : W | .| | | | | | | | ` | | ` ` _ ` ` _ _ _ ( _ ( ( ( ( ! / ! ! ! E ! E E L L L L L L G G G D D V V V V V N m m m b b 0 & < d d d p i y y y y e y e e 8 7 : ).).).).).).",
+").).).).).).). % 2 ( [ ) | ) | ) | | ` | | | | [ | | ` | | .) | | | | ) | ) | | ) ` ` ` ` ` ` ` _ _ _ _ _ _ _ ^ ^ ( / ! ! ! ! Q Q E E E E E L L G P G G G D B 1 3 C V V N m m b m b c c * o - g k d d d p p y y y y e e e 8 8 8 q O ).).).).).).",
+").).).).).).).). + ; 4 | | ) | ` ` ` ) | ) | [ | ) | ` | | [ | ) ) | | | ) | ) | ` ` ` _ _ _ _ _ ( _ ^ _ ^ ^ ^ ^ ! ! ! ! Q Q Q Q E E L L L L P G G G 3 : % * V V N m m m b b b c c 5 0 k k d d d p p i y y y y e e e 8 8 8 > ).).).).).).).",
+").).).).).).).). $ : W ` ` ` | ` ` | | ) ` | ` | ) | ) [ | | ) ) | ) | ) ) _ _ _ _ _ _ _ ( ( ( ^ ^ ^ ^ ^ ! ! ! ! Q Q E E L E L L L P 5 1 ; & o + N N m m m b b c c c k k k k d d d p p i i y i y e e e 8 8 8 w + ).).).).).).).",
+").).).).).).).).). o W ) ) [ ) [ [ ) ) | ) [ ) | [ ) | | ) | [ ) ) ) ) [ ) ) ) ( ( ) ( / / ^ ( ^ ^ ^ ! ^ Q Q Q Q Q Q L L U L K U K , f b N v n v c c c k k k k d d d d i i i y y y e e e e e 8 8 : ).).).).).).).",
+").).).).).).).).). * ( ) ) [ ) ) ) ) [ ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ( ( ) ) ) ( / / / / / ^ ^ ^ Q ! ! Q Q Q G U L L U L L L K K A f > N v N v c c c k k k k d d d p i i y y y y y e e e 8 7 8 5 o ).).).).).).).).",
+").).).).).).).).).). O 1 ( ( ( ( ) ) ) ) ) ) W $ % - ; 1 1 2 f f f W ( ( / / ( / / / ! / 5 f 1 1 > - - % % # : L Q L L K K K K K K V X # - < v b v n v v z z k k d d d d p p i y y y e y e e e 8 7 7 8 * ).).).).).).).).",
+").).).).).).).).).). > : % F / / / / / / / ( ) ) g P / / / ( / / ! / ! ! - - L L L L K J K A A A A % X # = < 5 N N b N b v c c c c j k k d d d p p i i y y i y e e e 8 7 7 7 < ).).).).).).).).).",
+").).).).).).).).).).). O G ! F ! / ! / ! / / ( ( ( ( 3 W / / ! ! ! ! ! ! ! ! ; % L L L K A A A D V A C < > 3 v V N b b N b b v v c c c z j z k d d d p p i y y y e e e e 8 8 7 7 8 q # ).).).).).).).).).).",
+").).).).).).).).).).). & ! ! ! ! ! ! ! ! / ! / ! ! , W ! 2 # 3 ! ! Q Q ! E ; + L G A K K D A D A V C V V V N N b N N b b b v c c c j z j j d d d p p i y y i y e y e e 8 7 7 7 8 - ).).).).).).).).).).",
+").).).).).).).).).).).). : ! Q Q Q ! ! ! ! ! ! ! ! 2 % @ g & @ G ! Q E E E ; % % - F G A A A A A C V V V V V N m N N b N v v c c c j j j j j d d d p p p y y y y e e e e 8 7 7 7 7 , ).).).).).).).).).).).",
+").).).).).).).).).).).).). 3 E Q Q Q Q ! Q Q Q Q Q ! Q Q Q G f 2 1 1 : - - O = E Q L L E 2 : 1 1 f f 9 L J A G G D D A A A C V V V V V N N m m b b b v v c c z z j c j j d j d p p p y y y y e y e e 8 8 7 7 7 g O ).).).).).).).).).).).",
+").).).).).).).).).).).).). + F E L E L Q E E E E ! Q Q Q Q Q Q Q Q Q U Q U 1 1 L L L L L L L K K K K A K K D D A D D V V V V V V N N m m m b m b b b c c c c j j j j d d p p p i y y y e e i e e e 7 7 7 7 w $ ).).).).).).).).).).).).",
+").).).).).).).).).).).).).). & F E E E L L E E L E L U L L Q L L L L L L U U * + G L L L L L K K K D K A A A V D A V V V V V V N N b m m m b b b c c c c k k j j j j j p d i p y y y y y y e e e 7 7 7 7 7 7 - ).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).). * G L L L E L E L L E L U U L Q L L Q U L L L J + * L L K K A A K D D A A A C D V V V V V V V N b N b b m b b b c c c c k k k d j d d p p p i y y y y y e e e e 8 7 7 8 8 8 > ).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).). - L L L L L L L L L L L L K L L L L L L K U K 1 < A K A K A A V D A A C C V V V V V N N N N b b N b b b b c c c k k k k d d d d d d p i i y y y y e y e e 8 7 7 7 8 8 , ).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).). : K K K K L G L P K K K K K K K L L K K K K K = o g D A A A D A A V V V C V V N N N m m m N b N v v v c c c c k k k d k d d d p p i i y i y y y e y e 8 8 7 7 7 7 7 < ).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).). , G G G G G G G K K K D K A K A K D A D A K N @ $ V V D V V V V A V V V V N N m m m b m v N v v v v z c j k k k k d d d d d p p y i y i y e e y e e 8 7 7 7 8 8 f o ).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).). : G D D D G D A A K A D D A K A K D D A A A 3 > D V V V V V V V V N N N m N m m b m b v v v v c c c j j c j j d d d d p p i y y y i e e e e e 8 8 7 7 7 7 7 < o ).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).). - V D V D D A A A A V D A V D V D V A C A C : + 3 V V V V V V V V N N N b m m b m b b b c v c c z c j j j j j d d d d p i p p y y y y i e y 8 e e 8 8 7 7 7 7 , ).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).). = B D V D A C A A A C A V C C C C C C C C V ; 5 N V N V V N N N b b N N v m b m b v c c c c z c z j j j j d d d d d i p p y y y y i y e e e 8 8 7 7 7 8 8 8 > ).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).). & N V V C V V C C V C V C C C V V V V V V V V N N N N N m N b N b b b N b b b c c c c c z j j j c j j d d d d i p p y p y y y y e e e e e 8 7 7 7 7 7 8 - ).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).). + 3 V V V V V V V V V V V V V N V N N N N N m b N N b m b b b N b v v c c c c c c k k j c j j j j d d d p p i i y y y y y e y e e e 8 8 8 7 7 7 7 9 % ).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).). > N N N N V N N N N N N N N m m m m m m m m b b b m b b b b c c c c k c c k k k k j j j j d d d p p p i i i y y y y e y e e e 8 8 7 7 7 7 7 < + ).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).). * 0 N m b N N b m m m m m m N m m b m b b N v b b b c c c c c c c k k k k k d k d d d d p j p p i y y y y i e i e y e e e 8 7 7 7 7 7 7 : ).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).). O , m b b b N b m m b m b b b b m b b v v v v c c c c c c j k k k k k k d d d d d d d p p p i i y y y e i e e e 8 e 8 8 7 7 7 7 7 f $ ).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).). % g b b b m b m b b b b b b c c c c c c c c c c k g k k k k k d k d d d d p d i p i i i i y y y i e y e e e 8 8 7 7 7 7 7 q - ).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).). X ; c v b b c c c b c c c c c c c c c k k k d = o f j k k d d d d d d p i i i i i y y y y i e e e e e e e 8 8 8 7 7 7 , + ).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).). O : 0 c c c c c c c c c c k k k k k k 0 & + 5 d d d p d d d p i i y y y y y y e y e e e e e 8 8 7 7 8 8 8 , $ ).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). O ; 0 c z j j c k k k k k k k k d @ # 9 d d p p i p i y i y y y y y e y y e e e 8 e 7 7 7 8 7 , $ ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). o - f j k k k k d d j k d d d : % 0 i p p y y i y y y y y y e y e e e e 8 8 7 7 7 9 > # ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). $ > 9 k d d k d d d d d d = % w y p y y y y y e e y e y e e e 8 8 8 7 w < * ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). & > g d d d d p d p i & * w y y y y y y e y e e e e 8 8 8 0 < - O ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). + = , 5 i p p i 6 % * w y e e e e e 8 e e 8 q < : % ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). % = : < f # = y e y g f < , - & @ ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). o ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).)."
+};
diff --git a/frontends/gtk/res/nl/credits.html b/frontends/gtk/res/nl/credits.html
new file mode 120000
index 000000000..9c983987a
--- /dev/null
+++ b/frontends/gtk/res/nl/credits.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/nl/credits.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/nl/licence.html b/frontends/gtk/res/nl/licence.html
new file mode 120000
index 000000000..8a10d2073
--- /dev/null
+++ b/frontends/gtk/res/nl/licence.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/nl/licence.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/nl/welcome.html b/frontends/gtk/res/nl/welcome.html
new file mode 120000
index 000000000..6b2ba7742
--- /dev/null
+++ b/frontends/gtk/res/nl/welcome.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/nl/welcome.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/options.gtk2.ui b/frontends/gtk/res/options.gtk2.ui
new file mode 100644
index 000000000..d5542ba5d
--- /dev/null
+++ b/frontends/gtk/res/options.gtk2.ui
@@ -0,0 +1,3004 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkDialog" id="dialogPreferences">
+ <property name="can_focus">False</property>
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">preferencesTitle</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">dialog</property>
+ <signal name="destroy" handler="nsgtk_preferences_dialogPreferences_destroy" swapped="no"/>
+ <signal name="delete-event" handler="nsgtk_preferences_dialogPreferences_deleteevent" swapped="no"/>
+ <signal name="response" handler="nsgtk_preferences_dialogPreferences_response" swapped="no"/>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="vbox_dialog">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="layout_style">edge</property>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label">gtk-help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="close">
+ <property name="label">gtk-close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkNotebook" id="notebook1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkVBox" id="vbox_main">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_main_startup">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label_startup_page">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesStartupPage</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryHomePageURL">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">â—</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <signal name="changed" handler="nsgtk_preferences_entryHomePageURL_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_entryHomePageURL_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkButton" id="setDefaultPage">
+ <property name="label" translatable="yes">preferencesStartupPageDefault</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="clicked" handler="nsgtk_preferences_setDefaultPage_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="setCurrentPage">
+ <property name="label" translatable="yes">preferencesStartupPageCurrent</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="clicked" handler="nsgtk_preferences_setCurrentPage_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label_main_startup">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesStartup</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_main_search">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkUrlSearch">
+ <property name="label" translatable="yes">preferencesSearchURLBar</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkUrlSearch_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkUrlSearch_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesSearchProvider</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboSearch">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">liststore_search_provider</property>
+ <signal name="changed" handler="nsgtk_preferences_comboSearch_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboSearch_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext1"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesSearch</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_main_downloads">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkClearDownloads">
+ <property name="label" translatable="yes">preferencesDownloadsRemove</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkClearDownloads_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkClearDownloads_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkRequestOverwrite">
+ <property name="label" translatable="yes">preferencesDownloadsConfirm</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkRequestOverwrite_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkRequestOverwrite_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesDownloadsLocation</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFileChooserButton" id="fileChooserDownloads">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action">select-folder</property>
+ <signal name="selection-changed" handler="nsgtk_preferences_fileChooserDownloads_selectionchanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_fileChooserDownloads_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesDownloads</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesMainTabtitle</property>
+ </object>
+ <packing>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox_appearance">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_appearance_tabs">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkShowSingleTab">
+ <property name="label" translatable="yes">preferencesTabsAlways</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkShowSingleTab_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkShowSingleTab_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkFocusNew">
+ <property name="label" translatable="yes">preferencesTabsSwitch</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkFocusNew_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkFocusNew_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkNewBlank">
+ <property name="label" translatable="yes">preferencesTabsNewly</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkNewBlank_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkNewBlank_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesTabsPosition</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboTabPosition">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">liststore_tab_position</property>
+ <signal name="changed" handler="nsgtk_preferences_comboTabPosition_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboTabPosition_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext2"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesTabs</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_appearance_tools">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesDeveloperView</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboboxDeveloperView">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">liststore_developer_view</property>
+ <signal name="changed" handler="nsgtk_preferences_comboDeveloperView_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboDeveloperView_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext10"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesTools</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_appearance_url">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkDisplayRecentURLs">
+ <property name="label" translatable="yes">preferencesURLbarDisplay</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkDisplayRecentURLs_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkDisplayRecentURLs_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesURLbar</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">7</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_appearance_toolbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label16">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesToolbarButtons</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboButtonType">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">liststore_toolbar_buttontype</property>
+ <signal name="changed" handler="nsgtk_preferences_comboButtonType_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboButtonType_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext3"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesToolbar</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesAppearanceTabtitle</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox_content">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_content_control">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkDisablePopups">
+ <property name="label" translatable="yes">preferencesControlPrevent</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkDisablePopups_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkDisablePopups_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkHideAdverts">
+ <property name="label" translatable="yes">preferencesControlHide</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkHideAdverts_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkHideAdverts_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkEnableJavascript">
+ <property name="label" translatable="yes">preferencesControlEnable</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkEnableJavascript_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkEnableJavascript_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkDisablePlugins">
+ <property name="label" translatable="yes">preferencesControlDisable</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkDisablePlugins_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkDisablePlugins_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkResampleImages">
+ <property name="label" translatable="yes">preferencesControlHigh</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkResampleImages_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkResampleImages_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label17">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesControlLoad</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboboxLoadImages">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">liststore_image_loading</property>
+ <signal name="changed" handler="nsgtk_preferences_comboboxLoadImages_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboboxLoadImages_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext4"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesControl</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_content_animation">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkEnableAnimations">
+ <property name="label" translatable="yes">preferencesAnimationEnable</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkEnableAnimations_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkEnableAnimations_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label19">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesAnimationMinimum</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinAnimationSpeed">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesAnimationMinimumTooltip</property>
+ <property name="invisible_char">â—</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_animation_time</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">if-valid</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinAnimationSpeed_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinAnimationSpeed_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label18">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesAnimation</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_content_fonts">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkHBox" id="hbox11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkHBox" id="hbox12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label26">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesFontsDefault</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboDefault">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">liststore_defaultfont</property>
+ <signal name="changed" handler="nsgtk_preferences_comboDefault_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboDefault_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext5"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label27">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesFontsSize</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinDefaultSize">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesFontsSizeTooltip</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">4</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_font_default_size</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinDefaultSize_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinDefaultSize_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="fontPreview">
+ <property name="label" translatable="yes">preferencesFontsPreview</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="image">image1</property>
+ <property name="use_underline">True</property>
+ <signal name="clicked" handler="nsgtk_preferences_fontPreview_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label20">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesFonts</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_content_font">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label21">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesLanguagePreferred</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboboxLanguage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesLanguagePreferredTooltip</property>
+ <property name="model">liststore_content_language</property>
+ <signal name="changed" handler="nsgtk_preferences_comboboxLanguage_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboboxLanguage_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext7">
+ <property name="xalign">0</property>
+ </object>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext8">
+ <property name="xalign">1</property>
+ </object>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label25">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesLanguage</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesContentTabtitle</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox_privacy">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_privacy_general">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment20">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkSendReferer">
+ <property name="label" translatable="yes">preferencesGeneralReferral</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkSendReferer_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkSendReferer_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkSendDNT">
+ <property name="label" translatable="yes">preferencesGeneralDNT</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkSendDNT_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkSendDNT_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label61">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesGeneral</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_privacy_history">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkHoverURLs">
+ <property name="label" translatable="yes">preferencesHistoryShow</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkHoverURLs_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkHoverURLs_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label28">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesHistoryRemember</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinHistoryAge">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">4</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_history_age</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">if-valid</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinHistoryAge_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinHistoryAge_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label29">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesHistoryDays</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label23">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesHistory</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_privacy_cache">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkTable" id="table3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label30">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesCacheMemory</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label31">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesCacheDisc</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label34">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesCacheExpire</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMemoryCacheSize">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">5</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_cache_memory_size</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMemoryCacheSize_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMemoryCacheSize_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinDiscCacheSize">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">5</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_cache_disc_size</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinDiscCacheSize_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinDiscCacheSize_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinDiscCacheAge">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">3</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_disc_cache_age</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinDiscCacheAge_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinDiscCacheAge_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label32">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label">MB</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label33">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label">MB</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label35">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesHistoryDays</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox17">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonCacheMaintenance">
+ <property name="label" translatable="yes">preferencesCacheMaintenance</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label24">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesCache</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label22">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesPrivacyTabtitle</property>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox_network">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_network_proxy">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkTable" id="table2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">5</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label42">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesProxyType</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label43">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesProxyHost</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label44">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesProxyUsername</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label45">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesProxyPassword</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboProxyType">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyTypeTooltip</property>
+ <property name="model">liststore_proxy_type</property>
+ <signal name="changed" handler="nsgtk_preferences_comboProxyType_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboProxyType_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext6"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox19">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkEntry" id="entryProxyHost">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyHostTooltip</property>
+ <property name="invisible_char">â—</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <signal name="changed" handler="nsgtk_preferences_entryProxyHost_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_entryProxyHost_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label46">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label">:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinProxyPort">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyPortTooltip</property>
+ <property name="max_length">5</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">5</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_proxy_port</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">if-valid</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinProxyPort_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinProxyPort_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryProxyUser">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyUsernameTooltip</property>
+ <property name="invisible_char">â—</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <signal name="changed" handler="nsgtk_preferences_entryProxyUser_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_entryProxyUser_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryProxyPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyPasswordTooltip</property>
+ <property name="visibility">False</property>
+ <property name="invisible_char">â—</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <signal name="changed" handler="nsgtk_preferences_entryProxyPassword_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_entryProxyPassword_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label55">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesProxyNoproxy</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryProxyNoproxy">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyNoproxyTooltip</property>
+ <property name="caps_lock_warning">False</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <signal name="changed" handler="nsgtk_preferences_entryProxyNoproxy_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_entryProxyNoproxy_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label37">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesProxy</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_network_fetching">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment16">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label39">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesFetchingMax</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label40">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesFetchingPerhost</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label41">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesFetchingCached</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMaxFetchers">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesFetchingMaxTooltip</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">3</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_fetching_max</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMaxFetchers_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMaxFetchers_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinFetchesPerHost">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesFetchingPerhostTooltip</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">3</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_fetching_perhost</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinFetchesPerHost_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinFetchesPerHost_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinCachedConnections">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesFetchingCachedTooltip.</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">3</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_fetching_cached</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinCachedConnections_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinCachedConnections_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label38">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesFetching</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label36">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesNetworkTabtitle</property>
+ </object>
+ <packing>
+ <property name="position">4</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox_pdfexport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_pdfexport_appearance">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment17">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkSuppressImages">
+ <property name="label" translatable="yes">preferencesAppearanceImages</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkSuppressImages_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkSuppressImages_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkRemoveBackgrounds">
+ <property name="label" translatable="yes">preferencesAppearanceBackground</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkRemoveBackgrounds_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkRemoveBackgrounds_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkFitPage">
+ <property name="label" translatable="yes">preferencesAppearanceScalefit</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkFitPage_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkFitPage_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label59">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesAppearanceScale</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinExportScale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">4</property>
+ <property name="xalign">1</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_pdf_scale</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinExportScale_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinExportScale_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label60">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label">%</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label48">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesAppearance</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_pdfexport_margins">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment18">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox16">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox22">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="label62">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesMarginsMeasurements</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="table4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox16">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label51">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Top</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMarginTop">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">5</property>
+ <property name="xalign">1</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMarginTop_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMarginTop_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox18">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label52">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Left</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMarginLeft">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">5</property>
+ <property name="xalign">1</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_pdf_lmargin</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMarginLeft_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMarginLeft_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox20">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label53">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Bottom</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMarginBottom">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">5</property>
+ <property name="xalign">1</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMarginBottom_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMarginBottom_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox21">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label54">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Right</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMarginRight">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">5</property>
+ <property name="xalign">1</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMarginRight_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMarginRight_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label49">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesMargins</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_pdfexport_generation">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment19">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">7</property>
+ <child>
+ <object class="GtkCheckButton" id="checkCompressPDF">
+ <property name="label" translatable="yes">preferencesGenerationCompressed</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkCompressPDF_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkCompressPDF_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkPasswordPDF">
+ <property name="label" translatable="yes">preferencesGenerationPassword</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkPasswordPDF_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkPasswordPDF_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label50">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesGeneration</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label47">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesPDFTabtitle</property>
+ </object>
+ <packing>
+ <property name="position">5</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="0">help</action-widget>
+ <action-widget response="-7">close</action-widget>
+ </action-widgets>
+ </object>
+ <object class="GtkListStore" id="liststore_search_provider">
+ <columns>
+ <!-- column-name Provider -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0">Google</col>
+ </row>
+ <row>
+ <col id="0">Yahoo!</col>
+ </row>
+ <row>
+ <col id="0">Microsoft Live</col>
+ </row>
+ <row>
+ <col id="0">Buisiness.com</col>
+ </row>
+ <row>
+ <col id="0">Omgili</col>
+ </row>
+ <row>
+ <col id="0">BBC News</col>
+ </row>
+ <row>
+ <col id="0">Ubuntu Packages</col>
+ </row>
+ <row>
+ <col id="0">Creative Commons</col>
+ </row>
+ <row>
+ <col id="0">Ask</col>
+ </row>
+ <row>
+ <col id="0">Answers</col>
+ </row>
+ <row>
+ <col id="0">Dictionary.com</col>
+ </row>
+ <row>
+ <col id="0">YouTube</col>
+ </row>
+ <row>
+ <col id="0">AeroMP3</col>
+ </row>
+ <row>
+ <col id="0">AOL</col>
+ </row>
+ <row>
+ <col id="0">Baidu</col>
+ </row>
+ <row>
+ <col id="0">Amazon</col>
+ </row>
+ <row>
+ <col id="0">Ebay</col>
+ </row>
+ <row>
+ <col id="0">IMBD</col>
+ </row>
+ <row>
+ <col id="0">ESPN</col>
+ </row>
+ <row>
+ <col id="0">Wikipedia</col>
+ </row>
+ <row>
+ <col id="0">DuckDuckGo</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_tab_position">
+ <columns>
+ <!-- column-name Position -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesTabLocTop</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesTabLocLeft</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesTabLocRight</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesTabLocBottom</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_toolbar_buttontype">
+ <columns>
+ <!-- column-name Type -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesButtonTypeSmall</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesButtonTypeLarge</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesButtonTypeLargeText</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesButtonTypeText</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_image_loading">
+ <columns>
+ <!-- column-name Type -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesImageLoadBoth</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesImageLoadFore</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesImageLoadBack</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesImageLoadNone</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_defaultfont">
+ <columns>
+ <!-- column-name Type -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesFonttypeSans</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesFonttypeSerif</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesFonttypeMonospace</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesFonttypeCursive</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesFonttypeFantasy</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">3</property>
+ <property name="stock">gtk-apply</property>
+ </object>
+ <object class="GtkListStore" id="liststore_proxy_type">
+ <columns>
+ <!-- column-name Type -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesProxyTypeDirect</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesProxyTypeManual</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesProxyTypeBasic</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesProxyTypeNLTM</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesProxyTypeSystem</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_animation_time">
+ <property name="value">0.10000000000000001</property>
+ <property name="lower">0.10000000000000001</property>
+ <property name="upper">10</property>
+ <property name="step_increment">0.10000000000000001</property>
+ <property name="page_increment">1</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_font_default_size">
+ <property name="value">16</property>
+ <property name="lower">1</property>
+ <property name="upper">99.900000000000006</property>
+ <property name="step_increment">0.10000000000000001</property>
+ <property name="page_increment">2</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_history_age">
+ <property name="value">28</property>
+ <property name="upper">999</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">28</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_cache_memory_size">
+ <property name="value">16</property>
+ <property name="upper">2048</property>
+ <property name="step_increment">4</property>
+ <property name="page_increment">16</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_cache_disc_size">
+ <property name="value">1024</property>
+ <property name="upper">4096</property>
+ <property name="step_increment">32</property>
+ <property name="page_increment">256</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_disc_cache_age">
+ <property name="value">28</property>
+ <property name="upper">999</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_proxy_port">
+ <property name="value">3128</property>
+ <property name="lower">1</property>
+ <property name="upper">65535</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_fetching_max">
+ <property name="value">10</property>
+ <property name="lower">1</property>
+ <property name="upper">100</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_fetching_perhost">
+ <property name="value">1</property>
+ <property name="lower">1</property>
+ <property name="upper">100</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_fetching_cached">
+ <property name="value">1</property>
+ <property name="upper">100</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_pdf_scale">
+ <property name="value">100</property>
+ <property name="lower">1</property>
+ <property name="upper">1000</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_pdf_lmargin">
+ <property name="upper">999</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkListStore" id="liststore_content_language">
+ <columns>
+ <!-- column-name Code -->
+ <column type="gchararray"/>
+ <!-- column-name Description -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">en</col>
+ <col id="1" translatable="yes">English</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_developer_view">
+ <columns>
+ <!-- column-name Type -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesDeveloperViewWindow</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesDeveloperViewTab</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesDeveloperViewEditor</col>
+ </row>
+ </data>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/options.gtk3.ui b/frontends/gtk/res/options.gtk3.ui
new file mode 100644
index 000000000..2a3516f09
--- /dev/null
+++ b/frontends/gtk/res/options.gtk3.ui
@@ -0,0 +1,3057 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkAdjustment" id="adjustment_animation_time">
+ <property name="lower">0.10000000000000001</property>
+ <property name="upper">10</property>
+ <property name="value">0.10000000000000001</property>
+ <property name="step_increment">0.10000000000000001</property>
+ <property name="page_increment">1</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_cache_disc_size">
+ <property name="upper">4096</property>
+ <property name="value">1024</property>
+ <property name="step_increment">4</property>
+ <property name="page_increment">16</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_cache_memory_size">
+ <property name="upper">1024</property>
+ <property name="value">16</property>
+ <property name="step_increment">4</property>
+ <property name="page_increment">16</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_disc_cache_age">
+ <property name="upper">999</property>
+ <property name="value">28</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_fetching_cached">
+ <property name="upper">100</property>
+ <property name="value">1</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_fetching_max">
+ <property name="lower">1</property>
+ <property name="upper">100</property>
+ <property name="value">10</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_fetching_perhost">
+ <property name="lower">1</property>
+ <property name="upper">100</property>
+ <property name="value">1</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_font_default_size">
+ <property name="lower">1</property>
+ <property name="upper">99.900000000000006</property>
+ <property name="value">16</property>
+ <property name="step_increment">0.10000000000000001</property>
+ <property name="page_increment">2</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_history_age">
+ <property name="upper">999</property>
+ <property name="value">28</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">28</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_pdf_lmargin">
+ <property name="upper">999</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_pdf_scale">
+ <property name="lower">1</property>
+ <property name="upper">1000</property>
+ <property name="value">100</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_proxy_port">
+ <property name="lower">1</property>
+ <property name="upper">65535</property>
+ <property name="value">3128</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkDialog" id="dialogPreferences">
+ <property name="can_focus">False</property>
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">preferencesTitle</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">dialog</property>
+ <signal name="destroy" handler="nsgtk_preferences_dialogPreferences_destroy" swapped="no"/>
+ <signal name="delete-event" handler="nsgtk_preferences_dialogPreferences_deleteevent" swapped="no"/>
+ <signal name="response" handler="nsgtk_preferences_dialogPreferences_response" swapped="no"/>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="vbox_dialog">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label">gtk-help</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="close">
+ <property name="label">gtk-close</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkNotebook" id="notebook1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkVBox" id="vbox_main">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_main_startup">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label_startup_page">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesStartupPage</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryHomePageURL">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesStartupPageTooltip</property>
+ <property name="invisible_char">â—</property>
+ <signal name="changed" handler="nsgtk_preferences_entryHomePageURL_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_entryHomePageURL_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkButton" id="setDefaultPage">
+ <property name="label" translatable="yes">preferencesStartupPageDefault</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <signal name="clicked" handler="nsgtk_preferences_setDefaultPage_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="setCurrentPage">
+ <property name="label" translatable="yes">preferencesStartupPageCurrent</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <signal name="clicked" handler="nsgtk_preferences_setCurrentPage_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label_main_startup">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesStartup</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_main_search">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkUrlSearch">
+ <property name="label" translatable="yes">preferencesSearchURLBar</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkUrlSearch_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkUrlSearch_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesSearchProvider</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboSearch">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">preferencesSearchProviderTooltip</property>
+ <property name="halign">start</property>
+ <property name="model">liststore_search_provider</property>
+ <signal name="changed" handler="nsgtk_preferences_comboSearch_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboSearch_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext1"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesSearch</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_main_downloads">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkClearDownloads">
+ <property name="label" translatable="yes">preferencesDownloadsRemove</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkClearDownloads_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkClearDownloads_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkRequestOverwrite">
+ <property name="label" translatable="yes">preferencesDownloadsConfirm</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkRequestOverwrite_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkRequestOverwrite_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesDownloadsLocation</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFileChooserButton" id="fileChooserDownloads">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">preferencesDownloadsLocationTooltip</property>
+ <property name="halign">start</property>
+ <property name="action">select-folder</property>
+ <signal name="selection-changed" handler="nsgtk_preferences_fileChooserDownloads_selectionchanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_fileChooserDownloads_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesDownloads</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesMainTabtitle</property>
+ </object>
+ <packing>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox_appearance">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_appearance_tabs">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkShowSingleTab">
+ <property name="label" translatable="yes">preferencesTabsAlways</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkShowSingleTab_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkShowSingleTab_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkFocusNew">
+ <property name="label" translatable="yes">preferencesTabsSwitch</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkFocusNew_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkFocusNew_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkNewBlank">
+ <property name="label" translatable="yes">preferencesTabsNewly</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkNewBlank_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkNewBlank_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesTabsPosition</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboTabPosition">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="model">liststore_tab_position</property>
+ <signal name="changed" handler="nsgtk_preferences_comboTabPosition_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboTabPosition_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext2"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesTabs</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_appearance_tools">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesDeveloperView</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboboxDeveloperView">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="model">liststore_developer_view</property>
+ <signal name="changed" handler="nsgtk_preferences_comboDeveloperView_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboDeveloperView_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext10"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesTools</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_appearance_url">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkDisplayRecentURLs">
+ <property name="label" translatable="yes">preferencesURLbarDisplay</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkDisplayRecentURLs_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkDisplayRecentURLs_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesURLbar</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">7</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_appearance_toolbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label16">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesToolbarButtons</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboButtonType">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="model">liststore_toolbar_buttontype</property>
+ <signal name="changed" handler="nsgtk_preferences_comboButtonType_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboButtonType_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext3"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesToolbar</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesAppearanceTabtitle</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox_content">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_content_control">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkDisablePopups">
+ <property name="label" translatable="yes">preferencesControlPrevent</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkDisablePopups_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkDisablePopups_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkHideAdverts">
+ <property name="label" translatable="yes">preferencesControlHide</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkHideAdverts_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkHideAdverts_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkEnableJavascript">
+ <property name="label" translatable="yes">preferencesControlEnable</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkEnableJavascript_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkEnableJavascript_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkDisablePlugins">
+ <property name="label" translatable="yes">preferencesControlDisable</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkDisablePlugins_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkDisablePlugins_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkResampleImages">
+ <property name="label" translatable="yes">preferencesControlHigh</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkResampleImages_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkResampleImages_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label17">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesControlLoad</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboboxLoadImages">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="model">liststore_image_loading</property>
+ <signal name="changed" handler="nsgtk_preferences_comboboxLoadImages_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboboxLoadImages_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext4"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesControl</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_content_animation">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkEnableAnimations">
+ <property name="label" translatable="yes">preferencesAnimationEnable</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkEnableAnimations_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkEnableAnimations_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label19">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesAnimationMinimum</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinAnimationSpeed">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesAnimationMinimumTooltip</property>
+ <property name="invisible_char">â—</property>
+ <property name="adjustment">adjustment_animation_time</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">if-valid</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinAnimationSpeed_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinAnimationSpeed_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label18">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesAnimation</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_content_fonts">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkHBox" id="hbox11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkHBox" id="hbox12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label26">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesFontsDefault</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboDefault">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">liststore_defaultfont</property>
+ <signal name="changed" handler="nsgtk_preferences_comboDefault_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboDefault_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext5"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label27">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesFontsSize</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinDefaultSize">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesFontsSizeTooltip</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">4</property>
+ <property name="adjustment">adjustment_font_default_size</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinDefaultSize_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinDefaultSize_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="fontPreview">
+ <property name="label" translatable="yes">preferencesFontsPreview</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <property name="image">image1</property>
+ <property name="use_underline">True</property>
+ <signal name="clicked" handler="nsgtk_preferences_fontPreview_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label20">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesFonts</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_content_font">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label21">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesLanguagePreferred</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboboxLanguage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesLanguagePreferredTooltip</property>
+ <property name="halign">start</property>
+ <property name="model">liststore_content_language</property>
+ <signal name="changed" handler="nsgtk_preferences_comboboxLanguage_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboboxLanguage_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext7">
+ <property name="xalign">0</property>
+ </object>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext8">
+ <property name="xalign">1</property>
+ </object>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label25">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesLanguage</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesContentTabtitle</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox_privacy">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_privacy_general">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment20">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkSendReferer">
+ <property name="label" translatable="yes">preferencesGeneralReferral</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkSendReferer_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkSendReferer_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkSendDNT">
+ <property name="label" translatable="yes">preferencesGeneralDNT</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkSendDNT_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkSendDNT_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label61">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesGeneral</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_privacy_history">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkHoverURLs">
+ <property name="label" translatable="yes">preferencesHistoryShow</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkHoverURLs_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkHoverURLs_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label28">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesHistoryRemember</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinHistoryAge">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">4</property>
+ <property name="adjustment">adjustment_history_age</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">if-valid</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinHistoryAge_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinHistoryAge_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label29">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesHistoryDays</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label23">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesHistory</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_privacy_cache">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkTable" id="table3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label30">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesCacheMemory</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label31">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesCacheDisc</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label34">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesCacheExpire</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMemoryCacheSize">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">5</property>
+ <property name="adjustment">adjustment_cache_memory_size</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMemoryCacheSize_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMemoryCacheSize_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinDiscCacheSize">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">5</property>
+ <property name="adjustment">adjustment_cache_disc_size</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinDiscCacheSize_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinDiscCacheSize_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinDiscCacheAge">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">3</property>
+ <property name="adjustment">adjustment_disc_cache_age</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinDiscCacheAge_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinDiscCacheAge_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label32">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">MB</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label33">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="no">MB</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label35">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesCacheDays</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox17">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonCacheMaintenance">
+ <property name="label" translatable="yes">preferencesCacheMaintenance</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label24">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesCache</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label22">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesPrivacyTabtitle</property>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox_network">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_network_proxy">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkTable" id="table2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">5</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label42">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesProxyType</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label43">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesProxyHost</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label44">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesProxyUsername</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label45">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesProxyPassword</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboProxyType">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyTypeTooltip</property>
+ <property name="model">liststore_proxy_type</property>
+ <signal name="changed" handler="nsgtk_preferences_comboProxyType_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboProxyType_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext6"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox19">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkEntry" id="entryProxyHost">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyHostTooltip</property>
+ <property name="invisible_char">â—</property>
+ <signal name="changed" handler="nsgtk_preferences_entryProxyHost_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_entryProxyHost_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label46">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="no">:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinProxyPort">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyPortTooltip</property>
+ <property name="max_length">5</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">5</property>
+ <property name="adjustment">adjustment_proxy_port</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">if-valid</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinProxyPort_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinProxyPort_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryProxyUser">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyUsernameTooltip</property>
+ <property name="invisible_char">â—</property>
+ <signal name="changed" handler="nsgtk_preferences_entryProxyUser_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_entryProxyUser_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryProxyPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyPasswordTooltip</property>
+ <property name="visibility">False</property>
+ <property name="invisible_char">â—</property>
+ <signal name="changed" handler="nsgtk_preferences_entryProxyPassword_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_entryProxyPassword_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label55">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesProxyNoproxy</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryProxyNoproxy">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyNoproxyTooltip</property>
+ <property name="caps_lock_warning">False</property>
+ <signal name="changed" handler="nsgtk_preferences_entryProxyNoproxy_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_entryProxyNoproxy_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label37">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesProxy</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_network_fetching">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment16">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label39">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesFetchingMax</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label40">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesFetchingPerhost</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label41">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesFetchingCached</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMaxFetchers">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesFetchingMaxTooltip</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">3</property>
+ <property name="adjustment">adjustment_fetching_max</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMaxFetchers_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMaxFetchers_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinFetchesPerHost">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesFetchingPerhostTooltip</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">3</property>
+ <property name="adjustment">adjustment_fetching_perhost</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinFetchesPerHost_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinFetchesPerHost_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinCachedConnections">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesFetchingCachedTooltip</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">3</property>
+ <property name="adjustment">adjustment_fetching_cached</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinCachedConnections_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinCachedConnections_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label38">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesFetching</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label36">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesNetworkTabtitle</property>
+ </object>
+ <packing>
+ <property name="position">4</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox_pdfexport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_pdfexport_appearance">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment17">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkSuppressImages">
+ <property name="label" translatable="yes">preferencesAppearanceImages</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkSuppressImages_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkSuppressImages_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkRemoveBackgrounds">
+ <property name="label" translatable="yes">preferencesAppearanceBackground</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkRemoveBackgrounds_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkRemoveBackgrounds_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkFitPage">
+ <property name="label" translatable="yes">preferencesAppearanceScalefit</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkFitPage_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkFitPage_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label59">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesAppearanceScale</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinExportScale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">4</property>
+ <property name="xalign">1</property>
+ <property name="adjustment">adjustment_pdf_scale</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinExportScale_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinExportScale_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label60">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="no">%</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label48">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesAppearance</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_pdfexport_margins">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment18">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox16">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox22">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="label62">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesMarginsMeasurements</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="table4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox16">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label51">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Top</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMarginTop">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">5</property>
+ <property name="xalign">1</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMarginTop_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMarginTop_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox18">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label52">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Left</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMarginLeft">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">5</property>
+ <property name="xalign">1</property>
+ <property name="adjustment">adjustment_pdf_lmargin</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMarginLeft_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMarginLeft_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox20">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label53">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Bottom</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMarginBottom">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">5</property>
+ <property name="xalign">1</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMarginBottom_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMarginBottom_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox21">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label54">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Right</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMarginRight">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">â—</property>
+ <property name="width_chars">5</property>
+ <property name="xalign">1</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMarginRight_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMarginRight_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label49">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesMargins</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_pdfexport_generation">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment19">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">7</property>
+ <child>
+ <object class="GtkCheckButton" id="checkCompressPDF">
+ <property name="label" translatable="yes">preferencesGenerationCompressed</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkCompressPDF_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkCompressPDF_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkPasswordPDF">
+ <property name="label" translatable="yes">preferencesGenerationPassword</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkPasswordPDF_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkPasswordPDF_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label50">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesGeneration</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label47">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesPDFTabtitle</property>
+ </object>
+ <packing>
+ <property name="position">5</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="0">help</action-widget>
+ <action-widget response="-7">close</action-widget>
+ </action-widgets>
+ </object>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">3</property>
+ <property name="stock">gtk-apply</property>
+ </object>
+ <object class="GtkListStore" id="liststore_content_language">
+ <columns>
+ <!-- column-name Code -->
+ <column type="gchararray"/>
+ <!-- column-name Description -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">en</col>
+ <col id="1" translatable="yes">English</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_defaultfont">
+ <columns>
+ <!-- column-name Type -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesFonttypeSans</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesFonttypeSerif</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesFonttypeMonospace</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesFonttypeCursive</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesFonttypeFantasy</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_developer_view">
+ <columns>
+ <!-- column-name Type -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesDeveloperViewWindow</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesDeveloperViewTab</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesDeveloperViewEditor</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_image_loading">
+ <columns>
+ <!-- column-name Type -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesImageLoadBoth</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesImageLoadFore</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesImageLoadBack</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesImageLoadNone</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_proxy_type">
+ <columns>
+ <!-- column-name Type -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesProxyTypeDirect</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesProxyTypeManual</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesProxyTypeBasic</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesProxyTypeNLTM</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesProxyTypeSystem</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_search_provider">
+ <columns>
+ <!-- column-name Provider -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="no">Google</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Yahoo!</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Microsoft Live</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Buisiness.com</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Omgili</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">BBC News</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Ubuntu Packages</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Creative Commons</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Ask</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Answers</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Dictionary.com</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">YouTube</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">AeroMP3</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">AOL</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Baidu</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Amazon</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Ebay</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">IMBD</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">ESPN</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Wikipedia</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">DuckDuckGo</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_tab_position">
+ <columns>
+ <!-- column-name Position -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesTabLocTop</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesTabLocLeft</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesTabLocRight</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesTabLocBottom</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_toolbar_buttontype">
+ <columns>
+ <!-- column-name Type -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesButtonTypeSmall</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesButtonTypeLarge</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesButtonTypeLargeText</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesButtonTypeText</col>
+ </row>
+ </data>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/password.gtk2.ui b/frontends/gtk/res/password.gtk2.ui
new file mode 100644
index 000000000..eb51e4f8f
--- /dev/null
+++ b/frontends/gtk/res/password.gtk2.ui
@@ -0,0 +1,415 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+ <object class="GtkWindow" id="wndPDFPassword">
+ <property name="title" translatable="yes">PDF Password</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER</property>
+ <property name="modal">True</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="icon_size">6</property>
+ <property name="icon_name">gtk-dialog-authentication</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.10000000149</property>
+ <property name="xpad">12</property>
+ <property name="ypad">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Owner password</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.899999976158</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">15</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryPDFOwnerPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">False</property>
+ <property name="max_length">20</property>
+ <property name="text" translatable="yes"/>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ <property name="activates_default">False</property>
+ <property name="width_chars">20</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox3">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Repeat password</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.899999976158</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">15</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryPDFOwnerPassword1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">False</property>
+ <property name="max_length">20</property>
+ <property name="text" translatable="yes"/>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ <property name="activates_default">False</property>
+ <property name="width_chars">20</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox4">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">User password</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.899999976158</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">15</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryPDFUserPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">False</property>
+ <property name="max_length">20</property>
+ <property name="text" translatable="yes"/>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ <property name="activates_default">False</property>
+ <property name="width_chars">20</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox5">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Repeat password</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.899999976158</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">15</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryPDFUserPassword1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">False</property>
+ <property name="max_length">20</property>
+ <property name="text" translatable="yes"/>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ <property name="activates_default">False</property>
+ <property name="width_chars">20</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHButtonBox" id="hbuttonbox1">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <property name="spacing">10</property>
+ <child>
+ <object class="GtkButton" id="buttonPDFSetPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <child>
+ <object class="GtkHBox" id="hbox7">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkImage" id="image7">
+ <property name="visible">True</property>
+ <property name="stock">gtk-dialog-authentication</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Use these as passwords</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonPDFNoPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+ <child>
+ <object class="GtkHBox" id="hbox6">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkImage" id="image8">
+ <property name="visible">True</property>
+ <property name="stock">gtk-cancel</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Do not set any passwords</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/password.gtk3.ui b/frontends/gtk/res/password.gtk3.ui
new file mode 100644
index 000000000..eb51e4f8f
--- /dev/null
+++ b/frontends/gtk/res/password.gtk3.ui
@@ -0,0 +1,415 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+ <object class="GtkWindow" id="wndPDFPassword">
+ <property name="title" translatable="yes">PDF Password</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER</property>
+ <property name="modal">True</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="icon_size">6</property>
+ <property name="icon_name">gtk-dialog-authentication</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.10000000149</property>
+ <property name="xpad">12</property>
+ <property name="ypad">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Owner password</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.899999976158</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">15</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryPDFOwnerPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">False</property>
+ <property name="max_length">20</property>
+ <property name="text" translatable="yes"/>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ <property name="activates_default">False</property>
+ <property name="width_chars">20</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox3">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Repeat password</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.899999976158</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">15</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryPDFOwnerPassword1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">False</property>
+ <property name="max_length">20</property>
+ <property name="text" translatable="yes"/>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ <property name="activates_default">False</property>
+ <property name="width_chars">20</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox4">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">User password</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.899999976158</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">15</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryPDFUserPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">False</property>
+ <property name="max_length">20</property>
+ <property name="text" translatable="yes"/>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ <property name="activates_default">False</property>
+ <property name="width_chars">20</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox5">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Repeat password</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.899999976158</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">15</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryPDFUserPassword1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">False</property>
+ <property name="max_length">20</property>
+ <property name="text" translatable="yes"/>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ <property name="activates_default">False</property>
+ <property name="width_chars">20</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHButtonBox" id="hbuttonbox1">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <property name="spacing">10</property>
+ <child>
+ <object class="GtkButton" id="buttonPDFSetPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <child>
+ <object class="GtkHBox" id="hbox7">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkImage" id="image7">
+ <property name="visible">True</property>
+ <property name="stock">gtk-dialog-authentication</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Use these as passwords</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonPDFNoPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+ <child>
+ <object class="GtkHBox" id="hbox6">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkImage" id="image8">
+ <property name="visible">True</property>
+ <property name="stock">gtk-cancel</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Do not set any passwords</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/quirks.css b/frontends/gtk/res/quirks.css
new file mode 120000
index 000000000..88aabe48c
--- /dev/null
+++ b/frontends/gtk/res/quirks.css
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/Quirks,f79 \ No newline at end of file
diff --git a/frontends/gtk/res/ssl.gtk2.ui b/frontends/gtk/res/ssl.gtk2.ui
new file mode 100644
index 000000000..90f449ddd
--- /dev/null
+++ b/frontends/gtk/res/ssl.gtk2.ui
@@ -0,0 +1,202 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+ <object class="GtkDialog" id="wndSSLProblem">
+ <property name="border_width">1</property>
+ <property name="title" translatable="yes">SSL certificate problem</property>
+ <property name="modal">True</property>
+ <property name="default_width">500</property>
+ <property name="default_height">250</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox3">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkHBox" id="hbox15">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkImage" id="image6">
+ <property name="visible">True</property>
+ <property name="yalign">0</property>
+ <property name="icon_size">6</property>
+ <property name="icon_name">gtk-dialog-warning</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox13">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label62">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">NetSurf failed to verify the authenticity of an SSL certificate. Please verify the details presented below.</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame13">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <property name="label_xalign">0</property>
+ <child>
+ <object class="GtkAlignment" id="alignment17">
+ <property name="visible">True</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkScrolledWindow" id="SSLScrolled">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <child>
+ <object class="GtkViewport" id="SSLViewport">
+ <property name="visible">True</property>
+ <property name="resize_mode">GTK_RESIZE_QUEUE</property>
+ <child>
+ <object class="GtkDrawingArea" id="SSLDrawingArea">
+ <property name="visible">True</property>
+ <property name="app_paintable">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label63">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Certificate chain&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area3">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <object class="GtkButton" id="sslreject">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment16">
+ <property name="visible">True</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <child>
+ <object class="GtkHBox" id="hbox14">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkImage" id="image5">
+ <property name="visible">True</property>
+ <property name="stock">gtk-cancel</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label61">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Reject</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="sslaccept">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment15">
+ <property name="visible">True</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <child>
+ <object class="GtkHBox" id="hbox13">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkImage" id="image4">
+ <property name="visible">True</property>
+ <property name="stock">gtk-apply</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label60">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Accept</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">sslreject</action-widget>
+ <action-widget response="-5">sslaccept</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/ssl.gtk3.ui b/frontends/gtk/res/ssl.gtk3.ui
new file mode 100644
index 000000000..dace2a49e
--- /dev/null
+++ b/frontends/gtk/res/ssl.gtk3.ui
@@ -0,0 +1,181 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="stock">gtk-apply</property>
+ </object>
+ <object class="GtkImage" id="image3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="stock">gtk-cancel</property>
+ </object>
+ <object class="GtkDialog" id="wndSSLProblem">
+ <property name="can_focus">False</property>
+ <property name="border_width">5</property>
+ <property name="default_width">440</property>
+ <property name="default_height">260</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">dialog</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="sslreject">
+ <property name="label" translatable="yes">_Reject</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <property name="image">image3</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="sslaccept">
+ <property name="label" translatable="yes">_Accept</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <property name="image">image2</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_right">8</property>
+ <property name="stock">gtk-dialog-warning</property>
+ <property name="icon-size">6</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">NetSurf failed to verify the authenticity of an SSL certificate. Please verify the details presented below.</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkScrolledWindow" id="SSLScrolled">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkViewport" id="SSLViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkDrawingArea" id="SSLDrawingArea">
+ <property name="visible">True</property>
+ <property name="app_paintable">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK</property>
+ <property name="valign">start</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">&lt;b&gt;Certificate Chain&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">sslreject</action-widget>
+ <action-widget response="-5">sslaccept</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/tabcontents.gtk2.ui b/frontends/gtk/res/tabcontents.gtk2.ui
new file mode 100644
index 000000000..63e290e8b
--- /dev/null
+++ b/frontends/gtk/res/tabcontents.gtk2.ui
@@ -0,0 +1,92 @@
+<?xml version="1.0"?>
+<interface>
+ <!-- interface-requires gtk+ 2.12 -->
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkTable" id="tabContents">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <child>
+ <object class="GtkLayout" id="layout">
+ <property name="visible">True</property>
+ <property name="app_paintable">True</property>
+ <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK | GDK_SCROLL_MASK</property>
+ <property name="hadjustment">layouthadjustment</property>
+ <property name="vadjustment">layoutvadjustment</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkStatusbar" id="resizer">
+ <property name="height_request">1</property>
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHPaned" id="hpaned1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkLabel" id="status_bar">
+ <property name="width_request">1</property>
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Status</property>
+ </object>
+ <packing>
+ <property name="resize">False</property>
+ <property name="shrink">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHScrollbar" id="hscrollbar">
+ <property name="visible">True</property>
+ <property name="adjustment">layouthadjustment</property>
+ </object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVScrollbar" id="vscrollbar">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="adjustment">layoutvadjustment</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options"></property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkAdjustment" id="layouthadjustment">
+ <property name="upper">100</property>
+ <property name="step_increment">30</property>
+ <property name="page_increment">10</property>
+ <property name="page_size">10</property>
+ </object>
+ <object class="GtkAdjustment" id="layoutvadjustment">
+ <property name="upper">100</property>
+ <property name="step_increment">30</property>
+ <property name="page_increment">10</property>
+ <property name="page_size">10</property>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/tabcontents.gtk3.ui b/frontends/gtk/res/tabcontents.gtk3.ui
new file mode 100644
index 000000000..23328b3b7
--- /dev/null
+++ b/frontends/gtk/res/tabcontents.gtk3.ui
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkAdjustment" id="layouthadjustment">
+ <property name="upper">100</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="layoutvadjustment">
+ <property name="upper">100</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkGrid" id="tabContents">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">2</property>
+ <property name="column_spacing">2</property>
+ <child>
+ <object class="GtkLayout" id="layout">
+ <property name="visible">True</property>
+ <property name="app_paintable">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK | GDK_SCROLL_MASK</property>
+ <property name="hadjustment">layouthadjustment</property>
+ <property name="vadjustment">layoutvadjustment</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrollbar" id="vscrollbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="adjustment">layoutvadjustment</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkPaned" id="hpaned1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <child>
+ <object class="GtkLabel" id="status_bar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Status</property>
+ <property name="single_line_mode">True</property>
+ </object>
+ <packing>
+ <property name="resize">False</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrollbar" id="hscrollbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="adjustment">layouthadjustment</property>
+ </object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/throbber/throbber0.png b/frontends/gtk/res/throbber/throbber0.png
new file mode 100644
index 000000000..bfcb5d37f
--- /dev/null
+++ b/frontends/gtk/res/throbber/throbber0.png
Binary files differ
diff --git a/frontends/gtk/res/throbber/throbber1.png b/frontends/gtk/res/throbber/throbber1.png
new file mode 100644
index 000000000..a44b70d64
--- /dev/null
+++ b/frontends/gtk/res/throbber/throbber1.png
Binary files differ
diff --git a/frontends/gtk/res/throbber/throbber2.png b/frontends/gtk/res/throbber/throbber2.png
new file mode 100644
index 000000000..1bbdd8b4d
--- /dev/null
+++ b/frontends/gtk/res/throbber/throbber2.png
Binary files differ
diff --git a/frontends/gtk/res/throbber/throbber3.png b/frontends/gtk/res/throbber/throbber3.png
new file mode 100644
index 000000000..a62488ab8
--- /dev/null
+++ b/frontends/gtk/res/throbber/throbber3.png
Binary files differ
diff --git a/frontends/gtk/res/throbber/throbber4.png b/frontends/gtk/res/throbber/throbber4.png
new file mode 100644
index 000000000..4e685dcfb
--- /dev/null
+++ b/frontends/gtk/res/throbber/throbber4.png
Binary files differ
diff --git a/frontends/gtk/res/throbber/throbber5.png b/frontends/gtk/res/throbber/throbber5.png
new file mode 100644
index 000000000..72adf67d9
--- /dev/null
+++ b/frontends/gtk/res/throbber/throbber5.png
Binary files differ
diff --git a/frontends/gtk/res/throbber/throbber6.png b/frontends/gtk/res/throbber/throbber6.png
new file mode 100644
index 000000000..f7dcc2c0c
--- /dev/null
+++ b/frontends/gtk/res/throbber/throbber6.png
Binary files differ
diff --git a/frontends/gtk/res/throbber/throbber7.png b/frontends/gtk/res/throbber/throbber7.png
new file mode 100644
index 000000000..da9d8aee3
--- /dev/null
+++ b/frontends/gtk/res/throbber/throbber7.png
Binary files differ
diff --git a/frontends/gtk/res/throbber/throbber8.png b/frontends/gtk/res/throbber/throbber8.png
new file mode 100644
index 000000000..8505d1030
--- /dev/null
+++ b/frontends/gtk/res/throbber/throbber8.png
Binary files differ
diff --git a/frontends/gtk/res/toolbar.gtk2.ui b/frontends/gtk/res/toolbar.gtk2.ui
new file mode 100644
index 000000000..d84db5c8c
--- /dev/null
+++ b/frontends/gtk/res/toolbar.gtk2.ui
@@ -0,0 +1,189 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+ <object class="GtkWindow" id="toolbarwindow">
+ <property name="width_request">700</property>
+ <property name="height_request">450</property>
+ <property name="title" translatable="yes"/>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <child>
+ <object class="GtkVBox" id="windowvbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkLabel" id="toolbarlabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Move items from store to toolbar Rearrange items in toolbar Move items from toolbar to store</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+ <child>
+ <object class="GtkViewport" id="viewport1">
+ <property name="visible">True</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <child>
+ <object class="GtkVBox" id="widgetvbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="buttonhbox">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkButton" id="resetbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <child>
+ <object class="GtkHBox" id="button1hbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-refresh</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="refreshbuttonlabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Reset to defaults</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">10</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkButton" id="okbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="label">gtk-apply</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </object>
+ <packing>
+ <property name="padding">10</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancelbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/toolbar.gtk3.ui b/frontends/gtk/res/toolbar.gtk3.ui
new file mode 100644
index 000000000..d84db5c8c
--- /dev/null
+++ b/frontends/gtk/res/toolbar.gtk3.ui
@@ -0,0 +1,189 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+ <object class="GtkWindow" id="toolbarwindow">
+ <property name="width_request">700</property>
+ <property name="height_request">450</property>
+ <property name="title" translatable="yes"/>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <child>
+ <object class="GtkVBox" id="windowvbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkLabel" id="toolbarlabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Move items from store to toolbar Rearrange items in toolbar Move items from toolbar to store</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+ <child>
+ <object class="GtkViewport" id="viewport1">
+ <property name="visible">True</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <child>
+ <object class="GtkVBox" id="widgetvbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="buttonhbox">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkButton" id="resetbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <child>
+ <object class="GtkHBox" id="button1hbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-refresh</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="refreshbuttonlabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Reset to defaults</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">10</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkButton" id="okbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="label">gtk-apply</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </object>
+ <packing>
+ <property name="padding">10</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancelbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/viewdata.gtk2.ui b/frontends/gtk/res/viewdata.gtk2.ui
new file mode 100644
index 000000000..7589022ca
--- /dev/null
+++ b/frontends/gtk/res/viewdata.gtk2.ui
@@ -0,0 +1,204 @@
+<?xml version="1.0"?>
+<interface>
+ <!-- interface-requires gtk+ 2.12 -->
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkWindow" id="ViewDataWindow">
+ <property name="default_width">640</property>
+ <property name="default_height">480</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="menuitem1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_save_as">
+ <property name="label">gtk-save-as</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="S" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_print">
+ <property name="label">gtk-print</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="P" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="separatormenuitem1">
+ <property name="visible">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_close">
+ <property name="label">gtk-close</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu2">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_select_all">
+ <property name="label">gtk-select-all</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="A" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_cut">
+ <property name="label">gtk-cut</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_copy">
+ <property name="label">gtk-copy</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_paste">
+ <property name="label">gtk-paste</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_delete">
+ <property name="label">gtk-delete</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="Delete" signal="activate"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu4">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_zoom_in">
+ <property name="label">gtk-zoom-in</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="plus" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_zoom_out">
+ <property name="label">gtk-zoom-out</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="minus" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_zoom_normal">
+ <property name="label">gtk-zoom-100</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="0" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Help</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu3">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_about">
+ <property name="label">gtk-about</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="sourcescrolled">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkTextView" id="viewdata_view">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="pixels_above_lines">1</property>
+ <property name="pixels_below_lines">1</property>
+ <property name="editable">False</property>
+ <property name="wrap_mode">word</property>
+ <property name="left_margin">3</property>
+ <property name="right_margin">3</property>
+ <property name="accepts_tab">False</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/viewdata.gtk3.ui b/frontends/gtk/res/viewdata.gtk3.ui
new file mode 100644
index 000000000..b742d5f6b
--- /dev/null
+++ b/frontends/gtk/res/viewdata.gtk3.ui
@@ -0,0 +1,239 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkWindow" id="ViewDataWindow">
+ <property name="can_focus">False</property>
+ <property name="default_width">640</property>
+ <property name="default_height">480</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="menuitem1">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_save_as">
+ <property name="label">gtk-save-as</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_print">
+ <property name="label">gtk-print</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="separatormenuitem1">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_close">
+ <property name="label">gtk-close</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem2">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_select_all">
+ <property name="label">gtk-select-all</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_cut">
+ <property name="label">gtk-cut</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_copy">
+ <property name="label">gtk-copy</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_paste">
+ <property name="label">gtk-paste</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_delete">
+ <property name="label">gtk-delete</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem3">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_zoom_in">
+ <property name="label">gtk-zoom-in</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_zoom_out">
+ <property name="label">gtk-zoom-out</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_zoom_normal">
+ <property name="label">gtk-zoom-100</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem4">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Help</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_about">
+ <property name="label">gtk-about</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="sourcescrolled">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="window_placement">bottom-right</property>
+ <child>
+ <object class="GtkTextView" id="viewdata_view">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="pixels_above_lines">1</property>
+ <property name="pixels_below_lines">1</property>
+ <property name="editable">False</property>
+ <property name="wrap_mode">word</property>
+ <property name="left_margin">3</property>
+ <property name="right_margin">3</property>
+ <property name="accepts_tab">False</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/warning.gtk2.ui b/frontends/gtk/res/warning.gtk2.ui
new file mode 100644
index 000000000..e4fb4e662
--- /dev/null
+++ b/frontends/gtk/res/warning.gtk2.ui
@@ -0,0 +1,77 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+ <object class="GtkWindow" id="wndWarning">
+ <property name="title" translatable="yes">Warning from NetSurf</property>
+ <property name="window_position">GTK_WIN_POS_CENTER</property>
+ <property name="icon_name">gtk-dialog-warning</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="urgency_hint">True</property>
+ <child>
+ <object class="GtkVBox" id="vbox32">
+ <property name="visible">True</property>
+ <property name="border_width">2</property>
+ <child>
+ <object class="GtkHBox" id="hbox30">
+ <property name="visible">True</property>
+ <property name="border_width">3</property>
+ <child>
+ <object class="GtkImage" id="image519">
+ <property name="visible">True</property>
+ <property name="xpad">12</property>
+ <property name="icon_size">6</property>
+ <property name="icon_name">gtk-dialog-warning</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="labelWarning">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Help help help! I'm being held prisoner by a bunch of RISC OS zealots!</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="padding">1</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator2">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">3</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHButtonBox" id="hbuttonbox2">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <object class="GtkButton" id="button14">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <signal handler="gtk_widget_hide" name="clicked" object="wndWarning"/>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/warning.gtk3.ui b/frontends/gtk/res/warning.gtk3.ui
new file mode 100644
index 000000000..e4fb4e662
--- /dev/null
+++ b/frontends/gtk/res/warning.gtk3.ui
@@ -0,0 +1,77 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+ <object class="GtkWindow" id="wndWarning">
+ <property name="title" translatable="yes">Warning from NetSurf</property>
+ <property name="window_position">GTK_WIN_POS_CENTER</property>
+ <property name="icon_name">gtk-dialog-warning</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="urgency_hint">True</property>
+ <child>
+ <object class="GtkVBox" id="vbox32">
+ <property name="visible">True</property>
+ <property name="border_width">2</property>
+ <child>
+ <object class="GtkHBox" id="hbox30">
+ <property name="visible">True</property>
+ <property name="border_width">3</property>
+ <child>
+ <object class="GtkImage" id="image519">
+ <property name="visible">True</property>
+ <property name="xpad">12</property>
+ <property name="icon_size">6</property>
+ <property name="icon_name">gtk-dialog-warning</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="labelWarning">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Help help help! I'm being held prisoner by a bunch of RISC OS zealots!</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="padding">1</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator2">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">3</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHButtonBox" id="hbuttonbox2">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <object class="GtkButton" id="button14">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <signal handler="gtk_widget_hide" name="clicked" object="wndWarning"/>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/welcome.html b/frontends/gtk/res/welcome.html
new file mode 120000
index 000000000..1abdc5e8a
--- /dev/null
+++ b/frontends/gtk/res/welcome.html
@@ -0,0 +1 @@
+en/welcome.html \ No newline at end of file
diff --git a/frontends/gtk/resources.c b/frontends/gtk/resources.c
new file mode 100644
index 000000000..dfe3d3dad
--- /dev/null
+++ b/frontends/gtk/resources.c
@@ -0,0 +1,600 @@
+/*
+ * Copyright 2015 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * Implementation of gtk builtin resource handling.
+ *
+ * This presents a unified interface to the rest of the codebase to
+ * obtain resources. Note this is not anything to do with the resource
+ * scheme handling beyond possibly providing the underlying data.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "utils/filepath.h"
+
+#include "gtk/compat.h"
+#include "gtk/resources.h"
+
+/** log contents of gresource /org/netsource */
+#ifdef WITH_GRESOURCE
+#define SHOW_GRESOURCE
+#undef SHOW_GRESOURCE
+#endif
+
+#ifdef WITH_BUILTIN_PIXBUF
+#ifdef __GNUC__
+extern const guint8 menu_cursor_pixdata[] __attribute__ ((__aligned__ (4)));
+extern const guint8 favicon_pixdata[] __attribute__ ((__aligned__ (4)));
+extern const guint8 netsurf_pixdata[] __attribute__ ((__aligned__ (4)));
+#else
+extern const guint8 menu_cursor_pixdata[];
+extern const guint8 favicon_pixdata[];
+extern const guint8 netsurf_pixdata[];
+#endif
+#endif
+
+/** type of resource entry */
+enum nsgtk_resource_type_e {
+ NSGTK_RESOURCE_FILE, /**< entry is a file on disc */
+ NSGTK_RESOURCE_GLIB, /**< entry is a gresource accessed by path */
+ NSGTK_RESOURCE_DIRECT, /**< entry is a gresource accesed by gbytes */
+ NSGTK_RESOURCE_INLINE, /**< entry is compiled in accessed by pointer */
+};
+
+/** resource entry */
+struct nsgtk_resource_s {
+ const char *name;
+ unsigned int len;
+ enum nsgtk_resource_type_e type;
+ char *path;
+};
+
+#define RES_ENTRY(name) { name, sizeof((name)) - 1, NSGTK_RESOURCE_FILE, NULL }
+
+/** resources that are used for gtk builder */
+static struct nsgtk_resource_s ui_resource[] = {
+ RES_ENTRY("netsurf"),
+ RES_ENTRY("tabcontents"),
+ RES_ENTRY("password"),
+ RES_ENTRY("login"),
+ RES_ENTRY("ssl"),
+ RES_ENTRY("toolbar"),
+ RES_ENTRY("downloads"),
+ RES_ENTRY("history"),
+ RES_ENTRY("options"),
+ RES_ENTRY("hotlist"),
+ RES_ENTRY("cookies"),
+ RES_ENTRY("viewdata"),
+ RES_ENTRY("warning"),
+ { NULL, 0, NSGTK_RESOURCE_FILE, NULL },
+};
+
+/** resources that are used as pixbufs */
+static struct nsgtk_resource_s pixbuf_resource[] = {
+ RES_ENTRY("favicon.png"),
+ RES_ENTRY("netsurf.xpm"),
+ RES_ENTRY("menu_cursor.png"),
+ RES_ENTRY("arrow_down_8x32.png"),
+ RES_ENTRY("throbber/throbber0.png"),
+ RES_ENTRY("throbber/throbber1.png"),
+ RES_ENTRY("throbber/throbber2.png"),
+ RES_ENTRY("throbber/throbber3.png"),
+ RES_ENTRY("throbber/throbber4.png"),
+ RES_ENTRY("throbber/throbber5.png"),
+ RES_ENTRY("throbber/throbber6.png"),
+ RES_ENTRY("throbber/throbber7.png"),
+ RES_ENTRY("throbber/throbber8.png"),
+ { NULL, 0, NSGTK_RESOURCE_FILE, NULL },
+};
+
+/** resources that are used for direct data access */
+static struct nsgtk_resource_s direct_resource[] = {
+ RES_ENTRY("welcome.html"),
+ RES_ENTRY("credits.html"),
+ RES_ENTRY("licence.html"),
+ RES_ENTRY("maps.html"),
+ RES_ENTRY("default.css"),
+ RES_ENTRY("adblock.css"),
+ RES_ENTRY("internal.css"),
+ RES_ENTRY("quirks.css"),
+ RES_ENTRY("netsurf.png"),
+ RES_ENTRY("default.ico"),
+ RES_ENTRY("icons/arrow-l.png"),
+ RES_ENTRY("icons/content.png"),
+ RES_ENTRY("icons/directory2.png"),
+ RES_ENTRY("icons/directory.png"),
+ RES_ENTRY("icons/hotlist-add.png"),
+ RES_ENTRY("icons/hotlist-rmv.png"),
+ RES_ENTRY("icons/search.png"),
+ RES_ENTRY("languages"),
+ RES_ENTRY("Messages"),
+ { NULL, 0, NSGTK_RESOURCE_FILE, NULL },
+};
+
+
+/* exported interface documented in gtk/resources.h */
+GdkCursor *nsgtk_create_menu_cursor(void)
+{
+ GdkCursor *cursor = NULL;
+ GdkPixbuf *pixbuf;
+ nserror res;
+
+ res = nsgdk_pixbuf_new_from_resname("menu_cursor.png", &pixbuf);
+ if (res == NSERROR_OK) {
+ cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(),
+ pixbuf, 0, 3);
+ g_object_unref(pixbuf);
+ }
+
+ return cursor;
+}
+
+
+/**
+ * locate a resource
+ *
+ * The way GTK accesses resource files has changed greatly between
+ * releases. This initilises the interface that hides all the
+ * implementation details from the rest of the code.
+ *
+ * If the GResource is not enabled or the item cannot be found in the
+ * compiled in resources the files will be loaded directly from disc
+ * instead.
+ *
+ * \param respath A string vector containing the valid resource search paths
+ * \param resource A resource entry to initialise
+ */
+static nserror
+init_resource(char **respath, struct nsgtk_resource_s *resource)
+{
+ char *resname;
+#ifdef WITH_GRESOURCE
+ int resnamelen;
+ gboolean present;
+ const gchar * const *langv;
+ int langc = 0;
+
+ langv = g_get_language_names();
+
+ while (langv[langc] != NULL) {
+ resnamelen = snprintf(NULL, 0,
+ "/org/netsurf/%s/%s",
+ langv[langc], resource->name);
+
+ resname = malloc(resnamelen + 1);
+ if (resname == NULL) {
+ return NSERROR_NOMEM;
+ }
+ snprintf(resname, resnamelen + 1,
+ "/org/netsurf/%s/%s",
+ langv[langc], resource->name);
+
+ present = g_resources_get_info(resname,
+ G_RESOURCE_LOOKUP_FLAGS_NONE,
+ NULL, NULL, NULL);
+ if (present == TRUE) {
+ /* found an entry in the resources */
+ resource->path = resname;
+ resource->type = NSGTK_RESOURCE_GLIB;
+ LOG("Found gresource path %s", resource->path);
+ return NSERROR_OK;
+ }
+ /*LOG("gresource \"%s\" not found", resname);*/
+ free(resname);
+
+ langc++;
+ }
+ resnamelen = snprintf(NULL, 0, "/org/netsurf/%s", resource->name);
+
+ resname = malloc(resnamelen + 1);
+ if (resname == NULL) {
+ return NSERROR_NOMEM;
+ }
+ snprintf(resname, resnamelen + 1, "/org/netsurf/%s", resource->name);
+
+ present = g_resources_get_info(resname,
+ G_RESOURCE_LOOKUP_FLAGS_NONE,
+ NULL, NULL, NULL);
+ if (present == TRUE) {
+ /* found an entry in the resources */
+ resource->path = resname;
+ resource->type = NSGTK_RESOURCE_GLIB;
+ LOG("Found gresource path %s", resource->path);
+ return NSERROR_OK;
+ }
+ /*LOG("gresource \"%s\" not found", resname);*/
+ free(resname);
+
+#endif
+
+ resname = filepath_find(respath, resource->name);
+ if (resname == NULL) {
+ LOG("Unable to find resource %s on resource path",
+ resource->name);
+ return NSERROR_NOT_FOUND;
+ }
+
+ /* found an entry on the path */
+ resource->path = resname;
+ resource->type = NSGTK_RESOURCE_FILE;
+
+ LOG("Found file resource path %s", resource->path);
+ return NSERROR_OK;
+}
+
+/**
+ * locate and setup a direct resource
+ *
+ * Direct resources have general type of NSGTK_RESOURCE_GLIB but have
+ * g_resources_lookup_data() applied and the result stored so the data
+ * can be directly accessed without additional processing.
+ *
+ * \param respath A string vector containing the valid resource search paths
+ * \param resource A resource entry to initialise
+ */
+static nserror
+init_direct_resource(char **respath, struct nsgtk_resource_s *resource)
+{
+ nserror res;
+
+ res = init_resource(respath, resource);
+
+#ifdef WITH_GRESOURCE
+ if ((res == NSERROR_OK) &&
+ (resource->type == NSGTK_RESOURCE_GLIB)) {
+ /* found gresource we can convert */
+ GBytes *data;
+
+ data = g_resources_lookup_data(resource->path,
+ G_RESOURCE_LOOKUP_FLAGS_NONE,
+ NULL);
+ if (data != NULL) {
+ resource->type = NSGTK_RESOURCE_DIRECT;
+ resource->path = (char *)data;
+ }
+ }
+#endif
+
+ return res;
+}
+
+/**
+ * locate a pixbuf resource
+ *
+ * Pixbuf resources can be compiled inline
+ *
+ * \param respath A string vector containing the valid resource search paths
+ * \param resource A resource entry to initialise
+ */
+static nserror
+init_pixbuf_resource(char **respath, struct nsgtk_resource_s *resource)
+{
+#ifdef WITH_BUILTIN_PIXBUF
+ if (strncmp(resource->name, "menu_cursor.png", resource->len) == 0) {
+ resource->path = (char *)&menu_cursor_pixdata[0];
+ resource->type = NSGTK_RESOURCE_INLINE;
+ LOG("Found builtin for %s", resource->name);
+ return NSERROR_OK;
+ }
+
+ if (strncmp(resource->name, "netsurf.xpm", resource->len) == 0) {
+ resource->path = (char *)&netsurf_pixdata[0];
+ resource->type = NSGTK_RESOURCE_INLINE;
+ LOG("Found builtin for %s", resource->name);
+ return NSERROR_OK;
+ }
+
+ if (strncmp(resource->name, "favicon.png", resource->len) == 0) {
+ resource->path = (char *)&favicon_pixdata[0];
+ resource->type = NSGTK_RESOURCE_INLINE;
+ LOG("Found builtin for %s", resource->name);
+ return NSERROR_OK;
+ }
+#endif
+ return init_resource(respath, resource);
+}
+
+/**
+ * locate a ui resource
+ *
+ * UI resources need their resource name changing to account for gtk versions
+ *
+ * \param respath A string vector containing the valid resource search paths
+ * \param ui_res A resource entry to initialise
+ */
+static nserror init_ui_resource(char **respath, struct nsgtk_resource_s *ui_res)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ int gtkv = 3;
+#else
+ int gtkv = 2;
+#endif
+ int resnamelen;
+ char *resname;
+ struct nsgtk_resource_s resource;
+ nserror res;
+
+ resnamelen = ui_res->len + 10; /* allow for the expanded ui name */
+
+ resname = malloc(resnamelen);
+ if (resname == NULL) {
+ return NSERROR_NOMEM;
+ }
+ snprintf(resname, resnamelen, "%s.gtk%d.ui", ui_res->name, gtkv);
+ resource.name = resname;
+ resource.len = ui_res->len;
+ resource.path = NULL;
+
+ res = init_resource(respath, &resource);
+
+ ui_res->path = resource.path;
+ ui_res->type = resource.type;
+
+ free(resname);
+
+ return res;
+}
+
+/**
+ * Find a resource entry by name.
+ *
+ * \param resname The resource name to match.
+ * \param resource The list of resources entries to search.
+ */
+static struct nsgtk_resource_s *
+find_resource_from_name(const char *resname, struct nsgtk_resource_s *resource)
+{
+ /* find resource from name */
+ while ((resource->name != NULL) &&
+ ((resname[0] != resource->name[0]) ||
+ (strncmp(resource->name, resname, resource->len) != 0))) {
+ resource++;
+ }
+ return resource;
+}
+
+#ifdef SHOW_GRESOURCE
+/**
+ * Debug dump of all resources compile din via GResource.
+ */
+static void list_gresource(void)
+{
+ const char *nspath = "/org/netsurf";
+ char **reslist;
+ char **cur;
+ GError* gerror = NULL;
+ reslist = g_resources_enumerate_children(nspath,
+ G_RESOURCE_LOOKUP_FLAGS_NONE,
+ &gerror);
+ if (gerror) {
+ LOG("gerror %s", gerror->message);
+ g_error_free(gerror);
+
+ } else {
+ cur = reslist;
+ while (cur != NULL && *cur != NULL) {
+ LOG("gres %s", *cur);
+ cur++;
+ }
+ g_strfreev(reslist);
+ }
+}
+#endif
+
+/**
+ * Initialise UI resource table
+ *
+ */
+/* exported interface documented in gtk/resources.h */
+nserror nsgtk_init_resources(char **respath)
+{
+ struct nsgtk_resource_s *resource;
+ nserror res;
+
+#ifdef SHOW_GRESOURCE
+ list_gresource();
+#endif
+
+ /* iterate the ui resource table and initialise all its members */
+ resource = &ui_resource[0];
+ while (resource->name != NULL) {
+ res = init_ui_resource(respath, resource);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ resource++;
+ }
+
+ /* iterate the pixbuf resource table and initialise all its members */
+ resource = &pixbuf_resource[0];
+ while (resource->name != NULL) {
+ res = init_pixbuf_resource(respath, resource);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ resource++;
+ }
+
+ /* iterate the direct resource table and initialise all its members */
+ resource = &direct_resource[0];
+ while (resource->name != NULL) {
+ res = init_direct_resource(respath, resource);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ resource++;
+ }
+
+ return NSERROR_OK;
+}
+
+
+/* exported interface documented in gtk/resources.h */
+nserror
+nsgdk_pixbuf_new_from_resname(const char *resname, GdkPixbuf **pixbuf_out)
+{
+ struct nsgtk_resource_s *resource;
+ GdkPixbuf *new_pixbuf = NULL;
+ GError* error = NULL;
+
+ resource = find_resource_from_name(resname, &pixbuf_resource[0]);
+ if (resource->name == NULL) {
+ return NSERROR_NOT_FOUND;
+ }
+
+ switch (resource->type) {
+ case NSGTK_RESOURCE_FILE:
+ new_pixbuf = gdk_pixbuf_new_from_file(resource->path, &error);
+ break;
+
+ case NSGTK_RESOURCE_GLIB:
+#ifdef WITH_GRESOURCE
+ new_pixbuf = gdk_pixbuf_new_from_resource(resource->path, &error);
+#endif
+ break;
+
+ case NSGTK_RESOURCE_INLINE:
+#ifdef WITH_BUILTIN_PIXBUF
+ new_pixbuf = gdk_pixbuf_new_from_inline(-1, (const guint8 *)resource->path, FALSE, &error);
+#endif
+ break;
+
+ case NSGTK_RESOURCE_DIRECT:
+ /* pixbuf resources are not currently direct */
+ break;
+ }
+
+ if (new_pixbuf == NULL) {
+ if (error != NULL) {
+ LOG("Unable to create pixbuf from file for %s with path %s \"%s\"",
+ resource->name, resource->path, error->message);
+ g_error_free(error);
+ } else {
+ LOG("Unable to create pixbuf from file for %s with path %s",
+ resource->name, resource->path);
+ }
+ return NSERROR_INIT_FAILED;
+ }
+ *pixbuf_out = new_pixbuf;
+
+ return NSERROR_OK;
+}
+
+/* exported interface documented in gtk/resources.h */
+nserror
+nsgtk_builder_new_from_resname(const char *resname, GtkBuilder **builder_out)
+{
+ GtkBuilder *new_builder;
+ struct nsgtk_resource_s *ui_res;
+ GError* error = NULL;
+
+ ui_res = find_resource_from_name(resname, &ui_resource[0]);
+ if (ui_res->name == NULL) {
+ return NSERROR_NOT_FOUND;
+ }
+
+ new_builder = gtk_builder_new();
+
+ if (ui_res->type == NSGTK_RESOURCE_FILE) {
+ if (!gtk_builder_add_from_file(new_builder,
+ ui_res->path,
+ &error)) {
+ LOG("Unable to add UI builder from file for %s with path %s \"%s\"",
+ ui_res->name, ui_res->path, error->message);
+ g_error_free(error);
+ g_object_unref(G_OBJECT(new_builder));
+ return NSERROR_INIT_FAILED;
+ }
+ } else {
+ if (!nsgtk_builder_add_from_resource(new_builder,
+ ui_res->path,
+ &error)) {
+ LOG("Unable to add UI builder from resource for %s with path %s \"%s\"",
+ ui_res->name, ui_res->path, error->message);
+ g_error_free(error);
+ g_object_unref(G_OBJECT(new_builder));
+ return NSERROR_INIT_FAILED;
+ }
+ }
+
+ *builder_out = new_builder;
+
+ return NSERROR_OK;
+}
+
+/* exported interface documented in gtk/resources.h */
+nserror
+nsgtk_data_from_resname(const char *resname,
+ const uint8_t ** data_out,
+ size_t *data_size_out)
+{
+#ifdef WITH_GRESOURCE
+ struct nsgtk_resource_s *resource;
+ GBytes *data;
+ const gchar *buffer;
+ gsize buffer_length;
+
+ resource = find_resource_from_name(resname, &direct_resource[0]);
+ if ((resource->name == NULL) ||
+ (resource->type != NSGTK_RESOURCE_DIRECT)) {
+ return NSERROR_NOT_FOUND;
+ }
+
+ data = (GBytes *)resource->path;
+
+ buffer_length = 0;
+ buffer = g_bytes_get_data(data, &buffer_length);
+
+ if (buffer == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ *data_out = (const uint8_t *)buffer;
+ *data_size_out = (size_t)buffer_length;
+
+ return NSERROR_OK;
+#else
+ /** \todo consider adding compiled inline resources for things
+ * other than pixbufs.
+ */
+ return NSERROR_NOT_FOUND;
+#endif
+}
+
+/* exported interface documented in gtk/resources.h */
+nserror
+nsgtk_path_from_resname(const char *resname, const char **path_out)
+{
+ struct nsgtk_resource_s *resource;
+
+ resource = find_resource_from_name(resname, &direct_resource[0]);
+ if ((resource->name == NULL) ||
+ (resource->type != NSGTK_RESOURCE_FILE)) {
+ return NSERROR_NOT_FOUND;
+ }
+
+ *path_out = (const char *)resource->path;
+
+ return NSERROR_OK;
+}
diff --git a/frontends/gtk/resources.h b/frontends/gtk/resources.h
new file mode 100644
index 000000000..923031af4
--- /dev/null
+++ b/frontends/gtk/resources.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2006 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * Interface to gtk builtin resource handling.
+ *
+ * This presents a unified interface to the rest of the codebase to
+ * obtain resources. Note this is not anything to do with the resource
+ * scheme handling beyond possibly providing the underlying data.
+ *
+ */
+
+#ifndef NETSURF_GTK_RESOURCES_H
+#define NETSURF_GTK_RESOURCES_H 1
+
+/**
+ * Initialise GTK resources handling.
+ *
+ * Must be called before attempting to retrieve any resources but
+ * after logging is initialised as it logs.
+ *
+ * \param respath A string vector of paths to search for resources.
+ * \return NSERROR_OK if all resources were located else an
+ * appropriate error code.
+ */
+nserror nsgtk_init_resources(char **respath);
+
+/**
+ * Creates a menu cursor from internal resources.
+ *
+ * \return Cursor object or NULL on error.
+ */
+GdkCursor *nsgtk_create_menu_cursor(void);
+
+/**
+ * Create gtk builder object for the named ui resource.
+ *
+ * Creating gtk builder objects from a named resource requires the
+ * source xml resource to be parsed.
+ *
+ * This creates a gtk builder instance using an identifier name which
+ * is mapped to the ui_resource table which must be initialised with
+ * nsgtk_init_resources()
+ *
+ * \param resname The resource name to construct for
+ * \param builder_out The builder result
+ * \return NSERROR_OK and builder_out updated or appropriate error code
+ */
+nserror nsgtk_builder_new_from_resname(const char *resname, GtkBuilder **builder_out);
+
+
+/**
+ * Create gdk pixbuf for the named ui resource.
+ *
+ * This creates a pixbuf using an identifier name which is mapped to
+ * the ui_resource table which must be initialised with
+ * nsgtk_init_resources()
+ *
+ * \param resname The resource name to construct for
+ * \param pixbuf_out The pixbuf result
+ * \return NSERROR_OK and pixbuf_out updated or appropriate error code
+ */
+nserror nsgdk_pixbuf_new_from_resname(const char *resname, GdkPixbuf **pixbuf_out);
+
+/**
+ * Get direct pointer to resource data.
+ *
+ * For a named resource this obtains a direct acesss pointer to the
+ * data and its length.
+ *
+ * The data is read only through this pointer and remains valid until
+ * program exit.
+ *
+ * \param resname The resource name to obtain data for.
+ * \param data_out The resulting data.
+ * \param data_size_out The resulting data size.
+ * \return NSERROR_OK and data_out updated or appropriate error code.
+ */
+nserror nsgtk_data_from_resname(const char *resname, const uint8_t **data_out, size_t *data_size_out);
+
+/**
+ * Get path to resource data.
+ *
+ * For a named resource this obtains the on-disc path to that resource.
+ *
+ * The path is read only and remains valid untill program exit.
+ * \param resname The resource name to obtain path for.
+ * \param path_out The resulting data.
+ * \return NSERROR_OK and path_out updated or appropriate error code.
+ */
+nserror nsgtk_path_from_resname(const char *resname, const char **path_out);
+
+#endif
diff --git a/frontends/gtk/scaffolding.c b/frontends/gtk/scaffolding.c
new file mode 100644
index 000000000..777256703
--- /dev/null
+++ b/frontends/gtk/scaffolding.c
@@ -0,0 +1,2811 @@
+/*
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <gtk/gtk.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "utils/utils.h"
+#include "utils/dirent.h"
+#include "utils/messages.h"
+#include "utils/corestrings.h"
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "utils/file.h"
+#include "desktop/browser_history.h"
+#include "desktop/browser.h"
+#include "desktop/hotlist.h"
+#include "desktop/plotters.h"
+#include "desktop/print.h"
+#include "desktop/save_complete.h"
+#ifdef WITH_PDF_EXPORT
+#include "desktop/font_haru.h"
+#include "desktop/save_pdf.h"
+#endif
+#include "desktop/save_text.h"
+#include "desktop/searchweb.h"
+#include "desktop/textinput.h"
+#include "content/hlcache.h"
+
+#include "gtk/compat.h"
+#include "gtk/warn.h"
+#include "gtk/cookies.h"
+#include "gtk/completion.h"
+#include "gtk/preferences.h"
+#include "gtk/about.h"
+#include "gtk/viewsource.h"
+#include "gtk/bitmap.h"
+#include "gtk/gui.h"
+#include "gtk/history.h"
+#include "gtk/hotlist.h"
+#include "gtk/download.h"
+#include "gtk/menu.h"
+#include "gtk/plotters.h"
+#include "gtk/print.h"
+#include "gtk/search.h"
+#include "gtk/throbber.h"
+#include "gtk/toolbar.h"
+#include "gtk/window.h"
+#include "gtk/gdk.h"
+#include "gtk/scaffolding.h"
+#include "gtk/tabs.h"
+#include "gtk/schedule.h"
+#include "gtk/viewdata.h"
+#include "gtk/resources.h"
+#include "gtk/layout_pango.h"
+
+/** Macro to define a handler for menu, button and activate events. */
+#define MULTIHANDLER(q)\
+static gboolean nsgtk_on_##q##_activate(struct nsgtk_scaffolding *g);\
+static gboolean nsgtk_on_##q##_activate_menu(GtkMenuItem *widget, gpointer data)\
+{\
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;\
+ return nsgtk_on_##q##_activate(g);\
+}\
+static gboolean nsgtk_on_##q##_activate_button(GtkButton *widget, gpointer data)\
+{\
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;\
+ return nsgtk_on_##q##_activate(g);\
+}\
+static gboolean nsgtk_on_##q##_activate(struct nsgtk_scaffolding *g)
+
+/** Macro to define a handler for menu events. */
+#define MENUHANDLER(q)\
+static gboolean nsgtk_on_##q##_activate_menu(GtkMenuItem *widget, gpointer data)
+
+/** Macro to define a handler for button events. */
+#define BUTTONHANDLER(q)\
+static gboolean nsgtk_on_##q##_activate(GtkButton *widget, gpointer data)
+
+/** Core scaffolding structure. */
+struct nsgtk_scaffolding {
+ /** global linked list of scaffoldings for gui interface adjustments */
+ struct nsgtk_scaffolding *next, *prev;
+
+ /** currently active gui browsing context */
+ struct gui_window *top_level;
+
+ /** local history window */
+ struct gtk_history_window *history_window;
+
+ /** Builder object scaffold was created from */
+ GtkBuilder *builder;
+
+ /** scaffold container window */
+ GtkWindow *window;
+ bool fullscreen; /**< flag for the scaffold window fullscreen status */
+
+ /** tab widget holding displayed pages */
+ GtkNotebook *notebook;
+
+ /** entry widget holding the url of the current displayed page */
+ GtkWidget *url_bar;
+ GtkEntryCompletion *url_bar_completion; /**< Completions for url_bar */
+
+ /** Activity throbber */
+ GtkImage *throbber;
+ int throb_frame; /**< Current frame of throbber animation */
+
+ struct gtk_search *search;
+ /** Web search widget */
+ GtkWidget *webSearchEntry;
+
+ /** controls toolbar */
+ GtkToolbar *tool_bar;
+ struct nsgtk_button_connect *buttons[PLACEHOLDER_BUTTON];
+ int offset;
+ int toolbarmem;
+ int toolbarbase;
+ int historybase;
+
+ /** menu bar hierarchy */
+ struct nsgtk_bar_submenu *menu_bar;
+
+ /** right click popup menu hierarchy */
+ struct nsgtk_popup_menu *menu_popup;
+
+ /** link popup menu */
+ struct nsgtk_link_menu *link_menu;
+
+};
+
+/** current scaffold for model dialogue use */
+static struct nsgtk_scaffolding *scaf_current;
+
+/** global list for interface changes */
+static struct nsgtk_scaffolding *scaf_list = NULL;
+
+/** holds the context data for what's under the pointer, when the contextual
+ * menu is opened.
+ */
+static struct browser_window_features current_menu_features;
+
+
+/**
+ * Helper to hide popup menu entries by grouping
+ */
+static void popup_menu_hide(struct nsgtk_popup_menu *menu, bool submenu,
+ bool nav, bool cnp, bool custom)
+{
+ if (submenu){
+ gtk_widget_hide(GTK_WIDGET(menu->file_menuitem));
+ gtk_widget_hide(GTK_WIDGET(menu->edit_menuitem));
+ gtk_widget_hide(GTK_WIDGET(menu->view_menuitem));
+ gtk_widget_hide(GTK_WIDGET(menu->nav_menuitem));
+ gtk_widget_hide(GTK_WIDGET(menu->help_menuitem));
+
+ gtk_widget_hide(menu->first_separator);
+ }
+
+ if (nav) {
+ gtk_widget_hide(GTK_WIDGET(menu->back_menuitem));
+ gtk_widget_hide(GTK_WIDGET(menu->forward_menuitem));
+ gtk_widget_hide(GTK_WIDGET(menu->stop_menuitem));
+ gtk_widget_hide(GTK_WIDGET(menu->reload_menuitem));
+ }
+
+ if (cnp) {
+ gtk_widget_hide(GTK_WIDGET(menu->cut_menuitem));
+ gtk_widget_hide(GTK_WIDGET(menu->copy_menuitem));
+ gtk_widget_hide(GTK_WIDGET(menu->paste_menuitem));
+ }
+
+ if (custom) {
+ gtk_widget_hide(GTK_WIDGET(menu->customize_menuitem));
+ }
+
+}
+
+/**
+ * Helper to show popup menu entries by grouping
+ */
+static void popup_menu_show(struct nsgtk_popup_menu *menu, bool submenu,
+ bool nav, bool cnp, bool custom)
+{
+ if (submenu){
+ gtk_widget_show(GTK_WIDGET(menu->file_menuitem));
+ gtk_widget_show(GTK_WIDGET(menu->edit_menuitem));
+ gtk_widget_show(GTK_WIDGET(menu->view_menuitem));
+ gtk_widget_show(GTK_WIDGET(menu->nav_menuitem));
+ gtk_widget_show(GTK_WIDGET(menu->help_menuitem));
+
+ gtk_widget_show(menu->first_separator);
+ }
+
+ if (nav) {
+ gtk_widget_show(GTK_WIDGET(menu->back_menuitem));
+ gtk_widget_show(GTK_WIDGET(menu->forward_menuitem));
+ gtk_widget_show(GTK_WIDGET(menu->stop_menuitem));
+ gtk_widget_show(GTK_WIDGET(menu->reload_menuitem));
+ }
+
+ if (cnp) {
+ gtk_widget_show(GTK_WIDGET(menu->cut_menuitem));
+ gtk_widget_show(GTK_WIDGET(menu->copy_menuitem));
+ gtk_widget_show(GTK_WIDGET(menu->paste_menuitem));
+ }
+
+ if (custom) {
+ gtk_widget_show(GTK_WIDGET(menu->customize_menuitem));
+ }
+
+}
+
+
+/* event handlers and support functions for them */
+
+/**
+ * resource cleanup function for window destruction.
+ */
+static void scaffolding_window_destroy(GtkWidget *widget, gpointer data)
+{
+ struct nsgtk_scaffolding *gs = data;
+
+ LOG("scaffold:%p", gs);
+
+ if ((gs->history_window) && (gs->history_window->window)) {
+ gtk_widget_destroy(GTK_WIDGET(gs->history_window->window));
+ }
+
+ if (gs->prev != NULL) {
+ gs->prev->next = gs->next;
+ } else {
+ scaf_list = gs->next;
+ }
+ if (gs->next != NULL) {
+ gs->next->prev = gs->prev;
+ }
+
+ LOG("scaffold list head: %p", scaf_list);
+
+ if (scaf_list == NULL) {
+ /* no more open windows - stop the browser */
+ nsgtk_complete = true;
+ }
+}
+
+/* signal delivered on window delete event, allowing to halt close if
+ * download is in progress
+ */
+static gboolean scaffolding_window_delete_event(GtkWidget *widget,
+ GdkEvent *event, gpointer data)
+{
+ struct nsgtk_scaffolding *g = data;
+
+ if (nsgtk_check_for_downloads(GTK_WINDOW(widget)) == false) {
+ gtk_widget_destroy(GTK_WIDGET(g->window));
+ }
+ return TRUE;
+}
+
+/**
+ * Update the scaffoling button sensitivity, url bar and local history size
+ */
+static void scaffolding_update_context(struct nsgtk_scaffolding *g)
+{
+ int width, height;
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+
+ g->buttons[BACK_BUTTON]->sensitivity =
+ browser_window_history_back_available(bw);
+ g->buttons[FORWARD_BUTTON]->sensitivity =
+ browser_window_history_forward_available(bw);
+
+ nsgtk_scaffolding_set_sensitivity(g);
+
+ /* update the url bar, particularly necessary when tabbing */
+ browser_window_refresh_url_bar(bw);
+
+ /* update the local history window, as well as queuing a redraw
+ * for it.
+ */
+ browser_window_history_size(bw, &width, &height);
+ gtk_widget_set_size_request(GTK_WIDGET(g->history_window->drawing_area),
+ width, height);
+ gtk_widget_queue_draw(GTK_WIDGET(g->history_window->drawing_area));
+}
+
+/**
+ * Make the throbber run.
+ */
+static void nsgtk_throb(void *p)
+{
+ struct nsgtk_scaffolding *g = p;
+
+ if (g->throb_frame >= (nsgtk_throbber->nframes - 1))
+ g->throb_frame = 1;
+ else
+ g->throb_frame++;
+
+ gtk_image_set_from_pixbuf(g->throbber, nsgtk_throbber->framedata[
+ g->throb_frame]);
+
+ nsgtk_schedule(100, nsgtk_throb, p);
+}
+
+static guint nsgtk_scaffolding_update_edit_actions_sensitivity(
+ struct nsgtk_scaffolding *g)
+{
+ GtkWidget *widget = gtk_window_get_focus(g->window);
+ gboolean has_selection;
+
+ if (GTK_IS_EDITABLE(widget)) {
+ has_selection = gtk_editable_get_selection_bounds(
+ GTK_EDITABLE (widget), NULL, NULL);
+
+ g->buttons[COPY_BUTTON]->sensitivity = has_selection;
+ g->buttons[CUT_BUTTON]->sensitivity = has_selection;
+ g->buttons[PASTE_BUTTON]->sensitivity = true;
+ } else {
+ struct browser_window *bw =
+ nsgtk_get_browser_window(g->top_level);
+ browser_editor_flags edit_f =
+ browser_window_get_editor_flags(bw);
+
+ g->buttons[COPY_BUTTON]->sensitivity =
+ edit_f & BW_EDITOR_CAN_COPY;
+ g->buttons[CUT_BUTTON]->sensitivity =
+ edit_f & BW_EDITOR_CAN_CUT;
+ g->buttons[PASTE_BUTTON]->sensitivity =
+ edit_f & BW_EDITOR_CAN_PASTE;
+ }
+
+ nsgtk_scaffolding_set_sensitivity(g);
+ return ((g->buttons[COPY_BUTTON]->sensitivity) |
+ (g->buttons[CUT_BUTTON]->sensitivity) |
+ (g->buttons[PASTE_BUTTON]->sensitivity));
+}
+
+
+static void nsgtk_scaffolding_enable_edit_actions_sensitivity(
+ struct nsgtk_scaffolding *g)
+{
+
+ g->buttons[PASTE_BUTTON]->sensitivity = true;
+ g->buttons[COPY_BUTTON]->sensitivity = true;
+ g->buttons[CUT_BUTTON]->sensitivity = true;
+ nsgtk_scaffolding_set_sensitivity(g);
+
+ popup_menu_show(g->menu_popup, false, false, true, false);
+}
+
+/* signal handling functions for the toolbar, URL bar, and menu bar */
+static gboolean nsgtk_window_edit_menu_clicked(GtkWidget *widget,
+ struct nsgtk_scaffolding *g)
+{
+ nsgtk_scaffolding_update_edit_actions_sensitivity(g);
+
+ return TRUE;
+}
+
+static gboolean nsgtk_window_edit_menu_hidden(GtkWidget *widget,
+ struct nsgtk_scaffolding *g)
+{
+ nsgtk_scaffolding_enable_edit_actions_sensitivity(g);
+
+ return TRUE;
+}
+
+static gboolean nsgtk_window_popup_menu_hidden(GtkWidget *widget,
+ struct nsgtk_scaffolding *g)
+{
+ nsgtk_scaffolding_enable_edit_actions_sensitivity(g);
+ return TRUE;
+}
+
+gboolean nsgtk_window_url_activate_event(GtkWidget *widget, gpointer data)
+{
+ struct nsgtk_scaffolding *g = data;
+ nserror ret;
+ nsurl *url;
+
+ ret = search_web_omni(gtk_entry_get_text(GTK_ENTRY(g->url_bar)),
+ SEARCH_WEB_OMNI_NONE,
+ &url);
+ if (ret == NSERROR_OK) {
+ ret = browser_window_navigate(nsgtk_get_browser_window(g->top_level),
+ url, NULL, BW_NAVIGATE_HISTORY,
+ NULL, NULL, NULL);
+ nsurl_unref(url);
+ }
+ if (ret != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(ret), 0);
+ }
+
+ return TRUE;
+}
+
+/**
+ * update handler for URL entry widget
+ */
+gboolean
+nsgtk_window_url_changed(GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer data)
+{
+ return nsgtk_completion_update(GTK_ENTRY(widget));
+}
+
+/**
+ * Event handler for popup menu on toolbar.
+ */
+static gboolean nsgtk_window_tool_bar_clicked(GtkToolbar *toolbar,
+ gint x, gint y, gint button, gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+
+ /* set visibility for right-click popup menu */
+ popup_menu_hide(g->menu_popup, true, false, true, false);
+ popup_menu_show(g->menu_popup, false, false, false, true);
+
+ gtk_menu_popup(g->menu_popup->popup_menu, NULL, NULL, NULL, NULL, 0,
+ gtk_get_current_event_time());
+
+ return TRUE;
+}
+
+/**
+ * Update the menus when the number of tabs changes.
+ */
+static void nsgtk_window_tabs_add(GtkNotebook *notebook,
+ GtkWidget *page, guint page_num, struct nsgtk_scaffolding *g)
+{
+ gboolean visible = gtk_notebook_get_show_tabs(g->notebook);
+ g_object_set(g->menu_bar->view_submenu->tabs_menuitem, "visible", visible, NULL);
+ g_object_set(g->menu_popup->view_submenu->tabs_menuitem, "visible", visible, NULL);
+ g->buttons[NEXTTAB_BUTTON]->sensitivity = visible;
+ g->buttons[PREVTAB_BUTTON]->sensitivity = visible;
+ g->buttons[CLOSETAB_BUTTON]->sensitivity = visible;
+ nsgtk_scaffolding_set_sensitivity(g);
+}
+
+/**
+ * Update the menus when the number of tabs changes.
+ */
+static void
+nsgtk_window_tabs_remove(GtkNotebook *notebook,
+ GtkWidget *page,
+ guint page_num,
+ struct nsgtk_scaffolding *gs)
+{
+ /* if the scaffold is being destroyed it is not useful to
+ * update the state, futher many of the widgets may have
+ * already been destroyed.
+ */
+ if (gtk_widget_in_destruction(GTK_WIDGET(gs->window)) == TRUE) {
+ return;
+ }
+
+ /* if this is the last tab destroy the scaffold in addition */
+ if (gtk_notebook_get_n_pages(notebook) == 1) {
+ gtk_widget_destroy(GTK_WIDGET(gs->window));
+ return;
+ }
+
+ gboolean visible = gtk_notebook_get_show_tabs(gs->notebook);
+ g_object_set(gs->menu_bar->view_submenu->tabs_menuitem, "visible", visible, NULL);
+ g_object_set(gs->menu_popup->view_submenu->tabs_menuitem, "visible", visible, NULL);
+ gs->buttons[NEXTTAB_BUTTON]->sensitivity = visible;
+ gs->buttons[PREVTAB_BUTTON]->sensitivity = visible;
+ gs->buttons[CLOSETAB_BUTTON]->sensitivity = visible;
+ nsgtk_scaffolding_set_sensitivity(gs);
+}
+
+/**
+ * Handle opening a file path.
+ */
+static void nsgtk_openfile_open(const char *filename)
+{
+ struct browser_window *bw;
+ char *urltxt;
+ nsurl *url;
+ nserror error;
+
+ bw = nsgtk_get_browser_window(scaf_current->top_level);
+
+ urltxt = malloc(strlen(filename) + FILE_SCHEME_PREFIX_LEN + 1);
+
+ if (urltxt != NULL) {
+ sprintf(urltxt, FILE_SCHEME_PREFIX"%s", filename);
+
+ error = nsurl_create(urltxt, &url);
+ if (error != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(error), 0);
+ } else {
+ browser_window_navigate(bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ free(urltxt);
+ }
+}
+
+/* signal handlers for menu entries */
+
+MULTIHANDLER(newwindow)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ const char *addr;
+ nsurl *url;
+ nserror error;
+
+ if (nsoption_charp(homepage_url) != NULL) {
+ addr = nsoption_charp(homepage_url);
+ } else {
+ addr = NETSURF_HOMEPAGE;
+ }
+
+ error = nsurl_create(addr, &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ bw,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(error), 0);
+ }
+
+ return TRUE;
+}
+
+nserror nsgtk_scaffolding_new_tab(struct gui_window *gw)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(gw);
+ nsurl *url = NULL;
+ nserror error;
+
+ if (!nsoption_bool(new_blank)) {
+ const char *addr;
+ if (nsoption_charp(homepage_url) != NULL) {
+ addr = nsoption_charp(homepage_url);
+ } else {
+ addr = NETSURF_HOMEPAGE;
+ }
+ error = nsurl_create(addr, &url);
+ if (error != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(error), 0);
+ }
+ }
+
+ error = browser_window_create(BW_CREATE_HISTORY |
+ BW_CREATE_TAB,
+ url,
+ NULL,
+ bw,
+ NULL);
+ if (url != NULL) {
+ nsurl_unref(url);
+ }
+ return error;
+}
+
+MULTIHANDLER(newtab)
+{
+ nserror error;
+
+ error = nsgtk_scaffolding_new_tab(g->top_level);
+ if (error != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(error), 0);
+ }
+ return TRUE;
+}
+
+MULTIHANDLER(openfile)
+{
+ GtkWidget *dlgOpen;
+ gint response;
+
+ scaf_current = g;
+ dlgOpen = gtk_file_chooser_dialog_new("Open File",
+ scaf_current->window,
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NSGTK_STOCK_OPEN, GTK_RESPONSE_OK,
+ NULL, NULL);
+
+ response = gtk_dialog_run(GTK_DIALOG(dlgOpen));
+ if (response == GTK_RESPONSE_OK) {
+ gchar *filename;
+ filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlgOpen));
+
+ nsgtk_openfile_open((const char *)filename);
+
+ g_free(filename);
+ }
+
+ gtk_widget_destroy(dlgOpen);
+ return TRUE;
+}
+
+static gboolean nsgtk_filter_directory(const GtkFileFilterInfo *info,
+ gpointer data)
+{
+ DIR *d = opendir(info->filename);
+ if (d == NULL)
+ return FALSE;
+ closedir(d);
+ return TRUE;
+}
+
+MULTIHANDLER(savepage)
+{
+ if (!browser_window_has_content(nsgtk_get_browser_window(g->top_level)))
+ return FALSE;
+
+ GtkWidget *fc = gtk_file_chooser_dialog_new(
+ messages_get("gtkcompleteSave"), g->window,
+ GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER,
+ NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NSGTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+ DIR *d;
+ char *path;
+ nserror res;
+ GtkFileFilter *filter = gtk_file_filter_new();
+ gtk_file_filter_set_name(filter, "Directories");
+ gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME,
+ nsgtk_filter_directory, NULL, NULL);
+ gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fc), filter);
+ gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(fc), filter);
+
+ res = nsurl_nice(browser_window_get_url(
+ nsgtk_get_browser_window(g->top_level)), &path, false);
+ if (res != NSERROR_OK) {
+ path = strdup(messages_get("SaveText"));
+ if (path == NULL) {
+ nsgtk_warning("NoMemory", 0);
+ return FALSE;
+ }
+ }
+
+ if (access(path, F_OK) != 0)
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fc), path);
+ free(path);
+
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(fc),
+ TRUE);
+
+ if (gtk_dialog_run(GTK_DIALOG(fc)) != GTK_RESPONSE_ACCEPT) {
+ gtk_widget_destroy(fc);
+ return TRUE;
+ }
+
+ path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
+ d = opendir(path);
+ if (d == NULL) {
+ LOG("Unable to open directory %s for complete save: %s", path, strerror(errno));
+ if (errno == ENOTDIR)
+ nsgtk_warning("NoDirError", path);
+ else
+ nsgtk_warning("gtkFileError", path);
+ gtk_widget_destroy(fc);
+ g_free(path);
+ return TRUE;
+ }
+ closedir(d);
+ save_complete(browser_window_get_content(nsgtk_get_browser_window(
+ g->top_level)), path, NULL);
+ g_free(path);
+
+ gtk_widget_destroy(fc);
+
+ return TRUE;
+}
+
+
+MULTIHANDLER(pdf)
+{
+#ifdef WITH_PDF_EXPORT
+
+ GtkWidget *save_dialog;
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ struct print_settings *settings;
+ char filename[PATH_MAX];
+ char dirname[PATH_MAX];
+ char *url_name;
+ nserror res;
+
+ LOG("Print preview (generating PDF) started.");
+
+ res = nsurl_nice(browser_window_get_url(bw), &url_name, true);
+ if (res != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(res), 0);
+ return TRUE;
+ }
+
+ strncpy(filename, url_name, PATH_MAX);
+ strncat(filename, ".pdf", PATH_MAX - strlen(filename));
+ filename[PATH_MAX - 1] = '\0';
+
+ free(url_name);
+
+ strncpy(dirname, option_downloads_directory, PATH_MAX);
+ strncat(dirname, "/", PATH_MAX - strlen(dirname));
+ dirname[PATH_MAX - 1] = '\0';
+
+ /* this way the scale used by PDF functions is synchronized with that
+ * used by the all-purpose print interface
+ */
+ haru_nsfont_set_scale((float)option_export_scale / 100);
+
+ save_dialog = gtk_file_chooser_dialog_new("Export to PDF", g->window,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NSGTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(save_dialog),
+ dirname);
+
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_dialog),
+ filename);
+
+ if (gtk_dialog_run(GTK_DIALOG(save_dialog)) == GTK_RESPONSE_ACCEPT) {
+ gchar *filename = gtk_file_chooser_get_filename(
+ GTK_FILE_CHOOSER(save_dialog));
+
+ settings = print_make_settings(PRINT_OPTIONS,
+ (const char *) filename, &haru_nsfont);
+ g_free(filename);
+
+ if (settings == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ gtk_widget_destroy(save_dialog);
+ return TRUE;
+ }
+
+ /* This will clean up the print_settings object for us */
+ print_basic_run(browser_window_get_content(bw),
+ &pdf_printer, settings);
+ }
+
+ gtk_widget_destroy(save_dialog);
+
+#endif /* WITH_PDF_EXPORT */
+
+ return TRUE;
+}
+
+MULTIHANDLER(plaintext)
+{
+ if (!browser_window_has_content(nsgtk_get_browser_window(g->top_level)))
+ return FALSE;
+
+ GtkWidget *fc = gtk_file_chooser_dialog_new(
+ messages_get("gtkplainSave"), g->window,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NSGTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+ char *filename;
+ nserror res;
+
+ res = nsurl_nice(browser_window_get_url(
+ nsgtk_get_browser_window(g->top_level)),
+ &filename, false);
+ if (res != NSERROR_OK) {
+ filename = strdup(messages_get("SaveText"));
+ if (filename == NULL) {
+ nsgtk_warning("NoMemory", 0);
+ return FALSE;
+ }
+ }
+
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fc), filename);
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(fc),
+ TRUE);
+
+ free(filename);
+
+ if (gtk_dialog_run(GTK_DIALOG(fc)) == GTK_RESPONSE_ACCEPT) {
+ filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
+ save_as_text(browser_window_get_content(
+ nsgtk_get_browser_window(
+ g->top_level)), filename);
+ g_free(filename);
+ }
+
+ gtk_widget_destroy(fc);
+ return TRUE;
+}
+
+MULTIHANDLER(drawfile)
+{
+ return TRUE;
+}
+
+MULTIHANDLER(postscript)
+{
+ return TRUE;
+}
+
+MULTIHANDLER(printpreview)
+{
+ return TRUE;
+}
+
+
+MULTIHANDLER(print)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+
+ GtkPrintOperation *print_op;
+ GtkPageSetup *page_setup;
+ GtkPrintSettings *print_settings;
+ GtkPrintOperationResult res = GTK_PRINT_OPERATION_RESULT_ERROR;
+ struct print_settings *nssettings;
+ char *settings_fname = NULL;
+
+ print_op = gtk_print_operation_new();
+ if (print_op == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return TRUE;
+ }
+
+ /* use previously saved settings if any */
+ netsurf_mkpath(&settings_fname, NULL, 2, nsgtk_config_home, "Print");
+ if (settings_fname != NULL) {
+ print_settings = gtk_print_settings_new_from_file(settings_fname, NULL);
+ if (print_settings != NULL) {
+ gtk_print_operation_set_print_settings(print_op,
+ print_settings);
+
+ /* We're not interested in the settings any more */
+ g_object_unref(print_settings);
+ }
+ }
+
+ content_to_print = browser_window_get_content(bw);
+
+ page_setup = gtk_print_run_page_setup_dialog(g->window, NULL, NULL);
+ if (page_setup == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(settings_fname);
+ g_object_unref(print_op);
+ return TRUE;
+ }
+ gtk_print_operation_set_default_page_setup(print_op, page_setup);
+
+ nssettings = print_make_settings(PRINT_DEFAULT, NULL, nsgtk_layout_table);
+
+ g_signal_connect(print_op, "begin_print",
+ G_CALLBACK(gtk_print_signal_begin_print), nssettings);
+ g_signal_connect(print_op, "draw_page",
+ G_CALLBACK(gtk_print_signal_draw_page), NULL);
+ g_signal_connect(print_op, "end_print",
+ G_CALLBACK(gtk_print_signal_end_print), nssettings);
+
+ if (content_get_type(browser_window_get_content(bw)) !=
+ CONTENT_TEXTPLAIN) {
+ res = gtk_print_operation_run(print_op,
+ GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
+ g->window,
+ NULL);
+ }
+
+ /* if the settings were used save them for future use */
+ if (settings_fname != NULL) {
+ if (res == GTK_PRINT_OPERATION_RESULT_APPLY) {
+ /* Do not increment the settings reference */
+ print_settings =
+ gtk_print_operation_get_print_settings(print_op);
+
+ gtk_print_settings_to_file(print_settings,
+ settings_fname,
+ NULL);
+ }
+ free(settings_fname);
+ }
+
+ /* Our print_settings object is destroyed by the end print handler */
+ g_object_unref(page_setup);
+ g_object_unref(print_op);
+
+ return TRUE;
+}
+
+MULTIHANDLER(closewindow)
+{
+ gtk_widget_destroy(GTK_WIDGET(g->window));
+ return TRUE;
+}
+
+MULTIHANDLER(quit)
+{
+ struct nsgtk_scaffolding *gs;
+
+ if (nsgtk_check_for_downloads(g->window) == false) {
+ gs = scaf_list;
+ while (gs != NULL) {
+ gtk_widget_destroy(GTK_WIDGET(gs->window));
+ gs = gs->next;
+ }
+ }
+
+ return TRUE;
+}
+
+MENUHANDLER(savelink)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *) data;
+ struct gui_window *gui = g->top_level;
+ struct browser_window *bw = nsgtk_get_browser_window(gui);
+ nserror err;
+
+ if (current_menu_features.link == NULL)
+ return FALSE;
+
+ err = browser_window_navigate(bw,
+ current_menu_features.link,
+ NULL,
+ BW_NAVIGATE_DOWNLOAD,
+ NULL,
+ NULL,
+ NULL);
+ if (err != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(err), 0);
+ }
+
+ return TRUE;
+}
+
+/**
+ * Handler for opening new window from a link. attached to the popup menu.
+ */
+MENUHANDLER(link_openwin)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *) data;
+ struct gui_window *gui = g->top_level;
+ struct browser_window *bw = nsgtk_get_browser_window(gui);
+ nserror err;
+
+ if (current_menu_features.link == NULL)
+ return FALSE;
+
+ err = browser_window_create(BW_CREATE_CLONE | BW_CREATE_HISTORY,
+ current_menu_features.link, NULL, bw, NULL);
+ if (err != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(err), 0);
+ }
+
+ return TRUE;
+}
+
+/**
+ * Handler for opening new tab from a link. attached to the popup menu.
+ */
+MENUHANDLER(link_opentab)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *) data;
+ struct gui_window *gui = g->top_level;
+ struct browser_window *bw = nsgtk_get_browser_window(gui);
+ nserror err;
+
+ if (current_menu_features.link == NULL)
+ return FALSE;
+
+ temp_open_background = 1;
+
+ err = browser_window_create(BW_CREATE_CLONE |
+ BW_CREATE_HISTORY |
+ BW_CREATE_TAB,
+ current_menu_features.link, NULL, bw, NULL);
+ if (err != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(err), 0);
+ }
+
+ temp_open_background = -1;
+
+ return TRUE;
+}
+
+/**
+ * Handler for bookmarking a link. attached to the popup menu.
+ */
+MENUHANDLER(link_bookmark)
+{
+ if (current_menu_features.link == NULL)
+ return FALSE;
+
+ hotlist_add_url(current_menu_features.link);
+
+ return TRUE;
+}
+
+/**
+ * Handler for copying a link. attached to the popup menu.
+ */
+MENUHANDLER(link_copy)
+{
+ GtkClipboard *clipboard;
+
+ if (current_menu_features.link == NULL)
+ return FALSE;
+
+ clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+ gtk_clipboard_set_text(clipboard,
+ nsurl_access(current_menu_features.link), -1);
+
+ return TRUE;
+}
+
+
+MULTIHANDLER(cut)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ GtkWidget *focused = gtk_window_get_focus(g->window);
+
+ /* If the url bar has focus, let gtk handle it */
+ if (GTK_IS_EDITABLE (focused))
+ gtk_editable_cut_clipboard (GTK_EDITABLE(g->url_bar));
+ else
+ browser_window_key_press(bw, NS_KEY_CUT_SELECTION);
+
+ return TRUE;
+}
+
+MULTIHANDLER(copy)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ GtkWidget *focused = gtk_window_get_focus(g->window);
+
+ /* If the url bar has focus, let gtk handle it */
+ if (GTK_IS_EDITABLE (focused))
+ gtk_editable_copy_clipboard(GTK_EDITABLE(g->url_bar));
+ else
+ browser_window_key_press(bw, NS_KEY_COPY_SELECTION);
+
+ return TRUE;
+}
+
+MULTIHANDLER(paste)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ GtkWidget *focused = gtk_window_get_focus(g->window);
+
+ /* If the url bar has focus, let gtk handle it */
+ if (GTK_IS_EDITABLE (focused))
+ gtk_editable_paste_clipboard (GTK_EDITABLE (focused));
+ else
+ browser_window_key_press(bw, NS_KEY_PASTE);
+
+ return TRUE;
+}
+
+MULTIHANDLER(delete)
+{
+ return TRUE;
+}
+
+MENUHANDLER(customize)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ nsgtk_toolbar_customization_init(g);
+ return TRUE;
+}
+
+MULTIHANDLER(selectall)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+
+ if (nsgtk_widget_has_focus(GTK_WIDGET(g->url_bar))) {
+ LOG("Selecting all URL bar text");
+ gtk_editable_select_region(GTK_EDITABLE(g->url_bar), 0, -1);
+ } else {
+ LOG("Selecting all document text");
+ browser_window_key_press(bw, NS_KEY_SELECT_ALL);
+ }
+
+ return TRUE;
+}
+
+MULTIHANDLER(find)
+{
+ nsgtk_scaffolding_toggle_search_bar_visibility(g);
+ return TRUE;
+}
+
+MULTIHANDLER(preferences)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ GtkWidget* wndpreferences;
+
+ wndpreferences = nsgtk_preferences(bw, g->window);
+ if (wndpreferences != NULL) {
+ gtk_widget_show(GTK_WIDGET(wndpreferences));
+ }
+
+ return TRUE;
+}
+
+MULTIHANDLER(zoomplus)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ float old_scale = nsgtk_get_scale_for_gui(g->top_level);
+
+ browser_window_set_scale(bw, old_scale + 0.05, true);
+
+ return TRUE;
+}
+
+MULTIHANDLER(zoomnormal)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+
+ browser_window_set_scale(bw, 1.0, true);
+
+ return TRUE;
+}
+
+MULTIHANDLER(zoomminus)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ float old_scale = nsgtk_get_scale_for_gui(g->top_level);
+
+ browser_window_set_scale(bw, old_scale - 0.05, true);
+
+ return TRUE;
+}
+
+MULTIHANDLER(fullscreen)
+{
+ if (g->fullscreen) {
+ gtk_window_unfullscreen(g->window);
+ } else {
+ gtk_window_fullscreen(g->window);
+ }
+
+ g->fullscreen = !g->fullscreen;
+
+ return TRUE;
+}
+
+MULTIHANDLER(viewsource)
+{
+ nserror ret;
+
+ ret = nsgtk_viewsource(g->window, nsgtk_get_browser_window(g->top_level));
+ if (ret != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(ret), 0);
+ }
+
+ return TRUE;
+}
+
+MENUHANDLER(menubar)
+{
+ GtkWidget *w;
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+
+ /* if the menubar is not being shown the popup menu shows the
+ * menubar entries instead.
+ */
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
+ /* need to synchronise menus as gtk grumbles when one menu
+ * is attached to both headers */
+ w = GTK_WIDGET(g->menu_popup->view_submenu->toolbars_submenu->menubar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))
+ == FALSE)
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ TRUE);
+
+ w = GTK_WIDGET(g->menu_bar->view_submenu->toolbars_submenu->menubar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))
+ == FALSE)
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ TRUE);
+
+ gtk_widget_show(GTK_WIDGET(g->menu_bar->bar_menu));
+
+ popup_menu_show(g->menu_popup, false, true, true, true);
+ popup_menu_hide(g->menu_popup, true, false, false, false);
+ } else {
+ w = GTK_WIDGET(g->menu_popup->view_submenu->toolbars_submenu->menubar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)))
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ FALSE);
+
+ w = GTK_WIDGET(g->menu_bar->view_submenu->toolbars_submenu->menubar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)))
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ FALSE);
+
+ gtk_widget_hide(GTK_WIDGET(g->menu_bar->bar_menu));
+
+ popup_menu_show(g->menu_popup, true, true, true, true);
+
+ }
+ return TRUE;
+}
+
+MENUHANDLER(toolbar)
+{
+ GtkWidget *w;
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
+ w = GTK_WIDGET(g->menu_popup->view_submenu->toolbars_submenu->toolbar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))
+ == FALSE)
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ TRUE);
+
+ w = GTK_WIDGET(g->menu_bar->view_submenu->toolbars_submenu->toolbar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))
+ == FALSE)
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ TRUE);
+ gtk_widget_show(GTK_WIDGET(g->tool_bar));
+ } else {
+ w = GTK_WIDGET(g->menu_popup->view_submenu->toolbars_submenu->toolbar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)))
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ FALSE);
+ w = GTK_WIDGET(g->menu_bar->view_submenu->toolbars_submenu->toolbar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)))
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ FALSE);
+ gtk_widget_hide(GTK_WIDGET(g->tool_bar));
+ }
+
+ return TRUE;
+}
+
+MULTIHANDLER(downloads)
+{
+ nsgtk_download_show(g->window);
+
+ return TRUE;
+}
+
+MULTIHANDLER(savewindowsize)
+{
+ int x,y,w,h;
+ char *choices = NULL;
+
+ gtk_window_get_position(g->window, &x, &y);
+ gtk_window_get_size(g->window, &w, &h);
+
+ nsoption_set_int(window_width, w);
+ nsoption_set_int(window_height, h);
+ nsoption_set_int(window_x, x);
+ nsoption_set_int(window_y, y);
+
+ netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices");
+ if (choices != NULL) {
+ nsoption_write(choices, NULL, NULL);
+ free(choices);
+ }
+
+ return TRUE;
+}
+
+MULTIHANDLER(toggledebugging)
+{
+ struct browser_window *bw;
+
+ bw = nsgtk_get_browser_window(g->top_level);
+
+ browser_window_debug(bw, CONTENT_DEBUG_REDRAW);
+
+ nsgtk_reflow_all_windows();
+
+ return TRUE;
+}
+
+MULTIHANDLER(debugboxtree)
+{
+ gchar *fname;
+ gint handle;
+ FILE *f;
+ struct browser_window *bw;
+
+ handle = g_file_open_tmp("nsgtkboxtreeXXXXXX", &fname, NULL);
+ if ((handle == -1) || (fname == NULL)) {
+ return TRUE;
+ }
+ close(handle); /* in case it was binary mode */
+
+ /* save data to temporary file */
+ f = fopen(fname, "w");
+ if (f == NULL) {
+ nsgtk_warning("Error saving box tree dump.",
+ "Unable to open file for writing.");
+ unlink(fname);
+ return TRUE;
+ }
+
+ bw = nsgtk_get_browser_window(g->top_level);
+
+ browser_window_debug_dump(bw, f, CONTENT_DEBUG_RENDER);
+
+ fclose(f);
+
+ nsgtk_viewfile("Box Tree Debug", "boxtree", fname);
+
+ g_free(fname);
+
+ return TRUE;
+}
+
+MULTIHANDLER(debugdomtree)
+{
+ gchar *fname;
+ gint handle;
+ FILE *f;
+ struct browser_window *bw;
+
+ handle = g_file_open_tmp("nsgtkdomtreeXXXXXX", &fname, NULL);
+ if ((handle == -1) || (fname == NULL)) {
+ return TRUE;
+ }
+ close(handle); /* in case it was binary mode */
+
+ /* save data to temporary file */
+ f = fopen(fname, "w");
+ if (f == NULL) {
+ nsgtk_warning("Error saving box tree dump.",
+ "Unable to open file for writing.");
+ unlink(fname);
+ return TRUE;
+ }
+
+ bw = nsgtk_get_browser_window(g->top_level);
+
+ browser_window_debug_dump(bw, f, CONTENT_DEBUG_DOM);
+
+ fclose(f);
+
+ nsgtk_viewfile("DOM Tree Debug", "domtree", fname);
+
+ g_free(fname);
+
+ return TRUE;
+}
+
+
+MULTIHANDLER(stop)
+{
+ struct browser_window *bw =
+ nsgtk_get_browser_window(g->top_level);
+
+ browser_window_stop(bw);
+
+ return TRUE;
+}
+
+MULTIHANDLER(reload)
+{
+ struct browser_window *bw =
+ nsgtk_get_browser_window(g->top_level);
+ if (bw == NULL)
+ return TRUE;
+
+ /* clear potential search effects */
+ browser_window_search_clear(bw);
+
+ browser_window_reload(bw, true);
+
+ return TRUE;
+}
+
+MULTIHANDLER(back)
+{
+ struct browser_window *bw =
+ nsgtk_get_browser_window(g->top_level);
+
+ if ((bw == NULL) || (!browser_window_history_back_available(bw)))
+ return TRUE;
+
+ /* clear potential search effects */
+ browser_window_search_clear(bw);
+
+ browser_window_history_back(bw, false);
+ scaffolding_update_context(g);
+
+ return TRUE;
+}
+
+MULTIHANDLER(forward)
+{
+ struct browser_window *bw =
+ nsgtk_get_browser_window(g->top_level);
+
+ if ((bw == NULL) || (!browser_window_history_forward_available(bw)))
+ return TRUE;
+
+ /* clear potential search effects */
+ browser_window_search_clear(bw);
+
+ browser_window_history_forward(bw, false);
+ scaffolding_update_context(g);
+
+ return TRUE;
+}
+
+MULTIHANDLER(home)
+{
+ static const char *addr = NETSURF_HOMEPAGE;
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ nsurl *url;
+ nserror error;
+
+ if (nsoption_charp(homepage_url) != NULL) {
+ addr = nsoption_charp(homepage_url);
+ }
+
+ error = nsurl_create(addr, &url);
+ if (error != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(error), 0);
+ } else {
+ browser_window_navigate(bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+
+ return TRUE;
+}
+
+MULTIHANDLER(localhistory)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+
+ int x,y, width, height, mainwidth, mainheight, margin = 20;
+ /* if entries of the same url but different frag_ids have been added
+ * the history needs redrawing (what throbber code normally does)
+ */
+
+ scaffolding_update_context(g);
+ gtk_window_get_position(g->window, &x, &y);
+ gtk_window_get_size(g->window, &mainwidth, &mainheight);
+ browser_window_history_size(bw, &width, &height);
+ width = (width + g->historybase + margin > mainwidth) ?
+ mainwidth - g->historybase : width + margin;
+ height = (height + g->toolbarbase + margin > mainheight) ?
+ mainheight - g->toolbarbase : height + margin;
+ gtk_window_set_default_size(g->history_window->window, width, height);
+ gtk_widget_set_size_request(GTK_WIDGET(g->history_window->window),
+ -1, -1);
+ gtk_window_resize(g->history_window->window, width, height);
+ gtk_window_set_transient_for(g->history_window->window, g->window);
+ nsgtk_window_set_opacity(g->history_window->window, 0.9);
+ gtk_widget_show(GTK_WIDGET(g->history_window->window));
+ gtk_window_move(g->history_window->window, x + g->historybase, y +
+ g->toolbarbase);
+ gdk_window_raise(nsgtk_widget_get_window(GTK_WIDGET(g->history_window->window)));
+
+ return TRUE;
+}
+
+MULTIHANDLER(globalhistory)
+{
+ gtk_widget_show(GTK_WIDGET(wndHistory));
+ gdk_window_raise(nsgtk_widget_get_window(GTK_WIDGET(wndHistory)));
+
+ return TRUE;
+}
+
+MULTIHANDLER(addbookmarks)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+
+ if (bw == NULL || !browser_window_has_content(bw))
+ return TRUE;
+ hotlist_add_url(browser_window_get_url(bw));
+ return TRUE;
+}
+
+MULTIHANDLER(showbookmarks)
+{
+ gtk_widget_show(GTK_WIDGET(wndHotlist));
+ gdk_window_raise(nsgtk_widget_get_window(GTK_WIDGET(wndHotlist)));
+ gtk_window_set_focus(wndHotlist, NULL);
+
+ return TRUE;
+}
+
+MULTIHANDLER(showcookies)
+{
+ gtk_widget_show(GTK_WIDGET(wndCookies));
+ gdk_window_raise(nsgtk_widget_get_window(GTK_WIDGET(wndCookies)));
+
+ return TRUE;
+}
+
+MULTIHANDLER(openlocation)
+{
+ gtk_widget_grab_focus(GTK_WIDGET(g->url_bar));
+ return TRUE;
+}
+
+MULTIHANDLER(nexttab)
+{
+ nsgtk_tab_next(g->notebook);
+
+ return TRUE;
+}
+
+MULTIHANDLER(prevtab)
+{
+
+ nsgtk_tab_prev(g->notebook);
+
+ return TRUE;
+}
+
+MULTIHANDLER(closetab)
+{
+ nsgtk_tab_close_current(g->notebook);
+
+ return TRUE;
+}
+
+MULTIHANDLER(contents)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ nsurl *url;
+ nserror error;
+
+ error = nsurl_create("http://www.netsurf-browser.org/documentation/", &url);
+ if (error != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(error), 0);
+ } else {
+ browser_window_navigate(bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+
+ return TRUE;
+}
+
+MULTIHANDLER(guide)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ nsurl *url;
+
+ if (nsurl_create("http://www.netsurf-browser.org/documentation/guide", &url) != NSERROR_OK) {
+ nsgtk_warning("NoMemory", 0);
+ } else {
+ browser_window_navigate(bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+
+ return TRUE;
+}
+
+MULTIHANDLER(info)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ nsurl *url;
+
+ if (nsurl_create("http://www.netsurf-browser.org/documentation/info", &url) != NSERROR_OK) {
+ nsgtk_warning("NoMemory", 0);
+ } else {
+ browser_window_navigate(bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+
+ return TRUE;
+}
+
+MULTIHANDLER(about)
+{
+ nsgtk_about_dialog_init(g->window);
+ return TRUE;
+}
+
+BUTTONHANDLER(history)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ return nsgtk_on_localhistory_activate(g);
+}
+
+#undef MULTIHANDLER
+#undef CHECKHANDLER
+#undef BUTTONHANDLER
+
+#if GTK_CHECK_VERSION(3,0,0)
+
+static gboolean
+nsgtk_history_draw_event(GtkWidget *widget, cairo_t *cr, gpointer data)
+{
+ struct rect clip;
+ struct gtk_history_window *hw = (struct gtk_history_window *)data;
+ struct browser_window *bw =
+ nsgtk_get_browser_window(hw->g->top_level);
+
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &nsgtk_plotters
+ };
+ double x1;
+ double y1;
+ double x2;
+ double y2;
+
+ current_widget = widget;
+ current_cr = cr;
+
+ cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
+
+ clip.x0 = x1;
+ clip.y0 = y1;
+ clip.x1 = x2;
+ clip.y1 = y2;
+
+ ctx.plot->clip(&clip);
+
+ browser_window_history_redraw(bw, &ctx);
+
+ current_widget = NULL;
+
+ return FALSE;
+}
+#else
+
+/* signal handler functions for the local history window */
+static gboolean
+nsgtk_history_draw_event(GtkWidget *widget, GdkEventExpose *event, gpointer g)
+{
+ struct rect clip;
+ struct gtk_history_window *hw = (struct gtk_history_window *)g;
+ struct browser_window *bw =
+ nsgtk_get_browser_window(hw->g->top_level);
+
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &nsgtk_plotters
+ };
+
+ current_widget = widget;
+
+ current_cr = gdk_cairo_create(nsgtk_widget_get_window(widget));
+
+ clip.x0 = event->area.x;
+ clip.y0 = event->area.y;
+ clip.x1 = event->area.x + event->area.width;
+ clip.y1 = event->area.y + event->area.height;
+ ctx.plot->clip(&clip);
+
+ browser_window_history_redraw(bw, &ctx);
+
+ cairo_destroy(current_cr);
+
+ current_widget = NULL;
+
+ return FALSE;
+}
+
+#endif /* GTK_CHECK_VERSION(3,0,0) */
+
+static gboolean nsgtk_history_button_press_event(GtkWidget *widget,
+ GdkEventButton *event, gpointer g)
+{
+ struct gtk_history_window *hw = (struct gtk_history_window *)g;
+ struct browser_window *bw =
+ nsgtk_get_browser_window(hw->g->top_level);
+
+ LOG("X=%g, Y=%g", event->x, event->y);
+
+ browser_window_history_click(bw, event->x, event->y, false);
+
+ return TRUE;
+}
+
+
+
+static void nsgtk_attach_menu_handlers(struct nsgtk_scaffolding *g)
+{
+ for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ if (g->buttons[i]->main != NULL) {
+ g_signal_connect(g->buttons[i]->main, "activate",
+ G_CALLBACK(g->buttons[i]->mhandler), g);
+ }
+ if (g->buttons[i]->rclick != NULL) {
+ g_signal_connect(g->buttons[i]->rclick, "activate",
+ G_CALLBACK(g->buttons[i]->mhandler), g);
+ }
+ if (g->buttons[i]->popup != NULL) {
+ g_signal_connect(g->buttons[i]->popup, "activate",
+ G_CALLBACK(g->buttons[i]->mhandler), g);
+ }
+ }
+#define CONNECT_CHECK(q)\
+ g_signal_connect(g->menu_bar->view_submenu->toolbars_submenu->q##_menuitem, "toggled", G_CALLBACK(nsgtk_on_##q##_activate_menu), g);\
+ g_signal_connect(g->menu_popup->view_submenu->toolbars_submenu->q##_menuitem, "toggled", G_CALLBACK(nsgtk_on_##q##_activate_menu), g)
+ CONNECT_CHECK(menubar);
+ CONNECT_CHECK(toolbar);
+#undef CONNECT_CHECK
+
+}
+
+/**
+ * Create and connect handlers to popup menu.
+ *
+ * \param g scaffolding to attach popup menu to.
+ * \param group The accelerator group to use for the popup.
+ * \return menu structure on success or NULL on error.
+ */
+static struct nsgtk_popup_menu *
+nsgtk_new_scaffolding_popup(struct nsgtk_scaffolding *g, GtkAccelGroup *group)
+{
+ struct nsgtk_popup_menu *nmenu;
+
+ nmenu = nsgtk_popup_menu_create(group);
+
+ if (nmenu == NULL) {
+ return NULL;
+ }
+
+ g_signal_connect(nmenu->popup_menu, "hide",
+ G_CALLBACK(nsgtk_window_popup_menu_hidden), g);
+
+ g_signal_connect(nmenu->cut_menuitem, "activate",
+ G_CALLBACK(nsgtk_on_cut_activate_menu), g);
+
+ g_signal_connect(nmenu->copy_menuitem, "activate",
+ G_CALLBACK(nsgtk_on_copy_activate_menu), g);
+
+ g_signal_connect(nmenu->paste_menuitem, "activate",
+ G_CALLBACK(nsgtk_on_paste_activate_menu), g);
+
+ g_signal_connect(nmenu->customize_menuitem, "activate",
+ G_CALLBACK(nsgtk_on_customize_activate_menu), g);
+
+ /* set initial popup menu visibility */
+ popup_menu_hide(nmenu, true, false, false, true);
+
+ return nmenu;
+}
+
+/**
+ * Create and connect handlers to link popup menu.
+ *
+ * \param g scaffolding to attach popup menu to.
+ * \param group The accelerator group to use for the popup.
+ * \return true on success or false on error.
+ */
+static struct nsgtk_link_menu *
+nsgtk_new_scaffolding_link_popup(struct nsgtk_scaffolding *g, GtkAccelGroup *group)
+{
+ struct nsgtk_link_menu *nmenu;
+
+ nmenu = nsgtk_link_menu_create(group);
+
+ if (nmenu == NULL) {
+ return NULL;
+ }
+
+ g_signal_connect(nmenu->save_menuitem, "activate",
+ G_CALLBACK(nsgtk_on_savelink_activate_menu), g);
+
+ g_signal_connect(nmenu->opentab_menuitem, "activate",
+ G_CALLBACK(nsgtk_on_link_opentab_activate_menu), g);
+
+ g_signal_connect(nmenu->openwin_menuitem, "activate",
+ G_CALLBACK(nsgtk_on_link_openwin_activate_menu), g);
+
+ g_signal_connect(nmenu->bookmark_menuitem, "activate",
+ G_CALLBACK(nsgtk_on_link_bookmark_activate_menu), g);
+
+ g_signal_connect(nmenu->copy_menuitem, "activate",
+ G_CALLBACK(nsgtk_on_link_copy_activate_menu), g);
+
+ return nmenu;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+struct nsgtk_scaffolding *nsgtk_current_scaffolding(void)
+{
+ if (scaf_current == NULL) {
+ scaf_current = scaf_list;
+ }
+ return scaf_current;
+}
+
+/**
+ * init the array g->buttons[]
+ */
+static void nsgtk_scaffolding_toolbar_init(struct nsgtk_scaffolding *g)
+{
+#define ITEM_MAIN(p, q, r)\
+ g->buttons[p##_BUTTON]->main = g->menu_bar->q->r##_menuitem;\
+ g->buttons[p##_BUTTON]->rclick = g->menu_popup->q->r##_menuitem;\
+ g->buttons[p##_BUTTON]->mhandler = nsgtk_on_##r##_activate_menu;\
+ g->buttons[p##_BUTTON]->bhandler = nsgtk_on_##r##_activate_button;\
+ g->buttons[p##_BUTTON]->dataplus = nsgtk_toolbar_##r##_button_data;\
+ g->buttons[p##_BUTTON]->dataminus = nsgtk_toolbar_##r##_toolbar_button_data
+
+#define ITEM_SUB(p, q, r, s)\
+ g->buttons[p##_BUTTON]->main =\
+ g->menu_bar->q->r##_submenu->s##_menuitem;\
+ g->buttons[p##_BUTTON]->rclick =\
+ g->menu_popup->q->r##_submenu->s##_menuitem;\
+ g->buttons[p##_BUTTON]->mhandler =\
+ nsgtk_on_##s##_activate_menu;\
+ g->buttons[p##_BUTTON]->bhandler =\
+ nsgtk_on_##s##_activate_button;\
+ g->buttons[p##_BUTTON]->dataplus =\
+ nsgtk_toolbar_##s##_button_data;\
+ g->buttons[p##_BUTTON]->dataminus =\
+ nsgtk_toolbar_##s##_toolbar_button_data
+
+#define ITEM_BUTTON(p, q)\
+ g->buttons[p##_BUTTON]->bhandler =\
+ nsgtk_on_##q##_activate;\
+ g->buttons[p##_BUTTON]->dataplus =\
+ nsgtk_toolbar_##q##_button_data;\
+ g->buttons[p##_BUTTON]->dataminus =\
+ nsgtk_toolbar_##q##_toolbar_button_data
+
+#define ITEM_POP(p, q) \
+ g->buttons[p##_BUTTON]->popup = g->menu_popup->q##_menuitem
+
+#define SENSITIVITY(q) \
+ g->buttons[q##_BUTTON]->sensitivity = false
+
+#define ITEM_ITEM(p, q)\
+ g->buttons[p##_ITEM]->dataplus =\
+ nsgtk_toolbar_##q##_button_data;\
+ g->buttons[p##_ITEM]->dataminus =\
+ nsgtk_toolbar_##q##_toolbar_button_data
+
+ ITEM_ITEM(WEBSEARCH, websearch);
+ ITEM_ITEM(THROBBER, throbber);
+ ITEM_MAIN(NEWWINDOW, file_submenu, newwindow);
+ ITEM_MAIN(NEWTAB, file_submenu, newtab);
+ ITEM_MAIN(OPENFILE, file_submenu, openfile);
+ ITEM_MAIN(PRINT, file_submenu, print);
+ ITEM_MAIN(CLOSEWINDOW, file_submenu, closewindow);
+ ITEM_MAIN(SAVEPAGE, file_submenu, savepage);
+ ITEM_MAIN(PRINTPREVIEW, file_submenu, printpreview);
+ ITEM_MAIN(PRINT, file_submenu, print);
+ ITEM_MAIN(QUIT, file_submenu, quit);
+ ITEM_MAIN(CUT, edit_submenu, cut);
+ ITEM_MAIN(COPY, edit_submenu, copy);
+ ITEM_MAIN(PASTE, edit_submenu, paste);
+ ITEM_MAIN(DELETE, edit_submenu, delete);
+ ITEM_MAIN(SELECTALL, edit_submenu, selectall);
+ ITEM_MAIN(FIND, edit_submenu, find);
+ ITEM_MAIN(PREFERENCES, edit_submenu, preferences);
+ ITEM_MAIN(STOP, view_submenu, stop);
+ ITEM_POP(STOP, stop);
+ ITEM_MAIN(RELOAD, view_submenu, reload);
+ ITEM_POP(RELOAD, reload);
+ ITEM_MAIN(FULLSCREEN, view_submenu, fullscreen);
+ ITEM_MAIN(DOWNLOADS, tools_submenu, downloads);
+ ITEM_MAIN(SAVEWINDOWSIZE, view_submenu, savewindowsize);
+ ITEM_MAIN(BACK, nav_submenu, back);
+ ITEM_POP(BACK, back);
+ ITEM_MAIN(FORWARD, nav_submenu, forward);
+ ITEM_POP(FORWARD, forward);
+ ITEM_MAIN(HOME, nav_submenu, home);
+ ITEM_MAIN(LOCALHISTORY, nav_submenu, localhistory);
+ ITEM_MAIN(GLOBALHISTORY, nav_submenu, globalhistory);
+ ITEM_MAIN(ADDBOOKMARKS, nav_submenu, addbookmarks);
+ ITEM_MAIN(SHOWBOOKMARKS, nav_submenu, showbookmarks);
+ ITEM_MAIN(SHOWCOOKIES, tools_submenu, showcookies);
+ ITEM_MAIN(OPENLOCATION, nav_submenu, openlocation);
+ ITEM_MAIN(CONTENTS, help_submenu, contents);
+ ITEM_MAIN(INFO, help_submenu, info);
+ ITEM_MAIN(GUIDE, help_submenu, guide);
+ ITEM_MAIN(ABOUT, help_submenu, about);
+ ITEM_SUB(PLAINTEXT, file_submenu, export, plaintext);
+ ITEM_SUB(PDF, file_submenu, export, pdf);
+ ITEM_SUB(DRAWFILE, file_submenu, export, drawfile);
+ ITEM_SUB(POSTSCRIPT, file_submenu, export, postscript);
+ ITEM_SUB(ZOOMPLUS, view_submenu, scaleview, zoomplus);
+ ITEM_SUB(ZOOMMINUS, view_submenu, scaleview, zoomminus);
+ ITEM_SUB(ZOOMNORMAL, view_submenu, scaleview, zoomnormal);
+ ITEM_SUB(NEXTTAB, view_submenu, tabs, nexttab);
+ ITEM_SUB(PREVTAB, view_submenu, tabs, prevtab);
+ ITEM_SUB(CLOSETAB, view_submenu, tabs, closetab);
+
+ /* development submenu */
+ ITEM_SUB(VIEWSOURCE, tools_submenu, developer, viewsource);
+ ITEM_SUB(TOGGLEDEBUGGING, tools_submenu, developer, toggledebugging);
+ ITEM_SUB(SAVEBOXTREE, tools_submenu, developer, debugboxtree);
+ ITEM_SUB(SAVEDOMTREE, tools_submenu, developer, debugdomtree);
+ ITEM_BUTTON(HISTORY, history);
+
+ /* disable items that make no sense initially, as well as
+ * as-yet-unimplemented items */
+ SENSITIVITY(BACK);
+ SENSITIVITY(FORWARD);
+ SENSITIVITY(STOP);
+ SENSITIVITY(PRINTPREVIEW);
+ SENSITIVITY(DELETE);
+ SENSITIVITY(DRAWFILE);
+ SENSITIVITY(POSTSCRIPT);
+ SENSITIVITY(NEXTTAB);
+ SENSITIVITY(PREVTAB);
+ SENSITIVITY(CLOSETAB);
+#ifndef WITH_PDF_EXPORT
+ SENSITIVITY(PDF);
+#endif
+
+#undef ITEM_MAIN
+#undef ITEM_SUB
+#undef ITEM_BUTTON
+#undef ITEM_POP
+#undef SENSITIVITY
+
+}
+
+static void nsgtk_scaffolding_initial_sensitivity(struct nsgtk_scaffolding *g)
+{
+ for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ if (g->buttons[i]->main != NULL)
+ gtk_widget_set_sensitive(GTK_WIDGET(
+ g->buttons[i]->main),
+ g->buttons[i]->sensitivity);
+ if (g->buttons[i]->rclick != NULL)
+ gtk_widget_set_sensitive(GTK_WIDGET(
+ g->buttons[i]->rclick),
+ g->buttons[i]->sensitivity);
+ if ((g->buttons[i]->location != -1) &&
+ (g->buttons[i]->button != NULL))
+ gtk_widget_set_sensitive(GTK_WIDGET(
+ g->buttons[i]->button),
+ g->buttons[i]->sensitivity);
+ if (g->buttons[i]->popup != NULL)
+ gtk_widget_set_sensitive(GTK_WIDGET(
+ g->buttons[i]->popup),
+ g->buttons[i]->sensitivity);
+ }
+ gtk_widget_set_sensitive(GTK_WIDGET(g->menu_bar->view_submenu->images_menuitem), FALSE);
+}
+
+
+void nsgtk_scaffolding_toolbars(struct nsgtk_scaffolding *g, int tbi)
+{
+ switch (tbi) {
+ /* case 0 is 'unset' [from fresh install / clearing options]
+ * see above */
+
+ case 1: /* Small icons */
+ /* main toolbar */
+ gtk_toolbar_set_style(GTK_TOOLBAR(g->tool_bar),
+ GTK_TOOLBAR_ICONS);
+ gtk_toolbar_set_icon_size(GTK_TOOLBAR(g->tool_bar),
+ GTK_ICON_SIZE_SMALL_TOOLBAR);
+ /* search toolbar */
+ gtk_toolbar_set_style(GTK_TOOLBAR(g->search->bar),
+ GTK_TOOLBAR_ICONS);
+ gtk_toolbar_set_icon_size(GTK_TOOLBAR(g->search->bar),
+ GTK_ICON_SIZE_SMALL_TOOLBAR);
+ break;
+
+ case 2: /* Large icons */
+ gtk_toolbar_set_style(GTK_TOOLBAR(g->tool_bar),
+ GTK_TOOLBAR_ICONS);
+ gtk_toolbar_set_icon_size(GTK_TOOLBAR(g->tool_bar),
+ GTK_ICON_SIZE_LARGE_TOOLBAR);
+ /* search toolbar */
+ gtk_toolbar_set_style(GTK_TOOLBAR(g->search->bar),
+ GTK_TOOLBAR_ICONS);
+ gtk_toolbar_set_icon_size(GTK_TOOLBAR(g->search->bar),
+ GTK_ICON_SIZE_LARGE_TOOLBAR);
+ break;
+
+ case 3: /* Large icons with text */
+ gtk_toolbar_set_style(GTK_TOOLBAR(g->tool_bar),
+ GTK_TOOLBAR_BOTH);
+ gtk_toolbar_set_icon_size(GTK_TOOLBAR(g->tool_bar),
+ GTK_ICON_SIZE_LARGE_TOOLBAR);
+ /* search toolbar */
+ gtk_toolbar_set_style(GTK_TOOLBAR(g->search->bar),
+ GTK_TOOLBAR_BOTH);
+ gtk_toolbar_set_icon_size(GTK_TOOLBAR(g->search->bar),
+ GTK_ICON_SIZE_LARGE_TOOLBAR);
+ break;
+
+ case 4: /* Text icons only */
+ gtk_toolbar_set_style(GTK_TOOLBAR(g->tool_bar),
+ GTK_TOOLBAR_TEXT);
+ /* search toolbar */
+ gtk_toolbar_set_style(GTK_TOOLBAR(g->search->bar),
+ GTK_TOOLBAR_TEXT);
+ default:
+ break;
+ }
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+struct nsgtk_scaffolding *nsgtk_new_scaffolding(struct gui_window *toplevel)
+{
+ struct nsgtk_scaffolding *gs;
+ int i;
+ GtkAccelGroup *group;
+
+ gs = malloc(sizeof(*gs));
+ if (gs == NULL) {
+ return NULL;
+ }
+
+ LOG("Constructing a scaffold of %p for gui_window %p", gs, toplevel);
+
+ gs->top_level = toplevel;
+
+ /* Construct UI widgets */
+ if (nsgtk_builder_new_from_resname("netsurf", &gs->builder) != NSERROR_OK) {
+ free(gs);
+ return NULL;
+ }
+
+ gtk_builder_connect_signals(gs->builder, NULL);
+
+/** Obtain a GTK widget handle from UI builder object */
+#define GET_WIDGET(x) GTK_WIDGET (gtk_builder_get_object(gs->builder, (x)))
+
+ gs->window = GTK_WINDOW(GET_WIDGET("wndBrowser"));
+ gs->notebook = GTK_NOTEBOOK(GET_WIDGET("notebook"));
+ gs->tool_bar = GTK_TOOLBAR(GET_WIDGET("toolbar"));
+
+ gs->search = malloc(sizeof(struct gtk_search));
+ if (gs->search == NULL) {
+ free(gs);
+ return NULL;
+ }
+
+ gs->search->bar = GTK_TOOLBAR(GET_WIDGET("searchbar"));
+ gs->search->entry = GTK_ENTRY(GET_WIDGET("searchEntry"));
+
+ gs->search->buttons[0] = GTK_TOOL_BUTTON(GET_WIDGET("searchBackButton"));
+ gs->search->buttons[1] = GTK_TOOL_BUTTON(GET_WIDGET("searchForwardButton"));
+ gs->search->buttons[2] = GTK_TOOL_BUTTON(GET_WIDGET("closeSearchButton"));
+ gs->search->checkAll = GTK_CHECK_BUTTON(GET_WIDGET("checkAllSearch"));
+ gs->search->caseSens = GTK_CHECK_BUTTON(GET_WIDGET("caseSensButton"));
+
+#undef GET_WIDGET
+
+ /* allocate buttons */
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ gs->buttons[i] = calloc(1, sizeof(struct nsgtk_button_connect));
+ if (gs->buttons[i] == NULL) {
+ for (i-- ; i >= BACK_BUTTON; i--) {
+ free(gs->buttons[i]);
+ }
+ free(gs);
+ return NULL;
+ }
+ gs->buttons[i]->location = -1;
+ gs->buttons[i]->sensitivity = true;
+ }
+
+ /* here custom toolbutton adding code */
+ gs->offset = 0;
+ gs->toolbarmem = 0;
+ gs->toolbarbase = 0;
+ gs->historybase = 0;
+ nsgtk_toolbar_customization_load(gs);
+ nsgtk_toolbar_set_physical(gs);
+
+ group = gtk_accel_group_new();
+ gtk_window_add_accel_group(gs->window, group);
+
+ gs->menu_bar = nsgtk_menu_bar_create(GTK_MENU_SHELL(gtk_builder_get_object(gs->builder, "menubar")), group);
+
+
+ /* set this window's size and position to what's in the options, or
+ * or some sensible default if they're not set yet.
+ */
+ if (nsoption_int(window_width) > 0) {
+ gtk_window_move(gs->window,
+ nsoption_int(window_x),
+ nsoption_int(window_y));
+ gtk_window_resize(gs->window,
+ nsoption_int(window_width),
+ nsoption_int(window_height));
+ } else {
+ /* Set to 1000x700, so we're very likely to fit even on
+ * 1024x768 displays, not being able to take into account
+ * window furniture or panels.
+ */
+ gtk_window_set_default_size(gs->window, 1000, 700);
+ }
+
+ /* Default toolbar button type uses system defaults */
+ if (nsoption_int(button_type) == 0) {
+ GtkSettings *settings = gtk_settings_get_default();
+ GtkIconSize tooliconsize;
+ GtkToolbarStyle toolbarstyle;
+
+ g_object_get(settings,
+ "gtk-toolbar-icon-size", &tooliconsize,
+ "gtk-toolbar-style", &toolbarstyle, NULL);
+
+ switch (toolbarstyle) {
+ case GTK_TOOLBAR_ICONS:
+ if (tooliconsize == GTK_ICON_SIZE_SMALL_TOOLBAR) {
+ nsoption_set_int(button_type, 1);
+ } else {
+ nsoption_set_int(button_type, 2);
+ }
+ break;
+
+ case GTK_TOOLBAR_TEXT:
+ nsoption_set_int(button_type, 4);
+ break;
+
+ case GTK_TOOLBAR_BOTH:
+ case GTK_TOOLBAR_BOTH_HORIZ:
+ /* no labels in default configuration */
+ default:
+ /* No system default, so use large icons */
+ nsoption_set_int(button_type, 2);
+ break;
+ }
+ }
+
+ nsgtk_scaffolding_toolbars(gs, nsoption_int(button_type));
+
+ gtk_toolbar_set_show_arrow(gs->tool_bar, TRUE);
+ gtk_widget_show_all(GTK_WIDGET(gs->tool_bar));
+ nsgtk_tab_init(gs);
+
+ gtk_widget_set_size_request(GTK_WIDGET(
+ gs->buttons[HISTORY_BUTTON]->button), 20, -1);
+
+ /* create the local history window to be associated with this scaffold */
+ gs->history_window = malloc(sizeof(struct gtk_history_window));
+ gs->history_window->g = gs;
+ gs->history_window->window =
+ GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
+ gtk_window_set_transient_for(gs->history_window->window, gs->window);
+ gtk_window_set_title(gs->history_window->window, "NetSurf History");
+ gtk_window_set_type_hint(gs->history_window->window,
+ GDK_WINDOW_TYPE_HINT_UTILITY);
+ gs->history_window->scrolled =
+ GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(0, 0));
+ gtk_container_add(GTK_CONTAINER(gs->history_window->window),
+ GTK_WIDGET(gs->history_window->scrolled));
+
+ gtk_widget_show(GTK_WIDGET(gs->history_window->scrolled));
+ gs->history_window->drawing_area =
+ GTK_DRAWING_AREA(gtk_drawing_area_new());
+
+ gtk_widget_set_events(GTK_WIDGET(gs->history_window->drawing_area),
+ GDK_EXPOSURE_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_PRESS_MASK);
+ nsgtk_widget_override_background_color(GTK_WIDGET(gs->history_window->drawing_area),
+ GTK_STATE_NORMAL,
+ 0, 0xffff, 0xffff, 0xffff);
+ nsgtk_scrolled_window_add_with_viewport(gs->history_window->scrolled,
+ GTK_WIDGET(gs->history_window->drawing_area));
+ gtk_widget_show(GTK_WIDGET(gs->history_window->drawing_area));
+
+
+ /* set up URL bar completion */
+ gs->url_bar_completion = nsgtk_url_entry_completion_new(gs);
+
+ /* set up the throbber. */
+ gs->throb_frame = 0;
+
+
+#define CONNECT(obj, sig, callback, ptr) \
+ g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr))
+
+ /* connect history window signals to their handlers */
+ nsgtk_connect_draw_event(GTK_WIDGET(gs->history_window->drawing_area),
+ G_CALLBACK(nsgtk_history_draw_event),
+ gs->history_window);
+ /*CONNECT(gs->history_window->drawing_area, "motion_notify_event",
+ nsgtk_history_motion_notify_event, gs->history_window);*/
+ CONNECT(gs->history_window->drawing_area, "button_press_event",
+ nsgtk_history_button_press_event, gs->history_window);
+ CONNECT(gs->history_window->window, "delete_event",
+ gtk_widget_hide_on_delete, NULL);
+
+ g_signal_connect_after(gs->notebook, "page-added",
+ G_CALLBACK(nsgtk_window_tabs_add), gs);
+ g_signal_connect_after(gs->notebook, "page-removed",
+ G_CALLBACK(nsgtk_window_tabs_remove), gs);
+
+ /* connect main window signals to their handlers. */
+ CONNECT(gs->window, "delete-event",
+ scaffolding_window_delete_event, gs);
+
+ CONNECT(gs->window, "destroy", scaffolding_window_destroy, gs);
+
+ /* toolbar URL bar menu bar search bar signal handlers */
+ CONNECT(gs->menu_bar->edit_submenu->edit, "show",
+ nsgtk_window_edit_menu_clicked, gs);
+ CONNECT(gs->menu_bar->edit_submenu->edit, "hide",
+ nsgtk_window_edit_menu_hidden, gs);
+
+ CONNECT(gs->search->buttons[1], "clicked",
+ nsgtk_search_forward_button_clicked, gs);
+
+ CONNECT(gs->search->buttons[0], "clicked",
+ nsgtk_search_back_button_clicked, gs);
+
+ CONNECT(gs->search->entry, "changed", nsgtk_search_entry_changed, gs);
+
+ CONNECT(gs->search->entry, "activate", nsgtk_search_entry_activate, gs);
+
+ CONNECT(gs->search->entry, "key-press-event",
+ nsgtk_search_entry_key, gs);
+
+ CONNECT(gs->search->buttons[2], "clicked",
+ nsgtk_search_close_button_clicked, gs);
+
+ CONNECT(gs->search->caseSens, "toggled",
+ nsgtk_search_entry_changed, gs);
+
+ CONNECT(gs->tool_bar, "popup-context-menu",
+ nsgtk_window_tool_bar_clicked, gs);
+
+ /* create popup menu */
+ gs->menu_popup = nsgtk_new_scaffolding_popup(gs, group);
+
+ gs->link_menu = nsgtk_new_scaffolding_link_popup(gs, group);
+
+ /* set up the menu signal handlers */
+ nsgtk_scaffolding_toolbar_init(gs);
+ nsgtk_toolbar_connect_all(gs);
+ nsgtk_attach_menu_handlers(gs);
+
+ nsgtk_scaffolding_initial_sensitivity(gs);
+
+ gs->fullscreen = false;
+
+ /* attach to the list */
+ if (scaf_list) {
+ scaf_list->prev = gs;
+ }
+ gs->next = scaf_list;
+ gs->prev = NULL;
+ scaf_list = gs;
+
+ /* set icon images */
+ nsgtk_theme_implement(gs);
+
+ /* set web search provider */
+ search_web_select_provider(nsoption_int(search_provider));
+
+ /* finally, show the window. */
+ gtk_widget_show(GTK_WIDGET(gs->window));
+
+ LOG("creation complete");
+
+ return gs;
+}
+
+/* exported function documented in gtk/scaffolding.h */
+void nsgtk_window_set_title(struct gui_window *gw, const char *title)
+{
+ struct nsgtk_scaffolding *gs = nsgtk_get_scaffold(gw);
+ int title_len;
+ char *newtitle;
+
+ if ((title == NULL) || (title[0] == '\0')) {
+ if (gs->top_level != gw) {
+ gtk_window_set_title(gs->window, "NetSurf");
+ }
+ return;
+ }
+
+ nsgtk_tab_set_title(gw, title);
+
+ if (gs->top_level != gw) {
+ /* not top level window so do not set window title */
+ return;
+ }
+
+ title_len = strlen(title) + SLEN(" - NetSurf") + 1;
+ newtitle = malloc(title_len);
+ if (newtitle == NULL) {
+ return;
+ }
+
+ snprintf(newtitle, title_len, "%s - NetSurf", title);
+
+ gtk_window_set_title(gs->window, newtitle);
+
+ free(newtitle);
+}
+
+
+nserror gui_window_set_url(struct gui_window *gw, nsurl *url)
+{
+ struct nsgtk_scaffolding *g;
+ size_t idn_url_l;
+ char *idn_url_s = NULL;
+
+ g = nsgtk_get_scaffold(gw);
+ if (g->top_level == gw) {
+ if (nsoption_bool(display_decoded_idn) == true) {
+ if (nsurl_get_utf8(url, &idn_url_s, &idn_url_l) != NSERROR_OK)
+ idn_url_s = NULL;
+ }
+
+ gtk_entry_set_text(GTK_ENTRY(g->url_bar), idn_url_s ? idn_url_s : nsurl_access(url));
+
+ if(idn_url_s)
+ free(idn_url_s);
+
+ gtk_editable_set_position(GTK_EDITABLE(g->url_bar), -1);
+ }
+ return NSERROR_OK;
+}
+
+void gui_window_start_throbber(struct gui_window* _g)
+{
+ struct nsgtk_scaffolding *g = nsgtk_get_scaffold(_g);
+ g->buttons[STOP_BUTTON]->sensitivity = true;
+ g->buttons[RELOAD_BUTTON]->sensitivity = false;
+ nsgtk_scaffolding_set_sensitivity(g);
+
+ scaffolding_update_context(g);
+
+ nsgtk_schedule(100, nsgtk_throb, g);
+}
+
+void gui_window_stop_throbber(struct gui_window* _g)
+{
+ struct nsgtk_scaffolding *g = nsgtk_get_scaffold(_g);
+ if (g == NULL)
+ return;
+ scaffolding_update_context(g);
+ nsgtk_schedule(-1, nsgtk_throb, g);
+ if (g->buttons[STOP_BUTTON] != NULL)
+ g->buttons[STOP_BUTTON]->sensitivity = false;
+ if (g->buttons[RELOAD_BUTTON] != NULL)
+ g->buttons[RELOAD_BUTTON]->sensitivity = true;
+
+ nsgtk_scaffolding_set_sensitivity(g);
+
+ if ((g->throbber == NULL) || (nsgtk_throbber == NULL) ||
+ (nsgtk_throbber->framedata == NULL) ||
+ (nsgtk_throbber->framedata[0] == NULL))
+ return;
+ gtk_image_set_from_pixbuf(g->throbber, nsgtk_throbber->framedata[0]);
+}
+
+
+/**
+ * set favicon
+ */
+void
+nsgtk_scaffolding_set_icon(struct gui_window *gw)
+{
+ struct nsgtk_scaffolding *sc = nsgtk_get_scaffold(gw);
+ GdkPixbuf *icon_pixbuf = nsgtk_get_icon(gw);
+
+ /* check icon needs to be shown */
+ if ((icon_pixbuf == NULL) ||
+ (sc->top_level != gw)) {
+ return;
+ }
+
+ nsgtk_entry_set_icon_from_pixbuf(sc->url_bar,
+ GTK_ENTRY_ICON_PRIMARY,
+ icon_pixbuf);
+
+ gtk_widget_show_all(GTK_WIDGET(sc->buttons[URL_BAR_ITEM]->button));
+}
+
+static void
+nsgtk_scaffolding_set_websearch(struct nsgtk_scaffolding *g, const char *content)
+{
+ /** \todo this code appears technically correct, though
+ * currently has no effect at all.
+ */
+ PangoLayout *lo = gtk_entry_get_layout(GTK_ENTRY(g->webSearchEntry));
+ if (lo != NULL) {
+ pango_layout_set_font_description(lo, NULL);
+ PangoFontDescription *desc = pango_font_description_new();
+ if (desc != NULL) {
+ pango_font_description_set_style(desc,
+ PANGO_STYLE_ITALIC);
+ pango_font_description_set_family(desc, "Arial");
+ pango_font_description_set_weight(desc,
+ PANGO_WEIGHT_ULTRALIGHT);
+ pango_font_description_set_size(desc,
+ 10 * PANGO_SCALE);
+ pango_layout_set_font_description(lo, desc);
+ }
+
+ PangoAttrList *list = pango_attr_list_new();
+ if (list != NULL) {
+ PangoAttribute *italic = pango_attr_style_new(
+ PANGO_STYLE_ITALIC);
+ if (italic != NULL) {
+ italic->start_index = 0;
+ italic->end_index = strlen(content);
+ }
+ PangoAttribute *grey = pango_attr_foreground_new(
+ 0x7777, 0x7777, 0x7777);
+ if (grey != NULL) {
+ grey->start_index = 0;
+ grey->end_index = strlen(content);
+ }
+ pango_attr_list_insert(list, italic);
+ pango_attr_list_insert(list, grey);
+ pango_layout_set_attributes(lo, list);
+ pango_attr_list_unref(list);
+ }
+ pango_layout_set_text(lo, content, -1);
+ }
+/* an alternative method */
+/* char *parse = malloc(strlen(content) + 1);
+ PangoAttrList *list = pango_layout_get_attributes(lo);
+ char *markup = g_strconcat("<span foreground='#777777'><i>", content,
+ "</i></span>", NULL);
+ pango_parse_markup(markup, -1, 0, &list, &parse, NULL, NULL);
+ gtk_widget_show_all(g->webSearchEntry);
+*/
+ gtk_entry_set_visibility(GTK_ENTRY(g->webSearchEntry), TRUE);
+ gtk_entry_set_text(GTK_ENTRY(g->webSearchEntry), content);
+}
+
+/**
+ * GTK UI callback when search provider details are updated.
+ *
+ * \param provider_name The providers name.
+ * \param provider_bitmap The bitmap representing the provider.
+ * \return NSERROR_OK on success else error code.
+ */
+static nserror
+gui_search_web_provider_update(const char *provider_name,
+ struct bitmap *provider_bitmap)
+{
+ struct nsgtk_scaffolding *current;
+ GdkPixbuf *srch_pixbuf = NULL;
+ char *searchcontent;
+
+ LOG("name:%s bitmap %p", provider_name, provider_bitmap);
+
+ if (provider_bitmap != NULL) {
+ srch_pixbuf = nsgdk_pixbuf_get_from_surface(provider_bitmap->surface, 16, 16);
+
+ if (srch_pixbuf == NULL) {
+ return NSERROR_NOMEM;
+ }
+ }
+
+ /* setup the search content name */
+ searchcontent = malloc(strlen(provider_name) + SLEN("Search ") + 1);
+ if (searchcontent != NULL) {
+ sprintf(searchcontent, "Search %s", provider_name);
+ }
+
+ /* set the search provider parameters up in each scaffold */
+ for (current = scaf_list; current != NULL; current = current->next) {
+ /* add ico to each window's toolbar */
+ if (srch_pixbuf != NULL) {
+ nsgtk_entry_set_icon_from_pixbuf(current->webSearchEntry,
+ GTK_ENTRY_ICON_PRIMARY,
+ srch_pixbuf);
+ } else {
+ nsgtk_entry_set_icon_from_stock(current->webSearchEntry,
+ GTK_ENTRY_ICON_PRIMARY,
+ NSGTK_STOCK_FIND);
+ }
+
+ /* set search entry text */
+ if (searchcontent != NULL) {
+ nsgtk_scaffolding_set_websearch(current, searchcontent);
+ } else {
+ nsgtk_scaffolding_set_websearch(current, provider_name);
+ }
+ }
+
+ free(searchcontent);
+
+ if (srch_pixbuf != NULL) {
+ g_object_unref(srch_pixbuf);
+ }
+
+ return NSERROR_OK;
+}
+
+static struct gui_search_web_table search_web_table = {
+ .provider_update = gui_search_web_provider_update,
+};
+
+struct gui_search_web_table *nsgtk_search_web_table = &search_web_table;
+
+/* exported interface documented in gtk/scaffolding.h */
+GtkWindow* nsgtk_scaffolding_window(struct nsgtk_scaffolding *g)
+{
+ return g->window;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+GtkNotebook* nsgtk_scaffolding_notebook(struct nsgtk_scaffolding *g)
+{
+ return g->notebook;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+GtkWidget *nsgtk_scaffolding_urlbar(struct nsgtk_scaffolding *g)
+{
+ return g->url_bar;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+GtkWidget *nsgtk_scaffolding_websearch(struct nsgtk_scaffolding *g)
+{
+ return g->webSearchEntry;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+GtkToolbar *nsgtk_scaffolding_toolbar(struct nsgtk_scaffolding *g)
+{
+ return g->tool_bar;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+struct nsgtk_button_connect *
+nsgtk_scaffolding_button(struct nsgtk_scaffolding *g, int i)
+{
+ return g->buttons[i];
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+struct gtk_search *nsgtk_scaffolding_search(struct nsgtk_scaffolding *g)
+{
+ return g->search;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+GtkMenuBar *nsgtk_scaffolding_menu_bar(struct nsgtk_scaffolding *g)
+{
+ return g->menu_bar->bar_menu;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+struct gtk_history_window *
+nsgtk_scaffolding_history_window(struct nsgtk_scaffolding *g)
+{
+ return g->history_window;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+struct nsgtk_scaffolding *nsgtk_scaffolding_iterate(struct nsgtk_scaffolding *g)
+{
+ if (g == NULL) {
+ return scaf_list;
+ }
+ return g->next;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+void nsgtk_scaffolding_reset_offset(struct nsgtk_scaffolding *g)
+{
+ g->offset = 0;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+void nsgtk_scaffolding_update_url_bar_ref(struct nsgtk_scaffolding *g)
+{
+ g->url_bar = GTK_WIDGET(gtk_bin_get_child(GTK_BIN(
+ nsgtk_scaffolding_button(g, URL_BAR_ITEM)->button)));
+
+ gtk_entry_set_completion(GTK_ENTRY(g->url_bar),
+ g->url_bar_completion);
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+void nsgtk_scaffolding_update_throbber_ref(struct nsgtk_scaffolding *g)
+{
+ g->throbber = GTK_IMAGE(gtk_bin_get_child(
+ GTK_BIN(g->buttons[THROBBER_ITEM]->button)));
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+void nsgtk_scaffolding_update_websearch_ref(struct nsgtk_scaffolding *g)
+{
+ g->webSearchEntry = gtk_bin_get_child(GTK_BIN(
+ g->buttons[WEBSEARCH_ITEM]->button));
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+void nsgtk_scaffolding_toggle_search_bar_visibility(struct nsgtk_scaffolding *g)
+{
+ gboolean vis;
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+
+ g_object_get(G_OBJECT(g->search->bar), "visible", &vis, NULL);
+ if (vis) {
+ if (bw != NULL) {
+ browser_window_search_clear(bw);
+ }
+
+ gtk_widget_hide(GTK_WIDGET(g->search->bar));
+ } else {
+ gtk_widget_show(GTK_WIDGET(g->search->bar));
+ gtk_widget_grab_focus(GTK_WIDGET(g->search->entry));
+ }
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+struct gui_window *nsgtk_scaffolding_top_level(struct nsgtk_scaffolding *g)
+{
+ return g->top_level;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+void nsgtk_scaffolding_set_top_level(struct gui_window *gw)
+{
+ struct browser_window *bw;
+ struct nsgtk_scaffolding *sc;
+
+ assert(gw != NULL);
+
+ bw = nsgtk_get_browser_window(gw);
+
+ assert(bw != NULL);
+
+ sc = nsgtk_get_scaffold(gw);
+ assert(sc != NULL);
+
+ sc->top_level = gw;
+
+ /* Synchronise the history (will also update the URL bar) */
+ scaffolding_update_context(sc);
+
+ /* clear effects of potential searches */
+ browser_window_search_clear(bw);
+
+ nsgtk_scaffolding_set_icon(gw);
+
+ /* Ensure the window's title bar is updated */
+ nsgtk_window_set_title(gw, browser_window_get_title(bw));
+
+}
+
+/* exported interface documented in scaffolding.h */
+void nsgtk_scaffolding_set_sensitivity(struct nsgtk_scaffolding *g)
+{
+ int i;
+#define SENSITIVITY(q)\
+ i = q##_BUTTON;\
+ if (g->buttons[i]->main != NULL)\
+ gtk_widget_set_sensitive(GTK_WIDGET(\
+ g->buttons[i]->main),\
+ g->buttons[i]->sensitivity);\
+ if (g->buttons[i]->rclick != NULL)\
+ gtk_widget_set_sensitive(GTK_WIDGET(\
+ g->buttons[i]->rclick),\
+ g->buttons[i]->sensitivity);\
+ if ((g->buttons[i]->location != -1) && \
+ (g->buttons[i]->button != NULL))\
+ gtk_widget_set_sensitive(GTK_WIDGET(\
+ g->buttons[i]->button),\
+ g->buttons[i]->sensitivity);\
+ if (g->buttons[i]->popup != NULL)\
+ gtk_widget_set_sensitive(GTK_WIDGET(\
+ g->buttons[i]->popup),\
+ g->buttons[i]->sensitivity);
+
+ SENSITIVITY(STOP)
+ SENSITIVITY(RELOAD)
+ SENSITIVITY(CUT)
+ SENSITIVITY(COPY)
+ SENSITIVITY(PASTE)
+ SENSITIVITY(BACK)
+ SENSITIVITY(FORWARD)
+ SENSITIVITY(NEXTTAB)
+ SENSITIVITY(PREVTAB)
+ SENSITIVITY(CLOSETAB)
+#undef SENSITIVITY
+}
+
+
+/* exported interface documented in gtk/scaffolding.h */
+void nsgtk_scaffolding_context_menu(struct nsgtk_scaffolding *g,
+ gdouble x,
+ gdouble y)
+{
+ GtkMenu *gtkmenu;
+
+ /* update the global context menu features */
+ browser_window_get_features(nsgtk_get_browser_window(g->top_level),
+ x, y, &current_menu_features);
+
+ if (current_menu_features.link != NULL) {
+ /* menu is opening over a link */
+ gtkmenu = g->link_menu->link_menu;
+ } else {
+ gtkmenu = g->menu_popup->popup_menu;
+
+ nsgtk_scaffolding_update_edit_actions_sensitivity(g);
+
+ if (!(g->buttons[COPY_BUTTON]->sensitivity)) {
+ gtk_widget_hide(GTK_WIDGET(g->menu_popup->copy_menuitem));
+ } else {
+ gtk_widget_show(GTK_WIDGET(g->menu_popup->copy_menuitem));
+ }
+
+ if (!(g->buttons[CUT_BUTTON]->sensitivity)) {
+ gtk_widget_hide(GTK_WIDGET(g->menu_popup->cut_menuitem));
+ } else {
+ gtk_widget_show(GTK_WIDGET(g->menu_popup->cut_menuitem));
+ }
+
+ if (!(g->buttons[PASTE_BUTTON]->sensitivity)) {
+ gtk_widget_hide(GTK_WIDGET(g->menu_popup->paste_menuitem));
+ } else {
+ gtk_widget_show(GTK_WIDGET(g->menu_popup->paste_menuitem));
+ }
+
+ /* hide customize */
+ popup_menu_hide(g->menu_popup, false, false, false, true);
+ }
+
+ gtk_menu_popup(gtkmenu, NULL, NULL, NULL, NULL, 0,
+ gtk_get_current_event_time());
+}
+
+/**
+ * reallocate width for history button, reallocate buttons right of history;
+ * memorise base of history button / toolbar
+ */
+void nsgtk_scaffolding_toolbar_size_allocate(GtkWidget *widget,
+ GtkAllocation *alloc, gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ int i = nsgtk_toolbar_get_id_from_widget(widget, g);
+ if (i == -1)
+ return;
+ if ((g->toolbarmem == alloc->x) ||
+ (g->buttons[i]->location <
+ g->buttons[HISTORY_BUTTON]->location))
+ /* no reallocation after first adjustment, no reallocation for buttons
+ * left of history button */
+ return;
+ if (widget == GTK_WIDGET(g->buttons[HISTORY_BUTTON]->button)) {
+ if (alloc->width == 20)
+ return;
+
+ g->toolbarbase = alloc->y + alloc->height;
+ g->historybase = alloc->x + 20;
+ if (g->offset == 0)
+ g->offset = alloc->width - 20;
+ alloc->width = 20;
+ } else if (g->buttons[i]->location <=
+ g->buttons[URL_BAR_ITEM]->location) {
+ alloc->x -= g->offset;
+ if (i == URL_BAR_ITEM)
+ alloc->width += g->offset;
+ }
+ g->toolbarmem = alloc->x;
+ gtk_widget_size_allocate(widget, alloc);
+}
diff --git a/frontends/gtk/scaffolding.h b/frontends/gtk/scaffolding.h
new file mode 100644
index 000000000..e1fd9bf2a
--- /dev/null
+++ b/frontends/gtk/scaffolding.h
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_GTK_SCAFFOLDING_H
+#define NETSURF_GTK_SCAFFOLDING_H 1
+
+#include <stdbool.h>
+#include "utils/errors.h"
+
+struct bitmap;
+struct hlcache_handle;
+struct gui_window;
+struct gui_search_web_table;
+struct nsurl;
+
+extern struct gui_search_web_table *nsgtk_search_web_table;
+
+typedef enum {
+ BACK_BUTTON = 0,
+ HISTORY_BUTTON,
+ FORWARD_BUTTON,
+ STOP_BUTTON,
+ RELOAD_BUTTON,
+ HOME_BUTTON,
+ URL_BAR_ITEM,
+ WEBSEARCH_ITEM,
+ THROBBER_ITEM,
+ NEWWINDOW_BUTTON,
+ NEWTAB_BUTTON,
+ OPENFILE_BUTTON,
+ CLOSETAB_BUTTON,
+ CLOSEWINDOW_BUTTON,
+ SAVEPAGE_BUTTON,
+ PDF_BUTTON,
+ PLAINTEXT_BUTTON,
+ DRAWFILE_BUTTON,
+ POSTSCRIPT_BUTTON,
+ PRINTPREVIEW_BUTTON,
+ PRINT_BUTTON,
+ QUIT_BUTTON,
+ CUT_BUTTON,
+ COPY_BUTTON,
+ PASTE_BUTTON,
+ DELETE_BUTTON,
+ SELECTALL_BUTTON,
+ FIND_BUTTON,
+ PREFERENCES_BUTTON,
+ ZOOMPLUS_BUTTON,
+ ZOOMMINUS_BUTTON,
+ ZOOMNORMAL_BUTTON,
+ FULLSCREEN_BUTTON,
+ VIEWSOURCE_BUTTON,
+ DOWNLOADS_BUTTON,
+ SAVEWINDOWSIZE_BUTTON,
+ TOGGLEDEBUGGING_BUTTON,
+ SAVEBOXTREE_BUTTON,
+ SAVEDOMTREE_BUTTON,
+ LOCALHISTORY_BUTTON,
+ GLOBALHISTORY_BUTTON,
+ ADDBOOKMARKS_BUTTON,
+ SHOWBOOKMARKS_BUTTON,
+ SHOWCOOKIES_BUTTON,
+ OPENLOCATION_BUTTON,
+ NEXTTAB_BUTTON,
+ PREVTAB_BUTTON,
+ CONTENTS_BUTTON,
+ GUIDE_BUTTON,
+ INFO_BUTTON,
+ ABOUT_BUTTON,
+ PLACEHOLDER_BUTTON /* size indicator; array maximum indices */
+} nsgtk_toolbar_button; /* PLACEHOLDER_BUTTON - 1 */
+
+struct gtk_history_window {
+ struct nsgtk_scaffolding *g;
+ GtkWindow *window;
+ GtkScrolledWindow *scrolled;
+ GtkDrawingArea *drawing_area;
+};
+
+struct gtk_search {
+ GtkToolbar *bar;
+ GtkEntry *entry;
+ GtkToolButton *buttons[3]; /* back, forward, */
+ GtkCheckButton *checkAll; /* close */
+ GtkCheckButton *caseSens;
+};
+
+struct nsgtk_button_connect {
+ GtkToolItem *button;
+ int location; /* in toolbar */
+ bool sensitivity;
+ GtkWidget *main; /* left click menu entry */
+ GtkWidget *rclick; /* right click menu */
+ GtkWidget *popup; /* popup menu entry */
+ void *mhandler; /* menu item clicked */
+ void *bhandler; /* button clicked */
+ void *dataplus; /* customization -> toolbar */
+ void *dataminus; /* customization -> store */
+};
+
+/**
+ * create a new scaffolding for a window.
+ *
+ * \param gw The gui window to create the new scaffold around.
+ * \return The newly constructed scaffold or NULL on error.
+ */
+struct nsgtk_scaffolding *nsgtk_new_scaffolding(struct gui_window *gw);
+
+/**
+ * Obtain the most recently used scaffolding element.
+ *
+ * This allows tabs to be opened in the most recently used window
+ */
+struct nsgtk_scaffolding *nsgtk_current_scaffolding(void);
+
+/* acessors for gtk elements within a scaffold */
+
+/**
+ * Get the gtk window for a scaffolding.
+ */
+GtkWindow *nsgtk_scaffolding_window(struct nsgtk_scaffolding *g);
+
+/**
+ * Get the gtk notebook from a scaffold.
+ */
+GtkNotebook *nsgtk_scaffolding_notebook(struct nsgtk_scaffolding *g);
+
+/**
+ * Get the gtk url bar from a scaffold.
+ */
+GtkWidget *nsgtk_scaffolding_urlbar(struct nsgtk_scaffolding *g);
+
+/**
+ * Get the gtk web search entry from a scaffold.
+ */
+GtkWidget *nsgtk_scaffolding_websearch(struct nsgtk_scaffolding *g);
+
+/**
+ * Get the gtk toolbar from a scaffold.
+ */
+GtkToolbar *nsgtk_scaffolding_toolbar(struct nsgtk_scaffolding *g);
+
+
+struct nsgtk_button_connect *nsgtk_scaffolding_button(struct nsgtk_scaffolding *g, int i);
+
+struct gtk_search *nsgtk_scaffolding_search(struct nsgtk_scaffolding *g);
+
+GtkMenuBar *nsgtk_scaffolding_menu_bar(struct nsgtk_scaffolding *g);
+
+struct gtk_history_window *nsgtk_scaffolding_history_window(struct nsgtk_scaffolding *g);
+
+struct gui_window *nsgtk_scaffolding_top_level(struct nsgtk_scaffolding *g);
+
+/**
+ * reset the scaffold offset value to 0.
+ *
+ * \todo The value is only ever altered in
+ * nsgtk_scaffolding_toolbar_size_allocate and is something to do with
+ * the history button either clarify or remove!
+ */
+void nsgtk_scaffolding_reset_offset(struct nsgtk_scaffolding *g);
+
+/**
+ * Iterate through available scaffolding.
+ */
+struct nsgtk_scaffolding *nsgtk_scaffolding_iterate(struct nsgtk_scaffolding *g);
+
+void nsgtk_scaffolding_update_url_bar_ref(struct nsgtk_scaffolding *g);
+
+void nsgtk_scaffolding_update_throbber_ref(struct nsgtk_scaffolding *g);
+
+void nsgtk_scaffolding_update_websearch_ref(struct nsgtk_scaffolding *g);
+
+void nsgtk_scaffolding_toggle_search_bar_visibility(struct nsgtk_scaffolding *g);
+
+/**
+ * Set the current active top level gui window.
+ */
+void nsgtk_scaffolding_set_top_level(struct gui_window *g);
+
+/**
+ * update the sensitivity of context sensitive UI elements
+ *
+ * widgets altered in arrays:
+ * main
+ * right click menu
+ * location
+ * popup
+ * current arrays are:
+ * stop
+ * reload
+ * cut
+ * copy
+ * paste
+ * back
+ * forward
+ * nexttab
+ * prevtab
+ * closetab
+ */
+void nsgtk_scaffolding_set_sensitivity(struct nsgtk_scaffolding *g);
+
+/**
+ * Open a context sensitive menu.
+ *
+ * \param g the scaffolding containing the browser window.
+ * \param x The x co-ordinate.
+ * \param y The y co-ordinate.
+ */
+void nsgtk_scaffolding_context_menu(struct nsgtk_scaffolding *g, gdouble x, gdouble y);
+
+void nsgtk_scaffolding_toolbar_size_allocate(GtkWidget *widget, GtkAllocation *alloc, gpointer data);
+
+void nsgtk_scaffolding_set_icon(struct gui_window *gw);
+
+gboolean nsgtk_window_url_activate_event(GtkWidget *, gpointer);
+
+gboolean nsgtk_window_url_changed(GtkWidget *, GdkEventKey *, gpointer);
+
+nserror nsgtk_scaffolding_new_tab(struct gui_window *gw);
+
+/* core acessors */
+/**
+ * set the title in the window
+ *
+ * \param gw The gui window to set title on
+ * \param title The title to set which may be NULL
+ */
+void nsgtk_window_set_title(struct gui_window *gw, const char *title);
+
+nserror gui_window_set_url(struct gui_window *g, struct nsurl *url);
+void gui_window_start_throbber(struct gui_window *g);
+void gui_window_stop_throbber(struct gui_window *g);
+
+void nsgtk_scaffolding_toolbars(struct nsgtk_scaffolding *g, int tbi);
+
+#endif /* NETSURF_GTK_SCAFFOLDING_H */
diff --git a/frontends/gtk/schedule.c b/frontends/gtk/schedule.c
new file mode 100644
index 000000000..cf0333388
--- /dev/null
+++ b/frontends/gtk/schedule.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2006-2007 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <glib.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "utils/errors.h"
+
+#include "gtk/schedule.h"
+
+#ifdef DEBUG_GTK_SCHEDULE
+#include "utils/log.h"
+#else
+#define LOG(format, args...) ((void) 0)
+#endif
+
+/** Killable callback closure embodiment. */
+typedef struct {
+ void (*callback)(void *); /**< The callback function. */
+ void *context; /**< The context for the callback. */
+ bool callback_killed; /**< Whether or not this was killed. */
+} _nsgtk_callback_t;
+
+/** List of callbacks which have occurred and are pending running. */
+static GList *pending_callbacks = NULL;
+/** List of callbacks which are queued to occur in the future. */
+static GList *queued_callbacks = NULL;
+/** List of callbacks which are about to be run in this ::schedule_run. */
+static GList *this_run = NULL;
+
+static gboolean
+nsgtk_schedule_generic_callback(gpointer data)
+{
+ _nsgtk_callback_t *cb = (_nsgtk_callback_t *)(data);
+ if (cb->callback_killed) {
+ /* This callback instance has been killed. */
+ LOG("CB at %p already dead.", cb);
+ }
+ queued_callbacks = g_list_remove(queued_callbacks, cb);
+ pending_callbacks = g_list_append(pending_callbacks, cb);
+ return FALSE;
+}
+
+static void
+nsgtk_schedule_kill_callback(void *_target, void *_match)
+{
+ _nsgtk_callback_t *target = (_nsgtk_callback_t *)_target;
+ _nsgtk_callback_t *match = (_nsgtk_callback_t *)_match;
+ if ((target->callback == match->callback) &&
+ (target->context == match->context)) {
+ LOG("Found match for %p(%p), killing.", target->callback, target->context);
+ target->callback = NULL;
+ target->context = NULL;
+ target->callback_killed = true;
+ }
+}
+
+static void
+schedule_remove(void (*callback)(void *p), void *p)
+{
+ _nsgtk_callback_t cb_match = {
+ .callback = callback,
+ .context = p,
+ };
+
+ g_list_foreach(queued_callbacks,
+ nsgtk_schedule_kill_callback, &cb_match);
+ g_list_foreach(pending_callbacks,
+ nsgtk_schedule_kill_callback, &cb_match);
+ g_list_foreach(this_run,
+ nsgtk_schedule_kill_callback, &cb_match);
+}
+
+/* exported interface documented in gtk/schedule.h */
+nserror nsgtk_schedule(int t, void (*callback)(void *p), void *p)
+{
+ _nsgtk_callback_t *cb;
+
+ /* Kill any pending schedule of this kind. */
+ schedule_remove(callback, p);
+
+ if (t < 0) {
+ return NSERROR_OK;
+ }
+
+ cb = malloc(sizeof(_nsgtk_callback_t));
+ cb->callback = callback;
+ cb->context = p;
+ cb->callback_killed = false;
+ /* Prepend is faster right now. */
+ queued_callbacks = g_list_prepend(queued_callbacks, cb);
+ g_timeout_add(t, nsgtk_schedule_generic_callback, cb);
+
+ return NSERROR_OK;
+}
+
+bool
+schedule_run(void)
+{
+ /* Capture this run of pending callbacks into the list. */
+ this_run = pending_callbacks;
+
+ if (this_run == NULL)
+ return false; /* Nothing to do */
+
+ /* Clear the pending list. */
+ pending_callbacks = NULL;
+
+ LOG("Captured a run of %d callbacks to fire.", g_list_length(this_run));
+
+ /* Run all the callbacks which made it this far. */
+ while (this_run != NULL) {
+ _nsgtk_callback_t *cb = (_nsgtk_callback_t *)(this_run->data);
+ this_run = g_list_remove(this_run, this_run->data);
+ if (!cb->callback_killed)
+ cb->callback(cb->context);
+ free(cb);
+ }
+ return true;
+}
diff --git a/frontends/gtk/schedule.h b/frontends/gtk/schedule.h
new file mode 100644
index 000000000..0a2d724d4
--- /dev/null
+++ b/frontends/gtk/schedule.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2006 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_GTK_CALLBACK_H
+#define NETSURF_GTK_CALLBACK_H 1
+
+nserror nsgtk_schedule(int t, void (*callback)(void *p), void *p);
+
+bool schedule_run(void);
+
+#endif /* NETSURF_GTK_CALLBACK_H */
diff --git a/frontends/gtk/search.c b/frontends/gtk/search.c
new file mode 100644
index 000000000..d2adcf1b5
--- /dev/null
+++ b/frontends/gtk/search.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+
+ /** \file
+ * Free text search (front component)
+ */
+#include <stdint.h>
+#include <ctype.h>
+#include <string.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "utils/config.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "content/content.h"
+#include "content/hlcache.h"
+#include "desktop/browser.h"
+#include "desktop/search.h"
+#include "desktop/searchweb.h"
+#include "desktop/gui_search.h"
+
+#include "gtk/warn.h"
+#include "gtk/compat.h"
+#include "gtk/search.h"
+#include "gtk/scaffolding.h"
+#include "gtk/window.h"
+
+/**
+ * activate search forwards button in gui.
+ *
+ * \param active activate/inactivate
+ * \param gw The gui window in which to activite the search button in.
+ */
+static void nsgtk_search_set_forward_state(bool active, struct gui_window *gw)
+{
+ if (gw != NULL && nsgtk_get_browser_window(gw) != NULL) {
+ struct nsgtk_scaffolding *g = nsgtk_get_scaffold(gw);
+ gtk_widget_set_sensitive(
+ GTK_WIDGET(nsgtk_scaffolding_search(g)->buttons[1]),
+ active);
+ }
+}
+
+/**
+ * activate search back button in gui.
+ *
+ * \param active activate/inactivate
+ * \param gw The gui window in which to activite the search button in.
+ */
+static void nsgtk_search_set_back_state(bool active, struct gui_window *gw)
+{
+ if (gw != NULL && nsgtk_get_browser_window(gw) != NULL) {
+ struct nsgtk_scaffolding *g = nsgtk_get_scaffold(gw);
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_search(
+ g)->buttons[0]), active);
+ }
+}
+
+/** connected to the search forward button */
+
+gboolean nsgtk_search_forward_button_clicked(GtkWidget *widget, gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ struct gui_window *gw = nsgtk_scaffolding_top_level(g);
+ struct browser_window *bw = nsgtk_get_browser_window(gw);
+
+ assert(bw);
+
+ search_flags_t flags = SEARCH_FLAG_FORWARDS |
+ (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
+ nsgtk_scaffolding_search(g)->caseSens)) ?
+ SEARCH_FLAG_CASE_SENSITIVE : 0) |
+ (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
+ nsgtk_scaffolding_search(g)->checkAll)) ?
+ SEARCH_FLAG_SHOWALL : 0);
+
+ browser_window_search(bw, gw, flags,
+ gtk_entry_get_text(nsgtk_scaffolding_search(g)->entry));
+ return TRUE;
+}
+
+/** connected to the search back button */
+
+gboolean nsgtk_search_back_button_clicked(GtkWidget *widget, gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ struct gui_window *gw = nsgtk_scaffolding_top_level(g);
+ struct browser_window *bw = nsgtk_get_browser_window(gw);
+
+ assert(bw);
+
+ search_flags_t flags = 0 |(gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(
+ nsgtk_scaffolding_search(g)->caseSens)) ?
+ SEARCH_FLAG_CASE_SENSITIVE : 0) |
+ (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
+ nsgtk_scaffolding_search(g)->checkAll)) ?
+ SEARCH_FLAG_SHOWALL : 0);
+
+ browser_window_search(bw, gw, flags,
+ gtk_entry_get_text(nsgtk_scaffolding_search(g)->entry));
+ return TRUE;
+}
+
+/** connected to the search close button */
+
+gboolean nsgtk_search_close_button_clicked(GtkWidget *widget, gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ nsgtk_scaffolding_toggle_search_bar_visibility(g);
+ return TRUE;
+}
+
+/** connected to the search entry [typing] */
+
+gboolean nsgtk_search_entry_changed(GtkWidget *widget, gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ struct gui_window *gw = nsgtk_scaffolding_top_level(g);
+ struct browser_window *bw = nsgtk_get_browser_window(gw);
+ search_flags_t flags;
+
+ assert(bw != NULL);
+
+ nsgtk_search_set_forward_state(true, gw);
+ nsgtk_search_set_back_state(true, gw);
+
+ flags = SEARCH_FLAG_FORWARDS |
+ (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
+ nsgtk_scaffolding_search(g)->caseSens)) ?
+ SEARCH_FLAG_CASE_SENSITIVE : 0) |
+ (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
+ nsgtk_scaffolding_search(g)->checkAll)) ?
+ SEARCH_FLAG_SHOWALL : 0);
+
+ browser_window_search(bw, gw, flags,
+ gtk_entry_get_text(nsgtk_scaffolding_search(g)->entry));
+ return TRUE;
+}
+
+/** connected to the search entry [return key] */
+
+gboolean nsgtk_search_entry_activate(GtkWidget *widget, gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ struct gui_window *gw = nsgtk_scaffolding_top_level(g);
+ struct browser_window *bw = nsgtk_get_browser_window(gw);
+ search_flags_t flags;
+
+ assert(bw);
+
+ flags = SEARCH_FLAG_FORWARDS |
+ (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
+ nsgtk_scaffolding_search(g)->caseSens)) ?
+ SEARCH_FLAG_CASE_SENSITIVE : 0) |
+ (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
+ nsgtk_scaffolding_search(g)->checkAll)) ?
+ SEARCH_FLAG_SHOWALL : 0);
+
+ browser_window_search(bw, gw, flags,
+ gtk_entry_get_text(nsgtk_scaffolding_search(g)->entry));
+ return FALSE;
+}
+
+/** allows escape key to close search bar too */
+
+gboolean
+nsgtk_search_entry_key(GtkWidget *widget, GdkEventKey *event, gpointer data)
+{
+ if (event->keyval == GDK_KEY(Escape)) {
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ nsgtk_scaffolding_toggle_search_bar_visibility(g);
+ }
+ return FALSE;
+}
+
+/** connected to the websearch entry [return key] */
+
+gboolean nsgtk_websearch_activate(GtkWidget *widget, gpointer data)
+{
+ struct nsgtk_scaffolding *g = data;
+ nserror ret;
+ nsurl *url;
+
+ ret = search_web_omni(
+ gtk_entry_get_text(GTK_ENTRY(nsgtk_scaffolding_websearch(g))),
+ SEARCH_WEB_OMNI_SEARCHONLY,
+ &url);
+ if (ret == NSERROR_OK) {
+ temp_open_background = 0;
+ ret = browser_window_create(
+ BW_CREATE_HISTORY | BW_CREATE_TAB,
+ url,
+ NULL,
+ nsgtk_get_browser_window(nsgtk_scaffolding_top_level(g)),
+ NULL);
+ temp_open_background = -1;
+ nsurl_unref(url);
+ }
+ if (ret != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(ret), 0);
+ }
+
+ return TRUE;
+}
+
+/**
+ * allows a click in the websearch entry field to clear the name of the
+ * provider
+ */
+
+gboolean nsgtk_websearch_clear(GtkWidget *widget, GdkEventFocus *f,
+ gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ gtk_editable_select_region(GTK_EDITABLE(
+ nsgtk_scaffolding_websearch(g)), 0, -1);
+ gtk_widget_grab_focus(GTK_WIDGET(nsgtk_scaffolding_websearch(g)));
+ return TRUE;
+}
+
+
+
+static struct gui_search_table search_table = {
+ .forward_state = (void *)nsgtk_search_set_forward_state,
+ .back_state = (void *)nsgtk_search_set_back_state,
+};
+
+struct gui_search_table *nsgtk_search_table = &search_table;
diff --git a/frontends/gtk/search.h b/frontends/gtk/search.h
new file mode 100644
index 000000000..dd8c60d0f
--- /dev/null
+++ b/frontends/gtk/search.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_GTK_SEARCH_H_
+#define _NETSURF_GTK_SEARCH_H_
+
+struct gui_search_table *nsgtk_search_table;
+
+struct nsgtk_scaffolding;
+
+void nsgtk_search_bar_toggle_visibility(struct nsgtk_scaffolding * g);
+gboolean nsgtk_search_entry_changed(GtkWidget *widget, gpointer data);
+gboolean nsgtk_search_entry_activate(GtkWidget *widget, gpointer data);
+gboolean nsgtk_search_entry_key(GtkWidget *widget, GdkEventKey *event, gpointer data);
+gboolean nsgtk_search_forward_button_clicked(GtkWidget *widget, gpointer data);
+gboolean nsgtk_search_back_button_clicked(GtkWidget *widget, gpointer data);
+gboolean nsgtk_search_close_button_clicked(GtkWidget *widget, gpointer data);
+gboolean nsgtk_websearch_activate(GtkWidget *widget, gpointer data);
+gboolean nsgtk_websearch_clear(GtkWidget *widget, GdkEventFocus *f, gpointer data);
+
+#endif
diff --git a/frontends/gtk/selection.c b/frontends/gtk/selection.c
new file mode 100644
index 000000000..d1388ed27
--- /dev/null
+++ b/frontends/gtk/selection.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2008 Mike Lester <element3260@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <string.h>
+#include <gtk/gtk.h>
+#include <stdlib.h>
+
+#include "utils/log.h"
+#include "desktop/browser.h"
+#include "desktop/gui_clipboard.h"
+
+#include "gtk/window.h"
+
+static GString *current_selection = NULL;
+static GtkClipboard *clipboard;
+
+
+/**
+ * Core asks front end for clipboard contents.
+ *
+ * \param buffer UTF-8 text, allocated by front end, ownership yeilded to core
+ * \param length Byte length of UTF-8 text in buffer
+ */
+static void gui_get_clipboard(char **buffer, size_t *length)
+{
+ gchar *gtext;
+
+ *buffer = NULL;
+ *length = 0;
+
+ /* get clipboard contents from gtk */
+ clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+ gtext = gtk_clipboard_wait_for_text(clipboard); /* conv to utf-8 */
+
+ if (gtext == NULL)
+ return;
+
+ *length = strlen(gtext);
+ *buffer = malloc(*length);
+ if (*buffer == NULL) {
+ *length = 0;
+ g_free(gtext);
+ return;
+ }
+
+ memcpy(*buffer, gtext, *length);
+
+ g_free(gtext);
+}
+
+
+/**
+ * Core tells front end to put given text in clipboard
+ *
+ * \param buffer UTF-8 text, owned by core
+ * \param length Byte length of UTF-8 text in buffer
+ * \param styles Array of styles given to text runs, owned by core, or NULL
+ * \param n_styles Number of text run styles in array
+ */
+static void gui_set_clipboard(const char *buffer, size_t length,
+ nsclipboard_styles styles[], int n_styles)
+{
+ clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+
+ if (!current_selection)
+ current_selection = g_string_new(NULL);
+ else
+ g_string_set_size(current_selection, 0);
+
+ current_selection = g_string_append_len(current_selection,
+ buffer, length);
+
+ gtk_clipboard_set_text(clipboard, current_selection->str, -1);
+}
+
+static struct gui_clipboard_table clipboard_table = {
+ .get = gui_get_clipboard,
+ .set = gui_set_clipboard,
+};
+
+struct gui_clipboard_table *nsgtk_clipboard_table = &clipboard_table;
diff --git a/frontends/gtk/selection.h b/frontends/gtk/selection.h
new file mode 100644
index 000000000..6463692cf
--- /dev/null
+++ b/frontends/gtk/selection.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2008 Mike Lester <element3260@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 GTK_SELECTION_H
+#define GTK_SELECTION_H
+
+struct gui_clipboard_table *nsgtk_clipboard_table;
+
+#endif
diff --git a/frontends/gtk/sexy_icon_entry.c b/frontends/gtk/sexy_icon_entry.c
new file mode 100644
index 000000000..fff650cc4
--- /dev/null
+++ b/frontends/gtk/sexy_icon_entry.c
@@ -0,0 +1,982 @@
+/*
+ * libsexy/sexy-icon-entry.c Entry widget
+ * Copyright (C) 2004-2006 Christian Hammond.
+ * modified for NetSurf
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ * This file before modifications was originally part of LibSexy,
+ * http://www.chipx86.com/; it is redistributed under GPLv2
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>
+ * or write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <gtk/gtk.h>
+
+#include "gtk/sexy_icon_entry.h"
+#include "gtk/compat.h"
+
+#define ICON_MARGIN 2
+#define MAX_ICONS 2
+
+#define IS_VALID_ICON_ENTRY_POSITION(pos) \
+ ((pos) == SEXY_ICON_ENTRY_PRIMARY || \
+ (pos) == SEXY_ICON_ENTRY_SECONDARY)
+
+typedef struct
+{
+ GtkImage *icon;
+ gboolean highlight;
+ gboolean hovered;
+ GdkWindow *window;
+
+} SexyIconInfo;
+
+struct _SexyIconEntryPriv
+{
+ SexyIconInfo icons[MAX_ICONS];
+
+ gulong icon_released_id;
+};
+
+enum
+{
+ ICON_PRESSED,
+ ICON_RELEASED,
+ LAST_SIGNAL
+};
+
+/* static void sexy_icon_entry_class_init(SexyIconEntryClass *klass); */
+static void sexy_icon_entry_editable_init(GtkEditableClass *iface);
+/* static void sexy_icon_entry_init(SexyIconEntry *entry); */
+static void sexy_icon_entry_finalize(GObject *obj);
+static void sexy_icon_entry_destroy(GtkObject *obj);
+static void sexy_icon_entry_map(GtkWidget *widget);
+static void sexy_icon_entry_unmap(GtkWidget *widget);
+static void sexy_icon_entry_realize(GtkWidget *widget);
+static void sexy_icon_entry_unrealize(GtkWidget *widget);
+static void sexy_icon_entry_size_request(GtkWidget *widget,
+ GtkRequisition *requisition);
+static void sexy_icon_entry_size_allocate(GtkWidget *widget,
+ GtkAllocation *allocation);
+static gint sexy_icon_entry_expose(GtkWidget *widget, GdkEventExpose *event);
+static gint sexy_icon_entry_enter_notify(GtkWidget *widget,
+ GdkEventCrossing *event);
+static gint sexy_icon_entry_leave_notify(GtkWidget *widget,
+ GdkEventCrossing *event);
+static gint sexy_icon_entry_button_press(GtkWidget *widget,
+ GdkEventButton *event);
+static gint sexy_icon_entry_button_release(GtkWidget *widget,
+ GdkEventButton *event);
+
+static GtkEntryClass *parent_class = NULL;
+static guint signals[LAST_SIGNAL] = {0};
+
+G_DEFINE_TYPE_EXTENDED(SexyIconEntry, sexy_icon_entry, GTK_TYPE_ENTRY,
+ 0,
+ G_IMPLEMENT_INTERFACE(GTK_TYPE_EDITABLE,
+ sexy_icon_entry_editable_init));
+
+void
+sexy_icon_entry_class_init(SexyIconEntryClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ GtkEntryClass *entry_class;
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ gobject_class = G_OBJECT_CLASS(klass);
+ object_class = GTK_OBJECT_CLASS(klass);
+ widget_class = GTK_WIDGET_CLASS(klass);
+ entry_class = GTK_ENTRY_CLASS(klass);
+
+ gobject_class->finalize = sexy_icon_entry_finalize;
+
+ object_class->destroy = sexy_icon_entry_destroy;
+
+ widget_class->map = sexy_icon_entry_map;
+ widget_class->unmap = sexy_icon_entry_unmap;
+ widget_class->realize = sexy_icon_entry_realize;
+ widget_class->unrealize = sexy_icon_entry_unrealize;
+ widget_class->size_request = sexy_icon_entry_size_request;
+ widget_class->size_allocate = sexy_icon_entry_size_allocate;
+ widget_class->expose_event = sexy_icon_entry_expose;
+ widget_class->enter_notify_event = sexy_icon_entry_enter_notify;
+ widget_class->leave_notify_event = sexy_icon_entry_leave_notify;
+ widget_class->button_press_event = sexy_icon_entry_button_press;
+ widget_class->button_release_event = sexy_icon_entry_button_release;
+
+ /*
+ * SexyIconEntry::icon-pressed:
+ * @entry: The entry on which the signal is emitted.
+ * @icon_pos: The position of the clicked icon.
+ * @button: The mouse button clicked.
+ *
+ * The ::icon-pressed signal is emitted when an icon is clicked.
+ */
+ /* signal modified to compile directly in NetSurf - param 8 of
+ * g_signal_new() changed from marshal type to NULL - so there may
+ * well be no working signal */
+ signals[ICON_PRESSED] =
+ g_signal_new("icon_pressed",
+ G_TYPE_FROM_CLASS(gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET(SexyIconEntryClass, icon_pressed),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 2,
+ G_TYPE_INT,
+ G_TYPE_INT);
+
+ /*
+ * SexyIconEntry::icon-released:
+ * @entry: The entry on which the signal is emitted.
+ * @icon_pos: The position of the clicked icon.
+ * @button: The mouse button clicked.
+ *
+ * The ::icon-released signal is emitted on the button release from a
+ * mouse click.
+ */
+ /* signal modified to compile directly in NetSurf - param 8 of
+ * g_signal_new() changed from marshal type to NULL - so there may
+ * well be no working signal */
+ signals[ICON_RELEASED] =
+ g_signal_new("icon_released",
+ G_TYPE_FROM_CLASS(gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET(SexyIconEntryClass, icon_released),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 2,
+ G_TYPE_INT,
+ G_TYPE_INT);
+}
+
+static void
+sexy_icon_entry_editable_init(GtkEditableClass *iface)
+{
+};
+
+void
+sexy_icon_entry_init(SexyIconEntry *entry)
+{
+ entry->priv = g_new0(SexyIconEntryPriv, 1);
+}
+
+static void
+sexy_icon_entry_finalize(GObject *obj)
+{
+ SexyIconEntry *entry;
+
+ g_return_if_fail(obj != NULL);
+ g_return_if_fail(SEXY_IS_ICON_ENTRY(obj));
+
+ entry = SEXY_ICON_ENTRY(obj);
+
+ g_free(entry->priv);
+
+ if (G_OBJECT_CLASS(parent_class)->finalize)
+ G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+static void
+sexy_icon_entry_destroy(GtkObject *obj)
+{
+ SexyIconEntry *entry;
+
+ entry = SEXY_ICON_ENTRY(obj);
+
+ sexy_icon_entry_set_icon(entry, SEXY_ICON_ENTRY_PRIMARY, NULL);
+ sexy_icon_entry_set_icon(entry, SEXY_ICON_ENTRY_SECONDARY, NULL);
+
+ if (GTK_OBJECT_CLASS(parent_class)->destroy)
+ GTK_OBJECT_CLASS(parent_class)->destroy(obj);
+}
+
+static void
+sexy_icon_entry_map(GtkWidget *widget)
+{
+ if (nsgtk_widget_get_realized(widget) && !nsgtk_widget_get_mapped(widget))
+ {
+ SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+ int i;
+
+ GTK_WIDGET_CLASS(parent_class)->map(widget);
+
+ for (i = 0; i < MAX_ICONS; i++)
+ {
+ if (entry->priv->icons[i].icon != NULL)
+ gdk_window_show(entry->priv->icons[i].window);
+ }
+ }
+}
+
+static void
+sexy_icon_entry_unmap(GtkWidget *widget)
+{
+ if (nsgtk_widget_get_mapped(widget))
+ {
+ SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+ int i;
+
+ for (i = 0; i < MAX_ICONS; i++)
+ {
+ if (entry->priv->icons[i].icon != NULL)
+ gdk_window_hide(entry->priv->icons[i].window);
+ }
+
+ GTK_WIDGET_CLASS(parent_class)->unmap(widget);
+ }
+}
+
+static gint
+get_icon_width(SexyIconEntry *entry, SexyIconEntryPosition icon_pos)
+{
+ GtkRequisition requisition;
+ gint menu_icon_width;
+ gint width;
+ SexyIconInfo *icon_info = &entry->priv->icons[icon_pos];
+
+ if (icon_info->icon == NULL)
+ return 0;
+
+ gtk_widget_size_request(GTK_WIDGET(icon_info->icon), &requisition);
+ gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &menu_icon_width, NULL);
+
+ width = MAX(requisition.width, menu_icon_width);
+
+ return width;
+}
+
+static void
+get_borders(SexyIconEntry *entry, gint *xborder, gint *yborder)
+{
+ GtkWidget *widget = GTK_WIDGET(entry);
+ gint focus_width;
+ gboolean interior_focus;
+
+ gtk_widget_style_get(widget,
+ "interior-focus", &interior_focus,
+ "focus-line-width", &focus_width,
+ NULL);
+
+ if (gtk_entry_get_has_frame(GTK_ENTRY(entry)))
+ {
+ *xborder = widget->style->xthickness;
+ *yborder = widget->style->ythickness;
+ }
+ else
+ {
+ *xborder = 0;
+ *yborder = 0;
+ }
+
+ if (!interior_focus)
+ {
+ *xborder += focus_width;
+ *yborder += focus_width;
+ }
+}
+
+static void
+get_text_area_size(SexyIconEntry *entry, GtkAllocation *alloc)
+{
+ GtkWidget *widget = GTK_WIDGET(entry);
+ GtkRequisition requisition;
+ gint xborder, yborder;
+
+ gtk_widget_get_child_requisition(widget, &requisition);
+ get_borders(entry, &xborder, &yborder);
+
+ alloc->x = xborder;
+ alloc->y = yborder;
+ alloc->width = widget->allocation.width - xborder * 2;
+ alloc->height = requisition.height - yborder * 2;
+}
+
+static void
+get_icon_allocation(SexyIconEntry *icon_entry,
+ gboolean left,
+ GtkAllocation *widget_alloc,
+ GtkAllocation *text_area_alloc,
+ GtkAllocation *allocation,
+ SexyIconEntryPosition *icon_pos)
+{
+ gboolean rtl;
+
+ rtl = (gtk_widget_get_direction(GTK_WIDGET(icon_entry)) ==
+ GTK_TEXT_DIR_RTL);
+
+ if (left)
+ *icon_pos = (rtl ? SEXY_ICON_ENTRY_SECONDARY : SEXY_ICON_ENTRY_PRIMARY);
+ else
+ *icon_pos = (rtl ? SEXY_ICON_ENTRY_PRIMARY : SEXY_ICON_ENTRY_SECONDARY);
+
+ allocation->y = text_area_alloc->y;
+ allocation->width = get_icon_width(icon_entry, *icon_pos);
+ allocation->height = text_area_alloc->height;
+
+ if (left)
+ allocation->x = text_area_alloc->x + ICON_MARGIN;
+ else
+ {
+ allocation->x = text_area_alloc->x + text_area_alloc->width -
+ allocation->width - ICON_MARGIN;
+ }
+}
+
+static void
+sexy_icon_entry_realize(GtkWidget *widget)
+{
+ SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+ int i;
+
+ GTK_WIDGET_CLASS(parent_class)->realize(widget);
+
+ attributes.x = 0;
+ attributes.y = 0;
+ attributes.width = 1;
+ attributes.height = 1;
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.visual = gtk_widget_get_visual(widget);
+ attributes.colormap = gtk_widget_get_colormap(widget);
+ attributes.event_mask = gtk_widget_get_events(widget);
+ attributes.event_mask |=
+ (GDK_EXPOSURE_MASK
+ | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+ for (i = 0; i < MAX_ICONS; i++)
+ {
+ SexyIconInfo *icon_info;
+
+ icon_info = &entry->priv->icons[i];
+ icon_info->window = gdk_window_new(widget->window, &attributes,
+ attributes_mask);
+ gdk_window_set_user_data(icon_info->window, widget);
+
+ gdk_window_set_background(icon_info->window,
+ &widget->style->base[nsgtk_widget_get_state(widget)]);
+ }
+
+ gtk_widget_queue_resize(widget);
+}
+
+static void
+sexy_icon_entry_unrealize(GtkWidget *widget)
+{
+ SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+ int i;
+
+ GTK_WIDGET_CLASS(parent_class)->unrealize(widget);
+
+ for (i = 0; i < MAX_ICONS; i++)
+ {
+ SexyIconInfo *icon_info = &entry->priv->icons[i];
+
+ gdk_window_destroy(icon_info->window);
+ icon_info->window = NULL;
+ }
+}
+
+static void
+sexy_icon_entry_size_request(GtkWidget *widget, GtkRequisition *requisition)
+{
+ GtkEntry *gtkentry;
+ SexyIconEntry *entry;
+ gint icon_widths = 0;
+ int i;
+
+ gtkentry = GTK_ENTRY(widget);
+ entry = SEXY_ICON_ENTRY(widget);
+
+ for (i = 0; i < MAX_ICONS; i++)
+ {
+ int icon_width = get_icon_width(entry, i);
+
+ if (icon_width > 0)
+ icon_widths += icon_width + ICON_MARGIN;
+ }
+
+ GTK_WIDGET_CLASS(parent_class)->size_request(widget, requisition);
+
+ if (icon_widths > requisition->width)
+ requisition->width += icon_widths;
+}
+
+static void
+place_windows(SexyIconEntry *icon_entry, GtkAllocation *widget_alloc)
+{
+ SexyIconEntryPosition left_icon_pos;
+ SexyIconEntryPosition right_icon_pos;
+ GtkAllocation left_icon_alloc;
+ GtkAllocation right_icon_alloc;
+ GtkAllocation text_area_alloc;
+
+ get_text_area_size(icon_entry, &text_area_alloc);
+ get_icon_allocation(icon_entry, TRUE, widget_alloc, &text_area_alloc,
+ &left_icon_alloc, &left_icon_pos);
+ get_icon_allocation(icon_entry, FALSE, widget_alloc, &text_area_alloc,
+ &right_icon_alloc, &right_icon_pos);
+
+ if (left_icon_alloc.width > 0)
+ {
+ text_area_alloc.x = left_icon_alloc.x + left_icon_alloc.width +
+ ICON_MARGIN;
+ }
+
+ if (right_icon_alloc.width > 0)
+ text_area_alloc.width -= right_icon_alloc.width + ICON_MARGIN;
+
+ text_area_alloc.width -= text_area_alloc.x;
+
+ gdk_window_move_resize(icon_entry->priv->icons[left_icon_pos].window,
+ left_icon_alloc.x, left_icon_alloc.y,
+ left_icon_alloc.width, left_icon_alloc.height);
+
+ gdk_window_move_resize(icon_entry->priv->icons[right_icon_pos].window,
+ right_icon_alloc.x, right_icon_alloc.y,
+ right_icon_alloc.width, right_icon_alloc.height);
+
+ gdk_window_move_resize(GTK_ENTRY(icon_entry)->text_area,
+ text_area_alloc.x, text_area_alloc.y,
+ text_area_alloc.width, text_area_alloc.height);
+}
+
+static void
+sexy_icon_entry_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
+{
+ g_return_if_fail(SEXY_IS_ICON_ENTRY(widget));
+ g_return_if_fail(allocation != NULL);
+
+ widget->allocation = *allocation;
+
+ GTK_WIDGET_CLASS(parent_class)->size_allocate(widget, allocation);
+
+ if (nsgtk_widget_get_realized(widget))
+ place_windows(SEXY_ICON_ENTRY(widget), allocation);
+}
+
+static GdkPixbuf *
+get_pixbuf_from_icon(SexyIconEntry *entry, SexyIconEntryPosition icon_pos)
+{
+ GdkPixbuf *pixbuf = NULL;
+ gchar *stock_id;
+ SexyIconInfo *icon_info = &entry->priv->icons[icon_pos];
+ GtkIconSize size;
+
+ switch (gtk_image_get_storage_type(GTK_IMAGE(icon_info->icon)))
+ {
+ case GTK_IMAGE_PIXBUF:
+ pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(icon_info->icon));
+ g_object_ref(pixbuf);
+ break;
+
+ case GTK_IMAGE_STOCK:
+ gtk_image_get_stock(GTK_IMAGE(icon_info->icon), &stock_id, &size);
+ pixbuf = gtk_widget_render_icon(GTK_WIDGET(entry),
+ stock_id, size, NULL);
+ break;
+
+ default:
+ return NULL;
+ }
+
+ return pixbuf;
+}
+
+/* Kudos to the gnome-panel guys. */
+static void
+colorshift_pixbuf(GdkPixbuf *dest, GdkPixbuf *src, int shift)
+{
+ gint i, j;
+ gint width, height, has_alpha, src_rowstride, dest_rowstride;
+ guchar *target_pixels;
+ guchar *original_pixels;
+ guchar *pix_src;
+ guchar *pix_dest;
+ int val;
+ guchar r, g, b;
+
+ has_alpha = gdk_pixbuf_get_has_alpha(src);
+ width = gdk_pixbuf_get_width(src);
+ height = gdk_pixbuf_get_height(src);
+ src_rowstride = gdk_pixbuf_get_rowstride(src);
+ dest_rowstride = gdk_pixbuf_get_rowstride(dest);
+ original_pixels = gdk_pixbuf_get_pixels(src);
+ target_pixels = gdk_pixbuf_get_pixels(dest);
+
+ for (i = 0; i < height; i++)
+ {
+ pix_dest = target_pixels + i * dest_rowstride;
+ pix_src = original_pixels + i * src_rowstride;
+
+ for (j = 0; j < width; j++)
+ {
+ r = *(pix_src++);
+ g = *(pix_src++);
+ b = *(pix_src++);
+
+ val = r + shift;
+ *(pix_dest++) = CLAMP(val, 0, 255);
+
+ val = g + shift;
+ *(pix_dest++) = CLAMP(val, 0, 255);
+
+ val = b + shift;
+ *(pix_dest++) = CLAMP(val, 0, 255);
+
+ if (has_alpha)
+ *(pix_dest++) = *(pix_src++);
+ }
+ }
+}
+
+static void
+draw_icon(GtkWidget *widget, SexyIconEntryPosition icon_pos)
+{
+ SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+ SexyIconInfo *icon_info = &entry->priv->icons[icon_pos];
+ GdkPixbuf *pixbuf;
+ gint x, y, width, height;
+
+ if (icon_info->icon == NULL || !nsgtk_widget_get_realized(widget))
+ return;
+
+ if ((pixbuf = get_pixbuf_from_icon(entry, icon_pos)) == NULL)
+ return;
+
+ gdk_drawable_get_size(icon_info->window, &width, &height);
+
+ if (width == 1 || height == 1)
+ {
+ /*
+ * size_allocate hasn't been called yet. These are the default values.
+ */
+ return;
+ }
+
+ if (gdk_pixbuf_get_height(pixbuf) > height)
+ {
+ GdkPixbuf *temp_pixbuf;
+ int scale;
+
+ scale = height - (2 * ICON_MARGIN);
+
+ temp_pixbuf = gdk_pixbuf_scale_simple(pixbuf, scale, scale,
+ GDK_INTERP_BILINEAR);
+
+ g_object_unref(pixbuf);
+
+ pixbuf = temp_pixbuf;
+ }
+
+ x = (width - gdk_pixbuf_get_width(pixbuf)) / 2;
+ y = (height - gdk_pixbuf_get_height(pixbuf)) / 2;
+
+ if (icon_info->hovered)
+ {
+ GdkPixbuf *temp_pixbuf;
+
+ temp_pixbuf = gdk_pixbuf_copy(pixbuf);
+
+ colorshift_pixbuf(temp_pixbuf, pixbuf, 30);
+
+ g_object_unref(pixbuf);
+
+ pixbuf = temp_pixbuf;
+ }
+
+ gdk_draw_pixbuf(icon_info->window, widget->style->black_gc, pixbuf,
+ 0, 0, x, y, -1, -1,
+ GDK_RGB_DITHER_NORMAL, 0, 0);
+
+ g_object_unref(pixbuf);
+}
+
+static gint
+sexy_icon_entry_expose(GtkWidget *widget, GdkEventExpose *event)
+{
+ SexyIconEntry *entry;
+
+ g_return_val_if_fail(SEXY_IS_ICON_ENTRY(widget), FALSE);
+ g_return_val_if_fail(event != NULL, FALSE);
+
+ entry = SEXY_ICON_ENTRY(widget);
+
+ if (nsgtk_widget_is_drawable(widget))
+ {
+ gboolean found = FALSE;
+ int i;
+
+ for (i = 0; i < MAX_ICONS && !found; i++)
+ {
+ SexyIconInfo *icon_info = &entry->priv->icons[i];
+
+ if (event->window == icon_info->window)
+ {
+ gint width;
+ GtkAllocation text_area_alloc;
+
+ get_text_area_size(entry, &text_area_alloc);
+ gdk_drawable_get_size(icon_info->window, &width, NULL);
+
+ gtk_paint_flat_box(widget->style, icon_info->window,
+ nsgtk_widget_get_state(widget), GTK_SHADOW_NONE,
+ NULL, widget, "entry_bg",
+ 0, 0, width, text_area_alloc.height);
+
+ draw_icon(widget, i);
+
+ found = TRUE;
+ }
+ }
+
+ if (!found)
+ GTK_WIDGET_CLASS(parent_class)->expose_event(widget, event);
+ }
+
+ return FALSE;
+}
+
+static void
+update_icon(GObject *obj, GParamSpec *param, SexyIconEntry *entry)
+{
+ if (param != NULL)
+ {
+ const char *name = g_param_spec_get_name(param);
+
+ if (strcmp(name, "pixbuf") && strcmp(name, "stock") &&
+ strcmp(name, "image") && strcmp(name, "pixmap") &&
+ strcmp(name, "icon_set") && strcmp(name, "pixbuf_animation"))
+ {
+ return;
+ }
+ }
+
+ gtk_widget_queue_resize(GTK_WIDGET(entry));
+}
+
+static gint
+sexy_icon_entry_enter_notify(GtkWidget *widget, GdkEventCrossing *event)
+{
+ SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+ int i;
+
+ for (i = 0; i < MAX_ICONS; i++)
+ {
+ if (event->window == entry->priv->icons[i].window)
+ {
+ if (sexy_icon_entry_get_icon_highlight(entry, i))
+ {
+ entry->priv->icons[i].hovered = TRUE;
+
+ update_icon(NULL, NULL, entry);
+
+ break;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static gint
+sexy_icon_entry_leave_notify(GtkWidget *widget, GdkEventCrossing *event)
+{
+ SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+ int i;
+
+ for (i = 0; i < MAX_ICONS; i++)
+ {
+ if (event->window == entry->priv->icons[i].window)
+ {
+ if (sexy_icon_entry_get_icon_highlight(entry, i))
+ {
+ entry->priv->icons[i].hovered = FALSE;
+
+ update_icon(NULL, NULL, entry);
+
+ break;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static gint
+sexy_icon_entry_button_press(GtkWidget *widget, GdkEventButton *event)
+{
+ SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+ int i;
+
+ for (i = 0; i < MAX_ICONS; i++)
+ {
+ if (event->window == entry->priv->icons[i].window)
+ {
+ if (event->button == 1 &&
+ sexy_icon_entry_get_icon_highlight(entry, i))
+ {
+ entry->priv->icons[i].hovered = FALSE;
+
+ update_icon(NULL, NULL, entry);
+ }
+
+ g_signal_emit(entry, signals[ICON_PRESSED], 0, i, event->button);
+
+ return TRUE;
+ }
+ }
+
+ if (GTK_WIDGET_CLASS(parent_class)->button_press_event)
+ return GTK_WIDGET_CLASS(parent_class)->button_press_event(widget,
+ event);
+
+ return FALSE;
+}
+
+static gint
+sexy_icon_entry_button_release(GtkWidget *widget, GdkEventButton *event)
+{
+ SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+ int i;
+
+ for (i = 0; i < MAX_ICONS; i++)
+ {
+ GdkWindow *icon_window = entry->priv->icons[i].window;
+
+ if (event->window == icon_window)
+ {
+ int width, height;
+ gdk_drawable_get_size(icon_window, &width, &height);
+
+ if (event->button == 1 &&
+ sexy_icon_entry_get_icon_highlight(entry, i) &&
+ event->x >= 0 && event->y >= 0 &&
+ event->x <= width && event->y <= height)
+ {
+ entry->priv->icons[i].hovered = TRUE;
+
+ update_icon(NULL, NULL, entry);
+ }
+
+ g_signal_emit(entry, signals[ICON_RELEASED], 0, i, event->button);
+
+ return TRUE;
+ }
+ }
+
+ if (GTK_WIDGET_CLASS(parent_class)->button_release_event)
+ return GTK_WIDGET_CLASS(parent_class)->button_release_event(widget,
+ event);
+
+ return FALSE;
+}
+
+/*
+ * sexy_icon_entry_new
+ *
+ * Creates a new SexyIconEntry widget.
+ *
+ * Returns a new #SexyIconEntry.
+ */
+GtkWidget *
+sexy_icon_entry_new(void)
+{
+ return GTK_WIDGET(g_object_new(SEXY_TYPE_ICON_ENTRY, NULL));
+}
+
+/*
+ * sexy_icon_entry_set_icon
+ * @param entry A #SexyIconEntry.
+ * @param position Icon position.
+ * @param icon A #GtkImage to set as the icon.
+ *
+ * Sets the icon shown in the entry
+ */
+void
+sexy_icon_entry_set_icon(SexyIconEntry *entry, SexyIconEntryPosition icon_pos,
+ GtkImage *icon)
+{
+ SexyIconInfo *icon_info;
+
+ g_return_if_fail(entry != NULL);
+ g_return_if_fail(SEXY_IS_ICON_ENTRY(entry));
+ g_return_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos));
+ g_return_if_fail(icon == NULL || GTK_IS_IMAGE(icon));
+
+ icon_info = &entry->priv->icons[icon_pos];
+
+ if (icon == icon_info->icon)
+ return;
+
+ if (icon_pos == SEXY_ICON_ENTRY_SECONDARY &&
+ entry->priv->icon_released_id != 0)
+ {
+ g_signal_handler_disconnect(entry, entry->priv->icon_released_id);
+ entry->priv->icon_released_id = 0;
+ }
+
+ if (icon == NULL)
+ {
+ if (icon_info->icon != NULL)
+ {
+ gtk_widget_destroy(GTK_WIDGET(icon_info->icon));
+ icon_info->icon = NULL;
+
+ /*
+ * Explicitly check, as the pointer may become invalidated
+ * during destruction.
+ */
+ if (icon_info->window != NULL && GDK_IS_WINDOW(icon_info->window))
+ gdk_window_hide(icon_info->window);
+ }
+ }
+ else
+ {
+ if (icon_info->window != NULL && icon_info->icon == NULL)
+ gdk_window_show(icon_info->window);
+
+ g_signal_connect(G_OBJECT(icon), "notify",
+ G_CALLBACK(update_icon), entry);
+
+ icon_info->icon = icon;
+ g_object_ref(icon);
+ }
+
+ update_icon(NULL, NULL, entry);
+}
+
+/*
+ * sexy_icon_entry_set_icon_highlight
+ * @param entry A #SexyIconEntry;
+ * @param position Icon position.
+ * @param highlight TRUE if the icon should highlight on mouse-over
+ *
+ * Determines whether the icon will highlight on mouse-over.
+ */
+void
+sexy_icon_entry_set_icon_highlight(SexyIconEntry *entry,
+ SexyIconEntryPosition icon_pos,
+ gboolean highlight)
+{
+ SexyIconInfo *icon_info;
+
+ g_return_if_fail(entry != NULL);
+ g_return_if_fail(SEXY_IS_ICON_ENTRY(entry));
+ g_return_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos));
+
+ icon_info = &entry->priv->icons[icon_pos];
+
+ if (icon_info->highlight == highlight)
+ return;
+
+ icon_info->highlight = highlight;
+}
+
+/*
+ * sexy_icon_entry_get_icon
+ * @param entry A #SexyIconEntry.
+ * @param position Icon position.
+ *
+ * Retrieves the image used for the icon
+ *
+ * Returns: A #GtkImage.
+ */
+GtkImage *
+sexy_icon_entry_get_icon(const SexyIconEntry *entry,
+ SexyIconEntryPosition icon_pos)
+{
+ g_return_val_if_fail(entry != NULL, NULL);
+ g_return_val_if_fail(SEXY_IS_ICON_ENTRY(entry), NULL);
+ g_return_val_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos), NULL);
+
+ return entry->priv->icons[icon_pos].icon;
+}
+
+/*
+ * sexy_icon_entry_get_icon_highlight
+ * @param entry A #SexyIconEntry.
+ * @param position Icon position.
+ *
+ * Retrieves whether entry will highlight the icon on mouseover.
+ *
+ * Returns: TRUE if icon highlights.
+ */
+gboolean
+sexy_icon_entry_get_icon_highlight(const SexyIconEntry *entry,
+ SexyIconEntryPosition icon_pos)
+{
+ g_return_val_if_fail(entry != NULL, FALSE);
+ g_return_val_if_fail(SEXY_IS_ICON_ENTRY(entry), FALSE);
+ g_return_val_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos), FALSE);
+
+ return entry->priv->icons[icon_pos].highlight;
+}
+
+static void
+clear_button_clicked_cb(SexyIconEntry *icon_entry,
+ SexyIconEntryPosition icon_pos,
+ int button)
+{
+ if (icon_pos != SEXY_ICON_ENTRY_SECONDARY || button != 1)
+ return;
+
+ gtk_entry_set_text(GTK_ENTRY(icon_entry), "");
+}
+
+/*
+ * sexy_icon_entry_add_clear_button
+ * @param icon_entry A #SexyIconEntry.
+ *
+ * A convenience function to add a clear button to the end of the entry.
+ * This is useful for search boxes.
+ */
+void
+sexy_icon_entry_add_clear_button(SexyIconEntry *icon_entry)
+{
+ GtkWidget *icon;
+
+ g_return_if_fail(icon_entry != NULL);
+ g_return_if_fail(SEXY_IS_ICON_ENTRY(icon_entry));
+
+ icon = nsgtk_image_new_from_stock(NSGTK_STOCK_CLEAR,
+ GTK_ICON_SIZE_MENU);
+ gtk_widget_show(icon);
+ sexy_icon_entry_set_icon(SEXY_ICON_ENTRY(icon_entry),
+ SEXY_ICON_ENTRY_SECONDARY,
+ GTK_IMAGE(icon));
+ sexy_icon_entry_set_icon_highlight(SEXY_ICON_ENTRY(icon_entry),
+ SEXY_ICON_ENTRY_SECONDARY, TRUE);
+
+ if (icon_entry->priv->icon_released_id != 0)
+ {
+ g_signal_handler_disconnect(icon_entry,
+ icon_entry->priv->icon_released_id);
+ }
+
+ icon_entry->priv->icon_released_id =
+ g_signal_connect(G_OBJECT(icon_entry), "icon_released",
+ G_CALLBACK(clear_button_clicked_cb), NULL);
+}
diff --git a/frontends/gtk/sexy_icon_entry.h b/frontends/gtk/sexy_icon_entry.h
new file mode 100644
index 000000000..bd7fb3eb9
--- /dev/null
+++ b/frontends/gtk/sexy_icon_entry.h
@@ -0,0 +1,100 @@
+/*
+ * file libsexy/sexy-icon-entry.h Entry widget
+ *
+ * Copyright (C) 2004-2006 Christian Hammond.
+ * redistributed under GPLv2
+ *
+ * libsexy 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; version 2 of the License.
+ *
+ * NetSurf 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/>
+ * or write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef _SEXY_ICON_ENTRY_H_
+#define _SEXY_ICON_ENTRY_H_
+
+typedef struct _SexyIconEntry SexyIconEntry;
+typedef struct _SexyIconEntryClass SexyIconEntryClass;
+typedef struct _SexyIconEntryPriv SexyIconEntryPriv;
+
+#include <gtk/gtk.h>
+
+#define SEXY_TYPE_ICON_ENTRY (sexy_icon_entry_get_type())
+#define SEXY_ICON_ENTRY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), SEXY_TYPE_ICON_ENTRY, SexyIconEntry))
+#define SEXY_ICON_ENTRY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), SEXY_TYPE_ICON_ENTRY, SexyIconEntryClass))
+#define SEXY_IS_ICON_ENTRY(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), SEXY_TYPE_ICON_ENTRY))
+#define SEXY_IS_ICON_ENTRY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), SEXY_TYPE_ICON_ENTRY))
+#define SEXY_ICON_ENTRY_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), SEXY_TYPE_ICON_ENTRY, SexyIconEntryClass))
+
+typedef enum
+{
+ SEXY_ICON_ENTRY_PRIMARY,
+ SEXY_ICON_ENTRY_SECONDARY
+
+} SexyIconEntryPosition;
+
+struct _SexyIconEntry
+{
+ GtkEntry parent_object;
+
+ SexyIconEntryPriv *priv;
+
+ void (*gtk_reserved1)(void);
+ void (*gtk_reserved2)(void);
+ void (*gtk_reserved3)(void);
+ void (*gtk_reserved4)(void);
+};
+
+struct _SexyIconEntryClass
+{
+ GtkEntryClass parent_class;
+
+ /* Signals */
+ void (*icon_pressed)(SexyIconEntry *entry, SexyIconEntryPosition icon_pos,
+ int button);
+ void (*icon_released)(SexyIconEntry *entry, SexyIconEntryPosition icon_pos,
+ int button);
+
+ void (*gtk_reserved1)(void);
+ void (*gtk_reserved2)(void);
+ void (*gtk_reserved3)(void);
+ void (*gtk_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+GType sexy_icon_entry_get_type(void);
+
+GtkWidget *sexy_icon_entry_new(void);
+
+void sexy_icon_entry_set_icon(SexyIconEntry *entry,
+ SexyIconEntryPosition position,
+ GtkImage *icon);
+
+void sexy_icon_entry_set_icon_highlight(SexyIconEntry *entry,
+ SexyIconEntryPosition position,
+ gboolean highlight);
+
+GtkImage *sexy_icon_entry_get_icon(const SexyIconEntry *entry,
+ SexyIconEntryPosition position);
+
+gboolean sexy_icon_entry_get_icon_highlight(const SexyIconEntry *entry,
+ SexyIconEntryPosition position);
+void sexy_icon_entry_add_clear_button(SexyIconEntry *icon_entry);
+
+G_END_DECLS
+
+#endif /* _SEXY_ICON_ENTRY_H_ */
diff --git a/frontends/gtk/ssl_cert.c b/frontends/gtk/ssl_cert.c
new file mode 100644
index 000000000..742029f83
--- /dev/null
+++ b/frontends/gtk/ssl_cert.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2015 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "utils/nsurl.h"
+#include "desktop/tree.h"
+#include "desktop/sslcert_viewer.h"
+
+#include "gtk/treeview.h"
+#include "gtk/scaffolding.h"
+#include "gtk/resources.h"
+#include "gtk/ssl_cert.h"
+
+
+static void nsgtk_ssl_accept(GtkButton *w, gpointer data)
+{
+ void **session = data;
+ GtkBuilder *x = session[0];
+ struct nsgtk_treeview *wnd = session[1];
+ struct sslcert_session_data *ssl_data = session[2];
+
+ sslcert_viewer_accept(ssl_data);
+
+ nsgtk_treeview_destroy(wnd);
+ g_object_unref(G_OBJECT(x));
+ free(session);
+}
+
+static void nsgtk_ssl_reject(GtkWidget *w, gpointer data)
+{
+ void **session = data;
+ GtkBuilder *x = session[0];
+ struct nsgtk_treeview *wnd = session[1];
+ struct sslcert_session_data *ssl_data = session[2];
+
+ sslcert_viewer_reject(ssl_data);
+
+ nsgtk_treeview_destroy(wnd);
+ g_object_unref(G_OBJECT(x));
+ free(session);
+}
+
+static gboolean nsgtk_ssl_delete_event(GtkWidget *w, GdkEvent *event, gpointer data)
+{
+ nsgtk_ssl_reject(w, data);
+ return FALSE;
+}
+
+void gtk_cert_verify(nsurl *url, const struct ssl_cert_info *certs,
+ unsigned long num, nserror (*cb)(bool proceed, void *pw),
+ void *cbpw)
+{
+ static struct nsgtk_treeview *ssl_window;
+ struct sslcert_session_data *data;
+ GtkButton *accept, *reject;
+ void **session;
+ GtkDialog *dlg;
+ GtkScrolledWindow *scrolled;
+ GtkDrawingArea *drawing_area;
+ GtkBuilder *builder;
+ GtkWindow *gtk_parent;
+ nserror res;
+
+ /* state while dlg is open */
+ session = calloc(sizeof(void *), 3);
+ if (session == NULL) {
+ return;
+ }
+
+ res = nsgtk_builder_new_from_resname("ssl", &builder);
+ if (res != NSERROR_OK) {
+ LOG("SSL UI builder init failed");
+ free(session);
+ cb(false, cbpw);
+ return;
+ }
+
+ gtk_builder_connect_signals(builder, NULL);
+
+ sslcert_viewer_create_session_data(num, url, cb, cbpw, certs, &data);
+ ssl_current_session = data;
+
+ dlg = GTK_DIALOG(gtk_builder_get_object(builder, "wndSSLProblem"));
+
+ /* set parent for transient dialog */
+ gtk_parent = nsgtk_scaffolding_window(nsgtk_current_scaffolding());
+ gtk_window_set_transient_for(GTK_WINDOW(dlg), gtk_parent);
+
+ scrolled = GTK_SCROLLED_WINDOW(gtk_builder_get_object(builder, "SSLScrolled"));
+ drawing_area = GTK_DRAWING_AREA(gtk_builder_get_object(builder, "SSLDrawingArea"));
+
+ ssl_window = nsgtk_treeview_create(TREE_SSLCERT, GTK_WINDOW(dlg), scrolled,
+ drawing_area);
+
+ if (ssl_window == NULL) {
+ free(session);
+ g_object_unref(G_OBJECT(dlg));
+ return;
+ }
+
+ accept = GTK_BUTTON(gtk_builder_get_object(builder, "sslaccept"));
+ reject = GTK_BUTTON(gtk_builder_get_object(builder, "sslreject"));
+
+ session[0] = builder;
+ session[1] = ssl_window;
+ session[2] = data;
+
+#define CONNECT(obj, sig, callback, ptr) \
+ g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr))
+
+ CONNECT(accept, "clicked", nsgtk_ssl_accept, session);
+ CONNECT(reject, "clicked", nsgtk_ssl_reject, session);
+ CONNECT(dlg, "delete_event", G_CALLBACK(nsgtk_ssl_delete_event),
+ (gpointer)session);
+
+ gtk_widget_show(GTK_WIDGET(dlg));
+}
diff --git a/frontends/gtk/ssl_cert.h b/frontends/gtk/ssl_cert.h
new file mode 100644
index 000000000..48937d457
--- /dev/null
+++ b/frontends/gtk/ssl_cert.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_GTK_SSL_CERT_H
+#define NETSURF_GTK_SSL_CERT_H 1
+
+struct nsurl;
+struct ssl_cert_info;
+
+/**
+ * Prompt the user to verify a certificate with issuse.
+ *
+ * \param url The URL being verified.
+ * \param certs The certificate to be verified
+ * \param num The number of certificates to be verified.
+ * \param cb Callback upon user decision.
+ * \param cbpw Context pointer passed to cb
+ */
+void gtk_cert_verify(struct nsurl *url, const struct ssl_cert_info *certs, unsigned long num, nserror (*cb)(bool proceed, void *pw), void *cbpw);
+
+#endif
diff --git a/frontends/gtk/tabs.c b/frontends/gtk/tabs.c
new file mode 100644
index 000000000..67a410d20
--- /dev/null
+++ b/frontends/gtk/tabs.c
@@ -0,0 +1,416 @@
+/*
+ * Copyright 2008 Michael Lester <element3260@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdint.h>
+#include <string.h>
+
+#include "utils/nsoption.h"
+#include "utils/messages.h"
+#include "desktop/browser.h"
+#include "content/content.h"
+#include "desktop/search.h"
+
+#include "gtk/compat.h"
+#include "gtk/window.h"
+#include "gtk/search.h"
+#include "gtk/tabs.h"
+
+#define TAB_WIDTH_N_CHARS 15
+
+/** callback to update sizes when style-set gtk signal */
+static void nsgtk_tab_update_size(GtkWidget *hbox, GtkStyle *previous_style,
+ GtkWidget *close_button)
+{
+ PangoFontMetrics *metrics;
+ PangoContext *context;
+ int char_width, h, w;
+ GtkStyleContext *style;
+ GtkStateFlags state;
+
+ state = nsgtk_widget_get_state_flags(hbox);
+ style = nsgtk_widget_get_style_context(hbox);
+
+ context = gtk_widget_get_pango_context(hbox);
+ metrics = pango_context_get_metrics(context,
+ nsgtk_style_context_get_font(style, state),
+ pango_context_get_language(context));
+
+ char_width = pango_font_metrics_get_approximate_digit_width(metrics);
+ pango_font_metrics_unref(metrics);
+
+ nsgtk_icon_size_lookup_for_settings(gtk_widget_get_settings (hbox),
+ GTK_ICON_SIZE_MENU, &w, &h);
+
+ gtk_widget_set_size_request(hbox,
+ TAB_WIDTH_N_CHARS * PANGO_PIXELS(char_width) + 2 * w,
+ -1);
+
+ gtk_widget_set_size_request(close_button, w + 4, h + 4);
+}
+
+/** Create a notebook tab label */
+static GtkWidget *nsgtk_tab_label_setup(struct gui_window *window)
+{
+ GtkWidget *hbox, *label, *button, *close;
+
+ hbox = nsgtk_hbox_new(FALSE, 2);
+
+ label = gtk_label_new(messages_get("NewTab"));
+ gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END);
+ gtk_label_set_single_line_mode(GTK_LABEL(label), TRUE);
+ nsgtk_widget_set_alignment(label, GTK_ALIGN_START, GTK_ALIGN_CENTER);
+ nsgtk_widget_set_margins(label, 0, 0);
+ gtk_widget_show(label);
+
+ button = gtk_button_new();
+
+ close = nsgtk_image_new_from_stock(NSGTK_STOCK_CLOSE,
+ GTK_ICON_SIZE_MENU);
+ gtk_container_add(GTK_CONTAINER(button), close);
+ gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE);
+ gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
+ gtk_widget_set_tooltip_text(button, "Close this tab.");
+
+#ifdef FIXME
+ GtkRcStyle *rcstyle;
+ rcstyle = gtk_rc_style_new();
+ rcstyle->xthickness = rcstyle->ythickness = 0;
+ gtk_widget_modify_style(button, rcstyle);
+ g_object_unref(rcstyle);
+#endif
+
+ g_signal_connect_swapped(button, "clicked",
+ G_CALLBACK(nsgtk_window_destroy_browser), window);
+ g_signal_connect(hbox, "style-set",
+ G_CALLBACK(nsgtk_tab_update_size), button);
+
+ gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(hbox), "label", label);
+ g_object_set_data(G_OBJECT(hbox), "close-button", button);
+
+
+ gtk_widget_show_all(hbox);
+ return hbox;
+}
+#include "utils/log.h"
+
+/** callback when page is switched */
+
+static gint srcpagenum;
+
+/** The switch-page signal handler
+ *
+ * This signal is handled both before and after delivery to work round
+ * issue that setting the selected tab during the switch-page signal
+ * fails
+ */
+static void
+nsgtk_tab_switch_page(GtkNotebook *notebook,
+ GtkWidget *page,
+ guint selpagenum,
+ gpointer user_data)
+{
+ srcpagenum = gtk_notebook_get_current_page(notebook);
+}
+
+static void
+nsgtk_tab_switch_page_after(GtkNotebook *notebook,
+ GtkWidget *selpage,
+ guint selpagenum,
+ gpointer user_data)
+{
+ GtkWidget *srcpage;
+ GtkWidget *addpage;
+ struct gui_window *gw;
+ nserror error;
+
+ addpage = g_object_get_data(G_OBJECT(notebook), "addtab");
+
+ if (selpage == addpage) {
+ if ((srcpagenum != -1) &&
+ (srcpagenum != (gint)selpagenum)) {
+ /* ensure the add tab is not actually selected */
+ LOG("src %d sel %d", srcpagenum, selpagenum);
+ srcpage = gtk_notebook_get_nth_page(notebook, srcpagenum);
+ gw = g_object_get_data(G_OBJECT(srcpage), "gui_window");
+ if ((gw != NULL) && (nsgtk_get_scaffold(gw) != NULL)) {
+ error = nsgtk_scaffolding_new_tab(gw);
+ if (error != NSERROR_OK) {
+ LOG("Failed to open new tab.");
+ }
+ }
+ }
+ } else {
+ LOG("sel %d", selpagenum);
+ /* tab with page in it */
+ gw = g_object_get_data(G_OBJECT(selpage), "gui_window");
+ if (gw != NULL) {
+ nsgtk_scaffolding_set_top_level(gw);
+ }
+ }
+}
+
+static void nsgtk_tab_page_reordered(GtkNotebook *notebook,
+ GtkWidget *child,
+ guint page_num,
+ gpointer user_data)
+{
+ gint pages;
+ GtkWidget *addpage;
+
+ pages = gtk_notebook_get_n_pages(notebook);
+ addpage = g_object_get_data(G_OBJECT(notebook), "addtab");
+
+ if (((gint)page_num == (pages - 1)) &&
+ (child != addpage)) {
+ /* moved tab to end */
+ gtk_notebook_reorder_child(notebook, addpage, -1);
+ }
+}
+
+static void
+nsgtk_tab_orientation(GtkNotebook *notebook)
+{
+ switch (nsoption_int(position_tab)) {
+ case 0:
+ gtk_notebook_set_tab_pos(notebook, GTK_POS_TOP);
+ break;
+
+ case 1:
+ gtk_notebook_set_tab_pos(notebook, GTK_POS_LEFT);
+ break;
+
+ case 2:
+ gtk_notebook_set_tab_pos(notebook, GTK_POS_RIGHT);
+ break;
+
+ case 3:
+ gtk_notebook_set_tab_pos(notebook, GTK_POS_BOTTOM);
+ break;
+
+ }
+}
+
+/** adds a "new tab" tab */
+static GtkWidget *
+nsgtk_tab_add_newtab(GtkNotebook *notebook)
+{
+ GtkWidget *tablabel;
+ GtkWidget *tabcontents;
+ GtkWidget *add;
+
+ tablabel = nsgtk_hbox_new(FALSE, 1);
+ tabcontents = nsgtk_hbox_new(FALSE, 1);
+
+ add = nsgtk_image_new_from_stock(NSGTK_STOCK_ADD, GTK_ICON_SIZE_MENU);
+
+ gtk_box_pack_start(GTK_BOX(tablabel), add, FALSE, FALSE, 0);
+
+ gtk_widget_show_all(tablabel);
+
+ gtk_notebook_append_page(notebook, tabcontents, tablabel);
+
+ gtk_notebook_set_tab_reorderable(notebook, tabcontents, false);
+
+ gtk_widget_show_all(tabcontents);
+
+ g_object_set_data(G_OBJECT(notebook), "addtab", tabcontents);
+
+ return tablabel;
+}
+
+/** callback to alter tab visibility when pages are added or removed */
+static void
+nsgtk_tab_visibility_update(GtkNotebook *notebook, GtkWidget *child, guint page)
+{
+ gint pagec = gtk_notebook_get_n_pages(notebook);
+ GtkWidget *addpage = g_object_get_data(G_OBJECT(notebook), "addtab");
+
+ if (addpage != NULL) {
+ pagec--; /* skip the add tab */
+ if ((gint)page == pagec) {
+ /* ensure the add new tab cannot be current */
+ gtk_notebook_set_current_page(notebook, page - 1);
+ }
+ }
+
+ if ((nsoption_bool(show_single_tab) == true) || (pagec > 1)) {
+ gtk_notebook_set_show_tabs(notebook, TRUE);
+ } else {
+ gtk_notebook_set_show_tabs(notebook, FALSE);
+ }
+}
+
+/* exported interface documented in gtk/tabs.h */
+void nsgtk_tab_options_changed(GtkNotebook *notebook)
+{
+ nsgtk_tab_orientation(notebook);
+ nsgtk_tab_visibility_update(notebook, NULL, 0);
+}
+
+
+/* exported interface documented in gtk/tabs.h */
+void nsgtk_tab_init(struct nsgtk_scaffolding *gs)
+{
+ GtkNotebook *notebook;
+
+ notebook = nsgtk_scaffolding_notebook(gs);
+
+ nsgtk_tab_add_newtab(notebook);
+
+ g_signal_connect(notebook, "switch-page",
+ G_CALLBACK(nsgtk_tab_switch_page), NULL);
+ g_signal_connect_after(notebook, "switch-page",
+ G_CALLBACK(nsgtk_tab_switch_page_after), NULL);
+
+ g_signal_connect(notebook, "page-removed",
+ G_CALLBACK(nsgtk_tab_visibility_update), NULL);
+ g_signal_connect(notebook, "page-added",
+ G_CALLBACK(nsgtk_tab_visibility_update), NULL);
+ g_signal_connect(notebook, "page-reordered",
+ G_CALLBACK(nsgtk_tab_page_reordered), NULL);
+
+
+ nsgtk_tab_options_changed(notebook);
+}
+
+/* exported interface documented in gtk/tabs.h */
+void nsgtk_tab_add(struct gui_window *gw,
+ GtkWidget *tab_contents,
+ bool background)
+{
+ GtkNotebook *notebook;
+ GtkWidget *tabBox;
+ gint remember;
+ gint pages;
+ gint newpage;
+
+ g_object_set_data(G_OBJECT(tab_contents), "gui_window", gw);
+
+ notebook = nsgtk_scaffolding_notebook(nsgtk_get_scaffold(gw));
+
+ tabBox = nsgtk_tab_label_setup(gw);
+
+ nsgtk_window_set_tab(gw, tabBox);
+
+ remember = gtk_notebook_get_current_page(notebook);
+
+ pages = gtk_notebook_get_n_pages(notebook);
+
+ newpage = gtk_notebook_insert_page(notebook, tab_contents, tabBox, pages - 1);
+
+ gtk_notebook_set_tab_reorderable(notebook, tab_contents, true);
+
+ gtk_widget_show_all(tab_contents);
+
+ if (background) {
+ gtk_notebook_set_current_page(notebook, remember);
+ } else {
+ gtk_notebook_set_current_page(notebook, newpage);
+ }
+
+ gtk_widget_grab_focus(GTK_WIDGET(nsgtk_scaffolding_urlbar(
+ nsgtk_get_scaffold(gw))));
+}
+
+/* exported interface documented in gtk/tabs.h */
+void nsgtk_tab_set_title(struct gui_window *g, const char *title)
+{
+ GtkWidget *label;
+ GtkWidget *tab;
+
+ tab = nsgtk_window_get_tab(g);
+ if (tab == NULL) {
+ return;
+ }
+
+ label = g_object_get_data(G_OBJECT(tab), "label");
+ gtk_label_set_text(GTK_LABEL(label), title);
+ gtk_widget_set_tooltip_text(tab, title);
+}
+
+/* exported interface documented in gtk/tabs.h */
+nserror nsgtk_tab_close_current(GtkNotebook *notebook)
+{
+ gint pagen;
+ GtkWidget *page;
+ struct gui_window *gw;
+ GtkWidget *addpage;
+
+ pagen = gtk_notebook_get_current_page(notebook);
+ if (pagen == -1) {
+ return NSERROR_OK;
+ }
+
+ page = gtk_notebook_get_nth_page(notebook, pagen);
+ if (page == NULL) {
+ return NSERROR_OK;
+ }
+
+ addpage = g_object_get_data(G_OBJECT(notebook), "addtab");
+ if (page == addpage) {
+ /* the add new tab page is current, cannot close that */
+ return NSERROR_OK;
+ }
+
+ gw = g_object_get_data(G_OBJECT(page), "gui_window");
+ if (gw == NULL) {
+ return NSERROR_OK;
+ }
+
+ nsgtk_window_destroy_browser(gw);
+
+ return NSERROR_OK;
+}
+
+nserror nsgtk_tab_prev(GtkNotebook *notebook)
+{
+ gtk_notebook_prev_page(notebook);
+
+ return NSERROR_OK;
+
+}
+
+nserror nsgtk_tab_next(GtkNotebook *notebook)
+{
+ gint pagen;
+ GtkWidget *page;
+ GtkWidget *addpage;
+
+ pagen = gtk_notebook_get_current_page(notebook);
+ if (pagen == -1) {
+ return NSERROR_OK;
+ }
+
+ page = gtk_notebook_get_nth_page(notebook, pagen + 1);
+ if (page == NULL) {
+ return NSERROR_OK;
+ }
+
+ addpage = g_object_get_data(G_OBJECT(notebook), "addtab");
+ if (page == addpage) {
+ /* cannot make add new tab page current */
+ return NSERROR_OK;
+ }
+
+ gtk_notebook_set_current_page(notebook, pagen + 1);
+
+ return NSERROR_OK;
+}
diff --git a/frontends/gtk/tabs.h b/frontends/gtk/tabs.h
new file mode 100644
index 000000000..440d61336
--- /dev/null
+++ b/frontends/gtk/tabs.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2008 Michael Lester <element3260@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_GTK_TABS_H_
+#define _NETSURF_GTK_TABS_H_
+
+struct gui_window;
+
+void nsgtk_tab_init(struct nsgtk_scaffolding *gs);
+void nsgtk_tab_add(struct gui_window *window, GtkWidget *tab_contents, bool background);
+
+/** set the tab title
+ *
+ * The tab title will be set to the parameter
+ *
+ * \note currently only called from nsgtk_window_set_title()
+ *
+ * \param g the gui window to set tab title for.
+ * \param title The title text which may not be NULL.
+ */
+void nsgtk_tab_set_title(struct gui_window *g, const char *title);
+void nsgtk_tab_options_changed(GtkNotebook *notebook);
+nserror nsgtk_tab_close_current(GtkNotebook *notebook);
+nserror nsgtk_tab_prev(GtkNotebook *notebook);
+nserror nsgtk_tab_next(GtkNotebook *notebook);
+
+#endif
diff --git a/frontends/gtk/throbber.c b/frontends/gtk/throbber.c
new file mode 100644
index 000000000..9392c3909
--- /dev/null
+++ b/frontends/gtk/throbber.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2008 Rob Kendrick <rjek@netsurf-browser.org>
+ * Copyright 2008 Sean Fox <dyntryx@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <gtk/gtk.h>
+#include <stdint.h>
+
+#include "utils/log.h"
+
+#include "gtk/resources.h"
+#include "gtk/throbber.h"
+
+struct nsgtk_throbber *nsgtk_throbber = NULL;
+
+#define THROBBER_FRAMES 9
+#define THROBBER_FMT "throbber/throbber%d.png"
+
+/* exported interface documented in gtk/throbber.h */
+nserror nsgtk_throbber_init(void)
+{
+ struct nsgtk_throbber *throb; /**< structure we generate */
+ int frame;
+ char resname[] = THROBBER_FMT;
+ nserror res = NSERROR_OK;
+
+ throb = malloc(sizeof(*throb));
+ if (throb == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ throb->framedata = malloc(sizeof(GdkPixbuf *) * THROBBER_FRAMES);
+ if (throb->framedata == NULL) {
+ free(throb);
+ return false;
+ }
+
+ for (frame = 0; frame < THROBBER_FRAMES; frame++) {
+ snprintf(resname, sizeof(resname), THROBBER_FMT, frame);
+ res = nsgdk_pixbuf_new_from_resname(resname,
+ throb->framedata + frame);
+ if (res != NSERROR_OK) {
+ break;
+ }
+ LOG("%s",resname);
+ }
+
+ if (frame < 1) {
+ /* we need at least two frames - one for idle, one for active */
+ LOG("Insufficent number of frames (%d) in throbber animation.", frame);
+ res = NSERROR_INIT_FAILED;
+ }
+
+ throb->nframes = frame;
+ nsgtk_throbber = throb;
+ return res;
+
+
+}
+
+
+void nsgtk_throbber_finalise(void)
+{
+ int i;
+
+ for (i = 0; i < nsgtk_throbber->nframes; i++)
+ g_object_unref(nsgtk_throbber->framedata[i]);
+
+ free(nsgtk_throbber->framedata);
+ free(nsgtk_throbber);
+
+ nsgtk_throbber = NULL;
+}
+
diff --git a/frontends/gtk/throbber.h b/frontends/gtk/throbber.h
new file mode 100644
index 000000000..e0b47e15c
--- /dev/null
+++ b/frontends/gtk/throbber.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2008 Rob Kendrick <rjek@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 __GTK_THROBBER_H__
+#define __GTK_THROBBER_H__
+
+#include <gtk/gtk.h>
+
+struct nsgtk_throbber
+{
+ int nframes; /**< Number of frames in the throbber */
+ GdkPixbuf **framedata;
+};
+
+extern struct nsgtk_throbber *nsgtk_throbber;
+
+nserror nsgtk_throbber_init(void);
+void nsgtk_throbber_finalise(void);
+
+#endif /* __GTK_THROBBER_H__ */
diff --git a/frontends/gtk/toolbar.c b/frontends/gtk/toolbar.c
new file mode 100644
index 000000000..208b5c0b9
--- /dev/null
+++ b/frontends/gtk/toolbar.c
@@ -0,0 +1,1416 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <gtk/gtk.h>
+
+#include "desktop/browser.h"
+#include "desktop/searchweb.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+
+#include "gtk/gui.h"
+#include "gtk/warn.h"
+#include "gtk/scaffolding.h"
+#include "gtk/search.h"
+#include "gtk/throbber.h"
+#include "gtk/window.h"
+#include "gtk/compat.h"
+#include "gtk/resources.h"
+#include "gtk/toolbar.h"
+
+static GtkTargetEntry entry = {(char *)"nsgtk_button_data",
+ GTK_TARGET_SAME_APP, 0};
+
+static bool edit_mode = false;
+
+struct nsgtk_toolbar_custom_store {
+ GtkWidget *window;
+ GtkWidget *store_buttons[PLACEHOLDER_BUTTON];
+ GtkWidget *widgetvbox;
+ GtkWidget *currentbar;
+ char numberh; /* current horizontal location while adding */
+ GtkBuilder *builder; /* button widgets to store */
+ int buttonlocations[PLACEHOLDER_BUTTON];
+ int currentbutton;
+ bool fromstore;
+};
+/* the number of buttons that fit in the width of the store window */
+#define NSGTK_STORE_WIDTH 6
+
+/* the 'standard' width of a button that makes sufficient of its label
+visible */
+#define NSGTK_BUTTON_WIDTH 111
+
+/* the 'standard' height of a button that fits as many toolbars as
+possible into the store */
+#define NSGTK_BUTTON_HEIGHT 70
+
+/* the 'normal' width of the websearch bar */
+#define NSGTK_WEBSEARCH_WIDTH 150
+
+static struct nsgtk_toolbar_custom_store store;
+static struct nsgtk_toolbar_custom_store *window = &store;
+
+
+enum image_sets {
+ IMAGE_SET_MAIN_MENU = 0,
+ IMAGE_SET_RCLICK_MENU,
+ IMAGE_SET_POPUP_MENU,
+ IMAGE_SET_BUTTONS,
+ IMAGE_SET_COUNT
+};
+
+typedef enum search_buttons {
+ SEARCH_BACK_BUTTON = 0,
+ SEARCH_FORWARD_BUTTON,
+ SEARCH_CLOSE_BUTTON,
+ SEARCH_BUTTONS_COUNT
+} nsgtk_search_buttons;
+
+struct nsgtk_theme {
+ GtkImage *image[PLACEHOLDER_BUTTON];
+ GtkImage *searchimage[SEARCH_BUTTONS_COUNT];
+ /* apng throbber element */
+};
+
+/**
+ * returns a string without its underscores
+ *
+ * \param s The string to change.
+ * \param replacespace true to insert a space where there was an underscore
+ * \return The altered string
+ */
+static char *remove_underscores(const char *s, bool replacespace)
+{
+ size_t i, ii, len;
+ char *ret;
+ len = strlen(s);
+ ret = malloc(len + 1);
+ if (ret == NULL) {
+ return NULL;
+ }
+ for (i = 0, ii = 0; i < len; i++) {
+ if (s[i] != '_') {
+ ret[ii++] = s[i];
+ } else if (replacespace) {
+ ret[ii++] = ' ';
+ }
+ }
+ ret[ii] = '\0';
+ return ret;
+}
+
+
+/**
+ * get default image for buttons / menu items from gtk stock items.
+ *
+ * \param tbbutton button reference
+ * \param iconsize The size of icons to select.
+ * \return default images.
+ */
+static GtkImage *
+nsgtk_theme_image_default(nsgtk_toolbar_button tbbutton, GtkIconSize iconsize)
+{
+ GtkImage *image; /* The GTK image to return */
+
+ switch(tbbutton) {
+
+#define BUTTON_IMAGE(p, q) \
+ case p##_BUTTON: \
+ image = GTK_IMAGE(nsgtk_image_new_from_stock(q, iconsize)); \
+ break
+
+ BUTTON_IMAGE(BACK, NSGTK_STOCK_GO_BACK);
+ BUTTON_IMAGE(FORWARD, NSGTK_STOCK_GO_FORWARD);
+ BUTTON_IMAGE(STOP, NSGTK_STOCK_STOP);
+ BUTTON_IMAGE(RELOAD, NSGTK_STOCK_REFRESH);
+ BUTTON_IMAGE(HOME, NSGTK_STOCK_HOME);
+ BUTTON_IMAGE(NEWWINDOW, "gtk-new");
+ BUTTON_IMAGE(NEWTAB, "gtk-new");
+ BUTTON_IMAGE(OPENFILE, NSGTK_STOCK_OPEN);
+ BUTTON_IMAGE(CLOSETAB, NSGTK_STOCK_CLOSE);
+ BUTTON_IMAGE(CLOSEWINDOW, NSGTK_STOCK_CLOSE);
+ BUTTON_IMAGE(SAVEPAGE, NSGTK_STOCK_SAVE_AS);
+ BUTTON_IMAGE(PRINTPREVIEW, "gtk-print-preview");
+ BUTTON_IMAGE(PRINT, "gtk-print");
+ BUTTON_IMAGE(QUIT, "gtk-quit");
+ BUTTON_IMAGE(CUT, "gtk-cut");
+ BUTTON_IMAGE(COPY, "gtk-copy");
+ BUTTON_IMAGE(PASTE, "gtk-paste");
+ BUTTON_IMAGE(DELETE, "gtk-delete");
+ BUTTON_IMAGE(SELECTALL, "gtk-select-all");
+ BUTTON_IMAGE(FIND, NSGTK_STOCK_FIND);
+ BUTTON_IMAGE(PREFERENCES, "gtk-preferences");
+ BUTTON_IMAGE(ZOOMPLUS, "gtk-zoom-in");
+ BUTTON_IMAGE(ZOOMMINUS, "gtk-zoom-out");
+ BUTTON_IMAGE(ZOOMNORMAL, "gtk-zoom-100");
+ BUTTON_IMAGE(FULLSCREEN, "gtk-fullscreen");
+ BUTTON_IMAGE(VIEWSOURCE, "gtk-index");
+ BUTTON_IMAGE(CONTENTS, "gtk-help");
+ BUTTON_IMAGE(ABOUT, "gtk-about");
+#undef BUTTON_IMAGE
+
+ case HISTORY_BUTTON:
+ image = GTK_IMAGE(gtk_image_new_from_pixbuf(arrow_down_pixbuf));
+ break;
+
+ default:
+ image = GTK_IMAGE(nsgtk_image_new_from_stock("gtk-missing-image",
+ iconsize));
+ break;
+
+ }
+ return image;
+
+}
+
+/**
+ * Get default image for search buttons / menu items from gtk stock items
+ *
+ * \param tbbutton search button reference
+ * \param iconsize The size of icons to select.
+ * \return default search image.
+ */
+
+static GtkImage *
+nsgtk_theme_searchimage_default(nsgtk_search_buttons tbbutton,
+ GtkIconSize iconsize)
+{
+ switch (tbbutton) {
+
+ case (SEARCH_BACK_BUTTON):
+ return GTK_IMAGE(nsgtk_image_new_from_stock(NSGTK_STOCK_GO_BACK,
+ iconsize));
+ case (SEARCH_FORWARD_BUTTON):
+ return GTK_IMAGE(nsgtk_image_new_from_stock(NSGTK_STOCK_GO_FORWARD,
+ iconsize));
+ case (SEARCH_CLOSE_BUTTON):
+ return GTK_IMAGE(nsgtk_image_new_from_stock(NSGTK_STOCK_CLOSE,
+ iconsize));
+ default:
+ return NULL;
+ }
+}
+
+/**
+ * initialise a theme structure with gtk images
+ */
+static struct nsgtk_theme *nsgtk_theme_load(GtkIconSize iconsize)
+{
+ struct nsgtk_theme *theme = malloc(sizeof(struct nsgtk_theme));
+ int btnloop;
+
+ if (theme == NULL) {
+ nsgtk_warning("NoMemory", 0);
+ return NULL;
+ }
+
+ for (btnloop = BACK_BUTTON; btnloop < PLACEHOLDER_BUTTON ; btnloop++) {
+ theme->image[btnloop] = nsgtk_theme_image_default(btnloop, iconsize);
+ }
+
+ for (btnloop = SEARCH_BACK_BUTTON; btnloop < SEARCH_BUTTONS_COUNT; btnloop++) {
+ theme->searchimage[btnloop] = nsgtk_theme_searchimage_default(btnloop, iconsize);
+ }
+ return theme;
+}
+
+
+
+/* exported function documented in gtk/toolbar.h */
+void nsgtk_theme_implement(struct nsgtk_scaffolding *g)
+{
+ struct nsgtk_theme *theme[IMAGE_SET_COUNT];
+ int i;
+ struct nsgtk_button_connect *button;
+ struct gtk_search *search;
+
+ theme[IMAGE_SET_MAIN_MENU] = nsgtk_theme_load(GTK_ICON_SIZE_MENU);
+ theme[IMAGE_SET_RCLICK_MENU] = nsgtk_theme_load(GTK_ICON_SIZE_MENU);
+ theme[IMAGE_SET_POPUP_MENU] = nsgtk_theme_load(GTK_ICON_SIZE_MENU);
+ theme[IMAGE_SET_BUTTONS] = nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR);
+
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ if ((i == URL_BAR_ITEM) || (i == THROBBER_ITEM) ||
+ (i == WEBSEARCH_ITEM))
+ continue;
+
+ button = nsgtk_scaffolding_button(g, i);
+ if (button == NULL)
+ continue;
+
+ /* gtk_image_menu_item_set_image accepts NULL image */
+ if ((button->main != NULL) &&
+ (theme[IMAGE_SET_MAIN_MENU] != NULL)) {
+ nsgtk_image_menu_item_set_image(
+ GTK_WIDGET(button->main),
+ GTK_WIDGET(theme[IMAGE_SET_MAIN_MENU]->image[i]));
+ gtk_widget_show_all(GTK_WIDGET(button->main));
+ }
+ if ((button->rclick != NULL) &&
+ (theme[IMAGE_SET_RCLICK_MENU] != NULL)) {
+ nsgtk_image_menu_item_set_image(GTK_WIDGET(button->rclick),
+ GTK_WIDGET(
+ theme[IMAGE_SET_RCLICK_MENU]->
+ image[i]));
+ gtk_widget_show_all(GTK_WIDGET(button->rclick));
+ }
+ if ((button->popup != NULL) &&
+ (theme[IMAGE_SET_POPUP_MENU] != NULL)) {
+ nsgtk_image_menu_item_set_image(GTK_WIDGET(button->popup),
+ GTK_WIDGET(
+ theme[IMAGE_SET_POPUP_MENU]->
+ image[i]));
+ gtk_widget_show_all(GTK_WIDGET(button->popup));
+ }
+ if ((button->location != -1) && (button->button != NULL) &&
+ (theme[IMAGE_SET_BUTTONS] != NULL)) {
+ gtk_tool_button_set_icon_widget(
+ GTK_TOOL_BUTTON(button->button),
+ GTK_WIDGET(
+ theme[IMAGE_SET_BUTTONS]->
+ image[i]));
+ gtk_widget_show_all(GTK_WIDGET(button->button));
+ }
+ }
+
+ /* set search bar images */
+ search = nsgtk_scaffolding_search(g);
+ if ((search != NULL) && (theme[IMAGE_SET_MAIN_MENU] != NULL)) {
+ /* gtk_tool_button_set_icon_widget accepts NULL image */
+ if (search->buttons[SEARCH_BACK_BUTTON] != NULL) {
+ gtk_tool_button_set_icon_widget(
+ search->buttons[SEARCH_BACK_BUTTON],
+ GTK_WIDGET(theme[IMAGE_SET_MAIN_MENU]->
+ searchimage[SEARCH_BACK_BUTTON]));
+ gtk_widget_show_all(GTK_WIDGET(
+ search->buttons[SEARCH_BACK_BUTTON]));
+ }
+ if (search->buttons[SEARCH_FORWARD_BUTTON] != NULL) {
+ gtk_tool_button_set_icon_widget(
+ search->buttons[SEARCH_FORWARD_BUTTON],
+ GTK_WIDGET(theme[IMAGE_SET_MAIN_MENU]->
+ searchimage[SEARCH_FORWARD_BUTTON]));
+ gtk_widget_show_all(GTK_WIDGET(
+ search->buttons[
+ SEARCH_FORWARD_BUTTON]));
+ }
+ if (search->buttons[SEARCH_CLOSE_BUTTON] != NULL) {
+ gtk_tool_button_set_icon_widget(
+ search->buttons[SEARCH_CLOSE_BUTTON],
+ GTK_WIDGET(theme[IMAGE_SET_MAIN_MENU]->
+ searchimage[SEARCH_CLOSE_BUTTON]));
+ gtk_widget_show_all(GTK_WIDGET(
+ search->buttons[SEARCH_CLOSE_BUTTON]));
+ }
+ }
+
+ for (i = 0; i < IMAGE_SET_COUNT; i++) {
+ if (theme[i] != NULL) {
+ free(theme[i]);
+ }
+ }
+}
+
+
+/**
+ * callback function to iterate toolbar's widgets
+ */
+static void nsgtk_toolbar_clear_toolbar(GtkWidget *widget, gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ gtk_container_remove(GTK_CONTAINER(nsgtk_scaffolding_toolbar(g)),
+ widget);
+}
+
+/**
+ * connect temporary handler for toolbar edit events
+ *
+ * \param g The scaffolding
+ * \param bi The button index
+ */
+static void nsgtk_toolbar_temp_connect(struct nsgtk_scaffolding *g,
+ nsgtk_toolbar_button bi)
+{
+ struct nsgtk_button_connect *bc;
+
+ if (bi != URL_BAR_ITEM) {
+ bc = nsgtk_scaffolding_button(g, bi);
+ if ((bc->button != NULL) && (bc->dataminus != NULL)) {
+ g_signal_connect(bc->button,
+ "drag-data-get",
+ G_CALLBACK(bc->dataminus),
+ g);
+ }
+ }
+}
+
+/**
+ * get scaffolding button index of button at location
+ *
+ * \return toolbar item id from location when there is an item at that logical
+ * location; else -1
+ */
+static nsgtk_toolbar_button
+nsgtk_toolbar_get_id_at_location(struct nsgtk_scaffolding *g, int i)
+{
+ int q;
+ for (q = BACK_BUTTON; q < PLACEHOLDER_BUTTON; q++) {
+ if (nsgtk_scaffolding_button(g, q)->location == i) {
+ return q;
+ }
+ }
+ return -1;
+}
+
+/**
+ * widget factory for creation of toolbar item widgets
+ * \param g the reference scaffolding
+ * \param i the id of the widget
+ * \param theme the theme to make the widgets from
+ */
+static GtkWidget *
+nsgtk_toolbar_make_widget(struct nsgtk_scaffolding *g,
+ nsgtk_toolbar_button i,
+ struct nsgtk_theme *theme)
+{
+ GtkWidget *w = NULL;
+
+ switch(i) {
+
+/* gtk_tool_button_new() accepts NULL args */
+#define MAKE_STOCKBUTTON(p, q) \
+ case p##_BUTTON: { \
+ GtkStockItem item; \
+ char *label = NULL; \
+ if (nsgtk_stock_lookup(q, &item) && \
+ (item.label != NULL) && \
+ ((label = remove_underscores(item.label, false)) != NULL)) { \
+ w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET( \
+ theme->image[p##_BUTTON]), label)); \
+ free(label); \
+ } else { \
+ w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET( \
+ theme->image[p##_BUTTON]), q)); \
+ } \
+ break; \
+ }
+
+ MAKE_STOCKBUTTON(HOME, NSGTK_STOCK_HOME)
+ MAKE_STOCKBUTTON(BACK, NSGTK_STOCK_GO_BACK)
+ MAKE_STOCKBUTTON(FORWARD, NSGTK_STOCK_GO_FORWARD)
+ MAKE_STOCKBUTTON(STOP, NSGTK_STOCK_STOP)
+ MAKE_STOCKBUTTON(RELOAD, NSGTK_STOCK_REFRESH)
+#undef MAKE_STOCKBUTTON
+
+ case HISTORY_BUTTON:
+ w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET(
+ theme->image[HISTORY_BUTTON]), "H"));
+ break;
+
+ case URL_BAR_ITEM: {
+ GtkWidget *entry = nsgtk_entry_new();
+ w = GTK_WIDGET(gtk_tool_item_new());
+
+ if ((entry == NULL) || (w == NULL)) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+
+ nsgtk_entry_set_icon_from_pixbuf(entry,
+ GTK_ENTRY_ICON_PRIMARY,
+ favicon_pixbuf);
+
+ gtk_container_add(GTK_CONTAINER(w), entry);
+ gtk_tool_item_set_expand(GTK_TOOL_ITEM(w), TRUE);
+ break;
+ }
+
+ case THROBBER_ITEM: {
+ if ((nsgtk_throbber == NULL) ||
+ (nsgtk_throbber->framedata == NULL) ||
+ (nsgtk_throbber->framedata[0] == NULL)) {
+ return NULL;
+ }
+
+ if (edit_mode) {
+ w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET(
+ gtk_image_new_from_pixbuf(
+ nsgtk_throbber->framedata[0])),
+ "[throbber]"));
+ } else {
+ GtkWidget *image;
+
+ w = GTK_WIDGET(gtk_tool_item_new());
+
+ image = gtk_image_new_from_pixbuf(nsgtk_throbber->framedata[0]);
+ if (image != NULL) {
+ nsgtk_widget_set_alignment(image,
+ GTK_ALIGN_CENTER,
+ GTK_ALIGN_CENTER);
+ nsgtk_widget_set_margins(image, 3, 0);
+
+ gtk_container_add(GTK_CONTAINER(w), image);
+ }
+ }
+ break;
+ }
+
+ case WEBSEARCH_ITEM: {
+ if (edit_mode)
+ return GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET(
+ nsgtk_image_new_from_stock(NSGTK_STOCK_FIND,
+ GTK_ICON_SIZE_LARGE_TOOLBAR)),
+ "[websearch]"));
+
+ GtkWidget *entry = nsgtk_entry_new();
+
+ w = GTK_WIDGET(gtk_tool_item_new());
+
+ if ((entry == NULL) || (w == NULL)) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+
+ gtk_widget_set_size_request(entry, NSGTK_WEBSEARCH_WIDTH, -1);
+
+ nsgtk_entry_set_icon_from_stock(entry, GTK_ENTRY_ICON_PRIMARY,
+ NSGTK_STOCK_INFO);
+
+ gtk_container_add(GTK_CONTAINER(w), entry);
+ break;
+ }
+
+/* gtk_tool_button_new accepts NULL args */
+#define MAKE_MENUBUTTON(p, q) \
+ case p##_BUTTON: { \
+ char *label = NULL; \
+ label = remove_underscores(messages_get(#q), false); \
+ w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET( \
+ theme->image[p##_BUTTON]), label)); \
+ if (label != NULL) \
+ free(label); \
+ break; \
+ }
+
+ MAKE_MENUBUTTON(NEWWINDOW, gtkNewWindow)
+ MAKE_MENUBUTTON(NEWTAB, gtkNewTab)
+ MAKE_MENUBUTTON(OPENFILE, gtkOpenFile)
+ MAKE_MENUBUTTON(CLOSETAB, gtkCloseTab)
+ MAKE_MENUBUTTON(CLOSEWINDOW, gtkCloseWindow)
+ MAKE_MENUBUTTON(SAVEPAGE, gtkSavePage)
+ MAKE_MENUBUTTON(PRINTPREVIEW, gtkPrintPreview)
+ MAKE_MENUBUTTON(PRINT, gtkPrint)
+ MAKE_MENUBUTTON(QUIT, gtkQuitMenu)
+ MAKE_MENUBUTTON(CUT, gtkCut)
+ MAKE_MENUBUTTON(COPY, gtkCopy)
+ MAKE_MENUBUTTON(PASTE, gtkPaste)
+ MAKE_MENUBUTTON(DELETE, gtkDelete)
+ MAKE_MENUBUTTON(SELECTALL, gtkSelectAll)
+ MAKE_MENUBUTTON(PREFERENCES, gtkPreferences)
+ MAKE_MENUBUTTON(ZOOMPLUS, gtkZoomPlus)
+ MAKE_MENUBUTTON(ZOOMMINUS, gtkZoomMinus)
+ MAKE_MENUBUTTON(ZOOMNORMAL, gtkZoomNormal)
+ MAKE_MENUBUTTON(FULLSCREEN, gtkFullScreen)
+ MAKE_MENUBUTTON(VIEWSOURCE, gtkViewSource)
+ MAKE_MENUBUTTON(CONTENTS, gtkContents)
+ MAKE_MENUBUTTON(ABOUT, gtkAbout)
+ MAKE_MENUBUTTON(PDF, gtkPDF)
+ MAKE_MENUBUTTON(PLAINTEXT, gtkPlainText)
+ MAKE_MENUBUTTON(DRAWFILE, gtkDrawFile)
+ MAKE_MENUBUTTON(POSTSCRIPT, gtkPostScript)
+ MAKE_MENUBUTTON(FIND, gtkFind)
+ MAKE_MENUBUTTON(DOWNLOADS, gtkDownloads)
+ MAKE_MENUBUTTON(SAVEWINDOWSIZE, gtkSaveWindowSize)
+ MAKE_MENUBUTTON(TOGGLEDEBUGGING, gtkToggleDebugging)
+ MAKE_MENUBUTTON(SAVEBOXTREE, gtkDebugBoxTree)
+ MAKE_MENUBUTTON(SAVEDOMTREE, gtkDebugDomTree)
+ MAKE_MENUBUTTON(LOCALHISTORY, gtkLocalHistory)
+ MAKE_MENUBUTTON(GLOBALHISTORY, gtkGlobalHistory)
+ MAKE_MENUBUTTON(ADDBOOKMARKS, gtkAddBookMarks)
+ MAKE_MENUBUTTON(SHOWBOOKMARKS, gtkShowBookMarks)
+ MAKE_MENUBUTTON(SHOWCOOKIES, gtkShowCookies)
+ MAKE_MENUBUTTON(OPENLOCATION, gtkOpenLocation)
+ MAKE_MENUBUTTON(NEXTTAB, gtkNextTab)
+ MAKE_MENUBUTTON(PREVTAB, gtkPrevTab)
+ MAKE_MENUBUTTON(GUIDE, gtkGuide)
+ MAKE_MENUBUTTON(INFO, gtkUserInformation)
+#undef MAKE_MENUBUTTON
+
+ default:
+ break;
+
+ }
+ return w;
+}
+
+/**
+ * called when a widget is dropped onto the toolbar
+ */
+static gboolean
+nsgtk_toolbar_data(GtkWidget *widget,
+ GdkDragContext *gdc,
+ gint x,
+ gint y,
+ guint time,
+ gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ int ind = gtk_toolbar_get_drop_index(nsgtk_scaffolding_toolbar(g),
+ x, y);
+ int q, i;
+ if (window->currentbutton == -1)
+ return TRUE;
+ struct nsgtk_theme *theme =
+ nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR);
+ if (theme == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return TRUE;
+ }
+ if (nsgtk_scaffolding_button(g, window->currentbutton)->location
+ != -1) {
+ /* widget was already in the toolbar; so replace */
+ if (nsgtk_scaffolding_button(g, window->currentbutton)->
+ location < ind)
+ ind--;
+ gtk_container_remove(GTK_CONTAINER(
+ nsgtk_scaffolding_toolbar(g)), GTK_WIDGET(
+ nsgtk_scaffolding_button(g,
+ window->currentbutton)->button));
+ /* 'move' all widgets further right than the original location,
+ * one place to the left in logical schema */
+ for (i = nsgtk_scaffolding_button(g, window->currentbutton)->
+ location + 1; i < PLACEHOLDER_BUTTON; i++) {
+ q = nsgtk_toolbar_get_id_at_location(g, i);
+ if (q == -1)
+ continue;
+ nsgtk_scaffolding_button(g, q)->location--;
+ }
+ nsgtk_scaffolding_button(g, window->currentbutton)->
+ location = -1;
+ }
+ nsgtk_scaffolding_button(g, window->currentbutton)->button =
+ GTK_TOOL_ITEM(nsgtk_toolbar_make_widget(g,
+ window->currentbutton, theme));
+ free(theme);
+ if (nsgtk_scaffolding_button(g, window->currentbutton)->button
+ == NULL) {
+ nsgtk_warning("NoMemory", 0);
+ return TRUE;
+ }
+ /* update logical schema */
+ nsgtk_scaffolding_reset_offset(g);
+ /* 'move' all widgets further right than the new location, one place to
+ * the right in logical schema */
+ for (i = PLACEHOLDER_BUTTON - 1; i >= ind; i--) {
+ q = nsgtk_toolbar_get_id_at_location(g, i);
+ if (q == -1)
+ continue;
+ nsgtk_scaffolding_button(g, q)->location++;
+ }
+ nsgtk_scaffolding_button(g, window->currentbutton)->location = ind;
+
+ /* complete action */
+ GtkToolItem *current_button;
+
+ current_button = GTK_TOOL_ITEM(nsgtk_scaffolding_button(g, window->currentbutton)->button);
+
+ gtk_toolbar_insert(nsgtk_scaffolding_toolbar(g), current_button, ind);
+
+ gtk_tool_item_set_use_drag_window(current_button, TRUE);
+ gtk_drag_source_set(GTK_WIDGET(current_button),
+ GDK_BUTTON1_MASK, &entry, 1,
+ GDK_ACTION_COPY);
+ nsgtk_toolbar_temp_connect(g, window->currentbutton);
+ gtk_widget_show_all(GTK_WIDGET(current_button));
+
+
+ window->currentbutton = -1;
+
+ return TRUE;
+}
+
+/**
+ * connected to toolbutton drop; perhaps one day it'll work properly so it may
+ * replace the global current_button
+ */
+static gboolean
+nsgtk_toolbar_move_complete(GtkWidget *widget,
+ GdkDragContext *gdc,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time,
+ gpointer data)
+{
+ return FALSE;
+}
+
+/**
+ * called when hovering an item above the toolbar
+ */
+static gboolean
+nsgtk_toolbar_action(GtkWidget *widget, GdkDragContext *gdc, gint x,
+ gint y, guint time, gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ GtkToolItem *item = gtk_tool_button_new(NULL, NULL);
+ if (item != NULL)
+ gtk_toolbar_set_drop_highlight_item(
+ nsgtk_scaffolding_toolbar(g),
+ GTK_TOOL_ITEM(item),
+ gtk_toolbar_get_drop_index(
+ nsgtk_scaffolding_toolbar(g), x, y));
+ return FALSE;
+}
+
+/**
+ * called when hovering stops
+ */
+static void
+nsgtk_toolbar_clear(GtkWidget *widget, GdkDragContext *gdc, guint time,
+ gpointer data)
+{
+ gtk_toolbar_set_drop_highlight_item(GTK_TOOLBAR(widget), NULL, 0);
+}
+
+/**
+ * add item to toolbar.
+ *
+ * the function should be called, when multiple items are being added,
+ * in ascending order.
+ *
+ * \param g the scaffolding whose toolbar an item is added to.
+ * \param i the location in the toolbar.
+ * \param theme The theme in use.
+ */
+static void
+nsgtk_toolbar_add_item_to_toolbar(struct nsgtk_scaffolding *g, int i,
+ struct nsgtk_theme *theme)
+{
+ int q;
+ for (q = BACK_BUTTON; q < PLACEHOLDER_BUTTON; q++)
+ if (nsgtk_scaffolding_button(g, q)->location == i) {
+ nsgtk_scaffolding_button(g, q)->button = GTK_TOOL_ITEM(
+ nsgtk_toolbar_make_widget(g, q,
+ theme));
+ gtk_toolbar_insert(nsgtk_scaffolding_toolbar(g),
+ nsgtk_scaffolding_button(g, q)->button,
+ i);
+ break;
+ }
+}
+
+/**
+ * cleanup code physical update of all toolbars; resensitize
+ * \param g the 'front' scaffolding that called customize
+ */
+static void nsgtk_toolbar_close(struct nsgtk_scaffolding *g)
+{
+ int i;
+
+ struct nsgtk_scaffolding *list;
+ struct nsgtk_theme *theme;
+
+ list = nsgtk_scaffolding_iterate(NULL);
+ while (list) {
+ theme = nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR);
+ if (theme == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ continue;
+ }
+ /* clear toolbar */
+ gtk_container_foreach(GTK_CONTAINER(nsgtk_scaffolding_toolbar(
+ list)), nsgtk_toolbar_clear_toolbar, list);
+ /* then add items */
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ nsgtk_toolbar_add_item_to_toolbar(list, i, theme);
+ }
+ nsgtk_toolbar_connect_all(list);
+ gtk_widget_show_all(GTK_WIDGET(nsgtk_scaffolding_toolbar(
+ list)));
+ nsgtk_scaffolding_set_sensitivity(list);
+ nsgtk_widget_override_background_color(GTK_WIDGET(nsgtk_window_get_layout(nsgtk_scaffolding_top_level(list))), GTK_STATE_NORMAL, 0, 0xFFFF, 0xFFFF, 0xFFFF);
+ g_signal_handler_unblock(GTK_WIDGET(
+ nsgtk_window_get_layout(
+ nsgtk_scaffolding_top_level(list))),
+ nsgtk_window_get_signalhandler(
+ nsgtk_scaffolding_top_level(list),
+ NSGTK_WINDOW_SIGNAL_CLICK));
+ g_signal_handler_unblock(GTK_WIDGET(
+ nsgtk_window_get_layout(
+ nsgtk_scaffolding_top_level(list))),
+ nsgtk_window_get_signalhandler(
+ nsgtk_scaffolding_top_level(list),
+ NSGTK_WINDOW_SIGNAL_REDRAW));
+ browser_window_refresh_url_bar(
+ nsgtk_get_browser_window(
+ nsgtk_scaffolding_top_level(list)));
+
+ if (list != g)
+ gtk_widget_set_sensitive(GTK_WIDGET(
+ nsgtk_scaffolding_window(list)), TRUE);
+ free(theme);
+ list = nsgtk_scaffolding_iterate(list);
+ }
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_notebook(g)),
+ TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_menu_bar(g)),
+ TRUE);
+ /* update favicon etc */
+ nsgtk_scaffolding_set_top_level(nsgtk_scaffolding_top_level(g));
+
+ search_web_select_provider(-1);
+}
+
+/**
+ * when cancel button is clicked
+ */
+static gboolean nsgtk_toolbar_cancel_clicked(GtkWidget *widget, gpointer data)
+{
+ edit_mode = false;
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ /* reset g->buttons->location */
+ for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ nsgtk_scaffolding_button(g, i)->location =
+ window->buttonlocations[i];
+ }
+ nsgtk_toolbar_set_physical(g);
+ nsgtk_toolbar_connect_all(g);
+ nsgtk_toolbar_close(g);
+ nsgtk_scaffolding_set_sensitivity(g);
+ gtk_widget_destroy(window->window);
+ return TRUE;
+}
+
+/**
+ * physically add widgets to store window
+ */
+static bool nsgtk_toolbar_add_store_widget(GtkWidget *widget)
+{
+ if (window->numberh >= NSGTK_STORE_WIDTH) {
+ window->currentbar = gtk_toolbar_new();
+ if (window->currentbar == NULL) {
+ nsgtk_warning("NoMemory", 0);
+ return false;
+ }
+ gtk_toolbar_set_style(GTK_TOOLBAR(window->currentbar),
+ GTK_TOOLBAR_BOTH);
+ gtk_toolbar_set_icon_size(GTK_TOOLBAR(window->currentbar),
+ GTK_ICON_SIZE_LARGE_TOOLBAR);
+ gtk_box_pack_start(GTK_BOX(window->widgetvbox),
+ window->currentbar, FALSE, FALSE, 0);
+ window->numberh = 0;
+ }
+ gtk_widget_set_size_request(widget, NSGTK_BUTTON_WIDTH,
+ NSGTK_BUTTON_HEIGHT);
+ gtk_toolbar_insert(GTK_TOOLBAR(window->currentbar), GTK_TOOL_ITEM(
+ widget), window->numberh++);
+ gtk_tool_item_set_use_drag_window(GTK_TOOL_ITEM(widget), TRUE);
+ gtk_drag_source_set(widget, GDK_BUTTON1_MASK, &entry, 1,
+ GDK_ACTION_COPY);
+ gtk_widget_show_all(window->window);
+ return true;
+}
+
+/**
+ * save toolbar settings to file
+ */
+static void nsgtk_toolbar_customization_save(struct nsgtk_scaffolding *g)
+{
+ int i;
+ FILE *f = fopen(toolbar_indices_file_location, "w");
+ if (f == NULL){
+ nsgtk_warning("gtkFileError", toolbar_indices_file_location);
+ return;
+ }
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ fprintf(f, "%d;%d|", i, nsgtk_scaffolding_button(g, i)->location);
+ }
+ fclose(f);
+}
+
+/**
+ * cast toolbar settings to all scaffoldings referenced from the global linked
+ * list of gui_windows
+ */
+static void nsgtk_toolbar_cast(struct nsgtk_scaffolding *g)
+{
+ int i;
+ struct nsgtk_scaffolding *list;
+
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ window->buttonlocations[i] =
+ ((nsgtk_scaffolding_button(g, i)->location
+ >= -1) &&
+ (nsgtk_scaffolding_button(g, i)->location
+ < PLACEHOLDER_BUTTON)) ?
+ nsgtk_scaffolding_button(g, i)->location : -1;
+ }
+
+ list = nsgtk_scaffolding_iterate(NULL);
+ while (list) {
+ if (list != g)
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++)
+ nsgtk_scaffolding_button(list, i)->location =
+ window->buttonlocations[i];
+ list = nsgtk_scaffolding_iterate(list);
+ }
+}
+
+/**
+ * when 'save settings' button is clicked
+ */
+static gboolean nsgtk_toolbar_persist(GtkWidget *widget, gpointer data)
+{
+ edit_mode = false;
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ /* save state to file, update toolbars for all windows */
+ nsgtk_toolbar_customization_save(g);
+ nsgtk_toolbar_cast(g);
+ nsgtk_toolbar_set_physical(g);
+ nsgtk_toolbar_close(g);
+ gtk_widget_destroy(window->window);
+ return TRUE;
+}
+
+/**
+ * when 'reload defaults' button is clicked
+ */
+static gboolean nsgtk_toolbar_reset(GtkWidget *widget, gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ int i;
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++)
+ nsgtk_scaffolding_button(g, i)->location =
+ (i <= THROBBER_ITEM) ? i : -1;
+ nsgtk_toolbar_set_physical(g);
+ for (i = BACK_BUTTON; i <= THROBBER_ITEM; i++) {
+ if (i == URL_BAR_ITEM)
+ continue;
+ gtk_tool_item_set_use_drag_window(GTK_TOOL_ITEM(
+ nsgtk_scaffolding_button(g, i)->button), TRUE);
+ gtk_drag_source_set(GTK_WIDGET(
+ nsgtk_scaffolding_button(g, i)->button),
+ GDK_BUTTON1_MASK, &entry, 1, GDK_ACTION_COPY);
+ nsgtk_toolbar_temp_connect(g, i);
+ }
+ return TRUE;
+}
+
+/**
+ * when titlebar / alt-F4 window close event happens
+ */
+static gboolean nsgtk_toolbar_delete(GtkWidget *widget, GdkEvent *event,
+ gpointer data)
+{
+ edit_mode = false;
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ /* reset g->buttons->location */
+ for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ nsgtk_scaffolding_button(g, i)->location =
+ window->buttonlocations[i];
+ }
+ nsgtk_toolbar_set_physical(g);
+ nsgtk_toolbar_connect_all(g);
+ nsgtk_toolbar_close(g);
+ nsgtk_scaffolding_set_sensitivity(g);
+ gtk_widget_destroy(window->window);
+ return TRUE;
+}
+
+/**
+ * called when a widget is dropped onto the store window
+ */
+static gboolean
+nsgtk_toolbar_store_return(GtkWidget *widget, GdkDragContext *gdc,
+ gint x, gint y, guint time, gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ int q, i;
+
+ if ((window->fromstore) || (window->currentbutton == -1)) {
+ window->currentbutton = -1;
+ return FALSE;
+ }
+ if (nsgtk_scaffolding_button(g, window->currentbutton)->location
+ != -1) {
+ /* 'move' all widgets further right, one place to the left
+ * in logical schema */
+ for (i = nsgtk_scaffolding_button(g, window->currentbutton)->
+ location + 1; i < PLACEHOLDER_BUTTON; i++) {
+ q = nsgtk_toolbar_get_id_at_location(g, i);
+ if (q == -1)
+ continue;
+ nsgtk_scaffolding_button(g, q)->location--;
+ }
+ gtk_container_remove(GTK_CONTAINER(
+ nsgtk_scaffolding_toolbar(g)), GTK_WIDGET(
+ nsgtk_scaffolding_button(g,
+ window->currentbutton)->button));
+ nsgtk_scaffolding_button(g, window->currentbutton)->location
+ = -1;
+ }
+ window->currentbutton = -1;
+ gtk_drag_finish(gdc, TRUE, TRUE, time);
+ return FALSE;
+}
+
+/**
+ * called when hovering above the store
+ */
+static gboolean
+nsgtk_toolbar_store_action(GtkWidget *widget, GdkDragContext *gdc,
+ gint x, gint y, guint time, gpointer data)
+{
+ return FALSE;
+}
+
+/**
+ * create store window
+ */
+static void nsgtk_toolbar_window_open(struct nsgtk_scaffolding *g)
+{
+ int x = 0, y = 0;
+ struct nsgtk_theme *theme;
+ nserror res;
+
+ theme = nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR);
+ if (theme == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ nsgtk_toolbar_cancel_clicked(NULL, g);
+ return;
+ }
+
+ res = nsgtk_builder_new_from_resname("toolbar", &window->builder);
+ if (res != NSERROR_OK) {
+ LOG("Toolbar UI builder init failed");
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ nsgtk_toolbar_cancel_clicked(NULL, g);
+ free(theme);
+ return;
+ }
+
+ gtk_builder_connect_signals(window->builder, NULL);
+
+ window->window = GTK_WIDGET(gtk_builder_get_object(window->builder,
+ "toolbarwindow"));
+ if (window->window == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ nsgtk_toolbar_cancel_clicked(NULL, g);
+ free(theme);
+ return;
+ }
+
+ window->widgetvbox = GTK_WIDGET(gtk_builder_get_object(window->builder,
+ "widgetvbox"));
+ if (window->widgetvbox == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ nsgtk_toolbar_cancel_clicked(NULL, g);
+ free(theme);
+ return;
+ }
+
+ window->numberh = NSGTK_STORE_WIDTH; /* preset to width [in buttons] of */
+ /* store to cause creation of a new toolbar */
+ window->currentbutton = -1;
+ /* load toolbuttons */
+ /* add toolbuttons to window */
+ /* set event handlers */
+ for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ if (i == URL_BAR_ITEM)
+ continue;
+ window->store_buttons[i] =
+ nsgtk_toolbar_make_widget(g, i, theme);
+ if (window->store_buttons[i] == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ continue;
+ }
+ nsgtk_toolbar_add_store_widget(window->store_buttons[i]);
+ g_signal_connect(window->store_buttons[i], "drag-data-get",
+ G_CALLBACK(
+ nsgtk_scaffolding_button(g, i)->dataplus), g);
+ }
+ free(theme);
+
+ gtk_window_set_transient_for(GTK_WINDOW(window->window),
+ nsgtk_scaffolding_window(g));
+ gtk_window_set_title(GTK_WINDOW(window->window), messages_get(
+ "gtkToolBarTitle"));
+ gtk_window_set_accept_focus(GTK_WINDOW(window->window), FALSE);
+ gtk_drag_dest_set(GTK_WIDGET(window->window), GTK_DEST_DEFAULT_MOTION |
+ GTK_DEST_DEFAULT_DROP, &entry, 1, GDK_ACTION_COPY);
+ gtk_widget_show_all(window->window);
+ gtk_window_set_position(GTK_WINDOW(window->window),
+ GTK_WIN_POS_CENTER_ON_PARENT);
+ gtk_window_get_position(nsgtk_scaffolding_window(g), &x, &y);
+ gtk_window_move(GTK_WINDOW(window->window), x, y + 100);
+
+ g_signal_connect(GTK_WIDGET(gtk_builder_get_object(window->builder,
+ "cancelbutton")),
+ "clicked",
+ G_CALLBACK(nsgtk_toolbar_cancel_clicked),
+ g);
+
+ g_signal_connect(GTK_WIDGET(gtk_builder_get_object(window->builder,
+ "okbutton")),
+ "clicked",
+ G_CALLBACK(nsgtk_toolbar_persist),
+ g);
+
+ g_signal_connect(GTK_WIDGET(gtk_builder_get_object(window->builder,
+ "resetbutton")),
+ "clicked",
+ G_CALLBACK(nsgtk_toolbar_reset),
+ g);
+
+ g_signal_connect(window->window, "delete-event",
+ G_CALLBACK(nsgtk_toolbar_delete), g);
+
+ g_signal_connect(window->window, "drag-drop",
+ G_CALLBACK(nsgtk_toolbar_store_return), g);
+
+ g_signal_connect(window->window, "drag-motion",
+ G_CALLBACK(nsgtk_toolbar_store_action), g);
+}
+
+/**
+ * change behaviour of scaffoldings while editing toolbar
+ *
+ * All buttons as well as window clicks are desensitized; then buttons
+ * in the front window are changed to movable buttons
+ */
+void nsgtk_toolbar_customization_init(struct nsgtk_scaffolding *g)
+{
+ int i;
+ struct nsgtk_scaffolding *list;
+ edit_mode = true;
+
+ list = nsgtk_scaffolding_iterate(NULL);
+ while (list) {
+ g_signal_handler_block(GTK_WIDGET(
+ nsgtk_window_get_layout(
+ nsgtk_scaffolding_top_level(list))),
+ nsgtk_window_get_signalhandler(
+ nsgtk_scaffolding_top_level(list),
+ NSGTK_WINDOW_SIGNAL_CLICK));
+ g_signal_handler_block(GTK_WIDGET(
+ nsgtk_window_get_layout(
+ nsgtk_scaffolding_top_level(list))),
+ nsgtk_window_get_signalhandler(
+ nsgtk_scaffolding_top_level(list),
+ NSGTK_WINDOW_SIGNAL_REDRAW));
+ nsgtk_widget_override_background_color(
+ GTK_WIDGET(nsgtk_window_get_layout(
+ nsgtk_scaffolding_top_level(list))),
+ GTK_STATE_NORMAL, 0, 0xEEEE, 0xEEEE, 0xEEEE);
+
+ if (list == g) {
+ list = nsgtk_scaffolding_iterate(list);
+ continue;
+ }
+ /* set sensitive for all gui_windows save g */
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_window(
+ list)), FALSE);
+ list = nsgtk_scaffolding_iterate(list);
+ }
+ /* set sensitive for all of g save toolbar */
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_menu_bar(g)),
+ FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_notebook(g)),
+ FALSE);
+
+ /* set editable aspect for toolbar */
+ gtk_container_foreach(GTK_CONTAINER(nsgtk_scaffolding_toolbar(g)),
+ nsgtk_toolbar_clear_toolbar, g);
+ nsgtk_toolbar_set_physical(g);
+ /* memorize button locations, set editable */
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ window->buttonlocations[i] = nsgtk_scaffolding_button(g, i)
+ ->location;
+ if ((window->buttonlocations[i] == -1) || (i == URL_BAR_ITEM))
+ continue;
+ gtk_tool_item_set_use_drag_window(GTK_TOOL_ITEM(
+ nsgtk_scaffolding_button(g, i)->button), TRUE);
+ gtk_drag_source_set(GTK_WIDGET(nsgtk_scaffolding_button(
+ g, i)->button), GDK_BUTTON1_MASK, &entry, 1,
+ GDK_ACTION_COPY);
+ nsgtk_toolbar_temp_connect(g, i);
+ }
+
+ /* add move button listeners */
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)),
+ "drag-drop", G_CALLBACK(nsgtk_toolbar_data), g);
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)),
+ "drag-data-received", G_CALLBACK(
+ nsgtk_toolbar_move_complete), g);
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)),
+ "drag-motion", G_CALLBACK(nsgtk_toolbar_action), g);
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)),
+ "drag-leave", G_CALLBACK(
+ nsgtk_toolbar_clear), g);
+
+ /* set data types */
+ gtk_drag_dest_set(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)),
+ GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
+ &entry, 1, GDK_ACTION_COPY);
+
+ /* open toolbar window */
+ nsgtk_toolbar_window_open(g);
+}
+
+/**
+ * set toolbar logical -> physical; physically visible toolbar buttons are made
+ * to correspond to the logically stored schema in terms of location
+ * visibility etc
+ */
+void nsgtk_toolbar_set_physical(struct nsgtk_scaffolding *g)
+{
+ int i;
+ struct nsgtk_theme *theme =
+ nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR);
+ if (theme == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return;
+ }
+ /* simplest is to clear the toolbar then reload it from memory */
+ gtk_container_foreach(GTK_CONTAINER(nsgtk_scaffolding_toolbar(g)),
+ nsgtk_toolbar_clear_toolbar, g);
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++)
+ nsgtk_toolbar_add_item_to_toolbar(g, i, theme);
+ gtk_widget_show_all(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)));
+ free(theme);
+}
+
+/**
+ * \return toolbar item id when a widget is an element of the scaffolding
+ * else -1
+ */
+int nsgtk_toolbar_get_id_from_widget(GtkWidget *widget,
+ struct nsgtk_scaffolding *g)
+{
+ int i;
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ if ((nsgtk_scaffolding_button(g, i)->location != -1)
+ && (widget == GTK_WIDGET(
+ nsgtk_scaffolding_button(g, i)->button))) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+/**
+ * add handlers to factory widgets
+ * \param g the scaffolding to attach handlers to
+ * \param i the toolbar item id
+ */
+static void
+nsgtk_toolbar_set_handler(struct nsgtk_scaffolding *g, nsgtk_toolbar_button i)
+{
+ switch(i){
+ case URL_BAR_ITEM:
+ nsgtk_scaffolding_update_url_bar_ref(g);
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_urlbar(g)),
+ "activate", G_CALLBACK(
+ nsgtk_window_url_activate_event), g);
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_urlbar(g)),
+ "changed", G_CALLBACK(
+ nsgtk_window_url_changed), g);
+ break;
+ case THROBBER_ITEM:
+ nsgtk_scaffolding_update_throbber_ref(g);
+ break;
+ case WEBSEARCH_ITEM:
+ nsgtk_scaffolding_update_websearch_ref(g);
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_websearch(g)),
+ "activate", G_CALLBACK(
+ nsgtk_websearch_activate), g);
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_websearch(g)),
+ "button-press-event", G_CALLBACK(
+ nsgtk_websearch_clear), g);
+ break;
+ default:
+ if ((nsgtk_scaffolding_button(g, i)->bhandler != NULL) &&
+ (nsgtk_scaffolding_button(g, i)->button
+ != NULL))
+ g_signal_connect(nsgtk_scaffolding_button(g, i)->
+ button, "clicked",
+ G_CALLBACK(nsgtk_scaffolding_button(g,
+ i)->bhandler), g);
+ break;
+ }
+}
+
+/**
+ * connect 'normal' handlers to toolbar buttons
+ */
+void nsgtk_toolbar_connect_all(struct nsgtk_scaffolding *g)
+{
+ int q, i;
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ q = nsgtk_toolbar_get_id_at_location(g, i);
+ if (q == -1)
+ continue;
+ if (nsgtk_scaffolding_button(g, q)->button != NULL)
+ g_signal_connect(
+ nsgtk_scaffolding_button(g, q)->button,
+ "size-allocate", G_CALLBACK(
+ nsgtk_scaffolding_toolbar_size_allocate
+ ), g);
+ nsgtk_toolbar_set_handler(g, q);
+ }
+}
+
+
+#define DATAHANDLER(p, q, r)\
+gboolean nsgtk_toolbar_##p##_button_data(GtkWidget *widget, GdkDragContext\
+ *cont, GtkSelectionData *selection, guint info, guint time,\
+ gpointer data)\
+{\
+ r->currentbutton = q##_BUTTON;\
+ r->fromstore = true;\
+ return TRUE;\
+}\
+gboolean nsgtk_toolbar_##p##_toolbar_button_data(GtkWidget *widget,\
+ GdkDragContext *cont, GtkSelectionData *selection, guint info,\
+ guint time, gpointer data)\
+{\
+ r->currentbutton = q##_BUTTON;\
+ r->fromstore = false;\
+ return TRUE;\
+}
+
+DATAHANDLER(home, HOME, window)
+DATAHANDLER(forward, FORWARD, window)
+DATAHANDLER(back, BACK, window)
+DATAHANDLER(stop, STOP, window)
+DATAHANDLER(reload, RELOAD, window)
+DATAHANDLER(history, HISTORY, window)
+DATAHANDLER(newwindow, NEWWINDOW, window)
+DATAHANDLER(newtab, NEWTAB, window)
+DATAHANDLER(openfile, OPENFILE, window)
+DATAHANDLER(closetab, CLOSETAB, window)
+DATAHANDLER(closewindow, CLOSEWINDOW, window)
+DATAHANDLER(savepage, SAVEPAGE, window)
+DATAHANDLER(printpreview, PRINTPREVIEW, window)
+DATAHANDLER(print, PRINT, window)
+DATAHANDLER(quit, QUIT, window)
+DATAHANDLER(cut, CUT, window)
+DATAHANDLER(copy, COPY, window)
+DATAHANDLER(paste, PASTE, window)
+DATAHANDLER(delete, DELETE, window)
+DATAHANDLER(selectall, SELECTALL, window)
+DATAHANDLER(preferences, PREFERENCES, window)
+DATAHANDLER(zoomplus, ZOOMPLUS, window)
+DATAHANDLER(zoomminus, ZOOMMINUS, window)
+DATAHANDLER(zoomnormal, ZOOMNORMAL, window)
+DATAHANDLER(fullscreen, FULLSCREEN, window)
+DATAHANDLER(viewsource, VIEWSOURCE, window)
+DATAHANDLER(contents, CONTENTS, window)
+DATAHANDLER(about, ABOUT, window)
+DATAHANDLER(pdf, PDF, window)
+DATAHANDLER(plaintext, PLAINTEXT, window)
+DATAHANDLER(drawfile, DRAWFILE, window)
+DATAHANDLER(postscript, POSTSCRIPT, window)
+DATAHANDLER(find, FIND, window)
+DATAHANDLER(downloads, DOWNLOADS, window)
+DATAHANDLER(savewindowsize, SAVEWINDOWSIZE, window)
+DATAHANDLER(toggledebugging, TOGGLEDEBUGGING, window)
+DATAHANDLER(debugboxtree, SAVEBOXTREE, window)
+DATAHANDLER(debugdomtree, SAVEDOMTREE, window)
+DATAHANDLER(localhistory, LOCALHISTORY, window)
+DATAHANDLER(globalhistory, GLOBALHISTORY, window)
+DATAHANDLER(addbookmarks, ADDBOOKMARKS, window)
+DATAHANDLER(showbookmarks, SHOWBOOKMARKS, window)
+DATAHANDLER(showcookies, SHOWCOOKIES, window)
+DATAHANDLER(openlocation, OPENLOCATION, window)
+DATAHANDLER(nexttab, NEXTTAB, window)
+DATAHANDLER(prevtab, PREVTAB, window)
+DATAHANDLER(guide, GUIDE, window)
+DATAHANDLER(info, INFO, window)
+#undef DATAHANDLER
+#define DATAHANDLER(p, q, r)\
+gboolean nsgtk_toolbar_##p##_button_data(GtkWidget *widget, GdkDragContext\
+ *cont, GtkSelectionData *selection, guint info, guint time,\
+ gpointer data)\
+{\
+ r->currentbutton = q##_ITEM;\
+ r->fromstore = true;\
+ return TRUE;\
+}\
+gboolean nsgtk_toolbar_##p##_toolbar_button_data(GtkWidget *widget,\
+ GdkDragContext *cont, GtkSelectionData *selection, guint info,\
+ guint time, gpointer data)\
+{\
+ r->currentbutton = q##_ITEM;\
+ r->fromstore = false;\
+ return TRUE;\
+}
+
+DATAHANDLER(throbber, THROBBER, window)
+DATAHANDLER(websearch, WEBSEARCH, window)
+#undef DATAHANDLER
+
+
+/**
+ * load toolbar settings from file; file is a set of fields arranged as
+ * [itemreference];[itemlocation]|[itemreference];[itemlocation]| etc
+ */
+void nsgtk_toolbar_customization_load(struct nsgtk_scaffolding *g)
+{
+ int i, ii;
+ char *val;
+ char buffer[SLEN("11;|") * 2 * PLACEHOLDER_BUTTON]; /* numbers 0-99 */
+ buffer[0] = '\0';
+ char *buffer1, *subbuffer, *ptr = NULL, *pter = NULL;
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++)
+ nsgtk_scaffolding_button(g, i)->location =
+ (i <= THROBBER_ITEM) ? i : -1;
+ FILE *f = fopen(toolbar_indices_file_location, "r");
+ if (f == NULL) {
+ nsgtk_warning(messages_get("gtkFileError"),
+ toolbar_indices_file_location);
+ return;
+ }
+ val = fgets(buffer, sizeof buffer, f);
+ if (val == NULL) {
+ LOG("empty read toolbar settings");
+ }
+ fclose(f);
+ i = BACK_BUTTON;
+ ii = BACK_BUTTON;
+ buffer1 = strtok_r(buffer, "|", &ptr);
+ while (buffer1 != NULL) {
+ subbuffer = strtok_r(buffer1, ";", &pter);
+ i = atoi(subbuffer);
+ subbuffer = strtok_r(NULL, ";", &pter);
+ ii = atoi(subbuffer);
+ if ((i >= BACK_BUTTON) && (i < PLACEHOLDER_BUTTON) &&
+ (ii >= -1) && (ii < PLACEHOLDER_BUTTON)) {
+ nsgtk_scaffolding_button(g, i)->location = ii;
+ }
+ buffer1 = strtok_r(NULL, "|", &ptr);
+ }
+}
diff --git a/frontends/gtk/toolbar.h b/frontends/gtk/toolbar.h
new file mode 100644
index 000000000..0453109ca
--- /dev/null
+++ b/frontends/gtk/toolbar.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_GTK_TOOLBAR_H_
+#define _NETSURF_GTK_TOOLBAR_H_
+
+#include <gtk/gtk.h>
+
+#include "gtk/scaffolding.h"
+
+/**
+ * sets up the images for scaffolding.
+ */
+void nsgtk_theme_implement(struct nsgtk_scaffolding *g);
+
+void nsgtk_toolbar_customization_init(struct nsgtk_scaffolding *g);
+void nsgtk_toolbar_init(struct nsgtk_scaffolding *g);
+void nsgtk_toolbar_customization_load(struct nsgtk_scaffolding *g);
+void nsgtk_toolbar_set_physical(struct nsgtk_scaffolding *g);
+void nsgtk_toolbar_connect_all(struct nsgtk_scaffolding *g);
+int nsgtk_toolbar_get_id_from_widget(GtkWidget *widget, struct nsgtk_scaffolding *g);
+
+#define TOOLPROTO(q) gboolean nsgtk_toolbar_##q##_button_data(\
+ GtkWidget *widget, GdkDragContext *cont, GtkSelectionData\
+ *selection, guint info, guint time, gpointer data);\
+gboolean nsgtk_toolbar_##q##_toolbar_button_data(GtkWidget *widget,\
+ GdkDragContext *cont, GtkSelectionData *selection, guint info,\
+ guint time, gpointer data)
+TOOLPROTO(home);
+TOOLPROTO(back);
+TOOLPROTO(forward);
+TOOLPROTO(reload);
+TOOLPROTO(stop);
+TOOLPROTO(throbber);
+TOOLPROTO(websearch);
+TOOLPROTO(history);
+TOOLPROTO(newwindow);
+TOOLPROTO(newtab);
+TOOLPROTO(openfile);
+TOOLPROTO(closetab);
+TOOLPROTO(closewindow);
+TOOLPROTO(savepage);
+TOOLPROTO(pdf);
+TOOLPROTO(plaintext);
+TOOLPROTO(drawfile);
+TOOLPROTO(postscript);
+TOOLPROTO(printpreview);
+TOOLPROTO(print);
+TOOLPROTO(quit);
+TOOLPROTO(cut);
+TOOLPROTO(copy);
+TOOLPROTO(paste);
+TOOLPROTO(delete);
+TOOLPROTO(selectall);
+TOOLPROTO(find);
+TOOLPROTO(preferences);
+TOOLPROTO(zoomplus);
+TOOLPROTO(zoomminus);
+TOOLPROTO(zoomnormal);
+TOOLPROTO(fullscreen);
+TOOLPROTO(viewsource);
+TOOLPROTO(downloads);
+TOOLPROTO(localhistory);
+TOOLPROTO(globalhistory);
+TOOLPROTO(addbookmarks);
+TOOLPROTO(showbookmarks);
+TOOLPROTO(showcookies);
+TOOLPROTO(openlocation);
+TOOLPROTO(nexttab);
+TOOLPROTO(prevtab);
+TOOLPROTO(savewindowsize);
+TOOLPROTO(toggledebugging);
+TOOLPROTO(debugboxtree);
+TOOLPROTO(debugdomtree);
+TOOLPROTO(contents);
+TOOLPROTO(guide);
+TOOLPROTO(info);
+TOOLPROTO(about);
+#undef TOOLPROTO
+
+#endif
diff --git a/frontends/gtk/treeview.c b/frontends/gtk/treeview.c
new file mode 100644
index 000000000..9baf57b62
--- /dev/null
+++ b/frontends/gtk/treeview.c
@@ -0,0 +1,586 @@
+/*
+ * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
+ * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Generic tree handling (implementation).
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include <math.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "utils/log.h"
+#include "utils/utf8.h"
+#include "desktop/tree.h"
+#include "desktop/plotters.h"
+
+#include "gtk/warn.h"
+#include "gtk/compat.h"
+#include "gtk/gui.h"
+#include "gtk/plotters.h"
+#include "gtk/treeview.h"
+
+struct nsgtk_treeview {
+ GtkWindow *window;
+ GtkScrolledWindow *scrolled;
+ GtkDrawingArea *drawing_area;
+ GtkIMContext *input_method;
+ bool mouse_pressed;
+ int mouse_pressed_x;
+ int mouse_pressed_y;
+ int last_x, last_y;
+ browser_mouse_state mouse_state;
+ struct tree *tree;
+};
+
+void nsgtk_treeview_destroy(struct nsgtk_treeview *tv)
+{
+ tree_delete(tv->tree);
+ g_object_unref(tv->input_method);
+ gtk_widget_destroy(GTK_WIDGET(tv->window));
+ free(tv);
+}
+
+struct tree *nsgtk_treeview_get_tree(struct nsgtk_treeview *tv)
+{
+ return tv->tree;
+}
+
+static void nsgtk_tree_redraw_request(int x, int y, int width, int height, void *data)
+{
+ struct nsgtk_treeview *tw = data;
+
+ gtk_widget_queue_draw_area(GTK_WIDGET(tw->drawing_area),
+ x, y, width, height);
+}
+
+
+/**
+ * Updates the tree owner following a tree resize
+ *
+ * \param tree the tree to update the owner of
+ * \param width The width to resize to.
+ * \param height The height to resize to.
+ * \param data The treeview resize.
+ */
+static void nsgtk_tree_resized(struct tree *tree, int width, int height, void *data)
+{
+ struct nsgtk_treeview *tw = data;
+
+ gtk_widget_set_size_request(GTK_WIDGET(tw->drawing_area),
+ width, height);
+ return;
+}
+
+/**
+ * Scrolls the tree to make an element visible
+ *
+ * \param y Y coordinate of the element
+ * \param height height of the element
+ * \param data user data assigned to the tree on tree creation
+ */
+static void nsgtk_tree_scroll_visible(int y, int height, void *data)
+{
+ int y0, y1;
+ gdouble page;
+ struct nsgtk_treeview *tw = data;
+ GtkAdjustment *vadj = gtk_scrolled_window_get_vadjustment(tw->scrolled);
+
+ assert(vadj);
+
+ g_object_get(vadj, "page-size", &page, NULL);
+
+ y0 = (int)(gtk_adjustment_get_value(vadj));
+ y1 = y0 + page;
+
+ if ((y >= y0) && (y + height <= y1))
+ return;
+ if (y + height > y1)
+ y0 = y0 + (y + height - y1);
+ if (y < y0)
+ y0 = y;
+ gtk_adjustment_set_value(vadj, y0);
+}
+
+
+/**
+ * Retrieves the dimensions of the window with the tree
+ *
+ * \param data user data assigned to the tree on tree creation
+ * \param width will be updated to window width if not NULL
+ * \param height will be updated to window height if not NULL
+ */
+static void nsgtk_tree_get_window_dimensions(int *width, int *height, void *data)
+{
+ struct nsgtk_treeview *tw = data;
+ GtkAdjustment *vadj;
+ GtkAdjustment *hadj;
+ gdouble page;
+
+ if (width != NULL) {
+ hadj = gtk_scrolled_window_get_hadjustment(tw->scrolled);
+ g_object_get(hadj, "page-size", &page, NULL);
+ *width = page;
+ }
+
+ if (height != NULL) {
+ vadj = gtk_scrolled_window_get_vadjustment(tw->scrolled);
+ g_object_get(vadj, "page-size", &page, NULL);
+ *height = page;
+ }
+}
+
+#if GTK_CHECK_VERSION(3,0,0)
+
+static gboolean
+nsgtk_tree_window_draw_event(GtkWidget *widget, cairo_t *cr, gpointer data)
+{
+ struct tree *tree = (struct tree *)data;
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &nsgtk_plotters
+ };
+ double x1;
+ double y1;
+ double x2;
+ double y2;
+
+ current_widget = widget;
+ current_cr = cr;
+
+ cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
+
+ tree_draw(tree, 0, 0, x1, y1, x2 - x1, y2 - y1, &ctx);
+
+ current_widget = NULL;
+
+ return FALSE;
+}
+
+#else
+
+/* signal handler functions for a tree window */
+static gboolean
+nsgtk_tree_window_draw_event(GtkWidget *widget, GdkEventExpose *event, gpointer g)
+{
+ struct tree *tree = (struct tree *) g;
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &nsgtk_plotters
+ };
+ int x, y, width, height;
+
+ x = event->area.x;
+ y = event->area.y;
+ width = event->area.width;
+ height = event->area.height;
+
+ current_widget = widget;
+ current_cr = gdk_cairo_create(nsgtk_widget_get_window(widget));
+
+ tree_draw(tree, 0, 0, x, y, width, height, &ctx);
+
+ current_widget = NULL;
+ cairo_destroy(current_cr);
+
+ return FALSE;
+}
+
+#endif
+
+void nsgtk_tree_window_hide(GtkWidget *widget, gpointer g)
+{
+}
+
+static gboolean
+nsgtk_tree_window_button_press_event(GtkWidget *widget,
+ GdkEventButton *event, gpointer g)
+{
+ struct nsgtk_treeview *tw = g;
+ struct tree *tree = tw->tree;
+
+ gtk_im_context_reset(tw->input_method);
+ gtk_widget_grab_focus(GTK_WIDGET(tw->drawing_area));
+
+ tw->mouse_pressed = true;
+ tw->mouse_pressed_x = event->x;
+ tw->mouse_pressed_y = event->y;
+
+ if (event->type == GDK_2BUTTON_PRESS)
+ tw->mouse_state = BROWSER_MOUSE_DOUBLE_CLICK;
+
+ switch (event->button) {
+ case 1: tw->mouse_state |= BROWSER_MOUSE_PRESS_1; break;
+ case 2: tw->mouse_state |= BROWSER_MOUSE_PRESS_2; break;
+ }
+ /* Handle the modifiers too */
+ if (event->state & GDK_SHIFT_MASK)
+ tw->mouse_state |= BROWSER_MOUSE_MOD_1;
+ if (event->state & GDK_CONTROL_MASK)
+ tw->mouse_state |= BROWSER_MOUSE_MOD_2;
+ if (event->state & GDK_MOD1_MASK)
+ tw->mouse_state |= BROWSER_MOUSE_MOD_3;
+
+ /* Record where we pressed, for use when determining whether to start
+ * a drag in motion notify events. */
+ tw->last_x = event->x;
+ tw->last_y = event->y;
+
+ tree_mouse_action(tree, tw->mouse_state, event->x, event->y);
+
+ return TRUE;
+}
+
+static gboolean
+nsgtk_tree_window_button_release_event(GtkWidget *widget,
+ GdkEventButton *event, gpointer g)
+{
+ bool shift = event->state & GDK_SHIFT_MASK;
+ bool ctrl = event->state & GDK_CONTROL_MASK;
+ bool alt = event->state & GDK_MOD1_MASK;
+ struct nsgtk_treeview *tw = (struct nsgtk_treeview *) g;
+ struct tree *tree = tw->tree;
+
+ /* We consider only button 1 clicks as double clicks.
+ * If the mouse state is PRESS then we are waiting for a release to emit
+ * a click event, otherwise just reset the state to nothing*/
+ if (tw->mouse_state & BROWSER_MOUSE_DOUBLE_CLICK) {
+
+ if (tw->mouse_state & BROWSER_MOUSE_PRESS_1)
+ tw->mouse_state ^= BROWSER_MOUSE_PRESS_1 |
+ BROWSER_MOUSE_CLICK_1;
+ else if (tw->mouse_state & BROWSER_MOUSE_PRESS_2)
+ tw->mouse_state ^= (BROWSER_MOUSE_PRESS_2 |
+ BROWSER_MOUSE_CLICK_2 |
+ BROWSER_MOUSE_DOUBLE_CLICK);
+
+ } else if (tw->mouse_state & BROWSER_MOUSE_PRESS_1) {
+ tw->mouse_state ^= (BROWSER_MOUSE_PRESS_1 |
+ BROWSER_MOUSE_CLICK_1);
+ } else if (tw->mouse_state & BROWSER_MOUSE_PRESS_2) {
+ tw->mouse_state ^= (BROWSER_MOUSE_PRESS_2 |
+ BROWSER_MOUSE_CLICK_2);
+ } else if (tw->mouse_state & BROWSER_MOUSE_HOLDING_1) {
+ tw->mouse_state ^= (BROWSER_MOUSE_HOLDING_1 |
+ BROWSER_MOUSE_DRAG_ON);
+ } else if (tw->mouse_state & BROWSER_MOUSE_HOLDING_2) {
+ tw->mouse_state ^= (BROWSER_MOUSE_HOLDING_2 |
+ BROWSER_MOUSE_DRAG_ON);
+ }
+
+ /* Handle modifiers being removed */
+ if (tw->mouse_state & BROWSER_MOUSE_MOD_1 && !shift)
+ tw->mouse_state ^= BROWSER_MOUSE_MOD_1;
+ if (tw->mouse_state & BROWSER_MOUSE_MOD_2 && !ctrl)
+ tw->mouse_state ^= BROWSER_MOUSE_MOD_2;
+ if (tw->mouse_state & BROWSER_MOUSE_MOD_3 && !alt)
+ tw->mouse_state ^= BROWSER_MOUSE_MOD_3;
+
+
+ if (tw->mouse_state &
+ ~(BROWSER_MOUSE_MOD_1 |
+ BROWSER_MOUSE_MOD_2 |
+ BROWSER_MOUSE_MOD_3)) {
+ tree_mouse_action(tree, tw->mouse_state,
+ event->x, event->y);
+ } else {
+ tree_drag_end(tree, tw->mouse_state,
+ tw->mouse_pressed_x,
+ tw->mouse_pressed_y,
+ event->x, event->y);
+ }
+
+ tw->mouse_state = 0;
+ tw->mouse_pressed = false;
+
+ return TRUE;
+}
+
+static gboolean
+nsgtk_tree_window_motion_notify_event(GtkWidget *widget,
+ GdkEventMotion *event, gpointer g)
+{
+ bool shift = event->state & GDK_SHIFT_MASK;
+ bool ctrl = event->state & GDK_CONTROL_MASK;
+ bool alt = event->state & GDK_MOD1_MASK;
+ struct nsgtk_treeview *tw = (struct nsgtk_treeview *) g;
+ struct tree *tree = tw->tree;
+
+ if (tw->mouse_pressed == false)
+ return TRUE;
+
+ if ((fabs(event->x - tw->last_x) < 5.0) &&
+ (fabs(event->y - tw->last_y) < 5.0)) {
+ /* Mouse hasn't moved far enough from press coordinate
+ * for this to be considered a drag.
+ */
+ return FALSE;
+ } else {
+ /* This is a drag, ensure it's always treated as such,
+ * even if we drag back over the press location.
+ */
+ tw->last_x = INT_MIN;
+ tw->last_y = INT_MIN;
+ }
+
+ if (tw->mouse_state & BROWSER_MOUSE_PRESS_1) {
+ /* Start button 1 drag */
+ tree_mouse_action(tree, BROWSER_MOUSE_DRAG_1,
+ tw->mouse_pressed_x, tw->mouse_pressed_y);
+ /* Replace PRESS with HOLDING and declare drag in progress */
+ tw->mouse_state ^= (BROWSER_MOUSE_PRESS_1 |
+ BROWSER_MOUSE_HOLDING_1);
+ tw->mouse_state |= BROWSER_MOUSE_DRAG_ON;
+ return TRUE;
+ }
+ else if (tw->mouse_state & BROWSER_MOUSE_PRESS_2){
+ /* Start button 2s drag */
+ tree_mouse_action(tree, BROWSER_MOUSE_DRAG_2,
+ tw->mouse_pressed_x, tw->mouse_pressed_y);
+ /* Replace PRESS with HOLDING and declare drag in progress */
+ tw->mouse_state ^= (BROWSER_MOUSE_PRESS_2 |
+ BROWSER_MOUSE_HOLDING_2);
+ tw->mouse_state |= BROWSER_MOUSE_DRAG_ON;
+ return TRUE;
+ }
+
+ /* Handle modifiers being removed */
+ if (tw->mouse_state & BROWSER_MOUSE_MOD_1 && !shift)
+ tw->mouse_state ^= BROWSER_MOUSE_MOD_1;
+ if (tw->mouse_state & BROWSER_MOUSE_MOD_2 && !ctrl)
+ tw->mouse_state ^= BROWSER_MOUSE_MOD_2;
+ if (tw->mouse_state & BROWSER_MOUSE_MOD_3 && !alt)
+ tw->mouse_state ^= BROWSER_MOUSE_MOD_3;
+
+ if (tw->mouse_state & (BROWSER_MOUSE_HOLDING_1 |
+ BROWSER_MOUSE_HOLDING_2))
+ tree_mouse_action(tree, tw->mouse_state, event->x,
+ event->y);
+
+ return TRUE;
+}
+
+
+static gboolean
+nsgtk_tree_window_keypress_event(GtkWidget *widget, GdkEventKey *event,
+ gpointer g)
+{
+ struct nsgtk_treeview *tw = (struct nsgtk_treeview *) g;
+ struct tree *tree = tw->tree;
+ uint32_t nskey;
+ double value;
+ GtkAdjustment *vscroll;
+ GtkAdjustment *hscroll;
+ GtkAdjustment *scroll = NULL;
+ gdouble hpage, vpage;
+
+ if (gtk_im_context_filter_keypress(tw->input_method, event))
+ return TRUE;
+
+ nskey = gtk_gui_gdkkey_to_nskey(event);
+
+ if (tree_keypress(tree, nskey) == true)
+ return TRUE;
+
+ vscroll = gtk_scrolled_window_get_vadjustment(tw->scrolled);
+ hscroll = gtk_scrolled_window_get_hadjustment(tw->scrolled);
+ g_object_get(vscroll, "page-size", &vpage, NULL);
+ g_object_get(hscroll, "page-size", &hpage, NULL);
+
+ switch (event->keyval) {
+ case GDK_KEY(Home):
+ case GDK_KEY(KP_Home):
+ scroll = vscroll;
+ value = nsgtk_adjustment_get_lower(scroll);
+ break;
+
+ case GDK_KEY(End):
+ case GDK_KEY(KP_End):
+ scroll = vscroll;
+ value = nsgtk_adjustment_get_upper(scroll) - vpage;
+ if (value < nsgtk_adjustment_get_lower(scroll))
+ value = nsgtk_adjustment_get_lower(scroll);
+ break;
+
+ case GDK_KEY(Left):
+ case GDK_KEY(KP_Left):
+ scroll = hscroll;
+ value = gtk_adjustment_get_value(scroll) -
+ nsgtk_adjustment_get_step_increment(scroll);
+ if (value < nsgtk_adjustment_get_lower(scroll))
+ value = nsgtk_adjustment_get_lower(scroll);
+ break;
+
+ case GDK_KEY(Up):
+ case GDK_KEY(KP_Up):
+ scroll = vscroll;
+ value = gtk_adjustment_get_value(scroll) -
+ nsgtk_adjustment_get_step_increment(scroll);
+ if (value < nsgtk_adjustment_get_lower(scroll))
+ value = nsgtk_adjustment_get_lower(scroll);
+ break;
+
+ case GDK_KEY(Right):
+ case GDK_KEY(KP_Right):
+ scroll = hscroll;
+ value = gtk_adjustment_get_value(scroll) +
+ nsgtk_adjustment_get_step_increment(scroll);
+ if (value > nsgtk_adjustment_get_upper(scroll) - hpage)
+ value = nsgtk_adjustment_get_upper(scroll) - hpage;
+ break;
+
+ case GDK_KEY(Down):
+ case GDK_KEY(KP_Down):
+ scroll = vscroll;
+ value = gtk_adjustment_get_value(scroll) +
+ nsgtk_adjustment_get_step_increment(scroll);
+ if (value > nsgtk_adjustment_get_upper(scroll) - vpage)
+ value = nsgtk_adjustment_get_upper(scroll) - vpage;
+ break;
+
+ case GDK_KEY(Page_Up):
+ case GDK_KEY(KP_Page_Up):
+ scroll = vscroll;
+ value = gtk_adjustment_get_value(scroll) -
+ nsgtk_adjustment_get_page_increment(scroll);
+
+ if (value < nsgtk_adjustment_get_lower(scroll))
+ value = nsgtk_adjustment_get_lower(scroll);
+
+ break;
+
+ case GDK_KEY(Page_Down):
+ case GDK_KEY(KP_Page_Down):
+ scroll = vscroll;
+ value = gtk_adjustment_get_value(scroll) +
+ nsgtk_adjustment_get_page_increment(scroll);
+
+ if (value > nsgtk_adjustment_get_upper(scroll) - vpage)
+ value = nsgtk_adjustment_get_upper(scroll) - vpage;
+ break;
+
+ default:
+ break;
+ }
+
+ if (scroll != NULL)
+ gtk_adjustment_set_value(scroll, value);
+
+ return TRUE;
+}
+
+static gboolean
+nsgtk_tree_window_keyrelease_event(GtkWidget *widget, GdkEventKey *event,
+ gpointer g)
+{
+ struct nsgtk_treeview *tw = (struct nsgtk_treeview *) g;
+
+ return gtk_im_context_filter_keypress(tw->input_method, event);
+}
+
+static void
+nsgtk_tree_window_input_method_commit(GtkIMContext *ctx,
+ const gchar *str, gpointer data)
+{
+ struct nsgtk_treeview *tw = (struct nsgtk_treeview *) data;
+ size_t len = strlen(str), offset = 0;
+
+ while (offset < len) {
+ uint32_t nskey = utf8_to_ucs4(str + offset, len - offset);
+
+ tree_keypress(tw->tree, nskey);
+
+ offset = utf8_next(str, len, offset);
+ }
+}
+
+
+static const struct treeview_table nsgtk_tree_callbacks = {
+ .redraw_request = nsgtk_tree_redraw_request,
+ .resized = nsgtk_tree_resized,
+ .scroll_visible = nsgtk_tree_scroll_visible,
+ .get_window_dimensions = nsgtk_tree_get_window_dimensions
+};
+
+struct nsgtk_treeview *nsgtk_treeview_create(unsigned int flags,
+ GtkWindow *window, GtkScrolledWindow *scrolled,
+ GtkDrawingArea *drawing_area)
+{
+ struct nsgtk_treeview *tv;
+
+ assert(drawing_area != NULL);
+
+ tv = malloc(sizeof(struct nsgtk_treeview));
+ if (tv == NULL) {
+ LOG("malloc failed");
+ nsgtk_warning("NoMemory", 0);
+ return NULL;
+ }
+
+ tv->window = window;
+ tv->scrolled = scrolled;
+ tv->drawing_area = drawing_area;
+ tv->input_method = gtk_im_multicontext_new();
+ tv->tree = tree_create(flags, &nsgtk_tree_callbacks, tv);
+ tv->mouse_state = 0;
+ tv->mouse_pressed = false;
+
+ nsgtk_widget_override_background_color(GTK_WIDGET(drawing_area),
+ GTK_STATE_NORMAL,
+ 0, 0xffff, 0xffff, 0xffff);
+
+ nsgtk_connect_draw_event(GTK_WIDGET(drawing_area), G_CALLBACK(nsgtk_tree_window_draw_event), tv->tree);
+
+#define CONNECT(obj, sig, callback, ptr) \
+ g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr))
+ CONNECT(drawing_area, "button-press-event",
+ nsgtk_tree_window_button_press_event,
+ tv);
+ CONNECT(drawing_area, "button-release-event",
+ nsgtk_tree_window_button_release_event,
+ tv);
+ CONNECT(drawing_area, "motion-notify-event",
+ nsgtk_tree_window_motion_notify_event,
+ tv);
+ CONNECT(drawing_area, "key-press-event",
+ nsgtk_tree_window_keypress_event,
+ tv);
+ CONNECT(drawing_area, "key-release-event",
+ nsgtk_tree_window_keyrelease_event,
+ tv);
+
+
+ /* input method */
+ gtk_im_context_set_client_window(tv->input_method,
+ nsgtk_widget_get_window(GTK_WIDGET(tv->window)));
+ gtk_im_context_set_use_preedit(tv->input_method, FALSE);
+ /* input method signals */
+ CONNECT(tv->input_method, "commit",
+ nsgtk_tree_window_input_method_commit,
+ tv);
+
+ return tv;
+}
diff --git a/frontends/gtk/treeview.h b/frontends/gtk/treeview.h
new file mode 100644
index 000000000..ad8180f33
--- /dev/null
+++ b/frontends/gtk/treeview.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
+ * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Generic tree handling.
+ */
+
+#ifndef __NSGTK_TREEVIEW_H__
+#define __NSGTK_TREEVIEW_H__
+
+struct nsgtk_treeview;
+
+struct nsgtk_treeview *nsgtk_treeview_create(unsigned int flags,
+ GtkWindow *window, GtkScrolledWindow *scrolled,
+ GtkDrawingArea *drawing_area);
+void nsgtk_treeview_destroy(struct nsgtk_treeview *tv);
+
+struct tree *nsgtk_treeview_get_tree(struct nsgtk_treeview *tv);
+
+void nsgtk_tree_window_hide(GtkWidget *widget, gpointer g);
+
+#endif /*__NSGTK_TREEVIEW_H__*/
diff --git a/frontends/gtk/viewdata.c b/frontends/gtk/viewdata.c
new file mode 100644
index 000000000..55b25467e
--- /dev/null
+++ b/frontends/gtk/viewdata.c
@@ -0,0 +1,990 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * generic data viewer implementation.
+ *
+ * This viewer can be used for utf-8 encoded chunk of data. Thie data
+ * might be page source or the debugging of dom or box trees. It will
+ * show the data in a tab, window or editor as per user configuration.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#define _WITH_GETLINE /* necessary for FreeBSD */
+#include <stdio.h>
+#include <unistd.h>
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "utils/utf8.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "utils/file.h"
+#include "utils/filepath.h"
+
+#include "desktop/browser.h"
+#include "content/hlcache.h"
+#include "content/content.h"
+
+#include "gtk/warn.h"
+#include "gtk/about.h"
+#include "gtk/fetch.h"
+#include "gtk/compat.h"
+#include "gtk/resources.h"
+#include "gtk/viewdata.h"
+
+struct nsgtk_viewdata_ctx {
+ char *data;
+ size_t data_len;
+ char *filename;
+
+ GtkBuilder *builder; /**< The gtk builder that built the widgets. */
+ GtkWindow *window; /**< handle to gtk window (builder holds reference) */
+ GtkTextView *gv; /**< handle to gtk text view (builder holds reference) */
+
+ struct nsgtk_viewdata_ctx *next;
+ struct nsgtk_viewdata_ctx *prev;
+};
+
+struct menu_events {
+ const char *widget;
+ GCallback handler;
+};
+
+static struct nsgtk_viewdata_ctx *nsgtk_viewdata_list = NULL;
+static char viewdata_zoomlevel = 10;
+
+#define MENUEVENT(x) { #x, G_CALLBACK(nsgtk_on_##x##_activate) }
+#define MENUPROTO(x) static gboolean nsgtk_on_##x##_activate( \
+ GtkMenuItem *widget, gpointer g)
+
+MENUPROTO(viewdata_save_as);
+MENUPROTO(viewdata_print);
+MENUPROTO(viewdata_close);
+MENUPROTO(viewdata_select_all);
+MENUPROTO(viewdata_cut);
+MENUPROTO(viewdata_copy);
+MENUPROTO(viewdata_paste);
+MENUPROTO(viewdata_delete);
+MENUPROTO(viewdata_zoom_in);
+MENUPROTO(viewdata_zoom_out);
+MENUPROTO(viewdata_zoom_normal);
+MENUPROTO(viewdata_about);
+
+static struct menu_events viewdata_menu_events[] = {
+ MENUEVENT(viewdata_save_as),
+ MENUEVENT(viewdata_print),
+ MENUEVENT(viewdata_close),
+ MENUEVENT(viewdata_select_all),
+ MENUEVENT(viewdata_cut),
+ MENUEVENT(viewdata_copy),
+ MENUEVENT(viewdata_paste),
+ MENUEVENT(viewdata_delete),
+ MENUEVENT(viewdata_zoom_in),
+ MENUEVENT(viewdata_zoom_out),
+ MENUEVENT(viewdata_zoom_normal),
+ MENUEVENT(viewdata_about),
+ {NULL, NULL}
+};
+
+static void nsgtk_attach_viewdata_menu_handlers(GtkBuilder *xml, gpointer g)
+{
+ struct menu_events *event = viewdata_menu_events;
+
+ while (event->widget != NULL)
+ {
+ GtkWidget *w = GTK_WIDGET(gtk_builder_get_object(xml, event->widget));
+ g_signal_connect(G_OBJECT(w), "activate", event->handler, g);
+ event++;
+ }
+}
+
+static gboolean nsgtk_viewdata_destroy_event(GtkBuilder *window, gpointer g)
+{
+ struct nsgtk_viewdata_ctx *vdctx = (struct nsgtk_viewdata_ctx *)g;
+
+ if (vdctx->next != NULL) {
+ vdctx->next->prev = vdctx->prev;
+ }
+
+ if (vdctx->prev != NULL) {
+ vdctx->prev->next = vdctx->next;
+ } else {
+ nsgtk_viewdata_list = vdctx->next;
+ }
+
+ /* release the data */
+ free(vdctx->data);
+
+ /* free the builder */
+ g_object_unref(G_OBJECT(vdctx->builder));
+
+ /* free the context structure */
+ free(vdctx);
+
+ return FALSE;
+}
+
+static gboolean nsgtk_viewdata_delete_event(GtkWindow * window, gpointer g)
+{
+ return FALSE;
+}
+
+
+
+static void nsgtk_viewdata_file_save(GtkWindow *parent, const char *filename,
+ const char *data, size_t data_size)
+{
+ FILE *f;
+ GtkWidget *notif;
+ GtkWidget *label;
+
+ f = fopen(filename, "w+");
+ if (f != NULL) {
+ fwrite(data, data_size, 1, f);
+ fclose(f);
+ return;
+ }
+
+ /* inform user of faliure */
+ notif = gtk_dialog_new_with_buttons(messages_get("gtkSaveFailedTitle"),
+ parent,
+ GTK_DIALOG_MODAL,
+ NSGTK_STOCK_OK,
+ GTK_RESPONSE_NONE,
+ NULL);
+
+ g_signal_connect_swapped(notif, "response",
+ G_CALLBACK(gtk_widget_destroy), notif);
+
+ label = gtk_label_new(messages_get("gtkSaveFailed"));
+ gtk_container_add(GTK_CONTAINER(nsgtk_dialog_get_content_area(GTK_DIALOG(notif))), label);
+ gtk_widget_show_all(notif);
+
+}
+
+
+gboolean nsgtk_on_viewdata_save_as_activate(GtkMenuItem *widget, gpointer g)
+{
+ struct nsgtk_viewdata_ctx *nsg = (struct nsgtk_viewdata_ctx *) g;
+ GtkWidget *fc;
+
+ fc = gtk_file_chooser_dialog_new(messages_get("gtkSaveFile"),
+ nsg->window,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ NSGTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ NSGTK_STOCK_SAVE,
+ GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fc), nsg->filename);
+
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(fc),
+ TRUE);
+
+ if (gtk_dialog_run(GTK_DIALOG(fc)) == GTK_RESPONSE_ACCEPT) {
+ char *filename;
+ filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
+ nsgtk_viewdata_file_save(nsg->window, filename, nsg->data, nsg->data_len);
+ g_free(filename);
+ }
+
+ gtk_widget_destroy(fc);
+
+ return TRUE;
+}
+
+
+gboolean nsgtk_on_viewdata_print_activate( GtkMenuItem *widget, gpointer g)
+{
+ /* correct printing */
+
+ return TRUE;
+}
+
+gboolean nsgtk_on_viewdata_close_activate( GtkMenuItem *widget, gpointer g)
+{
+ struct nsgtk_viewdata_ctx *nsg = (struct nsgtk_viewdata_ctx *) g;
+
+ gtk_widget_destroy(GTK_WIDGET(nsg->window));
+
+ return TRUE;
+}
+
+
+
+gboolean nsgtk_on_viewdata_select_all_activate (GtkMenuItem *widget, gpointer g)
+{
+ struct nsgtk_viewdata_ctx *nsg = (struct nsgtk_viewdata_ctx *) g;
+ GtkTextBuffer *buf = gtk_text_view_get_buffer(nsg->gv);
+ GtkTextIter start, end;
+
+ gtk_text_buffer_get_bounds(buf, &start, &end);
+
+ gtk_text_buffer_select_range(buf, &start, &end);
+
+ return TRUE;
+}
+
+gboolean nsgtk_on_viewdata_cut_activate(GtkMenuItem *widget, gpointer g)
+{
+ return TRUE;
+}
+
+gboolean nsgtk_on_viewdata_copy_activate(GtkMenuItem *widget, gpointer g)
+{
+ struct nsgtk_viewdata_ctx *nsg = (struct nsgtk_viewdata_ctx *) g;
+ GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(nsg->gv));
+
+ gtk_text_buffer_copy_clipboard(buf,
+ gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
+
+ return TRUE;
+}
+
+gboolean nsgtk_on_viewdata_paste_activate(GtkMenuItem *widget, gpointer g)
+{
+ return TRUE;
+}
+
+gboolean nsgtk_on_viewdata_delete_activate(GtkMenuItem *widget, gpointer g)
+{
+ return TRUE;
+}
+
+static void nsgtk_viewdata_update_zoomlevel(gpointer g)
+{
+ struct nsgtk_viewdata_ctx *nsg;
+ GtkTextBuffer *buf;
+ GtkTextTagTable *tab;
+ GtkTextTag *tag;
+
+ nsg = nsgtk_viewdata_list;
+ while (nsg) {
+ if (nsg->gv) {
+ buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(nsg->gv));
+
+ tab = gtk_text_buffer_get_tag_table(
+ GTK_TEXT_BUFFER(buf));
+
+ tag = gtk_text_tag_table_lookup(tab, "zoomlevel");
+ if (!tag) {
+ tag = gtk_text_tag_new("zoomlevel");
+ gtk_text_tag_table_add(tab, GTK_TEXT_TAG(tag));
+ }
+
+ gdouble fscale = ((gdouble) viewdata_zoomlevel) / 10;
+
+ g_object_set(GTK_TEXT_TAG(tag), "scale", fscale, NULL);
+
+ GtkTextIter start, end;
+
+ gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(buf),
+ &start, &end);
+ gtk_text_buffer_remove_all_tags(GTK_TEXT_BUFFER(buf),
+ &start, &end);
+ gtk_text_buffer_apply_tag(GTK_TEXT_BUFFER(buf),
+ GTK_TEXT_TAG(tag), &start, &end);
+ }
+ nsg = nsg->next;
+ }
+}
+
+gboolean nsgtk_on_viewdata_zoom_in_activate(GtkMenuItem *widget, gpointer g)
+{
+ viewdata_zoomlevel++;
+ nsgtk_viewdata_update_zoomlevel(g);
+
+ return TRUE;
+}
+
+gboolean nsgtk_on_viewdata_zoom_out_activate(GtkMenuItem *widget, gpointer g)
+{
+ if (viewdata_zoomlevel > 1) {
+ viewdata_zoomlevel--;
+ nsgtk_viewdata_update_zoomlevel(g);
+ }
+
+ return TRUE;
+}
+
+
+gboolean nsgtk_on_viewdata_zoom_normal_activate(GtkMenuItem *widget, gpointer g)
+{
+ viewdata_zoomlevel = 10;
+ nsgtk_viewdata_update_zoomlevel(g);
+
+ return TRUE;
+}
+
+gboolean nsgtk_on_viewdata_about_activate(GtkMenuItem *widget, gpointer g)
+{
+ struct nsgtk_viewdata_ctx *nsg = (struct nsgtk_viewdata_ctx *) g;
+
+ nsgtk_about_dialog_init(nsg->window);
+
+ return TRUE;
+}
+
+/**
+ * View the data in a gtk text window.
+ */
+static nserror
+window_init(const char *title,
+ const char *filename,
+ char *ndata,
+ size_t ndata_len)
+{
+ GtkWindow *window;
+ GtkWidget *cutbutton;
+ GtkWidget *pastebutton;
+ GtkWidget *deletebutton;
+ GtkWidget *printbutton;
+ GtkTextView *dataview;
+ PangoFontDescription *fontdesc;
+ GtkTextBuffer *tb;
+ struct nsgtk_viewdata_ctx *newctx;
+ nserror res;
+
+ newctx = malloc(sizeof(struct nsgtk_viewdata_ctx));
+ if (newctx == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ res = nsgtk_builder_new_from_resname("viewdata", &newctx->builder);
+ if (res != NSERROR_OK) {
+ LOG("Viewdata UI builder init failed");
+ free(newctx);
+ return res;
+ }
+
+ gtk_builder_connect_signals(newctx->builder, NULL);
+
+ window = GTK_WINDOW(gtk_builder_get_object(newctx->builder,
+ "ViewDataWindow"));
+ if (window == NULL) {
+ LOG("Unable to find window in builder ");
+
+ /* free the builder */
+ g_object_unref(G_OBJECT(newctx->builder));
+
+ /* free the context structure */
+ free(newctx);
+
+ return NSERROR_INIT_FAILED;
+ }
+
+ cutbutton = GTK_WIDGET(gtk_builder_get_object(newctx->builder, "viewdata_cut"));
+ pastebutton = GTK_WIDGET(gtk_builder_get_object(newctx->builder, "viewdata_paste"));
+ deletebutton = GTK_WIDGET(gtk_builder_get_object(newctx->builder, "viewdata_delete"));
+ printbutton = GTK_WIDGET(gtk_builder_get_object(newctx->builder, "viewdata_print"));
+ gtk_widget_set_sensitive(cutbutton, FALSE);
+ gtk_widget_set_sensitive(pastebutton, FALSE);
+ gtk_widget_set_sensitive(deletebutton, FALSE);
+ /* for now */
+ gtk_widget_set_sensitive(printbutton, FALSE);
+
+
+ newctx->filename = strdup(filename);
+
+ newctx->data = ndata;
+ newctx->data_len = ndata_len;
+
+ newctx->window = window;
+
+ newctx->next = nsgtk_viewdata_list;
+ newctx->prev = NULL;
+ if (nsgtk_viewdata_list != NULL) {
+ nsgtk_viewdata_list->prev = newctx;
+ }
+ nsgtk_viewdata_list = newctx;
+
+ nsgtk_attach_viewdata_menu_handlers(newctx->builder, newctx);
+
+ gtk_window_set_title(window, title);
+
+ g_signal_connect(G_OBJECT(window), "destroy",
+ G_CALLBACK(nsgtk_viewdata_destroy_event),
+ newctx);
+ g_signal_connect(G_OBJECT(window), "delete-event",
+ G_CALLBACK(nsgtk_viewdata_delete_event),
+ newctx);
+
+ dataview = GTK_TEXT_VIEW(gtk_builder_get_object(newctx->builder,
+ "viewdata_view"));
+
+ fontdesc = pango_font_description_from_string("Monospace 8");
+
+ newctx->gv = dataview;
+ nsgtk_widget_modify_font(GTK_WIDGET(dataview), fontdesc);
+
+ tb = gtk_text_view_get_buffer(dataview);
+ gtk_text_buffer_set_text(tb, newctx->data, -1);
+
+ gtk_widget_show(GTK_WIDGET(window));
+
+ return NSERROR_OK;
+}
+
+/**
+ * open a window to dispaly an existing file.
+ */
+static nserror
+window_init_fname(const char *title,
+ const char *leafname,
+ const char *filename)
+{
+ nserror ret;
+ FILE *f;
+ char *ndata;
+ long tell_len;
+ size_t ndata_len;
+
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ return NSERROR_NOT_FOUND;
+ }
+ if (fseek(f, 0, SEEK_END) != 0) {
+ fclose(f);
+ return NSERROR_BAD_SIZE;
+ }
+
+ tell_len = ftell(f);
+ if (tell_len == -1) {
+ fclose(f);
+ return NSERROR_BAD_SIZE;
+ }
+
+ if (fseek(f, 0, SEEK_SET) != 0) {
+ fclose(f);
+ return NSERROR_BAD_SIZE;
+ }
+
+ ndata = malloc(tell_len);
+
+ ndata_len = fread(ndata, 1, tell_len, f);
+
+ fclose(f);
+
+ /* window init takes ownership of the ndata if there is no error */
+ ret = window_init(title, leafname, ndata, ndata_len);
+ if (ret != NSERROR_OK) {
+ free(ndata);
+ }
+
+ return ret;
+}
+
+/**
+ * open a new tab from an existing file.
+ */
+static nserror
+tab_init_fname(const char *title,
+ const char *leafname,
+ const char *fname)
+{
+ nsurl *url;
+ nserror ret;
+
+ /* Open tab on temporary file */
+ ret = netsurf_path_to_nsurl(fname, &url);
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+
+ /* open tab on temportary file */
+ ret = browser_window_create(BW_CREATE_TAB | BW_CREATE_HISTORY, url, NULL, NULL, NULL);
+ nsurl_unref(url);
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+
+ return NSERROR_OK;
+}
+
+/**
+ * create a new tab from data.
+ */
+static nserror
+tab_init(const char *title,
+ const char *leafname,
+ char *ndata,
+ size_t ndata_len)
+{
+ nserror ret;
+ gchar *fname;
+ gint handle;
+ FILE *f;
+
+ handle = g_file_open_tmp("nsgtkdataXXXXXX", &fname, NULL);
+ if ((handle == -1) || (fname == NULL)) {
+ return NSERROR_SAVE_FAILED;
+ }
+ close(handle); /* in case it was binary mode */
+
+ /* save data to temporary file */
+ f = fopen(fname, "w");
+ if (f == NULL) {
+ nsgtk_warning(messages_get("gtkSourceTabError"), 0);
+ g_free(fname);
+ return NSERROR_SAVE_FAILED;
+ }
+ fprintf(f, "%s", ndata);
+ fclose(f);
+
+ ret = tab_init_fname(title, leafname, fname);
+ if (ret == NSERROR_OK) {
+ free(ndata);
+ }
+
+ g_free(fname);
+
+ return ret;
+}
+
+
+/**
+ * Build string vector of search path.
+ *
+ * ${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}
+ *
+ * $XDG_DATA_HOME if empty use $HOME/.local/share
+ *
+ * XDG_DATA_DIRS if empty use /usr/local/share/:/usr/share/
+ *
+ * \return string vector of search pathnames or NULL on error.
+ */
+static char** xdg_data_strvec(void)
+{
+ const char *xdg_data_dirs;
+ const char *xdg_data_home;
+ const char *home_dir;
+ char *xdg_data_path;
+ int xdg_data_size;
+ char **svec;
+
+ xdg_data_dirs = getenv("XDG_DATA_DIRS");
+ if ((xdg_data_dirs == NULL) ||
+ (*xdg_data_dirs == 0) ||
+ (strlen(xdg_data_dirs) > 4096)) {
+ xdg_data_dirs = "/usr/local/share/:/usr/share/";
+ }
+
+ xdg_data_home = getenv("XDG_DATA_HOME");
+ if ((xdg_data_home == NULL) ||
+ (*xdg_data_home == 0) ||
+ (strlen(xdg_data_home) > 4096)) {
+ /* $XDG_DATA_HOME is empty use $HOME/.local/share */
+
+ home_dir = getenv("HOME");
+ if ((home_dir == NULL) ||
+ (*home_dir == 0) ||
+ (strlen(home_dir) > 4096)) {
+ xdg_data_path = strdup(xdg_data_dirs);
+ } else {
+ xdg_data_size = strlen(home_dir) +
+ SLEN("/.local/share:") +
+ strlen(xdg_data_dirs) + 1;
+ xdg_data_path = malloc(xdg_data_size);
+ snprintf(xdg_data_path, xdg_data_size ,
+ "%s/.local/share/:%s",
+ home_dir, xdg_data_dirs);
+ }
+ } else {
+ xdg_data_size = strlen(xdg_data_home) +
+ strlen(xdg_data_dirs) + 2;
+ xdg_data_path = malloc(xdg_data_size);
+ snprintf(xdg_data_path, xdg_data_size , "%s:%s",
+ xdg_data_home, xdg_data_dirs);
+ }
+
+ LOG("%s", xdg_data_path);
+
+ svec = filepath_path_to_strvec(xdg_data_path);
+ free(xdg_data_path);
+
+ return svec;
+}
+
+/**
+ * Search application defaults file for matching mime type.
+ *
+ * create filename form path and applications/defaults.list
+ *
+ * look for [Default Applications]
+ * search lines looking like mime/type=Desktop
+ *
+ * \param path The base path.
+ * \param mimetype The mimetype to search for.
+ * \return The desktop file associated with the mime type or NULL if not found.
+ */
+static char *xdg_get_default_app(const char *path, const char *mimetype)
+{
+ FILE *fp;
+ char *line = NULL;
+ size_t len = 0;
+ ssize_t rd;
+ int fname_len;
+ char *fname;
+ int mimetype_len;
+ char *ret = NULL;
+
+ fname_len = strlen(path) + SLEN("/applications/defaults.list") + 1;
+ fname = malloc(fname_len);
+ snprintf(fname, fname_len, "%s/applications/defaults.list", path);
+
+ LOG("Checking %s", fname);
+
+ fp = fopen(fname, "r");
+ free(fname);
+ if (fp == NULL) {
+ return NULL;
+ }
+
+ mimetype_len = strlen(mimetype);
+ while ((rd = getline(&line, &len, fp)) != -1) {
+ /* line includes line endings if present, remove them */
+ while ((line[rd - 1] == '\n') || (line[rd - 1] == '\r')) {
+ rd--;
+ }
+ line[rd] = 0;
+
+ /* look for mimetype */
+ if ((rd > mimetype_len) &&
+ (line[mimetype_len] == '=') &&
+ (strncmp(line, mimetype, mimetype_len) == 0)) {
+
+ ret = strdup(line + mimetype_len + 1);
+
+ LOG("Found line match for %s length %zu\n", mimetype, rd);
+ LOG("Result %s", ret);
+
+ break;
+ }
+ }
+
+ free(line);
+ fclose(fp);
+
+ return ret;
+}
+
+/**
+ * Search desktop file for an Exec line.
+ *
+ * search path is combined with applications/application.desktop to
+ * create a filename.
+ *
+ * Desktop file format http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html
+ *
+ * \todo The parsing of the desktop file is badly incomplete and needs
+ * improving. For example the handling of the = delimiter is wrong and
+ * selection from the "Desktop Entry" group is completely absent.
+ *
+ */
+static char *xdg_get_exec_cmd(const char *path, const char *desktop)
+{
+ FILE *fp;
+ char *line = NULL;
+ size_t len = 0;
+ ssize_t rd;
+ int fname_len;
+ char *fname;
+ char *ret = NULL;
+
+ fname_len = strlen(path) + SLEN("/applications/") + strlen(desktop) + 1;
+ fname = malloc(fname_len);
+ snprintf(fname, fname_len, "%s/applications/%s", path, desktop);
+
+ LOG("Checking %s", fname);
+
+ fp = fopen(fname, "r");
+ free(fname);
+ if (fp == NULL) {
+ return NULL;
+ }
+
+ while ((rd = getline(&line, &len, fp)) != -1) {
+ /* line includes line endings if present, remove them */
+ while ((line[rd - 1] == '\n') || (line[rd - 1] == '\r')) {
+ rd--;
+ }
+ line[rd] = 0;
+
+ /* look for mimetype */
+ if ((rd > (ssize_t)SLEN("Exec=")) &&
+ (strncmp(line, "Exec=", SLEN("Exec=")) == 0)) {
+
+ ret = strdup(line + SLEN("Exec="));
+
+ LOG("Found Exec length %zu", rd);
+ LOG("Result %s", ret);
+
+ break;
+ }
+ }
+
+ free(line);
+ fclose(fp);
+
+ return ret;
+}
+
+static char *exec_arg(const char *arg, int len, const char *fname)
+{
+ char *res = NULL;
+
+ if (*arg == '%') {
+ arg++;
+ if ((*arg == 'f') || (*arg == 'F') ||
+ (*arg == 'u') || (*arg == 'U')) {
+ res = strdup(fname);
+ }
+ } else {
+ res = calloc(1, len + 1);
+ if (res != NULL) {
+ memcpy(res, arg, len);
+ }
+ }
+
+ return res;
+}
+
+/**
+ * Build vector for executing app.
+ */
+static char **build_exec_argv(const char *fname, const char *exec_cmd)
+{
+ char **argv;
+ const char *start; /* current arguments start */
+ const char *cur; /* current ptr within exec cmd */
+ int aidx = 0; /* argv index */
+
+ argv = calloc(10, sizeof(char *));
+ if (argv == NULL) {
+ return NULL;
+ }
+
+ cur = exec_cmd;
+ while (*cur != 0) {
+ /* skip whitespace */
+ while ((*cur != 0) && (*cur == ' ')) {
+ cur++;
+ }
+ if (*cur == 0) {
+ break;
+ }
+ start = cur;
+
+ /* find end of element */
+ while ((*cur != 0) && (*cur != ' ')) {
+ cur++;
+ }
+
+ argv[aidx] = exec_arg(start, cur - start, fname);
+ if (argv[aidx] != NULL) {
+ LOG("adding \"%s\"", argv[aidx]);
+ aidx++;
+ }
+ }
+
+ /* if no arguments were found there was nothing to execute */
+ if (aidx == 0) {
+ free(argv);
+ return NULL;
+ }
+
+ return argv;
+}
+
+
+/**
+ * open an editor from an existing file.
+ */
+static nserror
+editor_init_fname(const char *title,
+ const char *leafname,
+ const char *fname)
+{
+ char **xdg_data_vec;
+ int veci;
+ /* desktop file of default app for mimetype */
+ char *def_app_desktop = NULL;
+ char *exec_cmd = NULL;
+ char **argv;
+
+ /* build string vector of search path */
+ xdg_data_vec = xdg_data_strvec();
+
+ /* find user configured app for opening text/plain */
+ veci = 0;
+ while (xdg_data_vec[veci] != NULL) {
+ def_app_desktop = xdg_get_default_app(xdg_data_vec[veci],
+ "text/plain");
+ if (def_app_desktop != NULL) {
+ break;
+ }
+ veci++;
+ }
+
+ if (def_app_desktop == NULL) {
+ /* no default app */
+ filepath_free_strvec(xdg_data_vec);
+ return NSERROR_NOT_FOUND;
+ }
+
+ /* find app to execute */
+ veci = 0;
+ while (xdg_data_vec[veci] != NULL) {
+ exec_cmd = xdg_get_exec_cmd(xdg_data_vec[veci], def_app_desktop);
+ if (exec_cmd != NULL) {
+ break;
+ }
+ veci++;
+ }
+ free(def_app_desktop);
+ filepath_free_strvec(xdg_data_vec);
+
+ if (exec_cmd == NULL) {
+ /* no exec entry */
+ return NSERROR_NOT_FOUND;
+ }
+
+ /* build exec vector */
+ argv = build_exec_argv(fname, exec_cmd);
+ free(exec_cmd);
+
+ /* execute target app on saved data */
+ if (g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
+ NULL, NULL) != TRUE) {
+ return NSERROR_NOT_FOUND;
+ }
+ filepath_free_strvec(argv);
+
+ return NSERROR_OK;
+}
+
+/**
+ * open an editor with data.
+ */
+static nserror
+editor_init(const char *title,
+ const char *leafname,
+ char *ndata,
+ size_t ndata_len)
+{
+
+ nserror ret;
+ gchar *fname;
+ gint handle;
+ FILE *f;
+
+ handle = g_file_open_tmp("nsgtkdataXXXXXX", &fname, NULL);
+ if ((handle == -1) || (fname == NULL)) {
+ return NSERROR_SAVE_FAILED;
+ }
+ close(handle); /* in case it was binary mode */
+
+ /* save data to temporary file */
+ f = fopen(fname, "w");
+ if (f == NULL) {
+ nsgtk_warning(messages_get("gtkSourceTabError"), 0);
+ g_free(fname);
+ return NSERROR_SAVE_FAILED;
+ }
+ fprintf(f, "%s", ndata);
+ fclose(f);
+
+ ret = editor_init_fname(title, leafname, fname);
+ if (ret == NSERROR_OK) {
+ free(ndata);
+ }
+
+ g_free(fname);
+
+ return ret;
+}
+
+/* exported interface documented in gtk/viewdata.h */
+nserror
+nsgtk_viewdata(const char *title,
+ const char *filename,
+ char *ndata,
+ size_t ndata_len)
+{
+ nserror ret;
+
+ switch (nsoption_int(developer_view)) {
+ case 0:
+ ret = window_init(title, filename, ndata, ndata_len);
+ break;
+
+ case 1:
+ ret = tab_init(title, filename, ndata, ndata_len);
+ break;
+
+ case 2:
+ ret = editor_init(title, filename, ndata, ndata_len);
+ break;
+
+ default:
+ ret = NSERROR_BAD_PARAMETER;
+ break;
+ }
+ if (ret != NSERROR_OK) {
+ /* release the data */
+ free(ndata);
+ }
+
+
+ return ret;
+}
+
+/* exported interface documented in gtk/viewdata.h */
+nserror
+nsgtk_viewfile(const char *title,
+ const char *leafname,
+ const char *filename)
+{
+ nserror ret;
+
+ switch (nsoption_int(developer_view)) {
+ case 0:
+ ret = window_init_fname(title, leafname, filename);
+ break;
+
+ case 1:
+ ret = tab_init_fname(title, leafname, filename);
+ break;
+
+ case 2:
+ ret = editor_init_fname(title, leafname, filename);
+ break;
+
+ default:
+ ret = NSERROR_BAD_PARAMETER;
+ break;
+ }
+
+ return ret;
+}
diff --git a/frontends/gtk/viewdata.h b/frontends/gtk/viewdata.h
new file mode 100644
index 000000000..1767b4821
--- /dev/null
+++ b/frontends/gtk/viewdata.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_GTK_VIEWDATA_H_
+#define _NETSURF_GTK_VIEWDATA_H_
+
+/**
+ * Display text to a user.
+ *
+ * The data is utf-8 encoded text and will be presented in a window, a
+ * tab or an editor as per the user configuration.
+ *
+ * \param title The title of the data being displayed.
+ * \param filename The suggested filename to be used.
+ * \param data The data to be shown. This data will be freed once the
+ * display is complete, the caller no longer owns the allocation.
+ * \param data_size The size of the data in data.
+ */
+nserror nsgtk_viewdata(const char *title, const char *filename, char *data, size_t data_size);
+
+/**
+ * Display file to a user.
+ *
+ * The file is interpreted as utf-8 encoded text and will be presented
+ * in a window, a tab or an editor as per the user configuration.
+ *
+ * \param title The title of the data being displayed.
+ * \param leafname The suggested leafname to be used.
+ * \param filename The filename of the data to be viewed.
+ */
+nserror nsgtk_viewfile(const char *title, const char *leafname, const char *filename);
+
+#endif
diff --git a/frontends/gtk/viewsource.c b/frontends/gtk/viewsource.c
new file mode 100644
index 000000000..554cfbf39
--- /dev/null
+++ b/frontends/gtk/viewsource.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2009 Mark Benjamin <MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <gtk/gtk.h>
+
+#include "utils/utils.h"
+#include "utils/utf8.h"
+#include "utils/nsurl.h"
+#include "utils/messages.h"
+#include "desktop/browser.h"
+#include "content/content.h"
+
+#include "gtk/viewdata.h"
+#include "gtk/viewsource.h"
+
+nserror nsgtk_viewsource(GtkWindow *parent, struct browser_window *bw)
+{
+ nserror ret;
+ struct hlcache_handle *hlcontent;
+ const char *source_data;
+ unsigned long source_size;
+ char *ndata = NULL;
+ size_t ndata_len;
+ char *filename;
+ char *title;
+
+ hlcontent = browser_window_get_content(bw);
+ if (hlcontent == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ if (content_get_type(hlcontent) != CONTENT_HTML) {
+ return NSERROR_BAD_CONTENT;
+ }
+
+ source_data = content_get_source_data(hlcontent, &source_size);
+
+ ret = nsurl_nice(browser_window_get_url(bw), &filename, false);
+ if (ret != NSERROR_OK) {
+ filename = strdup(messages_get("SaveSource"));
+ if (filename == NULL) {
+ return NSERROR_NOMEM;
+ }
+ }
+
+ title = malloc(strlen(nsurl_access(browser_window_get_url(bw))) + SLEN("Source of - NetSurf") + 1);
+ if (title == NULL) {
+ free(filename);
+ return NSERROR_NOMEM;
+ }
+ sprintf(title, "Source of %s - NetSurf", nsurl_access(browser_window_get_url(bw)));
+
+ ret = utf8_from_enc(source_data,
+ content_get_encoding(hlcontent, CONTENT_ENCODING_NORMAL),
+ source_size,
+ &ndata,
+ &ndata_len);
+ if (ret == NSERROR_OK) {
+ ret = nsgtk_viewdata(title, filename, ndata, ndata_len);
+ }
+
+ free(filename);
+ free(title);
+
+ return ret;
+}
diff --git a/frontends/gtk/viewsource.h b/frontends/gtk/viewsource.h
new file mode 100644
index 000000000..bba878874
--- /dev/null
+++ b/frontends/gtk/viewsource.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_GTK_VIEWSOURCE_H_
+#define _NETSURF_GTK_VIEWSOURCE_H_
+
+nserror nsgtk_viewsource(GtkWindow *parent, struct browser_window *bw);
+
+#endif
+
diff --git a/frontends/gtk/warn.h b/frontends/gtk/warn.h
new file mode 100644
index 000000000..d24f55438
--- /dev/null
+++ b/frontends/gtk/warn.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 GTK_WARN_H
+#define GTK_WARN_H
+
+/**
+ * Warn the user of an event.
+ *
+ * \param[in] warning A warning looked up in the message translation table
+ * \param[in] detail Additional text to be displayed or NULL.
+ * \return NSERROR_OK on success or error code if there was a
+ * faliure displaying the message to the user.
+ */
+nserror nsgtk_warning(const char *warning, const char *detail);
+
+#endif
diff --git a/frontends/gtk/window.c b/frontends/gtk/window.c
new file mode 100644
index 000000000..de333d1b0
--- /dev/null
+++ b/frontends/gtk/window.c
@@ -0,0 +1,1337 @@
+/*
+ * Copyright 2006 Daniel Silverstone <dsilvers@digital-scurf.org>
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * Implementation of gtk windowing.
+ */
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+#include <math.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk-pixbuf/gdk-pixdata.h>
+
+#include "utils/log.h"
+#include "utils/utf8.h"
+#include "utils/utils.h"
+#include "utils/nsoption.h"
+#include "content/hlcache.h"
+#include "gtk/window.h"
+#include "gtk/selection.h"
+#include "desktop/browser.h"
+#include "desktop/mouse.h"
+#include "desktop/searchweb.h"
+#include "desktop/textinput.h"
+#include "desktop/gui_window.h"
+#include "desktop/plotters.h"
+#include "render/form.h"
+
+#include "gtk/warn.h"
+#include "gtk/compat.h"
+#include "gtk/gui.h"
+#include "gtk/scaffolding.h"
+#include "gtk/plotters.h"
+#include "gtk/schedule.h"
+#include "gtk/tabs.h"
+#include "gtk/bitmap.h"
+#include "gtk/gdk.h"
+#include "gtk/resources.h"
+
+static GtkWidget *select_menu;
+static struct form_control *select_menu_control;
+
+static void nsgtk_select_menu_clicked(GtkCheckMenuItem *checkmenuitem,
+ gpointer user_data);
+
+struct gui_window {
+ /**
+ * The gtk scaffold object containing menu, buttons, url bar, [tabs],
+ * drawing area, etc that may contain one or more gui_windows.
+ */
+ struct nsgtk_scaffolding *scaffold;
+
+ /** The 'content' window that is rendered in the gui_window */
+ struct browser_window *bw;
+
+ /** mouse state and events. */
+ struct {
+ struct gui_window *gui;
+
+ gdouble pressed_x;
+ gdouble pressed_y;
+ gboolean waiting;
+ browser_mouse_state state;
+ } mouse;
+
+ /** caret dimension and location for rendering */
+ int caretx, carety, careth;
+
+ /** caret shape for rendering */
+ gui_pointer_shape current_pointer;
+
+ /** previous event location */
+ int last_x, last_y;
+
+ /** The top level container (tabContents) */
+ GtkWidget *container;
+
+ /** display widget for this page or frame */
+ GtkLayout *layout;
+
+ /** handle to the the visible tab */
+ GtkWidget *tab;
+
+ /** statusbar */
+ GtkLabel *status_bar;
+
+ /** status pane */
+ GtkPaned *paned;
+
+ /** has the status pane had its first size operation yet? */
+ bool paned_sized;
+
+ /** to allow disactivation / resume of normal window behaviour */
+ gulong signalhandler[NSGTK_WINDOW_SIGNAL_COUNT];
+
+ /** The icon this window should have */
+ GdkPixbuf *icon;
+
+ /** The input method to use with this window */
+ GtkIMContext *input_method;
+
+ /** list for cleanup */
+ struct gui_window *next, *prev;
+};
+
+/**< first entry in window list */
+struct gui_window *window_list = NULL;
+
+/** flag controlling opening of tabs in teh background */
+int temp_open_background = -1;
+
+struct nsgtk_scaffolding *nsgtk_get_scaffold(struct gui_window *g)
+{
+ return g->scaffold;
+}
+
+GdkPixbuf *nsgtk_get_icon(struct gui_window *gw)
+{
+ return gw->icon;
+}
+
+struct browser_window *nsgtk_get_browser_window(struct gui_window *g)
+{
+ return g->bw;
+}
+
+unsigned long nsgtk_window_get_signalhandler(struct gui_window *g, int i)
+{
+ return g->signalhandler[i];
+}
+
+GtkLayout *nsgtk_window_get_layout(struct gui_window *g)
+{
+ return g->layout;
+}
+
+GtkWidget *nsgtk_window_get_tab(struct gui_window *g)
+{
+ return g->tab;
+}
+
+void nsgtk_window_set_tab(struct gui_window *g, GtkWidget *w)
+{
+ g->tab = w;
+}
+
+
+struct gui_window *nsgtk_window_iterate(struct gui_window *g)
+{
+ return g->next;
+}
+
+float nsgtk_get_scale_for_gui(struct gui_window *g)
+{
+ return browser_window_get_scale(g->bw);
+}
+
+static void nsgtk_select_menu_clicked(GtkCheckMenuItem *checkmenuitem,
+ gpointer user_data)
+{
+ form_select_process_selection(select_menu_control,
+ (intptr_t)user_data);
+}
+
+#if GTK_CHECK_VERSION(3,0,0)
+
+static gboolean
+nsgtk_window_draw_event(GtkWidget *widget, cairo_t *cr, gpointer data)
+{
+ struct gui_window *gw = data;
+ struct gui_window *z;
+ struct rect clip;
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &nsgtk_plotters
+ };
+
+ double x1;
+ double y1;
+ double x2;
+ double y2;
+
+ assert(gw);
+ assert(gw->bw);
+
+ for (z = window_list; z && z != gw; z = z->next)
+ continue;
+ assert(z);
+ assert(GTK_WIDGET(gw->layout) == widget);
+
+ current_widget = (GtkWidget *)gw->layout;
+ current_cr = cr;
+
+ GtkAdjustment *vscroll = nsgtk_layout_get_vadjustment(gw->layout);
+ GtkAdjustment *hscroll = nsgtk_layout_get_hadjustment(gw->layout);
+
+ cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
+
+ clip.x0 = x1;
+ clip.y0 = y1;
+ clip.x1 = x2;
+ clip.y1 = y2;
+
+ browser_window_redraw(gw->bw,
+ -gtk_adjustment_get_value(hscroll),
+ -gtk_adjustment_get_value(vscroll),
+ &clip,
+ &ctx);
+
+ if (gw->careth != 0) {
+ nsgtk_plot_caret(gw->caretx, gw->carety, gw->careth);
+ }
+
+ current_widget = NULL;
+
+ return FALSE;
+}
+
+#else
+
+static gboolean
+nsgtk_window_draw_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
+{
+ struct gui_window *gw = data;
+ struct gui_window *z;
+ struct rect clip;
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &nsgtk_plotters
+ };
+
+ assert(gw);
+ assert(gw->bw);
+
+ for (z = window_list; z && z != gw; z = z->next)
+ continue;
+ assert(z);
+ assert(GTK_WIDGET(gw->layout) == widget);
+
+ current_widget = (GtkWidget *)gw->layout;
+ current_cr = gdk_cairo_create(nsgtk_layout_get_bin_window(gw->layout));
+
+ clip.x0 = event->area.x;
+ clip.y0 = event->area.y;
+ clip.x1 = event->area.x + event->area.width;
+ clip.y1 = event->area.y + event->area.height;
+
+ browser_window_redraw(gw->bw, 0, 0, &clip, &ctx);
+
+ if (gw->careth != 0) {
+ nsgtk_plot_caret(gw->caretx, gw->carety, gw->careth);
+ }
+
+ cairo_destroy(current_cr);
+
+ current_widget = NULL;
+
+ return FALSE;
+}
+
+#endif
+
+static gboolean nsgtk_window_motion_notify_event(GtkWidget *widget,
+ GdkEventMotion *event, gpointer data)
+{
+ struct gui_window *g = data;
+ bool shift = event->state & GDK_SHIFT_MASK;
+ bool ctrl = event->state & GDK_CONTROL_MASK;
+
+ if ((fabs(event->x - g->last_x) < 5.0) &&
+ (fabs(event->y - g->last_y) < 5.0)) {
+ /* Mouse hasn't moved far enough from press coordinate
+ * for this to be considered a drag.
+ */
+ return FALSE;
+ } else {
+ /* This is a drag, ensure it's always treated as such,
+ * even if we drag back over the press location.
+ */
+ g->last_x = INT_MIN;
+ g->last_y = INT_MIN;
+ }
+
+ if (g->mouse.state & BROWSER_MOUSE_PRESS_1) {
+ /* Start button 1 drag */
+ browser_window_mouse_click(g->bw, BROWSER_MOUSE_DRAG_1,
+ g->mouse.pressed_x, g->mouse.pressed_y);
+
+ /* Replace PRESS with HOLDING and declare drag in progress */
+ g->mouse.state ^= (BROWSER_MOUSE_PRESS_1 |
+ BROWSER_MOUSE_HOLDING_1);
+ g->mouse.state |= BROWSER_MOUSE_DRAG_ON;
+ } else if (g->mouse.state & BROWSER_MOUSE_PRESS_2) {
+ /* Start button 2 drag */
+ browser_window_mouse_click(g->bw, BROWSER_MOUSE_DRAG_2,
+ g->mouse.pressed_x, g->mouse.pressed_y);
+
+ /* Replace PRESS with HOLDING and declare drag in progress */
+ g->mouse.state ^= (BROWSER_MOUSE_PRESS_2 |
+ BROWSER_MOUSE_HOLDING_2);
+ g->mouse.state |= BROWSER_MOUSE_DRAG_ON;
+ }
+
+ /* Handle modifiers being removed */
+ if (g->mouse.state & BROWSER_MOUSE_MOD_1 && !shift)
+ g->mouse.state ^= BROWSER_MOUSE_MOD_1;
+ if (g->mouse.state & BROWSER_MOUSE_MOD_2 && !ctrl)
+ g->mouse.state ^= BROWSER_MOUSE_MOD_2;
+
+ browser_window_mouse_track(g->bw, g->mouse.state,
+ event->x / browser_window_get_scale(g->bw),
+ event->y / browser_window_get_scale(g->bw));
+
+ return TRUE;
+}
+
+static gboolean nsgtk_window_button_press_event(GtkWidget *widget,
+ GdkEventButton *event, gpointer data)
+{
+ struct gui_window *g = data;
+
+ gtk_im_context_reset(g->input_method);
+ gtk_widget_grab_focus(GTK_WIDGET(g->layout));
+ gtk_widget_hide(GTK_WIDGET(nsgtk_scaffolding_history_window(
+ g->scaffold)->window));
+
+ g->mouse.pressed_x = event->x / browser_window_get_scale(g->bw);
+ g->mouse.pressed_y = event->y / browser_window_get_scale(g->bw);
+
+ switch (event->button) {
+ case 1: /* Left button, usually. Pass to core as BUTTON 1. */
+ g->mouse.state = BROWSER_MOUSE_PRESS_1;
+ break;
+
+ case 2: /* Middle button, usually. Pass to core as BUTTON 2 */
+ g->mouse.state = BROWSER_MOUSE_PRESS_2;
+ break;
+
+ case 3: /* Right button, usually. Action button, context menu. */
+ browser_window_remove_caret(g->bw, true);
+ nsgtk_scaffolding_context_menu(g->scaffold,
+ g->mouse.pressed_x,
+ g->mouse.pressed_y);
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+
+ /* Modify for double & triple clicks */
+ if (event->type == GDK_3BUTTON_PRESS)
+ g->mouse.state |= BROWSER_MOUSE_TRIPLE_CLICK;
+ else if (event->type == GDK_2BUTTON_PRESS)
+ g->mouse.state |= BROWSER_MOUSE_DOUBLE_CLICK;
+
+ /* Handle the modifiers too */
+ if (event->state & GDK_SHIFT_MASK)
+ g->mouse.state |= BROWSER_MOUSE_MOD_1;
+ if (event->state & GDK_CONTROL_MASK)
+ g->mouse.state |= BROWSER_MOUSE_MOD_2;
+
+ /* Record where we pressed, for use when determining whether to start
+ * a drag in motion notify events. */
+ g->last_x = event->x;
+ g->last_y = event->y;
+
+ browser_window_mouse_click(g->bw, g->mouse.state, g->mouse.pressed_x,
+ g->mouse.pressed_y);
+
+ return TRUE;
+}
+
+static gboolean nsgtk_window_button_release_event(GtkWidget *widget,
+ GdkEventButton *event, gpointer data)
+{
+ struct gui_window *g = data;
+ bool shift = event->state & GDK_SHIFT_MASK;
+ bool ctrl = event->state & GDK_CONTROL_MASK;
+
+ /* If the mouse state is PRESS then we are waiting for a release to emit
+ * a click event, otherwise just reset the state to nothing */
+ if (g->mouse.state & BROWSER_MOUSE_PRESS_1)
+ g->mouse.state ^= (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_CLICK_1);
+ else if (g->mouse.state & BROWSER_MOUSE_PRESS_2)
+ g->mouse.state ^= (BROWSER_MOUSE_PRESS_2 | BROWSER_MOUSE_CLICK_2);
+
+ /* Handle modifiers being removed */
+ if (g->mouse.state & BROWSER_MOUSE_MOD_1 && !shift)
+ g->mouse.state ^= BROWSER_MOUSE_MOD_1;
+ if (g->mouse.state & BROWSER_MOUSE_MOD_2 && !ctrl)
+ g->mouse.state ^= BROWSER_MOUSE_MOD_2;
+
+ if (g->mouse.state & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2)) {
+ browser_window_mouse_click(g->bw, g->mouse.state,
+ event->x / browser_window_get_scale(g->bw),
+ event->y / browser_window_get_scale(g->bw));
+ } else {
+ browser_window_mouse_track(g->bw, 0,
+ event->x / browser_window_get_scale(g->bw),
+ event->y / browser_window_get_scale(g->bw));
+ }
+
+ g->mouse.state = 0;
+ return TRUE;
+}
+
+static gboolean
+nsgtk_window_scroll_event(GtkWidget *widget,
+ GdkEventScroll *event,
+ gpointer data)
+{
+ struct gui_window *g = data;
+ double value;
+ double deltax = 0;
+ double deltay = 0;
+ GtkAdjustment *vscroll = nsgtk_layout_get_vadjustment(g->layout);
+ GtkAdjustment *hscroll = nsgtk_layout_get_hadjustment(g->layout);
+ GtkAllocation alloc;
+
+ switch (event->direction) {
+ case GDK_SCROLL_LEFT:
+ deltax = -1.0;
+ break;
+
+ case GDK_SCROLL_UP:
+ deltay = -1.0;
+ break;
+
+ case GDK_SCROLL_RIGHT:
+ deltax = 1.0;
+ break;
+
+ case GDK_SCROLL_DOWN:
+ deltay = 1.0;
+ break;
+
+#if GTK_CHECK_VERSION(3,4,0)
+ case GDK_SCROLL_SMOOTH:
+ gdk_event_get_scroll_deltas((GdkEvent *)event, &deltax, &deltay);
+ break;
+#endif
+ default:
+ LOG("Unhandled mouse scroll direction");
+ return TRUE;
+ }
+
+ deltax *= nsgtk_adjustment_get_step_increment(hscroll);
+ deltay *= nsgtk_adjustment_get_step_increment(vscroll);
+
+ if (browser_window_scroll_at_point(g->bw,
+ event->x / browser_window_get_scale(g->bw),
+ event->y / browser_window_get_scale(g->bw),
+ deltax, deltay) != true) {
+
+ /* core did not handle event so change adjustments */
+
+ /* Horizontal */
+ if (deltax != 0) {
+ value = gtk_adjustment_get_value(hscroll) + deltax;
+
+ /* @todo consider gtk_widget_get_allocated_width() */
+ nsgtk_widget_get_allocation(GTK_WIDGET(g->layout), &alloc);
+
+ if (value > nsgtk_adjustment_get_upper(hscroll) - alloc.width) {
+ value = nsgtk_adjustment_get_upper(hscroll) - alloc.width;
+ }
+ if (value < nsgtk_adjustment_get_lower(hscroll)) {
+ value = nsgtk_adjustment_get_lower(hscroll);
+ }
+
+ gtk_adjustment_set_value(hscroll, value);
+ }
+
+ /* Vertical */
+ if (deltay != 0) {
+ value = gtk_adjustment_get_value(vscroll) + deltay;
+
+ /* @todo consider gtk_widget_get_allocated_height */
+ nsgtk_widget_get_allocation(GTK_WIDGET(g->layout), &alloc);
+
+ if (value > (nsgtk_adjustment_get_upper(vscroll) - alloc.height)) {
+ value = nsgtk_adjustment_get_upper(vscroll) - alloc.height;
+ }
+ if (value < nsgtk_adjustment_get_lower(vscroll)) {
+ value = nsgtk_adjustment_get_lower(vscroll);
+ }
+
+ gtk_adjustment_set_value(vscroll, value);
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean nsgtk_window_keypress_event(GtkWidget *widget,
+ GdkEventKey *event, gpointer data)
+{
+ struct gui_window *g = data;
+ uint32_t nskey;
+
+ if (gtk_im_context_filter_keypress(g->input_method, event))
+ return TRUE;
+
+ nskey = gtk_gui_gdkkey_to_nskey(event);
+
+ if (browser_window_key_press(g->bw, nskey))
+ return TRUE;
+
+ if ((event->state & 0x7) != 0)
+ return TRUE;
+
+ double value;
+ GtkAdjustment *vscroll = nsgtk_layout_get_vadjustment(g->layout);
+ GtkAdjustment *hscroll = nsgtk_layout_get_hadjustment(g->layout);
+ GtkAllocation alloc;
+
+ /* @todo consider gtk_widget_get_allocated_width() */
+ nsgtk_widget_get_allocation(GTK_WIDGET(g->layout), &alloc);
+
+ switch (event->keyval) {
+
+ case GDK_KEY(Home):
+ case GDK_KEY(KP_Home):
+ value = nsgtk_adjustment_get_lower(vscroll);
+ gtk_adjustment_set_value(vscroll, value);
+ break;
+
+ case GDK_KEY(End):
+ case GDK_KEY(KP_End):
+ value = nsgtk_adjustment_get_upper(vscroll) - alloc.height;
+
+ if (value < nsgtk_adjustment_get_lower(vscroll))
+ value = nsgtk_adjustment_get_lower(vscroll);
+
+ gtk_adjustment_set_value(vscroll, value);
+ break;
+
+ case GDK_KEY(Left):
+ case GDK_KEY(KP_Left):
+ value = gtk_adjustment_get_value(hscroll) -
+ nsgtk_adjustment_get_step_increment(hscroll);
+
+ if (value < nsgtk_adjustment_get_lower(hscroll))
+ value = nsgtk_adjustment_get_lower(hscroll);
+
+ gtk_adjustment_set_value(hscroll, value);
+ break;
+
+ case GDK_KEY(Up):
+ case GDK_KEY(KP_Up):
+ value = gtk_adjustment_get_value(vscroll) -
+ nsgtk_adjustment_get_step_increment(vscroll);
+
+ if (value < nsgtk_adjustment_get_lower(vscroll))
+ value = nsgtk_adjustment_get_lower(vscroll);
+
+ gtk_adjustment_set_value(vscroll, value);
+ break;
+
+ case GDK_KEY(Right):
+ case GDK_KEY(KP_Right):
+ value = gtk_adjustment_get_value(hscroll) +
+ nsgtk_adjustment_get_step_increment(hscroll);
+
+ if (value > nsgtk_adjustment_get_upper(hscroll) - alloc.width)
+ value = nsgtk_adjustment_get_upper(hscroll) - alloc.width;
+
+ gtk_adjustment_set_value(hscroll, value);
+ break;
+
+ case GDK_KEY(Down):
+ case GDK_KEY(KP_Down):
+ value = gtk_adjustment_get_value(vscroll) +
+ nsgtk_adjustment_get_step_increment(vscroll);
+
+ if (value > nsgtk_adjustment_get_upper(vscroll) - alloc.height)
+ value = nsgtk_adjustment_get_upper(vscroll) - alloc.height;
+
+ gtk_adjustment_set_value(vscroll, value);
+ break;
+
+ case GDK_KEY(Page_Up):
+ case GDK_KEY(KP_Page_Up):
+ value = gtk_adjustment_get_value(vscroll) -
+ nsgtk_adjustment_get_page_increment(vscroll);
+
+ if (value < nsgtk_adjustment_get_lower(vscroll))
+ value = nsgtk_adjustment_get_lower(vscroll);
+
+ gtk_adjustment_set_value(vscroll, value);
+ break;
+
+ case GDK_KEY(Page_Down):
+ case GDK_KEY(KP_Page_Down):
+ value = gtk_adjustment_get_value(vscroll) +
+ nsgtk_adjustment_get_page_increment(vscroll);
+
+ if (value > nsgtk_adjustment_get_upper(vscroll) - alloc.height)
+ value = nsgtk_adjustment_get_upper(vscroll) - alloc.height;
+
+ gtk_adjustment_set_value(vscroll, value);
+ break;
+
+ default:
+ break;
+
+ }
+
+ return TRUE;
+}
+
+static gboolean nsgtk_window_keyrelease_event(GtkWidget *widget,
+ GdkEventKey *event, gpointer data)
+{
+ struct gui_window *g = data;
+
+ return gtk_im_context_filter_keypress(g->input_method, event);
+}
+
+
+static void nsgtk_window_input_method_commit(GtkIMContext *ctx,
+ const gchar *str, gpointer data)
+{
+ struct gui_window *g = data;
+ size_t len = strlen(str), offset = 0;
+
+ while (offset < len) {
+ uint32_t nskey = utf8_to_ucs4(str + offset, len - offset);
+
+ browser_window_key_press(g->bw, nskey);
+
+ offset = utf8_next(str, len, offset);
+ }
+}
+
+
+static gboolean nsgtk_window_size_allocate_event(GtkWidget *widget,
+ GtkAllocation *allocation, gpointer data)
+{
+ struct gui_window *g = data;
+
+ browser_window_schedule_reformat(g->bw);
+
+ return TRUE;
+}
+
+
+/** when the pane position is changed update the user option
+ *
+ * The slightly awkward implementation with the first allocation flag
+ * is necessary because the initial window creation does not cause an
+ * allocate-event signal so the position value in the pane is incorrect
+ * and we cannot know what it should be until after the allocation
+ * (which did not generate a signal) is done as the user position is a
+ * percentage of pane total width not an absolute value.
+ */
+static void
+nsgtk_paned_notify__position(GObject *gobject, GParamSpec *pspec, gpointer data)
+{
+ struct gui_window *g = data;
+ GtkAllocation pane_alloc;
+
+ gtk_widget_get_allocation(GTK_WIDGET(g->paned), &pane_alloc);
+
+ if (g->paned_sized == false)
+ {
+ g->paned_sized = true;
+ gtk_paned_set_position(g->paned,
+ (nsoption_int(toolbar_status_size) * pane_alloc.width) / 10000);
+ return;
+ }
+
+ nsoption_set_int(toolbar_status_size,
+ ((gtk_paned_get_position(g->paned) * 10000) / (pane_alloc.width - 1)));
+}
+
+/** Set status bar / scroll bar proportion according to user option
+ * when pane is resized.
+ */
+static gboolean nsgtk_paned_size_allocate_event(GtkWidget *widget,
+ GtkAllocation *allocation, gpointer data)
+{
+ gtk_paned_set_position(GTK_PANED(widget),
+ (nsoption_int(toolbar_status_size) * allocation->width) / 10000);
+
+ return TRUE;
+}
+
+/* destroy the browsing context as there is nothing to display it now */
+static void window_destroy(GtkWidget *widget, gpointer data)
+{
+ struct gui_window *gw = data;
+
+ browser_window_destroy(gw->bw);
+
+ g_object_unref(gw->input_method);
+}
+
+
+/**
+ * Create and open a gtk container (window or tab) for a browsing context.
+ *
+ * \param bw The browsing context to create gui_window for.
+ * \param existing An existing gui_window, may be NULL
+ * \param flags flags to control the container creation
+ * \return gui window, or NULL on error
+ *
+ * If GW_CREATE_CLONE flag is set existing is non-NULL.
+ *
+ * Front end's gui_window must include a reference to the
+ * browser window passed in the bw param.
+ */
+static struct gui_window *
+gui_window_create(struct browser_window *bw,
+ struct gui_window *existing,
+ gui_window_create_flags flags)
+{
+ struct gui_window *g; /* what is being created to return */
+ bool tempback;
+ GtkBuilder* tab_builder;
+
+ nserror res;
+
+ res = nsgtk_builder_new_from_resname("tabcontents", &tab_builder);
+ if (res != NSERROR_OK) {
+ LOG("Tab contents UI builder init failed");
+ return NULL;
+ }
+
+ gtk_builder_connect_signals(tab_builder, NULL);
+
+ g = calloc(1, sizeof(*g));
+ if (!g) {
+ nsgtk_warning("NoMemory", 0);
+ g_object_unref(tab_builder);
+ return NULL;
+ }
+
+ LOG("Creating gui window %p for browser window %p", g, bw);
+
+ g->bw = bw;
+ g->mouse.state = 0;
+ g->current_pointer = GUI_POINTER_DEFAULT;
+
+ /* attach scaffold */
+ if (flags & GW_CREATE_TAB) {
+ /* open in new tab, attach to existing scaffold */
+ if (existing != NULL) {
+ g->scaffold = existing->scaffold;
+ } else {
+ g->scaffold = nsgtk_current_scaffolding();
+ }
+ } else {
+ /* open in new window, create and attach to scaffold */
+ g->scaffold = nsgtk_new_scaffolding(g);
+ }
+ if (g->scaffold == NULL) {
+ nsgtk_warning("NoMemory", 0);
+ free(g);
+ g_object_unref(tab_builder);
+ return NULL;
+ }
+
+ /* Construct our primary elements */
+ g->container = GTK_WIDGET(gtk_builder_get_object(tab_builder, "tabContents"));
+ g->layout = GTK_LAYOUT(gtk_builder_get_object(tab_builder, "layout"));
+ g->status_bar = GTK_LABEL(gtk_builder_get_object(tab_builder, "status_bar"));
+ g->paned = GTK_PANED(gtk_builder_get_object(tab_builder, "hpaned1"));
+ g->input_method = gtk_im_multicontext_new();
+
+ /* set a default favicon */
+ g_object_ref(favicon_pixbuf);
+ g->icon = favicon_pixbuf;
+
+ /* add new gui window to global list (push_top) */
+ if (window_list) {
+ window_list->prev = g;
+ }
+ g->next = window_list;
+ g->prev = NULL;
+ window_list = g;
+
+ /* set the events we're interested in receiving from the browser's
+ * drawing area.
+ */
+ gtk_widget_add_events(GTK_WIDGET(g->layout),
+ GDK_EXPOSURE_MASK |
+ GDK_LEAVE_NOTIFY_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK |
+ GDK_KEY_PRESS_MASK |
+ GDK_KEY_RELEASE_MASK |
+ GDK_SCROLL_MASK);
+ nsgtk_widget_set_can_focus(GTK_WIDGET(g->layout), TRUE);
+
+ /* set the default background colour of the drawing area to white. */
+ nsgtk_widget_override_background_color(GTK_WIDGET(g->layout),
+ GTK_STATE_NORMAL,
+ 0, 0xffff, 0xffff, 0xffff);
+
+ g->signalhandler[NSGTK_WINDOW_SIGNAL_REDRAW] =
+ nsgtk_connect_draw_event(GTK_WIDGET(g->layout),
+ G_CALLBACK(nsgtk_window_draw_event), g);
+
+ /* helper macro to conect signals to callbacks */
+#define CONNECT(obj, sig, callback, ptr) \
+ g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr))
+
+ /* layout signals */
+ CONNECT(g->layout, "motion-notify-event",
+ nsgtk_window_motion_notify_event, g);
+ g->signalhandler[NSGTK_WINDOW_SIGNAL_CLICK] =
+ CONNECT(g->layout, "button-press-event",
+ nsgtk_window_button_press_event, g);
+ CONNECT(g->layout, "button-release-event",
+ nsgtk_window_button_release_event, g);
+ CONNECT(g->layout, "key-press-event",
+ nsgtk_window_keypress_event, g);
+ CONNECT(g->layout, "key-release-event",
+ nsgtk_window_keyrelease_event, g);
+ CONNECT(g->layout, "size-allocate",
+ nsgtk_window_size_allocate_event, g);
+ CONNECT(g->layout, "scroll-event",
+ nsgtk_window_scroll_event, g);
+
+ /* status pane signals */
+ CONNECT(g->paned, "size-allocate",
+ nsgtk_paned_size_allocate_event, g);
+
+ CONNECT(g->paned, "notify::position",
+ nsgtk_paned_notify__position, g);
+
+ /* gtk container destructor */
+ CONNECT(g->container, "destroy", window_destroy, g);
+
+ /* input method */
+ gtk_im_context_set_client_window(g->input_method,
+ nsgtk_layout_get_bin_window(g->layout));
+ gtk_im_context_set_use_preedit(g->input_method, FALSE);
+
+ /* input method signals */
+ CONNECT(g->input_method, "commit",
+ nsgtk_window_input_method_commit, g);
+
+ /* add the tab container to the scaffold notebook */
+ switch (temp_open_background) {
+ case -1:
+ tempback = !(nsoption_bool(focus_new));
+ break;
+ case 0:
+ tempback = false;
+ break;
+ default:
+ tempback = true;
+ break;
+ }
+ nsgtk_tab_add(g, g->container, tempback);
+
+ /* safe to drop the reference to the tab_builder as the container is
+ * referenced by the notebook now.
+ */
+ g_object_unref(tab_builder);
+
+ return g;
+}
+
+
+
+void nsgtk_reflow_all_windows(void)
+{
+ for (struct gui_window *g = window_list; g; g = g->next) {
+ nsgtk_tab_options_changed(nsgtk_scaffolding_notebook(g->scaffold));
+ browser_window_schedule_reformat(g->bw);
+ }
+}
+
+
+/**
+ * callback from core to reformat a window.
+ */
+static void nsgtk_window_reformat(struct gui_window *gw)
+{
+ GtkAllocation alloc;
+
+ if (gw != NULL) {
+ /** @todo consider gtk_widget_get_allocated_width() */
+ nsgtk_widget_get_allocation(GTK_WIDGET(gw->layout), &alloc);
+
+ browser_window_reformat(gw->bw, false, alloc.width, alloc.height);
+ }
+}
+
+void nsgtk_window_destroy_browser(struct gui_window *gw)
+{
+ /* remove tab */
+ gtk_widget_destroy(gw->container);
+}
+
+static void gui_window_destroy(struct gui_window *g)
+{
+ LOG("gui_window: %p", g);
+ assert(g != NULL);
+ assert(g->bw != NULL);
+ LOG("scaffolding: %p", g->scaffold);
+
+ if (g->prev) {
+ g->prev->next = g->next;
+ } else {
+ window_list = g->next;
+ }
+
+ if (g->next) {
+ g->next->prev = g->prev;
+ }
+
+ LOG("window list head: %p", window_list);
+}
+
+/**
+ * favicon setting for gtk gui window.
+ *
+ * \param gw gtk gui window to set favicon on.
+ * \param icon A handle to the new favicon content.
+ */
+static void gui_window_set_icon(struct gui_window *gw, hlcache_handle *icon)
+{
+ struct bitmap *icon_bitmap = NULL;
+
+ /* free any existing icon */
+ if (gw->icon != NULL) {
+ g_object_unref(gw->icon);
+ gw->icon = NULL;
+ }
+
+ if (icon != NULL) {
+ icon_bitmap = content_get_bitmap(icon);
+ if (icon_bitmap != NULL) {
+ LOG("Using %p bitmap", icon_bitmap);
+ gw->icon = nsgdk_pixbuf_get_from_surface(icon_bitmap->surface, 16, 16);
+ }
+ }
+
+ if (gw->icon == NULL) {
+ LOG("Using default favicon");
+ g_object_ref(favicon_pixbuf);
+ gw->icon = favicon_pixbuf;
+ }
+
+ nsgtk_scaffolding_set_icon(gw);
+}
+
+static bool gui_window_get_scroll(struct gui_window *g, int *sx, int *sy)
+{
+ GtkAdjustment *vadj = nsgtk_layout_get_vadjustment(g->layout);
+ GtkAdjustment *hadj = nsgtk_layout_get_hadjustment(g->layout);
+
+ assert(vadj);
+ assert(hadj);
+
+ *sy = (int)(gtk_adjustment_get_value(vadj));
+ *sx = (int)(gtk_adjustment_get_value(hadj));
+
+ return true;
+}
+
+static void nsgtk_redraw_caret(struct gui_window *g)
+{
+ int sx, sy;
+
+ if (g->careth == 0)
+ return;
+
+ gui_window_get_scroll(g, &sx, &sy);
+
+ gtk_widget_queue_draw_area(GTK_WIDGET(g->layout),
+ g->caretx - sx, g->carety - sy, 1, g->careth + 1);
+
+}
+
+static void gui_window_remove_caret(struct gui_window *g)
+{
+ int sx, sy;
+ int oh = g->careth;
+
+ if (oh == 0)
+ return;
+
+ g->careth = 0;
+
+ gui_window_get_scroll(g, &sx, &sy);
+
+ gtk_widget_queue_draw_area(GTK_WIDGET(g->layout),
+ g->caretx - sx, g->carety - sy, 1, oh + 1);
+
+}
+
+static void gui_window_redraw_window(struct gui_window *g)
+{
+ gtk_widget_queue_draw(GTK_WIDGET(g->layout));
+}
+
+static void gui_window_update_box(struct gui_window *g, const struct rect *rect)
+{
+ int sx, sy;
+ float scale;
+
+ if (!browser_window_has_content(g->bw))
+ return;
+
+ gui_window_get_scroll(g, &sx, &sy);
+ scale = browser_window_get_scale(g->bw);
+
+ gtk_widget_queue_draw_area(GTK_WIDGET(g->layout),
+ rect->x0 * scale - sx,
+ rect->y0 * scale - sy,
+ (rect->x1 - rect->x0) * scale,
+ (rect->y1 - rect->y0) * scale);
+}
+
+static void gui_window_set_status(struct gui_window *g, const char *text)
+{
+ assert(g);
+ assert(g->status_bar);
+ gtk_label_set_text(g->status_bar, text);
+}
+
+
+static void gui_window_set_scroll(struct gui_window *g, int sx, int sy)
+{
+ GtkAdjustment *vadj = nsgtk_layout_get_vadjustment(g->layout);
+ GtkAdjustment *hadj = nsgtk_layout_get_hadjustment(g->layout);
+ gdouble vlower, vpage, vupper, hlower, hpage, hupper, x = (double)sx, y = (double)sy;
+
+ assert(vadj);
+ assert(hadj);
+
+ g_object_get(vadj, "page-size", &vpage, "lower", &vlower, "upper", &vupper, NULL);
+ g_object_get(hadj, "page-size", &hpage, "lower", &hlower, "upper", &hupper, NULL);
+
+ if (x < hlower)
+ x = hlower;
+ if (x > (hupper - hpage))
+ x = hupper - hpage;
+ if (y < vlower)
+ y = vlower;
+ if (y > (vupper - vpage))
+ y = vupper - vpage;
+
+ gtk_adjustment_set_value(vadj, y);
+ gtk_adjustment_set_value(hadj, x);
+}
+
+static void gui_window_update_extent(struct gui_window *g)
+{
+ int w, h;
+
+ if (browser_window_get_extents(g->bw, true, &w, &h) == NSERROR_OK) {
+ gtk_layout_set_size(g->layout, w, h);
+ }
+}
+
+static void gui_window_set_pointer(struct gui_window *g,
+ gui_pointer_shape shape)
+{
+ GdkCursor *cursor = NULL;
+ GdkCursorType cursortype;
+ bool nullcursor = false;
+
+ if (g->current_pointer == shape)
+ return;
+
+ g->current_pointer = shape;
+
+ switch (shape) {
+ case GUI_POINTER_POINT:
+ cursortype = GDK_HAND2;
+ break;
+ case GUI_POINTER_CARET:
+ cursortype = GDK_XTERM;
+ break;
+ case GUI_POINTER_UP:
+ cursortype = GDK_TOP_SIDE;
+ break;
+ case GUI_POINTER_DOWN:
+ cursortype = GDK_BOTTOM_SIDE;
+ break;
+ case GUI_POINTER_LEFT:
+ cursortype = GDK_LEFT_SIDE;
+ break;
+ case GUI_POINTER_RIGHT:
+ cursortype = GDK_RIGHT_SIDE;
+ break;
+ case GUI_POINTER_LD:
+ cursortype = GDK_BOTTOM_LEFT_CORNER;
+ break;
+ case GUI_POINTER_RD:
+ cursortype = GDK_BOTTOM_RIGHT_CORNER;
+ break;
+ case GUI_POINTER_LU:
+ cursortype = GDK_TOP_LEFT_CORNER;
+ break;
+ case GUI_POINTER_RU:
+ cursortype = GDK_TOP_RIGHT_CORNER;
+ break;
+ case GUI_POINTER_CROSS:
+ cursortype = GDK_CROSS;
+ break;
+ case GUI_POINTER_MOVE:
+ cursortype = GDK_FLEUR;
+ break;
+ case GUI_POINTER_WAIT:
+ cursortype = GDK_WATCH;
+ break;
+ case GUI_POINTER_HELP:
+ cursortype = GDK_QUESTION_ARROW;
+ break;
+ case GUI_POINTER_MENU:
+ cursor = nsgtk_create_menu_cursor();
+ nullcursor = true;
+ break;
+ case GUI_POINTER_PROGRESS:
+ /* In reality, this needs to be the funky left_ptr_watch
+ * which we can't do easily yet.
+ */
+ cursortype = GDK_WATCH;
+ break;
+ /* The following we're not sure about */
+ case GUI_POINTER_NO_DROP:
+ case GUI_POINTER_NOT_ALLOWED:
+ case GUI_POINTER_DEFAULT:
+ default:
+ nullcursor = true;
+ }
+
+ if (!nullcursor)
+ cursor = gdk_cursor_new_for_display(
+ gtk_widget_get_display(
+ GTK_WIDGET(g->layout)),
+ cursortype);
+ gdk_window_set_cursor(nsgtk_widget_get_window(GTK_WIDGET(g->layout)),
+ cursor);
+
+ if (!nullcursor)
+ nsgdk_cursor_unref(cursor);
+}
+
+
+static void gui_window_place_caret(struct gui_window *g, int x, int y, int height,
+ const struct rect *clip)
+{
+ nsgtk_redraw_caret(g);
+
+ y += 1;
+ height -= 1;
+
+ if (y < clip->y0) {
+ height -= clip->y0 - y;
+ y = clip->y0;
+ }
+
+ if (y + height > clip->y1) {
+ height = clip->y1 - y + 1;
+ }
+
+ g->caretx = x;
+ g->carety = y;
+ g->careth = height;
+
+ nsgtk_redraw_caret(g);
+
+ gtk_widget_grab_focus(GTK_WIDGET(g->layout));
+}
+
+
+static void gui_window_get_dimensions(struct gui_window *g, int *width, int *height,
+ bool scaled)
+{
+ GtkAllocation alloc;
+
+ /* @todo consider gtk_widget_get_allocated_width() */
+ nsgtk_widget_get_allocation(GTK_WIDGET(g->layout), &alloc);
+
+ *width = alloc.width;
+ *height = alloc.height;
+
+ if (scaled) {
+ float scale = browser_window_get_scale(g->bw);
+ *width /= scale;
+ *height /= scale;
+ }
+ LOG("width: %i", *width);
+ LOG("height: %i", *height);
+}
+
+static void gui_window_start_selection(struct gui_window *g)
+{
+ gtk_widget_grab_focus(GTK_WIDGET(g->layout));
+}
+
+static void gui_window_create_form_select_menu(struct gui_window *g,
+ struct form_control *control)
+{
+ intptr_t item;
+ struct form_option *option;
+
+ GtkWidget *menu_item;
+
+ /* control->data.select.multiple is true if multiple selections
+ * are allowable. We ignore this, as the core handles it for us.
+ * Yay. \o/
+ */
+
+ if (select_menu != NULL) {
+ gtk_widget_destroy(select_menu);
+ }
+
+ select_menu = gtk_menu_new();
+ select_menu_control = control;
+
+ item = 0;
+ option = form_select_get_option(control, item);
+ while (option != NULL) {
+ LOG("Item %"PRIdPTR" option %p text %s",
+ item, option, option->text);
+ menu_item = gtk_check_menu_item_new_with_label(option->text);
+ if (option->selected) {
+ gtk_check_menu_item_set_active(
+ GTK_CHECK_MENU_ITEM(menu_item), TRUE);
+ }
+
+ /*
+ * This casts the item index integer into an integer
+ * the size of a pointer. This allows the callback
+ * parameter to be passed avoiding allocating memory
+ * for a context with a single integer in it.
+ */
+ g_signal_connect(menu_item, "toggled",
+ G_CALLBACK(nsgtk_select_menu_clicked), (gpointer)item);
+
+ gtk_menu_shell_append(GTK_MENU_SHELL(select_menu), menu_item);
+
+ item++;
+ option = form_select_get_option(control, item);
+ }
+
+ gtk_widget_show_all(select_menu);
+
+ gtk_menu_popup(GTK_MENU(select_menu), NULL, NULL, NULL,
+ NULL /* data */, 0, gtk_get_current_event_time());
+
+}
+
+static void
+gui_window_file_gadget_open(struct gui_window *g,
+ hlcache_handle *hl,
+ struct form_control *gadget)
+{
+ GtkWidget *dialog;
+
+ dialog = gtk_file_chooser_dialog_new("Select File",
+ nsgtk_scaffolding_window(g->scaffold),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NSGTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ LOG("*** open dialog: %p", dialog);
+
+ int ret = gtk_dialog_run(GTK_DIALOG(dialog));
+ LOG("*** return value: %d", ret);
+ if (ret == GTK_RESPONSE_ACCEPT) {
+ char *filename;
+
+ filename = gtk_file_chooser_get_filename(
+ GTK_FILE_CHOOSER(dialog));
+
+ browser_window_set_gadget_filename(g->bw, gadget, filename);
+
+ g_free(filename);
+ }
+
+ gtk_widget_destroy(dialog);
+}
+
+static struct gui_window_table window_table = {
+ .create = gui_window_create,
+ .destroy = gui_window_destroy,
+ .redraw = gui_window_redraw_window,
+ .update = gui_window_update_box,
+ .get_scroll = gui_window_get_scroll,
+ .set_scroll = gui_window_set_scroll,
+ .get_dimensions = gui_window_get_dimensions,
+ .update_extent = gui_window_update_extent,
+ .reformat = nsgtk_window_reformat,
+
+ .set_icon = gui_window_set_icon,
+ .set_status = gui_window_set_status,
+ .set_pointer = gui_window_set_pointer,
+ .place_caret = gui_window_place_caret,
+ .remove_caret = gui_window_remove_caret,
+ .create_form_select_menu = gui_window_create_form_select_menu,
+ .file_gadget_open = gui_window_file_gadget_open,
+ .start_selection = gui_window_start_selection,
+
+ /* from scaffold */
+ .set_title = nsgtk_window_set_title,
+ .set_url = gui_window_set_url,
+ .start_throbber = gui_window_start_throbber,
+ .stop_throbber = gui_window_stop_throbber,
+};
+
+struct gui_window_table *nsgtk_window_table = &window_table;
diff --git a/frontends/gtk/window.h b/frontends/gtk/window.h
new file mode 100644
index 000000000..c604bf3f7
--- /dev/null
+++ b/frontends/gtk/window.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2006 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_GTK_WINDOW_H
+#define NETSURF_GTK_WINDOW_H 1
+
+#include "gtk/scaffolding.h"
+
+extern struct gui_window_table *nsgtk_window_table;
+
+typedef enum nsgtk_window_signals {
+ NSGTK_WINDOW_SIGNAL_CLICK,
+ NSGTK_WINDOW_SIGNAL_REDRAW,
+ NSGTK_WINDOW_SIGNAL_COUNT
+} nsgtk_window_signal;
+
+extern struct gui_window *window_list;
+extern int temp_open_background;
+
+struct browser_window *nsgtk_get_browser_window(struct gui_window *g);
+struct nsgtk_scaffolding *nsgtk_get_scaffold(struct gui_window *g);
+GdkPixbuf *nsgtk_get_icon(struct gui_window *gw);
+void nsgtk_reflow_all_windows(void);
+float nsgtk_get_scale_for_gui(struct gui_window *g);
+int nsgtk_gui_window_update_targets(struct gui_window *g);
+void nsgtk_window_destroy_browser(struct gui_window *g);
+unsigned long nsgtk_window_get_signalhandler(struct gui_window *g, int i);
+GtkLayout *nsgtk_window_get_layout(struct gui_window *g);
+struct gui_window *nsgtk_window_iterate(struct gui_window *g);
+GtkWidget *nsgtk_window_get_tab(struct gui_window *g);
+void nsgtk_window_set_tab(struct gui_window *g, GtkWidget *w);
+
+#endif /* NETSURF_GTK_WINDOW_H */
diff --git a/frontends/monkey/401login.c b/frontends/monkey/401login.c
new file mode 100644
index 000000000..8b4d33d7d
--- /dev/null
+++ b/frontends/monkey/401login.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2011 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "utils/ring.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "monkey/401login.h"
+
+typedef struct monkey401 {
+ struct monkey401 *r_next, *r_prev;
+ uint32_t num;
+ lwc_string *host; /* Ignore */
+ nserror (*cb)(bool,void*);
+ void *pw;
+} monkey401_t;
+
+static monkey401_t *m4_ring = NULL;
+static uint32_t m4_ctr = 0;
+
+void gui_401login_open(nsurl *url, const char *realm,
+ nserror (*cb)(bool proceed, void *pw), void *cbpw)
+{
+ monkey401_t *m4t = calloc(sizeof(*m4t), 1);
+ if (m4t == NULL) {
+ cb(false, cbpw);
+ return;
+ }
+ m4t->cb = cb;
+ m4t->pw = cbpw;
+ m4t->num = m4_ctr++;
+
+ RING_INSERT(m4_ring, m4t);
+
+ fprintf(stdout, "401LOGIN OPEN M4 %u URL %s REALM %s\n",
+ m4t->num, nsurl_access(url), realm);
+}
+
+
diff --git a/frontends/monkey/401login.h b/frontends/monkey/401login.h
new file mode 100644
index 000000000..e78355ea2
--- /dev/null
+++ b/frontends/monkey/401login.h
@@ -0,0 +1,9 @@
+
+#include <stdbool.h>
+
+#include "utils/nsurl.h"
+#include "utils/errors.h"
+
+
+void gui_401login_open(nsurl *url, const char *realm,
+ nserror (*cb)(bool proceed, void *pw), void *cbpw);
diff --git a/frontends/monkey/Makefile b/frontends/monkey/Makefile
new file mode 100644
index 000000000..86f1d912e
--- /dev/null
+++ b/frontends/monkey/Makefile
@@ -0,0 +1,55 @@
+#
+# Makefile for NetSurf monkey target
+#
+# This file is part of NetSurf
+
+# ----------------------------------------------------------------------------
+# Monkey flag setup (using pkg-config)
+# ----------------------------------------------------------------------------
+
+CWARNFLAGS += -Werror
+
+CFLAGS += -std=c99 -Dmonkey -Dnsmonkey -g \
+ -D_BSD_SOURCE \
+ -D_DEFAULT_SOURCE \
+ -D_XOPEN_SOURCE=700 \
+ -D_POSIX_C_SOURCE=200809L \
+ -D_NETBSD_SOURCE \
+ -DMONKEY_RESPATH=\"$(NETSURF_MONKEY_RESOURCES)\"
+
+LDFLAGS += -lm
+
+# ---------------------------------------------------------------------------
+# Windows flag setup
+# ---------------------------------------------------------------------------
+
+ifeq ($(HOST),Windows_NT)
+ CFLAGS += -U__STRICT_ANSI__
+endif
+
+# ----------------------------------------------------------------------------
+# Source file setup
+# ----------------------------------------------------------------------------
+
+# S_MONKEY are sources purely for the MONKEY build
+S_FRONTEND := main.c filetype.c schedule.c bitmap.c plot.c browser.c \
+ download.c 401login.c cert.c layout.c dispatch.c fetch.c
+
+
+# This is the final source build list
+# Note this is deliberately *not* expanded here as common and image
+# are not yet available
+SOURCES = $(S_COMMON) $(S_IMAGE) $(S_BROWSER) $(S_PDF) $(S_FRONTEND)
+EXETARGET := nsmonkey
+
+# ----------------------------------------------------------------------------
+# Install target
+# ----------------------------------------------------------------------------
+
+install-monkey:
+
+# ----------------------------------------------------------------------------
+# Package target
+# ----------------------------------------------------------------------------
+
+package-monkey:
diff --git a/frontends/monkey/Makefile.defaults b/frontends/monkey/Makefile.defaults
new file mode 100644
index 000000000..d6a90a3dc
--- /dev/null
+++ b/frontends/monkey/Makefile.defaults
@@ -0,0 +1,13 @@
+# ----------------------------------------------------------------------------
+# Monkey-specific options
+# ----------------------------------------------------------------------------
+
+# How did I get mixed up with this fucking monkey anyhow?
+NETSURF_MONKEY_RESOURCES := $(PREFIX)/share/netsurf/
+NETSURF_MONKEY_BIN := $(PREFIX)/bin/
+NETSURF_USE_RSVG := NO
+NETSURF_USE_NSSVG := NO
+NETSURF_USE_ROSPRITE := NO
+NETSURF_USE_HARU_PDF := NO
+
+CFLAGS += -O2
diff --git a/frontends/monkey/bitmap.c b/frontends/monkey/bitmap.c
new file mode 100644
index 000000000..5605073ba
--- /dev/null
+++ b/frontends/monkey/bitmap.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2011 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "utils/errors.h"
+#include "image/bitmap.h"
+
+#include "monkey/bitmap.h"
+
+struct bitmap {
+ void *ptr;
+ size_t rowstride;
+ int width;
+ int height;
+ unsigned int state;
+};
+
+static void *bitmap_create(int width, int height, unsigned int state)
+{
+ struct bitmap *ret = calloc(sizeof(*ret), 1);
+ if (ret == NULL)
+ return NULL;
+
+ ret->width = width;
+ ret->height = height;
+ ret->state = state;
+
+ ret->ptr = calloc(width, height * 4);
+
+ if (ret->ptr == NULL) {
+ free(ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
+static void bitmap_destroy(void *bitmap)
+{
+ struct bitmap *bmap = bitmap;
+ free(bmap->ptr);
+ free(bmap);
+}
+
+static void bitmap_set_opaque(void *bitmap, bool opaque)
+{
+ struct bitmap *bmap = bitmap;
+
+ if (opaque)
+ bmap->state |= (BITMAP_OPAQUE);
+ else
+ bmap->state &= ~(BITMAP_OPAQUE);
+}
+
+static bool bitmap_test_opaque(void *bitmap)
+{
+ return false;
+}
+
+static bool bitmap_get_opaque(void *bitmap)
+{
+ struct bitmap *bmap = bitmap;
+
+ return (bmap->state & BITMAP_OPAQUE) == BITMAP_OPAQUE;
+}
+
+static unsigned char *bitmap_get_buffer(void *bitmap)
+{
+ struct bitmap *bmap = bitmap;
+
+ return (unsigned char *)(bmap->ptr);
+}
+
+static size_t bitmap_get_rowstride(void *bitmap)
+{
+ struct bitmap *bmap = bitmap;
+ return bmap->width * 4;
+}
+
+static size_t bitmap_get_bpp(void *bitmap)
+{
+ /* OMG?! */
+ return 4;
+}
+
+static bool bitmap_save(void *bitmap, const char *path, unsigned flags)
+{
+ return true;
+}
+
+static void bitmap_modified(void *bitmap)
+{
+ struct bitmap *bmap = bitmap;
+ bmap->state |= BITMAP_MODIFIED;
+}
+
+static int bitmap_get_width(void *bitmap)
+{
+ struct bitmap *bmap = bitmap;
+ return bmap->width;
+}
+
+static int bitmap_get_height(void *bitmap)
+{
+ struct bitmap *bmap = bitmap;
+ return bmap->height;
+}
+
+static nserror bitmap_render(struct bitmap *bitmap,
+ struct hlcache_handle *content)
+{
+ fprintf(stdout, "GENERIC BITMAP RENDER\n");
+ return NSERROR_OK;
+}
+
+static struct gui_bitmap_table bitmap_table = {
+ .create = bitmap_create,
+ .destroy = bitmap_destroy,
+ .set_opaque = bitmap_set_opaque,
+ .get_opaque = bitmap_get_opaque,
+ .test_opaque = bitmap_test_opaque,
+ .get_buffer = bitmap_get_buffer,
+ .get_rowstride = bitmap_get_rowstride,
+ .get_width = bitmap_get_width,
+ .get_height = bitmap_get_height,
+ .get_bpp = bitmap_get_bpp,
+ .save = bitmap_save,
+ .modified = bitmap_modified,
+ .render = bitmap_render,
+};
+
+struct gui_bitmap_table *monkey_bitmap_table = &bitmap_table;
diff --git a/frontends/monkey/bitmap.h b/frontends/monkey/bitmap.h
new file mode 100644
index 000000000..e293ce93f
--- /dev/null
+++ b/frontends/monkey/bitmap.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2015 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_MONKEY_BITMAP_H
+#define NS_MONKEY_BITMAP_H
+
+extern struct gui_bitmap_table *monkey_bitmap_table;
+
+#endif /* NS_MONKEY_BITMAP_H */
diff --git a/frontends/monkey/browser.c b/frontends/monkey/browser.c
new file mode 100644
index 000000000..dfd2dcf6c
--- /dev/null
+++ b/frontends/monkey/browser.c
@@ -0,0 +1,527 @@
+/*
+ * Copyright 2011 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/* Browser-related callbacks */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "utils/utils.h"
+#include "utils/ring.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "desktop/mouse.h"
+#include "desktop/gui_window.h"
+#include "desktop/browser.h"
+#include "desktop/plotters.h"
+#include "content/hlcache.h"
+
+#include "monkey/browser.h"
+#include "monkey/plot.h"
+
+static uint32_t win_ctr = 0;
+
+static struct gui_window *gw_ring = NULL;
+
+/* exported function documented in monkey/browser.h */
+nserror monkey_warn_user(const char *warning, const char *detail)
+{
+ fprintf(stderr, "WARN %s %s\n", warning, detail);
+ return NSERROR_OK;
+}
+
+struct gui_window *
+monkey_find_window_by_num(uint32_t win_num)
+{
+ struct gui_window *ret = NULL;
+
+ RING_ITERATE_START(struct gui_window, gw_ring, c_ring) {
+ if (c_ring->win_num == win_num) {
+ ret = c_ring;
+ RING_ITERATE_STOP(gw_ring, c_ring);
+ }
+ } RING_ITERATE_END(gw_ring, c_ring);
+
+ return ret;
+}
+
+
+/**
+ * callback from core to reformat a window.
+ */
+static void monkey_window_reformat(struct gui_window *gw)
+{
+ browser_window_reformat(gw->bw, false, gw->width, gw->height);
+}
+
+void
+monkey_kill_browser_windows(void)
+{
+ while (gw_ring != NULL) {
+ browser_window_destroy(gw_ring->bw);
+ }
+}
+
+static struct gui_window *
+gui_window_create(struct browser_window *bw,
+ struct gui_window *existing,
+ gui_window_create_flags flags)
+{
+ struct gui_window *ret = calloc(sizeof(*ret), 1);
+ if (ret == NULL)
+ return NULL;
+
+ ret->win_num = win_ctr++;
+ ret->bw = bw;
+
+ ret->width = 800;
+ ret->height = 600;
+
+ fprintf(stdout, "WINDOW NEW WIN %u FOR %p EXISTING %p NEWTAB %s CLONE %s\n",
+ ret->win_num, bw, existing, flags & GW_CREATE_TAB ? "TRUE" : "FALSE",
+ flags & GW_CREATE_CLONE ? "TRUE" : "FALSE");
+ fprintf(stdout, "WINDOW SIZE WIN %u WIDTH %d HEIGHT %d\n",
+ ret->win_num, ret->width, ret->height);
+
+ RING_INSERT(gw_ring, ret);
+
+ return ret;
+}
+
+static void
+gui_window_destroy(struct gui_window *g)
+{
+ fprintf(stdout, "WINDOW DESTROY WIN %u\n", g->win_num);
+ RING_REMOVE(gw_ring, g);
+ free(g);
+}
+
+static void
+gui_window_set_title(struct gui_window *g, const char *title)
+{
+ fprintf(stdout, "WINDOW TITLE WIN %u STR %s\n", g->win_num, title);
+}
+
+static void
+gui_window_redraw_window(struct gui_window *g)
+{
+ fprintf(stdout, "WINDOW REDRAW WIN %u\n", g->win_num);
+}
+
+static void
+gui_window_get_dimensions(struct gui_window *g, int *width, int *height,
+ bool scaled)
+{
+ fprintf(stdout, "WINDOW GET_DIMENSIONS WIN %u WIDTH %d HEIGHT %d\n",
+ g->win_num, g->width, g->height);
+ *width = g->width;
+ *height = g->height;
+}
+
+static void
+gui_window_new_content(struct gui_window *g)
+{
+ fprintf(stdout, "WINDOW NEW_CONTENT WIN %u\n", g->win_num);
+}
+
+static void
+gui_window_set_icon(struct gui_window *g, hlcache_handle *icon)
+{
+ fprintf(stdout, "WINDOW NEW_ICON WIN %u\n", g->win_num);
+}
+
+static void
+gui_window_start_throbber(struct gui_window *g)
+{
+ fprintf(stdout, "WINDOW START_THROBBER WIN %u\n", g->win_num);
+}
+
+static void
+gui_window_stop_throbber(struct gui_window *g)
+{
+ fprintf(stdout, "WINDOW STOP_THROBBER WIN %u\n", g->win_num);
+}
+
+static void
+gui_window_set_scroll(struct gui_window *g, int sx, int sy)
+{
+ g->scrollx = sx;
+ g->scrolly = sy;
+ fprintf(stdout, "WINDOW SET_SCROLL WIN %u X %d Y %d\n", g->win_num, sx, sy);
+}
+
+static void
+gui_window_update_box(struct gui_window *g, const struct rect *rect)
+{
+ fprintf(stdout, "WINDOW UPDATE_BOX WIN %u X %d Y %d WIDTH %d HEIGHT %d\n",
+ g->win_num, rect->x0, rect->y0,
+ (rect->x1 - rect->x0), (rect->y1 - rect->y0));
+
+}
+
+static void
+gui_window_update_extent(struct gui_window *g)
+{
+ int width, height;
+
+ if (browser_window_get_extents(g->bw, false, &width, &height) != NSERROR_OK)
+ return;
+
+ fprintf(stdout, "WINDOW UPDATE_EXTENT WIN %u WIDTH %d HEIGHT %d\n",
+ g->win_num, width, height);
+}
+
+static void
+gui_window_set_status(struct gui_window *g, const char *text)
+{
+ fprintf(stdout, "WINDOW SET_STATUS WIN %u STR %s\n", g->win_num, text);
+}
+
+static void
+gui_window_set_pointer(struct gui_window *g, gui_pointer_shape shape)
+{
+ const char *ptr_name = "UNKNOWN";
+
+ switch (shape) {
+ case GUI_POINTER_POINT:
+ ptr_name = "POINT";
+ break;
+ case GUI_POINTER_CARET:
+ ptr_name = "CARET";
+ break;
+ case GUI_POINTER_UP:
+ ptr_name = "UP";
+ break;
+ case GUI_POINTER_DOWN:
+ ptr_name = "DOWN";
+ break;
+ case GUI_POINTER_LEFT:
+ ptr_name = "LEFT";
+ break;
+ case GUI_POINTER_RIGHT:
+ ptr_name = "RIGHT";
+ break;
+ case GUI_POINTER_LD:
+ ptr_name = "LD";
+ break;
+ case GUI_POINTER_RD:
+ ptr_name = "RD";
+ break;
+ case GUI_POINTER_LU:
+ ptr_name = "LU";
+ break;
+ case GUI_POINTER_RU:
+ ptr_name = "RU";
+ break;
+ case GUI_POINTER_CROSS:
+ ptr_name = "CROSS";
+ break;
+ case GUI_POINTER_MOVE:
+ ptr_name = "MOVE";
+ break;
+ case GUI_POINTER_WAIT:
+ ptr_name = "WAIT";
+ break;
+ case GUI_POINTER_HELP:
+ ptr_name = "HELP";
+ break;
+ case GUI_POINTER_MENU:
+ ptr_name = "MENU";
+ break;
+ case GUI_POINTER_PROGRESS:
+ ptr_name = "PROGRESS";
+ break;
+ case GUI_POINTER_NO_DROP:
+ ptr_name = "NO_DROP";
+ break;
+ case GUI_POINTER_NOT_ALLOWED:
+ ptr_name = "NOT_ALLOWED";
+ break;
+ case GUI_POINTER_DEFAULT:
+ ptr_name = "DEFAULT";
+ break;
+ default:
+ break;
+ }
+ fprintf(stdout, "WINDOW SET_POINTER WIN %u POINTER %s\n", g->win_num, ptr_name);
+}
+
+static nserror
+gui_window_set_url(struct gui_window *g, nsurl *url)
+{
+ fprintf(stdout, "WINDOW SET_URL WIN %u URL %s\n", g->win_num, nsurl_access(url));
+ return NSERROR_OK;
+}
+
+static bool
+gui_window_get_scroll(struct gui_window *g, int *sx, int *sy)
+{
+ fprintf(stdout, "WINDOW GET_SCROLL WIN %u X %d Y %d\n",
+ g->win_num, g->scrollx, g->scrolly);
+ *sx = g->scrollx;
+ *sy = g->scrolly;
+ return true;
+}
+
+static bool
+gui_window_scroll_start(struct gui_window *g)
+{
+ fprintf(stdout, "WINDOW SCROLL_START WIN %u\n", g->win_num);
+ g->scrollx = g->scrolly = 0;
+ return true;
+}
+
+static void
+gui_window_scroll_visible(struct gui_window *g, int x0, int y0,
+ int x1, int y1)
+{
+ fprintf(stdout, "WINDOW SCROLL_VISIBLE WIN %u X0 %d Y0 %d X1 %d Y1 %d\n",
+ g->win_num, x0, y0, x1, y1);
+}
+
+static void
+gui_window_place_caret(struct gui_window *g, int x, int y, int height,
+ const struct rect *clip)
+{
+ fprintf(stdout, "WINDOW PLACE_CARET WIN %u X %d Y %d HEIGHT %d\n",
+ g->win_num, x, y, height);
+}
+
+static void
+gui_window_remove_caret(struct gui_window *g)
+{
+ fprintf(stdout, "WINDOW REMOVE_CARET WIN %u\n", g->win_num);
+}
+
+static bool
+gui_window_drag_start(struct gui_window *g, gui_drag_type type,
+ const struct rect *rect)
+{
+ fprintf(stdout, "WINDOW SCROLL_START WIN %u TYPE %i\n", g->win_num, type);
+ return false;
+}
+
+static nserror
+gui_window_save_link(struct gui_window *g, nsurl *url, const char *title)
+{
+ fprintf(stdout, "WINDOW SAVE_LINK WIN %u URL %s TITLE %s\n",
+ g->win_num, nsurl_access(url), title);
+ return NSERROR_OK;
+}
+
+
+
+/**** Handlers ****/
+
+static void
+monkey_window_handle_new(int argc, char **argv)
+{
+ nsurl *url = NULL;
+ nserror error = NSERROR_OK;
+
+ if (argc > 3)
+ return;
+
+ if (argc == 3) {
+ error = nsurl_create(argv[2], &url);
+ }
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ if (url != NULL) {
+ nsurl_unref(url);
+ }
+ }
+ if (error != NSERROR_OK) {
+ monkey_warn_user(messages_get_errorcode(error), 0);
+ }
+}
+
+static void
+monkey_window_handle_destroy(int argc, char **argv)
+{
+ struct gui_window *gw;
+ uint32_t nr = atoi((argc > 2) ? argv[2] : "-1");
+
+ gw = monkey_find_window_by_num(nr);
+
+ if (gw == NULL) {
+ fprintf(stdout, "ERROR WINDOW NUM BAD\n");
+ } else {
+ browser_window_destroy(gw->bw);
+ }
+}
+
+static void
+monkey_window_handle_go(int argc, char **argv)
+{
+ struct gui_window *gw;
+ nsurl *url;
+ nsurl *ref_url = NULL;
+ nserror error;
+
+ if (argc < 4 || argc > 5) {
+ fprintf(stdout, "ERROR WINDOW GO ARGS BAD\n");
+ return;
+ }
+
+ gw = monkey_find_window_by_num(atoi(argv[2]));
+
+ if (gw == NULL) {
+ fprintf(stdout, "ERROR WINDOW NUM BAD\n");
+ return;
+ }
+
+ error = nsurl_create(argv[3], &url);
+ if (error == NSERROR_OK) {
+ if (argc == 5) {
+ error = nsurl_create(argv[4], &ref_url);
+ }
+
+ if (error == NSERROR_OK) {
+ error = browser_window_navigate(gw->bw,
+ url,
+ ref_url,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ if (ref_url != NULL) {
+ nsurl_unref(ref_url);
+ }
+ }
+ nsurl_unref(url);
+ }
+
+ if (error != NSERROR_OK) {
+ monkey_warn_user(messages_get_errorcode(error), 0);
+ }
+}
+
+static void
+monkey_window_handle_redraw(int argc, char **argv)
+{
+ struct gui_window *gw;
+ struct rect clip;
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = monkey_plotters
+ };
+
+ if (argc != 3 && argc != 7) {
+ fprintf(stdout, "ERROR WINDOW REDRAW ARGS BAD\n");
+ return;
+ }
+
+ gw = monkey_find_window_by_num(atoi(argv[2]));
+
+ if (gw == NULL) {
+ fprintf(stdout, "ERROR WINDOW NUM BAD\n");
+ return;
+ }
+
+ clip.x0 = 0;
+ clip.y0 = 0;
+ clip.x1 = gw->width;
+ clip.y1 = gw->height;
+
+ if (argc == 7) {
+ clip.x0 = atoi(argv[3]);
+ clip.y0 = atoi(argv[4]);
+ clip.x1 = atoi(argv[5]);
+ clip.y1 = atoi(argv[6]);
+ }
+
+ LOG("Issue redraw");
+ fprintf(stdout, "WINDOW REDRAW WIN %d START\n", atoi(argv[2]));
+ browser_window_redraw(gw->bw, gw->scrollx, gw->scrolly, &clip, &ctx);
+ fprintf(stdout, "WINDOW REDRAW WIN %d STOP\n", atoi(argv[2]));
+}
+
+static void
+monkey_window_handle_reload(int argc, char **argv)
+{
+ struct gui_window *gw;
+ if (argc != 3 && argc != 4) {
+ fprintf(stdout, "ERROR WINDOW RELOAD ARGS BAD\n");
+ }
+
+ gw = monkey_find_window_by_num(atoi(argv[2]));
+
+ if (gw == NULL) {
+ fprintf(stdout, "ERROR WINDOW NUM BAD\n");
+ } else {
+ browser_window_reload(gw->bw, argc == 4);
+ }
+}
+
+
+void
+monkey_window_handle_command(int argc, char **argv)
+{
+ if (argc == 1)
+ return;
+
+ if (strcmp(argv[1], "NEW") == 0) {
+ monkey_window_handle_new(argc, argv);
+ } else if (strcmp(argv[1], "DESTROY") == 0) {
+ monkey_window_handle_destroy(argc, argv);
+ } else if (strcmp(argv[1], "GO") == 0) {
+ monkey_window_handle_go(argc, argv);
+ } else if (strcmp(argv[1], "REDRAW") == 0) {
+ monkey_window_handle_redraw(argc, argv);
+ } else if (strcmp(argv[1], "RELOAD") == 0) {
+ monkey_window_handle_reload(argc, argv);
+ } else {
+ fprintf(stdout, "ERROR WINDOW COMMAND UNKNOWN %s\n", argv[1]);
+ }
+
+}
+
+static struct gui_window_table window_table = {
+ .create = gui_window_create,
+ .destroy = gui_window_destroy,
+ .redraw = gui_window_redraw_window,
+ .update = gui_window_update_box,
+ .get_scroll = gui_window_get_scroll,
+ .set_scroll = gui_window_set_scroll,
+ .get_dimensions = gui_window_get_dimensions,
+ .update_extent = gui_window_update_extent,
+ .reformat = monkey_window_reformat,
+
+ .set_title = gui_window_set_title,
+ .set_url = gui_window_set_url,
+ .set_icon = gui_window_set_icon,
+ .set_status = gui_window_set_status,
+ .set_pointer = gui_window_set_pointer,
+ .place_caret = gui_window_place_caret,
+ .remove_caret = gui_window_remove_caret,
+ .drag_start = gui_window_drag_start,
+ .save_link = gui_window_save_link,
+ .scroll_visible = gui_window_scroll_visible,
+ .scroll_start = gui_window_scroll_start,
+ .new_content = gui_window_new_content,
+ .start_throbber = gui_window_start_throbber,
+ .stop_throbber = gui_window_stop_throbber,
+};
+
+struct gui_window_table *monkey_window_table = &window_table;
diff --git a/frontends/monkey/browser.h b/frontends/monkey/browser.h
new file mode 100644
index 000000000..32572742a
--- /dev/null
+++ b/frontends/monkey/browser.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2011 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_MONKEY_BROWSER_H
+#define NETSURF_MONKEY_BROWSER_H
+
+extern struct gui_window_table *monkey_window_table;
+extern struct gui_download_table *monkey_download_table;
+
+struct gui_window {
+ struct gui_window *r_next;
+ struct gui_window *r_prev;
+
+ uint32_t win_num;
+ struct browser_window *bw;
+
+ int width, height;
+ int scrollx, scrolly;
+
+ char *host; /* Ignore this, it's in case RING*() gets debugging for fetchers */
+
+};
+
+struct gui_window *monkey_find_window_by_num(uint32_t win_num);
+struct gui_window *monkey_find_window_by_content(hlcache_handle *content);
+void monkey_window_process_reformats(void);
+
+void monkey_window_handle_command(int argc, char **argv);
+void monkey_kill_browser_windows(void);
+
+nserror monkey_warn_user(const char *warning, const char *detail);
+
+#endif /* NETSURF_MONKEY_BROWSER_H */
diff --git a/frontends/monkey/cert.c b/frontends/monkey/cert.c
new file mode 100644
index 000000000..ec1b1ce43
--- /dev/null
+++ b/frontends/monkey/cert.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2011 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <stdio.h>
+
+#include "utils/ring.h"
+#include "utils/nsurl.h"
+
+#include "monkey/cert.h"
+
+typedef struct monkey_cert {
+ struct monkey_cert *r_next, *r_prev;
+ uint32_t num;
+ char *host; /* Ignore */
+ nserror (*cb)(bool,void*);
+ void *pw;
+} monkey_cert_t;
+
+static monkey_cert_t *cert_ring = NULL;
+static uint32_t cert_ctr = 0;
+
+void
+gui_cert_verify(nsurl *url, const struct ssl_cert_info *certs,
+ unsigned long num, nserror (*cb)(bool proceed, void *pw),
+ void *cbpw)
+{
+ monkey_cert_t *m4t = calloc(sizeof(*m4t), 1);
+ if (m4t == NULL) {
+ cb(false, cbpw);
+ return;
+ }
+ m4t->cb = cb;
+ m4t->pw = cbpw;
+ m4t->num = cert_ctr++;
+
+ RING_INSERT(cert_ring, m4t);
+
+ fprintf(stdout, "SSLCERT VERIFY CERT %u URL %s\n",
+ m4t->num, nsurl_access(url));
+}
+
+
diff --git a/frontends/monkey/cert.h b/frontends/monkey/cert.h
new file mode 100644
index 000000000..283817f3d
--- /dev/null
+++ b/frontends/monkey/cert.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_MONKEY_CERT_H_
+#define _NETSURF_MONKEY_CERT_H_
+
+struct ssl_cert_info;
+
+void gui_cert_verify(nsurl *url, const struct ssl_cert_info *certs,
+ unsigned long num, nserror (*cb)(bool proceed, void *pw),
+ void *cbpw);
+
+#endif
diff --git a/frontends/monkey/dispatch.c b/frontends/monkey/dispatch.c
new file mode 100644
index 000000000..563534d64
--- /dev/null
+++ b/frontends/monkey/dispatch.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2011 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "utils/ring.h"
+
+#include "monkey/dispatch.h"
+
+typedef struct cmdhandler {
+ struct cmdhandler *r_next, *r_prev;
+ const char *cmd;
+ handle_command_fn fn;
+} monkey_cmdhandler_t;
+
+static monkey_cmdhandler_t *handler_ring = NULL;
+
+nserror
+monkey_register_handler(const char *cmd, handle_command_fn fn)
+{
+ monkey_cmdhandler_t *ret = calloc(sizeof(*ret), 1);
+ if (ret == NULL) {
+ LOG("Unable to allocate handler");
+ return NSERROR_NOMEM;
+ }
+ ret->cmd = strdup(cmd);
+ ret->fn = fn;
+ RING_INSERT(handler_ring, ret);
+ return NSERROR_OK;
+}
+
+void
+monkey_process_command(void)
+{
+ char buffer[PATH_MAX];
+ int argc = 0;
+ char **argv = NULL;
+ char *p, *r = NULL;
+ handle_command_fn fn = NULL;
+ char **nargv;
+
+ if (fgets(buffer, PATH_MAX, stdin) == NULL) {
+ /* end of input or read error so issue QUIT */
+ sprintf(buffer, "QUIT\n");
+ }
+
+ /* remove newline */
+ buffer[strlen(buffer) - 1] = '\0';
+
+ argv = malloc(sizeof *argv);
+ if (argv == NULL) {
+ return;
+ }
+ argc = 1;
+ *argv = buffer;
+
+ for (p = r = buffer; *p != '\0'; p++) {
+ if (*p == ' ') {
+ nargv = realloc(argv, sizeof(*argv) * (argc + 1));
+ if (nargv == NULL) {
+ /* reallocation of argument vector failed, try using what is
+ * already processed.
+ */
+ break;
+ } else {
+ argv = nargv;
+ }
+ argv[argc++] = r = p + 1;
+ *p = '\0';
+ }
+ }
+
+ RING_ITERATE_START(monkey_cmdhandler_t, handler_ring, handler) {
+ if (strcmp(argv[0], handler->cmd) == 0) {
+ fn = handler->fn;
+ RING_ITERATE_STOP(handler_ring, handler);
+ }
+ } RING_ITERATE_END(handler_ring, handler);
+
+ if (fn != NULL) {
+ fn(argc, argv);
+ }
+
+ free(argv);
+}
diff --git a/frontends/monkey/dispatch.h b/frontends/monkey/dispatch.h
new file mode 100644
index 000000000..dc6e50a0b
--- /dev/null
+++ b/frontends/monkey/dispatch.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_MONKEY_DISPATCH_H
+#define NETSURF_MONKEY_DISPATCH_H 1
+
+typedef void (*handle_command_fn)(int argc, char **argv);
+
+nserror monkey_register_handler(const char *cmd, handle_command_fn fn);
+
+void monkey_process_command(void);
+
+#endif /* NETSURF_MONKEY_DISPATCH_H */
diff --git a/frontends/monkey/download.c b/frontends/monkey/download.c
new file mode 100644
index 000000000..242136662
--- /dev/null
+++ b/frontends/monkey/download.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2011 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "utils/errors.h"
+#include "utils/ring.h"
+#include "desktop/gui_download.h"
+#include "desktop/download.h"
+#include "content/hlcache.h"
+
+#include "monkey/browser.h"
+
+static uint32_t dwin_ctr = 0;
+
+struct gui_download_window {
+ struct gui_download_window *r_next;
+ struct gui_download_window *r_prev;
+ struct gui_window *g;
+ uint32_t dwin_num;
+ char *host; /* ignore */
+};
+
+static struct gui_download_window *dw_ring = NULL;
+
+static struct gui_download_window *
+gui_download_window_create(download_context *ctx,
+ struct gui_window *parent)
+{
+ struct gui_download_window *ret = calloc(sizeof(*ret), 1);
+ if (ret == NULL)
+ return NULL;
+ ret->g = parent;
+ ret->dwin_num = dwin_ctr++;
+
+ RING_INSERT(dw_ring, ret);
+
+ fprintf(stdout, "DOWNLOAD_WINDOW CREATE DWIN %u WIN %u\n",
+ ret->dwin_num, parent->win_num);
+
+ return ret;
+}
+
+static nserror
+gui_download_window_data(struct gui_download_window *dw,
+ const char *data, unsigned int size)
+{
+ fprintf(stdout, "DOWNLOAD_WINDOW DATA DWIN %u SIZE %u DATA %s\n",
+ dw->dwin_num, size, data);
+ return NSERROR_OK;
+}
+
+static void
+gui_download_window_error(struct gui_download_window *dw,
+ const char *error_msg)
+{
+ fprintf(stdout, "DOWNLOAD_WINDOW ERROR DWIN %u ERROR %s\n",
+ dw->dwin_num, error_msg);
+}
+
+static void
+gui_download_window_done(struct gui_download_window *dw)
+{
+ fprintf(stdout, "DOWNLOAD_WINDOW DONE DWIN %u\n",
+ dw->dwin_num);
+ RING_REMOVE(dw_ring, dw);
+ free(dw);
+}
+
+static struct gui_download_table download_table = {
+ .create = gui_download_window_create,
+ .data = gui_download_window_data,
+ .error = gui_download_window_error,
+ .done = gui_download_window_done,
+};
+
+struct gui_download_table *monkey_download_table = &download_table;
diff --git a/frontends/monkey/fetch.c b/frontends/monkey/fetch.c
new file mode 100644
index 000000000..86732cfce
--- /dev/null
+++ b/frontends/monkey/fetch.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <limits.h>
+
+#include "utils/errors.h"
+#include "utils/file.h"
+#include "utils/nsurl.h"
+#include "utils/filepath.h"
+#include "desktop/gui_fetch.h"
+
+#include "monkey/filetype.h"
+#include "monkey/fetch.h"
+
+extern char **respaths;
+
+
+static nsurl *gui_get_resource_url(const char *path)
+{
+ char buf[PATH_MAX];
+ nsurl *url = NULL;
+
+ netsurf_path_to_nsurl(filepath_sfind(respaths, buf, path), &url);
+
+ return url;
+}
+
+static struct gui_fetch_table fetch_table = {
+ .filetype = monkey_fetch_filetype,
+
+ .get_resource_url = gui_get_resource_url,
+};
+
+struct gui_fetch_table *monkey_fetch_table = &fetch_table;
diff --git a/frontends/monkey/fetch.h b/frontends/monkey/fetch.h
new file mode 100644
index 000000000..f146e2ef8
--- /dev/null
+++ b/frontends/monkey/fetch.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_MONKEY_FETCH_H
+#define NS_MONKEY_FETCH_H
+
+struct gui_fetch_table *monkey_fetch_table;
+
+#endif /* NS_MONKEY_FETCH_H */
diff --git a/frontends/monkey/filetype.c b/frontends/monkey/filetype.c
new file mode 100644
index 000000000..d5517bf06
--- /dev/null
+++ b/frontends/monkey/filetype.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2007 Rob Kendrick <rjek@netsurf-browser.org>
+ * Copyright 2007 Vincent Sanders <vince@debian.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "content/fetch.h"
+#include "utils/log.h"
+#include "utils/hashtable.h"
+
+#include "monkey/filetype.h"
+
+static struct hash_table *mime_hash = NULL;
+
+void monkey_fetch_filetype_init(const char *mimefile)
+{
+ struct stat statbuf;
+ FILE *fh = NULL;
+
+ mime_hash = hash_create(117);
+
+ /* first, check to see if /etc/mime.types in preference */
+
+ if ((stat("/etc/mime.types", &statbuf) == 0) &&
+ S_ISREG(statbuf.st_mode)) {
+ mimefile = "/etc/mime.types";
+
+ }
+
+ fh = fopen(mimefile, "r");
+
+ /* Some OSes (mentioning no Solarises) have a worthlessly tiny
+ * /etc/mime.types that don't include essential things, so we
+ * pre-seed our hash with the essentials. These will get
+ * over-ridden if they are mentioned in the mime.types file.
+ */
+
+ hash_add(mime_hash, "css", "text/css");
+ hash_add(mime_hash, "htm", "text/html");
+ hash_add(mime_hash, "html", "text/html");
+ hash_add(mime_hash, "jpg", "image/jpeg");
+ hash_add(mime_hash, "jpeg", "image/jpeg");
+ hash_add(mime_hash, "gif", "image/gif");
+ hash_add(mime_hash, "png", "image/png");
+ hash_add(mime_hash, "jng", "image/jng");
+ hash_add(mime_hash, "mng", "image/mng");
+ hash_add(mime_hash, "webp", "image/webp");
+ hash_add(mime_hash, "spr", "image/x-riscos-sprite");
+
+ if (fh == NULL) {
+ LOG("Unable to open a mime.types file, so using a minimal one for you.");
+ return;
+ }
+
+ while (!feof(fh)) {
+ char line[256], *ptr, *type, *ext;
+ if (fgets(line, 256, fh) == NULL)
+ break;
+ if (!feof(fh) && line[0] != '#') {
+ ptr = line;
+
+ /* search for the first non-whitespace character */
+ while (isspace(*ptr))
+ ptr++;
+
+ /* is this line empty other than leading whitespace? */
+ if (*ptr == '\n' || *ptr == '\0')
+ continue;
+
+ type = ptr;
+
+ /* search for the first non-whitespace char or NUL or
+ * NL */
+ while (*ptr && (!isspace(*ptr)) && *ptr != '\n')
+ ptr++;
+
+ if (*ptr == '\0' || *ptr == '\n') {
+ /* this mimetype has no extensions - read next
+ * line.
+ */
+ continue;
+ }
+
+ *ptr++ = '\0';
+
+ /* search for the first non-whitespace character which
+ * will be the first filename extenion */
+ while (isspace(*ptr))
+ ptr++;
+
+ while(true) {
+ ext = ptr;
+
+ /* search for the first whitespace char or
+ * NUL or NL which is the end of the ext.
+ */
+ while (*ptr && (!isspace(*ptr)) &&
+ *ptr != '\n')
+ ptr++;
+
+ if (*ptr == '\0' || *ptr == '\n') {
+ /* special case for last extension on
+ * the line
+ */
+ *ptr = '\0';
+ hash_add(mime_hash, ext, type);
+ break;
+ }
+
+ *ptr++ = '\0';
+ hash_add(mime_hash, ext, type);
+
+ /* search for the first non-whitespace char or
+ * NUL or NL, to find start of next ext.
+ */
+ while (*ptr && (isspace(*ptr)) && *ptr != '\n')
+ ptr++;
+ }
+ }
+ }
+
+ fclose(fh);
+}
+
+void monkey_fetch_filetype_fin(void)
+{
+ hash_destroy(mime_hash);
+}
+
+/**
+ * Determine the MIME type of a local file.
+ *
+ * @note used in file fetcher
+ *
+ * \param unix_path Unix style path to file on disk
+ * \return Pointer to static MIME type string (should not be freed) not NULL.
+ * invalidated on next call to fetch_filetype.
+ */
+const char *monkey_fetch_filetype(const char *unix_path)
+{
+ struct stat statbuf;
+ char *ext;
+ const char *ptr;
+ char *lowerchar;
+ const char *type;
+ int l;
+
+ if (stat(unix_path, &statbuf) != 0) {
+ /* error calling stat, the file has probably become
+ * inacessible, this routine cannot fail so just
+ * return the default mime type.
+ */
+ return "text/plain";
+ }
+ if (S_ISDIR(statbuf.st_mode)) {
+ return "application/x-netsurf-directory";
+ }
+
+ l = strlen(unix_path);
+ if ((3 < l) && (strcasecmp(unix_path + l - 4, ",f79") == 0)) {
+ return "text/css";
+ }
+
+ if (strchr(unix_path, '.') == NULL) {
+ /* no extension anywhere! */
+ return "text/plain";
+ }
+
+ ptr = unix_path + strlen(unix_path);
+ while (*ptr != '.' && *ptr != '/')
+ ptr--;
+
+ if (*ptr != '.') {
+ return "text/plain";
+ }
+
+ ext = strdup(ptr + 1); /* skip the . */
+
+ /* the hash table only contains lower-case versions - make sure this
+ * copy is lower case too.
+ */
+ lowerchar = ext;
+ while(*lowerchar) {
+ *lowerchar = tolower(*lowerchar);
+ lowerchar++;
+ }
+
+ type = hash_get(mime_hash, ext);
+ free(ext);
+
+ return type != NULL ? type : "text/plain";
+}
diff --git a/frontends/monkey/filetype.h b/frontends/monkey/filetype.h
new file mode 100644
index 000000000..6c16db01b
--- /dev/null
+++ b/frontends/monkey/filetype.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2007 Rob Kendrick <rjek@netsurf-browser.org>
+ * Copyright 2007 Vincent Sanders <vince@debian.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+void monkey_fetch_filetype_init(const char *mimefile);
+void monkey_fetch_filetype_fin(void);
+const char *monkey_fetch_filetype(const char *unix_path);
diff --git a/frontends/monkey/layout.c b/frontends/monkey/layout.c
new file mode 100644
index 000000000..401ca158c
--- /dev/null
+++ b/frontends/monkey/layout.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2011 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * monkey implementation of font layout.
+ */
+
+#include <stddef.h>
+
+#include "utils/utf8.h"
+#include "desktop/plot_style.h"
+#include "desktop/gui_layout.h"
+
+#include "monkey/layout.h"
+
+static nserror nsfont_width(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int *width)
+{
+ *width = (fstyle->size * utf8_bounded_length(string, length)) / FONT_SIZE_SCALE;
+ return NSERROR_OK;
+}
+
+/**
+ * Find the position in a string where an x coordinate falls.
+ *
+ * \param fstyle style for this text
+ * \param string UTF-8 string to measure
+ * \param length length of string
+ * \param x x coordinate to search for
+ * \param char_offset updated to offset in string of actual_x, [0..length]
+ * \param actual_x updated to x coordinate of character closest to x
+ * \return true on success, false on error and error reported
+ */
+
+static nserror nsfont_position_in_string(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x)
+{
+ *char_offset = x / (fstyle->size / FONT_SIZE_SCALE);
+ if (*char_offset > length)
+ *char_offset = length;
+ *actual_x = *char_offset * (fstyle->size / FONT_SIZE_SCALE);
+ return NSERROR_OK;
+}
+
+
+/**
+ * Find where to split a string to make it fit a width.
+ *
+ * \param fstyle style for this text
+ * \param string UTF-8 string to measure
+ * \param length length of string, in bytes
+ * \param x width available
+ * \param char_offset updated to offset in string of actual_x, [1..length]
+ * \param actual_x updated to x coordinate of character closest to x
+ * \return true on success, false on error and error reported
+ *
+ * On exit, char_offset indicates first character after split point.
+ *
+ * Note: char_offset of 0 should never be returned.
+ *
+ * Returns:
+ * char_offset giving split point closest to x, where actual_x <= x
+ * else
+ * char_offset giving split point closest to x, where actual_x > x
+ *
+ * Returning char_offset == length means no split possible
+ */
+
+static nserror nsfont_split(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x)
+{
+ int c_off = *char_offset = x / (fstyle->size / FONT_SIZE_SCALE);
+ if (*char_offset > length) {
+ *char_offset = length;
+ } else {
+ while (*char_offset > 0) {
+ if (string[*char_offset] == ' ')
+ break;
+ (*char_offset)--;
+ }
+ if (*char_offset == 0) {
+ *char_offset = c_off;
+ while (*char_offset < length && string[*char_offset] != ' ') {
+ (*char_offset)++;
+ }
+ }
+ }
+ *actual_x = *char_offset * (fstyle->size / FONT_SIZE_SCALE);
+ return NSERROR_OK;
+}
+
+static struct gui_layout_table layout_table = {
+ .width = nsfont_width,
+ .position = nsfont_position_in_string,
+ .split = nsfont_split,
+};
+
+struct gui_layout_table *monkey_layout_table = &layout_table;
diff --git a/frontends/monkey/layout.h b/frontends/monkey/layout.h
new file mode 100644
index 000000000..1e713c27f
--- /dev/null
+++ b/frontends/monkey/layout.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2016 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_MONKEY_LAYOUT_H
+#define NS_MONKEY_LAYOUT_H
+
+extern struct gui_layout_table *monkey_layout_table;
+
+#endif /* NS_MONKEY_LAYOUT_H */
diff --git a/frontends/monkey/main.c b/frontends/monkey/main.c
new file mode 100644
index 000000000..ff70bda71
--- /dev/null
+++ b/frontends/monkey/main.c
@@ -0,0 +1,398 @@
+/*
+ * Copyright 2011 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "utils/config.h"
+#include "utils/sys_time.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/filepath.h"
+#include "utils/nsoption.h"
+#include "content/urldb.h"
+#include "content/fetchers.h"
+#include "content/fetchers/resource.h"
+#include "content/hlcache.h"
+#include "desktop/gui_misc.h"
+#include "desktop/netsurf.h"
+
+#include "monkey/dispatch.h"
+#include "monkey/browser.h"
+#include "monkey/cert.h"
+#include "monkey/401login.h"
+#include "monkey/filetype.h"
+#include "monkey/fetch.h"
+#include "monkey/schedule.h"
+#include "monkey/bitmap.h"
+#include "monkey/layout.h"
+
+/** maximum number of languages in language vector */
+#define LANGV_SIZE 32
+/** maximum length of all strings in language vector */
+#define LANGS_SIZE 4096
+
+/** resource search path vector */
+char **respaths;
+
+static bool monkey_done = false;
+
+/**
+ * Cause an abnormal program termination.
+ *
+ * \note This never returns and is intended to terminate without any cleanup.
+ *
+ * \param error The message to display to the user.
+ */
+static void die(const char * const error)
+{
+ fprintf(stderr, "DIE %s\n", error);
+ exit(EXIT_FAILURE);
+}
+
+/** obtain language from environment
+ *
+ * start with GNU extension LANGUAGE environment variable and then try
+ * POSIX variables LC_ALL, LC_MESSAGES and LANG
+ *
+ */
+static const char *get_language(void)
+{
+ const char *lang;
+
+ lang = getenv("LANGUAGE");
+ if ((lang != NULL) && (lang[0] != '\0')) {
+ return lang;
+ }
+
+ lang = getenv("LC_ALL");
+ if ((lang != NULL) && (lang[0] != '\0')) {
+ return lang;
+ }
+
+ lang = getenv("LC_MESSAGES");
+ if ((lang != NULL) && (lang[0] != '\0')) {
+ return lang;
+ }
+
+ lang = getenv("LANG");
+ if ((lang != NULL) && (lang[0] != '\0')) {
+ return lang;
+ }
+
+ return NULL;
+}
+
+
+/** provide a string vector of languages in preference order
+ *
+ * environment variables are processed to aquire a colon separated
+ * list of languages which are converted into a string vector. The
+ * vector will always have the C language as its last entry.
+ *
+ * This implementation creates an internal static representation of
+ * the vector when first called and returns that for all subsequent
+ * calls. i.e. changing the environment does not change the returned
+ * vector on repeated calls.
+ *
+ * If the environment variables have more than LANGV_SIZE languages or
+ * LANGS_SIZE bytes of data the results list will be curtailed.
+ */
+static const char * const *get_languagev(void)
+{
+ static const char *langv[LANGV_SIZE];
+ int langidx = 0; /* index of next entry in vector */
+ static char langs[LANGS_SIZE];
+ char *curp; /* next language parameter in langs string */
+ const char *lange; /* language from environment variable */
+ int lang_len;
+ char *cln; /* colon in lange */
+
+ /* return cached vector */
+ if (langv[0] != NULL) {
+ return &langv[0];
+ }
+
+ curp = &langs[0];
+
+ lange = get_language();
+
+ if (lange != NULL) {
+ lang_len = strlen(lange) + 1;
+ if (lang_len < (LANGS_SIZE - 2)) {
+ memcpy(curp, lange, lang_len);
+ while ((curp[0] != 0) &&
+ (langidx < (LANGV_SIZE - 2))) {
+ /* avoid using strchrnul as it is not portable */
+ cln = strchr(curp, ':');
+ if (cln == NULL) {
+ langv[langidx++] = curp;
+ curp += lang_len;
+ break;
+ } else {
+ if ((cln - curp) > 1) {
+ /* only place non empty entries in vector */
+ langv[langidx++] = curp;
+ }
+ *cln++ = 0; /* null terminate */
+ lang_len -= (cln - curp);
+ curp = cln;
+ }
+ }
+ }
+ }
+
+ /* ensure C language is present */
+ langv[langidx++] = curp;
+ *curp++ = 'C';
+ *curp++ = 0;
+ langv[langidx] = NULL;
+
+ return &langv[0];
+}
+
+/* Stolen from gtk/gui.c */
+static char **
+nsmonkey_init_resource(const char *resource_path)
+{
+ const char * const *langv;
+ char **pathv; /* resource path string vector */
+ char **respath; /* resource paths vector */
+
+ pathv = filepath_path_to_strvec(resource_path);
+
+ langv = get_languagev();
+
+ respath = filepath_generate(pathv, langv);
+
+ filepath_free_strvec(pathv);
+
+ return respath;
+}
+
+static void monkey_quit(void)
+{
+ urldb_save_cookies(nsoption_charp(cookie_jar));
+ urldb_save(nsoption_charp(url_file));
+ monkey_fetch_filetype_fin();
+}
+
+static nserror gui_launch_url(struct nsurl *url)
+{
+ fprintf(stdout, "GENERIC LAUNCH URL %s\n", nsurl_access(url));
+ return NSERROR_OK;
+}
+
+static void quit_handler(int argc, char **argv)
+{
+ monkey_done = true;
+}
+
+/**
+ * Set option defaults for monkey frontend
+ *
+ * @param defaults The option table to update.
+ * @return error status.
+ */
+static nserror set_defaults(struct nsoption_s *defaults)
+{
+ /* Set defaults for absent option strings */
+ nsoption_setnull_charp(cookie_file, strdup("~/.netsurf/Cookies"));
+ nsoption_setnull_charp(cookie_jar, strdup("~/.netsurf/Cookies"));
+ nsoption_setnull_charp(url_file, strdup("~/.netsurf/URLs"));
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Ensures output logging stream is correctly configured
+ */
+static bool nslog_stream_configure(FILE *fptr)
+{
+ /* set log stream to be non-buffering */
+ setbuf(fptr, NULL);
+
+ return true;
+}
+
+static struct gui_misc_table monkey_misc_table = {
+ .schedule = monkey_schedule,
+ .warning = monkey_warn_user,
+
+ .quit = monkey_quit,
+ .launch_url = gui_launch_url,
+ .cert_verify = gui_cert_verify,
+ .login = gui_401login_open,
+};
+
+static void monkey_run(void)
+{
+ fd_set read_fd_set, write_fd_set, exc_fd_set;
+ int max_fd;
+ int rdy_fd;
+ int schedtm;
+ struct timeval tv;
+ struct timeval* timeout;
+
+ while (!monkey_done) {
+
+ /* clears fdset */
+ fetcher_fdset(&read_fd_set, &write_fd_set, &exc_fd_set, &max_fd);
+
+ /* add stdin to the set */
+ if (max_fd < 0) {
+ max_fd = 0;
+ }
+ FD_SET(0, &read_fd_set);
+ FD_SET(0, &exc_fd_set);
+
+ /* discover the next scheduled event time */
+ schedtm = monkey_schedule_run();
+
+ /* setup timeout */
+ switch (schedtm) {
+ case -1:
+ LOG("Iterate blocking");
+ fprintf(stdout, "GENERIC POLL BLOCKING\n");
+ timeout = NULL;
+ break;
+
+ case 0:
+ LOG("Iterate immediate");
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ timeout = &tv;
+ break;
+
+ default:
+ LOG("Iterate non-blocking");
+ fprintf(stdout, "GENERIC POLL TIMED\n");
+ tv.tv_sec = schedtm / 1000; /* miliseconds to seconds */
+ tv.tv_usec = (schedtm % 1000) * 1000; /* remainder to microseconds */
+ timeout = &tv;
+ break;
+ }
+
+ rdy_fd = select(max_fd + 1,
+ &read_fd_set,
+ &write_fd_set,
+ &exc_fd_set,
+ timeout);
+ if (rdy_fd < 0) {
+ monkey_done = true;
+ } else if (rdy_fd > 0) {
+ if (FD_ISSET(0, &read_fd_set)) {
+ monkey_process_command();
+ }
+ }
+ }
+
+}
+
+int
+main(int argc, char **argv)
+{
+ char *messages;
+ char *options;
+ char buf[PATH_MAX];
+ nserror ret;
+ struct netsurf_table monkey_table = {
+ .misc = &monkey_misc_table,
+ .window = monkey_window_table,
+ .download = monkey_download_table,
+ .fetch = monkey_fetch_table,
+ .bitmap = monkey_bitmap_table,
+ .layout = monkey_layout_table,
+ };
+
+ ret = netsurf_register(&monkey_table);
+ if (ret != NSERROR_OK) {
+ die("NetSurf operation table failed registration");
+ }
+
+ /* Unbuffer stdin/out/err */
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+
+ /* Prep the search paths */
+ respaths = nsmonkey_init_resource("${HOME}/.netsurf/:${NETSURFRES}:"MONKEY_RESPATH":./monkey/res");
+
+ /* initialise logging. Not fatal if it fails but not much we can do
+ * about it either.
+ */
+ nslog_init(nslog_stream_configure, &argc, argv);
+
+ /* user options setup */
+ ret = nsoption_init(set_defaults, &nsoptions, &nsoptions_default);
+ if (ret != NSERROR_OK) {
+ die("Options failed to initialise");
+ }
+ options = filepath_find(respaths, "Choices");
+ nsoption_read(options, nsoptions);
+ free(options);
+ nsoption_commandline(&argc, argv, nsoptions);
+
+ messages = filepath_find(respaths, "Messages");
+ ret = messages_add_from_file(messages);
+ if (ret != NSERROR_OK) {
+ LOG("Messages failed to load");
+ }
+
+ /* common initialisation */
+ ret = netsurf_init(NULL);
+ free(messages);
+ if (ret != NSERROR_OK) {
+ die("NetSurf failed to initialise");
+ }
+
+ filepath_sfinddef(respaths, buf, "mime.types", "/etc/");
+ monkey_fetch_filetype_init(buf);
+
+ urldb_load(nsoption_charp(url_file));
+ urldb_load_cookies(nsoption_charp(cookie_file));
+
+ ret = monkey_register_handler("QUIT", quit_handler);
+ if (ret != NSERROR_OK) {
+ die("quit handler failed to register");
+ }
+
+ ret = monkey_register_handler("WINDOW", monkey_window_handle_command);
+ if (ret != NSERROR_OK) {
+ die("window handler fialed to register");
+ }
+
+ fprintf(stdout, "GENERIC STARTED\n");
+ monkey_run();
+
+ fprintf(stdout, "GENERIC CLOSING_DOWN\n");
+ monkey_kill_browser_windows();
+
+ netsurf_exit();
+ fprintf(stdout, "GENERIC FINISHED\n");
+
+ /* finalise options */
+ nsoption_finalise(nsoptions, nsoptions_default);
+
+ return 0;
+}
diff --git a/frontends/monkey/options.h b/frontends/monkey/options.h
new file mode 100644
index 000000000..57cce7e1f
--- /dev/null
+++ b/frontends/monkey/options.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_MONKEY_OPTIONS_H_
+#define _NETSURF_MONKEY_OPTIONS_H_
+
+/* currently nothing here */
+
+#endif
+
+NSOPTION_BOOL(render_resample, true)
+NSOPTION_BOOL(downloads_clear, false)
+NSOPTION_BOOL(request_overwrite, true)
+NSOPTION_STRING(downloads_directory, NULL)
+NSOPTION_STRING(url_file, NULL)
+NSOPTION_BOOL(show_single_tab, false)
+NSOPTION_INTEGER(button_type, 0)
+NSOPTION_BOOL(disable_popups, false)
+NSOPTION_BOOL(disable_plugins, false)
+NSOPTION_INTEGER(history_age, 0)
+NSOPTION_BOOL(hover_urls, false)
+NSOPTION_BOOL(focus_new, false)
+NSOPTION_BOOL(new_blank, false)
+NSOPTION_STRING(hotlist_path, NULL)
+NSOPTION_BOOL(source_tab, false)
+NSOPTION_INTEGER(current_theme, 0)
+
+
diff --git a/frontends/monkey/plot.c b/frontends/monkey/plot.c
new file mode 100644
index 000000000..50f812480
--- /dev/null
+++ b/frontends/monkey/plot.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2011 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdio.h>
+
+#include "utils/utils.h"
+#include "desktop/plotters.h"
+
+static bool
+monkey_plot_disc(int x, int y, int radius, const plot_style_t *style)
+{
+ return true;
+}
+
+static bool
+monkey_plot_arc(int x, int y, int radius, int angle1, int angle2, const plot_style_t *style)
+{
+ return true;
+}
+
+static bool
+monkey_plot_polygon(const int *p, unsigned int n, const plot_style_t *style)
+{
+ return true;
+}
+
+
+static bool
+monkey_plot_text(int x, int y, const char *text, size_t length,
+ const plot_font_style_t *fstyle)
+{
+ fprintf(stdout, "PLOT TEXT X %d Y %d STR %*s\n", x, y, (int)length, text);
+ return true;
+}
+
+static bool
+monkey_plot_bitmap(int x, int y,
+ int width, int height,
+ struct bitmap *bitmap, colour bg,
+ bitmap_flags_t flags)
+{
+ fprintf(stdout, "PLOT BITMAP X %d Y %d WIDTH %d HEIGHT %d\n",
+ x, y, width, height);
+ return true;
+}
+
+static bool
+monkey_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ fprintf(stdout, "PLOT RECT X0 %d Y0 %d X1 %d Y1 %d\n",
+ x0, y0, x1, y1);
+ return true;
+}
+
+static bool
+monkey_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ fprintf(stdout, "PLOT LINE X0 %d Y0 %d X1 %d Y1 %d\n",
+ x0, y0, x1, y1);
+ return true;
+}
+
+
+static bool
+monkey_plot_path(const float *p,
+ unsigned int n,
+ colour fill,
+ float width,
+ colour c,
+ const float transform[6])
+{
+ return true;
+}
+
+static bool
+monkey_plot_clip(const struct rect *clip)
+{
+ fprintf(stdout, "PLOT CLIP X0 %d Y0 %d X1 %d Y1 %d\n",
+ clip->x0, clip->y0, clip->x1, clip->y1);
+ return true;
+}
+
+static const struct plotter_table plotters = {
+ .clip = monkey_plot_clip,
+ .arc = monkey_plot_arc,
+ .disc = monkey_plot_disc,
+ .line = monkey_plot_line,
+ .rectangle = monkey_plot_rectangle,
+ .polygon = monkey_plot_polygon,
+ .path = monkey_plot_path,
+ .bitmap = monkey_plot_bitmap,
+ .text = monkey_plot_text,
+ .option_knockout = true,
+};
+
+const struct plotter_table* monkey_plotters = &plotters;
diff --git a/frontends/monkey/plot.h b/frontends/monkey/plot.h
new file mode 100644
index 000000000..3632bcf4a
--- /dev/null
+++ b/frontends/monkey/plot.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2016 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NS_MONKEY_PLOT_H
+#define NS_MONKEY_PLOT_H
+
+struct plotter_table;
+
+extern const struct plotter_table *monkey_plotters;
+
+#endif
diff --git a/frontends/monkey/res b/frontends/monkey/res
new file mode 120000
index 000000000..ea9dff35c
--- /dev/null
+++ b/frontends/monkey/res
@@ -0,0 +1 @@
+../gtk/res \ No newline at end of file
diff --git a/frontends/monkey/schedule.c b/frontends/monkey/schedule.c
new file mode 100644
index 000000000..8c638c0b9
--- /dev/null
+++ b/frontends/monkey/schedule.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2012 Vincent Sanders <vince@netsurf-browser.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <time.h>
+#include <stdlib.h>
+
+#include "utils/sys_time.h"
+#include "utils/log.h"
+
+#include "monkey/schedule.h"
+
+#ifdef DEBUG_SCHEDULER
+#define SRLOG(x...) LOG(x)
+#else
+#define SRLOG(x...) ((void) 0)
+#endif
+
+/* linked list of scheduled callbacks */
+static struct nscallback *schedule_list = NULL;
+
+/**
+ * scheduled callback.
+ */
+struct nscallback
+{
+ struct nscallback *next;
+ struct timeval tv;
+ void (*callback)(void *p);
+ void *p;
+};
+
+/**
+ * Unschedule a callback.
+ *
+ * \param callback callback function
+ * \param p user parameter, passed to callback function
+ *
+ * All scheduled callbacks matching both callback and p are removed.
+ */
+static nserror schedule_remove(void (*callback)(void *p), void *p)
+{
+ struct nscallback *cur_nscb;
+ struct nscallback *prev_nscb;
+ struct nscallback *unlnk_nscb;
+
+ /* check there is something on the list to remove */
+ if (schedule_list == NULL) {
+ return NSERROR_OK;
+ }
+
+ SRLOG("removing %p, %p", callback, p);
+
+ cur_nscb = schedule_list;
+ prev_nscb = NULL;
+
+ while (cur_nscb != NULL) {
+ if ((cur_nscb->callback == callback) &&
+ (cur_nscb->p == p)) {
+ /* item to remove */
+
+ SRLOG("callback entry %p removing %p(%p)",
+ cur_nscb, cur_nscb->callback, cur_nscb->p);
+
+ /* remove callback */
+ unlnk_nscb = cur_nscb;
+ cur_nscb = unlnk_nscb->next;
+
+ if (prev_nscb == NULL) {
+ schedule_list = cur_nscb;
+ } else {
+ prev_nscb->next = cur_nscb;
+ }
+ free (unlnk_nscb);
+ } else {
+ /* move to next element */
+ prev_nscb = cur_nscb;
+ cur_nscb = prev_nscb->next;
+ }
+ }
+
+ return NSERROR_OK;
+}
+
+/* exported function documented in framebuffer/schedule.h */
+nserror monkey_schedule(int tival, void (*callback)(void *p), void *p)
+{
+ struct nscallback *nscb;
+ struct timeval tv;
+ nserror ret;
+
+ /* ensure uniqueness of the callback and context */
+ ret = schedule_remove(callback, p);
+ if ((tival < 0) || (ret != NSERROR_OK)) {
+ return ret;
+ }
+
+ SRLOG("Adding %p(%p) in %d", callback, p, tival);
+
+ tv.tv_sec = tival / 1000; /* miliseconds to seconds */
+ tv.tv_usec = (tival % 1000) * 1000; /* remainder to microseconds */
+
+ nscb = calloc(1, sizeof(struct nscallback));
+
+ gettimeofday(&nscb->tv, NULL);
+ timeradd(&nscb->tv, &tv, &nscb->tv);
+
+ nscb->callback = callback;
+ nscb->p = p;
+
+ /* add to list front */
+ nscb->next = schedule_list;
+ schedule_list = nscb;
+
+ return NSERROR_OK;
+}
+
+/* exported function documented in framebuffer/schedule.h */
+int monkey_schedule_run(void)
+{
+ struct timeval tv;
+ struct timeval nexttime;
+ struct timeval rettime;
+ struct nscallback *cur_nscb;
+ struct nscallback *prev_nscb;
+ struct nscallback *unlnk_nscb;
+
+ if (schedule_list == NULL)
+ return -1;
+
+ /* reset enumeration to the start of the list */
+ cur_nscb = schedule_list;
+ prev_nscb = NULL;
+ nexttime = cur_nscb->tv;
+
+ gettimeofday(&tv, NULL);
+
+ while (cur_nscb != NULL) {
+ if (timercmp(&tv, &cur_nscb->tv, >)) {
+ /* scheduled time */
+
+ /* remove callback */
+ unlnk_nscb = cur_nscb;
+
+ if (prev_nscb == NULL) {
+ schedule_list = unlnk_nscb->next;
+ } else {
+ prev_nscb->next = unlnk_nscb->next;
+ }
+
+ unlnk_nscb->callback(unlnk_nscb->p);
+
+ free(unlnk_nscb);
+
+ /* need to deal with callback modifying the list. */
+ if (schedule_list == NULL)
+ return -1; /* no more callbacks scheduled */
+
+ /* reset enumeration to the start of the list */
+ cur_nscb = schedule_list;
+ prev_nscb = NULL;
+ nexttime = cur_nscb->tv;
+ } else {
+ /* if the time to the event is sooner than the
+ * currently recorded soonest event record it
+ */
+ if (timercmp(&nexttime, &cur_nscb->tv, >)) {
+ nexttime = cur_nscb->tv;
+ }
+ /* move to next element */
+ prev_nscb = cur_nscb;
+ cur_nscb = prev_nscb->next;
+ }
+ }
+
+ /* make rettime relative to now */
+ timersub(&nexttime, &tv, &rettime);
+
+ SRLOG("returning time to next event as %ldms",
+ (rettime.tv_sec * 1000) + (rettime.tv_usec / 1000));
+
+ /* return next event time in milliseconds (24days max wait) */
+ return (rettime.tv_sec * 1000) + (rettime.tv_usec / 1000);
+}
+
+void monkey_schedule_list(void)
+{
+ struct timeval tv;
+ struct nscallback *cur_nscb;
+
+ gettimeofday(&tv, NULL);
+
+ LOG("schedule list at %lld:%ld", (long long)tv.tv_sec, tv.tv_usec);
+
+ cur_nscb = schedule_list;
+
+ while (cur_nscb != NULL) {
+ LOG("Schedule %p at %lld:%ld",
+ cur_nscb, (long long)cur_nscb->tv.tv_sec, cur_nscb->tv.tv_usec);
+ cur_nscb = cur_nscb->next;
+ }
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset:2
+ * End:
+ */
diff --git a/frontends/monkey/schedule.h b/frontends/monkey/schedule.h
new file mode 100644
index 000000000..14fad8247
--- /dev/null
+++ b/frontends/monkey/schedule.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 NETSURF_MONKEY_SCHEDULE_H
+#define NETSURF_MONKEY_SCHEDULE_H
+
+/**
+ * Schedule a callback.
+ *
+ * \param tival interval before the callback should be made in ms
+ * \param callback callback function
+ * \param p user parameter, passed to callback function
+ *
+ * The callback function will be called as soon as possible after t ms have
+ * passed.
+ */
+
+nserror monkey_schedule(int tival, void (*callback)(void *p), void *p);
+
+/**
+ * Process scheduled callbacks up to current time.
+ *
+ * @return The number of milliseconds untill the next scheduled event
+ * or -1 for no event.
+ */
+int monkey_schedule_run(void);
+
+/**
+ * Log a list of all scheduled callbacks.
+ */
+void monkey_schedule_list(void);
+
+#endif
diff --git a/frontends/riscos/401login.c b/frontends/riscos/401login.c
new file mode 100644
index 000000000..e95a74809
--- /dev/null
+++ b/frontends/riscos/401login.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "utils/config.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+#include <oslib/wimp.h>
+
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "content/content.h"
+#include "content/hlcache.h"
+#include "content/urldb.h"
+#include "desktop/browser.h"
+
+#include "riscos/gui.h"
+#include "riscos/dialog.h"
+#include "riscos/wimp_event.h"
+
+#define ICON_401LOGIN_LOGIN 0
+#define ICON_401LOGIN_CANCEL 1
+#define ICON_401LOGIN_HOST 2
+#define ICON_401LOGIN_REALM 3
+#define ICON_401LOGIN_USERNAME 4
+#define ICON_401LOGIN_PASSWORD 5
+
+static void ro_gui_401login_close(wimp_w w);
+static bool ro_gui_401login_apply(wimp_w w);
+static void ro_gui_401login_open(nsurl *url, lwc_string *host,
+ const char *realm,
+ nserror (*cb)(bool proceed, void *pw), void *cbpw);
+
+static wimp_window *dialog_401_template;
+
+struct session_401 {
+ lwc_string *host; /**< Host for user display */
+ char *realm; /**< Authentication realm */
+ char uname[256]; /**< Buffer for username */
+ nsurl *url; /**< URL being fetched */
+ char pwd[256]; /**< Buffer for password */
+ nserror (*cb)(bool proceed, void *pw); /**< Continuation callback */
+ void *cbpw; /**< Continuation callback data */
+};
+
+
+/**
+ * Load the 401 login window template.
+ */
+
+void ro_gui_401login_init(void)
+{
+ dialog_401_template = ro_gui_dialog_load_template("login");
+}
+
+
+/**
+ * Open the login dialog
+ */
+void gui_401login_open(nsurl *url, const char *realm,
+ nserror (*cb)(bool proceed, void *pw), void *cbpw)
+{
+ lwc_string *host = nsurl_get_component(url, NSURL_HOST);
+ assert(host != NULL);
+
+ ro_gui_401login_open(url, host, realm, cb, cbpw);
+
+ lwc_string_unref(host);
+}
+
+
+/**
+ * Open a 401 login window.
+ */
+
+void ro_gui_401login_open(nsurl *url, lwc_string *host, const char *realm,
+ nserror (*cb)(bool proceed, void *pw), void *cbpw)
+{
+ struct session_401 *session;
+ wimp_w w;
+ const char *auth;
+
+ session = calloc(1, sizeof(struct session_401));
+ if (!session) {
+ ro_warn_user("NoMemory", 0);
+ return;
+ }
+
+ session->url = nsurl_ref(url);
+ if (realm == NULL)
+ realm = "Secure Area";
+ auth = urldb_get_auth_details(session->url, realm);
+ if (auth == NULL) {
+ session->uname[0] = '\0';
+ session->pwd[0] = '\0';
+ } else {
+ const char *pwd;
+ size_t pwd_len;
+
+ pwd = strchr(auth, ':');
+ assert(pwd && pwd < auth + sizeof(session->uname));
+ memcpy(session->uname, auth, pwd - auth);
+ session->uname[pwd - auth] = '\0';
+ ++pwd;
+ pwd_len = strlen(pwd);
+ assert(pwd_len < sizeof(session->pwd));
+ memcpy(session->pwd, pwd, pwd_len);
+ session->pwd[pwd_len] = '\0';
+ }
+ session->host = lwc_string_ref(host);
+ session->realm = strdup(realm);
+ session->cb = cb;
+ session->cbpw = cbpw;
+
+ if (!session->realm) {
+ nsurl_unref(session->url);
+ lwc_string_unref(session->host);
+ free(session);
+ ro_warn_user("NoMemory", 0);
+ return;
+ }
+
+ /* fill in download window icons */
+ dialog_401_template->icons[ICON_401LOGIN_HOST].data.
+ indirected_text.text =
+ (char *)lwc_string_data(session->host);
+ dialog_401_template->icons[ICON_401LOGIN_HOST].data.
+ indirected_text.size =
+ lwc_string_length(session->host) + 1;
+ dialog_401_template->icons[ICON_401LOGIN_REALM].data.
+ indirected_text.text = session->realm;
+ dialog_401_template->icons[ICON_401LOGIN_REALM].data.
+ indirected_text.size = strlen(session->realm) + 1;
+ dialog_401_template->icons[ICON_401LOGIN_USERNAME].data.
+ indirected_text.text = session->uname;
+ dialog_401_template->icons[ICON_401LOGIN_USERNAME].data.
+ indirected_text.size = sizeof(session->uname);
+ dialog_401_template->icons[ICON_401LOGIN_PASSWORD].data.
+ indirected_text.text = session->pwd;
+ dialog_401_template->icons[ICON_401LOGIN_PASSWORD].data.
+ indirected_text.size = sizeof(session->pwd);
+
+ /* create and open the window */
+ w = wimp_create_window(dialog_401_template);
+
+ ro_gui_wimp_event_register_text_field(w, ICON_401LOGIN_USERNAME);
+ ro_gui_wimp_event_register_text_field(w, ICON_401LOGIN_PASSWORD);
+ ro_gui_wimp_event_register_cancel(w, ICON_401LOGIN_CANCEL);
+ ro_gui_wimp_event_register_ok(w, ICON_401LOGIN_LOGIN,
+ ro_gui_401login_apply);
+ ro_gui_wimp_event_register_close_window(w, ro_gui_401login_close);
+ ro_gui_wimp_event_set_user_data(w, session);
+
+ ro_gui_dialog_open_persistent(NULL, w, false);
+}
+
+/**
+ * Handle closing of login dialog
+ */
+void ro_gui_401login_close(wimp_w w)
+{
+ os_error *error;
+ struct session_401 *session;
+
+ session = (struct session_401 *)ro_gui_wimp_event_get_user_data(w);
+
+ assert(session);
+
+ /* If ok didn't happen, send failure response */
+ if (session->cb != NULL)
+ session->cb(false, session->cbpw);
+
+ nsurl_unref(session->url);
+ lwc_string_unref(session->host);
+ free(session->realm);
+ free(session);
+
+ error = xwimp_delete_window(w);
+ if (error) {
+ LOG("xwimp_delete_window: 0x%x:%s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ ro_gui_wimp_event_finalise(w);
+}
+
+
+/* Login Clicked -> create a new fetch request, specifying uname & pwd
+ * CURLOPT_USERPWD takes a string "username:password"
+ */
+bool ro_gui_401login_apply(wimp_w w)
+{
+ struct session_401 *session;
+ char *auth;
+
+ session = (struct session_401 *)ro_gui_wimp_event_get_user_data(w);
+
+ assert(session);
+
+ auth = malloc(strlen(session->uname) + strlen(session->pwd) + 2);
+ if (!auth) {
+ LOG("calloc failed");
+ ro_warn_user("NoMemory", 0);
+ return false;
+ }
+
+ sprintf(auth, "%s:%s", session->uname, session->pwd);
+
+ urldb_set_auth_details(session->url, session->realm, auth);
+
+ free(auth);
+
+ session->cb(true, session->cbpw);
+
+ /* Flag that we sent response by invalidating callback details */
+ session->cb = NULL;
+ session->cbpw = NULL;
+
+ return true;
+}
+
diff --git a/frontends/riscos/Makefile b/frontends/riscos/Makefile
new file mode 100644
index 000000000..6ed076800
--- /dev/null
+++ b/frontends/riscos/Makefile
@@ -0,0 +1,153 @@
+#
+# Makefile for NetSurf RISC OS target
+#
+# This file is part of NetSurf
+
+# ----------------------------------------------------------------------------
+# RISC OS target setup
+# ----------------------------------------------------------------------------
+
+$(eval $(call pkg_config_find_and_add,libcares,Cares))
+
+$(eval $(call feature_enabled,DRAW,-DWITH_DRAW,,Drawfile rendering))
+$(eval $(call feature_enabled,SPRITE,-DWITH_SPRITE,,Sprite rendering))
+$(eval $(call feature_enabled,ARTWORKS,-DWITH_ARTWORKS,,ArtWorks rendering))
+$(eval $(call feature_enabled,DRAW_EXPORT,-DWITH_DRAW_EXPORT,-lpencil,Drawfile export))
+
+
+TPD_RISCOS = $(foreach TPL,$(notdir $(TPL_RISCOS)), \
+ !NetSurf/Resources/$(TPL)/Templates$(TPLEXT))
+
+RESOURCES = $(TPD_RISCOS)
+
+CFLAGS += -Driscos -std=c99 -D_BSD_SOURCE -D_POSIX_C_SOURCE \
+ -mpoke-function-name -fno-strict-aliasing
+
+CFLAGS += -I$(GCCSDK_INSTALL_ENV)/include
+ifeq ($(HOST),riscos)
+ CFLAGS += -I<OSLib$$Dir> -mthrowback
+endif
+ASFLAGS += -xassembler-with-cpp -I. -I$(GCCSDK_INSTALL_ENV)/include
+LDFLAGS += -L$(GCCSDK_INSTALL_ENV)/lib -lrufl
+ifeq ($(HOST),riscos)
+ LDFLAGS += -LOSLib: -lOSLib32
+else
+ LDFLAGS += -lOSLib32
+ ifeq ($(SUBTARGET),-elf)
+ # Go for static builds & AIF binary at the moment:
+ CFLAGS += -static
+ LDFLAGS += -static
+ EXEEXT := ,ff8
+ endif
+endif
+
+# ----------------------------------------------------------------------------
+# Source file setup
+# ----------------------------------------------------------------------------
+
+# S_RISCOS are sources purely for the RISC OS build
+S_FRONTEND := 401login.c assert.c bitmap.c buffer.c cookies.c configure.c \
+ dialog.c download.c filetype.c font.c \
+ global_history.c gui.c help.c history.c hotlist.c iconbar.c \
+ image.c menus.c message.c mouse.c palettes.c plotters.c \
+ print.c query.c save.c save_draw.c save_pdf.c schedule.c \
+ search.c searchweb.c sslcert.c textarea.c \
+ textselection.c theme.c theme_install.c toolbar.c \
+ treeview.c ucstables.c uri.c url_complete.c url_protocol.c \
+ url_suggest.c wimp.c wimp_event.c window.c \
+ $(addprefix content-handlers/,artworks.c awrender.s draw.c \
+ sprite.c) \
+ $(addprefix gui/,button_bar.c progress_bar.c status_bar.c \
+ throbber.c url_bar.c) \
+ $(addprefix configure/,con_cache.c con_connect.c con_content.c \
+ con_fonts.c con_home.c con_image.c con_inter.c con_language.c \
+ con_secure.c con_theme.c)
+
+# This is the final source build list
+# Note this is deliberately *not* expanded here as common and image
+# are not yet available
+SOURCES = $(S_COMMON) $(S_IMAGE) $(S_BROWSER) $(S_FRONTEND)
+
+EXETARGET := !NetSurf/!RunImage$(EXEEXT)
+
+# The filter and target for split messages
+MESSAGES_FILTER=ro
+
+!NetSurf/!Run$(RUNEXT): $(FRONTEND_SOURCE_DIR)/scripts/Run $(EXETARGET)
+ $(VQ)echo " MAKERUN: $@"
+ $(Q)$(MAKERUN) $(EXETARGET) $< $@
+
+!NetSurf/!Help$(RUNEXT): $(FRONTEND_SOURCE_DIR)/scripts/Help
+ $(VQ)echo " CP: $@"
+ $(Q)cp $< $@
+
+$(DEPROOT)/squeeze.d: $(EXETARGET)
+ $(VQ)echo " SQUEEZE: $<"
+ $(Q)$(SQUEEZE) -f -v $(EXETARGET)
+ $(Q)$(TOUCH) $@
+
+POSTEXES += !NetSurf/!Run$(RUNEXT) !NetSurf/!Help$(RUNEXT) $(DEPROOT)/squeeze.d
+
+
+clean-run:
+ $(VQ)echo " CLEAN: !NetSurf/!Run$(RUNEXT)"
+ $(Q) $(RM) !NetSurf/!Run$(RUNEXT)
+
+clean-help:
+ $(VQ)echo " CLEAN: !NetSurf/!Help$(RUNEXT)"
+ $(Q) $(RM) !NetSurf/!Help$(RUNEXT)
+
+CLEANS += clean-run clean-help
+
+# ----------------------------------------------------------------------------
+# Template targets
+# ----------------------------------------------------------------------------
+
+# Template objects
+TPL_RISCOS := de en fr nl # TODO: It'd be nice to auto-detect these
+TPL_RISCOS := $(addprefix $(FRONTEND_SOURCE_DIR)/templates/,$(TPL_RISCOS))
+
+# Template target creation macro
+define compile_template
+!NetSurf/Resources/$(1)/Templates$$(TPLEXT): $(2)
+ $$(VQ)echo "TEMPLATE: $(2)"
+ $$(Q)mkdir -p !NetSurf/Resources/$(1)
+ $$(Q)$$(CC) -x c -E -P $$(CFLAGS) $(2) | $$(CCRES) - $$@
+
+CLEAN_TEMPLATES += !NetSurf/Resources/$(1)/Templates$$(TPLEXT)
+
+endef
+
+$(eval $(foreach TPL,$(TPL_RISCOS), \
+ $(call compile_template,$(notdir $(TPL)),$(TPL))))
+
+clean-templates:
+ $(VQ)echo " CLEAN: $(CLEAN_TEMPLATES)"
+ $(Q)$(RM) $(CLEAN_TEMPLATES)
+CLEANS += clean-templates
+
+
+# ----------------------------------------------------------------------------
+# Install target
+# ----------------------------------------------------------------------------
+
+install-riscos:
+
+# ----------------------------------------------------------------------------
+# Package target
+# ----------------------------------------------------------------------------
+
+package-riscos: netsurf.zip
+
+netsurf.zip: $(EXETARGET)
+ $(eval $@_TMPDIR := $(shell mktemp -d))
+ $(Q) $(RM) $@
+ $(Q) rsync --archive --verbose $(CURDIR)/!NetSurf $($@_TMPDIR)
+ $(Q) $(CURDIR)/utils/git-date.sh $(FRONTEND_SOURCE_DIR)/distribution
+ $(Q) rsync --archive --verbose $(FRONTEND_SOURCE_DIR)/distribution/!Boot $($@_TMPDIR)
+ $(Q) rsync --archive --verbose $(FRONTEND_SOURCE_DIR)/distribution/!System $($@_TMPDIR)
+ $(Q) rsync --archive --verbose $(FRONTEND_SOURCE_DIR)/distribution/3rdParty $($@_TMPDIR)
+ $(Q) cp $(FRONTEND_SOURCE_DIR)/distribution/ReadMe $($@_TMPDIR)
+ $(Q) cp $(FRONTEND_SOURCE_DIR)/distribution/LeesMij $($@_TMPDIR)
+ $(Q) cd $($@_TMPDIR) && /opt/netsurf/arm-unknown-riscos/env/bin/zip -9vr\, $(CURDIR)/$@ *
+ $(Q) $(RM) -rf $($@_TMPDIR)
diff --git a/frontends/riscos/Makefile.defaults b/frontends/riscos/Makefile.defaults
new file mode 100644
index 000000000..aed361631
--- /dev/null
+++ b/frontends/riscos/Makefile.defaults
@@ -0,0 +1,37 @@
+# ----------------------------------------------------------------------------
+# RISC OS-specific options
+# ----------------------------------------------------------------------------
+
+# Enable NetSurf's use of libsvgtiny for displaying SVGs
+# Valid options: YES, NO
+NETSURF_USE_NSSVG := YES
+
+# Enable NetSurf's support for displaying RISC OS Draw files
+# Valid options: YES, NO
+NETSURF_USE_DRAW := YES
+
+# Enable NetSurf's support for displaying RISC OS Sprites
+# Valid options: YES, NO
+NETSURF_USE_SPRITE := YES
+
+# Enable NetSurf's use of librosprite for displaying RISC OS Sprites
+# Valid options: YES, NO, AUTO
+NETSURF_USE_ROSPRITE := NO
+
+# Enable NetSurf's use of AWRender for displaying ArtWorks files
+# Valid options: YES, NO
+NETSURF_USE_ARTWORKS := YES
+
+# Enable NetSurf's support for the Acorn plugin protocol
+# Valid options: YES, NO
+NETSURF_USE_PLUGINS := NO
+
+# Enable NetSurf's use of pencil for Drawfile export
+# Valid options: YES, NO
+NETSURF_USE_DRAW_EXPORT := YES
+
+# Enable building the source object cache filesystem based backing store.
+NETSURF_FS_BACKING_STORE := YES
+
+# Optimisation levels
+CFLAGS += -O2
diff --git a/frontends/riscos/assert.c b/frontends/riscos/assert.c
new file mode 100644
index 000000000..50b8f5d54
--- /dev/null
+++ b/frontends/riscos/assert.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Assert reporting (RISC OS implementation).
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "oslib/wimp.h"
+
+
+/**
+ * Report an assert() failure and exit.
+ */
+
+void __assert2(const char *expr, const char *function, const char *file,
+ int line)
+{
+ fprintf(stderr, "\n\"%s\", line %d: %s%sAssertion failed: %s\n",
+ file, line,
+ function ? function : "",
+ function ? ": " : "",
+ expr);
+ fflush(stderr);
+
+ abort();
+}
diff --git a/frontends/riscos/bitmap.c b/frontends/riscos/bitmap.c
new file mode 100644
index 000000000..cc4be590c
--- /dev/null
+++ b/frontends/riscos/bitmap.c
@@ -0,0 +1,876 @@
+/*
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2008 Adrian Lees <adrianl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * RISC OS implementation of bitmap operations.
+ *
+ * This implements the interface given by image/bitmap.h using RISC OS
+ * sprites.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+#include <swis.h>
+#include <rufl.h>
+#include <unixlib/local.h>
+#include <oslib/colourtrans.h>
+#include <oslib/osfile.h>
+#include <oslib/osfind.h>
+#include <oslib/osgbpb.h>
+#include <oslib/osspriteop.h>
+#include <oslib/wimp.h>
+
+#include "utils/nsoption.h"
+#include "utils/filename.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "desktop/plotters.h"
+#include "content/content.h"
+#include "image/bitmap.h"
+
+#include "riscos/gui.h"
+#include "riscos/image.h"
+#include "riscos/palettes.h"
+#include "riscos/content-handlers/sprite.h"
+#include "riscos/tinct.h"
+#include "riscos/bitmap.h"
+
+/** Colour in the overlay sprite that allows the bitmap to show through */
+#define OVERLAY_INDEX 0xfe
+
+/** Size of buffer used when constructing mask data to be saved */
+#define SAVE_CHUNK_SIZE 4096
+
+/**
+ * Whether we can use 32bpp sprites
+ */
+static int thumbnail_32bpp_available = -1;
+
+/**
+ * Sprite output context saving
+ */
+struct thumbnail_save_area {
+ osspriteop_save_area *save_area;
+ int context1;
+ int context2;
+ int context3;
+};
+
+/**
+ * Initialise a bitmaps sprite area.
+ *
+ * \param bitmap the bitmap to initialise
+ * \return true if bitmap initialised else false.
+ */
+
+static bool bitmap_initialise(struct bitmap *bitmap)
+{
+ unsigned int area_size;
+ osspriteop_area *sprite_area;
+ osspriteop_header *sprite;
+
+ assert(!bitmap->sprite_area);
+
+ area_size = 16 + 44 + bitmap->width * bitmap->height * 4;
+ if (bitmap->state & BITMAP_CLEAR_MEMORY)
+ bitmap->sprite_area = calloc(1, area_size);
+ else
+ bitmap->sprite_area = malloc(area_size);
+
+ if (!bitmap->sprite_area)
+ return false;
+
+ /* area control block */
+ sprite_area = bitmap->sprite_area;
+ sprite_area->size = area_size;
+ sprite_area->sprite_count = 1;
+ sprite_area->first = 16;
+ sprite_area->used = area_size;
+
+ /* sprite control block */
+ sprite = (osspriteop_header *) (sprite_area + 1);
+ sprite->size = area_size - 16;
+ memset(sprite->name, 0x00, 12);
+ strncpy(sprite->name, "bitmap", 12);
+ sprite->width = bitmap->width - 1;
+ sprite->height = bitmap->height - 1;
+ sprite->left_bit = 0;
+ sprite->right_bit = 31;
+ sprite->image = sprite->mask = 44;
+ sprite->mode = tinct_SPRITE_MODE;
+
+ return true;
+}
+
+
+/* exported interface documented in riscos/bitmap.h */
+void *riscos_bitmap_create(int width, int height, unsigned int state)
+{
+ struct bitmap *bitmap;
+
+ if (width == 0 || height == 0)
+ return NULL;
+
+ bitmap = calloc(1, sizeof(struct bitmap));
+ if (!bitmap)
+ return NULL;
+ bitmap->width = width;
+ bitmap->height = height;
+ bitmap->state = state;
+
+ return bitmap;
+}
+
+
+/* exported interface documented in riscos/bitmap.h */
+unsigned char *riscos_bitmap_get_buffer(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *) vbitmap;
+ assert(bitmap);
+
+ /* dynamically create the buffer */
+ if (bitmap->sprite_area == NULL) {
+ if (!bitmap_initialise(bitmap))
+ return NULL;
+ }
+
+ /* image data area should exist */
+ if (bitmap->sprite_area)
+ return ((unsigned char *) (bitmap->sprite_area)) + 16 + 44;
+
+ return NULL;
+}
+
+
+/**
+ * Sets whether a bitmap should be plotted opaque
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \param opaque whether the bitmap should be plotted opaque
+ */
+static void bitmap_set_opaque(void *vbitmap, bool opaque)
+{
+ struct bitmap *bitmap = (struct bitmap *) vbitmap;
+ assert(bitmap);
+
+ if (opaque)
+ bitmap->state |= BITMAP_OPAQUE;
+ else
+ bitmap->state &= ~BITMAP_OPAQUE;
+}
+
+
+/**
+ * Find the width of a pixel row in bytes.
+ *
+ * \param vbitmap A bitmap, as returned by riscos_bitmap_create()
+ * \return width of a pixel row in the bitmap
+ */
+static size_t bitmap_get_rowstride(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *) vbitmap;
+ return bitmap->width * 4;
+}
+
+
+/**
+ * Tests whether a bitmap has an opaque alpha channel
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \return whether the bitmap is opaque
+ */
+static bool bitmap_test_opaque(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *) vbitmap;
+ unsigned char *sprite;
+ unsigned int width, height, size;
+ osspriteop_header *sprite_header;
+ unsigned *p, *ep;
+
+ assert(bitmap);
+
+ sprite = riscos_bitmap_get_buffer(bitmap);
+ if (!sprite)
+ return false;
+
+ width = bitmap_get_rowstride(bitmap);
+
+ sprite_header = (osspriteop_header *) (bitmap->sprite_area + 1);
+
+ height = (sprite_header->height + 1);
+
+ size = width * height;
+
+ p = (void *) sprite;
+
+ ep = (void *) (sprite + (size & ~31));
+ while (p < ep) {
+ /* \todo prefetch(p, 128)? */
+ if (((p[0] & p[1] & p[2] & p[3] & p[4] & p[5] & p[6] & p[7])
+ & 0xff000000U) != 0xff000000U)
+ return false;
+ p += 8;
+ }
+
+ ep = (void *) (sprite + size);
+ while (p < ep) {
+ if ((*p & 0xff000000U) != 0xff000000U) return false;
+ p++;
+ }
+
+ return true;
+}
+
+
+/* exported interface documented in riscos/bitmap.h */
+bool riscos_bitmap_get_opaque(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *) vbitmap;
+ assert(bitmap);
+ return (bitmap->state & BITMAP_OPAQUE);
+}
+
+
+/* exported interface documented in riscos/bitmap.h */
+void riscos_bitmap_destroy(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *) vbitmap;
+
+ assert(bitmap);
+
+ /* destroy bitmap */
+ if (bitmap->sprite_area) {
+ free(bitmap->sprite_area);
+ }
+
+ free(bitmap);
+}
+
+
+/* exported interface documented in riscos/bitmap.h */
+bool riscos_bitmap_save(void *vbitmap, const char *path, unsigned flags)
+{
+ struct bitmap *bitmap = (struct bitmap *) vbitmap;
+ os_error *error;
+
+ if (bitmap == NULL) {
+ ro_warn_user("SaveError", messages_get("SprIsNull"));
+ return false;
+ }
+
+ if (!bitmap->sprite_area) {
+ riscos_bitmap_get_buffer(bitmap);
+ }
+ if (!bitmap->sprite_area)
+ return false;
+
+ if (riscos_bitmap_get_opaque(bitmap)) {
+ error = xosspriteop_save_sprite_file(osspriteop_USER_AREA,
+ (bitmap->sprite_area), path);
+ if (error) {
+ LOG("xosspriteop_save_sprite_file: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ return false;
+ }
+ return true;
+ } else {
+ /* to make the saved sprite useful we must convert from 'Tinct'
+ * format to either a bi-level mask or a Select-style full
+ * alpha channel */
+ osspriteop_area *area = bitmap->sprite_area;
+ osspriteop_header *hdr = (void *) ((char *) area + area->first);
+ unsigned width = hdr->width + 1, height = hdr->height + 1;
+ unsigned image_size = height * width * 4;
+ unsigned char *chunk_buf;
+ unsigned *p, *elp, *eip;
+ unsigned mask_size;
+ size_t chunk_pix;
+ struct {
+ osspriteop_area area;
+ osspriteop_header hdr;
+ } file_hdr;
+ os_fw fw;
+
+ /* we only support 32bpp sprites */
+ if ((((unsigned)hdr->mode >> 27)&15) != 6) {
+ assert(!"Unsupported sprite format in bitmap_save");
+ return false;
+ }
+
+ chunk_buf = malloc(SAVE_CHUNK_SIZE);
+ if (!chunk_buf) {
+ ro_warn_user("NoMemory", NULL);
+ return false;
+ }
+
+ file_hdr.area = *area;
+ file_hdr.hdr = *hdr;
+
+ if (flags & BITMAP_SAVE_FULL_ALPHA) {
+ mask_size = ((width + 3) & ~3) * height;
+ chunk_pix = SAVE_CHUNK_SIZE;
+ file_hdr.hdr.mode = (os_mode)((unsigned)file_hdr.hdr.mode
+ | (1U<<31));
+ } else {
+ mask_size = (((width + 31) & ~31)/8) * height;
+ chunk_pix = SAVE_CHUNK_SIZE<<3;
+ file_hdr.hdr.mode = (os_mode)((unsigned)file_hdr.hdr.mode
+ & ~(1U<<31));
+ }
+
+ file_hdr.area.sprite_count = 1;
+ file_hdr.area.first = sizeof(file_hdr.area);
+ file_hdr.area.used = sizeof(file_hdr) + image_size + mask_size;
+
+ file_hdr.hdr.image = sizeof(file_hdr.hdr);
+ file_hdr.hdr.mask = file_hdr.hdr.image + image_size;
+ file_hdr.hdr.size = file_hdr.hdr.mask + mask_size;
+
+ error = xosfind_openoutw(0, path, NULL, &fw);
+ if (error) {
+ LOG("xosfind_openoutw: 0x%x: %s", error->errnum, error->errmess);
+ free(chunk_buf);
+ ro_warn_user("SaveError", error->errmess);
+ return false;
+ }
+
+ p = (void *) ((char *) hdr + hdr->image);
+
+ /* write out the area header, sprite header and image data */
+ error = xosgbpb_writew(fw, (byte*)&file_hdr + 4,
+ sizeof(file_hdr)-4, NULL);
+ if (!error)
+ error = xosgbpb_writew(fw, (byte*)p, image_size, NULL);
+ if (error) {
+ LOG("xosgbpb_writew: 0x%x: %s", error->errnum, error->errmess);
+ free(chunk_buf);
+ xosfind_closew(fw);
+ ro_warn_user("SaveError", error->errmess);
+ return false;
+ }
+
+ /* then write out the mask data in chunks */
+ eip = p + (width * height); /* end of image */
+ elp = p + width; /* end of line */
+
+ while (p < eip) {
+ unsigned char *dp = chunk_buf;
+ unsigned *ep = p + chunk_pix;
+ if (ep > elp) ep = elp;
+
+ if (flags & BITMAP_SAVE_FULL_ALPHA) {
+ while (p < ep) {
+ *dp++ = ((unsigned char*)p)[3];
+ p++;
+ }
+ }
+ else {
+ unsigned char mb = 0;
+ int msh = 0;
+ while (p < ep) {
+ if (((unsigned char*)p)[3]) mb |= (1 << msh);
+ if (++msh >= 8) {
+ *dp++ = mb;
+ msh = 0;
+ mb = 0;
+ }
+ p++;
+ }
+ if (msh > 0) *dp++ = mb;
+ }
+
+ if (p >= elp) { /* end of line yet? */
+ /* align to word boundary */
+ while ((int)dp & 3) *dp++ = 0;
+ /* advance end of line pointer */
+ elp += width;
+ }
+ error = xosgbpb_writew(fw, (byte*)chunk_buf, dp-chunk_buf, NULL);
+ if (error) {
+ LOG("xosgbpb_writew: 0x%x: %s", error->errnum, error->errmess);
+ free(chunk_buf);
+ xosfind_closew(fw);
+ ro_warn_user("SaveError", error->errmess);
+ return false;
+ }
+ }
+
+ error = xosfind_closew(fw);
+ if (error) {
+ LOG("xosfind_closew: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ }
+
+ error = xosfile_set_type(path, osfile_TYPE_SPRITE);
+ if (error) {
+ LOG("xosfile_set_type: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ }
+
+ free(chunk_buf);
+ return true;
+ }
+}
+
+
+/**
+ * The bitmap image has changed, so flush any persistent cache.
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ */
+static void bitmap_modified(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *) vbitmap;
+ bitmap->state |= BITMAP_MODIFIED;
+}
+
+
+/**
+ * Get the width of a bitmap.
+ *
+ * \param vbitmap A bitmap, as returned by bitmap_create()
+ * \return The bitmaps width in pixels.
+ */
+static int bitmap_get_width(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *) vbitmap;
+ return bitmap->width;
+}
+
+
+/**
+ * Get the height of a bitmap.
+ *
+ * \param vbitmap A bitmap, as returned by bitmap_create()
+ * \return The bitmaps height in pixels.
+ */
+static int bitmap_get_height(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *) vbitmap;
+ return bitmap->height;
+}
+
+
+/**
+ * Find the bytes per pixel of a bitmap
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \return bytes per pixel
+ */
+static size_t bitmap_get_bpp(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *)vbitmap;
+ assert(bitmap);
+ return 4;
+}
+
+
+/* exported interface documented in riscos/bitmap.h */
+void riscos_bitmap_overlay_sprite(struct bitmap *bitmap,
+ const osspriteop_header *s)
+{
+ const os_colour *palette;
+ const byte *sp, *mp;
+ bool masked = false;
+ bool alpha = false;
+ os_error *error;
+ int dp_offset;
+ int sp_offset;
+ unsigned *dp;
+ int x, y;
+ int w, h;
+
+ assert(sprite_bpp(s) == 8);
+
+ if ((unsigned)s->mode & 0x80000000U)
+ alpha = true;
+
+ error = xosspriteop_read_sprite_info(osspriteop_PTR,
+ (osspriteop_area *)0x100,
+ (osspriteop_id)s,
+ &w, &h, NULL, NULL);
+ if (error) {
+ LOG("xosspriteop_read_sprite_info: 0x%x:%s", error->errnum, error->errmess);
+ return;
+ }
+ sp_offset = ((s->width + 1) * 4) - w;
+
+ if (w > bitmap->width)
+ w = bitmap->width;
+ if (h > bitmap->height)
+ h = bitmap->height;
+
+ dp_offset = bitmap_get_rowstride(bitmap) / 4;
+
+ dp = (void*)riscos_bitmap_get_buffer(bitmap);
+ if (!dp)
+ return;
+ sp = (byte*)s + s->image;
+ mp = (byte*)s + s->mask;
+
+ sp += s->left_bit / 8;
+ mp += s->left_bit / 8;
+
+ if (s->image > (int)sizeof(*s))
+ palette = (os_colour*)(s + 1);
+ else
+ palette = default_palette8;
+
+ if (s->mask != s->image) {
+ masked = true;
+ bitmap_set_opaque(bitmap, false);
+ }
+
+ /* (partially-)transparent pixels in the overlayed sprite retain
+ * their transparency in the output bitmap; opaque sprite pixels
+ * are also propagated to the bitmap, except those which are the
+ * OVERLAY_INDEX colour which allow the original bitmap contents to
+ * show through */
+ for (y = 0; y < h; y++) {
+ unsigned *sdp = dp;
+ for(x = 0; x < w; x++) {
+ os_colour d = ((unsigned)palette[(*sp) << 1]) >> 8;
+ if (*sp++ == OVERLAY_INDEX)
+ d = *dp;
+ if (masked) {
+ if (alpha)
+ d |= ((*mp << 24) ^ 0xff000000U);
+ else if (*mp)
+ d |= 0xff000000U;
+ }
+ *dp++ = d;
+ mp++;
+ }
+ dp = sdp + dp_offset;
+ sp += sp_offset;
+ mp += sp_offset;
+ }
+}
+
+
+/**
+ * Creates an 8bpp canvas.
+ *
+ * \param bitmap the bitmap to clone the size of
+ * \return a sprite area containing an 8bpp sprite
+ */
+static osspriteop_area *thumbnail_create_8bpp(struct bitmap *bitmap)
+{
+ unsigned image_size = ((bitmap->width + 3) & ~3) * bitmap->height;
+ bool opaque = riscos_bitmap_get_opaque(bitmap);
+ osspriteop_header *sprite_header = NULL;
+ osspriteop_area *sprite_area = NULL;
+ unsigned area_size;
+
+ /* clone the sprite */
+ area_size = sizeof(osspriteop_area) +
+ sizeof(osspriteop_header) +
+ image_size +
+ 2048;
+
+ if (!opaque) area_size += image_size;
+
+ sprite_area = (osspriteop_area *)malloc(area_size);
+ if (!sprite_area) {
+ LOG("no memory for malloc()");
+ return NULL;
+ }
+ sprite_area->size = area_size;
+ sprite_area->sprite_count = 1;
+ sprite_area->first = 16;
+ sprite_area->used = area_size;
+ sprite_header = (osspriteop_header *)(sprite_area + 1);
+ sprite_header->size = area_size - sizeof(osspriteop_area);
+ memset(sprite_header->name, 0x00, 12);
+ strcpy(sprite_header->name, "bitmap");
+ sprite_header->left_bit = 0;
+ sprite_header->height = bitmap->height - 1;
+ sprite_header->mode = os_MODE8BPP90X90;
+ sprite_header->right_bit = ((bitmap->width << 3) - 1) & 31;
+ sprite_header->width = ((bitmap->width + 3) >> 2) - 1;
+ sprite_header->image = sizeof(osspriteop_header) + 2048;
+ sprite_header->mask = sizeof(osspriteop_header) + 2048;
+ if (!opaque) sprite_header->mask += image_size;
+
+ /* create the palette. we don't read the necessary size like
+ * we really should as we know it's going to have 256 entries
+ * of 8 bytes = 2048. */
+ xcolourtrans_read_palette((osspriteop_area *)os_MODE8BPP90X90,
+ (osspriteop_id)0,
+ (os_palette *)(sprite_header + 1), 2048,
+ (colourtrans_palette_flags)(1 << 1), 0);
+ return sprite_area;
+}
+
+
+/**
+ * Switches output to the specified sprite and returns the previous context.
+ */
+static struct thumbnail_save_area*
+thumbnail_switch_output(osspriteop_area *sprite_area,
+ osspriteop_header *sprite_header)
+{
+ struct thumbnail_save_area *save_area;
+ int size;
+
+ /* create a save area */
+ save_area = calloc(sizeof(struct thumbnail_save_area), 1);
+ if (save_area == NULL) return NULL;
+
+ /* allocate OS_SpriteOp save area */
+ if (xosspriteop_read_save_area_size(osspriteop_PTR, sprite_area,
+ (osspriteop_id)sprite_header, &size)) {
+ free(save_area);
+ return NULL;
+ }
+
+ /* create the save area */
+ save_area->save_area = malloc((unsigned)size);
+ if (save_area->save_area == NULL) {
+ free(save_area);
+ return NULL;
+ }
+ save_area->save_area->a[0] = 0;
+
+ /* switch output to sprite */
+ if (xosspriteop_switch_output_to_sprite(osspriteop_PTR, sprite_area,
+ (osspriteop_id)sprite_header, save_area->save_area,
+ 0, &save_area->context1, &save_area->context2,
+ &save_area->context3)) {
+ free(save_area->save_area);
+ free(save_area);
+ return NULL;
+ }
+ return save_area;
+}
+
+
+/**
+ * Restores output to the specified context, and destroys it.
+ */
+static void thumbnail_restore_output(struct thumbnail_save_area *save_area)
+{
+ /* we don't care if we err, as there's nothing we can do about it */
+ xosspriteop_switch_output_to_sprite(osspriteop_PTR,
+ (osspriteop_area *)save_area->context1,
+ (osspriteop_id)save_area->context2,
+ (osspriteop_save_area *)save_area->context3,
+ 0, 0, 0, 0);
+ free(save_area->save_area);
+ free(save_area);
+}
+
+
+/**
+ * Convert a bitmap to 8bpp.
+ *
+ * \param bitmap the bitmap to convert
+ * \return a sprite area containing an 8bpp sprite
+ */
+osspriteop_area *riscos_bitmap_convert_8bpp(struct bitmap *bitmap)
+{
+ struct thumbnail_save_area *save_area;
+ osspriteop_area *sprite_area = NULL;
+ osspriteop_header *sprite_header = NULL;
+
+ sprite_area = thumbnail_create_8bpp(bitmap);
+ if (!sprite_area)
+ return NULL;
+ sprite_header = (osspriteop_header *)(sprite_area + 1);
+
+
+ /* switch output and redraw */
+ save_area = thumbnail_switch_output(sprite_area, sprite_header);
+ if (save_area == NULL) {
+ if (thumbnail_32bpp_available != 1)
+ free(sprite_area);
+ return false;
+ }
+ _swix(Tinct_Plot, _IN(2) | _IN(3) | _IN(4) | _IN(7),
+ (osspriteop_header *)(bitmap->sprite_area + 1),
+ 0, 0,
+ tinct_ERROR_DIFFUSE);
+ thumbnail_restore_output(save_area);
+
+ if (sprite_header->image != sprite_header->mask) {
+ /* build the sprite mask from the alpha channel */
+ void *buf = riscos_bitmap_get_buffer(bitmap);
+ unsigned *dp = (unsigned *) buf;
+ if (!dp)
+ return sprite_area;
+ int w = bitmap_get_width(bitmap);
+ int h = bitmap_get_height(bitmap);
+ int dp_offset = bitmap_get_rowstride(bitmap) / 4 - w;
+ int mp_offset = ((sprite_header->width + 1) * 4) - w;
+ byte *mp = (byte*)sprite_header + sprite_header->mask;
+ bool alpha = ((unsigned)sprite_header->mode & 0x80000000U) != 0;
+
+ while (h-- > 0) {
+ int x = 0;
+ for(x = 0; x < w; x++) {
+ unsigned d = *dp++;
+ if (alpha)
+ *mp++ = (d >> 24) ^ 0xff;
+ else
+ *mp++ = (d < 0xff000000U) ? 0 : 0xff;
+ }
+ dp += dp_offset;
+ mp += mp_offset;
+ }
+ }
+
+ return sprite_area;
+}
+
+
+
+
+/**
+ * Check to see whether 32bpp sprites are available.
+ *
+ * Rather than using Wimp_ReadSysInfo we test if 32bpp sprites are available
+ * in case the user has a 3rd party patch to enable them.
+ */
+static void thumbnail_test(void)
+{
+ unsigned int area_size;
+ osspriteop_area *sprite_area;
+
+ /* try to create a 1x1 32bpp sprite */
+ area_size = sizeof(osspriteop_area) +
+ sizeof(osspriteop_header) + sizeof(int);
+ if ((sprite_area = (osspriteop_area *)malloc(area_size)) == NULL) {
+ LOG("Insufficient memory to perform sprite test.");
+ return;
+ }
+ sprite_area->size = area_size + 1;
+ sprite_area->sprite_count = 0;
+ sprite_area->first = 16;
+ sprite_area->used = 16;
+ if (xosspriteop_create_sprite(osspriteop_NAME, sprite_area,
+ "test", false, 1, 1, (os_mode)tinct_SPRITE_MODE))
+ thumbnail_32bpp_available = 0;
+ else
+ thumbnail_32bpp_available = 1;
+ free(sprite_area);
+}
+
+
+/* exported interface documented in riscos/bitmap.h */
+nserror riscos_bitmap_render(struct bitmap *bitmap,
+ struct hlcache_handle *content)
+{
+ struct thumbnail_save_area *save_area;
+ osspriteop_area *sprite_area = NULL;
+ osspriteop_header *sprite_header = NULL;
+ struct redraw_context ctx = {
+ .interactive = false,
+ .background_images = true,
+ .plot = &ro_plotters
+ };
+
+ assert(content);
+ assert(bitmap);
+
+ LOG("content %p in bitmap %p", content, bitmap);
+
+ /* check if we have access to 32bpp sprites natively */
+ if (thumbnail_32bpp_available == -1) {
+ thumbnail_test();
+ }
+
+ /* if we don't support 32bpp sprites then we redirect to an 8bpp
+ * image and then convert back.
+ */
+ if (thumbnail_32bpp_available != 1) {
+ sprite_area = thumbnail_create_8bpp(bitmap);
+ if (!sprite_area)
+ return false;
+ sprite_header = (osspriteop_header *)(sprite_area + 1);
+ } else {
+ const uint8_t *pixbufp = riscos_bitmap_get_buffer(bitmap);
+ if (!pixbufp || !bitmap->sprite_area)
+ return false;
+ sprite_area = bitmap->sprite_area;
+ sprite_header = (osspriteop_header *)(sprite_area + 1);
+ }
+
+ /* set up the plotters */
+ ro_plot_origin_x = 0;
+ ro_plot_origin_y = bitmap->height * 2;
+
+ /* switch output and redraw */
+ save_area = thumbnail_switch_output(sprite_area, sprite_header);
+ if (!save_area) {
+ if (thumbnail_32bpp_available != 1)
+ free(sprite_area);
+ return false;
+ }
+ rufl_invalidate_cache();
+ colourtrans_set_gcol(os_COLOUR_WHITE, colourtrans_SET_BG_GCOL,
+ os_ACTION_OVERWRITE, 0);
+
+ /* render the content */
+ content_scaled_redraw(content, bitmap->width, bitmap->height, &ctx);
+
+ thumbnail_restore_output(save_area);
+ rufl_invalidate_cache();
+
+ /* if we changed to 8bpp then go back to 32bpp */
+ if (thumbnail_32bpp_available != 1) {
+ const uint8_t *pixbufp = riscos_bitmap_get_buffer(bitmap);
+ _kernel_oserror *error;
+
+ if (!pixbufp || !bitmap->sprite_area) {
+ free(sprite_area);
+ return false;
+ }
+ error = _swix(Tinct_ConvertSprite, _INR(2,3),
+ sprite_header,
+ (osspriteop_header *)(bitmap->sprite_area + 1));
+ free(sprite_area);
+ if (error)
+ return false;
+ }
+
+ bitmap_modified(bitmap);
+
+ return NSERROR_OK;
+}
+
+static struct gui_bitmap_table bitmap_table = {
+ .create = riscos_bitmap_create,
+ .destroy = riscos_bitmap_destroy,
+ .set_opaque = bitmap_set_opaque,
+ .get_opaque = riscos_bitmap_get_opaque,
+ .test_opaque = bitmap_test_opaque,
+ .get_buffer = riscos_bitmap_get_buffer,
+ .get_rowstride = bitmap_get_rowstride,
+ .get_width = bitmap_get_width,
+ .get_height = bitmap_get_height,
+ .get_bpp = bitmap_get_bpp,
+ .save = riscos_bitmap_save,
+ .modified = bitmap_modified,
+ .render = riscos_bitmap_render,
+};
+
+struct gui_bitmap_table *riscos_bitmap_table = &bitmap_table;
diff --git a/frontends/riscos/bitmap.h b/frontends/riscos/bitmap.h
new file mode 100644
index 000000000..3aca30de6
--- /dev/null
+++ b/frontends/riscos/bitmap.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_RISCOS_BITMAP_H_
+#define _NETSURF_RISCOS_BITMAP_H_
+
+struct osspriteop_area;
+struct osspriteop_header;
+struct hlcache_handle;
+struct bitmap;
+
+/** bitmap operations table */
+struct gui_bitmap_table *riscos_bitmap_table;
+
+/** save with full alpha channel (if not opaque) */
+#define BITMAP_SAVE_FULL_ALPHA (1 << 0)
+
+/**
+ * RISC OS wimp toolkit bitmap.
+ */
+struct bitmap {
+ int width; /**< width of bitmap */
+ int height; /**< height of bitmap */
+
+ unsigned int state; /**< The bitmap attributes (opaque/dirty etc.) */
+
+ struct osspriteop_area *sprite_area; /**< Uncompressed data, or NULL */
+};
+
+/**
+ * Convert bitmap to 8bpp sprite.
+ *
+ * \param bitmap the bitmap to convert.
+ * \return The converted sprite.
+ */
+struct osspriteop_area *riscos_bitmap_convert_8bpp(struct bitmap *bitmap);
+
+/**
+ * Render content into bitmap.
+ *
+ * \param bitmap the bitmap to draw to
+ * \param content content structure to render
+ * \return true on success and bitmap updated else false
+ */
+nserror riscos_bitmap_render(struct bitmap *bitmap, struct hlcache_handle *content);
+
+/**
+ * Overlay a sprite onto the given bitmap
+ *
+ * \param bitmap bitmap object
+ * \param s 8bpp sprite to be overlayed onto bitmap
+ */
+void riscos_bitmap_overlay_sprite(struct bitmap *bitmap, const struct osspriteop_header *s);
+
+/**
+ * Create a bitmap.
+ *
+ * \param width width of image in pixels
+ * \param height width of image in pixels
+ * \param state the state to create the bitmap in.
+ * \return an opaque struct bitmap, or NULL on memory exhaustion
+ */
+void *riscos_bitmap_create(int width, int height, unsigned int state);
+
+/**
+ * Free a bitmap.
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ */
+void riscos_bitmap_destroy(void *vbitmap);
+
+/**
+ * Return a pointer to the pixel data in a bitmap.
+ *
+ * The pixel data is packed as BITMAP_FORMAT, possibly with padding at
+ * the end of rows. The width of a row in bytes is given by
+ * riscos_bitmap_get_rowstride().
+ *
+ * \param vbitmap A bitmap as returned by riscos_bitmap_create()
+ * \return pointer to the pixel buffer
+ */
+unsigned char *riscos_bitmap_get_buffer(void *vbitmap);
+
+/**
+ * Gets whether a bitmap should be plotted opaque
+ *
+ * \param vbitmap A bitmap, as returned by riscos_bitmap_create()
+ */
+bool riscos_bitmap_get_opaque(void *vbitmap);
+
+/**
+ * Save a bitmap in the platform's native format.
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \param path pathname for file
+ * \param flags modify the behaviour of the save
+ * \return true on success, false on error and error reported
+ */
+bool riscos_bitmap_save(void *vbitmap, const char *path, unsigned flags);
+
+#endif
diff --git a/frontends/riscos/buffer.c b/frontends/riscos/buffer.c
new file mode 100644
index 000000000..7176c1c1c
--- /dev/null
+++ b/frontends/riscos/buffer.c
@@ -0,0 +1,396 @@
+/*
+ * Copyright 2004, 2005 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <swis.h>
+#include <oslib/colourtrans.h>
+#include <oslib/os.h>
+#include <oslib/osspriteop.h>
+#include <oslib/wimp.h>
+#include <oslib/wimpreadsysinfo.h>
+
+#include "utils/nsoption.h"
+#include "utils/log.h"
+
+#include "riscos/buffer.h"
+#include "riscos/gui.h"
+#include "riscos/tinct.h"
+#include "riscos/wimp.h"
+#include "riscos/wimputils.h"
+
+#define BUFFER_EXCLUSIVE_USER_REDRAW "Only support pure user redraw (faster)"
+//#define BUFFER_EMULATE_32BPP "Redirect to a 32bpp sprite and plot with Tinct"
+
+/** Absent from OSLib
+*/
+#ifndef osspriteop_TYPEEXPANSION
+#define osspriteop_TYPEEXPANSION ((osspriteop_mode_word) 0xFu)
+#endif
+#ifndef osspriteop_TYPE16BPP4K
+#define osspriteop_TYPE16BPP4K ((osspriteop_mode_word) 0x10u)
+#endif
+
+static void ro_gui_buffer_free(void);
+
+
+/** The buffer characteristics
+*/
+static osspriteop_area *buffer = NULL;
+static char buffer_name[12] = "scr_buffer";
+
+/** The current clip area
+*/
+static os_box clipping;
+
+/** The current save area
+*/
+static osspriteop_save_area *save_area;
+static int context0;
+static int context1;
+static int context2;
+static int context3;
+
+/** The current sprite mode
+*/
+static os_mode mode;
+
+
+/**
+ * Opens a buffer for writing to.
+ *
+ * The ro_plot_origin_ variables are updated to reflect the new screen origin,
+ * so the variables should be set before calling this function, and not
+ * changed until after ro_gui_buffer_close() has been called.
+ *
+ * \param redraw the current WIMP redraw area to buffer
+ */
+void ro_gui_buffer_open(wimp_draw *redraw)
+{
+ int size;
+ int total_size;
+ os_coord sprite_size;
+ int bpp, word_width;
+ bool palette;
+ os_error *error;
+ int palette_size = 0;
+#ifdef BUFFER_EXCLUSIVE_USER_REDRAW
+ osspriteop_header *header;
+#endif
+
+ /* Close any open buffer
+ */
+ if (buffer)
+ ro_gui_buffer_close();
+
+ /* Store our clipping region
+ */
+ clipping = redraw->clip;
+
+ /* Stop bad rectangles
+ */
+ if ((clipping.x1 < clipping.x0) ||
+ (clipping.y1 < clipping.y0)) {
+ LOG("Invalid clipping rectangle (%i, %i) to (%i,%i)", clipping.x0, clipping.y0, clipping.x1, clipping.y1);
+ return;
+ }
+
+ /* Work out how much buffer we need
+ */
+ sprite_size.x = clipping.x1 - clipping.x0 + 1;
+ sprite_size.y = clipping.y1 - clipping.y0 + 1;
+ ro_convert_os_units_to_pixels(&sprite_size, (os_mode)-1);
+ if (sprite_size.y == 1) /* work around SpriteExtend bug */
+ sprite_size.y = 2;
+
+#ifdef BUFFER_EMULATE_32BPP
+ bpp = 5;
+ palette = false;
+#else
+ /* Get the screen depth as we can't use palettes for >8bpp
+ */
+ xos_read_mode_variable((os_mode)-1, os_MODEVAR_LOG2_BPP, &bpp, 0);
+ palette = (bpp < 4);
+#endif
+
+ /* Get our required buffer size
+ */
+ word_width = ((sprite_size.x << bpp) + 31) >> 5;
+ if (palette)
+ palette_size = ((1 << (1 << bpp)) << 3);
+ total_size = sizeof(osspriteop_area) + sizeof(osspriteop_header) +
+ (word_width * sprite_size.y * 4) + palette_size;
+ buffer = (osspriteop_area *)malloc(total_size);
+ if (!buffer) {
+ LOG("Failed to allocate memory");
+ ro_gui_buffer_free();
+ return;
+ }
+ buffer->size = total_size;
+ buffer->first = 16;
+
+#ifdef BUFFER_EMULATE_32BPP
+ mode = tinct_SPRITE_MODE;
+#else
+ if ((error = xwimpreadsysinfo_wimp_mode(&mode)) != NULL) {
+ LOG("Error reading mode '%s'", error->errmess);
+ ro_gui_buffer_free();
+ return;
+ }
+
+ /* if we're not in a numbered screen mode then we need
+ to build a suitable sprite mode word */
+ if (mode >= (os_mode)0x100) {
+ static const ns_os_vdu_var_list vars = {
+ os_MODEVAR_LOG2_BPP,
+ {
+ os_MODEVAR_MODE_FLAGS,
+ os_MODEVAR_NCOLOUR,
+ os_MODEVAR_XEIG_FACTOR,
+ os_MODEVAR_YEIG_FACTOR,
+ os_VDUVAR_END_LIST
+ }
+ };
+ struct {
+ int log2bpp;
+ int flags;
+ int ncolour;
+ int xeig, yeig;
+ } vals;
+ int type;
+
+ error = xos_read_vdu_variables(PTR_OS_VDU_VAR_LIST(&vars), (int *)&vals);
+ if (error) {
+ LOG("Error reading mode properties '%s'", error->errmess);
+ ro_gui_buffer_free();
+ return;
+ }
+
+ switch (vals.ncolour) {
+ case 1:
+ case 3:
+ case 15:
+ case 63:
+ case 255:
+ /* Paletted modes are pixel packing order agnostic */
+ type = 1 + vals.log2bpp;
+ mode = (os_mode)((type << osspriteop_TYPE_SHIFT) |
+ osspriteop_NEW_STYLE |
+ ((180 >> vals.yeig) << osspriteop_YRES_SHIFT) |
+ ((180 >> vals.xeig) << osspriteop_XRES_SHIFT));
+ break;
+ case 4095:
+ /* 16bpp 4k colours */
+ type = osspriteop_TYPE16BPP4K;
+ mode = (os_mode)((osspriteop_TYPEEXPANSION << osspriteop_TYPE_SHIFT) |
+ osspriteop_NEW_STYLE |
+ (vals.yeig << 6) |
+ (vals.xeig << 4) |
+ (type << 20) |
+ (vals.flags & 0xFF00));
+ break;
+ case 65535:
+ switch ((vals.flags & 0x3000) >> os_MODE_FLAG_DATA_FORMAT_SHIFT) {
+ case os_MODE_FLAG_DATA_FORMAT_RGB:
+ if (vals.flags & 0xC000) {
+ /* Non VIDC packing order */
+ if (vals.flags & os_MODE_FLAG_FULL_PALETTE)
+ type = osspriteop_TYPE16BPP64K;
+ else
+ type = osspriteop_TYPE16BPP;
+ mode = (os_mode)((osspriteop_TYPEEXPANSION << osspriteop_TYPE_SHIFT) |
+ osspriteop_NEW_STYLE |
+ (vals.yeig << 6) |
+ (vals.xeig << 4) |
+ (type << 20) |
+ (vals.flags & 0xFF00));
+ } else {
+ /* VIDC packing order */
+ if (vals.flags & os_MODE_FLAG_FULL_PALETTE)
+ type = osspriteop_TYPE16BPP64K;
+ else
+ type = osspriteop_TYPE16BPP;
+ mode = (os_mode)((type << osspriteop_TYPE_SHIFT) |
+ osspriteop_NEW_STYLE |
+ ((180 >> vals.yeig) << osspriteop_YRES_SHIFT) |
+ ((180 >> vals.xeig) << osspriteop_XRES_SHIFT));
+ }
+ break;
+ default:
+ LOG("Unhandled 16bpp format from flags %d", vals.flags);
+ ro_gui_buffer_free();
+ return;
+ }
+ break;
+ case -1:
+ /* 16M colours */
+ switch ((vals.flags & 0x3000) >> os_MODE_FLAG_DATA_FORMAT_SHIFT) {
+ case os_MODE_FLAG_DATA_FORMAT_RGB:
+ if (vals.flags & 0xC000) {
+ /* Non VIDC packing order */
+ type = osspriteop_TYPE32BPP;
+ mode = (os_mode)((osspriteop_TYPEEXPANSION << osspriteop_TYPE_SHIFT) |
+ osspriteop_NEW_STYLE |
+ (vals.yeig << 6) |
+ (vals.xeig << 4) |
+ (type << 20) |
+ (vals.flags & 0xFF00));
+ } else {
+ /* VIDC packing order */
+ type = osspriteop_TYPE32BPP;
+ mode = (os_mode)((type << osspriteop_TYPE_SHIFT) |
+ osspriteop_NEW_STYLE |
+ ((180 >> vals.yeig) << osspriteop_YRES_SHIFT) |
+ ((180 >> vals.xeig) << osspriteop_XRES_SHIFT));
+ }
+ break;
+ default:
+ LOG("Unhandled 32bpp data format from flags %d", vals.flags);
+ ro_gui_buffer_free();
+ return;
+ }
+ break;
+ default:
+ LOG("Unhandled NCOLOUR value %d", vals.ncolour);
+ ro_gui_buffer_free();
+ return;
+ }
+ }
+#endif
+
+#ifdef BUFFER_EXCLUSIVE_USER_REDRAW
+ /* Create the sprite manually so we don't waste time clearing the
+ background.
+ */
+ buffer->sprite_count = 1;
+ buffer->used = total_size;
+ header = (osspriteop_header *)(buffer + 1);
+ header->size = total_size - sizeof(osspriteop_area);
+ memcpy(header->name, buffer_name, 12);
+ header->width = word_width - 1;
+ header->height = sprite_size.y - 1;
+ header->left_bit = 0;
+ header->right_bit = ((sprite_size.x << bpp) - 1) & 31;
+ header->image = sizeof(osspriteop_header) + palette_size;
+ header->mask = header->image;
+ header->mode = mode;
+ if (palette)
+ xcolourtrans_read_palette((osspriteop_area *)mode,
+ (osspriteop_id)os_CURRENT_MODE,
+ (os_palette *)(header + 1), palette_size,
+ (colourtrans_palette_flags)
+ colourtrans_FLASHING_PALETTE, 0);
+#else
+ /* Read the current contents of the screen
+ */
+ buffer->sprite_count = 0;
+ buffer->used = 16;
+ if ((error = xosspriteop_get_sprite_user_coords(osspriteop_NAME,
+ buffer, buffer_name, palette,
+ clipping.x0, clipping.y0,
+ clipping.x1, clipping.y1)) != NULL) {
+ LOG("Grab error '%s'", error->errmess);
+ ro_gui_buffer_free();
+ return;
+ }
+#endif
+ /* Allocate OS_SpriteOp save area
+ */
+ if ((error = xosspriteop_read_save_area_size(osspriteop_PTR,
+ buffer, (osspriteop_id)(buffer + 1), &size)) != NULL) {
+ LOG("Save area error '%s'", error->errmess);
+ ro_gui_buffer_free();
+ return;
+ }
+ if ((save_area = malloc((size_t)size)) == NULL) {
+ ro_gui_buffer_free();
+ return;
+ }
+ save_area->a[0] = 0;
+
+ /* Switch output to sprite
+ */
+ if ((error = xosspriteop_switch_output_to_sprite(osspriteop_PTR,
+ buffer, (osspriteop_id)(buffer + 1), save_area,
+ &context0, &context1, &context2, &context3)) != NULL) {
+ LOG("Switching error '%s'", error->errmess);
+ free(save_area);
+ ro_gui_buffer_free();
+ return;
+ }
+
+ /* Emulate an origin as the FontManager doesn't respect it in
+ most cases.
+ */
+ ro_plot_origin_x -= clipping.x0;
+ ro_plot_origin_y -= clipping.y0;
+
+ /* Update the ECF origin
+ */
+ if ((error = xos_set_ecf_origin(-ro_plot_origin_x,
+ -ro_plot_origin_y)) != NULL) {
+ LOG("Invalid ECF origin: '%s'", error->errmess);
+ }
+}
+
+
+/**
+ * Closes any open buffer and flushes the contents to screen
+ */
+void ro_gui_buffer_close(void)
+{
+ /* Check we have an open buffer
+ */
+ if (!buffer)
+ return;
+
+ /* Remove any previous redirection
+ */
+ ro_plot_origin_x += clipping.x0;
+ ro_plot_origin_y += clipping.y0;
+ xosspriteop_unswitch_output(context0, context1, context2, context3);
+ free(save_area);
+
+ /* Plot the contents to screen
+ */
+ if (mode == tinct_SPRITE_MODE)
+ _swix(Tinct_Plot, _IN(2) | _IN(3) | _IN(4) | _IN(7),
+ (char *)(buffer + 1),
+ clipping.x0, clipping.y0,
+ nsoption_int(plot_fg_quality));
+ else
+ xosspriteop_put_sprite_user_coords(osspriteop_PTR,
+ buffer, (osspriteop_id)(buffer + 1),
+ clipping.x0, clipping.y0, (os_action)0);
+ ro_gui_buffer_free();
+
+ /* Update the ECF origin
+ */
+ os_set_ecf_origin(0, 0);
+}
+
+
+/**
+ * Releases any buffer memory depending on cache constraints.
+ */
+static void ro_gui_buffer_free(void)
+{
+ free(buffer);
+ buffer = NULL;
+}
diff --git a/frontends/riscos/buffer.h b/frontends/riscos/buffer.h
new file mode 100644
index 000000000..a683c324c
--- /dev/null
+++ b/frontends/riscos/buffer.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Screen buffering (interface).
+ */
+
+#ifndef _NETSURF_RISCOS_BUFFER_H_
+#define _NETSURF_RISCOS_BUFFER_H_
+
+#include "oslib/wimp.h"
+
+void ro_gui_buffer_open(wimp_draw *redraw);
+void ro_gui_buffer_close(void);
+
+#endif
diff --git a/frontends/riscos/configure.c b/frontends/riscos/configure.c
new file mode 100644
index 000000000..9d28616ec
--- /dev/null
+++ b/frontends/riscos/configure.c
@@ -0,0 +1,410 @@
+/*
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * RISC OS option setting (implementation).
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <oslib/os.h>
+#include <oslib/osbyte.h>
+#include <oslib/territory.h>
+#include <oslib/wimp.h>
+
+#include "utils/log.h"
+#include "utils/messages.h"
+
+#include "riscos/gui.h"
+#include "riscos/dialog.h"
+#include "riscos/configure.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/configure/configure.h"
+
+#define CONFIGURE_ICON_PADDING_H 32
+#define CONFIGURE_ICON_PADDING_V 32
+#define CONFIGURE_DEFAULT_ICON_WIDTH (68 + CONFIGURE_ICON_PADDING_H)
+#define CONFIGURE_DEFAULT_ICON_HEIGHT (128 + CONFIGURE_ICON_PADDING_V)
+
+struct configure_tool {
+ const char *name;
+#define CONFIGURE_TOOL_TRANSLATED_SIZE 64
+ char translated[CONFIGURE_TOOL_TRANSLATED_SIZE];
+ char *validation;
+ bool (*initialise)(wimp_w w);
+ void (*finalise)(wimp_w w);
+ wimp_w w;
+ wimp_i i;
+ bool open;
+ struct configure_tool *next;
+};
+
+static wimp_w configure_window;
+static int configure_current_encoding;
+static int configure_icons = 0;
+static struct configure_tool *configure_tools = NULL;
+static int configure_icon_width = CONFIGURE_DEFAULT_ICON_WIDTH;
+static int configure_icon_height = CONFIGURE_DEFAULT_ICON_HEIGHT;
+static int configure_icons_per_line = 0;
+static int configure_width;
+static int configure_height;
+
+static bool ro_gui_configure_click(wimp_pointer *pointer);
+static void ro_gui_configure_open_window(wimp_open *open);
+static void ro_gui_configure_close(wimp_w w);
+static bool ro_gui_configure_translate(void);
+static void ro_gui_configure_register(const char *window,
+ bool (*initialise)(wimp_w w), void (*finalise)(wimp_w w));
+
+void ro_gui_configure_initialise(void)
+{
+ /* create our window */
+ configure_window = ro_gui_dialog_create("configure");
+ ro_gui_wimp_event_register_open_window(configure_window,
+ ro_gui_configure_open_window);
+ ro_gui_wimp_event_register_mouse_click(configure_window,
+ ro_gui_configure_click);
+ ro_gui_wimp_event_set_help_prefix(configure_window, "HelpConfigure");
+
+ /* add in our option windows */
+ ro_gui_configure_register("con_cache",
+ ro_gui_options_cache_initialise,
+ ro_gui_wimp_event_finalise);
+ ro_gui_configure_register("con_connect",
+ ro_gui_options_connection_initialise,
+ ro_gui_wimp_event_finalise);
+ ro_gui_configure_register("con_content",
+ ro_gui_options_content_initialise,
+ ro_gui_wimp_event_finalise);
+ ro_gui_configure_register("con_fonts",
+ ro_gui_options_fonts_initialise,
+ ro_gui_wimp_event_finalise);
+ ro_gui_configure_register("con_home",
+ ro_gui_options_home_initialise,
+ ro_gui_wimp_event_finalise);
+ ro_gui_configure_register("con_image",
+ ro_gui_options_image_initialise,
+ ro_gui_options_image_finalise);
+ ro_gui_configure_register("con_inter",
+ ro_gui_options_interface_initialise,
+ ro_gui_wimp_event_finalise);
+ ro_gui_configure_register("con_lang",
+ ro_gui_options_language_initialise,
+ ro_gui_wimp_event_finalise);
+ ro_gui_configure_register("con_theme",
+ ro_gui_options_theme_initialise,
+ ro_gui_options_theme_finalise);
+ ro_gui_configure_register("con_secure",
+ ro_gui_options_security_initialise,
+ ro_gui_wimp_event_finalise);
+
+ /* translate the icons */
+ if (!ro_gui_configure_translate())
+ die("ro_gui_configure_translate failed");
+}
+
+void ro_gui_configure_show(void)
+{
+ int width, height;
+
+ width = configure_icon_width << 2;
+ height = ((configure_icons + 3) >> 2) * configure_icon_height;
+ ro_gui_dialog_open_top(configure_window, NULL, width, height);
+}
+
+bool ro_gui_configure_click(wimp_pointer *pointer)
+{
+ struct configure_tool *tool;
+
+ if (pointer->buttons == wimp_CLICK_MENU)
+ return true;
+
+ for (tool = configure_tools; tool; tool = tool->next) {
+ if (tool->i == pointer->i) {
+ if (!tool->open) {
+ tool->open = true;
+ if (!tool->initialise(tool->w))
+ return false;
+ ro_gui_dialog_open_persistent(
+ configure_window,
+ tool->w, true);
+ ro_gui_wimp_event_register_close_window(
+ tool->w,
+ ro_gui_configure_close);
+ } else {
+ ro_gui_dialog_open_top(tool->w, NULL, 0, 0);
+ }
+ break;
+ }
+ }
+ return true;
+}
+
+void ro_gui_configure_close(wimp_w w)
+{
+ struct configure_tool *tool;
+
+ for (tool = configure_tools; tool; tool = tool->next) {
+ if (tool->w == w) {
+ tool->open = false;
+ if (tool->finalise)
+ tool->finalise(w);
+ break;
+ }
+ }
+}
+
+void ro_gui_configure_open_window(wimp_open *open)
+{
+ os_error *error;
+ int screen_width, screen_height;
+ int height, width;
+ int icons_per_line, icon_lines;
+ int max_height;
+ os_box extent = { 0, 0, 0, 0 };
+ struct configure_tool *tool;
+
+ if (!ro_gui_configure_translate()) {
+ ro_warn_user("ro_gui_configure_translate failed", 0);
+ return;
+ }
+
+ width = open->visible.x1 - open->visible.x0;
+ height = open->visible.y1 - open->visible.y0;
+ icons_per_line = width / configure_icon_width;
+ if (icons_per_line < 1)
+ icons_per_line = 1;
+
+ /* move our icons */
+ if (icons_per_line != configure_icons_per_line) {
+ int x, y, l;
+ configure_icons_per_line = icons_per_line;
+ x = CONFIGURE_ICON_PADDING_H / 2;
+ y = -configure_icon_height + (CONFIGURE_ICON_PADDING_V / 2);
+ l = 0;
+ for (tool = configure_tools; tool; tool = tool->next) {
+ error = xwimp_resize_icon(configure_window,
+ tool->i,
+ x,
+ y,
+ x + configure_icon_width -
+ CONFIGURE_ICON_PADDING_H,
+ y + configure_icon_height -
+ CONFIGURE_ICON_PADDING_V);
+ if (error) {
+ LOG("xwimp_resize_icon: 0x%x: %s", error->errnum, error->errmess);
+ }
+ x += configure_icon_width;
+ l++;
+ if (l >= icons_per_line) {
+ x = CONFIGURE_ICON_PADDING_H / 2;
+ l = 0;
+ y -= configure_icon_height;
+ }
+ }
+ error = xwimp_force_redraw(configure_window,
+ 0, -16384, 16384, 0);
+ if (error) {
+ LOG("xwimp_force_redraw: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ }
+
+ /* restrict our height */
+ icon_lines = (configure_icons + icons_per_line - 1) /
+ icons_per_line;
+ max_height = (icon_lines * configure_icon_height);
+ if (height > max_height)
+ open->visible.y0 = open->visible.y1 - max_height;
+
+ /* set the extent */
+ if ((configure_height != height) || (configure_width != width)) {
+ int max_icons_per_line;
+ ro_gui_screen_size(&screen_width, &screen_height);
+ max_icons_per_line = screen_width / configure_icon_width;
+ if (max_icons_per_line > configure_icons)
+ max_icons_per_line = configure_icons;
+ extent.x1 = configure_icon_width * max_icons_per_line;
+ extent.y0 = -max_height;
+ error = xwimp_set_extent(open->w, &extent);
+ if (error) {
+ LOG("xwimp_set_extent: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ configure_height = height;
+ configure_width = width;
+ }
+
+ /* open the window */
+ error = xwimp_open_window(open);
+ if (error) {
+ LOG("xwimp_open_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+}
+
+void ro_gui_configure_register(const char *window,
+ bool (*initialise)(wimp_w w), void (*finalise)(wimp_w w))
+{
+ wimp_icon_create new_icon;
+ struct configure_tool *tool;
+ struct configure_tool *link;
+ os_error *error;
+
+ /* create our tool */
+ tool = calloc(sizeof(struct configure_tool), 1);
+ if (!tool) {
+ LOG("Insufficient memory for calloc()");
+ die("Insufficient memory");
+ return; /* For the benefit of scan-build */
+ }
+ tool->name = window;
+ tool->translated[0] = '\0';
+ tool->validation = malloc(strlen(window) + 2);
+ if (!tool->validation) {
+ LOG("Insufficient memory for malloc()");
+ die("Insufficient memory");
+ }
+ sprintf(tool->validation, "S%s", window);
+ tool->initialise = initialise;
+ tool->finalise = finalise;
+ tool->w = ro_gui_dialog_create(tool->name);
+
+ /* create the icon */
+ new_icon.w = configure_window;
+ new_icon.icon.extent.x0 = 0;
+ new_icon.icon.extent.x1 = configure_icon_width;
+ new_icon.icon.extent.y1 = 0;
+ new_icon.icon.extent.y0 = -configure_icon_height;
+ new_icon.icon.flags = wimp_ICON_TEXT | wimp_ICON_SPRITE |
+ wimp_ICON_INDIRECTED | wimp_ICON_HCENTRED |
+ (wimp_COLOUR_VERY_LIGHT_GREY << wimp_ICON_BG_COLOUR_SHIFT) |
+ (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) |
+ (wimp_BUTTON_CLICK << wimp_ICON_BUTTON_TYPE_SHIFT);
+ new_icon.icon.data.indirected_text_and_sprite.text =
+ tool->translated;
+ new_icon.icon.data.indirected_text_and_sprite.validation =
+ tool->validation;
+ new_icon.icon.data.indirected_text_and_sprite.size =
+ CONFIGURE_TOOL_TRANSLATED_SIZE;
+ error = xwimp_create_icon(&new_icon, &tool->i);
+ if (error) {
+ LOG("xwimp_create_icon: 0x%x: %s", error->errnum, error->errmess);
+ die(error->errmess);
+ }
+
+ /* Set the icon's text in current local encoding */
+ ro_gui_set_icon_string(configure_window, tool->i,
+ messages_get(tool->name), true);
+
+ /* link into our list alphabetically */
+ if ((!configure_tools) ||
+ (strcmp(configure_tools->translated,
+ tool->translated) > 0)) {
+ tool->next = configure_tools;
+ configure_tools = tool;
+ } else {
+ for (link = configure_tools; link; link = link->next) {
+ if (link->next) {
+ if (strcmp(link->next->translated,
+ tool->translated) > 0) {
+ tool->next = link->next;
+ link->next = tool;
+ break;
+ }
+ } else {
+ link->next = tool;
+ break;
+ }
+ }
+ }
+ configure_icons++;
+}
+
+/**
+ * Translate tool icons into the system local encoding.
+ * This will also recalculate the minimum required icon width.
+ *
+ * \return true on success, false on memory exhaustion
+ */
+bool ro_gui_configure_translate(void)
+{
+ int alphabet;
+ struct configure_tool *tool;
+ int icon_width;
+ os_error *error;
+
+ /* read current alphabet */
+ error = xosbyte1(osbyte_ALPHABET_NUMBER, 127, 0,
+ &alphabet);
+ if (error) {
+ LOG("failed reading alphabet: 0x%x: %s", error->errnum, error->errmess);
+ /* assume Latin1 */
+ alphabet = territory_ALPHABET_LATIN1;
+ }
+
+ if (alphabet == configure_current_encoding)
+ /* text is already in the correct encoding */
+ return true;
+
+ /* reset icon width */
+ configure_icon_width = CONFIGURE_DEFAULT_ICON_WIDTH;
+
+ for (tool = configure_tools; tool; tool = tool->next) {
+ /* re-translate the text */
+ ro_gui_set_icon_string(configure_window, tool->i,
+ messages_get(tool->name), true);
+
+ /* update the width */
+ error = xwimptextop_string_width(tool->translated,
+ strlen(tool->translated), &icon_width);
+ if (error) {
+ LOG("xwimptextop_string_width: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+ icon_width += CONFIGURE_ICON_PADDING_H;
+ if (icon_width > configure_icon_width)
+ configure_icon_width = icon_width;
+
+ error = xwimp_resize_icon(configure_window,
+ tool->i,
+ 0,
+ -configure_icon_height,
+ configure_icon_width,
+ 0);
+ if (error) {
+ LOG("xwimp_resize_icon: 0x%x: %s", error->errnum, error->errmess);
+ }
+ }
+
+ /* invalidate our global icons_per_line setting
+ * so the icons get reflowed */
+ configure_icons_per_line = 0;
+
+ /* finally, set the current encoding */
+ configure_current_encoding = alphabet;
+
+ return true;
+}
diff --git a/frontends/riscos/configure.h b/frontends/riscos/configure.h
new file mode 100644
index 000000000..c190a6d0c
--- /dev/null
+++ b/frontends/riscos/configure.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * RISC OS option setting (interface).
+ */
+
+
+#ifndef _NETSURF_RISCOS_CONFIGURE_H_
+#define _NETSURF_RISCOS_CONFIGURE_H_
+
+#include <stdbool.h>
+#include "oslib/os.h"
+#include "oslib/wimp.h"
+
+void ro_gui_configure_initialise(void);
+void ro_gui_configure_show(void);
+
+#endif
diff --git a/frontends/riscos/configure/con_cache.c b/frontends/riscos/configure/con_cache.c
new file mode 100644
index 000000000..730d6f82f
--- /dev/null
+++ b/frontends/riscos/configure/con_cache.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include <oslib/hourglass.h>
+
+#include "utils/nsoption.h"
+#include "utils/filename.h"
+#include "utils/messages.h"
+
+#include "riscos/gui.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/configure.h"
+#include "riscos/configure/configure.h"
+#include "riscos/dialog.h"
+
+
+#define CACHE_MEMORY_SIZE 3
+#define CACHE_MEMORY_DEC 4
+#define CACHE_MEMORY_INC 5
+#define CACHE_DISC_SIZE 10
+#define CACHE_DISC_DEC 11
+#define CACHE_DISC_INC 12
+#define CACHE_DISC_EXPIRE 15
+#define CACHE_DISC_EXPIRE_DEC 16
+#define CACHE_DISC_EXPIRE_INC 17
+#define CACHE_DEFAULT_BUTTON 19
+#define CACHE_CANCEL_BUTTON 20
+#define CACHE_OK_BUTTON 21
+
+static bool ro_gui_options_cache_click(wimp_pointer *pointer);
+static bool ro_gui_options_cache_ok(wimp_w w);
+
+bool ro_gui_options_cache_initialise(wimp_w w)
+{
+ /* set the current values */
+ ro_gui_set_icon_decimal(w, CACHE_MEMORY_SIZE,
+ (nsoption_int(memory_cache_size) * 10) >> 20, 1);
+ ro_gui_set_icon_decimal(w, CACHE_DISC_SIZE,
+ (int) ((nsoption_uint(disc_cache_size)) >> 20), 0);
+ ro_gui_set_icon_decimal(w, CACHE_DISC_EXPIRE,
+ (nsoption_int(disc_cache_age)), 0);
+
+ /* initialise all functions for a newly created window */
+ ro_gui_wimp_event_register_numeric_field(w, CACHE_MEMORY_SIZE,
+ CACHE_MEMORY_INC, CACHE_MEMORY_DEC, 0, 640, 1, 1);
+ ro_gui_wimp_event_register_numeric_field(w, CACHE_DISC_SIZE,
+ CACHE_DISC_INC, CACHE_DISC_DEC, 0, 4095, 1, 0);
+ ro_gui_wimp_event_register_numeric_field(w, CACHE_DISC_EXPIRE,
+ CACHE_DISC_EXPIRE_INC, CACHE_DISC_EXPIRE_DEC, 1, 3650,
+ 1, 0);
+ ro_gui_wimp_event_register_mouse_click(w, ro_gui_options_cache_click);
+ ro_gui_wimp_event_register_cancel(w, CACHE_CANCEL_BUTTON);
+ ro_gui_wimp_event_register_ok(w, CACHE_OK_BUTTON,
+ ro_gui_options_cache_ok);
+ ro_gui_wimp_event_set_help_prefix(w, "HelpCacheConfig");
+ ro_gui_wimp_event_memorise(w);
+ return true;
+
+}
+
+bool ro_gui_options_cache_click(wimp_pointer *pointer)
+{
+ switch (pointer->i) {
+ case CACHE_DEFAULT_BUTTON:
+ /* set the default values */
+ ro_gui_set_icon_decimal(pointer->w, CACHE_MEMORY_SIZE,
+ 120, 1);
+ ro_gui_set_icon_decimal(pointer->w, CACHE_DISC_SIZE,
+ 1024, 0);
+ ro_gui_set_icon_decimal(pointer->w, CACHE_DISC_EXPIRE,
+ 28, 0);
+ return true;
+ }
+ return false;
+}
+
+bool ro_gui_options_cache_ok(wimp_w w)
+{
+ nsoption_set_int(memory_cache_size,
+ (((ro_gui_get_icon_decimal(w,
+ CACHE_MEMORY_SIZE, 1) + 1) << 20) - 1) / 10);
+ nsoption_set_uint(disc_cache_size,
+ (uint) (ro_gui_get_icon_decimal(w,
+ CACHE_DISC_SIZE, 0) << 20));
+ nsoption_set_int(disc_cache_age,
+ ro_gui_get_icon_decimal(w, CACHE_DISC_EXPIRE, 0));
+
+ ro_gui_save_options();
+ return true;
+}
diff --git a/frontends/riscos/configure/con_connect.c b/frontends/riscos/configure/con_connect.c
new file mode 100644
index 000000000..9515c5d6f
--- /dev/null
+++ b/frontends/riscos/configure/con_connect.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2006 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include "swis.h"
+#include "oslib/osspriteop.h"
+#include "oslib/wimp.h"
+
+#include "utils/nsoption.h"
+#include "utils/log.h"
+
+#include "riscos/configure/configure.h"
+#include "riscos/dialog.h"
+#include "riscos/menus.h"
+#include "riscos/tinct.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+
+
+#define CONNECTION_PROXY_FIELD 3
+#define CONNECTION_PROXY_MENU 4
+#define CONNECTION_PROXY_HOST_LABEL 5
+#define CONNECTION_PROXY_HOST 6
+#define CONNECTION_PROXY_PORT_LABEL 7
+#define CONNECTION_PROXY_PORT 8
+#define CONNECTION_PROXY_USERNAME_LABEL 9
+#define CONNECTION_PROXY_USERNAME 10
+#define CONNECTION_PROXY_PASSWORD_LABEL 11
+#define CONNECTION_PROXY_PASSWORD 12
+#define CONNECTION_MAX_FETCH_FIELD 16
+#define CONNECTION_MAX_FETCH_DEC 17
+#define CONNECTION_MAX_FETCH_INC 18
+#define CONNECTION_HOST_FETCH_FIELD 20
+#define CONNECTION_HOST_FETCH_DEC 21
+#define CONNECTION_HOST_FETCH_INC 22
+#define CONNECTION_CACHE_FETCH_FIELD 24
+#define CONNECTION_CACHE_FETCH_DEC 25
+#define CONNECTION_CACHE_FETCH_INC 26
+#define CONNECTION_DEFAULT_BUTTON 27
+#define CONNECTION_CANCEL_BUTTON 28
+#define CONNECTION_OK_BUTTON 29
+
+#define http_proxy_type (nsoption_bool(http_proxy) ? (nsoption_int(http_proxy_auth) + 1) : 0)
+
+static int ro_gui_options_connection_proxy_type(wimp_w w);
+static void ro_gui_options_connection_default(wimp_pointer *pointer);
+static bool ro_gui_options_connection_ok(wimp_w w);
+static bool ro_gui_options_connection_update(wimp_w w, wimp_i i, wimp_menu *m,
+ wimp_selection *s, menu_action a);
+
+bool ro_gui_options_connection_initialise(wimp_w w)
+{
+ int proxy_type;
+
+ /* set the current values */
+ proxy_type = (nsoption_bool(http_proxy) ? (nsoption_int(http_proxy_auth) + 1) : 0);
+ ro_gui_set_icon_string(w, CONNECTION_PROXY_FIELD,
+ proxy_type_menu->entries[proxy_type].
+ data.indirected_text.text, true);
+ ro_gui_set_icon_string(w, CONNECTION_PROXY_HOST,
+ nsoption_charp(http_proxy_host) ?
+ nsoption_charp(http_proxy_host) : "", true);
+ ro_gui_set_icon_integer(w, CONNECTION_PROXY_PORT,
+ nsoption_int(http_proxy_port));
+ ro_gui_set_icon_string(w, CONNECTION_PROXY_USERNAME,
+ nsoption_charp(http_proxy_auth_user) ?
+ nsoption_charp(http_proxy_auth_user) : "", true);
+ ro_gui_set_icon_string(w, CONNECTION_PROXY_PASSWORD,
+ nsoption_charp(http_proxy_auth_pass) ?
+ nsoption_charp(http_proxy_auth_pass) : "", true);
+ ro_gui_set_icon_integer(w, CONNECTION_MAX_FETCH_FIELD,
+ nsoption_int(max_fetchers));
+ ro_gui_set_icon_integer(w, CONNECTION_HOST_FETCH_FIELD,
+ nsoption_int(max_fetchers_per_host));
+ ro_gui_set_icon_integer(w, CONNECTION_CACHE_FETCH_FIELD,
+ nsoption_int(max_cached_fetch_handles));
+ ro_gui_options_connection_update(w, -1, NULL, NULL, NO_ACTION);
+
+ /* register icons */
+ ro_gui_wimp_event_register_menu_gright(w, CONNECTION_PROXY_FIELD,
+ CONNECTION_PROXY_MENU, proxy_type_menu);
+ ro_gui_wimp_event_register_text_field(w, CONNECTION_PROXY_HOST_LABEL);
+ ro_gui_wimp_event_register_text_field(w, CONNECTION_PROXY_HOST);
+ ro_gui_wimp_event_register_text_field(w, CONNECTION_PROXY_PORT_LABEL);
+ ro_gui_wimp_event_register_text_field(w, CONNECTION_PROXY_PORT);
+ ro_gui_wimp_event_register_text_field(w, CONNECTION_PROXY_USERNAME_LABEL);
+ ro_gui_wimp_event_register_text_field(w, CONNECTION_PROXY_USERNAME);
+ ro_gui_wimp_event_register_text_field(w, CONNECTION_PROXY_PASSWORD_LABEL);
+ ro_gui_wimp_event_register_text_field(w, CONNECTION_PROXY_PASSWORD);
+ ro_gui_wimp_event_register_numeric_field(w, CONNECTION_MAX_FETCH_FIELD,
+ CONNECTION_MAX_FETCH_INC, CONNECTION_MAX_FETCH_DEC,
+ 1, 99, 1, 0);
+ ro_gui_wimp_event_register_numeric_field(w, CONNECTION_HOST_FETCH_FIELD,
+ CONNECTION_HOST_FETCH_INC, CONNECTION_HOST_FETCH_DEC,
+ 1, 99, 1, 0);
+ ro_gui_wimp_event_register_numeric_field(w, CONNECTION_CACHE_FETCH_FIELD,
+ CONNECTION_CACHE_FETCH_INC, CONNECTION_CACHE_FETCH_DEC,
+ 1, 99, 1, 0);
+ ro_gui_wimp_event_register_menu_selection(w,
+ ro_gui_options_connection_update);
+ ro_gui_wimp_event_register_button(w, CONNECTION_DEFAULT_BUTTON,
+ ro_gui_options_connection_default);
+ ro_gui_wimp_event_register_cancel(w, CONNECTION_CANCEL_BUTTON);
+ ro_gui_wimp_event_register_ok(w, CONNECTION_OK_BUTTON,
+ ro_gui_options_connection_ok);
+
+ ro_gui_wimp_event_set_help_prefix(w, "HelpConnectConfig");
+ ro_gui_wimp_event_memorise(w);
+ return true;
+
+}
+
+bool ro_gui_options_connection_update(wimp_w w, wimp_i i, wimp_menu *m,
+ wimp_selection *s, menu_action a)
+{
+ int proxy_type;
+ bool host, user;
+
+ /* update the shaded state */
+ proxy_type = ro_gui_options_connection_proxy_type(w);
+ host = (proxy_type > 0);
+ user = (proxy_type > 1);
+
+ ro_gui_set_icon_shaded_state(w, CONNECTION_PROXY_HOST_LABEL, !host);
+ ro_gui_set_icon_shaded_state(w, CONNECTION_PROXY_HOST, !host);
+ ro_gui_set_icon_shaded_state(w, CONNECTION_PROXY_PORT_LABEL, !host);
+ ro_gui_set_icon_shaded_state(w, CONNECTION_PROXY_PORT, !host);
+ ro_gui_set_icon_shaded_state(w, CONNECTION_PROXY_USERNAME_LABEL, !user);
+ ro_gui_set_icon_shaded_state(w, CONNECTION_PROXY_USERNAME, !user);
+ ro_gui_set_icon_shaded_state(w, CONNECTION_PROXY_PASSWORD_LABEL, !user);
+ ro_gui_set_icon_shaded_state(w, CONNECTION_PROXY_PASSWORD, !user);
+
+ return true;
+}
+
+int ro_gui_options_connection_proxy_type(wimp_w w)
+{
+ const char *text;
+ int i;
+
+ text = ro_gui_get_icon_string(w, CONNECTION_PROXY_FIELD);
+ for (i = 0; (i < 4); i++)
+ if (!strcmp(text, proxy_type_menu->entries[i].
+ data.indirected_text.text))
+ return i;
+ assert(false);
+}
+
+void ro_gui_options_connection_default(wimp_pointer *pointer)
+{
+ ro_gui_set_icon_string(pointer->w, CONNECTION_PROXY_FIELD,
+ proxy_type_menu->entries[0].
+ data.indirected_text.text, true);
+ ro_gui_set_icon_string(pointer->w, CONNECTION_PROXY_HOST, "", true);
+ ro_gui_set_icon_integer(pointer->w, CONNECTION_PROXY_PORT, 8080);
+ ro_gui_set_icon_string(pointer->w, CONNECTION_PROXY_USERNAME, "", true);
+ ro_gui_set_icon_string(pointer->w, CONNECTION_PROXY_PASSWORD, "", true);
+ ro_gui_set_icon_integer(pointer->w, CONNECTION_MAX_FETCH_FIELD, 24);
+ ro_gui_set_icon_integer(pointer->w, CONNECTION_HOST_FETCH_FIELD, 5);
+ ro_gui_set_icon_integer(pointer->w, CONNECTION_CACHE_FETCH_FIELD, 6);
+ ro_gui_options_connection_update(pointer->w, -1, NULL, NULL, NO_ACTION);
+}
+
+bool ro_gui_options_connection_ok(wimp_w w)
+{
+ int proxy_type;
+
+ proxy_type = ro_gui_options_connection_proxy_type(w);
+ if (proxy_type == 0) {
+ nsoption_set_bool(http_proxy, false);
+ } else {
+ nsoption_set_bool(http_proxy, true);
+ nsoption_set_int(http_proxy_auth, proxy_type - 1);
+ }
+
+ nsoption_set_charp(http_proxy_host,
+ strdup(ro_gui_get_icon_string(w,
+ CONNECTION_PROXY_HOST)));
+
+ nsoption_set_int(http_proxy_port,
+ ro_gui_get_icon_decimal(w, CONNECTION_PROXY_PORT, 0));
+
+ nsoption_set_charp(http_proxy_auth_user,
+ strdup(ro_gui_get_icon_string(w,
+ CONNECTION_PROXY_USERNAME)));
+
+ nsoption_set_charp(http_proxy_auth_pass,
+ strdup(ro_gui_get_icon_string(w,
+ CONNECTION_PROXY_PASSWORD)));
+
+ nsoption_set_int(max_fetchers,
+ ro_gui_get_icon_decimal(w,
+ CONNECTION_MAX_FETCH_FIELD, 0));
+
+ nsoption_set_int(max_fetchers_per_host,
+ ro_gui_get_icon_decimal(w,
+ CONNECTION_HOST_FETCH_FIELD, 0));
+
+ nsoption_set_int(max_cached_fetch_handles,
+ ro_gui_get_icon_decimal(w,
+ CONNECTION_CACHE_FETCH_FIELD, 0));
+
+ ro_gui_save_options();
+ return true;
+}
diff --git a/frontends/riscos/configure/con_content.c b/frontends/riscos/configure/con_content.c
new file mode 100644
index 000000000..50bbd15ef
--- /dev/null
+++ b/frontends/riscos/configure/con_content.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2006 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+
+#include "utils/nsoption.h"
+#include "utils/messages.h"
+
+#include "riscos/gui.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/configure.h"
+#include "riscos/configure/configure.h"
+#include "riscos/dialog.h"
+
+#define CONTENT_BLOCK_ADVERTISEMENTS 2
+#define CONTENT_BLOCK_POPUPS 3
+#define CONTENT_NO_PLUGINS 4
+#define CONTENT_TARGET_BLANK 7
+#define CONTENT_DEFAULT_BUTTON 8
+#define CONTENT_CANCEL_BUTTON 9
+#define CONTENT_OK_BUTTON 10
+#define CONTENT_NO_JAVASCRIPT 11
+
+static void ro_gui_options_content_default(wimp_pointer *pointer);
+static bool ro_gui_options_content_ok(wimp_w w);
+
+bool ro_gui_options_content_initialise(wimp_w w)
+{
+ /* set the current values */
+ ro_gui_set_icon_selected_state(w, CONTENT_BLOCK_ADVERTISEMENTS,
+ nsoption_bool(block_advertisements));
+ ro_gui_set_icon_selected_state(w, CONTENT_BLOCK_POPUPS,
+ nsoption_bool(block_popups));
+ ro_gui_set_icon_selected_state(w, CONTENT_NO_PLUGINS,
+ nsoption_bool(no_plugins));
+ ro_gui_set_icon_selected_state(w, CONTENT_TARGET_BLANK,
+ nsoption_bool(target_blank));
+ ro_gui_set_icon_selected_state(w, CONTENT_NO_JAVASCRIPT,
+ !nsoption_bool(enable_javascript));
+
+ /* initialise all functions for a newly created window */
+ ro_gui_wimp_event_register_checkbox(w, CONTENT_BLOCK_ADVERTISEMENTS);
+ ro_gui_wimp_event_register_checkbox(w, CONTENT_BLOCK_POPUPS);
+ ro_gui_wimp_event_register_checkbox(w, CONTENT_NO_PLUGINS);
+ ro_gui_wimp_event_register_checkbox(w, CONTENT_TARGET_BLANK);
+ ro_gui_wimp_event_register_checkbox(w, CONTENT_NO_JAVASCRIPT);
+ ro_gui_wimp_event_register_button(w, CONTENT_DEFAULT_BUTTON,
+ ro_gui_options_content_default);
+ ro_gui_wimp_event_register_cancel(w, CONTENT_CANCEL_BUTTON);
+ ro_gui_wimp_event_register_ok(w, CONTENT_OK_BUTTON,
+ ro_gui_options_content_ok);
+ ro_gui_wimp_event_set_help_prefix(w, "HelpContentConfig");
+ ro_gui_wimp_event_memorise(w);
+ return true;
+
+}
+
+void ro_gui_options_content_default(wimp_pointer *pointer)
+{
+ /* set the default values */
+ ro_gui_set_icon_selected_state(pointer->w, CONTENT_BLOCK_ADVERTISEMENTS,
+ false);
+ ro_gui_set_icon_selected_state(pointer->w, CONTENT_BLOCK_POPUPS,
+ false);
+ ro_gui_set_icon_selected_state(pointer->w, CONTENT_NO_PLUGINS,
+ false);
+ ro_gui_set_icon_selected_state(pointer->w, CONTENT_TARGET_BLANK,
+ true);
+ ro_gui_set_icon_selected_state(pointer->w, CONTENT_NO_JAVASCRIPT,
+ false);
+}
+
+bool ro_gui_options_content_ok(wimp_w w)
+{
+ nsoption_set_bool(block_advertisements,
+ ro_gui_get_icon_selected_state(w, CONTENT_BLOCK_ADVERTISEMENTS));
+
+ nsoption_set_bool(block_popups,
+ ro_gui_get_icon_selected_state(w, CONTENT_BLOCK_POPUPS));
+ nsoption_set_bool(no_plugins,
+ ro_gui_get_icon_selected_state(w, CONTENT_NO_PLUGINS));
+
+ nsoption_set_bool(target_blank,
+ ro_gui_get_icon_selected_state(w, CONTENT_TARGET_BLANK));
+
+ nsoption_set_bool(enable_javascript,
+ !ro_gui_get_icon_selected_state(w, CONTENT_NO_JAVASCRIPT));
+
+ ro_gui_save_options();
+ return true;
+}
diff --git a/frontends/riscos/configure/con_fonts.c b/frontends/riscos/configure/con_fonts.c
new file mode 100644
index 000000000..280312843
--- /dev/null
+++ b/frontends/riscos/configure/con_fonts.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include <stddef.h>
+
+#include "utils/nsoption.h"
+#include "utils/messages.h"
+#include "desktop/plot_style.h"
+
+#include "riscos/gui.h"
+#include "riscos/font.h"
+#include "riscos/menus.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/configure.h"
+#include "riscos/configure/configure.h"
+#include "riscos/dialog.h"
+
+
+#define FONT_SANS_FIELD 3
+#define FONT_SANS_MENU 4
+#define FONT_SERIF_FIELD 6
+#define FONT_SERIF_MENU 7
+#define FONT_MONOSPACE_FIELD 9
+#define FONT_MONOSPACE_MENU 10
+#define FONT_CURSIVE_FIELD 12
+#define FONT_CURSIVE_MENU 13
+#define FONT_FANTASY_FIELD 15
+#define FONT_FANTASY_MENU 16
+#define FONT_DEFAULT_FIELD 18
+#define FONT_DEFAULT_MENU 19
+#define FONT_DEFAULT_SIZE 23
+#define FONT_DEFAULT_DEC 24
+#define FONT_DEFAULT_INC 25
+#define FONT_MINIMUM_SIZE 28
+#define FONT_MINIMUM_DEC 29
+#define FONT_MINIMUM_INC 30
+#define FONT_DEFAULT_BUTTON 32
+#define FONT_CANCEL_BUTTON 33
+#define FONT_OK_BUTTON 34
+
+/* This menu only ever gets created once */
+/** \todo The memory claimed for this menu should
+ * probably be released at some point */
+static wimp_menu *default_menu;
+
+static const char *font_names[PLOT_FONT_FAMILY_COUNT] = {
+ "Sans-serif",
+ "Serif",
+ "Monospace",
+ "Cursive",
+ "Fantasy"
+};
+
+static void ro_gui_options_fonts_default(wimp_pointer *pointer);
+static bool ro_gui_options_fonts_ok(wimp_w w);
+static bool ro_gui_options_fonts_init_menu(void);
+
+bool ro_gui_options_fonts_initialise(wimp_w w)
+{
+ /* set the current values */
+ ro_gui_set_icon_decimal(w, FONT_DEFAULT_SIZE, nsoption_int(font_size), 1);
+ ro_gui_set_icon_decimal(w, FONT_MINIMUM_SIZE, nsoption_int(font_min_size), 1);
+ ro_gui_set_icon_string(w, FONT_SANS_FIELD, nsoption_charp(font_sans), true);
+ ro_gui_set_icon_string(w, FONT_SERIF_FIELD, nsoption_charp(font_serif), true);
+ ro_gui_set_icon_string(w, FONT_MONOSPACE_FIELD, nsoption_charp(font_mono), true);
+ ro_gui_set_icon_string(w, FONT_CURSIVE_FIELD, nsoption_charp(font_cursive), true);
+ ro_gui_set_icon_string(w, FONT_FANTASY_FIELD, nsoption_charp(font_fantasy), true);
+ ro_gui_set_icon_string(w, FONT_DEFAULT_FIELD,
+ font_names[nsoption_int(font_default)], true);
+
+ if (!ro_gui_options_fonts_init_menu())
+ return false;
+
+ /* initialise all functions for a newly created window */
+ ro_gui_wimp_event_register_menu_gright(w, FONT_SANS_FIELD,
+ FONT_SANS_MENU, rufl_family_menu);
+ ro_gui_wimp_event_register_menu_gright(w, FONT_SERIF_FIELD,
+ FONT_SERIF_MENU, rufl_family_menu);
+ ro_gui_wimp_event_register_menu_gright(w, FONT_MONOSPACE_FIELD,
+ FONT_MONOSPACE_MENU, rufl_family_menu);
+ ro_gui_wimp_event_register_menu_gright(w, FONT_CURSIVE_FIELD,
+ FONT_CURSIVE_MENU, rufl_family_menu);
+ ro_gui_wimp_event_register_menu_gright(w, FONT_FANTASY_FIELD,
+ FONT_FANTASY_MENU, rufl_family_menu);
+ ro_gui_wimp_event_register_menu_gright(w, FONT_DEFAULT_FIELD,
+ FONT_DEFAULT_MENU, default_menu);
+ ro_gui_wimp_event_register_numeric_field(w, FONT_DEFAULT_SIZE,
+ FONT_DEFAULT_INC, FONT_DEFAULT_DEC, 50, 1000, 1, 1);
+ ro_gui_wimp_event_register_numeric_field(w, FONT_MINIMUM_SIZE,
+ FONT_MINIMUM_INC, FONT_MINIMUM_DEC, 10, 500, 1, 1);
+ ro_gui_wimp_event_register_button(w, FONT_DEFAULT_BUTTON,
+ ro_gui_options_fonts_default);
+ ro_gui_wimp_event_register_cancel(w, FONT_CANCEL_BUTTON);
+ ro_gui_wimp_event_register_ok(w, FONT_OK_BUTTON,
+ ro_gui_options_fonts_ok);
+ ro_gui_wimp_event_set_help_prefix(w, "HelpFontConfig");
+ ro_gui_wimp_event_memorise(w);
+ return true;
+
+}
+
+void ro_gui_options_fonts_default(wimp_pointer *pointer)
+{
+ const char *fallback = nsfont_fallback_font();
+
+ /* set the default values */
+ ro_gui_set_icon_decimal(pointer->w, FONT_DEFAULT_SIZE, 128, 1);
+ ro_gui_set_icon_decimal(pointer->w, FONT_MINIMUM_SIZE, 85, 1);
+ ro_gui_set_icon_string(pointer->w, FONT_SANS_FIELD,
+ nsfont_exists("Homerton") ? "Homerton" : fallback, true);
+ ro_gui_set_icon_string(pointer->w, FONT_SERIF_FIELD,
+ nsfont_exists("Trinity") ? "Trinity" : fallback, true);
+ ro_gui_set_icon_string(pointer->w, FONT_MONOSPACE_FIELD,
+ nsfont_exists("Corpus") ? "Corpus" : fallback, true);
+ ro_gui_set_icon_string(pointer->w, FONT_CURSIVE_FIELD,
+ nsfont_exists("Churchill") ? "Churchill" : fallback, true);
+ ro_gui_set_icon_string(pointer->w, FONT_FANTASY_FIELD,
+ nsfont_exists("Sassoon") ? "Sassoon" : fallback, true);
+ ro_gui_set_icon_string(pointer->w, FONT_DEFAULT_FIELD,
+ font_names[0], true);
+}
+
+bool ro_gui_options_fonts_ok(wimp_w w)
+{
+ unsigned int i;
+
+ nsoption_set_int(font_size,
+ ro_gui_get_icon_decimal(w, FONT_DEFAULT_SIZE, 1));
+
+ nsoption_set_int(font_min_size,
+ ro_gui_get_icon_decimal(w, FONT_MINIMUM_SIZE, 1));
+
+ if (nsoption_int(font_size) < nsoption_int(font_min_size)) {
+ nsoption_set_int(font_size, nsoption_int(font_min_size));
+ ro_gui_set_icon_decimal(w, FONT_DEFAULT_SIZE, nsoption_int(font_size), 1);
+
+}
+
+ nsoption_set_charp(font_sans,
+ strdup(ro_gui_get_icon_string(w, FONT_SANS_FIELD)));
+
+ nsoption_set_charp(font_serif,
+ strdup(ro_gui_get_icon_string(w, FONT_SERIF_FIELD)));
+
+ nsoption_set_charp(font_mono,
+ strdup(ro_gui_get_icon_string(w, FONT_MONOSPACE_FIELD)));
+
+ nsoption_set_charp(font_cursive,
+ strdup(ro_gui_get_icon_string(w, FONT_CURSIVE_FIELD)));
+
+ nsoption_set_charp(font_fantasy,
+ strdup(ro_gui_get_icon_string(w, FONT_FANTASY_FIELD)));
+
+ for (i = 0; i != 5; i++) {
+ if (!strcmp(font_names[i], ro_gui_get_icon_string(w,
+ FONT_DEFAULT_FIELD)))
+ break;
+ }
+ if (i == 5)
+ /* this should never happen, but still */
+ i = 0;
+
+ nsoption_set_int(font_default, i);
+
+ ro_gui_save_options();
+ return true;
+}
+
+bool ro_gui_options_fonts_init_menu(void)
+{
+ unsigned int i;
+
+ if (default_menu)
+ /* Already exists */
+ return true;
+
+ default_menu = malloc(wimp_SIZEOF_MENU(5));
+ if (!default_menu) {
+ ro_warn_user("NoMemory", 0);
+ return false;
+ }
+ default_menu->title_data.indirected_text.text =
+ (char *) messages_get("DefaultFonts");
+ ro_gui_menu_init_structure(default_menu, 5);
+ for (i = 0; i < 5; i++) {
+ default_menu->entries[i].data.indirected_text.text =
+ (char *) font_names[i];
+ default_menu->entries[i].data.indirected_text.size =
+ strlen(font_names[i]);
+ }
+ return true;
+}
diff --git a/frontends/riscos/configure/con_home.c b/frontends/riscos/configure/con_home.c
new file mode 100644
index 000000000..ea8e243ed
--- /dev/null
+++ b/frontends/riscos/configure/con_home.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include <stdlib.h>
+
+#include "utils/messages.h"
+#include "utils/nsoption.h"
+
+#include "riscos/gui.h"
+#include "riscos/menus.h"
+#include "riscos/url_suggest.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/configure.h"
+#include "riscos/configure/configure.h"
+#include "riscos/dialog.h"
+
+#define HOME_URL_FIELD 3
+#define HOME_URL_GRIGHT 4
+#define HOME_OPEN_STARTUP 5
+#define HOME_DEFAULT_BUTTON 6
+#define HOME_CANCEL_BUTTON 7
+#define HOME_OK_BUTTON 8
+
+static void ro_gui_options_home_default(wimp_pointer *pointer);
+static bool ro_gui_options_home_ok(wimp_w w);
+static bool ro_gui_options_home_menu_prepare(wimp_w w, wimp_i i,
+ wimp_menu *menu, wimp_pointer *pointer);
+
+bool ro_gui_options_home_initialise(wimp_w w)
+{
+ /* set the current values */
+ ro_gui_set_icon_string(w, HOME_URL_FIELD,
+ nsoption_charp(homepage_url) ?
+ nsoption_charp(homepage_url) : "", true);
+
+ ro_gui_set_icon_selected_state(w, HOME_OPEN_STARTUP,
+ nsoption_bool(open_browser_at_startup));
+
+ ro_gui_set_icon_shaded_state(w,
+ HOME_URL_GRIGHT, !ro_gui_url_suggest_prepare_menu());
+
+ /* initialise all functions for a newly created window */
+ ro_gui_wimp_event_register_menu_gright(w, HOME_URL_FIELD,
+ HOME_URL_GRIGHT, ro_gui_url_suggest_menu);
+ ro_gui_wimp_event_register_checkbox(w, HOME_OPEN_STARTUP);
+ ro_gui_wimp_event_register_button(w, HOME_DEFAULT_BUTTON,
+ ro_gui_options_home_default);
+ ro_gui_wimp_event_register_cancel(w, HOME_CANCEL_BUTTON);
+ ro_gui_wimp_event_register_ok(w, HOME_OK_BUTTON,
+ ro_gui_options_home_ok);
+ ro_gui_wimp_event_register_menu_prepare(w,
+ ro_gui_options_home_menu_prepare);
+ ro_gui_wimp_event_set_help_prefix(w, "HelpHomeConfig");
+ ro_gui_wimp_event_memorise(w);
+ return true;
+
+}
+
+void ro_gui_options_home_default(wimp_pointer *pointer)
+{
+ /* set the default values */
+ ro_gui_set_icon_string(pointer->w, HOME_URL_FIELD, "", true);
+ ro_gui_set_icon_selected_state(pointer->w, HOME_OPEN_STARTUP, false);
+}
+
+bool ro_gui_options_home_ok(wimp_w w)
+{
+ nsoption_set_charp(homepage_url,
+ strdup(ro_gui_get_icon_string(w, HOME_URL_FIELD)));
+
+ nsoption_set_bool(open_browser_at_startup,
+ ro_gui_get_icon_selected_state(w, HOME_OPEN_STARTUP));
+
+ ro_gui_save_options();
+ return true;
+}
+
+
+/**
+ * Callback to prepare menus in the Configure Home dialog. At present, this
+ * only has to handle the URL Suggestion pop-up.
+ *
+ * \param w The window handle owning the menu.
+ * \param i The icon handle owning the menu.
+ * \param *menu The menu to be prepared.
+ * \param *pointer The associated mouse click event block, or NULL
+ * on an Adjust-click re-opening.
+ * \return true if the event was handled; false if not.
+ */
+
+bool ro_gui_options_home_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_pointer *pointer)
+{
+ if (menu != ro_gui_url_suggest_menu || i != HOME_URL_GRIGHT)
+ return false;
+
+ if (pointer != NULL)
+ ro_gui_url_suggest_prepare_menu();
+
+ return true;
+}
diff --git a/frontends/riscos/configure/con_image.c b/frontends/riscos/configure/con_image.c
new file mode 100644
index 000000000..49dd4f76d
--- /dev/null
+++ b/frontends/riscos/configure/con_image.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright 2006 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include <swis.h>
+#include <oslib/osspriteop.h>
+#include <oslib/wimp.h>
+
+#include "utils/nsoption.h"
+#include "utils/log.h"
+
+#include "riscos/gui.h"
+#include "riscos/configure/configure.h"
+#include "riscos/dialog.h"
+#include "riscos/menus.h"
+#include "riscos/tinct.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+
+
+#define IMAGE_FOREGROUND_FIELD 3
+#define IMAGE_FOREGROUND_MENU 4
+#define IMAGE_BACKGROUND_FIELD 6
+#define IMAGE_BACKGROUND_MENU 7
+#define IMAGE_CURRENT_DISPLAY 8
+#define IMAGE_SPEED_TEXT 11
+#define IMAGE_SPEED_FIELD 12
+#define IMAGE_SPEED_DEC 13
+#define IMAGE_SPEED_INC 14
+#define IMAGE_SPEED_CS 15
+#define IMAGE_DISABLE_ANIMATION 16
+#define IMAGE_DEFAULT_BUTTON 17
+#define IMAGE_CANCEL_BUTTON 18
+#define IMAGE_OK_BUTTON 19
+
+static bool ro_gui_options_image_click(wimp_pointer *pointer);
+static bool ro_gui_options_image_ok(wimp_w w);
+static void ro_gui_options_image_redraw(wimp_draw *redraw);
+static bool ro_gui_options_image_update(wimp_w w, wimp_i i, wimp_menu *m,
+ wimp_selection *s, menu_action a);
+static void ro_gui_options_image_read(wimp_w w, unsigned int *bg,
+ unsigned int *fg);
+static void ro_gui_options_update_shading(wimp_w w);
+
+static osspriteop_area *example_images;
+int example_users = 0;
+unsigned int tinct_options[] = {tinct_USE_OS_SPRITE_OP, 0, tinct_DITHER,
+ tinct_ERROR_DIFFUSE};
+
+bool ro_gui_options_image_initialise(wimp_w w)
+{
+ int i;
+
+ /* load the sprite file */
+ if (example_users == 0) {
+ char pathname[256];
+ snprintf(pathname, 256, "%s.Resources.Image", NETSURF_DIR);
+ pathname[255] = '\0';
+ example_images = ro_gui_load_sprite_file(pathname);
+ if (!example_images)
+ return false;
+ }
+ example_users++;
+
+ /* set the current values */
+ for (i = 0; (i < 4); i++) {
+ if ((unsigned int)nsoption_int(plot_fg_quality) == tinct_options[i])
+ ro_gui_set_icon_string(w, IMAGE_FOREGROUND_FIELD,
+ image_quality_menu->entries[i].
+ data.indirected_text.text, true);
+ if ((unsigned int)nsoption_int(plot_bg_quality) == tinct_options[i])
+ ro_gui_set_icon_string(w, IMAGE_BACKGROUND_FIELD,
+ image_quality_menu->entries[i].
+ data.indirected_text.text, true);
+ }
+ ro_gui_set_icon_decimal(w, IMAGE_SPEED_FIELD,
+ nsoption_int(minimum_gif_delay), 2);
+ ro_gui_set_icon_selected_state(w, IMAGE_DISABLE_ANIMATION,
+ !nsoption_bool(animate_images));
+ ro_gui_options_update_shading(w);
+
+ /* register icons */
+ ro_gui_wimp_event_register_menu_gright(w, IMAGE_FOREGROUND_FIELD,
+ IMAGE_FOREGROUND_MENU, image_quality_menu);
+ ro_gui_wimp_event_register_menu_gright(w, IMAGE_BACKGROUND_FIELD,
+ IMAGE_BACKGROUND_MENU, image_quality_menu);
+ ro_gui_wimp_event_register_text_field(w, IMAGE_SPEED_TEXT);
+ ro_gui_wimp_event_register_numeric_field(w, IMAGE_SPEED_FIELD,
+ IMAGE_SPEED_INC, IMAGE_SPEED_DEC, 0, 6000, 10, 2);
+ ro_gui_wimp_event_register_checkbox(w, IMAGE_DISABLE_ANIMATION);
+ ro_gui_wimp_event_register_text_field(w, IMAGE_SPEED_CS);
+ ro_gui_wimp_event_register_redraw_window(w,
+ ro_gui_options_image_redraw);
+ ro_gui_wimp_event_register_mouse_click(w,
+ ro_gui_options_image_click);
+ ro_gui_wimp_event_register_menu_selection(w,
+ ro_gui_options_image_update);
+ ro_gui_wimp_event_register_cancel(w, IMAGE_CANCEL_BUTTON);
+ ro_gui_wimp_event_register_ok(w, IMAGE_OK_BUTTON,
+ ro_gui_options_image_ok);
+ ro_gui_wimp_event_set_help_prefix(w, "HelpImageConfig");
+ ro_gui_wimp_event_memorise(w);
+
+ return true;
+}
+
+void ro_gui_options_image_finalise(wimp_w w)
+{
+ example_users--;
+ if (example_users == 0) {
+ free(example_images);
+ example_images = NULL;
+ }
+ ro_gui_wimp_event_finalise(w);
+}
+
+bool ro_gui_options_image_update(wimp_w w, wimp_i i, wimp_menu *m,
+ wimp_selection *s, menu_action a)
+{
+ ro_gui_redraw_icon(w, IMAGE_CURRENT_DISPLAY);
+
+ return true;
+}
+
+void ro_gui_options_image_redraw(wimp_draw *redraw)
+{
+ osbool more;
+ os_error *error;
+ wimp_icon_state icon_state;
+ osspriteop_header *bg = NULL, *fg = NULL;
+ unsigned int bg_tinct = 0, fg_tinct = 0;
+
+ /* get the icon location */
+ icon_state.w = redraw->w;
+ icon_state.i = IMAGE_CURRENT_DISPLAY;
+ error = xwimp_get_icon_state(&icon_state);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s",
+ error->errnum, error->errmess);
+ ro_warn_user("MenuError", error->errmess);
+ return;
+ }
+
+ /* find the sprites */
+ if (example_images) {
+ ro_gui_options_image_read(redraw->w, &bg_tinct, &fg_tinct);
+ fg_tinct |= 0xeeeeee00;
+ xosspriteop_select_sprite(osspriteop_USER_AREA,
+ example_images, (osspriteop_id)"img_bg", &bg);
+ xosspriteop_select_sprite(osspriteop_USER_AREA,
+ example_images, (osspriteop_id)"img_fg", &fg);
+ }
+
+ /* perform the redraw */
+ more = wimp_redraw_window(redraw);
+ while (more) {
+ int origin_x, origin_y;
+ origin_x = redraw->box.x0 - redraw->xscroll +
+ icon_state.icon.extent.x0 + 2;
+ origin_y = redraw->box.y1 - redraw->yscroll +
+ icon_state.icon.extent.y0 + 2;
+ if (bg)
+ _swix(Tinct_Plot, _INR(2,4) | _IN(7),
+ bg, origin_x, origin_y, bg_tinct);
+ if (fg)
+ _swix(Tinct_PlotAlpha, _INR(2,4) | _IN(7),
+ fg, origin_x, origin_y, fg_tinct);
+ more = wimp_get_rectangle(redraw);
+ }
+}
+
+void ro_gui_options_image_read(wimp_w w, unsigned int *bg, unsigned int *fg)
+{
+ const char *text;
+ int i;
+
+ text = ro_gui_get_icon_string(w, IMAGE_FOREGROUND_FIELD);
+ for (i = 0; i < 4; i++)
+ if (!strcmp(text, image_quality_menu->entries[i].
+ data.indirected_text.text))
+ *fg = tinct_options[i];
+
+ text = ro_gui_get_icon_string(w, IMAGE_BACKGROUND_FIELD);
+ for (i = 0; i < 4; i++)
+ if (!strcmp(text, image_quality_menu->entries[i].
+ data.indirected_text.text))
+ *bg = tinct_options[i];
+}
+
+bool ro_gui_options_image_click(wimp_pointer *pointer)
+{
+ unsigned int old_fg, old_bg, bg, fg;
+
+ ro_gui_options_image_read(pointer->w, &old_bg, &old_fg);
+ switch (pointer->i) {
+ case IMAGE_DEFAULT_BUTTON:
+ ro_gui_set_icon_string(pointer->w,
+ IMAGE_FOREGROUND_FIELD,
+ image_quality_menu->entries[3].
+ data.indirected_text.text, true);
+ ro_gui_set_icon_string(pointer->w,
+ IMAGE_BACKGROUND_FIELD,
+ image_quality_menu->entries[2].
+ data.indirected_text.text, true);
+ ro_gui_set_icon_decimal(pointer->w, IMAGE_SPEED_FIELD,
+ 10, 2);
+ ro_gui_set_icon_selected_state(pointer->w,
+ IMAGE_DISABLE_ANIMATION, false);
+ case IMAGE_DISABLE_ANIMATION:
+ ro_gui_options_update_shading(pointer->w);
+ break;
+ case IMAGE_CANCEL_BUTTON:
+ ro_gui_wimp_event_restore(pointer->w);
+ break;
+ default:
+ return false;
+ }
+
+ ro_gui_options_image_read(pointer->w, &bg, &fg);
+ if ((bg != old_bg) || (fg != old_fg))
+ ro_gui_options_image_update(pointer->w, pointer->i,
+ NULL, NULL, NO_ACTION);
+
+ return false;
+}
+
+void ro_gui_options_update_shading(wimp_w w)
+{
+ bool shaded;
+
+ shaded = ro_gui_get_icon_selected_state(w, IMAGE_DISABLE_ANIMATION);
+ ro_gui_set_icon_shaded_state(w, IMAGE_SPEED_TEXT, shaded);
+ ro_gui_set_icon_shaded_state(w, IMAGE_SPEED_FIELD, shaded);
+ ro_gui_set_icon_shaded_state(w, IMAGE_SPEED_DEC, shaded);
+ ro_gui_set_icon_shaded_state(w, IMAGE_SPEED_INC, shaded);
+ ro_gui_set_icon_shaded_state(w, IMAGE_SPEED_CS, shaded);
+}
+
+bool ro_gui_options_image_ok(wimp_w w)
+{
+ ro_gui_options_image_read(w,
+ (unsigned int *)&nsoption_int(plot_bg_quality),
+ (unsigned int *)&nsoption_int(plot_fg_quality));
+
+ nsoption_set_int(minimum_gif_delay,
+ ro_gui_get_icon_decimal(w, IMAGE_SPEED_FIELD, 2));
+
+ nsoption_set_bool(animate_images,
+ !ro_gui_get_icon_selected_state(w,
+ IMAGE_DISABLE_ANIMATION));
+ ro_gui_save_options();
+
+ return true;
+}
diff --git a/frontends/riscos/configure/con_inter.c b/frontends/riscos/configure/con_inter.c
new file mode 100644
index 000000000..7ab912c54
--- /dev/null
+++ b/frontends/riscos/configure/con_inter.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2006 Adrian Lees <adrianl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+
+#include "utils/nsoption.h"
+
+#include "riscos/gui.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/configure.h"
+#include "riscos/configure/configure.h"
+#include "riscos/dialog.h"
+
+#define INTERFACE_STRIP_EXTNS_OPTION 2
+#define INTERFACE_CONFIRM_OVWR_OPTION 3
+#define INTERFACE_URL_COMPLETE_OPTION 6
+#define INTERFACE_HISTORY_TOOLTIP_OPTION 7
+#define INTERFACE_THUMBNAIL_ICONISE_OPTION 10
+#define INTERFACE_DEFAULT_BUTTON 11
+#define INTERFACE_CANCEL_BUTTON 12
+#define INTERFACE_OK_BUTTON 13
+#define INTERFACE_USE_EXTERNAL_HOTLIST 16
+#define INTERFACE_EXTERNAL_HOTLIST_APP 18
+
+
+static bool ro_gui_options_interface_click(wimp_pointer *pointer);
+static void ro_gui_options_interface_default(wimp_pointer *pointer);
+static bool ro_gui_options_interface_ok(wimp_w w);
+
+bool ro_gui_options_interface_initialise(wimp_w w)
+{
+ /* set the current values */
+ ro_gui_set_icon_selected_state(w, INTERFACE_STRIP_EXTNS_OPTION,
+ nsoption_bool(strip_extensions));
+ ro_gui_set_icon_selected_state(w, INTERFACE_CONFIRM_OVWR_OPTION,
+ nsoption_bool(confirm_overwrite));
+ ro_gui_set_icon_selected_state(w, INTERFACE_URL_COMPLETE_OPTION,
+ nsoption_bool(url_suggestion));
+ ro_gui_set_icon_selected_state(w, INTERFACE_HISTORY_TOOLTIP_OPTION,
+ nsoption_bool(history_tooltip));
+ ro_gui_set_icon_selected_state(w, INTERFACE_THUMBNAIL_ICONISE_OPTION,
+ nsoption_bool(thumbnail_iconise));
+ ro_gui_set_icon_selected_state(w, INTERFACE_USE_EXTERNAL_HOTLIST,
+ nsoption_bool(external_hotlists));
+ ro_gui_set_icon_string(w, INTERFACE_EXTERNAL_HOTLIST_APP,
+ (nsoption_charp(external_hotlist_app)) ?
+ nsoption_charp(external_hotlist_app) : "", false);
+
+ ro_gui_set_icon_shaded_state(w, INTERFACE_EXTERNAL_HOTLIST_APP,
+ !nsoption_bool(external_hotlists));
+
+ /* initialise all functions for a newly created window */
+ ro_gui_wimp_event_register_mouse_click(w,
+ ro_gui_options_interface_click);
+ ro_gui_wimp_event_register_button(w, INTERFACE_DEFAULT_BUTTON,
+ ro_gui_options_interface_default);
+ ro_gui_wimp_event_register_cancel(w, INTERFACE_CANCEL_BUTTON);
+ ro_gui_wimp_event_register_ok(w, INTERFACE_OK_BUTTON,
+ ro_gui_options_interface_ok);
+ ro_gui_wimp_event_set_help_prefix(w, "HelpInterfaceConfig");
+ ro_gui_wimp_event_memorise(w);
+ return true;
+
+}
+
+
+bool ro_gui_options_interface_click(wimp_pointer *pointer)
+{
+ bool shaded;
+
+ switch (pointer->i) {
+ case INTERFACE_USE_EXTERNAL_HOTLIST:
+ shaded = !ro_gui_get_icon_selected_state(pointer->w,
+ INTERFACE_USE_EXTERNAL_HOTLIST);
+ ro_gui_set_icon_shaded_state(pointer->w,
+ INTERFACE_EXTERNAL_HOTLIST_APP, shaded);
+ return false;
+ break;
+ }
+ return false;
+}
+
+
+
+void ro_gui_options_interface_default(wimp_pointer *pointer)
+{
+ ro_gui_set_icon_selected_state(pointer->w,
+ INTERFACE_STRIP_EXTNS_OPTION, true);
+ ro_gui_set_icon_selected_state(pointer->w,
+ INTERFACE_CONFIRM_OVWR_OPTION, true);
+ ro_gui_set_icon_selected_state(pointer->w,
+ INTERFACE_URL_COMPLETE_OPTION, true);
+ ro_gui_set_icon_selected_state(pointer->w,
+ INTERFACE_HISTORY_TOOLTIP_OPTION, true);
+ ro_gui_set_icon_selected_state(pointer->w,
+ INTERFACE_THUMBNAIL_ICONISE_OPTION, true);
+ ro_gui_set_icon_selected_state(pointer->w,
+ INTERFACE_USE_EXTERNAL_HOTLIST, false);
+ ro_gui_set_icon_string(pointer->w, INTERFACE_EXTERNAL_HOTLIST_APP,
+ "", false);
+}
+
+bool ro_gui_options_interface_ok(wimp_w w)
+{
+ nsoption_set_bool(strip_extensions,
+ ro_gui_get_icon_selected_state(w,
+ INTERFACE_STRIP_EXTNS_OPTION));
+ nsoption_set_bool(confirm_overwrite,
+ ro_gui_get_icon_selected_state(w,
+ INTERFACE_CONFIRM_OVWR_OPTION));
+ nsoption_set_bool(url_suggestion,
+ ro_gui_get_icon_selected_state(w,
+ INTERFACE_URL_COMPLETE_OPTION));
+ nsoption_set_bool(history_tooltip,
+ ro_gui_get_icon_selected_state(w,
+ INTERFACE_HISTORY_TOOLTIP_OPTION));
+ nsoption_set_bool(thumbnail_iconise,
+ ro_gui_get_icon_selected_state(w,
+ INTERFACE_THUMBNAIL_ICONISE_OPTION));
+ nsoption_set_bool(external_hotlists,
+ ro_gui_get_icon_selected_state(w,
+ INTERFACE_USE_EXTERNAL_HOTLIST));
+ nsoption_set_charp(external_hotlist_app,
+ strdup(ro_gui_get_icon_string(w,
+ INTERFACE_EXTERNAL_HOTLIST_APP)));
+
+ ro_gui_save_options();
+ return true;
+}
diff --git a/frontends/riscos/configure/con_language.c b/frontends/riscos/configure/con_language.c
new file mode 100644
index 000000000..2030c65c0
--- /dev/null
+++ b/frontends/riscos/configure/con_language.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk>
+ * Copyright 2006 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+
+#include "utils/nsoption.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+
+#include "riscos/gui.h"
+#include "riscos/menus.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/configure.h"
+#include "riscos/configure/configure.h"
+#include "riscos/dialog.h"
+
+
+#define LANGUAGE_INTERFACE_FIELD 3
+#define LANGUAGE_INTERFACE_GRIGHT 4
+#define LANGUAGE_WEB_PAGES_FIELD 6
+#define LANGUAGE_WEB_PAGES_GRIGHT 7
+#define LANGUAGE_DEFAULT_BUTTON 8
+#define LANGUAGE_CANCEL_BUTTON 9
+#define LANGUAGE_OK_BUTTON 10
+
+static void ro_gui_options_language_default(wimp_pointer *pointer);
+static bool ro_gui_options_language_ok(wimp_w w);
+static const char *ro_gui_options_language_name(const char *code);
+
+bool ro_gui_options_language_initialise(wimp_w w)
+{
+ /* set the current values */
+ ro_gui_set_icon_string(w, LANGUAGE_INTERFACE_FIELD,
+ ro_gui_options_language_name(nsoption_charp(language) ?
+ nsoption_charp(language) : "en"), true);
+ ro_gui_set_icon_string(w, LANGUAGE_WEB_PAGES_FIELD,
+ ro_gui_options_language_name(nsoption_charp(accept_language) ?
+ nsoption_charp(accept_language) : "en"), true);
+
+ /* initialise all functions for a newly created window */
+ ro_gui_wimp_event_register_menu_gright(w, LANGUAGE_INTERFACE_FIELD,
+ LANGUAGE_INTERFACE_GRIGHT, languages_menu);
+ ro_gui_wimp_event_register_menu_gright(w, LANGUAGE_WEB_PAGES_FIELD,
+ LANGUAGE_WEB_PAGES_GRIGHT, languages_menu);
+ ro_gui_wimp_event_register_button(w, LANGUAGE_DEFAULT_BUTTON,
+ ro_gui_options_language_default);
+ ro_gui_wimp_event_register_cancel(w, LANGUAGE_CANCEL_BUTTON);
+ ro_gui_wimp_event_register_ok(w, LANGUAGE_OK_BUTTON,
+ ro_gui_options_language_ok);
+ ro_gui_wimp_event_set_help_prefix(w, "HelpLanguageConfig");
+ ro_gui_wimp_event_memorise(w);
+ return true;
+
+}
+
+void ro_gui_options_language_default(wimp_pointer *pointer)
+{
+ const char *code;
+
+ code = ro_gui_default_language();
+ ro_gui_set_icon_string(pointer->w, LANGUAGE_INTERFACE_FIELD,
+ ro_gui_options_language_name(code ?
+ code : "en"), true);
+ ro_gui_set_icon_string(pointer->w, LANGUAGE_WEB_PAGES_FIELD,
+ ro_gui_options_language_name(code ?
+ code : "en"), true);
+}
+
+bool ro_gui_options_language_ok(wimp_w w)
+{
+ const char *code;
+ char *temp;
+
+ code = ro_gui_menu_find_menu_entry_key(languages_menu,
+ ro_gui_get_icon_string(w, LANGUAGE_INTERFACE_FIELD));
+ if (code) {
+ code += 5; /* skip 'lang_' */
+ if ((!nsoption_charp(language)) ||
+ (strcmp(nsoption_charp(language), code))) {
+ temp = strdup(code);
+ if (temp) {
+ nsoption_set_charp(language, temp);
+ } else {
+ LOG("No memory to duplicate language code");
+ ro_warn_user("NoMemory", 0);
+ }
+ }
+ }
+ code = ro_gui_menu_find_menu_entry_key(languages_menu,
+ ro_gui_get_icon_string(w, LANGUAGE_WEB_PAGES_FIELD));
+ if (code) {
+ code += 5; /* skip 'lang_' */
+ if ((!nsoption_charp(accept_language)) ||
+ (strcmp(nsoption_charp(accept_language), code))) {
+ temp = strdup(code);
+ if (temp) {
+ nsoption_set_charp(accept_language,temp);
+ } else {
+ LOG("No memory to duplicate language code");
+ ro_warn_user("NoMemory", 0);
+ }
+ }
+ }
+ ro_gui_save_options();
+ return true;
+}
+
+
+/**
+ * Convert a 2-letter ISO language code to the language name.
+ *
+ * \param code 2-letter ISO language code
+ * \return language name, or code if unknown
+ */
+const char *ro_gui_options_language_name(const char *code)
+{
+ char key[] = "lang_xx";
+ key[5] = code[0];
+ key[6] = code[1];
+
+ return messages_get(key);
+}
diff --git a/frontends/riscos/configure/con_secure.c b/frontends/riscos/configure/con_secure.c
new file mode 100644
index 000000000..9c8a846c3
--- /dev/null
+++ b/frontends/riscos/configure/con_secure.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2006 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+
+#include "utils/nsoption.h"
+#include "utils/messages.h"
+
+#include "riscos/gui.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/configure.h"
+#include "riscos/configure/configure.h"
+#include "riscos/dialog.h"
+
+#define SECURITY_REFERRER 2
+#define SECURITY_DURATION_FIELD 6
+#define SECURITY_DURATION_INC 7
+#define SECURITY_DURATION_DEC 8
+#define SECURITY_DEFAULT_BUTTON 10
+#define SECURITY_CANCEL_BUTTON 11
+#define SECURITY_OK_BUTTON 12
+
+static void ro_gui_options_security_default(wimp_pointer *pointer);
+static bool ro_gui_options_security_ok(wimp_w w);
+
+bool ro_gui_options_security_initialise(wimp_w w)
+{
+ /* set the current values */
+ ro_gui_set_icon_selected_state(w, SECURITY_REFERRER,
+ nsoption_bool(send_referer));
+ ro_gui_set_icon_integer(w, SECURITY_DURATION_FIELD,
+ nsoption_int(expire_url));
+
+ /* initialise all functions for a newly created window */
+ ro_gui_wimp_event_register_checkbox(w, SECURITY_REFERRER);
+ ro_gui_wimp_event_register_numeric_field(w, SECURITY_DURATION_FIELD,
+ SECURITY_DURATION_DEC, SECURITY_DURATION_INC,
+ 0, 365, 1, 0);
+ ro_gui_wimp_event_register_button(w, SECURITY_DEFAULT_BUTTON,
+ ro_gui_options_security_default);
+ ro_gui_wimp_event_register_cancel(w, SECURITY_CANCEL_BUTTON);
+ ro_gui_wimp_event_register_ok(w, SECURITY_OK_BUTTON,
+ ro_gui_options_security_ok);
+ ro_gui_wimp_event_set_help_prefix(w, "HelpSecurityConfig");
+ ro_gui_wimp_event_memorise(w);
+ return true;
+
+}
+
+void ro_gui_options_security_default(wimp_pointer *pointer)
+{
+ /* set the default values */
+ ro_gui_set_icon_integer(pointer->w, SECURITY_DURATION_FIELD, 28);
+ ro_gui_set_icon_selected_state(pointer->w, SECURITY_REFERRER, true);
+}
+
+bool ro_gui_options_security_ok(wimp_w w)
+{
+ nsoption_set_bool(send_referer,
+ ro_gui_get_icon_selected_state(w, SECURITY_REFERRER));
+
+ nsoption_set_int(expire_url,
+ ro_gui_get_icon_decimal(w,SECURITY_DURATION_FIELD, 0));
+
+ ro_gui_save_options();
+ return true;
+}
diff --git a/frontends/riscos/configure/con_theme.c b/frontends/riscos/configure/con_theme.c
new file mode 100644
index 000000000..fb0d3dfb0
--- /dev/null
+++ b/frontends/riscos/configure/con_theme.c
@@ -0,0 +1,420 @@
+/*
+ * Copyright 2006 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <oslib/osspriteop.h>
+#include <oslib/wimp.h>
+#include <oslib/wimpspriteop.h>
+
+#include "utils/config.h"
+#include "utils/nsoption.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+
+#include "riscos/gui.h"
+#include "riscos/configure/configure.h"
+#include "riscos/configure.h"
+#include "riscos/dialog.h"
+#include "riscos/menus.h"
+#include "riscos/theme.h"
+#include "riscos/toolbar.h"
+#include "riscos/url_complete.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/wimputils.h"
+
+
+#define THEME_PANE_AREA 0
+#define THEME_DEFAULT_BUTTON 2
+#define THEME_CANCEL_BUTTON 3
+#define THEME_OK_BUTTON 4
+
+struct toolbar_display {
+ struct toolbar *toolbar;
+ struct theme_descriptor *descriptor;
+ int icon_number;
+ struct toolbar_display *next;
+};
+
+static wimp_window theme_pane_definition = {
+ {0, 0, 16, 16},
+ 0,
+ 0,
+ wimp_TOP,
+ wimp_WINDOW_NEW_FORMAT | wimp_WINDOW_VSCROLL | wimp_WINDOW_AUTO_REDRAW,
+ wimp_COLOUR_BLACK,
+ wimp_COLOUR_LIGHT_GREY,
+ wimp_COLOUR_LIGHT_GREY,
+ wimp_COLOUR_VERY_LIGHT_GREY,
+ wimp_COLOUR_DARK_GREY,
+ wimp_COLOUR_MID_LIGHT_GREY,
+ wimp_COLOUR_CREAM,
+ 0,
+ {0, -16384, 16384, 0},
+ wimp_ICON_TEXT | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED,
+ wimp_BUTTON_CLICK << wimp_ICON_BUTTON_TYPE_SHIFT,
+ wimpspriteop_AREA,
+ 1,
+ 1,
+ {""},
+ 0,
+ {}
+};
+
+
+static wimp_w theme_pane;
+static struct theme_descriptor *theme_list = NULL;
+static struct toolbar_display *toolbars = NULL;
+static char theme_radio_validation[] = "Sradiooff,radioon";
+static char theme_null_validation[] = "";
+static char theme_line_validation[] = "R2";
+
+static bool ro_gui_options_theme_ok(wimp_w w);
+static bool ro_gui_options_theme_click(wimp_pointer *pointer);
+static void ro_gui_options_theme_load(void);
+static void ro_gui_options_theme_free(void);
+
+bool ro_gui_options_theme_initialise(wimp_w w)
+{
+ wimp_window_state state;
+ wimp_icon_state icon_state;
+ os_error *error;
+ struct theme_descriptor *theme_choice;
+ struct toolbar_display *toolbar;
+
+ /* only allow one instance for now*/
+ if (theme_pane)
+ return false;
+ error = xwimp_create_window(&theme_pane_definition, &theme_pane);
+ if (error) {
+ LOG("xwimp_create_window: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+ state.w = w;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+ icon_state.w = w;
+ icon_state.i = THEME_PANE_AREA;
+ error = xwimp_get_icon_state(&icon_state);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+ state.w = theme_pane;
+ state.visible.x1 = state.visible.x0 + icon_state.icon.extent.x1 - 16 -
+ ro_get_vscroll_width(theme_pane);
+ state.visible.x0 += icon_state.icon.extent.x0 + 16;
+ state.visible.y0 = state.visible.y1 + icon_state.icon.extent.y0 + 16;
+ state.visible.y1 += icon_state.icon.extent.y1 - 28;
+ LOG("Y0 = %i, y1 = %i", icon_state.icon.extent.y0, icon_state.icon.extent.y1);
+ error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state), w,
+ wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
+ << wimp_CHILD_XORIGIN_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
+ << wimp_CHILD_YORIGIN_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
+ << wimp_CHILD_LS_EDGE_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
+ << wimp_CHILD_BS_EDGE_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
+ << wimp_CHILD_RS_EDGE_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
+ << wimp_CHILD_TS_EDGE_SHIFT);
+ if (error) {
+ LOG("xwimp_open_window_nested: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+
+ /* load themes */
+ ro_gui_options_theme_load();
+
+ /* set the current selection */
+ theme_choice = ro_gui_theme_find(nsoption_charp(theme));
+ if (!theme_choice)
+ theme_choice = ro_gui_theme_find("Aletheia");
+ for (toolbar = toolbars; toolbar; toolbar = toolbar->next)
+ ro_gui_set_icon_selected_state(theme_pane, toolbar->icon_number,
+ (toolbar->descriptor == theme_choice));
+ ro_gui_wimp_event_memorise(theme_pane);
+ ro_gui_wimp_event_set_help_prefix(theme_pane, "HelpThemePConfig");
+
+ ro_gui_wimp_event_register_mouse_click(w, ro_gui_options_theme_click);
+ ro_gui_wimp_event_register_cancel(w, THEME_CANCEL_BUTTON);
+ ro_gui_wimp_event_register_ok(w, THEME_OK_BUTTON,
+ ro_gui_options_theme_ok);
+ ro_gui_wimp_event_set_help_prefix(w, "HelpThemeConfig");
+ ro_gui_wimp_event_memorise(w);
+
+ return true;
+}
+
+void ro_gui_options_theme_finalise(wimp_w w)
+{
+ ro_gui_options_theme_free();
+ if (theme_pane) {
+ os_error *error;
+ ro_gui_wimp_event_finalise(theme_pane);
+ error = xwimp_delete_window(theme_pane);
+ if (error) {
+ LOG("xwimp_delete_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ theme_pane = 0;
+ }
+ ro_gui_wimp_event_finalise(w);
+}
+
+bool ro_gui_options_theme_ok(wimp_w w)
+{
+ struct toolbar_display *toolbar;
+ struct theme_descriptor *theme_new = NULL;
+
+ /* find the current selection */
+ for (toolbar = toolbars; toolbar; toolbar = toolbar->next) {
+ if (ro_gui_get_icon_selected_state(theme_pane, toolbar->icon_number)) {
+ theme_new = toolbar->descriptor;
+ break;
+ }
+ }
+
+ /* set the options */
+ if (theme_new) {
+ nsoption_set_charp(theme, strdup(theme_new->leafname));
+ ro_gui_theme_apply(theme_new);
+ } else {
+ nsoption_set_charp(theme, NULL);
+ }
+ ro_gui_save_options();
+
+ /* store the pane status */
+ ro_gui_wimp_event_memorise(theme_pane);
+ return true;
+}
+
+bool ro_gui_options_theme_click(wimp_pointer *pointer)
+{
+ struct theme_descriptor *theme_default;
+ struct toolbar_display *toolbar;
+
+ switch (pointer->i) {
+ case THEME_DEFAULT_BUTTON:
+ theme_default = ro_gui_theme_find("Aletheia");
+ for (toolbar = toolbars; toolbar; toolbar = toolbar->next)
+ ro_gui_set_icon_selected_state(theme_pane,
+ toolbar->icon_number,
+ (toolbar->descriptor == theme_default));
+ break;
+ case THEME_CANCEL_BUTTON:
+ ro_gui_wimp_event_restore(theme_pane);
+ break;
+ case THEME_OK_BUTTON:
+ ro_gui_wimp_event_memorise(theme_pane);
+ break;
+ }
+ return false;
+}
+
+void ro_gui_options_theme_load(void)
+{
+ os_error *error;
+ os_box extent = { 0, 0, 0, 0 };
+ struct theme_descriptor *descriptor;
+ struct toolbar_display *link;
+ struct toolbar_display *toolbar_display;
+ struct toolbar *toolbar;
+ wimp_icon_create new_icon;
+ wimp_window_state state;
+ int parent_width, nested_y, min_extent, base_extent;
+ int *radio_icons, *radio_set;
+ int theme_count;
+
+ /* delete our old list and get/open a new one */
+ ro_gui_options_theme_free();
+ theme_list = ro_gui_theme_get_available();
+ ro_gui_theme_open(theme_list, true);
+
+ /* create toolbars for each theme */
+ theme_count = 0;
+ descriptor = theme_list;
+ while (descriptor != NULL) {
+ /* try to create a toolbar */
+ toolbar = ro_toolbar_create(descriptor, NULL,
+ THEME_STYLE_BROWSER_TOOLBAR,
+ TOOLBAR_FLAGS_DISPLAY, NULL, NULL, NULL);
+ if (toolbar != NULL) {
+ ro_toolbar_add_buttons(toolbar, brower_toolbar_buttons,
+ nsoption_charp(toolbar_browser));
+ ro_toolbar_add_url(toolbar);
+ ro_toolbar_add_throbber(toolbar);
+ ro_toolbar_rebuild(toolbar);
+ toolbar_display = calloc(sizeof(struct toolbar_display), 1);
+ if (!toolbar_display) {
+ LOG("No memory for calloc()");
+ ro_warn_user("NoMemory", 0);
+ return;
+ }
+ toolbar_display->toolbar = toolbar;
+ toolbar_display->descriptor = descriptor;
+ if (!toolbars) {
+ toolbars = toolbar_display;
+ } else {
+ link = toolbars;
+ while (link->next) link = link->next;
+ link->next = toolbar_display;
+ }
+ theme_count++;
+ }
+ descriptor = descriptor->next;
+ }
+
+ /* nest the toolbars */
+ state.w = theme_pane;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ parent_width = state.visible.x1 - state.visible.x0;
+ min_extent = state.visible.y0 - state.visible.y1;
+ nested_y = 0;
+ base_extent = state.visible.y1 - state.yscroll;
+ extent.x1 = parent_width;
+ link = toolbars;
+ new_icon.w = theme_pane;
+ new_icon.icon.flags = wimp_ICON_TEXT | wimp_ICON_INDIRECTED |
+ wimp_ICON_VCENTRED |
+ (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) |
+ (wimp_COLOUR_VERY_LIGHT_GREY << wimp_ICON_BG_COLOUR_SHIFT);
+ while (link) {
+ /* update the toolbar */
+ int item_height = 44 + 44 + 16;
+ if (link->next) item_height += 16;
+ ro_toolbar_process(link->toolbar, parent_width, false);
+ extent.y0 = nested_y -
+ ro_toolbar_height(link->toolbar) -
+ item_height;
+ if (link->next) extent.y0 -= 16;
+ if (extent.y0 > min_extent) extent.y0 = min_extent;
+ xwimp_set_extent(theme_pane, &extent);
+
+ /* create the descriptor icons and separator line */
+ new_icon.icon.extent.x0 = 8;
+ new_icon.icon.extent.x1 = parent_width - 8;
+ new_icon.icon.flags &= ~wimp_ICON_BORDER;
+ new_icon.icon.flags |= wimp_ICON_SPRITE;
+ new_icon.icon.extent.y1 = nested_y -
+ ro_toolbar_height(link->toolbar) - 8;
+ new_icon.icon.extent.y0 = nested_y -
+ ro_toolbar_height(link->toolbar) - 52;
+ new_icon.icon.data.indirected_text_and_sprite.text =
+ (char *)&link->descriptor->name;
+ new_icon.icon.data.indirected_text_and_sprite.size =
+ strlen(link->descriptor->name) + 1;
+ new_icon.icon.data.indirected_text_and_sprite.validation =
+ theme_radio_validation;
+ new_icon.icon.flags |= (wimp_BUTTON_RADIO <<
+ wimp_ICON_BUTTON_TYPE_SHIFT);
+ xwimp_create_icon(&new_icon, &link->icon_number);
+ new_icon.icon.flags &= ~wimp_ICON_SPRITE;
+ new_icon.icon.extent.x0 = 52;
+ new_icon.icon.extent.y1 -= 44;
+ new_icon.icon.extent.y0 -= 44;
+ new_icon.icon.data.indirected_text.text =
+ (char *)&link->descriptor->author;
+ new_icon.icon.data.indirected_text.size =
+ strlen(link->descriptor->author) + 1;
+ new_icon.icon.data.indirected_text.validation =
+ theme_null_validation;
+ new_icon.icon.flags &= ~(wimp_BUTTON_RADIO <<
+ wimp_ICON_BUTTON_TYPE_SHIFT);
+ xwimp_create_icon(&new_icon, 0);
+ if (link->next) {
+ new_icon.icon.flags |= wimp_ICON_BORDER;
+ new_icon.icon.extent.x0 = -8;
+ new_icon.icon.extent.x1 = parent_width + 8;
+ new_icon.icon.extent.y1 -= 52;
+ new_icon.icon.extent.y0 = new_icon.icon.extent.y1 - 8;
+ new_icon.icon.data.indirected_text.text =
+ theme_null_validation;
+ new_icon.icon.data.indirected_text.validation =
+ theme_line_validation;
+ new_icon.icon.data.indirected_text.size = 1;
+ xwimp_create_icon(&new_icon, 0);
+ }
+
+ /* nest the toolbar window */
+ state.w = ro_toolbar_get_window(link->toolbar);
+ state.yscroll = 0;
+ state.visible.y1 = nested_y + base_extent;
+ state.visible.y0 = state.visible.y1 -
+ ro_toolbar_height(link->toolbar) + 2;
+ xwimp_open_window_nested(PTR_WIMP_OPEN(&state), theme_pane,
+ wimp_CHILD_LINKS_PARENT_WORK_AREA
+ << wimp_CHILD_BS_EDGE_SHIFT |
+ wimp_CHILD_LINKS_PARENT_WORK_AREA
+ << wimp_CHILD_TS_EDGE_SHIFT);
+
+ /* continue processing */
+ nested_y -= ro_toolbar_height(link->toolbar) +
+ item_height;
+ link = link->next;
+ }
+
+ /* set the icons as radios */
+ radio_icons = (int *)calloc(theme_count + 1, sizeof(int));
+ radio_set = radio_icons;
+ for (link = toolbars; link; link = link->next)
+ *radio_set++ = link->icon_number;
+ *radio_set = -1;
+ ro_gui_wimp_event_register_radio(theme_pane, radio_icons);
+
+ /* update our display */
+ xwimp_force_redraw(theme_pane, 0, -16384, 16384, 16384);
+}
+
+void ro_gui_options_theme_free(void)
+{
+ struct toolbar_display *toolbar;
+ struct toolbar_display *next_toolbar;
+
+ /* free all our toolbars */
+ next_toolbar = toolbars;
+ while ((toolbar = next_toolbar) != NULL) {
+ next_toolbar = toolbar->next;
+ xwimp_delete_icon(theme_pane, toolbar->icon_number);
+ xwimp_delete_icon(theme_pane, toolbar->icon_number + 1);
+ if (next_toolbar)
+ xwimp_delete_icon(theme_pane,
+ toolbar->icon_number + 2);
+ ro_toolbar_destroy(toolbar->toolbar);
+ free(toolbar);
+ }
+ toolbars = NULL;
+
+ /* close all our themes */
+ if (theme_list)
+ ro_gui_theme_close(theme_list, true);
+ theme_list = NULL;
+}
diff --git a/frontends/riscos/configure/configure.h b/frontends/riscos/configure/configure.h
new file mode 100644
index 000000000..e5cdb392e
--- /dev/null
+++ b/frontends/riscos/configure/configure.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Automated RISC OS WIMP event handling (interface).
+ */
+
+
+#ifndef _NETSURF_RISCOS_OPTIONS_CONFIGURE_H_
+#define _NETSURF_RISCOS_OPTIONS_CONFIGURE_H_
+
+#include <stdbool.h>
+#include "oslib/wimp.h"
+
+bool ro_gui_options_cache_initialise(wimp_w w);
+bool ro_gui_options_connection_initialise(wimp_w w);
+bool ro_gui_options_content_initialise(wimp_w w);
+bool ro_gui_options_fonts_initialise(wimp_w w);
+bool ro_gui_options_home_initialise(wimp_w w);
+bool ro_gui_options_image_initialise(wimp_w w);
+void ro_gui_options_image_finalise(wimp_w w);
+bool ro_gui_options_interface_initialise(wimp_w w);
+bool ro_gui_options_language_initialise(wimp_w w);
+bool ro_gui_options_security_initialise(wimp_w w);
+bool ro_gui_options_theme_initialise(wimp_w w);
+void ro_gui_options_theme_finalise(wimp_w w);
+
+#endif
diff --git a/frontends/riscos/content-handlers/artworks.c b/frontends/riscos/content-handlers/artworks.c
new file mode 100644
index 000000000..b6f7a0d08
--- /dev/null
+++ b/frontends/riscos/content-handlers/artworks.c
@@ -0,0 +1,435 @@
+/*
+ * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Content for image/x-artworks (RISC OS implementation).
+ *
+ * Uses the ArtworksRenderer module
+ */
+
+#include "utils/config.h"
+#ifdef WITH_ARTWORKS
+
+#include <assert.h>
+#include <limits.h>
+#include <stdlib.h>
+#include "swis.h"
+#include "oslib/os.h"
+#include "oslib/wimp.h"
+
+#include "utils/config.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "content/content_protected.h"
+#include "desktop/plotters.h"
+
+#include "riscos/content-handlers/artworks.h"
+#include "riscos/gui.h"
+#include "riscos/wimputils.h"
+
+#define AWRender_FileInitAddress 0x46080
+#define AWRender_RenderAddress 0x46081
+#define AWRender_DocBounds 0x46082
+#define AWRender_SendDefs 0x46083
+#define AWRender_ClaimVectors 0x46084
+#define AWRender_ReleaseVectors 0x46085
+#define AWRender_FindFirstFont 0x46086
+#define AWRender_FindNextFont 0x46087
+
+
+#define INITIAL_BLOCK_SIZE 0x1000
+
+typedef struct artworks_content {
+ struct content base;
+
+ int x0, y0, x1, y1;
+
+ void *render_routine;
+ void *render_workspace;
+
+ /* dynamically-resizable block required by
+ ArtWorksRenderer rendering routine */
+
+ void *block;
+ size_t size;
+} artworks_content;
+
+struct awinfo_block {
+ int ditherx;
+ int dithery;
+ int clip_x0;
+ int clip_y0;
+ int clip_x1;
+ int clip_y1;
+ int print_lowx;
+ int print_lowy;
+ int print_handle;
+ int print_x1;
+ int print_y1;
+ int bgcolour;
+};
+
+
+/* Assembler routines for interfacing with the ArtworksRenderer module */
+
+extern os_error *awrender_init(const char **doc,
+ unsigned long *doc_size,
+ void *routine,
+ void *workspace);
+
+extern os_error *awrender_render(const char *doc,
+ const struct awinfo_block *info,
+ const os_trfm *trans,
+ const int *vdu_vars,
+ void **rsz_block,
+ size_t *rsz_size,
+ int wysiwyg_setting,
+ int output_dest,
+ size_t doc_size,
+ void *routine,
+ void *workspace);
+
+static nserror artworks_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c);
+static bool artworks_convert(struct content *c);
+static void artworks_destroy(struct content *c);
+static bool artworks_redraw(struct content *c, struct content_redraw_data *data,
+ const struct rect *clip, const struct redraw_context *ctx);
+static nserror artworks_clone(const struct content *old, struct content **newc);
+static content_type artworks_content_type(void);
+
+static const content_handler artworks_content_handler = {
+ .create = artworks_create,
+ .data_complete = artworks_convert,
+ .destroy = artworks_destroy,
+ .redraw = artworks_redraw,
+ .clone = artworks_clone,
+ .type = artworks_content_type,
+ .no_share = false,
+};
+
+static const char *artworks_types[] = {
+ "image/x-artworks"
+};
+
+CONTENT_FACTORY_REGISTER_TYPES(artworks, artworks_types,
+ artworks_content_handler)
+
+nserror artworks_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c)
+{
+ artworks_content *aw;
+ nserror error;
+
+ aw = calloc(1, sizeof(artworks_content));
+ if (aw == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__init(&aw->base, handler, imime_type, params,
+ llcache, fallback_charset, quirks);
+ if (error != NSERROR_OK) {
+ free(aw);
+ return error;
+ }
+
+ *c = (struct content *) aw;
+
+ return NSERROR_OK;
+}
+
+/**
+ * Convert a CONTENT_ARTWORKS for display.
+ *
+ * No conversion is necessary. We merely read the ArtWorks
+ * bounding box bottom-left.
+ */
+
+bool artworks_convert(struct content *c)
+{
+ artworks_content *aw = (artworks_content *) c;
+ union content_msg_data msg_data;
+ const char *source_data;
+ unsigned long source_size;
+ void *init_workspace;
+ void *init_routine;
+ os_error *error;
+ int used = -1; /* slightly better with older OSLib versions */
+ char *title;
+
+ /* check whether AWViewer has been seen and we can therefore
+ locate the ArtWorks rendering modules */
+ xos_read_var_val_size("Alias$LoadArtWorksModules", 0, os_VARTYPE_STRING,
+ &used, NULL, NULL);
+ if (used >= 0) {
+ LOG("Alias$LoadArtWorksModules not defined");
+ msg_data.error = messages_get("AWNotSeen");
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ /* load the modules, or do nothing if they're already loaded */
+ error = xos_cli("LoadArtWorksModules");
+ if (error) {
+ LOG("xos_cli: 0x%x: %s", error->errnum, error->errmess);
+ msg_data.error = error->errmess;
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ /* lookup the addresses of the init and render routines */
+ error = (os_error*)_swix(AWRender_FileInitAddress, _OUT(0) | _OUT(1),
+ &init_routine, &init_workspace);
+ if (error) {
+ LOG("AWRender_FileInitAddress: 0x%x: %s", error->errnum, error->errmess);
+ msg_data.error = error->errmess;
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ error = (os_error*)_swix(AWRender_RenderAddress, _OUT(0) | _OUT(1),
+ &aw->render_routine,
+ &aw->render_workspace);
+ if (error) {
+ LOG("AWRender_RenderAddress: 0x%x: %s", error->errnum, error->errmess);
+ msg_data.error = error->errmess;
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ source_data = content__get_source_data(c, &source_size);
+
+ /* initialise (convert file to new format if required) */
+ error = awrender_init(&source_data, &source_size,
+ init_routine, init_workspace);
+ if (error) {
+ LOG("awrender_init: 0x%x : %s", error->errnum, error->errmess);
+ msg_data.error = error->errmess;
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ error = (os_error*)_swix(AWRender_DocBounds,
+ _IN(0) | _OUT(2) | _OUT(3) | _OUT(4) | _OUT(5),
+ source_data,
+ &aw->x0,
+ &aw->y0,
+ &aw->x1,
+ &aw->y1);
+
+ if (error) {
+ LOG("AWRender_DocBounds: 0x%x: %s", error->errnum, error->errmess);
+ msg_data.error = error->errmess;
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ LOG("bounding box: %d,%d,%d,%d", aw->x0, aw->y0, aw->x1, aw->y1);
+
+ /* create the resizable workspace required by the
+ ArtWorksRenderer rendering routine */
+
+ aw->size = INITIAL_BLOCK_SIZE;
+ aw->block = malloc(INITIAL_BLOCK_SIZE);
+ if (!aw->block) {
+ LOG("failed to create block for ArtworksRenderer");
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ c->width = (aw->x1 - aw->x0) / 512;
+ c->height = (aw->y1 - aw->y0) / 512;
+
+ title = messages_get_buff("ArtWorksTitle",
+ nsurl_access_leaf(llcache_handle_get_url(c->llcache)),
+ c->width, c->height);
+ if (title != NULL) {
+ content__set_title(c, title);
+ free(title);
+ }
+ content_set_ready(c);
+ content_set_done(c);
+ /* Done: update status bar */
+ content_set_status(c, "");
+ return true;
+}
+
+
+/**
+ * Destroy a CONTENT_ARTWORKS and free all resources it owns.
+ */
+
+void artworks_destroy(struct content *c)
+{
+ artworks_content *aw = (artworks_content *) c;
+
+ free(aw->block);
+}
+
+
+/**
+ * Redraw a CONTENT_ARTWORKS.
+ */
+
+bool artworks_redraw(struct content *c, struct content_redraw_data *data,
+ const struct rect *clip, const struct redraw_context *ctx)
+{
+ static const ns_os_vdu_var_list vars = {
+ os_MODEVAR_XEIG_FACTOR,
+ {
+ os_MODEVAR_YEIG_FACTOR,
+ os_MODEVAR_LOG2_BPP,
+ os_VDUVAR_END_LIST
+ }
+ };
+ artworks_content *aw = (artworks_content *) c;
+ struct awinfo_block info;
+ const char *source_data;
+ unsigned long source_size;
+ os_error *error;
+ os_trfm matrix;
+ int vals[24];
+
+ int clip_x0 = clip->x0;
+ int clip_y0 = clip->y0;
+ int clip_x1 = clip->x1;
+ int clip_y1 = clip->y1;
+
+ if (ctx->plot->flush && !ctx->plot->flush())
+ return false;
+
+ /* pick up render addresses again in case they've changed
+ (eg. newer AWRender module loaded since we first loaded this file) */
+ (void)_swix(AWRender_RenderAddress, _OUT(0) | _OUT(1),
+ &aw->render_routine,
+ &aw->render_workspace);
+
+ /* Scaled image. Transform units (65536*OS units) */
+ matrix.entries[0][0] = data->width * 65536 / c->width;
+ matrix.entries[0][1] = 0;
+ matrix.entries[1][0] = 0;
+ matrix.entries[1][1] = data->height * 65536 / c->height;
+ /* Draw units. (x,y) = bottom left */
+ matrix.entries[2][0] = ro_plot_origin_x * 256 + data->x * 512 -
+ aw->x0 * data->width / c->width;
+ matrix.entries[2][1] = ro_plot_origin_y * 256 -
+ (data->y + data->height) * 512 -
+ aw->y0 * data->height / c->height;
+
+ info.ditherx = ro_plot_origin_x;
+ info.dithery = ro_plot_origin_y;
+
+ clip_x0 -= data->x;
+ clip_y0 -= data->y;
+ clip_x1 -= data->x;
+ clip_y1 -= data->y;
+
+ if (data->scale == 1.0) {
+ info.clip_x0 = (clip_x0 * 512) + aw->x0 - 511;
+ info.clip_y0 = ((c->height - clip_y1) * 512) + aw->y0 - 511;
+ info.clip_x1 = (clip_x1 * 512) + aw->x0 + 511;
+ info.clip_y1 = ((c->height - clip_y0) * 512) + aw->y0 + 511;
+ }
+ else {
+ info.clip_x0 = (clip_x0 * 512 / data->scale) + aw->x0 - 511;
+ info.clip_y0 = ((c->height - (clip_y1 / data->scale)) * 512) +
+ aw->y0 - 511;
+ info.clip_x1 = (clip_x1 * 512 / data->scale) + aw->x0 + 511;
+ info.clip_y1 = ((c->height - (clip_y0 / data->scale)) * 512) +
+ aw->y0 + 511;
+ }
+
+ info.print_lowx = 0;
+ info.print_lowy = 0;
+ info.print_handle = 0;
+ info.bgcolour = 0x20000000 | data->background_colour;
+
+ error = xos_read_vdu_variables(PTR_OS_VDU_VAR_LIST(&vars), vals);
+ if (error) {
+ LOG("xos_read_vdu_variables: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+
+ error = xwimp_read_palette((os_palette*)&vals[3]);
+ if (error) {
+ LOG("xwimp_read_palette: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+
+ source_data = content__get_source_data(c, &source_size);
+
+ error = awrender_render(source_data,
+ &info,
+ &matrix,
+ vals,
+ &aw->block,
+ &aw->size,
+ 110, /* fully anti-aliased */
+ 0,
+ source_size,
+ aw->render_routine,
+ aw->render_workspace);
+
+ if (error) {
+ LOG("awrender_render: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+
+ return true;
+}
+
+nserror artworks_clone(const struct content *old, struct content **newc)
+{
+ artworks_content *aw;
+ nserror error;
+
+ aw = calloc(1, sizeof(artworks_content));
+ if (aw == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__clone(old, &aw->base);
+ if (error != NSERROR_OK) {
+ content_destroy(&aw->base);
+ return error;
+ }
+
+ /* Simply re-run convert */
+ if (old->status == CONTENT_STATUS_READY ||
+ old->status == CONTENT_STATUS_DONE) {
+ if (artworks_convert(&aw->base) == false) {
+ content_destroy(&aw->base);
+ return NSERROR_CLONE_FAILED;
+ }
+ }
+
+ *newc = (struct content *) aw;
+
+ return NSERROR_OK;
+}
+
+content_type artworks_content_type(void)
+{
+ return CONTENT_IMAGE;
+}
+
+#endif
diff --git a/frontends/riscos/content-handlers/artworks.h b/frontends/riscos/content-handlers/artworks.h
new file mode 100644
index 000000000..67832cc54
--- /dev/null
+++ b/frontends/riscos/content-handlers/artworks.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Content for image/x-artworks (RISC OS interface).
+ */
+
+#ifndef _NETSURF_RISCOS_ARTWORKS_H_
+#define _NETSURF_RISCOS_ARTWORKS_H_
+
+#include "utils/config.h"
+#include "utils/errors.h"
+
+#ifdef WITH_ARTWORKS
+
+nserror artworks_init(void);
+
+#else
+
+static inline nserror artworks_init(void)
+{
+ return NSERROR_OK;
+}
+
+#endif
+
+#endif
diff --git a/frontends/riscos/content-handlers/awrender.s b/frontends/riscos/content-handlers/awrender.s
new file mode 100644
index 000000000..5bcafe520
--- /dev/null
+++ b/frontends/riscos/content-handlers/awrender.s
@@ -0,0 +1,390 @@
+#if defined(__aof__)
+ AREA |ARM$$code|,CODE,READONLY
+
+ IMPORT messages_get
+ IMPORT realloc
+ IMPORT strcpy
+ IMPORT |__rt_stkovf_split_big|
+
+ EXPORT awrender_init
+ EXPORT awrender_render
+
+
+aw_rsz_block * 0
+aw_rsz_size * 4
+aw_fixed_block * 8
+aw_fixed_size * 12
+aw_sl * 16
+aw_fp * 20
+sizeof_aw * 24
+
+
+; os_error *awrender_init(byte **doc, size_t *doc_size, void *init_routine, void *init_workspace);
+
+awrender_init MOV ip,sp
+ STMFD sp!,{a1,a2,v1,v2,fp,ip,lr,pc}
+ SUB fp,ip,#4
+ SUB ip,sp,#512
+ CMP ip,sl
+ BLMI |__rt_stkovf_split_big|
+
+ LDR v2,=aw_temp
+ LDR a1,[a1]
+ MOV v1,a3
+ LDR a3,[a2]
+ MOV ip,a4
+ STR a1,[v2,#aw_rsz_block]
+ STR a3,[v2,#aw_rsz_size]
+ MOV a2,#-1
+ STR a2,[v2,#aw_fixed_block]
+ STR a3,[v2,#aw_fixed_size]
+ STR sl,[v2,#aw_sl]
+ STR fp,[v2,#aw_fp]
+ ADR a2,aw_callback
+ MOV lr,pc
+ MOV pc,v1
+ MOVVC a1,#0
+
+ ;return updated block ptr & size to caller
+
+ LDR a2,[fp,#-28]
+ LDR a3,[fp,#-24]
+ LDR ip,[v2,#aw_rsz_block]
+ LDR lr,[v2,#aw_rsz_size]
+ STR ip,[a2]
+ STR lr,[a3]
+
+ LDMEA fp,{v1,v2,fp,sp,pc}
+
+
+; os_error *awrender_render(const char *doc,
+; const struct awinfo_block *info,
+; const os_trfm *trans,
+; const int *vdu_vars,
+; char **rsz_block,
+; size_t *rsz_size,
+; int wysiwyg_setting,
+; int output_dest,
+; size_t doc_size,
+; void *routine,
+; void *workspace);
+
+awrender_render MOV ip,sp
+ STMFD sp!,{v1-v4,fp,ip,lr,pc}
+ SUB fp,ip,#4
+ SUB ip,sp,#512
+ CMP ip,sl
+ BLMI |__rt_stkovf_split_big|
+
+ LDR R12,[fp,#20]
+ LDR R14,=aw_temp
+ LDR R5,[fp,#4]
+ LDR R6,[fp,#12]
+ LDR R4,[R5] ;resizable block
+ LDR R7,[fp,#16]
+ STR R4,[R14,#aw_rsz_block]
+ STR R0,[R14,#aw_fixed_block] ;document ptr
+ STR R12,[R14,#aw_fixed_size] ;document size
+ LDR R12,[fp,#8]
+
+ STR R5,[sp,#-4]! ;ptr to receive block
+ STR R12,[sp,#-4]! ;ptr to receive size
+
+ LDR R12,[R12]
+ ADR R5,aw_callback
+ STR R12,[R14,#aw_rsz_size]
+
+ STR sl,[R14,#aw_sl]
+ STR fp,[R14,#aw_fp]
+
+ LDR R12,[fp,#28]
+ MOV lr,pc
+ LDR pc,[fp,#24]
+ MOVVC a1,#0
+
+ ;return updated block ptr & size to caller
+
+ LDR R7,=aw_temp
+ LDR R12,[sp],#4
+ LDR R4,[sp],#4
+ LDR R5,[R7,#aw_rsz_size]
+ LDR R6,[R7,#aw_rsz_block]
+ STR R5,[R12]
+ STR R6,[R4]
+
+ LDMEA fp,{v1-v4,fp,sp,pc}
+
+
+; Callback routine for block resizing
+; (passed to AWRender init and render routines)
+;
+; entry R11 = reason code
+; 0 = CallBackReason_Memory
+; 3 = CallBackReason_Interface
+; (0 => return capabilities)
+; exit R0 => base of resizable block
+; R1 = size of resizable block
+; R2 => base of fixed block (or -1 if no fixed block)
+; R3 = size of fixed block (or document in resizable block)
+; VC if resize successful, VS and R0 => error otherwise
+
+aw_callback TEQ R11,#3
+ TEQEQ R0,#0
+ MOVEQ R0,#1<<10 ;background colour supplied
+ TEQ R11,#0
+ LDREQ R11,=aw_temp
+ MOVNE PC,R14
+
+ CMP R0,#-1 ;read block size?
+ LDRNE R2,[R11,#aw_rsz_size]
+ MOVNE R1,R0 ;new block size
+ LDR R0,[R11,#aw_rsz_block]
+ BEQ aw_read
+
+ ; Note: because ArtworksRenderer seems to call
+ ; this routine for every scanline rendered
+ ; we never call realloc unless we have to in
+ ; order to expand the block. Also it calls
+ ; us with a size request of 0 which we must
+ ; safely ignore otherwise rendering will stop.
+
+ CMP R1,R2
+ BLS aw_read
+
+ STMFD R13!,{R1,R10-R12,R14}
+ LDR sl,[R11,#aw_sl]
+ LDR fp,[R11,#aw_fp]
+ BL realloc
+ LDMFD R13!,{R1,R10-R12,R14}
+
+ CMP R0,#0 ;did it work?
+ BEQ aw_nomem
+
+ STR R0,[R11]
+ STR R1,[R11,#aw_rsz_size]
+
+aw_read ; return details of fixed block
+
+ LDR R2,[R11,#aw_fixed_block]
+ LDR R3,[R11,#aw_fixed_size]
+ SUBS R11,R11,R11 ;clear V
+ MOV PC,R14
+
+aw_nomem STMFD R13!,{R10,R12,R14}
+ LDR sl,[R11,#aw_sl]
+ LDR fp,[R11,#aw_fp]
+ ADR R0,tok_nomem
+ BL messages_get
+ MOV a2,a1
+ LDR a1,=errblk + 4
+ BL strcpy
+ SUB R0,R0,#4 ;error number already 0
+ MOV R11,#0 ;restore reason code
+ CMP PC,#1<<31 ;set V
+ LDMFD R13!,{R10,R12,PC}
+
+tok_nomem = "NoMemory",0
+ ALIGN
+
+
+ AREA |ARM$$zidata|,DATA,NOINIT
+
+aw_temp % sizeof_aw
+errblk % 256
+
+ END
+
+#elif defined(__ELF__)
+
+ .text
+
+.set aw_rsz_block, 0
+.set aw_rsz_size, 4
+.set aw_fixed_block, 8
+.set aw_fixed_size, 12
+.set aw_sl, 16
+.set aw_fp, 20
+.set sizeof_aw, 24
+
+@ os_error *awrender_init(byte **doc, size_t *doc_size, void *init_routine, void *init_workspace);
+
+ .global awrender_init
+awrender_init: MOV ip,sp
+ STMFD sp!,{a1,a2,v1,v2,fp,ip,lr,pc}
+ SUB fp,ip,#4
+ SUB ip,sp,#512
+ CMP ip,sl
+ BLMI __rt_stkovf_split_big
+
+ LDR v2,=aw_temp
+ LDR a1,[a1]
+ MOV v1,a3
+ LDR a3,[a2]
+ MOV ip,a4
+ STR a1,[v2,#aw_rsz_block]
+ STR a3,[v2,#aw_rsz_size]
+ MOV a2,#-1
+ STR a2,[v2,#aw_fixed_block]
+ STR a3,[v2,#aw_fixed_size]
+ STR sl,[v2,#aw_sl]
+ STR fp,[v2,#aw_fp]
+ ADR a2,aw_callback
+ MOV lr,pc
+ MOV pc,v1
+ MOVVC a1,#0
+
+ @ return updated block ptr & size to caller
+
+ LDR a2,[fp,#-28]
+ LDR a3,[fp,#-24]
+ LDR ip,[v2,#aw_rsz_block]
+ LDR lr,[v2,#aw_rsz_size]
+ STR ip,[a2]
+ STR lr,[a3]
+
+ LDMEA fp,{v1,v2,fp,sp,pc}
+
+
+@ os_error *awrender_render(const char *doc,
+@ const struct awinfo_block *info,
+@ const os_trfm *trans,
+@ const int *vdu_vars,
+@ char **rsz_block,
+@ size_t *rsz_size,
+@ int wysiwyg_setting,
+@ int output_dest,
+@ size_t doc_size,
+@ void *routine,
+@ void *workspace);
+
+ .global awrender_render
+awrender_render: MOV ip,sp
+ STMFD sp!,{v1-v4,fp,ip,lr,pc}
+ SUB fp,ip,#4
+ SUB ip,sp,#512
+ CMP ip,sl
+ BLMI __rt_stkovf_split_big
+
+ LDR R12,[fp,#20]
+ LDR R14,=aw_temp
+ LDR R5,[fp,#4]
+ LDR R6,[fp,#12]
+ LDR R4,[R5] @ resizable block
+ LDR R7,[fp,#16]
+ STR R4,[R14,#aw_rsz_block]
+ STR R0,[R14,#aw_fixed_block] @ document ptr
+ STR R12,[R14,#aw_fixed_size] @ document size
+ LDR R12,[fp,#8]
+
+ STR R5,[sp,#-4]! @ ptr to receive block
+ STR R12,[sp,#-4]! @ ptr to receive size
+
+ LDR R12,[R12]
+ ADR R5,aw_callback
+ STR R12,[R14,#aw_rsz_size]
+
+ STR sl,[R14,#aw_sl]
+ STR fp,[R14,#aw_fp]
+
+ LDR R12,[fp,#28]
+ MOV lr,pc
+ LDR pc,[fp,#24]
+ MOVVC a1,#0
+
+ @ return updated block ptr & size to caller
+
+ LDR R7,=aw_temp
+ LDR R12,[sp],#4
+ LDR R4,[sp],#4
+ LDR R5,[R7,#aw_rsz_size]
+ LDR R6,[R7,#aw_rsz_block]
+ STR R5,[R12]
+ STR R6,[R4]
+
+ LDMEA fp,{v1-v4,fp,sp,pc}
+
+
+@ Callback routine for block resizing
+@ (passed to AWRender init and render routines)
+@
+@ entry R11 = reason code
+@ 0 = CallBackReason_Memory
+@ 3 = CallBackReason_Interface
+@ (0 => return capabilities)
+@ exit R0 => base of resizable block
+@ R1 = size of resizable block
+@ R2 => base of fixed block (or -1 if no fixed block)
+@ R3 = size of fixed block (or document in resizable block)
+@ VC if resize successful, VS and R0 => error otherwise
+
+aw_callback: TEQ R11,#3
+ TEQEQ R0,#0
+ MOVEQ R0,#1<<10 @ background colour supplied
+ TEQ R11,#0
+ LDREQ R11,=aw_temp
+ MOVNE PC,R14
+
+ CMP R0,#-1 @ read block size?
+ LDRNE R2,[R11,#aw_rsz_size]
+ MOVNE R1,R0 @ new block size
+ LDR R0,[R11,#aw_rsz_block]
+ BEQ aw_read
+
+ @ Note: because ArtworksRenderer seems to call
+ @ this routine for every scanline rendered
+ @ we never call realloc unless we have to in
+ @ order to expand the block. Also it calls
+ @ us with a size request of 0 which we must
+ @ safely ignore otherwise rendering will stop.
+
+ CMP R1,R2
+ BLS aw_read
+
+ STMFD R13!,{R1,R10-R12,R14}
+ LDR sl,[R11,#aw_sl]
+ LDR fp,[R11,#aw_fp]
+ BL realloc
+ LDMFD R13!,{R1,R10-R12,R14}
+
+ CMP R0,#0 @ did it work?
+ BEQ aw_nomem
+
+ STR R0,[R11]
+ STR R1,[R11,#aw_rsz_size]
+
+aw_read: @ return details of fixed block
+
+ LDR R2,[R11,#aw_fixed_block]
+ LDR R3,[R11,#aw_fixed_size]
+ SUBS R11,R11,R11 @ clear V
+ MOV PC,R14
+
+aw_nomem: STMFD R13!,{R10,R12,R14}
+ LDR sl,[R11,#aw_sl]
+ LDR fp,[R11,#aw_fp]
+ ADR R0,tok_nomem
+ BL messages_get
+ MOV a2,a1
+ LDR a1,=errblk + 4
+ BL strcpy
+ SUB R0,R0,#4 @ error number already 0
+ MOV R11,#0 @ restore reason code
+ CMP PC,#1<<31 @ set V
+ LDMFD R13!,{R10,R12,PC}
+
+tok_nomem: .asciz "NoMemory"
+ .align
+
+ .bss
+
+aw_temp: .space sizeof_aw
+ .type aw_temp, %object
+ .size aw_temp, . - aw_temp
+
+errblk: .space 256
+ .type errblk, %object
+ .size errblk, . - errblk
+
+ .end
+#endif
+
diff --git a/frontends/riscos/content-handlers/draw.c b/frontends/riscos/content-handlers/draw.c
new file mode 100644
index 000000000..f2bee16dc
--- /dev/null
+++ b/frontends/riscos/content-handlers/draw.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Content for image/x-drawfile (RISC OS implementation).
+ *
+ * The DrawFile module is used to plot the DrawFile.
+ */
+
+#include "utils/config.h"
+#ifdef WITH_DRAW
+
+#include <string.h>
+#include <stdlib.h>
+#include "oslib/drawfile.h"
+
+#include "utils/config.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "content/content_protected.h"
+#include "desktop/plotters.h"
+
+#include "riscos/content-handlers/draw.h"
+#include "riscos/gui.h"
+
+typedef struct draw_content {
+ struct content base;
+
+ int x0, y0;
+} draw_content;
+
+static nserror draw_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c);
+static bool draw_convert(struct content *c);
+static void draw_destroy(struct content *c);
+static bool draw_redraw(struct content *c, struct content_redraw_data *data,
+ const struct rect *clip, const struct redraw_context *ctx);
+static nserror draw_clone(const struct content *old, struct content **newc);
+static content_type draw_content_type(void);
+
+static const content_handler draw_content_handler = {
+ .create = draw_create,
+ .data_complete = draw_convert,
+ .destroy = draw_destroy,
+ .redraw = draw_redraw,
+ .clone = draw_clone,
+ .type = draw_content_type,
+ .no_share = false,
+};
+
+static const char *draw_types[] = {
+ "application/drawfile",
+ "application/x-drawfile",
+ "image/drawfile",
+ "image/x-drawfile"
+};
+
+CONTENT_FACTORY_REGISTER_TYPES(draw, draw_types, draw_content_handler)
+
+nserror draw_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c)
+{
+ draw_content *draw;
+ nserror error;
+
+ draw = calloc(1, sizeof(draw_content));
+ if (draw == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__init(&draw->base, handler, imime_type, params,
+ llcache, fallback_charset, quirks);
+ if (error != NSERROR_OK) {
+ free(draw);
+ return error;
+ }
+
+ *c = (struct content *) draw;
+
+ return NSERROR_OK;
+}
+
+/**
+ * Convert a CONTENT_DRAW for display.
+ *
+ * No conversion is necessary. We merely read the DrawFile dimensions and
+ * bounding box bottom-left.
+ */
+
+bool draw_convert(struct content *c)
+{
+ draw_content *draw = (draw_content *) c;
+ union content_msg_data msg_data;
+ const char *source_data;
+ unsigned long source_size;
+ const void *data;
+ os_box bbox;
+ os_error *error;
+ char *title;
+
+ source_data = content__get_source_data(c, &source_size);
+ data = source_data;
+
+ /* BBox contents in Draw units (256*OS unit) */
+ error = xdrawfile_bbox(0, (drawfile_diagram *) data,
+ (int) source_size, 0, &bbox);
+ if (error) {
+ LOG("xdrawfile_bbox: 0x%x: %s", error->errnum, error->errmess);
+ msg_data.error = error->errmess;
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ if (bbox.x1 > bbox.x0 && bbox.y1 > bbox.y0) {
+ /* c->width & c->height stored as (OS units/2)
+ => divide by 512 to convert from draw units */
+ c->width = ((bbox.x1 - bbox.x0) / 512);
+ c->height = ((bbox.y1 - bbox.y0) / 512);
+ }
+ else
+ /* invalid/undefined bounding box */
+ c->height = c->width = 0;
+
+ draw->x0 = bbox.x0;
+ draw->y0 = bbox.y0;
+
+ title = messages_get_buff("DrawTitle",
+ nsurl_access_leaf(llcache_handle_get_url(c->llcache)),
+ c->width, c->height);
+ if (title != NULL) {
+ content__set_title(c, title);
+ free(title);
+ }
+
+ content_set_ready(c);
+ content_set_done(c);
+ /* Done: update status bar */
+ content_set_status(c, "");
+ return true;
+}
+
+
+/**
+ * Destroy a CONTENT_DRAW and free all resources it owns.
+ */
+
+void draw_destroy(struct content *c)
+{
+}
+
+
+/**
+ * Redraw a CONTENT_DRAW.
+ */
+
+bool draw_redraw(struct content *c, struct content_redraw_data *data,
+ const struct rect *clip, const struct redraw_context *ctx)
+{
+ draw_content *draw = (draw_content *) c;
+ os_trfm matrix;
+ const char *source_data;
+ unsigned long source_size;
+ const void *src_data;
+ os_error *error;
+
+ if (ctx->plot->flush && !ctx->plot->flush())
+ return false;
+
+ if (!c->width || !c->height)
+ return false;
+
+ source_data = content__get_source_data(c, &source_size);
+ src_data = source_data;
+
+ /* Scaled image. Transform units (65536*OS units) */
+ matrix.entries[0][0] = data->width * 65536 / c->width;
+ matrix.entries[0][1] = 0;
+ matrix.entries[1][0] = 0;
+ matrix.entries[1][1] = data->height * 65536 / c->height;
+ /* Draw units. (x,y) = bottom left */
+ matrix.entries[2][0] = ro_plot_origin_x * 256 + data->x * 512 -
+ draw->x0 * data->width / c->width;
+ matrix.entries[2][1] = ro_plot_origin_y * 256 -
+ (data->y + data->height) * 512 -
+ draw->y0 * data->height / c->height;
+
+ error = xdrawfile_render(0, (drawfile_diagram *) src_data,
+ (int) source_size, &matrix, 0, 0);
+ if (error) {
+ LOG("xdrawfile_render: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Clone a CONTENT_DRAW
+ */
+
+nserror draw_clone(const struct content *old, struct content **newc)
+{
+ draw_content *draw;
+ nserror error;
+
+ draw = calloc(1, sizeof(draw_content));
+ if (draw == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__clone(old, &draw->base);
+ if (error != NSERROR_OK) {
+ content_destroy(&draw->base);
+ return error;
+ }
+
+ /* Simply rerun convert */
+ if (old->status == CONTENT_STATUS_READY ||
+ old->status == CONTENT_STATUS_DONE) {
+ if (draw_convert(&draw->base) == false) {
+ content_destroy(&draw->base);
+ return NSERROR_CLONE_FAILED;
+ }
+ }
+
+ *newc = (struct content *) draw;
+
+ return NSERROR_OK;
+}
+
+content_type draw_content_type(void)
+{
+ return CONTENT_IMAGE;
+}
+
+#endif
diff --git a/frontends/riscos/content-handlers/draw.h b/frontends/riscos/content-handlers/draw.h
new file mode 100644
index 000000000..9f5baf6dc
--- /dev/null
+++ b/frontends/riscos/content-handlers/draw.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Content for image/x-drawfile (RISC OS interface).
+ */
+
+#ifndef _NETSURF_RISCOS_DRAW_H_
+#define _NETSURF_RISCOS_DRAW_H_
+
+#include "utils/config.h"
+#include "utils/errors.h"
+
+#ifdef WITH_DRAW
+
+nserror draw_init(void);
+
+#else
+
+static inline nserror draw_init(void)
+{
+ return NSERROR_OK;
+}
+
+#endif /* WITH_DRAW */
+
+#endif
diff --git a/frontends/riscos/content-handlers/sprite.c b/frontends/riscos/content-handlers/sprite.c
new file mode 100644
index 000000000..12fed4931
--- /dev/null
+++ b/frontends/riscos/content-handlers/sprite.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Content for image/x-riscos-sprite (RISC OS implementation).
+ *
+ * No conversion is necessary: we can render RISC OS sprites directly under
+ * RISC OS.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include "oslib/osspriteop.h"
+
+#include "utils/config.h"
+#include "utils/config.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "content/content_protected.h"
+#include "desktop/plotters.h"
+
+#include "riscos/gui.h"
+#include "riscos/image.h"
+#include "riscos/content-handlers/sprite.h"
+
+#ifdef WITH_SPRITE
+
+typedef struct sprite_content {
+ struct content base;
+
+ void *data;
+} sprite_content;
+
+static nserror sprite_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c);
+static bool sprite_convert(struct content *c);
+static void sprite_destroy(struct content *c);
+static bool sprite_redraw(struct content *c, struct content_redraw_data *data,
+ const struct rect *clip, const struct redraw_context *ctx);
+static nserror sprite_clone(const struct content *old, struct content **newc);
+static content_type sprite_content_type(void);
+
+static const content_handler sprite_content_handler = {
+ .create = sprite_create,
+ .data_complete = sprite_convert,
+ .destroy = sprite_destroy,
+ .redraw = sprite_redraw,
+ .clone = sprite_clone,
+ .type = sprite_content_type,
+ .no_share = false,
+};
+
+static const char *sprite_types[] = {
+ "image/x-riscos-sprite"
+};
+
+CONTENT_FACTORY_REGISTER_TYPES(sprite, sprite_types, sprite_content_handler)
+
+nserror sprite_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c)
+{
+ sprite_content *sprite;
+ nserror error;
+
+ sprite = calloc(1, sizeof(sprite_content));
+ if (sprite == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__init(&sprite->base, handler, imime_type, params,
+ llcache, fallback_charset, quirks);
+ if (error != NSERROR_OK) {
+ free(sprite);
+ return error;
+ }
+
+ *c = (struct content *) sprite;
+
+ return NSERROR_OK;
+}
+
+/**
+ * Convert a CONTENT_SPRITE for display.
+ *
+ * No conversion is necessary. We merely read the sprite dimensions.
+ */
+
+bool sprite_convert(struct content *c)
+{
+ sprite_content *sprite = (sprite_content *) c;
+ os_error *error;
+ int w, h;
+ union content_msg_data msg_data;
+ const char *source_data;
+ unsigned long source_size;
+ const void *sprite_data;
+ char *title;
+
+ source_data = content__get_source_data(c, &source_size);
+
+ sprite_data = source_data - 4;
+ osspriteop_area *area = (osspriteop_area*) sprite_data;
+ sprite->data = area;
+
+ /* check for bad data */
+ if ((int)source_size + 4 != area->used) {
+ msg_data.error = messages_get("BadSprite");
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ error = xosspriteop_read_sprite_info(osspriteop_PTR,
+ (osspriteop_area *)0x100,
+ (osspriteop_id) ((char *) area + area->first),
+ &w, &h, NULL, NULL);
+ if (error) {
+ LOG("xosspriteop_read_sprite_info: 0x%x: %s", error->errnum, error->errmess);
+ msg_data.error = error->errmess;
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ c->width = w;
+ c->height = h;
+
+ /* set title text */
+ title = messages_get_buff("SpriteTitle",
+ nsurl_access_leaf(llcache_handle_get_url(c->llcache)),
+ c->width, c->height);
+ if (title != NULL) {
+ content__set_title(c, title);
+ free(title);
+ }
+ content_set_ready(c);
+ content_set_done(c);
+ /* Done: update status bar */
+ content_set_status(c, "");
+ return true;
+}
+
+
+/**
+ * Destroy a CONTENT_SPRITE and free all resources it owns.
+ */
+
+void sprite_destroy(struct content *c)
+{
+ /* do not free c->data.sprite.data at it is simply a pointer to
+ * 4 bytes beforec->source_data. */
+}
+
+
+/**
+ * Redraw a CONTENT_SPRITE.
+ */
+
+bool sprite_redraw(struct content *c, struct content_redraw_data *data,
+ const struct rect *clip, const struct redraw_context *ctx)
+{
+ sprite_content *sprite = (sprite_content *) c;
+
+ if (ctx->plot->flush && !ctx->plot->flush())
+ return false;
+
+ return image_redraw(sprite->data,
+ ro_plot_origin_x + data->x * 2,
+ ro_plot_origin_y - data->y * 2,
+ data->width, data->height,
+ c->width,
+ c->height,
+ data->background_colour,
+ false, false, false,
+ IMAGE_PLOT_OS);
+}
+
+nserror sprite_clone(const struct content *old, struct content **newc)
+{
+ sprite_content *sprite;
+ nserror error;
+
+ sprite = calloc(1, sizeof(sprite_content));
+ if (sprite == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__clone(old, &sprite->base);
+ if (error != NSERROR_OK) {
+ content_destroy(&sprite->base);
+ return error;
+ }
+
+ /* Simply rerun convert */
+ if (old->status == CONTENT_STATUS_READY ||
+ old->status == CONTENT_STATUS_DONE) {
+ if (sprite_convert(&sprite->base) == false) {
+ content_destroy(&sprite->base);
+ return NSERROR_CLONE_FAILED;
+ }
+ }
+
+ *newc = (struct content *) sprite;
+
+ return NSERROR_OK;
+}
+
+content_type sprite_content_type(void)
+{
+ return CONTENT_IMAGE;
+}
+
+#endif
+
+
+/**
+ * Returns the bit depth of a sprite
+ *
+ * \param s sprite
+ * \return depth in bpp
+ */
+
+byte sprite_bpp(const osspriteop_header *s)
+{
+ /* bit 31 indicates the presence of a full alpha channel
+ * rather than a binary mask */
+ int type = ((unsigned)s->mode >> osspriteop_TYPE_SHIFT) & 15;
+ byte bpp = 0;
+
+ switch (type) {
+ case osspriteop_TYPE_OLD:
+ {
+ bits psr;
+ int val;
+ if (!xos_read_mode_variable(s->mode,
+ os_MODEVAR_LOG2_BPP, &val, &psr) &&
+ !(psr & _C))
+ bpp = 1 << val;
+ }
+ break;
+ case osspriteop_TYPE1BPP: bpp = 1; break;
+ case osspriteop_TYPE2BPP: bpp = 2; break;
+ case osspriteop_TYPE4BPP: bpp = 4; break;
+ case osspriteop_TYPE8BPP: bpp = 8; break;
+ case osspriteop_TYPE16BPP: bpp = 16; break;
+ case osspriteop_TYPE32BPP: bpp = 32; break;
+ case osspriteop_TYPE_CMYK: bpp = 32; break;
+ }
+ return bpp;
+}
diff --git a/frontends/riscos/content-handlers/sprite.h b/frontends/riscos/content-handlers/sprite.h
new file mode 100644
index 000000000..ab6d312a5
--- /dev/null
+++ b/frontends/riscos/content-handlers/sprite.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Content for image/x-riscos-sprite (RISC OS interface).
+ */
+
+#ifndef _NETSURF_RISCOS_SPRITE_H_
+#define _NETSURF_RISCOS_SPRITE_H_
+
+#include "utils/config.h"
+#include "utils/errors.h"
+
+#ifdef WITH_SPRITE
+
+nserror sprite_init(void);
+
+#else
+
+static inline nserror sprite_init(void)
+{
+ return NSERROR_OK;
+}
+
+#endif
+
+byte sprite_bpp(const osspriteop_header *s);
+
+#endif
diff --git a/frontends/riscos/cookies.c b/frontends/riscos/cookies.c
new file mode 100644
index 000000000..93c9f39cf
--- /dev/null
+++ b/frontends/riscos/cookies.c
@@ -0,0 +1,385 @@
+/*
+ * Copyright 2006 Richard Wilson <info@tinct.net>
+ * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Cookies (implementation).
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "oslib/wimp.h"
+#include "oslib/wimpspriteop.h"
+
+#include "utils/nsoption.h"
+#include "utils/messages.h"
+#include "utils/log.h"
+#include "content/urldb.h"
+#include "desktop/cookie_manager.h"
+#include "desktop/tree.h"
+#include "desktop/textinput.h"
+
+#include "riscos/cookies.h"
+#include "riscos/dialog.h"
+#include "riscos/menus.h"
+#include "riscos/toolbar.h"
+#include "riscos/treeview.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+
+static void ro_gui_cookies_toolbar_update_buttons(void);
+static void ro_gui_cookies_toolbar_save_buttons(char *config);
+static bool ro_gui_cookies_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_pointer *pointer);
+static void ro_gui_cookies_menu_warning(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action);
+static bool ro_gui_cookies_menu_select(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action);
+static void ro_gui_cookies_toolbar_click(button_bar_action action);
+
+struct ro_treeview_callbacks ro_cookies_treeview_callbacks = {
+ ro_gui_cookies_toolbar_click,
+ ro_gui_cookies_toolbar_update_buttons,
+ ro_gui_cookies_toolbar_save_buttons
+};
+
+/* The RISC OS cookie window, toolbar and treeview data. */
+
+static struct ro_cookies_window {
+ wimp_w window;
+ struct toolbar *toolbar;
+ ro_treeview *tv;
+ wimp_menu *menu;
+} cookies_window;
+
+/**
+ * Pre-Initialise the cookies tree. This is called for things that
+ * need to be done at the gui_init() stage, such as loading templates.
+ */
+
+void ro_gui_cookies_preinitialise(void)
+{
+ /* Create our window. */
+
+ cookies_window.window = ro_gui_dialog_create("tree");
+ ro_gui_set_window_title(cookies_window.window,
+ messages_get("Cookies"));
+}
+
+/**
+ * Initialise cookies tree, at the gui_init2() stage.
+ */
+
+void ro_gui_cookies_postinitialise(void)
+{
+ /* Create our toolbar. */
+
+ cookies_window.toolbar = ro_toolbar_create(NULL, cookies_window.window,
+ THEME_STYLE_COOKIES_TOOLBAR, TOOLBAR_FLAGS_NONE,
+ ro_treeview_get_toolbar_callbacks(), NULL,
+ "HelpCookiesToolbar");
+ if (cookies_window.toolbar != NULL) {
+ ro_toolbar_add_buttons(cookies_window.toolbar,
+ cookies_toolbar_buttons,
+ nsoption_charp(toolbar_cookies));
+ ro_toolbar_rebuild(cookies_window.toolbar);
+ }
+
+ /* Create the treeview with the window and toolbar. */
+
+ cookies_window.tv = ro_treeview_create(cookies_window.window,
+ cookies_window.toolbar, &ro_cookies_treeview_callbacks,
+ TREE_COOKIES);
+ if (cookies_window.tv == NULL) {
+ LOG("Failed to allocate treeview");
+ return;
+ }
+
+ ro_toolbar_update_client_data(cookies_window.toolbar,
+ cookies_window.tv);
+
+ /* Build the cookies window menu. */
+
+ static const struct ns_menu cookies_definition = {
+ "Cookies", {
+ { "Cookies", NO_ACTION, 0 },
+ { "Cookies.Expand", TREE_EXPAND_ALL, 0 },
+ { "Cookies.Expand.All", TREE_EXPAND_ALL, 0 },
+ { "Cookies.Expand.Folders", TREE_EXPAND_FOLDERS, 0 },
+ { "Cookies.Expand.Links", TREE_EXPAND_LINKS, 0 },
+ { "Cookies.Collapse", TREE_COLLAPSE_ALL, 0 },
+ { "Cookies.Collapse.All", TREE_COLLAPSE_ALL, 0 },
+ { "Cookies.Collapse.Folders", TREE_COLLAPSE_FOLDERS, 0 },
+ { "Cookies.Collapse.Links", TREE_COLLAPSE_LINKS, 0 },
+ { "Cookies.Toolbars", NO_ACTION, 0 },
+ { "_Cookies.Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 },
+ { "Cookies.Toolbars.EditToolbar",TOOLBAR_EDIT, 0 },
+ { "Selection", TREE_SELECTION, 0 },
+ { "Selection.Delete", TREE_SELECTION_DELETE, 0 },
+ { "SelectAll", TREE_SELECT_ALL, 0 },
+ { "Clear", TREE_CLEAR_SELECTION, 0 },
+ {NULL, 0, 0}
+ }
+ };
+ cookies_window.menu = ro_gui_menu_define_menu(&cookies_definition);
+
+ ro_gui_wimp_event_register_menu(cookies_window.window,
+ cookies_window.menu, false, false);
+ ro_gui_wimp_event_register_menu_prepare(cookies_window.window,
+ ro_gui_cookies_menu_prepare);
+ ro_gui_wimp_event_register_menu_selection(cookies_window.window,
+ ro_gui_cookies_menu_select);
+ ro_gui_wimp_event_register_menu_warning(cookies_window.window,
+ ro_gui_cookies_menu_warning);
+}
+
+/**
+ * Destroy the cookies window.
+ */
+
+void ro_gui_cookies_destroy(void)
+{
+ if (cookies_window.tv == NULL)
+ return;
+
+ ro_treeview_destroy(cookies_window.tv);
+}
+
+/**
+ * Open the cookies window.
+ *
+ */
+
+void ro_gui_cookies_open(void)
+{
+ ro_gui_cookies_toolbar_update_buttons();
+
+ if (!ro_gui_dialog_open_top(cookies_window.window,
+ cookies_window.toolbar, 600, 800)) {
+ ro_treeview_set_origin(cookies_window.tv, 0,
+ -(ro_toolbar_height(cookies_window.toolbar)));
+ }
+}
+
+
+/**
+ * Handle toolbar button clicks.
+ *
+ * \param action The action to handle
+ */
+
+void ro_gui_cookies_toolbar_click(button_bar_action action)
+{
+ switch (action) {
+ case TOOLBAR_BUTTON_DELETE:
+ cookie_manager_keypress(NS_KEY_DELETE_LEFT);
+ break;
+
+ case TOOLBAR_BUTTON_EXPAND:
+ cookie_manager_expand(false);
+ break;
+
+ case TOOLBAR_BUTTON_COLLAPSE:
+ cookie_manager_contract(false);
+ break;
+
+ case TOOLBAR_BUTTON_OPEN:
+ cookie_manager_expand(true);
+ break;
+
+ case TOOLBAR_BUTTON_CLOSE:
+ cookie_manager_contract(true);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/**
+ * Update the button state in the cookies toolbar.
+ */
+
+void ro_gui_cookies_toolbar_update_buttons(void)
+{
+ ro_toolbar_set_button_shaded_state(cookies_window.toolbar,
+ TOOLBAR_BUTTON_DELETE,
+ !cookie_manager_has_selection());
+}
+
+
+/**
+ * Save a new button arrangement in the cookies toolbar.
+ *
+ * \param *config The new button configuration string.
+ */
+
+void ro_gui_cookies_toolbar_save_buttons(char *config)
+{
+ nsoption_set_charp(toolbar_cookies, config);
+ ro_gui_save_options();
+}
+
+
+/**
+ * Prepare the cookies menu for opening
+ *
+ * \param w The window owning the menu.
+ * \param i The icon owning the menu.
+ * \param *menu The menu about to be opened.
+ * \param *pointer Pointer to the relevant wimp event block, or
+ * NULL for an Adjust click.
+ * \return true if the event was handled; else false.
+ */
+
+bool ro_gui_cookies_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_pointer *pointer)
+{
+ bool selection;
+
+ if (menu != cookies_window.menu)
+ return false;
+
+ selection = cookie_manager_has_selection();
+
+ ro_gui_menu_set_entry_shaded(cookies_window.menu,
+ TREE_SELECTION, !selection);
+ ro_gui_menu_set_entry_shaded(cookies_window.menu,
+ TREE_CLEAR_SELECTION, !selection);
+
+ ro_gui_menu_set_entry_shaded(menu, TOOLBAR_BUTTONS,
+ ro_toolbar_menu_option_shade(cookies_window.toolbar));
+ ro_gui_menu_set_entry_ticked(menu, TOOLBAR_BUTTONS,
+ ro_toolbar_menu_buttons_tick(cookies_window.toolbar));
+
+ ro_gui_menu_set_entry_shaded(menu, TOOLBAR_EDIT,
+ ro_toolbar_menu_edit_shade(cookies_window.toolbar));
+ ro_gui_menu_set_entry_ticked(menu, TOOLBAR_EDIT,
+ ro_toolbar_menu_edit_tick(cookies_window.toolbar));
+
+ return true;
+}
+
+/**
+ * Handle submenu warnings for the cookies menu
+ *
+ * \param w The window owning the menu.
+ * \param i The icon owning the menu.
+ * \param *menu The menu to which the warning applies.
+ * \param *selection The wimp menu selection data.
+ * \param action The selected menu action.
+ */
+
+void ro_gui_cookies_menu_warning(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action)
+{
+ /* Do nothing */
+}
+
+/**
+ * Handle selections from the cookies menu
+ *
+ * \param w The window owning the menu.
+ * \param i The icon owning the menu.
+ * \param *menu The menu from which the selection was made.
+ * \param *selection The wimp menu selection data.
+ * \param action The selected menu action.
+ * \return true if action accepted; else false.
+ */
+
+bool ro_gui_cookies_menu_select(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action)
+{
+ switch (action) {
+ case TREE_EXPAND_ALL:
+ cookie_manager_expand(false);
+ return true;
+ case TREE_EXPAND_FOLDERS:
+ cookie_manager_expand(true);
+ return true;
+ case TREE_EXPAND_LINKS:
+ cookie_manager_expand(false);
+ return true;
+ case TREE_COLLAPSE_ALL:
+ cookie_manager_contract(true);
+ return true;
+ case TREE_COLLAPSE_FOLDERS:
+ cookie_manager_contract(true);
+ return true;
+ case TREE_COLLAPSE_LINKS:
+ cookie_manager_contract(false);
+ return true;
+ case TREE_SELECTION_DELETE:
+ cookie_manager_keypress(NS_KEY_DELETE_LEFT);
+ return true;
+ case TREE_SELECT_ALL:
+ cookie_manager_keypress(NS_KEY_SELECT_ALL);
+ return true;
+ case TREE_CLEAR_SELECTION:
+ cookie_manager_keypress(NS_KEY_CLEAR_SELECTION);
+ return true;
+ case TOOLBAR_BUTTONS:
+ ro_toolbar_set_display_buttons(cookies_window.toolbar,
+ !ro_toolbar_get_display_buttons(
+ cookies_window.toolbar));
+ return true;
+ case TOOLBAR_EDIT:
+ ro_toolbar_toggle_edit(cookies_window.toolbar);
+ return true;
+ default:
+ return false;
+ }
+
+ return false;
+}
+
+/**
+ * Check if a particular window handle is the cookies window
+ *
+ * \param window the window in question
+ * \return true if this window is the cookies
+ */
+
+bool ro_gui_cookies_check_window(wimp_w window)
+{
+ if (cookies_window.window == window)
+ return true;
+ else
+ return false;
+}
+
+/**
+ * Check if a particular menu handle is the cookies menu
+ *
+ * \param *menu The menu in question.
+ * \return true if this menu is the cookies menu
+ */
+
+bool ro_gui_cookies_check_menu(wimp_menu *menu)
+{
+ if (cookies_window.menu == menu)
+ return true;
+ else
+ return false;
+}
+
diff --git a/frontends/riscos/cookies.h b/frontends/riscos/cookies.h
new file mode 100644
index 000000000..b7313393e
--- /dev/null
+++ b/frontends/riscos/cookies.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2006 Richard Wilson <info@tinct.net>
+ * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Cookies (interface).
+ */
+
+#ifndef _NETSURF_RISCOS_COOKIES_H_
+#define _NETSURF_RISCOS_COOKIES_H_
+
+#include "riscos/menus.h"
+
+void ro_gui_cookies_preinitialise(void);
+void ro_gui_cookies_postinitialise(void);
+void ro_gui_cookies_destroy(void);
+bool ro_gui_cookies_check_window(wimp_w window);
+bool ro_gui_cookies_check_menu(wimp_menu *menu);
+
+void ro_gui_cookies_open(void);
+
+#endif
+
diff --git a/frontends/riscos/dialog.c b/frontends/riscos/dialog.c
new file mode 100644
index 000000000..d4356086d
--- /dev/null
+++ b/frontends/riscos/dialog.c
@@ -0,0 +1,816 @@
+/*
+ * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2004 Andrew Timmins <atimmins@blueyonder.co.uk>
+ * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net>
+ * Copyright 2014 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "utils/config.h"
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <oslib/colourtrans.h>
+#include <oslib/osfile.h>
+#include <oslib/osgbpb.h>
+#include <oslib/osspriteop.h>
+#include <oslib/wimp.h>
+
+#include "utils/nsoption.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/nsurl.h"
+#include "desktop/version.h"
+#include "desktop/browser.h"
+
+#include "riscos/configure.h"
+#include "riscos/cookies.h"
+#include "riscos/dialog.h"
+#include "riscos/global_history.h"
+#include "riscos/gui.h"
+#include "riscos/hotlist.h"
+#include "riscos/menus.h"
+#include "riscos/save.h"
+#include "riscos/sslcert.h"
+#include "riscos/toolbar.h"
+#include "riscos/url_complete.h"
+#include "riscos/url_suggest.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/wimputils.h"
+
+#define ICON_ZOOM_VALUE 1
+#define ICON_ZOOM_DEC 2
+#define ICON_ZOOM_INC 3
+#define ICON_ZOOM_FRAMES 5
+#define ICON_ZOOM_CANCEL 7
+#define ICON_ZOOM_OK 8
+
+/* The maximum number of persistent dialogues
+*/
+#define MAX_PERSISTENT 64
+
+
+wimp_w dialog_info, dialog_saveas,
+ dialog_401li,
+ dialog_zoom, dialog_pageinfo, dialog_objinfo, dialog_tooltip,
+ dialog_warning,
+ dialog_folder, dialog_entry, dialog_search, dialog_print,
+ dialog_url_complete, dialog_openurl;
+
+struct gui_window *ro_gui_current_zoom_gui;
+
+
+/* A simple mapping of parent and child
+*/
+static struct {
+ wimp_w dialog;
+ wimp_w parent;
+} persistent_dialog[MAX_PERSISTENT];
+
+
+static bool ro_gui_dialog_open_url_init(void);
+static bool ro_gui_dialog_openurl_apply(wimp_w w);
+static bool ro_gui_dialog_open_url_menu_prepare(wimp_w w, wimp_i i,
+ wimp_menu *menu, wimp_pointer *pointer);
+
+static bool ro_gui_dialog_zoom_apply(wimp_w w);
+
+/**
+ * Load and create dialogs from template file.
+ */
+
+void ro_gui_dialog_init(void)
+{
+ /* warning dialog */
+ dialog_warning = ro_gui_dialog_create("warning");
+ ro_gui_wimp_event_register_ok(dialog_warning, ICON_WARNING_CONTINUE,
+ NULL);
+ ro_gui_wimp_event_set_help_prefix(dialog_warning, "HelpWarning");
+
+ /* tooltip for history */
+ dialog_tooltip = ro_gui_dialog_create("tooltip");
+
+ /* configure window */
+ ro_gui_configure_initialise();
+
+ /* 401 login window */
+ ro_gui_401login_init();
+
+ /* theme installation */
+ dialog_theme_install = ro_gui_dialog_create("theme_inst");
+ ro_gui_wimp_event_register_cancel(dialog_theme_install,
+ ICON_THEME_INSTALL_CANCEL);
+ ro_gui_wimp_event_register_ok(dialog_theme_install,
+ ICON_THEME_INSTALL_INSTALL,
+ ro_gui_theme_install_apply);
+ ro_gui_wimp_event_set_help_prefix(dialog_theme_install, "HelpThemeInst");
+
+ /* search */
+ ro_gui_search_init();
+
+ /* print */
+ ro_gui_print_init();
+
+ /* about us */
+ dialog_info = ro_gui_dialog_create("info");
+ ro_gui_set_icon_string(dialog_info, 4, netsurf_version, true);
+ ro_gui_wimp_event_set_help_prefix(dialog_info, "HelpAppInfo");
+
+ /* page info */
+ dialog_pageinfo = ro_gui_dialog_create("pageinfo");
+ ro_gui_wimp_event_set_help_prefix(dialog_pageinfo, "HelpPageInfo");
+
+ /* object info */
+ dialog_objinfo = ro_gui_dialog_create("objectinfo");
+ ro_gui_wimp_event_set_help_prefix(dialog_objinfo, "HelpObjInfo");
+
+ /* save as */
+ dialog_saveas = ro_gui_saveas_create("saveas");
+ ro_gui_wimp_event_register_button(dialog_saveas, ICON_SAVE_ICON,
+ ro_gui_save_start_drag);
+ ro_gui_wimp_event_register_text_field(dialog_saveas, ICON_SAVE_PATH);
+ ro_gui_wimp_event_register_cancel(dialog_saveas, ICON_SAVE_CANCEL);
+ ro_gui_wimp_event_register_ok(dialog_saveas, ICON_SAVE_OK,
+ ro_gui_save_ok);
+ ro_gui_wimp_event_set_help_prefix(dialog_saveas, "HelpSaveAs");
+
+ /* url suggestion */
+ dialog_url_complete = ro_gui_dialog_create("url_suggest");
+ ro_gui_wimp_event_register_mouse_click(dialog_url_complete,
+ ro_gui_url_complete_click);
+ ro_gui_wimp_event_register_pointer_entering_window(dialog_url_complete,
+ ro_gui_url_complete_entering);
+ ro_gui_wimp_event_register_redraw_window(dialog_url_complete,
+ ro_gui_url_complete_redraw);
+ ro_gui_wimp_event_set_help_prefix(dialog_url_complete, "HelpAutoURL");
+
+ /* open URL */
+ ro_gui_dialog_open_url_init();
+
+ /* scale view */
+ dialog_zoom = ro_gui_dialog_create("zoom");
+ ro_gui_wimp_event_register_numeric_field(dialog_zoom, ICON_ZOOM_VALUE,
+ ICON_ZOOM_INC, ICON_ZOOM_DEC, 10, 1600, 10, 0);
+ ro_gui_wimp_event_register_checkbox(dialog_zoom, ICON_ZOOM_FRAMES);
+ ro_gui_wimp_event_register_cancel(dialog_zoom, ICON_ZOOM_CANCEL);
+ ro_gui_wimp_event_register_ok(dialog_zoom, ICON_ZOOM_OK,
+ ro_gui_dialog_zoom_apply);
+ ro_gui_wimp_event_set_help_prefix(dialog_zoom, "HelpScaleView");
+
+ /* Treeview initialisation has moved to the end, to allow any
+ * associated dialogues to be set up first.
+ */
+
+ /* certificate verification window */
+ ro_gui_cert_preinitialise();
+
+ /* hotlist window */
+ ro_gui_hotlist_preinitialise();
+
+ /* global history window */
+ ro_gui_global_history_preinitialise();
+
+ /* cookies window */
+ ro_gui_cookies_preinitialise();
+}
+
+
+/**
+ * Create a window from a template.
+ *
+ * \param template_name name of template to load
+ * \return window handle
+ *
+ * Exits through die() on error.
+ */
+
+wimp_w ro_gui_dialog_create(const char *template_name)
+{
+ wimp_window *window;
+ wimp_w w;
+ os_error *error;
+
+ window = ro_gui_dialog_load_template(template_name);
+
+ /* create window */
+ window->sprite_area = gui_sprites;
+ error = xwimp_create_window(window, &w);
+ if (error) {
+ LOG("xwimp_create_window: 0x%x: %s", error->errnum, error->errmess);
+ xwimp_close_template();
+ die(error->errmess);
+ }
+
+ /* the window definition is copied by the wimp and may be freed */
+ free(window);
+
+ return w;
+}
+
+
+/**
+ * Load a template without creating a window.
+ *
+ * \param template_name name of template to load
+ * \return window block
+ *
+ * Exits through die() on error.
+ */
+
+wimp_window * ro_gui_dialog_load_template(const char *template_name)
+{
+ char name[20];
+ int context, window_size, data_size;
+ char *data;
+ wimp_window *window;
+ os_error *error;
+
+ /* Template names must be <= 11 chars long */
+ assert(strlen(template_name) <= 11);
+
+ /* wimp_load_template won't accept a const char * */
+ strncpy(name, template_name, sizeof name);
+
+ /* find required buffer sizes */
+ error = xwimp_load_template(wimp_GET_SIZE, 0, 0, wimp_NO_FONTS,
+ name, 0, &window_size, &data_size, &context);
+ if (error) {
+ LOG("xwimp_load_template: 0x%x: %s", error->errnum, error->errmess);
+ xwimp_close_template();
+ die(error->errmess);
+ }
+ if (!context) {
+ LOG("template '%s' missing", template_name);
+ xwimp_close_template();
+ die("Template");
+ }
+
+ /* allocate space for indirected data and temporary window buffer */
+ data = malloc(data_size);
+ window = malloc(window_size);
+ if (!data || !window) {
+ xwimp_close_template();
+ die("NoMemory");
+ }
+
+ /* load template */
+ error = xwimp_load_template(window, data, data + data_size,
+ wimp_NO_FONTS, name, 0, 0, 0, 0);
+ if (error) {
+ LOG("xwimp_load_template: 0x%x: %s", error->errnum, error->errmess);
+ xwimp_close_template();
+ die(error->errmess);
+ }
+
+ return window;
+}
+
+
+/**
+ * Open a dialog box, centred on the screen.
+ */
+
+void ro_gui_dialog_open(wimp_w w)
+{
+ int screen_x, screen_y, dx, dy;
+ wimp_window_state state;
+ os_error *error;
+
+ /* find screen centre in os units */
+ ro_gui_screen_size(&screen_x, &screen_y);
+ screen_x /= 2;
+ screen_y /= 2;
+
+ /* centre and open */
+ state.w = w;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ dx = (state.visible.x1 - state.visible.x0) / 2;
+ dy = (state.visible.y1 - state.visible.y0) / 2;
+ state.visible.x0 = screen_x - dx;
+ state.visible.x1 = screen_x + dx;
+ state.visible.y0 = screen_y - dy;
+ state.visible.y1 = screen_y + dy;
+ state.next = wimp_TOP;
+ ro_gui_open_window_request(PTR_WIMP_OPEN(&state));
+
+ /* Set the caret position */
+ ro_gui_set_caret_first(w);
+}
+
+
+/**
+ * Close a dialog box.
+ */
+
+void ro_gui_dialog_close(wimp_w close)
+{
+ int i;
+ wimp_caret caret;
+ os_error *error;
+
+ /* Check if we're a persistent window */
+ for (i = 0; i < MAX_PERSISTENT; i++) {
+ if (persistent_dialog[i].dialog == close) {
+ /* We are => invalidate record */
+ persistent_dialog[i].parent = NULL;
+ persistent_dialog[i].dialog = NULL;
+ break;
+ }
+ }
+
+ /* Close any child windows */
+ ro_gui_dialog_close_persistent(close);
+
+ /* Give the caret back to the parent window. This code relies on
+ the fact that only tree windows and browser windows open
+ persistent dialogues, as the caret gets placed to no icon.
+ */
+ error = xwimp_get_caret_position(&caret);
+ if (error) {
+ LOG("xwimp_get_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ } else if (caret.w == close) {
+ /* Check if we are a persistent window */
+ if (i < MAX_PERSISTENT) {
+ error = xwimp_set_caret_position(
+ persistent_dialog[i].parent,
+ wimp_ICON_WINDOW, -100, -100,
+ 32, -1);
+ /* parent may have been closed first */
+ if ((error) && (error->errnum != 0x287)) {
+ LOG("xwimp_set_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ }
+ }
+
+ error = xwimp_close_window(close);
+ if (error) {
+ LOG("xwimp_close_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+}
+
+
+/**
+ * Moves a window to the top of the stack.
+ *
+ * If the window is currently closed then:
+ *
+ * * The window is opened in the centre of the screen (at the supplied size)
+ * * Any toolbar editing session is stopped
+ * * The scroll position is set to the top of the window
+ *
+ * If the window is currently open then:
+ *
+ * * The window is brought to the top of the stack
+ *
+ * \param w the window to show
+ * \param toolbar the toolbar to consider
+ * \param width the window width if it is currently closed (or 0 to retain)
+ * \param height the window height if it is currently closed (or 0 to retain)
+ * \return true if the window was previously open
+ */
+bool ro_gui_dialog_open_top(wimp_w w, struct toolbar *toolbar,
+ int width, int height) {
+ os_error *error;
+ int screen_width, screen_height;
+ wimp_window_state state;
+ bool open;
+
+ state.w = w;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ /* if we're open we jump to the top of the stack, if not then we
+ * open in the centre of the screen. */
+ open = state.flags & wimp_WINDOW_OPEN;
+ if (!open) {
+ int dimension;
+ int scroll_width;
+ /* cancel any editing */
+ if (ro_toolbar_get_editing(toolbar))
+ ro_toolbar_toggle_edit(toolbar);
+
+ /* move to the centre */
+ ro_gui_screen_size(&screen_width, &screen_height);
+ dimension = ((width == 0) ?
+ (state.visible.x1 - state.visible.x0) : width);
+ scroll_width = ro_get_vscroll_width(w);
+ state.visible.x0 = (screen_width - (dimension + scroll_width)) / 2;
+ state.visible.x1 = state.visible.x0 + dimension;
+ dimension = ((height == 0) ?
+ (state.visible.y1 - state.visible.y0) : height);
+ state.visible.y0 = (screen_height - dimension) / 2;
+ state.visible.y1 = state.visible.y0 + dimension;
+ state.xscroll = 0;
+ state.yscroll = 0;
+ if (toolbar)
+ state.yscroll = ro_toolbar_height(toolbar);
+ }
+
+ /* open the window at the top of the stack */
+ state.next = wimp_TOP;
+ ro_gui_open_window_request(PTR_WIMP_OPEN(&state));
+ return open;
+}
+
+
+/**
+ * Open window at the location of the pointer.
+ */
+
+void ro_gui_dialog_open_at_pointer(wimp_w w)
+{
+ wimp_pointer ptr;
+ os_error *error;
+
+ /* get the pointer position */
+ error = xwimp_get_pointer_info(&ptr);
+ if (error) {
+ LOG("xwimp_get_pointer_info: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ ro_gui_dialog_open_xy(w, ptr.pos.x - 64, ptr.pos.y);
+}
+
+
+/**
+ * Open window at a specified location.
+ */
+
+void ro_gui_dialog_open_xy(wimp_w w, int x, int y)
+{
+ wimp_window_state state;
+ os_error *error;
+ int dx, dy;
+
+ /* move the window */
+ state.w = w;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ dx = (state.visible.x1 - state.visible.x0);
+ dy = (state.visible.y1 - state.visible.y0);
+ state.visible.x0 = x;
+ state.visible.x1 = x + dx;
+ state.visible.y0 = y - dy;
+ state.visible.y1 = y;
+
+ /* if the window is already open, close it first so that it opens fully
+ * on screen */
+ error = xwimp_close_window(w);
+ if (error) {
+ LOG("xwimp_close_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* open the window at the top of the stack */
+ state.next = wimp_TOP;
+ ro_gui_open_window_request(PTR_WIMP_OPEN(&state));
+}
+
+
+/**
+ * Opens a window at the centre of either another window or the screen
+ *
+ * /param parent the parent window (NULL for centre of screen)
+ * /param child the child window
+ */
+void ro_gui_dialog_open_centre_parent(wimp_w parent, wimp_w child) {
+ os_error *error;
+ wimp_window_state state;
+ int mid_x, mid_y;
+ int dimension, scroll_width;
+
+ /* get the parent window state */
+ if (parent) {
+ state.w = parent;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ scroll_width = ro_get_vscroll_width(parent);
+ mid_x = (state.visible.x0 + state.visible.x1 + scroll_width);
+ mid_y = (state.visible.y0 + state.visible.y1);
+ } else {
+ ro_gui_screen_size(&mid_x, &mid_y);
+ }
+ mid_x /= 2;
+ mid_y /= 2;
+
+ /* get the child window state */
+ state.w = child;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* move to the centre of the parent at the top of the stack */
+ dimension = state.visible.x1 - state.visible.x0;
+ scroll_width = ro_get_vscroll_width(history_window);
+ state.visible.x0 = mid_x - (dimension + scroll_width) / 2;
+ state.visible.x1 = state.visible.x0 + dimension;
+ dimension = state.visible.y1 - state.visible.y0;
+ state.visible.y0 = mid_y - dimension / 2;
+ state.visible.y1 = state.visible.y0 + dimension;
+ state.next = wimp_TOP;
+ ro_gui_open_window_request(PTR_WIMP_OPEN(&state));
+}
+
+
+/**
+ * Open a persistent dialog box relative to the pointer.
+ *
+ * \param parent the owning window (NULL for no owner)
+ * \param w the dialog window
+ * \param pointer open the window at the pointer (centre of the parent
+ * otherwise)
+ */
+
+void ro_gui_dialog_open_persistent(wimp_w parent, wimp_w w, bool pointer) {
+
+ if (pointer)
+ ro_gui_dialog_open_at_pointer(w);
+ else
+ ro_gui_dialog_open_centre_parent(parent, w);
+
+ /* todo: use wimp_event definitions rather than special cases */
+ if ((w == dialog_pageinfo) || (w == dialog_objinfo))
+ ro_gui_wimp_update_window_furniture(w, wimp_WINDOW_CLOSE_ICON,
+ wimp_WINDOW_CLOSE_ICON);
+ ro_gui_dialog_add_persistent(parent, w);
+ ro_gui_set_caret_first(w);
+}
+
+
+void ro_gui_dialog_add_persistent(wimp_w parent, wimp_w w) {
+ int i;
+
+ /* all persistant windows have a back icon */
+ ro_gui_wimp_update_window_furniture(w, wimp_WINDOW_BACK_ICON,
+ wimp_WINDOW_BACK_ICON);
+
+ /* Add a mapping
+ */
+ if ((parent == NULL) || (parent == wimp_ICON_BAR))
+ return;
+ for (i = 0; i < MAX_PERSISTENT; i++) {
+ if (persistent_dialog[i].dialog == NULL ||
+ persistent_dialog[i].dialog == w) {
+ persistent_dialog[i].dialog = w;
+ persistent_dialog[i].parent = parent;
+ return;
+ }
+ }
+ LOG("Unable to map persistent dialog to parent.");
+ return;
+}
+
+
+/**
+ * Close persistent dialogs associated with a window.
+ *
+ * \param parent the window to close children of
+ */
+
+void ro_gui_dialog_close_persistent(wimp_w parent) {
+ int i;
+ wimp_w w;
+
+ /* Check our mappings.
+ *
+ * The window handle is copied into w before proceeding, as
+ * ro_gui_dialog_close() will NULL persistent_dialog[i].dialog as
+ * part of the closing process. This would mean that the subsequent
+ * event dispatch would fail. (These events are logged to allow
+ * side effects to be investigated -- this code hasn't worked before).
+ */
+ for (i = 0; i < MAX_PERSISTENT; i++) {
+ if (persistent_dialog[i].parent == parent &&
+ persistent_dialog[i].dialog != NULL) {
+ w = persistent_dialog[i].dialog;
+ ro_gui_dialog_close(w);
+ if (ro_gui_wimp_event_close_window(w))
+ LOG("Persistent dialog close event: 0x%x", (unsigned)w);
+ persistent_dialog[i].parent = NULL;
+ persistent_dialog[i].dialog = NULL;
+ }
+ }
+}
+
+
+/**
+ * Save the current options.
+ */
+
+void ro_gui_save_options(void)
+{
+ nsoption_write("<NetSurf$ChoicesSave>", NULL, NULL);
+}
+
+bool ro_gui_dialog_zoom_apply(wimp_w w) {
+ unsigned int scale;
+ bool all;
+
+ scale = atoi(ro_gui_get_icon_string(w, ICON_ZOOM_VALUE));
+ all = ro_gui_get_icon_selected_state(w, ICON_ZOOM_FRAMES);
+ ro_gui_window_set_scale(ro_gui_current_zoom_gui, scale * 0.01);
+ return true;
+}
+
+
+/**
+ * Prepares the Scale view dialog.
+ */
+
+void ro_gui_dialog_prepare_zoom(struct gui_window *g)
+{
+ char scale_buffer[8];
+ sprintf(scale_buffer, "%.0f", browser_window_get_scale(g->bw) * 100);
+ ro_gui_set_icon_string(dialog_zoom, ICON_ZOOM_VALUE, scale_buffer, true);
+ ro_gui_set_icon_selected_state(dialog_zoom, ICON_ZOOM_FRAMES, true);
+ ro_gui_set_icon_shaded_state(dialog_zoom, ICON_ZOOM_FRAMES, true);
+ ro_gui_current_zoom_gui = g;
+ ro_gui_wimp_event_memorise(dialog_zoom);
+}
+
+/**
+ * Update the Scale View dialog to reflect the current window settings
+ *
+ * \param g the gui_window to update for
+ */
+void ro_gui_dialog_update_zoom(struct gui_window *g) {
+ if (g == ro_gui_current_zoom_gui)
+ ro_gui_dialog_prepare_zoom(g);
+}
+
+
+/**
+ * Create the Open URL dialogue, allocating storage for the URL field icon
+ * as we go.
+ *
+ * \return true on success; false on failure (although errors with
+ * the templates or memory allocation will exit via die()).
+ */
+
+static bool ro_gui_dialog_open_url_init(void)
+{
+ wimp_window *definition;
+ char *buffer;
+ os_error *error;
+
+ definition = ro_gui_dialog_load_template("open_url");
+
+ /* _load_template() should die on any error, so we trust its data. */
+
+ assert(definition != NULL);
+
+ /* Create the dialogue, with modifications. */
+
+ if ((definition->icons[ICON_OPENURL_URL].flags & wimp_ICON_INDIRECTED)
+ == 0) {
+ LOG("open_url URL icon not indirected");
+ xwimp_close_template();
+ die("Template");
+ }
+
+ buffer = malloc(RO_GUI_MAX_URL_SIZE);
+ if (buffer == NULL) {
+ xwimp_close_template();
+ die("NoMemory");
+ }
+
+ definition->icons[ICON_OPENURL_URL].data.indirected_text.text = buffer;
+ definition->icons[ICON_OPENURL_URL].data.indirected_text.size =
+ RO_GUI_MAX_URL_SIZE;
+ definition->sprite_area = gui_sprites;
+
+ error = xwimp_create_window(definition, &dialog_openurl);
+ if (error != NULL) {
+ LOG("xwimp_create_window: 0x%x: %s", error->errnum, error->errmess);
+ xwimp_close_template();
+ die(error->errmess);
+ }
+
+ free(definition);
+
+ ro_gui_wimp_event_register_menu_gright(dialog_openurl, ICON_OPENURL_URL,
+ ICON_OPENURL_MENU, ro_gui_url_suggest_menu);
+ ro_gui_wimp_event_register_cancel(dialog_openurl, ICON_OPENURL_CANCEL);
+ ro_gui_wimp_event_register_ok(dialog_openurl, ICON_OPENURL_OPEN,
+ ro_gui_dialog_openurl_apply);
+ ro_gui_wimp_event_register_menu_prepare(dialog_openurl,
+ ro_gui_dialog_open_url_menu_prepare);
+ ro_gui_wimp_event_set_help_prefix(dialog_openurl, "HelpOpenURL");
+
+ return true;
+}
+
+
+
+bool ro_gui_dialog_openurl_apply(wimp_w w) {
+ const char *urltxt;
+ char *url2;
+ nsurl *url;
+ nserror error;
+
+ urltxt = ro_gui_get_icon_string(w, ICON_OPENURL_URL);
+ url2 = strdup(urltxt); /** @todo why is this copied */
+ if (url2 == NULL) {
+ return false;
+ }
+
+ error = nsurl_create(url2, &url);
+ free(url2);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ ro_warn_user(messages_get_errorcode(error), 0);
+ return false;
+ }
+
+ return true;
+
+}
+
+
+/**
+ * Prepares the Open URL dialog.
+ */
+
+void ro_gui_dialog_prepare_open_url(void)
+{
+ ro_gui_set_icon_string(dialog_openurl, ICON_OPENURL_URL, "", true);
+ ro_gui_set_icon_shaded_state(dialog_openurl,
+ ICON_OPENURL_MENU, !ro_gui_url_suggest_prepare_menu());
+ ro_gui_wimp_event_memorise(dialog_openurl);
+}
+
+
+/**
+ * Callback to prepare menus in the Open URL dialog. At present, this
+ * only has to handle the URL Suggestion pop-up.
+ *
+ * \param w The window handle owning the menu.
+ * \param i The icon handle owning the menu.
+ * \param *menu The menu to be prepared.
+ * \param *pointer The associated mouse click event block, or NULL
+ * on an Adjust-click re-opening.
+ * \return true if the event was handled; false if not.
+ */
+
+bool ro_gui_dialog_open_url_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_pointer *pointer)
+{
+ if (menu != ro_gui_url_suggest_menu || i != ICON_OPENURL_MENU)
+ return false;
+
+ if (pointer != NULL)
+ return ro_gui_url_suggest_prepare_menu();
+
+ return true;
+}
diff --git a/frontends/riscos/dialog.h b/frontends/riscos/dialog.h
new file mode 100644
index 000000000..463048436
--- /dev/null
+++ b/frontends/riscos/dialog.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_RISCOS_DIALOG_H_
+#define _NETSURF_RISCOS_DIALOG_H_
+
+struct toolbar;
+struct gui_window;
+
+void ro_gui_dialog_init(void);
+wimp_w ro_gui_dialog_create(const char *template_name);
+wimp_window * ro_gui_dialog_load_template(const char *template_name);
+
+void ro_gui_dialog_open(wimp_w w);
+void ro_gui_dialog_close(wimp_w close);
+
+bool ro_gui_dialog_open_top(wimp_w w, struct toolbar *toolbar,
+ int width, int height);
+void ro_gui_dialog_open_at_pointer(wimp_w w);
+void ro_gui_dialog_open_xy(wimp_w, int x, int y);
+void ro_gui_dialog_open_centre_parent(wimp_w parent, wimp_w w);
+
+void ro_gui_dialog_open_persistent(wimp_w parent, wimp_w w, bool pointer);
+void ro_gui_dialog_add_persistent(wimp_w parent, wimp_w w);
+void ro_gui_dialog_close_persistent(wimp_w parent);
+
+
+
+
+void ro_gui_dialog_click(wimp_pointer *pointer);
+void ro_gui_dialog_prepare_zoom(struct gui_window *g);
+void ro_gui_dialog_update_zoom(struct gui_window *g);
+void ro_gui_dialog_prepare_open_url(void);
+void ro_gui_save_options(void);
+void ro_gui_dialog_open_config(void);
+void ro_gui_dialog_proxyauth_menu_selection(int item);
+void ro_gui_dialog_image_menu_selection(int item);
+void ro_gui_dialog_languages_menu_selection(const char *lang);
+void ro_gui_dialog_font_menu_selection(int item);
+#endif
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Cache/!Boot,feb b/frontends/riscos/distribution/!Boot/Resources/!Cache/!Boot,feb
new file mode 100644
index 000000000..485bd521d
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Cache/!Boot,feb
@@ -0,0 +1,17 @@
+If (("<Cache$AppDir>" = "") OR ("<Cache$ForceVars>" = "1")) Then Set Cache$AppDir <Obey$Dir>
+
+IconSprites <Cache$AppDir>.!Sprites
+
+| Find and set up resource paths
+WimpSlot -min 64k -max 64k
+Run <Cache$AppDir>.Resources.ResFind CacheApp
+If (("<Cache$Meta>" = "") OR ("<Cache$ForceVars>" = "1")) Then Set Cache$Meta CacheAppRes:!Meta
+
+| Work out where the cache directory should be -- use Choices$User if set or Default, otherwise.
+Set Cache$Suffix "<Choices$User>"
+If "<Cache$Suffix>" = "" Then Set Cache$Suffix "Default"
+If (("<Cache$Dir>" = "") OR ("<Cache$ForceVars>" = "1")) Then Set Cache$Dir "<Cache$AppDir>.Caches.<Cache$Suffix>"
+Unset Cache$Suffix
+
+| Ensure cache directory exists (sadly, unavoidable)
+CDir <Cache$Dir>
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Cache/!Help,feb b/frontends/riscos/distribution/!Boot/Resources/!Cache/!Help,feb
new file mode 100644
index 000000000..35eeeb31b
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Cache/!Help,feb
@@ -0,0 +1,2 @@
+If "<CacheAppRes$Path>" = "" Then Run <Cache$AppDir>.Resources.ResFind CacheApp
+Filer_Run CacheAppRes:Help \ No newline at end of file
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Cache/!Run,feb b/frontends/riscos/distribution/!Boot/Resources/!Cache/!Run,feb
new file mode 100644
index 000000000..8aead9733
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Cache/!Run,feb
@@ -0,0 +1,9 @@
+Set Cache$ForceVars 1
+Run <Obey$Dir>.!Boot
+Unset Cache$ForceVars
+
+RMEnsure SysLog 0.17 IfThere <SysLog$Dir>.!Run Then Run <SysLog$Dir>.!Run
+RMEnsure SysLog 0.17 Set Cache$SysLogMissing "True"
+
+Wimpslot -min 128k -max 128k
+Run <Cache$AppDir>.!RunImage
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Cache/!RunImage,ffb b/frontends/riscos/distribution/!Boot/Resources/!Cache/!RunImage,ffb
new file mode 100644
index 000000000..61752af99
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Cache/!RunImage,ffb
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Cache/!Sprites,ff9 b/frontends/riscos/distribution/!Boot/Resources/!Cache/!Sprites,ff9
new file mode 100644
index 000000000..b71a51cf3
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Cache/!Sprites,ff9
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Cache/!Sprites22,ff9 b/frontends/riscos/distribution/!Boot/Resources/!Cache/!Sprites22,ff9
new file mode 100644
index 000000000..e43f88c89
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Cache/!Sprites22,ff9
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Cache/Caches/Blank b/frontends/riscos/distribution/!Boot/Resources/!Cache/Caches/Blank
new file mode 100644
index 000000000..898dc5872
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Cache/Caches/Blank
@@ -0,0 +1 @@
+This is here just to stop the directory structure getting lost when unzipping. \ No newline at end of file
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/MultiError,ffb b/frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/MultiError,ffb
new file mode 100644
index 000000000..ec348b0e9
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/MultiError,ffb
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/ResFind,ffb b/frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/ResFind,ffb
new file mode 100644
index 000000000..7766cc928
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/ResFind,ffb
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/UK/!Meta b/frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/UK/!Meta
new file mode 100644
index 000000000..2de40bd7c
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/UK/!Meta
@@ -0,0 +1,9 @@
+# Meta file for Cache
+Help:<CacheAppRes$Dir>.Help
+Version:1.13
+Web:http://www.snowstone.org.uk/riscos/
+Title:Cache
+Publisher:Adam Richardson
+Description:Cache provides a central location for semi-permanent data on your system.
+Email:riscos@snowstone.org.uk
+
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/UK/Help b/frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/UK/Help
new file mode 100644
index 000000000..aad9bf0b1
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/UK/Help
@@ -0,0 +1,60 @@
+Cache
+-----
+
+Cache provides a shared location for cached data. This location can be
+used by application authors to store semi-permanent data. Cache can be
+placed anywhere on your computer where it will be "seen" by the Filer
+during start up. (For instance, the "Resources" directory inside !Boot.)
+
+Once "seen" it will set up a cache location, which can be shown by
+double-clicking on !Cache.
+
+
+Application Authors
+----------- -------
+
+Use Cache in a similar way to using Scrap. You *must not* assume that
+Cache is present on the user's system however, as Cache is not an
+official part of the system (like Scrap).
+
+To use Cache you should:
+ * Check for the presence of "<Cache$Dir>" before proceeding
+ * Read from and write data to "<Cache$Dir>.APPNAME" where APPNAME has
+ been allocated to you by the allocations service. See:
+ http://www.riscosopen.com/content/allocate
+ * If the APPNAME directory does not exist, you should create it.
+
+This version of Cache is published by Adam Richardson who can be
+contacted at riscos@snowstone.org.uk.
+
+The website for Cache is: http://www.snowstone.org.uk/riscos/
+
+
+Credits
+-------
+
+Cache is (c) Adam Richardson, 2007.
+Thanks to Rob Kendrick for the initial idea and input.
+
+
+License
+-------
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/UK/Messages b/frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/UK/Messages
new file mode 100644
index 000000000..366122292
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/UK/Messages
@@ -0,0 +1,8 @@
+# Messages for Cache
+
+multiuser:Multi-user system present.
+singleuser:No multi-user system present.
+location:Cache directory set to:
+opendir:Opening cache location...
+fatalerror:Cache has suffered a fatal error and has quit.
+
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/UK/Templates,fec b/frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/UK/Templates,fec
new file mode 100644
index 000000000..22f910ad9
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Cache/Resources/UK/Templates,fec
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/!Boot,feb b/frontends/riscos/distribution/!Boot/Resources/!Unicode/!Boot,feb
new file mode 100644
index 000000000..7c0c46241
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/!Boot,feb
@@ -0,0 +1,5 @@
+| Unicode Boot file
+|
+Set Unicode$Dir <Obey$Dir>
+SetMacro Unicode$Path <Unicode$Dir>.,Resources:$.Resources.Unicode.
+IconSprites Unicode:!Sprites
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/!Help b/frontends/riscos/distribution/!Boot/Resources/!Unicode/!Help
new file mode 100644
index 000000000..8c0488185
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/!Help
@@ -0,0 +1 @@
+This application contains resources for Unicode support in applications.
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/!Run,feb b/frontends/riscos/distribution/!Boot/Resources/!Unicode/!Run,feb
new file mode 100644
index 000000000..bd70e96ac
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/!Run,feb
@@ -0,0 +1,5 @@
+| Unicode Run file
+|
+Set Unicode$Dir <Obey$Dir>
+SetMacro Unicode$Path <Unicode$Dir>.,Resources:$.Resources.Unicode.
+IconSprites Unicode:!Sprites
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/!Sprites,ff9 b/frontends/riscos/distribution/!Boot/Resources/!Unicode/!Sprites,ff9
new file mode 100644
index 000000000..3eb5b44b7
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/!Sprites,ff9
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/!Sprites11,ff9 b/frontends/riscos/distribution/!Boot/Resources/!Unicode/!Sprites11,ff9
new file mode 100644
index 000000000..48986b41e
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/!Sprites11,ff9
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/!Sprites22,ff9 b/frontends/riscos/distribution/!Boot/Resources/!Unicode/!Sprites22,ff9
new file mode 100644
index 000000000..63a6e6122
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/!Sprites22,ff9
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Acorn/Latin1 b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Acorn/Latin1
new file mode 100644
index 000000000..bdf5d3b67
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Acorn/Latin1
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Apple/CentEuro b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Apple/CentEuro
new file mode 100644
index 000000000..5ab69ff2e
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Apple/CentEuro
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Apple/Cyrillic b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Apple/Cyrillic
new file mode 100644
index 000000000..670fd6cdc
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Apple/Cyrillic
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Apple/Roman b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Apple/Roman
new file mode 100644
index 000000000..254579e2c
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Apple/Roman
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Apple/Ukrainian b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Apple/Ukrainian
new file mode 100644
index 000000000..a220587ba
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Apple/Ukrainian
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/BigFive b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/BigFive
new file mode 100644
index 000000000..c659cef19
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/BigFive
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/C0/40[ISO646] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/C0/40[ISO646]
new file mode 100644
index 000000000..cd92b5486
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/C0/40[ISO646]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/C1/43[IS6429] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/C1/43[IS6429]
new file mode 100644
index 000000000..74002a168
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/C1/43[IS6429]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/40[646old] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/40[646old]
new file mode 100644
index 000000000..00e2d1096
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/40[646old]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/41[646-GB] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/41[646-GB]
new file mode 100644
index 000000000..c293f93d6
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/41[646-GB]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/42[646IRV] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/42[646IRV]
new file mode 100644
index 000000000..e0b4bcadb
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/42[646IRV]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/43[FinSwe] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/43[FinSwe]
new file mode 100644
index 000000000..7d4646905
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/43[FinSwe]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/47[646-SE] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/47[646-SE]
new file mode 100644
index 000000000..a6b091a22
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/47[646-SE]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/48[646-SE] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/48[646-SE]
new file mode 100644
index 000000000..9bd24ab29
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/48[646-SE]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/49[JS201K] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/49[JS201K]
new file mode 100644
index 000000000..20ce8d498
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/49[JS201K]
@@ -0,0 +1 @@
+aÿbÿcÿdÿeÿfÿgÿhÿiÿjÿkÿlÿmÿnÿoÿpÿqÿrÿsÿtÿuÿvÿwÿxÿyÿzÿ{ÿ|ÿ}ÿ~ÿÿ€ÿÿ‚ÿƒÿ„ÿ…ÿ†ÿ‡ÿˆÿ‰ÿŠÿ‹ÿŒÿÿŽÿÿÿ‘ÿ’ÿ“ÿ”ÿ•ÿ–ÿ—ÿ˜ÿ™ÿšÿ›ÿœÿÿžÿŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ \ No newline at end of file
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/4A[JS201R] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/4A[JS201R]
new file mode 100644
index 000000000..21d2a479b
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/4A[JS201R]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/4B[646-DE] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/4B[646-DE]
new file mode 100644
index 000000000..a2e284e1b
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/4B[646-DE]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/4C[646-PT] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/4C[646-PT]
new file mode 100644
index 000000000..e076e2517
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/4C[646-PT]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/54[GB1988] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/54[GB1988]
new file mode 100644
index 000000000..3b43719ce
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/54[GB1988]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/56[Teltxt] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/56[Teltxt]
new file mode 100644
index 000000000..73ce49e17
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/56[Teltxt]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/59[646-IT] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/59[646-IT]
new file mode 100644
index 000000000..f1ae81962
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/59[646-IT]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/5A[646-ES] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/5A[646-ES]
new file mode 100644
index 000000000..674fc2d70
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/5A[646-ES]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/60[646-NO] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/60[646-NO]
new file mode 100644
index 000000000..fc92892ee
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/60[646-NO]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/66[646-FR] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/66[646-FR]
new file mode 100644
index 000000000..8dd604679
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/66[646-FR]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/69[646-HU] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/69[646-HU]
new file mode 100644
index 000000000..65300b2c5
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/69[646-HU]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/6B[Arabic] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/6B[Arabic]
new file mode 100644
index 000000000..c47689914
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/6B[Arabic]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/6C[IS6937] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/6C[IS6937]
new file mode 100644
index 000000000..93453f5de
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/6C[IS6937]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/7A[SerbCr] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/7A[SerbCr]
new file mode 100644
index 000000000..9740e784e
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94/7A[SerbCr]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/40[JS6226] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/40[JS6226]
new file mode 100644
index 000000000..a677dfc3d
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/40[JS6226]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/41[GB2312] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/41[GB2312]
new file mode 100644
index 000000000..679608ad2
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/41[GB2312]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/42[JIS208] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/42[JIS208]
new file mode 100644
index 000000000..532b1f4f3
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/42[JIS208]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/43[KS1001] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/43[KS1001]
new file mode 100644
index 000000000..36186c864
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/43[KS1001]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/44[JIS212] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/44[JIS212]
new file mode 100644
index 000000000..f5343a30e
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/44[JIS212]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/47[CNS1] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/47[CNS1]
new file mode 100644
index 000000000..da07f45d3
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/47[CNS1]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/48[CNS2] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/48[CNS2]
new file mode 100644
index 000000000..44ee24c91
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/48[CNS2]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/49[CNS3] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/49[CNS3]
new file mode 100644
index 000000000..a8464e5aa
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/49[CNS3]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/4A[CNS4] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/4A[CNS4]
new file mode 100644
index 000000000..a8f3e3270
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/4A[CNS4]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/4B[CNS5] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/4B[CNS5]
new file mode 100644
index 000000000..535b0f4b5
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/4B[CNS5]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/4C[CNS6] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/4C[CNS6]
new file mode 100644
index 000000000..7bfb2b1d4
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/4C[CNS6]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/4D[CNS7] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/4D[CNS7]
new file mode 100644
index 000000000..be14c7279
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G94x94/4D[CNS7]
@@ -0,0 +1,2 @@
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö5ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿUVÿÿÿÿÿÿÿÿÿÿÿÿg6ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿaÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ’9ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿE:ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉ;ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿU=ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿè>ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶?ÿÿÿÿÿÿ½?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖ@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿqAÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜BÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöFÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿzGÿÿÿÿÿÿÿÿÿÿÿÿ¸GÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿHÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ/Iÿÿÿÿÿÿÿÿÿÿ1IÿÿÿÿÿÿÿÿÿÿÿÿÿÿœIÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæIÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ8Kÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ:Lÿÿÿÿ±LÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGMÿÿQMÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿG7ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ8ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£8ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ;ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×qÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿš>ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÂ?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜ@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³Aÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿf†ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙEÿÿÝEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüFÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ:ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿIÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ=“ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¨IÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊJÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿKÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀLÿÿÿÿÿÿÿÿÿÿÿÿÊLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ%MÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿJMÿÿSMÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ6ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð;ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿÿÿÿÿÿÿÿÿÿÿÿ#>ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿCÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ]EÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿZFÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ…Gÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ Iÿÿÿÿÿÿÿÿÿÿÿÿ9Iÿÿÿÿÿÿ7IÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿkJÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍJÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿMLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ5ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ{6ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ8ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõdÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ';ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏ?ÿÿÿÿÿÿÍ?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‚AÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿRBÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQDÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿZEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿeFÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯HÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿAIÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ)Jÿÿÿÿÿÿÿÿÿÿÿÿÿÿ*Jÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ–JÿÿÿÿÿÿÿÿÿÿÿÿKÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ~6áXÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§9ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ Cÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿé|ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿeEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿdGÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#HÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙ•ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹4ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿK<ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñ@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿgFÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‰Hÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ LÿÿÿÿÿÿLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿmLÿÿpLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿMÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§4ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿgÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿZ>ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯BÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿkEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿcIÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€J„JJÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñJÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGšÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿMÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¨4ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‹Aÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ+CÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿzEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ FÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿmFÿÿÿÿÿÿÿÿÿÿGÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿjIlIÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²KÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿLÿÿÿÿÿÿ-LÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿEMÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
+Fÿÿÿÿÿÿÿÿ Fÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ.HÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ1Cÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼K»KÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ6ÿÿÿÿÿÿÿÿÿÿr7ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿMÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿtvÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìKÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿyBÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ \ No newline at end of file
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/41[Lat1] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/41[Lat1]
new file mode 100644
index 000000000..97e6b1106
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/41[Lat1]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/42[Lat2] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/42[Lat2]
new file mode 100644
index 000000000..b753c40fe
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/42[Lat2]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/43[Lat3] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/43[Lat3]
new file mode 100644
index 000000000..88d477886
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/43[Lat3]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/44[Lat4] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/44[Lat4]
new file mode 100644
index 000000000..a40662d45
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/44[Lat4]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/46[Greek] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/46[Greek]
new file mode 100644
index 000000000..c42397388
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/46[Greek]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/47[Arabic] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/47[Arabic]
new file mode 100644
index 000000000..4507f467a
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/47[Arabic]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/48[Hebrew] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/48[Hebrew]
new file mode 100644
index 000000000..70f39cca6
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/48[Hebrew]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/4C[Cyrill] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/4C[Cyrill]
new file mode 100644
index 000000000..8ff0115e4
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/4C[Cyrill]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/4D[Lat5] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/4D[Lat5]
new file mode 100644
index 000000000..6381e607e
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/4D[Lat5]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/50[LatSup] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/50[LatSup]
new file mode 100644
index 000000000..a320c7fe8
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/50[LatSup]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/52[IS6937] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/52[IS6937]
new file mode 100644
index 000000000..dff6ccba4
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/52[IS6937]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/54[Thai] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/54[Thai]
new file mode 100644
index 000000000..d74377759
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/54[Thai]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/56[Lat6] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/56[Lat6]
new file mode 100644
index 000000000..4e3e4f313
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/56[Lat6]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/58[L6Sami] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/58[L6Sami]
new file mode 100644
index 000000000..4dfd9188c
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/58[L6Sami]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/59[Lat7] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/59[Lat7]
new file mode 100644
index 000000000..256a88e76
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/59[Lat7]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/5C[Welsh] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/5C[Welsh]
new file mode 100644
index 000000000..b5e00509f
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/5C[Welsh]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/5D[Sami] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/5D[Sami]
new file mode 100644
index 000000000..15734c036
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/5D[Sami]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/5E[Hebrew] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/5E[Hebrew]
new file mode 100644
index 000000000..a6593b071
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/5E[Hebrew]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/5F[Lat8] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/5F[Lat8]
new file mode 100644
index 000000000..c15713e82
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/5F[Lat8]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/62[Lat9] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/62[Lat9]
new file mode 100644
index 000000000..5bf449d58
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/62[Lat9]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/66[Lat10] b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/66[Lat10]
new file mode 100644
index 000000000..e8ba925d3
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/ISO2022/G96/66[Lat10]
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/KOI8-R b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/KOI8-R
new file mode 100644
index 000000000..8063cd4bc
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/KOI8-R
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1250 b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1250
new file mode 100644
index 000000000..7a0d35ceb
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1250
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1251 b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1251
new file mode 100644
index 000000000..3d6009cab
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1251
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1252 b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1252
new file mode 100644
index 000000000..6d3bf293d
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1252
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1253 b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1253
new file mode 100644
index 000000000..50a48be13
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1253
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1254 b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1254
new file mode 100644
index 000000000..45ecfe907
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1254
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1256 b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1256
new file mode 100644
index 000000000..7fc95a92f
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP1256
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP866 b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP866
new file mode 100644
index 000000000..cd214d24b
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP866
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP874 b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP874
new file mode 100644
index 000000000..26a6fc8c3
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP874
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP932 b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP932
new file mode 100644
index 000000000..2c0c111f9
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Encodings/Microsoft/CP932
Binary files differ
diff --git a/frontends/riscos/distribution/!Boot/Resources/!Unicode/Files/Aliases b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Files/Aliases
new file mode 100644
index 000000000..8978ede4c
--- /dev/null
+++ b/frontends/riscos/distribution/!Boot/Resources/!Unicode/Files/Aliases
@@ -0,0 +1,303 @@
+# > Unicode:Files.Aliases
+# Mapping of character set encoding names to their canonical form
+#
+# Lines starting with a '#' are comments, blank lines are ignored.
+#
+# Based on http://www.iana.org/assignments/character-sets and
+# http://www.iana.org/assignments/ianacharset-mib
+#
+# Canonical Form MIBenum Aliases...
+#
+US-ASCII 3 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII ISO646-US ANSI_X3.4-1968 us IBM367 cp367 csASCII
+ISO-10646-UTF-1 27 csISO10646UTF1
+ISO_646.basic:1983 28 ref csISO646basic1983
+INVARIANT 29 csINVARIANT
+ISO_646.irv:1983 30 iso-ir-2 irv csISO2IntlRefVersion
+BS_4730 20 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom
+NATS-SEFI 31 iso-ir-8-1 csNATSSEFI
+NATS-SEFI-ADD 32 iso-ir-8-2 csNATSSEFIADD
+NATS-DANO 33 iso-ir-9-1 csNATSDANO
+NATS-DANO-ADD 34 iso-ir-9-2 csNATSDANOADD
+SEN_850200_B 35 iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish
+SEN_850200_C 21 iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames
+KS_C_5601-1987 36 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987
+ISO-2022-KR 37 csISO2022KR
+EUC-KR 38 csEUCKR EUCKR
+ISO-2022-JP 39 csISO2022JP
+ISO-2022-JP-2 40 csISO2022JP2
+ISO-2022-CN 104
+ISO-2022-CN-EXT 105
+JIS_C6220-1969-jp 41 JIS_C6220-1969 iso-ir-13 katakana x0201-7 csISO13JISC6220jp
+JIS_C6220-1969-ro 42 iso-ir-14 jp ISO646-JP csISO14JISC6220ro
+IT 22 iso-ir-15 ISO646-IT csISO15Italian
+PT 43 iso-ir-16 ISO646-PT csISO16Portuguese
+ES 23 iso-ir-17 ISO646-ES csISO17Spanish
+greek7-old 44 iso-ir-18 csISO18Greek7Old
+latin-greek 45 iso-ir-19 csISO19LatinGreek
+DIN_66003 24 iso-ir-21 de ISO646-DE csISO21German
+NF_Z_62-010_(1973) 46 iso-ir-25 ISO646-FR1 csISO25French
+Latin-greek-1 47 iso-ir-27 csISO27LatinGreek1
+ISO_5427 48 iso-ir-37 csISO5427Cyrillic
+JIS_C6226-1978 49 iso-ir-42 csISO42JISC62261978
+BS_viewdata 50 iso-ir-47 csISO47BSViewdata
+INIS 51 iso-ir-49 csISO49INIS
+INIS-8 52 iso-ir-50 csISO50INIS8
+INIS-cyrillic 53 iso-ir-51 csISO51INISCyrillic
+ISO_5427:1981 54 iso-ir-54 ISO5427Cyrillic1981
+ISO_5428:1980 55 iso-ir-55 csISO5428Greek
+GB_1988-80 56 iso-ir-57 cn ISO646-CN csISO57GB1988
+GB_2312-80 57 iso-ir-58 chinese csISO58GB231280
+NS_4551-1 25 iso-ir-60 ISO646-NO no csISO60DanishNorwegian csISO60Norwegian1
+NS_4551-2 58 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2
+NF_Z_62-010 26 iso-ir-69 ISO646-FR fr csISO69French
+videotex-suppl 59 iso-ir-70 csISO70VideotexSupp1
+PT2 60 iso-ir-84 ISO646-PT2 csISO84Portuguese2
+ES2 61 iso-ir-85 ISO646-ES2 csISO85Spanish2
+MSZ_7795.3 62 iso-ir-86 ISO646-HU hu csISO86Hungarian
+JIS_C6226-1983 63 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208
+greek7 64 iso-ir-88 csISO88Greek7
+ASMO_449 65 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449
+iso-ir-90 66 csISO90
+JIS_C6229-1984-a 67 iso-ir-91 jp-ocr-a csISO91JISC62291984a
+JIS_C6229-1984-b 68 iso-ir-92 ISO646-JP-OCR-B jp-ocr-b csISO92JISC62991984b
+JIS_C6229-1984-b-add 69 iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd
+JIS_C6229-1984-hand 70 iso-ir-94 jp-ocr-hand csISO94JIS62291984hand
+JIS_C6229-1984-hand-add 71 iso-ir-95 jp-ocr-hand-add csISO95JIS62291984handadd
+JIS_C6229-1984-kana 72 iso-ir-96 csISO96JISC62291984kana
+ISO_2033-1983 73 iso-ir-98 e13b csISO2033
+ANSI_X3.110-1983 74 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS
+ISO-8859-1 4 iso-ir-100 ISO_8859-1 ISO_8859-1:1987 latin1 l1 IBM819 CP819 csISOLatin1 8859_1 ISO8859-1
+ISO-8859-2 5 iso-ir-101 ISO_8859-2 ISO_8859-2:1987 latin2 l2 csISOLatin2 8859_2 ISO8859-2
+T.61-7bit 75 iso-ir-102 csISO102T617bit
+T.61-8bit 76 T.61 iso-ir-103 csISO103T618bit
+ISO-8859-3 6 iso-ir-109 ISO_8859-3 ISO_8859-3:1988 latin3 l3 csISOLatin3 8859_3 ISO8859-3
+ISO-8859-4 7 iso-ir-110 ISO_8859-4 ISO_8859-4:1988 latin4 l4 csISOLatin4 8859_4 ISO8859-4
+ECMA-cyrillic 77 iso-ir-111 KOI8-E csISO111ECMACyrillic
+CSA_Z243.4-1985-1 78 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1
+CSA_Z243.4-1985-2 79 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2
+CSA_Z243.4-1985-gr 80 iso-ir-123 csISO123CSAZ24341985gr
+ISO-8859-6 9 iso-ir-127 ISO_8859-6 ISO_8859-6:1987 ECMA-114 ASMO-708 arabic csISOLatinArabic
+ISO-8859-6-E 81 csISO88596E ISO_8859-6-E
+ISO-8859-6-I 82 csISO88596I ISO_8859-6-I
+ISO-8859-7 10 iso-ir-126 ISO_8859-7 ISO_8859-7:1987 ELOT_928 ECMA-118 greek greek8 csISOLatinGreek 8859_7 ISO8859-7
+T.101-G2 83 iso-ir-128 csISO128T101G2
+ISO-8859-8 11 iso-ir-138 ISO_8859-8 ISO_8859-8:1988 hebrew csISOLatinHebrew 8859_8 ISO8859-8
+ISO-8859-8-E 84 csISO88598E ISO_8859-8-E
+ISO-8859-8-I 85 csISO88598I ISO_8859-8-I
+CSN_369103 86 iso-ir-139 csISO139CSN369103
+JUS_I.B1.002 87 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002
+ISO_6937-2-add 14 iso-ir-142 csISOTextComm
+IEC_P27-1 88 iso-ir-143 csISO143IECP271
+ISO-8859-5 8 iso-ir-144 ISO_8859-5 ISO_8859-5:1988 cyrillic csISOLatinCyrillic 8859_5 ISO8859-5
+JUS_I.B1.003-serb 89 iso-ir-146 serbian csISO146Serbian
+JUS_I.B1.003-mac 90 macedonian iso-ir-147 csISO147Macedonian
+ISO-8859-9 12 iso-ir-148 ISO_8859-9 ISO_8859-9:1989 latin5 l5 csISOLatin5 8859_9 ISO8859-9
+greek-ccitt 91 iso-ir-150 csISO150 csISO150GreekCCITT
+NC_NC00-10:81 92 cuba iso-ir-151 ISO646-CU csISO151Cuba
+ISO_6937-2-25 93 iso-ir-152 csISO6937Add
+GOST_19768-74 94 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874
+ISO_8859-supp 95 iso-ir-154 latin1-2-5 csISO8859Supp
+ISO_10367-box 96 iso-ir-155 csISO10367Box
+ISO-8859-10 13 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 8859_10 ISO8859-10
+latin-lap 97 lap iso-ir-158 csISO158Lap
+JIS_X0212-1990 98 x0212 iso-ir-159 csISO159JISX02121990
+DS_2089 99 DS2089 ISO646-DK dk csISO646Danish
+us-dk 100 csUSDK
+dk-us 101 csDKUS
+JIS_X0201 15 X0201 csHalfWidthKatakana
+KSC5636 102 ISO646-KR csKSC5636
+ISO-10646-UCS-2 1000 csUnicode UCS-2 UCS2
+ISO-10646-UCS-4 1001 csUCS4 UCS-4 UCS4
+DEC-MCS 2008 dec csDECMCS
+hp-roman8 2004 roman8 r8 csHPRoman8
+macintosh 2027 mac csMacintosh MACROMAN MAC-ROMAN X-MAC-ROMAN
+IBM037 2028 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl csIBM037
+IBM038 2029 EBCDIC-INT cp038 csIBM038
+IBM273 2030 CP273 csIBM273
+IBM274 2031 EBCDIC-BE CP274 csIBM274
+IBM275 2032 EBCDIC-BR cp275 csIBM275
+IBM277 2033 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277
+IBM278 2034 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278
+IBM280 2035 CP280 ebcdic-cp-it csIBM280
+IBM281 2036 EBCDIC-JP-E cp281 csIBM281
+IBM284 2037 CP284 ebcdic-cp-es csIBM284
+IBM285 2038 CP285 ebcdic-cp-gb csIBM285
+IBM290 2039 cp290 EBCDIC-JP-kana csIBM290
+IBM297 2040 cp297 ebcdic-cp-fr csIBM297
+IBM420 2041 cp420 ebcdic-cp-ar1 csIBM420
+IBM423 2042 cp423 ebcdic-cp-gr csIBM423
+IBM424 2043 cp424 ebcdic-cp-he csIBM424
+IBM437 2011 cp437 437 csPC8CodePage437
+IBM500 2044 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500
+IBM775 2087 cp775 csPC775Baltic
+IBM850 2009 cp850 850 csPC850Multilingual
+IBM851 2045 cp851 851 csIBM851
+IBM852 2010 cp852 852 csPCp852
+IBM855 2046 cp855 855 csIBM855
+IBM857 2047 cp857 857 csIBM857
+IBM860 2048 cp860 860 csIBM860
+IBM861 2049 cp861 861 cp-is csIBM861
+IBM862 2013 cp862 862 csPC862LatinHebrew
+IBM863 2050 cp863 863 csIBM863
+IBM864 2051 cp864 csIBM864
+IBM865 2052 cp865 865 csIBM865
+IBM866 2086 cp866 866 csIBM866
+IBM868 2053 CP868 cp-ar csIBM868
+IBM869 2054 cp869 869 cp-gr csIBM869
+IBM870 2055 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870
+IBM871 2056 CP871 ebcdic-cp-is csIBM871
+IBM880 2057 cp880 EBCDIC-Cyrillic csIBM880
+IBM891 2058 cp891 csIBM891
+IBM903 2059 cp903 csIBM903
+IBM904 2060 cp904 904 csIBBM904
+IBM905 2061 CP905 ebcdic-cp-tr csIBM905
+IBM918 2062 CP918 ebcdic-cp-ar2 csIBM918
+IBM1026 2063 CP1026 csIBM1026
+EBCDIC-AT-DE 2064 csIBMEBCDICATDE
+EBCDIC-AT-DE-A 2065 csEBCDICATDEA
+EBCDIC-CA-FR 2066 csEBCDICCAFR
+EBCDIC-DK-NO 2067 csEBCDICDKNO
+EBCDIC-DK-NO-A 2068 csEBCDICDKNOA
+EBCDIC-FI-SE 2069 csEBCDICFISE
+EBCDIC-FI-SE-A 2070 csEBCDICFISEA
+EBCDIC-FR 2071 csEBCDICFR
+EBCDIC-IT 2072 csEBCDICIT
+EBCDIC-PT 2073 csEBCDICPT
+EBCDIC-ES 2074 csEBCDICES
+EBCDIC-ES-A 2075 csEBCDICESA
+EBCDIC-ES-S 2076 csEBCDICESS
+EBCDIC-UK 2077 csEBCDICUK
+EBCDIC-US 2078 csEBCDICUS
+UNKNOWN-8BIT 2079 csUnknown8BiT
+MNEMONIC 2080 csMnemonic
+MNEM 2081 csMnem
+VISCII 2082 csVISCII
+VIQR 2083 csVIQR
+KOI8-R 2084 csKOI8R
+KOI8-U 2088
+IBM00858 2089 CCSID00858 CP00858 PC-Multilingual-850+euro
+IBM00924 2090 CCSID00924 CP00924 ebcdic-Latin9--euro
+IBM01140 2091 CCSID01140 CP01140 ebcdic-us-37+euro
+IBM01141 2092 CCSID01141 CP01141 ebcdic-de-273+euro
+IBM01142 2093 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro
+IBM01143 2094 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro
+IBM01144 2095 CCSID01144 CP01144 ebcdic-it-280+euro
+IBM01145 2096 CCSID01145 CP01145 ebcdic-es-284+euro
+IBM01146 2097 CCSID01146 CP01146 ebcdic-gb-285+euro
+IBM01147 2098 CCSID01147 CP01147 ebcdic-fr-297+euro
+IBM01148 2099 CCSID01148 CP01148 ebcdic-international-500+euro
+IBM01149 2100 CCSID01149 CP01149 ebcdic-is-871+euro
+Big5-HKSCS 2101
+IBM1047 2102 IBM-1047
+PTCP154 2103 csPTCP154 PT154 CP154 Cyrillic-Asian
+Amiga-1251 2104 Ami1251 Amiga1251 Ami-1251
+KOI7-switched 2105
+UNICODE-1-1 1010 csUnicode11
+SCSU 1011
+UTF-7 1012
+UTF-16BE 1013
+UTF-16LE 1014
+UTF-16 1015
+CESU-8 1016 csCESU-8
+UTF-32 1017
+UTF-32BE 1018
+UTF-32LE 1019
+BOCU-1 1020 csBOCU-1
+UNICODE-1-1-UTF-7 103 csUnicode11UTF7
+UTF-8 106 UNICODE-1-1-UTF-8 UNICODE-2-0-UTF-8 utf8
+ISO-8859-13 109 8859_13 ISO8859-13
+ISO-8859-14 110 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic l8 8859_14 ISO8859-14
+ISO-8859-15 111 ISO_8859-15 Latin-9 8859_15 ISO8859-15
+ISO-8859-16 112 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10
+GBK 113 CP936 MS936 windows-936
+GB18030 114
+OSD_EBCDIC_DF04_15 115
+OSD_EBCDIC_DF03_IRV 116
+OSD_EBCDIC_DF04_1 117
+JIS_Encoding 16 csJISEncoding
+Shift_JIS 17 MS_Kanji csShiftJIS X-SJIS Shift-JIS
+EUC-JP 18 csEUCPkdFmtJapanese Extended_UNIX_Code_Packed_Format_for_Japanese EUCJP
+Extended_UNIX_Code_Fixed_Width_for_Japanese 19 csEUCFixWidJapanese
+ISO-10646-UCS-Basic 1002 csUnicodeASCII
+ISO-10646-Unicode-Latin1 1003 csUnicodeLatin1 ISO-10646
+ISO-Unicode-IBM-1261 1005 csUnicodeIBM1261
+ISO-Unicode-IBM-1268 1006 csUnicodeIBM1268
+ISO-Unicode-IBM-1276 1007 csUnicodeIBM1276
+ISO-Unicode-IBM-1264 1008 csUnicodeIBM1264
+ISO-Unicode-IBM-1265 1009 csUnicodeIBM1265
+ISO-8859-1-Windows-3.0-Latin-1 2000 csWindows30Latin1
+ISO-8859-1-Windows-3.1-Latin-1 2001 csWindows31Latin1
+ISO-8859-2-Windows-Latin-2 2002 csWindows31Latin2
+ISO-8859-9-Windows-Latin-5 2003 csWindows31Latin5
+Adobe-Standard-Encoding 2005 csAdobeStandardEncoding
+Ventura-US 2006 csVenturaUS
+Ventura-International 2007 csVenturaInternational
+PC8-Danish-Norwegian 2012 csPC8DanishNorwegian
+PC8-Turkish 2014 csPC8Turkish
+IBM-Symbols 2015 csIBMSymbols
+IBM-Thai 2016 csIBMThai
+HP-Legal 2017 csHPLegal
+HP-Pi-font 2018 csHPPiFont
+HP-Math8 2019 csHPMath8
+Adobe-Symbol-Encoding 2020 csHPPSMath
+HP-DeskTop 2021 csHPDesktop
+Ventura-Math 2022 csVenturaMath
+Microsoft-Publishing 2023 csMicrosoftPublishing
+Windows-31J 2024 csWindows31J
+GB2312 2025 csGB2312 EUC-CN EUCCN CN-GB
+Big5 2026 csBig5 BIG-FIVE BIG-5 CN-BIG5 BIG_FIVE x-x-big5
+windows-1250 2250 CP1250 MS-EE
+windows-1251 2251 CP1251 MS-CYRL
+windows-1252 2252 CP1252 MS-ANSI
+windows-1253 2253 CP1253 MS-GREEK
+windows-1254 2254 CP1254 MS-TURK
+windows-1255 2255
+windows-1256 2256 CP1256 MS-ARAB
+windows-1257 2257 CP1257 WINBALTRIM
+windows-1258 2258
+TIS-620 2259
+HZ-GB-2312 2085
+
+# Additional encodings not defined by IANA
+
+# Arbitrary allocations
+#CP737 3001
+#CP853 3002
+#CP856 3003
+CP874 3004 WINDOWS-874
+#CP922 3005
+#CP1046 3006
+#CP1124 3007
+#CP1125 3008 WINDOWS-1125
+#CP1129 3009
+#CP1133 3010 IBM-CP1133
+#CP1161 3011 IBM-1161 IBM1161 CSIBM1161
+#CP1162 3012 IBM-1162 IBM1162 CSIBM1162
+#CP1163 3013 IBM-1163 IBM1163 CSIBM1163
+#GEORGIAN-ACADEMY 3014
+#GEORGIAN-PS 3015
+#KOI8-RU 3016
+#KOI8-T 3017
+#MACARABIC 3018 X-MAC-ARABIC MAC-ARABIC
+#MACCROATIAN 3019 X-MAC-CROATIAN MAC-CROATIAN
+#MACGREEK 3020 X-MAC-GREEK MAC-GREEK
+#MACHEBREW 3021 X-MAC-HEBREW MAC-HEBREW
+#MACICELAND 3022 X-MAC-ICELAND MAC-ICELAND
+#MACROMANIA 3023 X-MAC-ROMANIA MAC-ROMANIA
+#MACTHAI 3024 X-MAC-THAI MAC-THAI
+#MACTURKISH 3025 X-MAC-TURKISH MAC-TURKISH
+#MULELAO-1 3026
+CP949 3027 WINDOWS-949
+
+# From Unicode Lib
+ISO-IR-182 4000
+ISO-IR-197 4002
+ISO-2022-JP-1 4008
+MACCYRILLIC 4009 X-MAC-CYRILLIC MAC-CYRILLIC
+MACUKRAINE 4010 X-MAC-UKRAINIAN MAC-UKRAINIAN
+MACCENTRALEUROPE 4011 X-MAC-CENTRALEURROMAN MAC-CENTRALEURROMAN
+JOHAB 4012
+ISO-8859-11 4014 iso-ir-166 ISO_8859-11 ISO8859-11 8859_11
+X-CURRENT 4999 X-SYSTEM
+X-ACORN-LATIN1 5001
+X-ACORN-FUZZY 5002
diff --git a/frontends/riscos/distribution/!System/310/Modules/CryptRand,ffa b/frontends/riscos/distribution/!System/310/Modules/CryptRand,ffa
new file mode 100644
index 000000000..f403843b3
--- /dev/null
+++ b/frontends/riscos/distribution/!System/310/Modules/CryptRand,ffa
Binary files differ
diff --git a/frontends/riscos/distribution/!System/310/Modules/Iconv,ffa b/frontends/riscos/distribution/!System/310/Modules/Iconv,ffa
new file mode 100644
index 000000000..8a9cc3adb
--- /dev/null
+++ b/frontends/riscos/distribution/!System/310/Modules/Iconv,ffa
Binary files differ
diff --git a/frontends/riscos/distribution/!System/310/Modules/Network/URI,ffa b/frontends/riscos/distribution/!System/310/Modules/Network/URI,ffa
new file mode 100644
index 000000000..431535e21
--- /dev/null
+++ b/frontends/riscos/distribution/!System/310/Modules/Network/URI,ffa
Binary files differ
diff --git a/frontends/riscos/distribution/!System/310/Modules/SharedULib,ffa b/frontends/riscos/distribution/!System/310/Modules/SharedULib,ffa
new file mode 100755
index 000000000..8dd0dd2c9
--- /dev/null
+++ b/frontends/riscos/distribution/!System/310/Modules/SharedULib,ffa
Binary files differ
diff --git a/frontends/riscos/distribution/!System/310/Modules/Tinct,ffa b/frontends/riscos/distribution/!System/310/Modules/Tinct,ffa
new file mode 100644
index 000000000..b8fda27b8
--- /dev/null
+++ b/frontends/riscos/distribution/!System/310/Modules/Tinct,ffa
Binary files differ
diff --git a/frontends/riscos/distribution/3rdParty/AcornURI/!ReadMe b/frontends/riscos/distribution/3rdParty/AcornURI/!ReadMe
new file mode 100644
index 000000000..4f4ca1e24
--- /dev/null
+++ b/frontends/riscos/distribution/3rdParty/AcornURI/!ReadMe
@@ -0,0 +1,34 @@
+AcornURI 1.04
+-------------
+
+Hi. This is a complete reimplementation of Acorn's URI module such that it
+works on Iyonix. This allows simple URI / URL launching from applications.
+Merge this !System with your own, then (re)launch your favourite browser to
+ensure it's running.
+
+This has a few advantages over the official offering: it's smaller,
+compatible with more browsers and more tolerant of errors.
+
+This is released under the terms of the LGPL, which is included in this
+archive as the file Copying. Previous versions of this module were released
+under the GPL, and are still available from sudden.recoil.org.
+
+Source is available from the same place you downloaded this archive, ie
+<http://sudden.recoil.org/others/acornuri/acornuri104src.zip>
+
+
+Changelog
+---------
+
+v1.04 20-May-06 Relicensed under the LGPL (rather than GPL)
+
+v1.03 11-May-04 Changed the order of things to try, so it now
+ always prefers browsers which are already loaded
+
+v1.02 19-Feb-04 Fixed claiming of URIs where I'd misread the spec
+ Added automatic fall-back to the ANT protocol
+ Removed some service calls to improve reliability
+
+--
+Christian Ludlam
+chris@recoil.org \ No newline at end of file
diff --git a/frontends/riscos/distribution/3rdParty/AcornURI/Copying b/frontends/riscos/distribution/3rdParty/AcornURI/Copying
new file mode 100644
index 000000000..5ab7695ab
--- /dev/null
+++ b/frontends/riscos/distribution/3rdParty/AcornURI/Copying
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/frontends/riscos/distribution/3rdParty/CryptRand/Copyright b/frontends/riscos/distribution/3rdParty/CryptRand/Copyright
new file mode 100644
index 000000000..abb4a9b80
--- /dev/null
+++ b/frontends/riscos/distribution/3rdParty/CryptRand/Copyright
@@ -0,0 +1,46 @@
+CryptRandom
+
+Upstream sources were downloaded and built by
+the GCCSDK Autobuilder.
+
+For information on the autobuilder see the URL:
+
+http://www.riscos.info/index.php/GCCSDK#GCCSDK_Autobuilder
+
+The source used for this build can be found at
+
+http://www.riscos.info/packages/src/System
+
+Upstream source fetched by CVS with
+
+CVS root theom@chiark.greenend.org.uk:/u3/theom/cvs-pub
+Module cryptrandom
+Upstream source fetched using SVN from
+
+
+# For AOF builds needing another branch (if this is not defined AB_SVN is
+
+Copyright
+---------
+
+Copyright 2000-11 Theo Markettos <theo@markettos.org.uk>
+Portions copyright Simon Tatham, Gary S. Brown and Eric Young
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including without
+limitation the rights to use, copy, modify, merge, publish, distribute,
+sublicense, and/or sell copies of the Software, and to permit persons to whom
+the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+SIMON TATHAM OR THEO MARKETTOS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/frontends/riscos/distribution/3rdParty/Iconv/ReadMe b/frontends/riscos/distribution/3rdParty/Iconv/ReadMe
new file mode 100644
index 000000000..907c2c7c8
--- /dev/null
+++ b/frontends/riscos/distribution/3rdParty/Iconv/ReadMe
@@ -0,0 +1,45 @@
+What is Iconv?
+==============
+
+Iconv is a module which provides character set conversion akin to that provided
+by the C iconv() function.
+
+Iconv Installation instructions
+===============================
+
+To install the Iconv module, simply use the System merge utility provided by
+Configure to merge the !System directory provided with the one on your system.
+
+Use the Boot merge facility in Configure to merge the provided !Boot directory
+with the one on your system. If there is no !Boot merge facility provided on
+your system, simply drag the !Boot directory over your existing boot structure.
+
+Further documentation can be found in the "doc" directory.
+
+Note for developers:
+~~~~~~~~~~~~~~~~~~~~
+The "stubs" directory contains source for a set of C stubs.
+See the ReadMe file in that directory for further information.
+
+Licence
+=======
+
+Iconv is Copyright © 2004-13 J-M Bell
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/frontends/riscos/distribution/3rdParty/Iconv/doc/API b/frontends/riscos/distribution/3rdParty/Iconv/doc/API
new file mode 100644
index 000000000..13fa22fce
--- /dev/null
+++ b/frontends/riscos/distribution/3rdParty/Iconv/doc/API
@@ -0,0 +1,132 @@
+Iconv Module API
+================
+
+If using C, then you really should be using the libiconv stubs provided
+(or UnixLib, if appropriate). See the iconv.h header file for further
+documentation of these calls.
+
+Iconv_Open (&57540)
+-------------------
+
+ Create a conversion descriptor
+
+ On Entry: r0 -> string containing name of destination encoding (eg "UTF-8")
+ r1 -> string containing name of source encoding (eg "CP1252")
+
+ On Exit: r0 = conversion descriptor
+ All others preserved
+
+ Either encoding name may have a number of parameters appended to them.
+ Parameters are separated by a pair of forward-slashes ("//").
+ Currently defined parameters are:
+
+ Parameter: Destination: Source:
+
+ TRANSLIT Transliterate unrepresentable None
+ output.
+
+ The conversion descriptor is an opaque value. The user should not,
+ therefore, assume anything about its meaning, nor modify it in any way.
+ Doing so is guaranteed to result in undefined behaviour.
+
+
+Iconv_Iconv (&57541)
+--------------------
+
+ This SWI is deprecated and Iconv_Convert should be used instead.
+
+
+Iconv_Close (&57542)
+--------------------
+
+ Destroy a conversion descriptor
+
+ On Entry: r0 = conversion descriptor to destroy
+
+ On Exit: r0 = 0
+ All others preserved
+
+
+Iconv_Convert (&57543)
+---------------------
+
+ Convert a byte sequence to another encoding
+
+ On Entry: r0 = conversion descriptor returned by Iconv_Open
+ r1 -> input buffer (or NULL to reset encoding context)
+ r2 = length of buffer pointed to by r1
+ r3 -> output buffer
+ r4 = length of buffer pointed to by r3
+
+ On Exit: r0 = number of non-reversible conversions performed (always 0)
+ r1 -> updated input buffer pointer (after last input read)
+ r2 = number of bytes remaining in input buffer
+ r3 -> updated output buffer pointer (i.e. end of output)
+ r4 = number of free bytes in the output buffer
+ All others preserved
+
+ Note that all strings should be NUL-terminated so, if calling from BASIC,
+ some terminating character munging may be needed.
+
+
+Errors:
+
+Should an error occur, the SWI will return with V set and r0 -> error buffer.
+Note that only the error number will be filled in and may be one of:
+
+ ICONV_NOMEM (&81b900)
+ ICONV_INVAL (&81b901)
+ ICONV_2BIG (&81b902)
+ ICONV_ILSEQ (&81b903)
+
+These map directly to the corresponding C errno values.
+
+
+Iconv_CreateMenu (&57544)
+-------------------------
+
+ Create a menu data structure containing all available encodings.
+
+ On Entry: r0 = flags. All bits reserved, must be 0
+ r1 -> buffer, or 0 to read required length
+ r2 = length of buffer in r1
+ r3 -> currently selected encoding name, or 0 if none selected
+ r4 -> buffer for indirected data, or 0 to read length
+ r5 = length of buffer in r4
+
+ On Exit: r2 = required size of buffer in r1 if r1 = 0 on entry,
+ or length of data placed in buffer
+ r5 = required size of buffer in r4 if r4 = 0 on entry,
+ or length of data placed in buffer
+
+ Menu titles are direct form text buffers. Menu entries are indirect text.
+ Entry text is stored in the buffer pointed to by R4 on entry to this call.
+
+
+Iconv_DecodeMenu (&57545)
+-------------------------
+
+ Decode a selection in a menu generated by Iconv_CreateMenu.
+ Places the corresponding encoding name in the result buffer.
+
+ On Entry: r0 = flags. All bits reserved, must be 0
+ r1 -> menu definition
+ r2 -> menu selections, as per Wimp_Poll
+ r3 -> buffer for result or 0 to read required length
+ r4 = buffer length
+
+ On Exit: r4 = required size of buffer if r3 = 0 on entry,
+ or length of data placed in buffer (0 if no selected
+ encoding)
+
+ The menu selections block pointed to by r2 on entry should be based at
+ the root of the encodings menu structure (i.e. index 0 in the block
+ should correspond to the selection in the main encoding menu).
+
+ This call will update the selection status of the menu(s) appropriately.
+
+
+Example Code:
+=============
+
+Example code may be found in the IconvEg BASIC file.
diff --git a/frontends/riscos/distribution/3rdParty/Iconv/doc/ChangeLog b/frontends/riscos/distribution/3rdParty/Iconv/doc/ChangeLog
new file mode 100644
index 000000000..3a22a45fa
--- /dev/null
+++ b/frontends/riscos/distribution/3rdParty/Iconv/doc/ChangeLog
@@ -0,0 +1,114 @@
+Iconv Changelog
+===============
+
+0.01 10-Sep-2004
+----------------
+
+ - Initial version - unreleased.
+
+0.02 27-Sep-2004
+----------------
+
+ - Use allocated SWI & error chunks.
+ - Fix issues in 8bit encoding handling.
+ - First public release.
+
+0.03 22-Jan-2005
+----------------
+
+ - Add Iconv_Convert SWI with improved interface.
+ - Deprecate Iconv_Iconv SWI.
+ - Add encoding name alias handling.
+ - Bundle !Unicode resource.
+
+0.04 08-Apr-2005
+----------------
+
+ - Improve parameter checking.
+ - Fix potential memory leaks.
+ - Add encoding menu creation and selection handling.
+
+0.05 27-Jun-2005
+----------------
+
+ - Improve encoding alias support, using external data file.
+ - Add StubsG build for A9home users.
+
+0.06 05-Nov-2005
+----------------
+
+ - Modified menu creation API to store indirected text in a
+ user-provided buffer. This change is backwards incompatible.
+
+0.07 11-Feb-2006
+----------------
+
+ - Corrected output values for E2BIG errors.
+ - Fixed input pointer update after successful conversion.
+
+0.08 11-Mar-2007
+----------------
+
+ - Tightened up parameter checking in various places.
+ - Improve aliases hash function.
+ - Make 8bit write function's return values match encoding_write
+ with encoding_WRITE_STRICT set.
+ - Fix bug in 8bit writing which resulted in the remaining buffer
+ size being reduced even if nothing was written.
+ - Improve support for endian-specific Unicode variants.
+ - Work around issue in UnicodeLib where remaining buffer size is
+ reduced if an attempt is made to write an unrepresentable character.
+ - Add rudimentary //TRANSLIT support - simply replaces with '?' for now.
+ - Make UnicodeLib handle raw ISO-8859-{1,2,9,10,15} and not attempt
+ ISO-6937-2-25 shift sequences.
+ - Remove StubsG build as A9home now has a C99 capable C library.
+ - Overhaul documentation.
+
+0.09 20-Nov-2008
+----------------
+
+ - Restructured source tree into cross-platform and RO-specific parts.
+ - New build system to go with this.
+ - Fixes for compiling with GCC4.
+ - Introduce *Iconv command which performs command line conversion.
+ - Fixes/improvements to the handlers for:
+ + US-ASCII
+ + UTF-8
+ + ISO-8859-7
+ + ISO-8859-8
+ + ISO-8859-11
+ + Windows-1256
+ + MacRoman
+ + JIS X 0208
+ + JIS X 0212
+ + KS X 1001
+ + EUC-JP
+ + Any ISO-2022 based charset that uses a 94x94 table in GR
+ + Johab
+ + ShiftJIS
+ - Add support for ISO-8859-16 (Latin 10)
+ - Significantly improve detection and reporting of error conditions
+
+0.10 29-Nov-2008
+----------------
+
+ - Fixes to the *Iconv command parameter parsing
+ - Ensure *Iconv outputs all converted data when the input is invalid
+ - Fix handling of illegal UTF-8 byte sequences
+ - Fix handling of incomplete multibyte input sequences.
+
+0.11 04-Jan-2011
+----------------
+
+ - Detect missing mapping file when using 8bit codecs. This prevents spurious
+ memory exhaustion errors.
+ - Toolchain used to build 0.10 turns out to have produced broken code.
+ - Minor additions to the charset alias mapping file.
+
+0.12 20-Jan-2013
+---------------
+
+ - Master alias mapping file now lives in ROOL repository.
+ - Correct handling of trailing valid shift sequences. Previously would
+ erroneously report EINVAL, instead of silently accepting them.
+ - Add proper transliteration behaviour when requested using //TRANSLIT.
diff --git a/frontends/riscos/distribution/3rdParty/Iconv/doc/Uni-iconv b/frontends/riscos/distribution/3rdParty/Iconv/doc/Uni-iconv
new file mode 100644
index 000000000..caea2d0f1
--- /dev/null
+++ b/frontends/riscos/distribution/3rdParty/Iconv/doc/Uni-iconv
@@ -0,0 +1,204 @@
+Introduction:
+=============
+
+This file documents an approximate correlation between the data files
+provided in the !Unicode distribution and the encoding headers in GNU
+libiconv 1.9.1.
+
+Those with '?' in the iconv column either are not represented in iconv
+or I've missed the relevant header file ;)
+
+A number of encodings are present in the iconv distribution but not
+in !Unicode. These are documented at the end of this file.
+
+Changelog:
+==========
+
+v 0.01 (09-Sep-2004)
+~~~~~~~~~~~~~~~~~~~~
+Initial Incarnation
+
+v 0.02 (11-Sep-2004)
+~~~~~~~~~~~~~~~~~~~~
+Documented additional encodings supported by the Iconv module.
+Corrected list of !Unicode deficiencies.
+
+
+!Unicode->iconv:
+================
+
+Unicode: iconv: notes:
+
+Acorn.Latin1 riscos1.h
+
+Apple.CentEuro mac_centraleurope.h
+Apple.Cyrillic mac_cyrillic.h
+Apple.Roman mac_roman.h
+Apple.Ukrainian mac_ukraine.h
+
+BigFive big5.h
+
+ISO2022.C0.40[ISO646] ?
+
+ISO2022.C1.43[IS6429] ?
+
+ISO2022.G94.40[646old] iso646_cn.h
+ISO2022.G94.41[646-GB] ?
+ISO2022.G94.42[646IRV] ?
+ISO2022.G94.43[FinSwe] ?
+ISO2022.G94.47[646-SE] ?
+ISO2022.G94.48[646-SE] ?
+ISO2022.G94.49[JS201K] jisx0201.h top of JIS range
+ISO2022.G94.4A[JS201R] jisx0201.h iso646_jp.h bottom of JIS range
+ISO2022.G94.4B[646-DE] ?
+ISO2022.G94.4C[646-PT] ?
+ISO2022.G94.54[GB1988] ?
+ISO2022.G94.56[Teltxt] ?
+ISO2022.G94.59[646-IT] ?
+ISO2022.G94.5A[646-ES] ?
+ISO2022.G94.60[646-NO] ?
+ISO2022.G94.66[646-FR] ?
+ISO2022.G94.69[646-HU] ?
+ISO2022.G94.6B[Arabic] ?
+ISO2022.G94.6C[IS6397] ?
+ISO2022.G94.7A[SerbCr] ?
+
+ISO2022.G94x94.40[JS6226] ?
+ISO2022.G94x94.41[GB2312] gb2312.h
+ISO2022.G94x94.42[JIS208] jis0x208.h
+ISO2022.G94x94.43[KS1001] ksc5601.h
+ISO2022.G94x94.44[JIS212] jis0x212.h
+ISO2022.G94x94.47[CNS1] cns11643_1.h the tables differ
+ISO2022.G94x94.48[CNS2] cns11643_2.h
+ISO2022.G94x94.49[CNS3] cns11643_3.h
+ISO2022.G94x94.4A[CNS4] cns11643_4.h
+ISO2022.G94x94.4B[CNS5] cns11643_5.h
+ISO2022.G94x94.4C[CNS6] cns11643_6.h
+ISO2022.G94x94.4D[CNS7] cns11643_7.h
+
+ISO2022.G96.41[Lat1] iso8859_1.h
+ISO2022.G96.42[Lat2] iso8859_2.h
+ISO2022.G96.43[Lat3] iso8859_3.h
+ISO2022.G96.44[Lat4] iso8859_4.h
+ISO2022.G96.46[Greek] ?
+ISO2022.G96.47[Arabic] iso8859_6.h ISO-8859-6 ignored
+ISO2022.G96.48[Hebrew] ?
+ISO2022.G96.4C[Cyrill] ?
+ISO2022.G96.4D[Lat5] iso8859_5.h
+ISO2022.G96.50[LatSup] ?
+ISO2022.G96.52[IS6397] ?
+ISO2022.G96.54[Thai] tis620.h
+ISO2022.G96.56[Lat6] iso8859_6.h
+ISO2022.G96.58[L6Sami] ?
+ISO2022.G96.59[Lat7] iso8859_7.h
+ISO2022.G96.5C[Welsh] ?
+ISO2022.G96.5D[Sami] ?
+ISO2022.G96.5E[Hebrew] ?
+ISO2022.G96.5F[Lat8] iso8859_8.h
+ISO2022.G96.62[Lat9] iso8859_9.h
+
+KOI8-R koi8_r.h
+
+Microsoft.CP1250 cp1250.h
+Microsoft.CP1251 cp1251.h
+Microsoft.CP1252 cp1252.h
+Microsoft.CP1254 cp1254.h
+Microsoft.CP866 cp866.h
+Microsoft.CP932 cp932.h cp932ext.h
+
+iconv->!Unicode:
+================
+
+Iconv has the following encodings, which are not present in !Unicode.
+Providing a suitable data file for !Unicode is trivial. Whether UnicodeLib
+will then act upon the addition of these is unknown.
+This list is ordered as per libiconv's NOTES file.
+
+European & Semitic languages:
+
+ ISO-8859-16 (iso8859_16.h)
+ KOI8-{U,RU,T} (koi8_xx.h)
+ CP125{3,5,6,7} (cp125n.h)
+ CP850 (cp850.h)
+ CP862 (cp862.h)
+ Mac{Croatian,Romania,Greek,Turkish,Hebrew,Arabic} (mac_foo.h)
+
+Japanese:
+
+ None afaikt.
+
+Simplified Chinese:
+
+ GB18030 (gb18030.h, gb18030ext.h)
+ HZ-GB-2312 (hz.h)
+
+Traditional Chinese:
+
+ CP950 (cp950.h)
+ BIG5-HKSCS (big5hkscs.h)
+
+Korean:
+
+ CP949 (cp949.h)
+
+Armenian:
+
+ ARMSCII-8 (armscii_8.h)
+
+Georgian:
+
+ Georgian-Academy, Georgian-PS (georgian_academy.h, georgian_ps.h)
+
+Thai:
+
+ CP874 (cp874.h)
+ MacThai (mac_thai.h)
+
+Laotian:
+
+ MuleLao-1, CP1133 (mulelao.h, cp1133.h)
+
+Vietnamese:
+
+ VISCII, TCVN (viscii.h, tcvn.h)
+ CP1258 (cp1258.h)
+
+Unicode:
+
+ BE/LE variants of normal encodings. I assume UnicodeLib handles
+ these, but can't be sure.
+ C99 / JAVA - well, yes.
+
+
+Iconv Module:
+=============
+
+The iconv module is effectively a thin veneer around UnicodeLib. However,
+8bit encodings are implemented within the module rather than using the
+support in UnicodeLib. The rationale for this is simply that, although
+UnicodeLib will understand (and act upon - reportedly...) additions to
+the ISO2022 Unicode resource, other encodings are ignored. As the vast
+majority of outstanding encodings fall into this category, and the code
+is fairly simple, it made sense to implement it within the module.
+
+With use of the iconv module, the list of outstanding encodings is
+reduced to:
+
+ CP1255 (requires state-based transcoding)
+
+ GB18030 (not 8bit - reportedly a requirement of PRC)
+ HZ-GB-2312 (not 8bit - supported by IE4)
+
+ CP950 (not 8bit - a (MS) variant of Big5)
+ BIG5-HKSCS (not 8bit - again, a Big5 variant)
+
+ CP949 (not 8bit)
+
+ ARMSCII-8 (easily implemented, if required)
+
+ VISCII (easily implemented, if required)
+ CP1258, TCVN (requires state-based transcoding)
+
+Additionally, the rest of the CodePage encodings implemented in iconv
+but not listed above (due to omissions from the iconv documentation)
+are implemented by the iconv module.
diff --git a/frontends/riscos/distribution/3rdParty/SharedULib/Copyright b/frontends/riscos/distribution/3rdParty/SharedULib/Copyright
new file mode 100644
index 000000000..b6784ed06
--- /dev/null
+++ b/frontends/riscos/distribution/3rdParty/SharedULib/Copyright
@@ -0,0 +1,761 @@
+UnixLib Copyright
+-----------------
+
+UnixLib is Copyright (c) 1995-1999 Simon Callan, Nick Burrett,
+Nicholas Clark and Peter Burwood.
+
+These contributors have expressed "no interest" in any further licensing or
+copyright in regards to UnixLib.
+
+Other sections are (c) 1999-2006 Nick Burrett, John Tytgat, Peter Naulls,
+Peter Teichmann, Alex Waugh, Christian Ludlam, Theo Markettos, Graham Shaw,
+James Bursa and John-Mark Bell.
+
+In January 2005, permission was obtained from all relevant contributors
+by Peter Naulls to license all past and present contributions to UnixLib
+(where possible) under the revised BSD license. The license is included
+in the next section and is applicable to all code in UnixLib that does not
+have an explicit license in its source.
+
+Prior to 4th January 2005 and after May 2001, UnixLib contained code licensed
+under the GNU General Public License, and versions of UnixLib produced
+between these dates are subject to the provisions of the GPL. We realised
+that this might cause potential problems with the wider use of UnixLib in
+RISC OS, and along with the desire to clarify the overall licensing status of
+UnixLib, GPL code was removed from UnixLib and the above permission from all
+copyright holders allowed UnixLib contributions to be relicensed as per the
+revised BSD license. The GPL is therefore not included in this notice
+as it is no longer relevant to UnixLib.
+
+
+Practical notes on using UnixLib in your own programs:
+
+This is a plain English version of guidelines for use of UnixLib in
+your programs. It does not override any of the licenses included
+below, but is intended to state instances when it may be used in
+free and non-free software. Where there is contradiction or
+ambiguity in this wording, please refer to the specifics of the licence
+in question. These recommendations are based upon our understading
+of the GPL/LGPL and BSD licenses and are subject to change should
+our understanding of the topics improve.
+
+ - Because UnixLib contains code that is subject to the Lesser GNU
+ Public License, the LGPL is the overriding consideration when
+ linking UnixLib to programs (unless the program itself is GPL).
+
+ - You are free to use sections of UnixLib in your own programs
+ subject to the conditions of that code. If the entirety of
+ that code is under a BSD license, then you can generally use
+ that code as you see fit, and there is no further obligation
+ from you as long as the copyright notice remains. If you
+ use LGPL code in your program, then your program must also be
+ distributed under the LGPL (or GPL).
+
+ - If you use UnixLib in its intended original form - that is as a
+ supporting library for ported programs to RISC OS - then your program
+ is subject to the LGPL; or the GPL if the program is covered by that.
+ Note that you must make the source and any modifications available to for
+ both if requested. This is of course equally true if you write an original
+ GPL program using UnixLib. In most cases, no additional action is
+ required of you, especially since source is usually readibly available.
+
+ - If you use UnixLib for a non-free program - whether that's commercial or
+ otherwise, then you should carefully read section 6 of the LGPL. This
+ applies, because at the present time, there is no practical method of
+ dynamic linking on RISC OS. At such time that UnixLib is available as a
+ shared library, then programs dynamically linking to it will no longer be
+ subject to the LPGL as applied to UnixLib.
+
+ - Section 6 means that in practice, you must supply, or offer to
+ supply either source or object code for your program.
+ This is mainly to allow rebuilding of the executable program
+ with later or modified versions of UnixLib. You must of course
+ supply (or better, contribute to the GCCSDK project) any
+ modifications you make to UnixLib upon request.
+
+
+Recommended reading:
+
+Frequently Asked Questions about the GNU GPL
+http://www.fsf.org/licenses/gpl-faq.html (has some sections on LGPL)
+
+About the justifications for using LGPL
+http://www.fsf.org/licenses/why-not-lgpl.html
+
+
+
+===========================================================================
+
+ Copyright (c) 1995-2005 UnixLib Developers
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+===========================================================================
+
+Portions of UnixLib are copyright The Regents of the University of
+California.
+
+Portions of this library are copyright Sun Microsystems, Inc. The
+
+Portions of this library are derived from the GNU C Library and fall under
+the GNU Library General Public License.
+
+Portions of this library are copyright Henry Spencer.
+
+Portions of this library are copyright The Regents of the University of
+California, Sun Microsystems, Inc., Scriptics Corporation, ActiveState
+Corporation and other parties.
+
+Portions of this library are copyright PostgreSQL Global Development Group.
+
+The licenses for the above are duplicated below.
+
+
+===========================================================================
+
+ Copyright (c) The Regents of the University of California.
+ All rights reserved.
+
+ This code is derived from software contributed to Berkeley by
+ Chris Torek.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+ This product includes software developed by the University of
+ California, Berkeley and its contributors.
+ 4. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+===========================================================================
+
+ Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+
+ Developed at SunPro, a Sun Microsystems, Inc. business.
+ Permission to use, copy, modify, and distribute this
+ software is freely granted, provided that this notice
+ is preserved.
+
+===========================================================================
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+===========================================================================
+
+Copyright (c) 1998, 1999 Henry Spencer. All rights reserved.
+
+Development of this software was funded, in part, by Cray Research Inc.,
+UUNET Communications Services Inc., Sun Microsystems Inc., and Scriptics
+Corporation, none of whom are responsible for the results. The author
+thanks all of them.
+
+Redistribution and use in source and binary forms -- with or without
+modification -- are permitted for any purpose, provided that
+redistributions in source form retain this entire copyright notice and
+indicate the origin and nature of any modifications.
+
+I'd appreciate being given credit for this package in the documentation
+of software which uses it, but that is not a requirement.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+HENRY SPENCER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+===========================================================================
+
+This software is copyrighted by the Regents of the University of
+California, Sun Microsystems, Inc., Scriptics Corporation, ActiveState
+Corporation and other parties. The following terms apply to all files
+associated with the software unless explicitly disclaimed in
+individual files.
+
+The authors hereby grant permission to use, copy, modify, distribute,
+and license this software and its documentation for any purpose, provided
+that existing copyright notices are retained in all copies and that this
+notice is included verbatim in any distributions. No written agreement,
+license, or royalty fee is required for any of the authorized uses.
+Modifications to this software may be copyrighted by their authors
+and need not follow the licensing terms described here, provided that
+the new terms are clearly indicated on the first page of each file where
+they apply.
+
+IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
+FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
+DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
+IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
+NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+MODIFICATIONS.
+
+GOVERNMENT USE: If you are acquiring this software on behalf of the
+U.S. government, the Government shall have only "Restricted Rights"
+in the software and related documentation as defined in the Federal
+Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
+are acquiring the software on behalf of the Department of Defense, the
+software shall be classified as "Commercial Computer Software" and the
+Government shall have only "Restricted Rights" as defined in Clause
+252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the
+authors grant the U.S. Government and others acting in its behalf
+permission to use and distribute the software in accordance with the
+terms specified in this license.
+
+===========================================================================
+
+PostgreSQL Database Management System
+(formerly known as Postgres, then as Postgres95)
+
+Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
+
+Portions Copyright (c) 1994, The Regents of the University of California
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose, without fee, and without a written agreement
+is hereby granted, provided that the above copyright notice and this
+paragraph and the following two paragraphs appear in all copies.
+
+IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
+DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
+PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
diff --git a/frontends/riscos/distribution/3rdParty/Tinct/!Help b/frontends/riscos/distribution/3rdParty/Tinct/!Help
new file mode 100644
index 000000000..2e27e354f
--- /dev/null
+++ b/frontends/riscos/distribution/3rdParty/Tinct/!Help
@@ -0,0 +1,304 @@
+Tinct
+=====
+This module provides the necessary functionality to display alpha-blended
+sprites both scaled and otherwise. It also provides functions for dithering,
+error diffusion and performing bi-linear filtering to improve their appearance.
+
+
+Technical information
+¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
+To ensure future compatibility, this module does not patch the OS in any way
+and works in a totally legal way. It also does not write to itself in any
+way, so is suitable for running from ROM.
+ Redirection to sprites is supported, although due to the overheads involved
+with caching the colour translation tables it is not recommended that this is
+done frequently. There are some exceptions to this, however, as redirecting to
+a 16bpp or 32bpp mode sprite does not require any translation tables, and
+redirecting to a sprite that has the same mode and palette as the previous
+destination that Tinct was used for causes a minimum overhead as the
+translation tables are checked and cached values are used if possible.
+
+Format of a sprite with 8-bit alpha channel
+¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
+The sprite format used by Tinct differs from those used by RISC OS Select,
+and whilst facilities are supplied to convert sprites into the required format,
+no facilities are provided to manipulate them.
+ All sprites used by Tinct must be 32bpp, and cannot have a standard RISC OS
+mask specified. The basic format of the sprite is shown below, with the
+restrictions to the standard sprite format marked with an asterisk (*):
+
+ [+0] Offset to next sprite
+ [+4] Sprite name, up to 12 characters with trailing zeroes
+ [+16] Width in words - 1
+ [+20] Height in scan lines - 1
+ [+24] First bit used
+ [+28] Last bit used
+ [+32] Offset to sprite image
+ [+36] * Offset to sprite image (no mask allowed)
+ [+40] * Sprite type (must be 0x301680B5)
+
+Whereas for normal sprites the sprite image would be a series of colour words
+of the format RrGgBb00, alpha-blended sprites use the empty byte to specify
+the alpha value, ie RrGgBbAa.
+ The alpha values represent the blending level on a linear scale where 0x00
+represents that the source pixel is totally transparent and 0xff that it is
+totally opaque. It should be noted that as a standard 32bpp sprite (eg as
+created with !Paint) will have the alpha channel set to 0x00 by default no
+output will be visible when plotting as an alpha-blended sprite.
+
+Error handling
+¨¨¨¨¨¨¨¨¨¨¨¨¨¨
+If an incorrect sprite is attempted to be used, Tinct currently always returns
+error number 0x700 (SBadSpriteFile) rather than the specific cause of the
+problem (eg. BadDPI, BadMSFlags or BadPixelDepth) as OS_SpriteOp would do.
+There are several technical reasons for this behaviour, and future versions of
+Tinct may return more descriptive errors depending on the cause.
+
+
+SWIs provided
+¯¯¯¯¯¯¯¯¯¯¯¯¯
+Tinct provides four SWIs to plot sprites and one to convert sprites to their
+32bpp equivalent. All values supplied to Tinct must be in OS units, and the
+current OS clipping rectangle is used.
+ The sprite pointers provided are equivalent to calling OS_SpriteOp with
+bit 9 of the reason code set. To plot a sprite by name, the sprite should
+first be found by using OS_SpriteOp with reason code 0x18 and using the
+returned sprite address.
+
+Tinct_PlotAlpha (0x57240)
+¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
+Plots an alpha-blended sprite at the specified coordinates.
+
+-> R2 Sprite pointer
+ R3 X coordinate
+ R4 Y coordinate
+ R7 Flag word
+
+
+Tinct_PlotScaledAlpha (0x57241)
+¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
+Plots a scaled alpha-blended sprite at the specified coordinates.
+
+-> R2 Sprite pointer
+ R3 X coordinate
+ R4 Y coordinate
+ R5 Scaled sprite width
+ R6 Scaled sprite height
+ R7 Flag word
+
+
+Tinct_Plot (0x57242)
+¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
+Plots a sprite at the specified coordinates with a constant 0xff value for
+the alpha channel, ie without a mask.
+
+-> R2 Sprite pointer
+ R3 X coordinate
+ R4 Y coordinate
+ R7 Flag word
+
+
+Tinct_PlotScaled (0x57243)
+¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
+Plots a scaled sprite at the specified coordinates with a constant 0xff value
+for the alpha channel, ie without a mask.
+
+-> R2 Sprite pointer
+ R3 X coordinate
+ R4 Y coordinate
+ R5 Scaled sprite width
+ R6 Scaled sprite height
+ R7 Flag word
+
+
+Tinct_ConvertSprite (0x57244)
+¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
+Converts a paletted sprite into its 32bpp equivalent. Sufficient memory must
+have previously been allocated for the sprite (44 + width * height * 4).
+ As sprites with 16bpp or 32bpp do not have palettes, conversion cannot be
+performed on these variants. All sprites must be supplied with a full palette,
+eg 8bpp must have 256 palette entries.
+
+-> R2 Source sprite pointer
+ R3 Destination sprite pointer
+
+
+Tinct_AvailableFeatures (0x57245)
+¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
+Returns the features available to the caller by specifying bits in the flag
+word. The features available are unique for each mode, although the current
+version of Tinct supports the same subset of features for all modes.
+
+-> R0 Feature to test for, or 0 for all features
+<- R0 Features available
+
+
+Tinct_Compress (0x57246)
+¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
+Compresses an image using a fast algorithm. Sufficient memory must have been
+previously allocated for the maximum possible compressed size. This value is
+equal to 28 + (width * height * 4) * 33 / 32.
+
+-> R0 Source sprite pointer
+ R2 Output data buffer
+ R3 Output bytes available
+ R7 Flag word
+<- R0 Size of compressed data
+
+
+Tinct_Decompress (0x57247)
+¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
+Decompresses an image previously compressed. Sufficient memory must have been
+previously allocated for the decompressed data (44 + width * height * 4) where
+width and height are available at +0 and +4 of the compressed data respectively.
+
+-> R0 Input data buffer
+ R2 Output data buffer
+ R7 Flag word (currently 0)
+<- R0 Size of decompressed data
+
+
+Flag word (plotting)
+¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
+All the SWIs provided by Tinct for plotting use a common flag word to
+describe the manner in which the plot is performed. Each bit controls a
+particular characteristic of the plotting:
+
+ 0 Forcibly read the screen base (only use if hardware scrolling)
+ 1 Use bi-linear filtering when scaling sprites
+ 2 Dither colours in 16bpp and below
+ 3 Perform error diffusion if bit 2 clear, invert dither pattern if set
+ 4 Horizontally fill the current graphics window with the sprite
+ 5 Vertically fill the current graphics window with the sprite
+ 6 Forcibly read the palette (only use if changing palette outside of
+ the WIMP)
+ 7 Use OS_SpriteOp to perform final plotting (see note)
+ 8+ Reserved (must be 0) if bit 7 is clear, background colour to
+ blend the alpha channel to otherwise
+
+If a bit is set in the flag word that cannot be honoured by the current
+version of Tinct then it is ignored. Tinct_AvailableFeatures can be used
+to test in advance what flags will be honoured.
+
+Bi-linear filtering
+¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
+Although bi-linear filtering is only relevant during scaled plotting, this
+situation occurs when the EigFactors of the mode are not equal. As such, an
+application should always set their preferred flags to ensure consistency. The
+case of XEig * 2 = YEig (rectangular pixel modes) for even height sprites is a
+special case and has optimised code implemented.
+ There is an upper limit to the size of sprite that can be bi-linear filtered.
+The checks that are currently made are:
+
+ scaled_width / sprite_width < 256
+ scaled_height / sprite_height < 256
+ scaled_width * max(sprite_height, scaled_height) < 32,768
+
+ It should be noted that as bi-linear filtering is performed as a pre-filter,
+it carries a sizable overhead. However, as all scaling calculations are
+performed during this filter, tiled plotting (bits 4 and 5) are affected by
+a smaller margin (in certain cases a speed gain can be achieved).
+ As bi-linear filtering is performed using a pre-filter, it can be used in
+association with OS_SpriteOp rendering.
+
+Error diffusion and dithering
+¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
+If both error diffusion and dithering are enabled then the image is plotted
+using only dithering, but with the dither pattern inverted. This enables an
+application to provide the user with what appears to be a higher quality image
+by redrawing every frame with the flag toggled.
+ There is a significant speed difference between dithering and error diffusion,
+and Tinct does not support error diffusion in all colour depths. If error
+diffusion is requested, but cannot be performed by Tinct then dithering with
+an inverted pattern is used (as if bits 2 and 3 were set).
+ There is an upper limit to the size of sprite that Tinct can perform error
+diffusion on. This is currently set to a display width of 2047 pixels wide with
+an unlimited height. Any attempt to use a higher resolution will result in
+dithered rendering with an inverted pattern (ie bits 2 and 3 set).
+ As error diffusion and dithering are implemented during the plot cycle, it is
+not possible to use them in association with OS_SpriteOp rendering. However,
+the bits should be set as future versions of Tinct may respect them for users
+of RISC OS 3.1 where true colour sprites are not supported.
+
+Sprite filling
+¨¨¨¨¨¨¨¨¨¨¨¨¨¨
+If filling is specified, then the supplied co-ordinate is the offset of the
+pattern relative to (0, 0) used for the fill. For example, a 64x64 sprite that
+is plotted with bits 4 and 5 set and a position of (32, 16) would fill the
+current graphics window with multiple copies of the image stating with the
+first image plotted at (-32, -48).
+ The caller should not concern itself with the size of the image being tiled
+as small images are internally optimised where possible to maximise the
+plotting speed.
+
+Rendering using OS_SpriteOp
+¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
+It can be useful to use Tinct to perform the rendering to using OS_SpriteOp.
+There are two general situations where this may be useful:
+
+ 1) To output to a printer driver
+ 2) To allow hardware acceleraton (such as a ViewFinder card)
+
+By using Tinct rather than a direct OS_SpriteOp call, it allows the caller to
+retain certain features Tinct provides (such as sprite filling and a limited
+version of the standard alpha blending) and allows the caller to have a common
+plotting interface.
+ When using this feature for alpha-blended sprites, the background colour
+specified in the top 24-bits of the flag word is used for blending with any
+pixels that are not transparent. This requires that Tinct requires a second
+copy of the sprite in memory to modify which may present a significant overhead
+in some situations. Plotting opaquely does not have any such overheads.
+ Using OS_SpriteOp rendering does not currently work on RISC OS 3.1 or earlier
+due to the lack of support for true colour sprites. Future versions of Tinct
+may remove this restriction.
+
+
+Flag word (compression)
+¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
+The flag word used by Tinct_Compress can be used to improve the compression
+ratio by performing pre-filtering on the data. The flags below relate only to
+compression and should not be passed to Tinct_Decompress.
+
+ 0 Image is opaque, remove the alpha channel prior to compression
+
+All unspecified bits are reserved for future expansion and as such should be
+set to 0.
+
+Compressed data format
+¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
+Certain aspects of the compressed data format are guaranteed to remain constant,
+so may be used by applications.
+
+ +0 Sprite width
+ +4 Sprite height
+ +8 Sprite name (12 chars)
+ +20 Compression flags
+ +24 Number of bytes of data following
+
+The method of compression is not guaranteed to remain constant over future
+revisions of Tinct, but subsequent versions will decompress data compressed
+with previous versions.
+
+
+Contact details
+¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
+If you would like to report a problem relating to Tinct, provide feedback, or
+request a licence for a commercial product, please use the details below:
+
+Address: 5 Queens Close, East Markham, Newark, Nottinghamshire, NG22 0QY. UK
+E-mail: info@tinct.net
+Website: www.tinct.net
+
+
+Copyright and licence details
+¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
+Tinct is © copyright Richard Wilson, 2004.
+
+Distribution and usage
+¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
+Unrestricted use of Tinct is hereby granted for any non-commercial product. Any
+use as part of a commercial product requires written consent from the author.
+No charge may be made relating to the distribution of this software, and this
+copyright information should be included in all copies of the software.
+ Modified versions of this program may not be distributed without the authors
+consent, nor may modified versions of the source code or relating files. \ No newline at end of file
diff --git a/frontends/riscos/distribution/LeesMij b/frontends/riscos/distribution/LeesMij
new file mode 100644
index 000000000..a0d3ff41f
--- /dev/null
+++ b/frontends/riscos/distribution/LeesMij
@@ -0,0 +1,73 @@
+NetSurf
+=======
+
+Dit is een ontwikkelversie van NetSurf, een webbrowser met open
+broncode.
+
+De nieuwste versie van NetSurf is verkrijgbaar via:
+
+ http://www.netsurf-browser.org/
+
+
+Installatie
+-----------
+
+De installatie gaat in drie stappen:
+
+ 1. Gebruik de samenvoegfaciliteit van !Boot (te openen via de
+ besturingssysteeminstellingen -> !Boot) om de meegeleverde
+ !Boot-map samen te voegen met die van het systeem.
+
+ Als het besturingssysteem geen !Boot-samenvoegfaciliteit
+ ondersteund, sleep dan de meegeleverde !Boot-map in de map
+ waarin de bestaande !Boot-structuur staat.
+
+ 2. Gebruik de samenvoegfaciliteit van !System (te openen via de
+ besturingssysteeminstellingen -> !System) om de meegeleverde
+ !System-map samen te voegen met die van het systeem.
+
+ 3. Sleep de !NetSurf-programmamap naar de gewenste map op de
+ harde schijf.
+
+Dubbelklik op het programma !NetSurf in de gekozen locatie om de
+NetSurf-browser te starten.
+
+
+Opmerking NetSurf vereist de WindowManager-module 3.80 of een
+ recentere versie. Deze is standaard aanwezig in RISC OS 4
+ of een recentere versie. Voor RISC OS 3-gebruikers zijn
+ er twee mogelijkheden om NetSurf te kunnen gebruiken:
+
+ - De 'Universal !Boot Sequence' van Acorn:
+ http://www.riscos.com/ftp_space/generic/uniboot/
+ - HardDisc4-schijfstructuur van RISC OS Open:
+ https://www.riscosopen.org/content/downloads/common/
+
+
+Opmerking RISC OS 3.1 of oudere versies worden niet ondersteund.
+
+
+Licenties
+---------
+
+NetSurf wordt geleverd onder de GPL, evenals verschillende andere
+licenties voor de verschillende componenten die het programma
+gebruikt. Bezoek in de NetSurf-browser het URL-adres 'about:licence'
+voor meer informatie.
+
+
+De meegeleverde !Boot- en !System-mappen bevatten items die door
+derden zijn geproduceerd. De bijbehorende licenties zijn meegeleverd
+in de map '3rdParty'.
+
+AcornURI
+ http://sudden.recoil.org/others/
+
+Iconv
+ http://www.netsurf-browser.org/iconv/
+
+SharedUnixLibrary
+ http://www.riscos.info/downloads/gccsdk/sharedunixlib/system.zip
+
+Tinct
+ http://www.tinct.net/tinct.asp
diff --git a/frontends/riscos/distribution/ReadMe b/frontends/riscos/distribution/ReadMe
new file mode 100644
index 000000000..eec39d6ab
--- /dev/null
+++ b/frontends/riscos/distribution/ReadMe
@@ -0,0 +1,61 @@
+NetSurf
+=======
+
+This is a development build of NetSurf, an open source web browser.
+
+The latest version of NetSurf is available from:
+
+ http://www.netsurf-browser.org/
+
+
+Installation
+------------
+
+Installation is a three step process:
+
+ 1. Use the Boot Merge facility provided by Configure to merge
+ the supplied !Boot directory with the one on your system.
+
+ If there is no !Boot merge facility on your system, simply
+ drag the supplied !Boot over your existing boot structure.
+
+ 2. Use the System Merge facility provided by Configure to merge
+ the supplied !System directory with the one on your system.
+
+ 3. Drag the !NetSurf application directory to a place on your
+ hard disc.
+
+Double click on !NetSurf in your chosen location to launch NetSurf.
+
+
+Note NetSurf requires WindowManager 3.80 or later. This comes
+ with RISC OS 4 and above. RISC OS 3 users should install
+ the Universal Boot Sequence from:
+ http://acorn.riscos.com/riscos/releases/UniBoot/
+
+Note RISC OS 3.1 and earlier are not supported.
+
+
+Licences
+--------
+
+NetSurf is provided under the GPL, as well as several other licences
+for different components it uses. Visit NetSurf's about:licence URL
+for details.
+
+
+The !Boot and !System directories contain items provided produced
+by third parties. Their licences are provided in the 3rd Party
+directory.
+
+AcornURI
+ http://sudden.recoil.org/others/
+
+Iconv
+ http://www.netsurf-browser.org/iconv/
+
+SharedUnixLibrary
+ http://www.riscos.info/downloads/gccsdk/sharedunixlib/system.zip
+
+Tinct
+ http://www.tinct.net/tinct.asp
diff --git a/frontends/riscos/download.c b/frontends/riscos/download.c
new file mode 100644
index 000000000..cddb449de
--- /dev/null
+++ b/frontends/riscos/download.c
@@ -0,0 +1,1629 @@
+/*
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2003 Rob Jackson <jacko@xms.ms>
+ * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * RISC OS download windows implementation.
+ *
+ * This file implements the interface given by desktop/gui_download.h
+ * for download windows. Each download window has an associated
+ * fetch. Downloads start by writing received data to a temporary
+ * file. At some point the user chooses a destination (by drag &
+ * drop), and the temporary file is then moved to the destination and
+ * the download continues until complete.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <time.h>
+#include <curl/curl.h>
+#include <libwapcaplet/libwapcaplet.h>
+
+#include "oslib/mimemap.h"
+#include "oslib/osargs.h"
+#include "oslib/osfile.h"
+#include "oslib/osfind.h"
+#include "oslib/osfscontrol.h"
+#include "oslib/osgbpb.h"
+#include "oslib/wimp.h"
+#include "oslib/wimpspriteop.h"
+
+#include "utils/sys_time.h"
+#include "utils/nsoption.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/nsurl.h"
+#include "utils/utf8.h"
+#include "utils/utils.h"
+#include "utils/string.h"
+#include "utils/corestrings.h"
+#include "desktop/gui_download.h"
+#include "desktop/download.h"
+
+#include "riscos/gui.h"
+#include "riscos/dialog.h"
+#include "riscos/mouse.h"
+#include "riscos/save.h"
+#include "riscos/query.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/ucstables.h"
+#include "riscos/filetype.h"
+
+#define ICON_DOWNLOAD_ICON 0
+#define ICON_DOWNLOAD_URL 1
+#define ICON_DOWNLOAD_PATH 2
+#define ICON_DOWNLOAD_DESTINATION 3
+#define ICON_DOWNLOAD_PROGRESS 5
+#define ICON_DOWNLOAD_STATUS 6
+
+#define RO_DOWNLOAD_MAX_PATH_LEN 255
+
+typedef enum
+{
+ QueryRsn_Quit,
+ QueryRsn_Abort,
+ QueryRsn_Overwrite
+} query_reason;
+
+
+/** Data for a download window. */
+struct gui_download_window {
+ /** Associated context, or 0 if the fetch has completed or aborted. */
+ download_context *ctx;
+ unsigned int received; /**< Amount of data received so far. */
+ unsigned int total_size; /**< Size of resource, or 0 if unknown. */
+
+ wimp_w window; /**< RISC OS window handle. */
+ bits file_type; /**< RISC OS file type. */
+
+ char url[256]; /**< Buffer for URL icon. */
+ char sprite_name[20]; /**< Buffer for sprite icon. */
+ char path[RO_DOWNLOAD_MAX_PATH_LEN]; /**< Buffer for pathname icon. */
+ char status[256]; /**< Buffer for status icon. */
+
+ /** User has chosen the destination, and it is being written. */
+ bool saved;
+ bool close_confirmed;
+ bool error; /**< Error occurred, aborted. */
+
+ /** RISC OS file handle, of temporary file when !saved, and of
+ * destination when saved. */
+ os_fw file;
+
+ query_id query;
+ query_reason query_rsn;
+
+ struct timeval start_time; /**< Time download started. */
+ struct timeval last_time; /**< Time status was last updated. */
+ unsigned int last_received; /**< Value of received at last_time. */
+ float average_rate; /**< Moving average download rate. */
+ unsigned int average_points; /**< Number of points in the average. */
+
+ bool send_dataload; /**< Should send DataLoad message when finished */
+ wimp_message save_message; /**< Copy of wimp DataSaveAck message */
+
+ struct gui_download_window *prev; /**< Previous in linked list. */
+ struct gui_download_window *next; /**< Next in linked list. */
+};
+
+
+/** List of all download windows. */
+static struct gui_download_window *download_window_list = 0;
+/** Download window with current save operation. */
+static struct gui_download_window *download_window_current = 0;
+
+/** Template for a download window. */
+static wimp_window *download_template;
+
+/** Width of progress bar at 100%. */
+static int download_progress_width;
+/** Coordinates of progress bar. */
+static int download_progress_x0;
+static int download_progress_y0;
+static int download_progress_y1;
+
+/** Current download directory. */
+static char *download_dir = NULL;
+static size_t download_dir_len;
+
+static void ro_gui_download_drag_end(wimp_dragged *drag, void *data);
+static const char *ro_gui_download_temp_name(struct gui_download_window *dw);
+static void ro_gui_download_update_status(struct gui_download_window *dw);
+static void ro_gui_download_update_status_wrapper(void *p);
+static void ro_gui_download_window_hide_caret(struct gui_download_window *dw);
+static char *ro_gui_download_canonicalise(const char *path);
+static bool ro_gui_download_check_space(struct gui_download_window *dw,
+ const char *dest_file, const char *orig_file);
+static os_error *ro_gui_download_move(struct gui_download_window *dw,
+ const char *dest_file, const char *src_file);
+static void ro_gui_download_remember_dir(const char *path);
+static bool ro_gui_download_save(struct gui_download_window *dw,
+ const char *file_name, bool force_overwrite);
+static void ro_gui_download_send_dataload(struct gui_download_window *dw);
+static void ro_gui_download_window_destroy_wrapper(void *p);
+static bool ro_gui_download_window_destroy(struct gui_download_window *dw, bool quit);
+static void ro_gui_download_close_confirmed(query_id, enum query_response res, void *p);
+static void ro_gui_download_close_cancelled(query_id, enum query_response res, void *p);
+static void ro_gui_download_overwrite_confirmed(query_id, enum query_response res, void *p);
+static void ro_gui_download_overwrite_cancelled(query_id, enum query_response res, void *p);
+
+static bool ro_gui_download_click(wimp_pointer *pointer);
+static bool ro_gui_download_keypress(wimp_key *key);
+static void ro_gui_download_close(wimp_w w);
+
+static const query_callback close_funcs =
+{
+ ro_gui_download_close_confirmed,
+ ro_gui_download_close_cancelled
+};
+
+static const query_callback overwrite_funcs =
+{
+ ro_gui_download_overwrite_confirmed,
+ ro_gui_download_overwrite_cancelled
+};
+
+
+/**
+ * Load the download window template.
+ */
+
+void ro_gui_download_init(void)
+{
+ download_template = ro_gui_dialog_load_template("download");
+ download_progress_width =
+ download_template->icons[ICON_DOWNLOAD_STATUS].extent.x1 -
+ download_template->icons[ICON_DOWNLOAD_STATUS].extent.x0;
+ download_progress_x0 =
+ download_template->icons[ICON_DOWNLOAD_PROGRESS].extent.x0;
+ download_progress_y0 =
+ download_template->icons[ICON_DOWNLOAD_PROGRESS].extent.y0;
+ download_progress_y1 =
+ download_template->icons[ICON_DOWNLOAD_PROGRESS].extent.y1;
+}
+
+
+/**
+ * Returns the pathname of a temporary file for this download.
+ *
+ * \param dw download window
+ * \return ptr to pathname
+ */
+
+const char *ro_gui_download_temp_name(struct gui_download_window *dw)
+{
+ static char temp_name[40];
+ snprintf(temp_name, sizeof temp_name, "<Wimp$ScrapDir>.ns%x",
+ (unsigned int) dw);
+ return temp_name;
+}
+
+/**
+ * Try and find the correct RISC OS filetype from a download context.
+ */
+static nserror download_ro_filetype(download_context *ctx, bits *ftype_out)
+{
+ nsurl *url = download_context_get_url(ctx);
+ bits ftype = 0;
+ lwc_string *scheme;
+
+ /* If the file is local try and read its filetype */
+ scheme = nsurl_get_component(url, NSURL_SCHEME);
+ if (scheme != NULL) {
+ bool filescheme;
+ if (lwc_string_isequal(scheme,
+ corestring_lwc_file,
+ &filescheme) != lwc_error_ok) {
+ filescheme = false;
+ }
+
+ if (filescheme) {
+ lwc_string *path = nsurl_get_component(url, NSURL_PATH);
+ if (path != NULL && lwc_string_length(path) != 0) {
+ char *raw_path;
+ raw_path = curl_unescape(lwc_string_data(path),
+ lwc_string_length(path));
+ if (raw_path != NULL) {
+ ftype = ro_filetype_from_unix_path(raw_path);
+ curl_free(raw_path);
+ }
+ }
+ }
+ }
+
+ /* If we still don't have a filetype (i.e. failed reading local
+ * one or fetching a remote object), then use the MIME type.
+ */
+ if (ftype == 0) {
+ /* convert MIME type to RISC OS file type */
+ os_error *error;
+ const char *mime_type;
+
+ mime_type = download_context_get_mime_type(ctx);
+ error = xmimemaptranslate_mime_type_to_filetype(mime_type, &ftype);
+ if (error) {
+ LOG("xmimemaptranslate_mime_type_to_filetype: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ ftype = 0xffd;
+ }
+ }
+
+ *ftype_out = ftype;
+ return NSERROR_OK;
+}
+
+/**
+ * Create and open a download progress window.
+ *
+ * \param ctx Download context
+ * \param gui The RISCOS gui window to download for.
+ * \return A new gui_download_window structure, or NULL on error and error
+ * reported
+ */
+
+static struct gui_download_window *
+gui_download_window_create(download_context *ctx, struct gui_window *gui)
+{
+ nsurl *url = download_context_get_url(ctx);
+ const char *temp_name;
+ char *filename = NULL;
+ struct gui_download_window *dw;
+ bool space_warning = false;
+ os_error *error;
+ char *local_path;
+ nserror err;
+ size_t i, last_dot;
+
+ dw = malloc(sizeof *dw);
+ if (!dw) {
+ ro_warn_user("NoMemory", 0);
+ return 0;
+ }
+
+ dw->ctx = ctx;
+ dw->saved = false;
+ dw->close_confirmed = false;
+ dw->error = false;
+ dw->query = QUERY_INVALID;
+ dw->received = 0;
+ dw->total_size = download_context_get_total_length(ctx);
+
+ /** @todo change this to take a reference to the nsurl and use
+ * that value directly rather than using a fixed buffer.
+ */
+ strncpy(dw->url, nsurl_access(url), sizeof dw->url);
+ dw->url[sizeof dw->url - 1] = 0;
+
+ dw->status[0] = 0;
+ gettimeofday(&dw->start_time, 0);
+ dw->last_time = dw->start_time;
+ dw->last_received = 0;
+ dw->file_type = 0;
+ dw->average_rate = 0;
+ dw->average_points = 0;
+
+ /* get filetype */
+ err = download_ro_filetype(ctx, &dw->file_type);
+ if (err != NSERROR_OK) {
+ ro_warn_user(messages_get_errorcode(err), 0);
+ free(dw);
+ return 0;
+ }
+
+ /* open temporary output file */
+ temp_name = ro_gui_download_temp_name(dw);
+ if (!ro_gui_download_check_space(dw, temp_name, NULL)) {
+ /* issue a warning but continue with the download because the
+ user can save it to another medium whilst it's downloading */
+ space_warning = true;
+ }
+ error = xosfind_openoutw(osfind_NO_PATH | osfind_ERROR_IF_DIR,
+ temp_name, 0, &dw->file);
+ if (error) {
+ LOG("xosfind_openoutw: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ free(dw);
+ return 0;
+ }
+
+ /* fill in download window icons */
+ download_template->icons[ICON_DOWNLOAD_URL].data.indirected_text.text =
+ dw->url;
+ download_template->icons[ICON_DOWNLOAD_URL].data.indirected_text.size =
+ sizeof dw->url;
+
+ download_template->icons[ICON_DOWNLOAD_STATUS].data.indirected_text.
+ text = dw->status;
+ download_template->icons[ICON_DOWNLOAD_STATUS].data.indirected_text.
+ size = sizeof dw->status;
+
+ sprintf(dw->sprite_name, "file_%.3x", dw->file_type);
+ if (!ro_gui_wimp_sprite_exists(dw->sprite_name))
+ strcpy(dw->sprite_name, "file_xxx");
+ download_template->icons[ICON_DOWNLOAD_ICON].data.indirected_sprite.id =
+ (osspriteop_id) dw->sprite_name;
+
+ /* Get a suitable path- and leafname for the download. */
+ temp_name = download_context_get_filename(dw->ctx);
+
+ if (temp_name == NULL)
+ temp_name = messages_get("SaveObject");
+
+ if (temp_name != NULL)
+ filename = strdup(temp_name);
+
+ if (filename == NULL) {
+ LOG("Failed to establish download filename.");
+ ro_warn_user("SaveError", error->errmess);
+ free(dw);
+ return 0;
+ }
+
+ for (i = 0, last_dot = (size_t) -1; filename[i] != '\0'; i++) {
+ const char c = filename[i];
+
+ if (c == '.') {
+ last_dot = i;
+ filename[i] = '/';
+ } else if (c <= ' ' || strchr(":*#$&@^%\\", c) != NULL)
+ filename[i] = '_';
+ }
+
+ if (nsoption_bool(strip_extensions) && last_dot != (size_t) -1)
+ filename[last_dot] = '\0';
+
+ if (download_dir != NULL && strlen(download_dir) > 0)
+ snprintf(dw->path, RO_DOWNLOAD_MAX_PATH_LEN, "%s.%s",
+ download_dir, filename);
+ else
+ snprintf(dw->path, RO_DOWNLOAD_MAX_PATH_LEN, "%s",
+ filename);
+
+ free(filename);
+
+ err = utf8_to_local_encoding(dw->path, 0, &local_path);
+ if (err != NSERROR_OK) {
+ /* badenc should never happen */
+ assert(err !=NSERROR_BAD_ENCODING);
+ LOG("utf8_to_local_encoding failed");
+ ro_warn_user("NoMemory", 0);
+ free(dw);
+ return 0;
+ }
+ else {
+ strncpy(dw->path, local_path, sizeof dw->path);
+ free(local_path);
+ }
+
+ download_template->icons[ICON_DOWNLOAD_PATH].data.indirected_text.text =
+ dw->path;
+ download_template->icons[ICON_DOWNLOAD_PATH].data.indirected_text.size =
+ sizeof dw->path;
+
+ download_template->icons[ICON_DOWNLOAD_DESTINATION].data.
+ indirected_text.text = dw->path;
+ download_template->icons[ICON_DOWNLOAD_DESTINATION].data.
+ indirected_text.size = sizeof dw->path;
+
+ download_template->icons[ICON_DOWNLOAD_DESTINATION].flags |=
+ wimp_ICON_DELETED;
+
+ /* create and open the download window */
+ error = xwimp_create_window(download_template, &dw->window);
+ if (error) {
+ LOG("xwimp_create_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ free(dw);
+ return 0;
+ }
+
+ dw->prev = 0;
+ dw->next = download_window_list;
+ if (download_window_list)
+ download_window_list->prev = dw;
+ download_window_list = dw;
+
+ ro_gui_download_update_status(dw);
+
+ ro_gui_dialog_open(dw->window);
+
+ ro_gui_wimp_event_set_user_data(dw->window, dw);
+ ro_gui_wimp_event_register_mouse_click(dw->window, ro_gui_download_click);
+ ro_gui_wimp_event_register_keypress(dw->window, ro_gui_download_keypress);
+ ro_gui_wimp_event_register_close_window(dw->window, ro_gui_download_close);
+
+ /* issue the warning now, so that it appears in front of the download
+ * window! */
+ if (space_warning)
+ ro_warn_user("DownloadWarn", messages_get("NoDiscSpace"));
+
+ return dw;
+}
+
+/**
+ * Handle failed downloads.
+ *
+ * \param dw download window
+ * \param error_msg error message
+ */
+
+static void gui_download_window_error(struct gui_download_window *dw,
+ const char *error_msg)
+{
+ os_error *error;
+
+ if (dw->ctx != NULL)
+ download_context_destroy(dw->ctx);
+ dw->ctx = NULL;
+ dw->error = true;
+
+ riscos_schedule(-1, ro_gui_download_update_status_wrapper, dw);
+
+ /* place error message in status icon in red */
+ strncpy(dw->status, error_msg, sizeof dw->status);
+ error = xwimp_set_icon_state(dw->window,
+ ICON_DOWNLOAD_STATUS,
+ wimp_COLOUR_RED << wimp_ICON_FG_COLOUR_SHIFT,
+ wimp_ICON_FG_COLOUR);
+ if (error) {
+ LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ /* grey out pathname icon */
+ error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_PATH,
+ wimp_ICON_SHADED, 0);
+ if (error) {
+ LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ /* grey out file icon */
+ error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_ICON,
+ wimp_ICON_SHADED, wimp_ICON_SHADED);
+ if (error) {
+ LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ ro_gui_download_window_hide_caret(dw);
+}
+
+/**
+ * Handle received download data.
+ *
+ * \param dw download window
+ * \param data pointer to block of data received
+ * \param size size of data
+ * \return NSERROR_OK on success, appropriate error otherwise
+ */
+
+static nserror gui_download_window_data(struct gui_download_window *dw,
+ const char *data, unsigned int size)
+{
+ while (true) {
+ const char *msg;
+ int unwritten;
+ os_error *error;
+
+ error = xosgbpb_writew(dw->file, (const byte *) data, size,
+ &unwritten);
+ if (error) {
+ LOG("xosgbpb_writew: 0x%x: %s", error->errnum, error->errmess);
+ msg = error->errmess;
+
+ } else if (unwritten) {
+ LOG("xosgbpb_writew: unwritten %i", unwritten);
+ msg = messages_get("Unwritten");
+ }
+ else {
+ dw->received += size;
+ return NSERROR_OK;
+ }
+
+ ro_warn_user("SaveError", msg);
+
+ if (dw->saved) {
+ /* try to continue with the temporary file */
+ const char *temp_name = ro_gui_download_temp_name(dw);
+
+ error = ro_gui_download_move(dw, temp_name, dw->path);
+ if (!error) {
+
+ /* re-allow saving */
+ dw->saved = false;
+
+ error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_ICON,
+ wimp_ICON_SHADED, 0);
+ if (error) {
+ LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_DESTINATION,
+ wimp_ICON_DELETED, wimp_ICON_DELETED);
+ if (error) {
+ LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ error = xwimp_set_icon_state(dw->window,
+ ICON_DOWNLOAD_PATH, wimp_ICON_DELETED, 0);
+ if (error) {
+ LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ continue;
+ }
+ }
+
+ /* give up then */
+ assert(dw->ctx);
+ download_context_abort(dw->ctx);
+ gui_download_window_error(dw, msg);
+
+ return NSERROR_SAVE_FAILED;
+ }
+}
+
+
+/**
+ * Update the status text and progress bar.
+ *
+ * \param dw download window
+ */
+
+void ro_gui_download_update_status(struct gui_download_window *dw)
+{
+ char *total_size;
+ char *speed;
+ char time[20] = "?";
+ struct timeval t;
+ float dt;
+ unsigned int left;
+ float rate;
+ os_error *error;
+ int width;
+ char *local_status;
+ nserror err;
+
+ gettimeofday(&t, 0);
+ dt = (t.tv_sec + 0.000001 * t.tv_usec) - (dw->last_time.tv_sec +
+ 0.000001 * dw->last_time.tv_usec);
+ if (dt == 0)
+ dt = 0.001;
+
+ total_size = human_friendly_bytesize(max(dw->received, dw->total_size));
+
+ if (dw->ctx) {
+ char *received;
+ rate = (dw->received - dw->last_received) / dt;
+ received = human_friendly_bytesize(dw->received);
+ /* A simple 'modified moving average' download rate calculation
+ * to smooth out rate fluctuations: chosen for simplicity.
+ */
+ dw->average_points++;
+ dw->average_rate =
+ ((dw->average_points - 1) *
+ dw->average_rate + rate) /
+ dw->average_points;
+ speed = human_friendly_bytesize(dw->average_rate);
+ if (dw->total_size) {
+ float f;
+
+ if (dw->average_rate > 0) {
+ left = (dw->total_size - dw->received) /
+ dw->average_rate;
+ sprintf(time, "%u:%.2u", left / 60, left % 60);
+ }
+
+ /* convert to local encoding */
+ err = utf8_to_local_encoding(
+ messages_get("Download"), 0, &local_status);
+ if (err != NSERROR_OK) {
+ /* badenc should never happen */
+ assert(err != NSERROR_BAD_ENCODING);
+ /* hide nomem error */
+ snprintf(dw->status, sizeof dw->status,
+ messages_get("Download"),
+ received, total_size, speed, time);
+ }
+ else {
+ snprintf(dw->status, sizeof dw->status,
+ local_status,
+ received, total_size, speed, time);
+ free(local_status);
+ }
+
+ f = (float) dw->received / (float) dw->total_size;
+ width = download_progress_width * f;
+ } else {
+ left = t.tv_sec - dw->start_time.tv_sec;
+ sprintf(time, "%u:%.2u", left / 60, left % 60);
+
+ err = utf8_to_local_encoding(
+ messages_get("DownloadU"), 0, &local_status);
+ if (err != NSERROR_OK) {
+ /* badenc should never happen */
+ assert(err != NSERROR_BAD_ENCODING);
+ /* hide nomem error */
+ snprintf(dw->status, sizeof dw->status,
+ messages_get("DownloadU"),
+ received, speed, time);
+ }
+ else {
+ snprintf(dw->status, sizeof dw->status,
+ local_status,
+ received, speed, time);
+ free(local_status);
+ }
+
+ /* length unknown, stay at 0 til finished */
+ width = 0;
+ }
+ } else {
+ left = dw->last_time.tv_sec - dw->start_time.tv_sec;
+ if (left == 0)
+ left = 1;
+ rate = (float) dw->received / (float) left;
+ sprintf(time, "%u:%.2u", left / 60, left % 60);
+ speed = human_friendly_bytesize(rate);
+
+ err = utf8_to_local_encoding(messages_get("Downloaded"), 0,
+ &local_status);
+ if (err != NSERROR_OK) {
+ /* badenc should never happen */
+ assert(err != NSERROR_BAD_ENCODING);
+ /* hide nomem error */
+ snprintf(dw->status, sizeof dw->status,
+ messages_get("Downloaded"),
+ total_size, speed, time);
+ }
+ else {
+ snprintf(dw->status, sizeof dw->status, local_status,
+ total_size, speed, time);
+ free(local_status);
+ }
+
+ /* all done */
+ width = download_progress_width;
+ }
+
+ dw->last_time = t;
+ dw->last_received = dw->received;
+
+ error = xwimp_resize_icon(dw->window, ICON_DOWNLOAD_PROGRESS,
+ download_progress_x0,
+ download_progress_y0,
+ download_progress_x0 + width,
+ download_progress_y1);
+ if (error) {
+ LOG("xwimp_resize_icon: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_STATUS, 0, 0);
+ if (error) {
+ LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ if (dw->ctx) {
+ riscos_schedule(1000, ro_gui_download_update_status_wrapper, dw);
+ } else {
+ riscos_schedule(-1, ro_gui_download_update_status_wrapper, dw);
+ }
+}
+
+
+/**
+ * Wrapper for ro_gui_download_update_status(), suitable for riscos_schedule().
+ */
+
+void ro_gui_download_update_status_wrapper(void *p)
+{
+ ro_gui_download_update_status((struct gui_download_window *) p);
+}
+
+
+
+/**
+ * Hide the caret but preserve input focus.
+ *
+ * \param dw download window
+ */
+
+void ro_gui_download_window_hide_caret(struct gui_download_window *dw)
+{
+ wimp_caret caret;
+ os_error *error;
+
+ error = xwimp_get_caret_position(&caret);
+ if (error) {
+ LOG("xwimp_get_caret_position: 0x%x : %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ else if (caret.w == dw->window) {
+ error = xwimp_set_caret_position(dw->window, (wimp_i)-1, 0, 0, 1 << 25, -1);
+ if (error) {
+ LOG("xwimp_get_caret_position: 0x%x : %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ }
+}
+
+
+
+
+/**
+ * Handle completed downloads.
+ *
+ * \param dw download window
+ */
+
+static void gui_download_window_done(struct gui_download_window *dw)
+{
+ os_error *error;
+
+ if (dw->ctx != NULL)
+ download_context_destroy(dw->ctx);
+ dw->ctx = NULL;
+ ro_gui_download_update_status(dw);
+
+ error = xosfind_closew(dw->file);
+ if (error) {
+ LOG("xosfind_closew: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ }
+ dw->file = 0;
+
+ if (dw->saved) {
+ error = xosfile_set_type(dw->path,
+ dw->file_type);
+ if (error) {
+ LOG("xosfile_set_type: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ }
+
+ if (dw->send_dataload) {
+ ro_gui_download_send_dataload(dw);
+ }
+
+ riscos_schedule(2000, ro_gui_download_window_destroy_wrapper, dw);
+ }
+}
+
+
+/**
+ * Handle Mouse_Click events in a download window.
+ *
+ * \param pointer block returned by Wimp_Poll
+ */
+
+bool ro_gui_download_click(wimp_pointer *pointer)
+{
+ struct gui_download_window *dw;
+
+ dw = (struct gui_download_window *)ro_gui_wimp_event_get_user_data(pointer->w);
+ if ((pointer->buttons & (wimp_DRAG_SELECT | wimp_DRAG_ADJUST)) &&
+ pointer->i == ICON_DOWNLOAD_ICON &&
+ !dw->error && !dw->saved) {
+ const char *sprite = ro_gui_get_icon_string(pointer->w, pointer->i);
+ int x = pointer->pos.x, y = pointer->pos.y;
+ wimp_window_state wstate;
+ wimp_icon_state istate;
+ /* start the drag from the icon's exact location, rather than the pointer */
+ istate.w = wstate.w = pointer->w;
+ istate.i = pointer->i;
+ if (!xwimp_get_window_state(&wstate) && !xwimp_get_icon_state(&istate)) {
+ x = (istate.icon.extent.x1 + istate.icon.extent.x0)/2 +
+ wstate.visible.x0 - wstate.xscroll;
+ y = (istate.icon.extent.y1 + istate.icon.extent.y0)/2 +
+ wstate.visible.y1 - wstate.yscroll;
+ }
+ ro_mouse_drag_start(ro_gui_download_drag_end, NULL, NULL, NULL);
+ download_window_current = dw;
+ ro_gui_drag_icon(x, y, sprite);
+
+ } else if (pointer->i == ICON_DOWNLOAD_DESTINATION) {
+ char command[256] = "Filer_OpenDir ";
+ char *dot;
+
+ strncpy(command + 14, dw->path, 242);
+ command[255] = 0;
+ dot = strrchr(command, '.');
+ if (dot) {
+ os_error *error;
+ *dot = 0;
+ error = xos_cli(command);
+ if (error) {
+ LOG("xos_cli: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ }
+ }
+ }
+ return true;
+}
+
+
+/**
+ * Handler Key_Press events in a download window.
+ *
+ * \param key key press returned by Wimp_Poll
+ * \return true iff key press handled
+ */
+
+bool ro_gui_download_keypress(wimp_key *key)
+{
+ struct gui_download_window *dw;
+
+ dw = (struct gui_download_window *)ro_gui_wimp_event_get_user_data(key->w);
+ switch (key->c)
+ {
+ case wimp_KEY_ESCAPE:
+ ro_gui_download_window_destroy(dw, false);
+ return true;
+
+ case wimp_KEY_RETURN: {
+ const char *name = ro_gui_get_icon_string(dw->window,
+ ICON_DOWNLOAD_PATH);
+ if (!strrchr(name, '.')) {
+ ro_warn_user("NoPathError", NULL);
+ return true;
+ }
+ ro_gui_convert_save_path(dw->path, sizeof dw->path, name);
+
+ dw->send_dataload = false;
+ if (ro_gui_download_save(dw, dw->path,
+ !nsoption_bool(confirm_overwrite)) && !dw->ctx)
+ {
+ /* finished already */
+ riscos_schedule(2000, ro_gui_download_window_destroy_wrapper, dw);
+ }
+ return true;
+ }
+ break;
+ }
+
+ /* ignore all other keypresses (F12 etc) */
+ return false;
+}
+
+
+/**
+ * Handle User_Drag_Box event for a drag from a download window.
+ *
+ * \param *drag block returned by Wimp_Poll
+ * \param *data NULL data to allow use as callback from ro_mouse.
+ */
+
+static void ro_gui_download_drag_end(wimp_dragged *drag, void *data)
+{
+ wimp_pointer pointer;
+ wimp_message message;
+ struct gui_download_window *dw = download_window_current;
+ const char *leaf;
+ os_error *error;
+
+ if (dw->saved || dw->error)
+ return;
+
+ error = xwimp_get_pointer_info(&pointer);
+ if (error) {
+ LOG("xwimp_get_pointer_info: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* ignore drags to the download window itself */
+ if (pointer.w == dw->window) return;
+
+ leaf = strrchr(dw->path, '.');
+ if (leaf)
+ leaf++;
+ else
+ leaf = dw->path;
+ ro_gui_convert_save_path(message.data.data_xfer.file_name, 212, leaf);
+
+ message.your_ref = 0;
+ message.action = message_DATA_SAVE;
+ message.data.data_xfer.w = pointer.w;
+ message.data.data_xfer.i = pointer.i;
+ message.data.data_xfer.pos.x = pointer.pos.x;
+ message.data.data_xfer.pos.y = pointer.pos.y;
+ message.data.data_xfer.est_size = dw->total_size ? dw->total_size :
+ dw->received;
+ message.data.data_xfer.file_type = dw->file_type;
+ message.size = 44 + ((strlen(message.data.data_xfer.file_name) + 4) &
+ (~3u));
+
+ error = xwimp_send_message_to_window(wimp_USER_MESSAGE, &message,
+ pointer.w, pointer.i, 0);
+ if (error) {
+ LOG("xwimp_send_message_to_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ gui_current_drag_type = GUI_DRAG_DOWNLOAD_SAVE;
+}
+
+
+/**
+ * Handle Message_DataSaveAck for a drag from a download window.
+ *
+ * \param message block returned by Wimp_Poll
+ */
+
+void ro_gui_download_datasave_ack(wimp_message *message)
+{
+ struct gui_download_window *dw = download_window_current;
+
+ dw->send_dataload = true;
+ memcpy(&dw->save_message, message, sizeof(wimp_message));
+
+ if (!ro_gui_download_save(dw, message->data.data_xfer.file_name,
+ !nsoption_bool(confirm_overwrite)))
+ return;
+
+ if (!dw->ctx) {
+ /* Ack successful completed save with message_DATA_LOAD immediately
+ to reduce the chance of the target app getting confused by it
+ being delayed */
+
+ ro_gui_download_send_dataload(dw);
+
+ riscos_schedule(2000, ro_gui_download_window_destroy_wrapper, dw);
+ }
+}
+
+
+/**
+ * Return a pathname in canonical form
+ *
+ * \param path pathnamee to be canonicalised
+ * \return ptr to pathname in malloc block, or NULL
+ */
+
+char *ro_gui_download_canonicalise(const char *path)
+{
+ os_error *error;
+ int spare = 0;
+ char *buf;
+
+ error = xosfscontrol_canonicalise_path(path, NULL, NULL, NULL, 0, &spare);
+ if (error) {
+ LOG("xosfscontrol_canonicalise_path: 0x%x: %s", error->errnum, error->errmess);
+ return NULL;
+ }
+
+ buf = malloc(1 - spare);
+ if (buf) {
+ error = xosfscontrol_canonicalise_path(path, buf, NULL, NULL,
+ 1 - spare, NULL);
+ if (error) {
+ LOG("xosfscontrol_canonicalise_path: 0x%x: %s", error->errnum, error->errmess);
+
+ free(buf);
+ return NULL;
+ }
+ }
+
+ return buf;
+}
+
+
+/**
+ * Check the available space on the medium containing the destination file,
+ * taking into account any space currently occupied by the file at its
+ * original location.
+ *
+ * \param dw download window
+ * \param dest_file destination pathname
+ * \param orig_file current pathname, NULL if no existing file
+ * \return true iff there's enough space
+ */
+
+bool ro_gui_download_check_space(struct gui_download_window *dw,
+ const char *dest_file, const char *orig_file)
+{
+ /* is there enough free space for this file? */
+ int dest_len = strlen(dest_file);
+ os_error *error;
+ int max_file;
+ bits free_lo;
+ int free_hi;
+ char *dir;
+
+ dir = malloc(dest_len + 1);
+ if (!dir) return true;
+
+ while (dest_len > 0 && dest_file[--dest_len] != '.');
+
+ memcpy(dir, dest_file, dest_len);
+ dir[dest_len] = '\0';
+
+ /* try the 64-bit variant first (RO 3.6+) */
+ error = xosfscontrol_free_space64(dir, &free_lo, &free_hi,
+ &max_file, NULL, NULL);
+ if (error) {
+ LOG("xosfscontrol_free_space64: 0x%x: %s", error->errnum, error->errmess);
+
+ free_hi = 0;
+ error = xosfscontrol_free_space(dir, (int*)&free_lo,
+ &max_file, NULL);
+ if (error) {
+ LOG("xosfscontrol_free_space: 0x%x: %s", error->errnum, error->errmess);
+ /* close our eyes and hope */
+ free(dir);
+ return true;
+ }
+ }
+
+ free(dir);
+
+ if ((bits)max_file < dw->total_size || (!free_hi && free_lo < dw->total_size)) {
+ char *dest_canon, *orig_canon;
+ bits space;
+
+ if (!orig_file || !dw->file) {
+ /* no original file to take into account */
+ return false;
+ }
+
+ space = min((bits)max_file, free_lo);
+
+ dest_canon = ro_gui_download_canonicalise(dest_file);
+ if (!dest_canon) dest_canon = (char*)dest_file;
+
+ orig_canon = ro_gui_download_canonicalise(orig_file);
+ if (!orig_canon) orig_canon = (char*)orig_file;
+
+ /* not enough space; allow for the file's original location
+ when space is tight by comparing the first part of the two
+ pathnames (and assuming the FS isn't brain damaged!) */
+
+ char *dot = strchr(orig_canon, '.');
+ if (dot && !strncasecmp(dest_canon, orig_canon, (dot + 1) - orig_canon)) {
+ int allocation;
+
+ error = xosargs_read_allocation(dw->file,
+ &allocation);
+ if (error) {
+ LOG("xosargs_read_allocation: 0x%x : %s", error->errnum, error->errmess);
+ }
+ else {
+ space += allocation;
+ }
+ }
+
+ if (dest_canon != dest_file) free(dest_canon);
+ if (orig_canon != orig_file) free(orig_canon);
+
+ if (space >= dw->total_size) {
+ /* OK, renaming should work */
+ return true;
+ }
+
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Move the downloading file to a new location and continue downloading there.
+ *
+ * \param dw download window
+ * \param dest_file new location
+ * \param src_file old location
+ * \return error iff failed to move file
+ */
+
+os_error *ro_gui_download_move(struct gui_download_window *dw,
+ const char *dest_file, const char *src_file)
+{
+ os_error *error;
+
+ /* close temporary file */
+ if (dw->file) {
+ error = xosfind_closew(dw->file);
+ dw->file = 0;
+ if (error) {
+ LOG("xosfind_closew: 0x%x: %s", error->errnum, error->errmess);
+ return error;
+ }
+ }
+
+ /* move or copy temporary file to destination file */
+ error = xosfscontrol_rename(src_file, dest_file);
+ /* Errors from a filing system have number 0x1XXnn, where XX is the FS
+ * number, and nn the error number. 0x9F is "Not same disc". */
+ if (error && (error->errnum == error_BAD_RENAME ||
+ (error->errnum & 0xFF00FFu) == 0x1009Fu)) {
+ /* rename failed: copy with delete */
+ error = xosfscontrol_copy(src_file, dest_file,
+ osfscontrol_COPY_FORCE |
+ osfscontrol_COPY_DELETE |
+ osfscontrol_COPY_LOOK,
+ 0, 0, 0, 0, 0);
+ if (error) {
+ LOG("xosfscontrol_copy: 0x%x: %s", error->errnum, error->errmess);
+ return error;
+ }
+ } else if (error) {
+ LOG("xosfscontrol_rename: 0x%x: %s", error->errnum, error->errmess);
+ return error;
+ }
+
+ if (dw->ctx) {
+ /* open new destination file if still fetching */
+ error = xosfile_write(dest_file, 0xdeaddead, 0xdeaddead,
+ fileswitch_ATTR_OWNER_READ |
+ fileswitch_ATTR_OWNER_WRITE);
+ if (error) {
+ LOG("xosfile_write: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ }
+
+ error = xosfind_openupw(osfind_NO_PATH | osfind_ERROR_IF_DIR,
+ dest_file, 0, &dw->file);
+ if (error) {
+ LOG("xosfind_openupw: 0x%x: %s", error->errnum, error->errmess);
+ return error;
+ }
+
+ error = xosargs_set_ptrw(dw->file, dw->received);
+ if (error) {
+ LOG("xosargs_set_ptrw: 0x%x: %s", error->errnum, error->errmess);
+ return error;
+ }
+
+ } else {
+ /* otherwise just set the file type */
+ error = xosfile_set_type(dest_file,
+ dw->file_type);
+ if (error) {
+ LOG("xosfile_set_type: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ }
+ }
+
+ /* success */
+ return NULL;
+}
+
+
+/**
+ * Remember the directory containing the given file,
+ * for use in further downloads.
+ *
+ * \param path pathname of downloaded file
+ * \return none
+ */
+
+void ro_gui_download_remember_dir(const char *path)
+{
+ const char *lastdot = NULL;
+ const char *p = path;
+
+ while (*p >= 0x20) {
+ if (*p == '.') {
+ /* don't remember the directory if it's a temporary file */
+ if (!lastdot && p == path + 12 &&
+ !memcmp(path, "<Wimp$Scrap>", 12)) break;
+ lastdot = p;
+ }
+ p++;
+ }
+
+ if (lastdot) {
+ /* remember the directory */
+ char *new_dir = realloc(download_dir, (lastdot+1)-path);
+ if (new_dir) {
+ download_dir_len = lastdot - path;
+ memcpy(new_dir, path, download_dir_len);
+ new_dir[download_dir_len] = '\0';
+ download_dir = new_dir;
+ }
+ }
+}
+
+/**
+ * Start of save operation, user has specified where the file should be saved.
+ *
+ * \param dw download window
+ * \param file_name pathname of destination file
+ & \param force_overwrite true iff required to overwrite without prompting
+ * \return true iff save successfully initiated
+ */
+
+bool ro_gui_download_save(struct gui_download_window *dw,
+ const char *file_name, bool force_overwrite)
+{
+ fileswitch_object_type obj_type;
+ const char *temp_name;
+ os_error *error;
+
+ if (dw->saved || dw->error)
+ return true;
+
+ temp_name = ro_gui_download_temp_name(dw);
+
+ /* does the user want to check for collisions when saving? */
+ if (!force_overwrite) {
+ /* check whether the destination file/dir already exists */
+ error = xosfile_read_stamped(file_name, &obj_type,
+ NULL, NULL, NULL, NULL, NULL);
+ if (error) {
+ LOG("xosfile_read_stamped: 0x%x:%s", error->errnum, error->errmess);
+ return false;
+ }
+
+ switch (obj_type) {
+ case osfile_NOT_FOUND:
+ break;
+
+ case osfile_IS_FILE:
+ dw->query = query_user("OverwriteFile", NULL, &overwrite_funcs, dw,
+ messages_get("Replace"), messages_get("DontReplace"));
+ dw->query_rsn = QueryRsn_Overwrite;
+ return false;
+
+ default:
+ error = xosfile_make_error(file_name, obj_type);
+ assert(error);
+ ro_warn_user("SaveError", error->errmess);
+ return false;
+ }
+ }
+
+ if (!ro_gui_download_check_space(dw, file_name, temp_name)) {
+ ro_warn_user("SaveError", messages_get("NoDiscSpace"));
+ return false;
+ }
+
+ error = ro_gui_download_move(dw, file_name, temp_name);
+ if (error) {
+ ro_warn_user("SaveError", error->errmess);
+
+ /* try to reopen at old location so that the download can continue
+ to the temporary file */
+ error = xosfind_openupw(osfind_NO_PATH | osfind_ERROR_IF_DIR,
+ temp_name, 0, &dw->file);
+ if (error) {
+ LOG("xosfind_openupw: 0x%x: %s", error->errnum, error->errmess);
+
+ } else {
+ error = xosargs_set_ptrw(dw->file, dw->received);
+ if (error) {
+ LOG("xosargs_set_ptrw: 0x%x: %s", error->errnum, error->errmess);
+ }
+ }
+
+ if (error) {
+ if (dw->ctx)
+ download_context_abort(dw->ctx);
+ gui_download_window_error(dw, error->errmess);
+ }
+ return false;
+ }
+
+ dw->saved = true;
+ strncpy(dw->path, file_name, sizeof dw->path);
+
+ if (!dw->send_dataload || dw->save_message.data.data_xfer.est_size != -1)
+ ro_gui_download_remember_dir(file_name);
+
+ /* grey out file icon */
+ error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_ICON,
+ wimp_ICON_SHADED, wimp_ICON_SHADED);
+ if (error) {
+ LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ /* hide writeable path icon and show destination icon
+ Note: must redraw icon bounding box because the destination icon
+ has rounded edges on RISC OS Select/Adjust and doesn't
+ completely cover the writeable icon */
+
+ ro_gui_force_redraw_icon(dw->window, ICON_DOWNLOAD_PATH);
+ error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_PATH,
+ wimp_ICON_DELETED, wimp_ICON_DELETED);
+ if (error) {
+ LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ error = xwimp_set_icon_state(dw->window,
+ ICON_DOWNLOAD_DESTINATION, wimp_ICON_DELETED, 0);
+ if (error) {
+ LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ ro_gui_download_window_hide_caret(dw);
+
+ return true;
+}
+
+
+/**
+ * Send DataLoad message in response to DataSaveAck, informing the
+ * target application that the transfer is complete.
+ *
+ * \param dw download window
+ */
+
+void ro_gui_download_send_dataload(struct gui_download_window *dw)
+{
+ /* Ack successful save with message_DATA_LOAD */
+ wimp_message *message = &dw->save_message;
+ os_error *error;
+
+ assert(dw->send_dataload);
+ dw->send_dataload = false;
+
+ message->action = message_DATA_LOAD;
+ message->your_ref = message->my_ref;
+ error = xwimp_send_message_to_window(wimp_USER_MESSAGE, message,
+ message->data.data_xfer.w,
+ message->data.data_xfer.i, 0);
+ /* The window we just attempted to send a message to may
+ * have been closed before the message was sent. As we've
+ * no clean way of detecting this, we'll just detect the
+ * error return from the message send attempt and judiciously
+ * ignore it.
+ *
+ * Ideally, we would have registered to receive Message_WindowClosed
+ * and then cleared dw->send_dataload flag for the appropriate
+ * window. Unfortunately, however, a long-standing bug in the
+ * Pinboard module prevents this from being a viable solution.
+ *
+ * See http://groups.google.co.uk/group/comp.sys.acorn.tech/msg/e3fbf70d8393e6cf?dmode=source&hl=en
+ * for the rather depressing details.
+ */
+ if (error && error->errnum != error_WIMP_BAD_HANDLE) {
+ LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ riscos_schedule(2000, ro_gui_download_window_destroy_wrapper, dw);
+}
+
+
+/**
+ * Handle closing of download window
+ */
+void ro_gui_download_close(wimp_w w)
+{
+ struct gui_download_window *dw;
+
+ dw = (struct gui_download_window *)ro_gui_wimp_event_get_user_data(w);
+ ro_gui_download_window_destroy(dw, false);
+}
+
+
+/**
+ * Close a download window and free any related resources.
+ *
+ * \param dw download window
+ * \param quit destroying because we're quitting the whole app
+ * \return true if window destroyed, not waiting for user confirmation
+ */
+
+bool ro_gui_download_window_destroy(struct gui_download_window *dw, bool quit)
+{
+ bool safe = dw->saved && !dw->ctx;
+ os_error *error;
+
+ if (!safe && !dw->close_confirmed)
+ {
+ query_reason rsn = quit ? QueryRsn_Quit : QueryRsn_Abort;
+
+ if (dw->query != QUERY_INVALID) {
+
+ /* can we just reuse the existing query? */
+ if (rsn == dw->query_rsn) {
+ ro_gui_query_window_bring_to_front(dw->query);
+ return false;
+ }
+
+ query_close(dw->query);
+ dw->query = QUERY_INVALID;
+ }
+
+ if (quit) {
+ /* bring all download windows to the front of the desktop as
+ a convenience if there are lots of windows open */
+
+ struct gui_download_window *d = download_window_list;
+ while (d) {
+ ro_gui_dialog_open_top(d->window, NULL, 0, 0);
+ d = d->next;
+ }
+ }
+
+ dw->query_rsn = rsn;
+ dw->query = query_user(quit ? "QuitDownload" : "AbortDownload",
+ NULL, &close_funcs, dw, NULL, NULL);
+
+ return false;
+ }
+
+ riscos_schedule(-1, ro_gui_download_update_status_wrapper, dw);
+ riscos_schedule(-1, ro_gui_download_window_destroy_wrapper, dw);
+
+ /* remove from list */
+ if (dw->prev)
+ dw->prev->next = dw->next;
+ else
+ download_window_list = dw->next;
+ if (dw->next)
+ dw->next->prev = dw->prev;
+
+ /* delete window */
+ error = xwimp_delete_window(dw->window);
+ if (error) {
+ LOG("xwimp_delete_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ ro_gui_wimp_event_finalise(dw->window);
+
+ /* close download file */
+ if (dw->file) {
+ error = xosfind_closew(dw->file);
+ if (error) {
+ LOG("xosfind_closew: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ }
+ }
+
+ /* delete temporary file */
+ if (!dw->saved) {
+ const char *temp_name = ro_gui_download_temp_name(dw);
+
+ error = xosfile_delete(temp_name, 0, 0, 0, 0, 0);
+ if (error) {
+ LOG("xosfile_delete: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ }
+ }
+
+ if (dw->ctx) {
+ download_context_abort(dw->ctx);
+ download_context_destroy(dw->ctx);
+ }
+
+ free(dw);
+
+ return true;
+}
+
+
+/**
+ * Wrapper for ro_gui_download_window_destroy(), suitable for riscos_schedule().
+ */
+
+void ro_gui_download_window_destroy_wrapper(void *p)
+{
+ struct gui_download_window *dw = p;
+ if (dw->query != QUERY_INVALID)
+ query_close(dw->query);
+ dw->query = QUERY_INVALID;
+ dw->close_confirmed = true;
+ ro_gui_download_window_destroy(dw, false);
+}
+
+
+/**
+ * User has opted to cancel the close, leaving the download to continue.
+ */
+
+void ro_gui_download_close_cancelled(query_id id, enum query_response res, void *p)
+{
+ struct gui_download_window *dw = p;
+ dw->query = QUERY_INVALID;
+}
+
+
+/**
+ * Download aborted, close window and tidy up.
+ */
+
+void ro_gui_download_close_confirmed(query_id id, enum query_response res, void *p)
+{
+ struct gui_download_window *dw = p;
+ dw->query = QUERY_INVALID;
+ dw->close_confirmed = true;
+ if (dw->query_rsn == QueryRsn_Quit) {
+
+ /* destroy all our downloads */
+ while (download_window_list)
+ ro_gui_download_window_destroy_wrapper(download_window_list);
+
+ /* and restart the shutdown */
+ if (ro_gui_prequit())
+ riscos_done = true;
+ }
+ else
+ ro_gui_download_window_destroy(dw, false);
+}
+
+
+/**
+ * User has opted not to overwrite the existing file.
+ */
+
+void ro_gui_download_overwrite_cancelled(query_id id, enum query_response res, void *p)
+{
+ struct gui_download_window *dw = p;
+ dw->query = QUERY_INVALID;
+}
+
+
+/**
+ * Overwrite of existing file confirmed, proceed with the save.
+ */
+
+void ro_gui_download_overwrite_confirmed(query_id id, enum query_response res, void *p)
+{
+ struct gui_download_window *dw = p;
+ dw->query = QUERY_INVALID;
+
+ if (!ro_gui_download_save(dw, dw->save_message.data.data_xfer.file_name, true))
+ return;
+
+ if (!dw->ctx) {
+ /* Ack successful completed save with message_DATA_LOAD immediately
+ to reduce the chance of the target app getting confused by it
+ being delayed */
+
+ ro_gui_download_send_dataload(dw);
+
+ riscos_schedule(2000, ro_gui_download_window_destroy_wrapper, dw);
+ }
+}
+
+
+/**
+ * Respond to PreQuit message, displaying a prompt message if we need
+ * the user to confirm the shutdown.
+ *
+ * \return true if we can shutdown straightaway
+ */
+
+bool ro_gui_download_prequit(void)
+{
+ while (download_window_list)
+ {
+ if (!ro_gui_download_window_destroy(download_window_list, true))
+ return false; /* awaiting user confirmation */
+ }
+ return true;
+}
+
+static struct gui_download_table download_table = {
+ .create = gui_download_window_create,
+ .data = gui_download_window_data,
+ .error = gui_download_window_error,
+ .done = gui_download_window_done,
+};
+
+struct gui_download_table *riscos_download_table = &download_table;
diff --git a/frontends/riscos/filetype.c b/frontends/riscos/filetype.c
new file mode 100644
index 000000000..99a44ae30
--- /dev/null
+++ b/frontends/riscos/filetype.c
@@ -0,0 +1,352 @@
+/*
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <string.h>
+#include <unixlib/local.h>
+#include "oslib/mimemap.h"
+#include "oslib/osfile.h"
+
+#include "utils/config.h"
+#include "utils/log.h"
+#include "content/content.h"
+#include "content/fetch.h"
+#include "content/hlcache.h"
+
+#include "riscos/filetype.h"
+#include "riscos/gui.h"
+
+/* type_map must be in sorted order by file_type */
+struct type_entry {
+ bits file_type;
+ char mime_type[40];
+};
+static const struct type_entry type_map[] = {
+ {0x132, "image/ico"},
+ {0x188, "application/x-shockwave-flash"},
+ {0x695, "image/gif"},
+ {0x69c, "image/x-ms-bmp"},
+ {0xaad, "image/svg+xml"},
+ {0xaff, "image/x-drawfile"},
+ {0xb60, "image/png"},
+ {0xc85, "image/jpeg"},
+ {0xd94, "image/x-artworks"},
+ {0xf78, "image/jng"},
+ {0xf79, "text/css"},
+ {0xf81, "application/javascript"},
+ {0xf83, "image/mng"},
+ {0xfaf, "text/html"},
+ {0xff9, "image/x-riscos-sprite"},
+ {0xfff, "text/plain"},
+};
+#define TYPE_MAP_COUNT (sizeof(type_map) / sizeof(type_map[0]))
+
+#define BUF_SIZE (256)
+static char type_buf[BUF_SIZE];
+
+
+static int cmp_type(const void *x, const void *y);
+
+/* exported interface documented in riscos/filetype.h */
+const char *fetch_filetype(const char *unix_path)
+{
+ struct type_entry *t;
+ unsigned int len = strlen(unix_path) + 100;
+ char *path = calloc(len, 1);
+ char *r;
+ os_error *error;
+ bits file_type, temp;
+ int objtype;
+
+ if (!path) {
+ LOG("Insufficient memory for calloc");
+ ro_warn_user("NoMemory", 0);
+ return "application/riscos";
+ }
+
+ /* convert path to RISC OS format and read file type */
+ r = __riscosify(unix_path, 0, __RISCOSIFY_NO_SUFFIX, path, len, 0);
+ if (r == 0) {
+ LOG("__riscosify failed");
+ free(path);
+ return "application/riscos";
+ }
+
+ error = xosfile_read_stamped_no_path(path, &objtype, 0, 0, 0, 0,
+ &file_type);
+ if (error) {
+ LOG("xosfile_read_stamped_no_path failed: %s", error->errmess);
+ free(path);
+ return "application/riscos";
+ }
+
+ if (objtype == osfile_IS_DIR) {
+ sprintf(type_buf, "application/x-netsurf-directory");
+ free(path);
+ return (const char *)type_buf;
+ }
+
+ /* If filetype is text or data, and the file has an extension, try to
+ * map the extension to a filetype via the MimeMap file. */
+ if (file_type == osfile_TYPE_TEXT || file_type == osfile_TYPE_DATA) {
+ char *slash = strrchr(path, '/');
+ if (slash) {
+ error = xmimemaptranslate_extension_to_filetype(
+ slash+1, &temp);
+ if (error)
+ /* ignore error and leave file_type alone */
+ LOG("xmimemaptranslate_extension_to_filetype: ""0x%x %s", error->errnum, error->errmess);
+ else
+ file_type = temp;
+ }
+ }
+
+ /* search for MIME type in our internal table */
+ t = bsearch(&file_type, type_map, TYPE_MAP_COUNT,
+ sizeof(type_map[0]), cmp_type);
+ if (t) {
+ /* found, so return it */
+ free(path);
+ return t->mime_type;
+ }
+
+ /* not in internal table, so ask MimeMap */
+ error = xmimemaptranslate_filetype_to_mime_type(file_type, type_buf);
+ if (error) {
+ LOG("0x%x %s", error->errnum, error->errmess);
+ free(path);
+ return "application/riscos";
+ }
+ /* make sure we're NULL terminated. If we're not, the MimeMap
+ * module's probably written past the end of the buffer from
+ * SVC mode. Short of rewriting MimeMap with an incompatible API,
+ * there's nothing we can do about it.
+ */
+ type_buf[BUF_SIZE - 1] = '\0';
+
+ free(path);
+
+ LOG("mime type '%s'", type_buf);
+ return (const char *)type_buf;
+
+}
+
+/* exported interface documented in riscos/filetype.h */
+char *fetch_mimetype(const char *ro_path)
+{
+ os_error *e;
+ bits filetype = 0, load;
+ int objtype;
+ char *mime = calloc(BUF_SIZE, sizeof(char));
+ char *slash;
+ struct type_entry *t;
+
+ if (!mime) {
+ LOG("Insufficient memory for calloc");
+ ro_warn_user("NoMemory", 0);
+ return 0;
+ }
+
+ e = xosfile_read_no_path(ro_path, &objtype, &load, 0, 0, 0);
+ if (e) {
+ LOG("xosfile_read_no_path: 0x%x: %s", e->errnum, e->errmess);
+ free(mime);
+ return 0;
+ }
+
+ if (objtype == osfile_IS_DIR) {
+ free(mime);
+ return 0; /* directories are pointless */
+ }
+
+ if ((load >> 20) & 0xFFF) {
+ filetype = (load>>8) & 0x000FFF;
+ }
+ else {
+ free(mime);
+ return 0; /* no idea */
+ }
+
+ /* If filetype is text and the file has an extension, try to map the
+ * extension to a filetype via the MimeMap file. */
+ slash = strrchr(ro_path, '/');
+ if (slash && filetype == osfile_TYPE_TEXT) {
+ e = xmimemaptranslate_extension_to_filetype(slash+1, &load);
+ if (e)
+ /* if we get an error here, simply ignore it and
+ * leave filetype unchanged */
+ LOG("0x%x %s", e->errnum, e->errmess);
+ else
+ filetype = load;
+ }
+
+ /* search for MIME type in our internal table */
+ t = bsearch(&filetype, type_map, TYPE_MAP_COUNT,
+ sizeof(type_map[0]), cmp_type);
+ if (t) {
+ /* found, so return it */
+ strncpy(mime, t->mime_type, BUF_SIZE);
+ return mime;
+ }
+
+ /* not in internal table, so ask MimeMap */
+ e = xmimemaptranslate_filetype_to_mime_type(filetype, mime);
+ if (e) {
+ LOG("xmimemaptranslate_filetype_to_mime_type: 0x%x: %s", e->errnum, e->errmess);
+ free(mime);
+ return 0;
+ }
+ /* make sure we're NULL terminated. If we're not, the MimeMap
+ * module's probably written past the end of the buffer from
+ * SVC mode. Short of rewriting MimeMap with an incompatible API,
+ * there's nothing we can do about it.
+ */
+ mime[BUF_SIZE - 1] = '\0';
+
+ return mime;
+}
+
+/**
+ * Comparison function for bsearch
+ */
+int cmp_type(const void *x, const void *y)
+{
+ const bits *p = x;
+ const struct type_entry *q = y;
+ return *p < q->file_type ? -1 : (*p == q->file_type ? 0 : +1);
+}
+
+/* exported interface documented in riscos/filetype.h */
+int ro_content_filetype(hlcache_handle *c)
+{
+ lwc_string *mime_type;
+ int file_type;
+
+ file_type = ro_content_filetype_from_type(content_get_type(c));
+ if (file_type != 0)
+ return file_type;
+
+ mime_type = content_get_mime_type(c);
+
+ file_type = ro_content_filetype_from_mime_type(mime_type);
+
+ lwc_string_unref(mime_type);
+
+ return file_type;
+}
+
+
+/* exported interface documented in riscos/filetype.h */
+int ro_content_native_type(hlcache_handle *c)
+{
+ switch (ro_content_filetype(c)) {
+ case FILETYPE_JPEG: /* jpeg */
+ case FILETYPE_JNG: /* jng */
+ case FILETYPE_MNG: /* mng */
+ case FILETYPE_GIF: /* gif */
+ case FILETYPE_BMP: /* bmp */
+ case FILETYPE_ICO: /* ico */
+ case FILETYPE_PNG: /* png */
+ case 0xff9: /* sprite */
+ return osfile_TYPE_SPRITE;
+ case FILETYPE_SVG: /* svg */
+ case 0xaff: /* draw */
+ return osfile_TYPE_DRAW;
+ default:
+ break;
+ }
+
+ return osfile_TYPE_DATA;
+}
+
+
+/* exported interface documented in riscos/filetype.h */
+int ro_content_filetype_from_mime_type(lwc_string *mime_type)
+{
+ int file_type, index;
+ os_error *error;
+
+ /* Search internal type map */
+ for (index = TYPE_MAP_COUNT; index > 0; index--) {
+ const struct type_entry *e = &type_map[index - 1];
+
+ if (strlen(e->mime_type) == lwc_string_length(mime_type) &&
+ strncasecmp(e->mime_type,
+ lwc_string_data(mime_type),
+ lwc_string_length(mime_type)) == 0)
+ return e->file_type;
+ }
+
+ /* Ask MimeMap module */
+ error = xmimemaptranslate_mime_type_to_filetype(
+ lwc_string_data(mime_type), (bits *) &file_type);
+ if (error)
+ file_type = 0xffd;
+
+ return file_type;
+}
+
+
+/* exported interface documented in riscos/filetype.h */
+int ro_content_filetype_from_type(content_type type) {
+ switch (type) {
+ case CONTENT_HTML: return FILETYPE_HTML;
+ case CONTENT_TEXTPLAIN: return 0xfff;
+ case CONTENT_CSS: return 0xf79;
+ default: break;
+ }
+ return 0;
+}
+
+
+/* exported interface documented in riscos/filetype.h */
+bits ro_filetype_from_unix_path(const char *unix_path)
+{
+ unsigned int len = strlen(unix_path) + 100;
+ char *path = calloc(len, 1);
+ char *r;
+ os_error *error;
+ bits file_type;
+
+ if (!path) {
+ LOG("Insufficient memory for calloc");
+ ro_warn_user("NoMemory", 0);
+ return osfile_TYPE_DATA;
+ }
+
+ /* convert path to RISC OS format and read file type */
+ r = __riscosify(unix_path, 0, __RISCOSIFY_NO_SUFFIX, path, len, 0);
+ if (r == 0) {
+ LOG("__riscosify failed");
+ free(path);
+ return osfile_TYPE_DATA;
+ }
+
+ error = xosfile_read_stamped_no_path(path, 0, 0, 0, 0, 0,
+ &file_type);
+ if (error) {
+ LOG("xosfile_read_stamped_no_path failed: %s", error->errmess);
+ free(path);
+ return osfile_TYPE_DATA;
+ }
+
+ free(path);
+
+ return file_type;
+}
+
diff --git a/frontends/riscos/filetype.h b/frontends/riscos/filetype.h
new file mode 100644
index 000000000..3ba613033
--- /dev/null
+++ b/frontends/riscos/filetype.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file riscos/filetype.h
+ * RISC OS filetpe interface.
+ */
+
+#ifndef _NETSURF_RISCOS_FILETYPE_H_
+#define _NETSURF_RISCOS_FILETYPE_H_
+
+#include "content/content_type.h"
+
+#ifndef FILETYPE_ACORN_URI
+#define FILETYPE_ACORN_URI 0xf91
+#endif
+#ifndef FILETYPE_ANT_URL
+#define FILETYPE_ANT_URL 0xb28
+#endif
+#ifndef FILETYPE_IEURL
+#define FILETYPE_IEURL 0x1ba
+#endif
+#ifndef FILETYPE_HTML
+#define FILETYPE_HTML 0xfaf
+#endif
+#ifndef FILETYPE_JNG
+#define FILETYPE_JNG 0xf78
+#endif
+#ifndef FILETYPE_CSS
+#define FILETYPE_CSS 0xf79
+#endif
+#ifndef FILETYPE_MNG
+#define FILETYPE_MNG 0xf83
+#endif
+#ifndef FILETYPE_GIF
+#define FILETYPE_GIF 0x695
+#endif
+#ifndef FILETYPE_BMP
+#define FILETYPE_BMP 0x69c
+#endif
+#ifndef FILETYPE_ICO
+#define FILETYPE_ICO 0x132
+#endif
+#ifndef FILETYPE_PNG
+#define FILETYPE_PNG 0xb60
+#endif
+#ifndef FILETYPE_JPEG
+#define FILETYPE_JPEG 0xc85
+#endif
+#ifndef FILETYPE_ARTWORKS
+#define FILETYPE_ARTWORKS 0xd94
+#endif
+#ifndef FILETYPE_SVG
+#define FILETYPE_SVG 0xaad
+#endif
+
+/**
+ * Determine the MIME type of a local file.
+ *
+ * \param unix_path Unix style path to file on disk
+ * \return Pointer to MIME type string (should not be freed) - invalidated
+ * on next call to fetch_filetype.
+ */
+const char *fetch_filetype(const char *unix_path);
+
+/**
+ * Find a MIME type for a local file
+ *
+ * \param ro_path RISC OS style path to file on disk
+ * \return MIME type string (on heap, caller should free), or NULL
+ */
+char *fetch_mimetype(const char *ro_path);
+
+/**
+ * Determine the RISC OS filetype for a content.
+ *
+ * \param h The handle of the content to examine.
+ * \return The RISC OS filetype corresponding to the content
+ */
+int ro_content_filetype(struct hlcache_handle *h);
+
+/**
+ * Determine the native RISC OS filetype to export a content as
+ *
+ * \param c The content to examine
+ * \return Native RISC OS filetype for export
+ */
+int ro_content_native_type(struct hlcache_handle *c);
+
+/**
+ * Determine the RISC OS filetype for a MIME type
+ *
+ * \param mime_type MIME type to consider
+ * \return Corresponding RISC OS filetype
+ */
+int ro_content_filetype_from_mime_type(lwc_string *mime_type);
+
+/**
+ * Determine the RISC OS filetype from a content type.
+ *
+ * \param type The content type to examine.
+ * \return The RISC OS filetype corresponding to the content, or 0 for unknown
+ */
+int ro_content_filetype_from_type(content_type type);
+
+/**
+ * Determine the type of a local file.
+ *
+ * \param unix_path Unix style path to file on disk
+ * \return File type
+ */
+bits ro_filetype_from_unix_path(const char *unix_path);
+
+#endif
diff --git a/frontends/riscos/font.c b/frontends/riscos/font.c
new file mode 100644
index 000000000..2f2ba9a35
--- /dev/null
+++ b/frontends/riscos/font.c
@@ -0,0 +1,598 @@
+/*
+ * Copyright 2006 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * RISC OS implementation of Font handling.
+ *
+ * The RUfl is used to handle and render fonts.
+ */
+
+#include "utils/config.h"
+
+#include <assert.h>
+#include <string.h>
+#include <oslib/wimp.h>
+#include <oslib/wimpreadsysinfo.h>
+
+#include "utils/nsoption.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "desktop/gui_layout.h"
+
+#include "riscos/gui.h"
+#include "riscos/font.h"
+
+
+/** desktop font, size and style being used */
+char ro_gui_desktop_font_family[80];
+int ro_gui_desktop_font_size = 12;
+rufl_style ro_gui_desktop_font_style = rufl_WEIGHT_400;
+bool no_font_blending = false;
+
+
+/**
+ * Check that at least Homerton.Medium is available.
+ */
+static void nsfont_check_fonts(void)
+{
+ char s[252];
+ font_f font;
+ os_error *error;
+
+ error = xfont_find_font("Homerton.Medium\\ELatin1",
+ 160, 160, 0, 0, &font, 0, 0);
+ if (error) {
+ if (error->errnum == error_FILE_NOT_FOUND) {
+ xwimp_start_task("TaskWindow -wimpslot 200K -quit "
+ "<NetSurf$Dir>.FixFonts", 0);
+ die("FontBadInst");
+ } else {
+ LOG("xfont_find_font: 0x%x: %s", error->errnum, error->errmess);
+ snprintf(s, sizeof s, messages_get("FontError"),
+ error->errmess);
+ die(s);
+ }
+ }
+
+ error = xfont_lose_font(font);
+ if (error) {
+ LOG("xfont_lose_font: 0x%x: %s", error->errnum, error->errmess);
+ snprintf(s, sizeof s, messages_get("FontError"),
+ error->errmess);
+ die(s);
+ }
+}
+
+
+/**
+ * Check that a font option is valid, and fix it if not.
+ *
+ * \param option pointer to option, as used by options.[ch]
+ * \param family font family to use if option is not set, or the set
+ * family is not available
+ * \param fallback font family to use if family is not available either
+ */
+static void nsfont_check_option(char **option, const char *family,
+ const char *fallback)
+{
+ if (*option && !nsfont_exists(*option)) {
+ free(*option);
+ *option = 0;
+ }
+ if (!*option) {
+ if (nsfont_exists(family))
+ *option = strdup(family);
+ else
+ *option = strdup(fallback);
+ }
+}
+
+
+/**
+ * Initialize font handling.
+ *
+ * Exits through die() on error.
+ */
+void nsfont_init(void)
+{
+ const char *fallback;
+ rufl_code code;
+
+ nsfont_check_fonts();
+
+ LOG("Initialise RUfl");
+ code = rufl_init();
+ if (code != rufl_OK) {
+ if (code == rufl_FONT_MANAGER_ERROR)
+ LOG("rufl_init: rufl_FONT_MANAGER_ERROR: 0x%x: %s", rufl_fm_error->errnum, rufl_fm_error->errmess);
+ else
+ LOG("rufl_init: 0x%x", code);
+ die("The Unicode font library could not be initialized. "
+ "Please report this to the developers.");
+ }
+ LOG("RUfl initialised");
+
+ if (rufl_family_list_entries == 0)
+ die("No fonts could be found. At least one font must be "
+ "installed.");
+
+ fallback = nsfont_fallback_font();
+
+ nsfont_check_option(&nsoption_charp(font_sans), "Homerton", fallback);
+ nsfont_check_option(&nsoption_charp(font_serif), "Trinity", fallback);
+ nsfont_check_option(&nsoption_charp(font_mono), "Corpus", fallback);
+ nsfont_check_option(&nsoption_charp(font_cursive), "Churchill", fallback);
+ nsfont_check_option(&nsoption_charp(font_fantasy), "Sassoon", fallback);
+
+ if (nsoption_int(font_default) != PLOT_FONT_FAMILY_SANS_SERIF &&
+ nsoption_int(font_default) != PLOT_FONT_FAMILY_SERIF &&
+ nsoption_int(font_default) != PLOT_FONT_FAMILY_MONOSPACE &&
+ nsoption_int(font_default) != PLOT_FONT_FAMILY_CURSIVE &&
+ nsoption_int(font_default) != PLOT_FONT_FAMILY_FANTASY) {
+ nsoption_set_int(font_default, PLOT_FONT_FAMILY_SANS_SERIF);
+ }
+}
+
+
+/**
+ * Retrieve the fallback font name
+ *
+ * \return Fallback font name
+ */
+const char *nsfont_fallback_font(void)
+{
+ const char *fallback = "Homerton";
+
+ if (!nsfont_exists(fallback)) {
+ LOG("Homerton not found, dumping RUfl family list");
+ for (unsigned int i = 0; i < rufl_family_list_entries; i++) {
+ LOG("'%s'", rufl_family_list[i]);
+ }
+ fallback = rufl_family_list[0];
+ }
+
+ return fallback;
+}
+
+
+/**
+ * bsearch comparison routine
+ */
+static int nsfont_list_cmp(const void *keyval, const void *datum)
+{
+ const char *key = keyval;
+ const char * const *entry = datum;
+ return strcasecmp(key, *entry);
+}
+
+
+/**
+ * Check if a font family is available.
+ *
+ * \param font_family name of font family
+ * \return true if the family is available
+ */
+bool nsfont_exists(const char *font_family)
+{
+ if (bsearch(font_family, rufl_family_list,
+ rufl_family_list_entries, sizeof rufl_family_list[0],
+ nsfont_list_cmp))
+ return true;
+ return false;
+}
+
+
+/**
+ * Measure the width of a string.
+ *
+ * \param fstyle plot style for this text
+ * \param string UTF-8 string to measure
+ * \param length length of string
+ * \param width updated to width of string[0..length)
+ * \return true on success, false on error and error reported
+ */
+static nserror
+ro_font_width(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int *width)
+{
+ const char *font_family;
+ unsigned int font_size;
+ rufl_style font_style;
+ rufl_code code;
+
+ nsfont_read_style(fstyle, &font_family, &font_size, &font_style);
+ if (font_size == 0) {
+ *width = 0;
+ return NSERROR_OK;
+ }
+
+ code = rufl_width(font_family, font_style, font_size,
+ string, length,
+ width);
+ if (code != rufl_OK) {
+ if (code == rufl_FONT_MANAGER_ERROR)
+ LOG("rufl_width: rufl_FONT_MANAGER_ERROR: 0x%x: %s", rufl_fm_error->errnum, rufl_fm_error->errmess);
+ else
+ LOG("rufl_width: 0x%x", code);
+/* ro_warn_user("MiscError", "font error"); */
+ *width = 0;
+ return NSERROR_INVALID;
+ }
+
+ *width /= 2;
+ return NSERROR_OK;
+}
+
+
+/**
+ * Find the position in a string where an x coordinate falls.
+ *
+ * \param fstyle style for this text
+ * \param string UTF-8 string to measure
+ * \param length length of string
+ * \param x x coordinate to search for
+ * \param char_offset updated to offset in string of actual_x, [0..length]
+ * \param actual_x updated to x coordinate of character closest to x
+ * \return true on success, false on error and error reported
+ */
+static nserror
+ro_font_position(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x)
+{
+ const char *font_family;
+ unsigned int font_size;
+ rufl_style font_style;
+ rufl_code code;
+
+ nsfont_read_style(fstyle, &font_family, &font_size, &font_style);
+ if (font_size == 0) {
+ *char_offset = 0;
+ *actual_x = 0;
+ return NSERROR_OK;
+ }
+
+ code = rufl_x_to_offset(font_family, font_style, font_size,
+ string, length,
+ x * 2, char_offset, actual_x);
+ if (code != rufl_OK) {
+ if (code == rufl_FONT_MANAGER_ERROR)
+ LOG("rufl_x_to_offset: rufl_FONT_MANAGER_ERROR: ""0x%x: %s", rufl_fm_error->errnum, rufl_fm_error->errmess);
+ else
+ LOG("rufl_x_to_offset: 0x%x", code);
+/* ro_warn_user("MiscError", "font error"); */
+ *char_offset = 0;
+ *actual_x = 0;
+ return NSERROR_INVALID;
+ }
+
+ *actual_x /= 2;
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Find where to split a string to make it fit a width.
+ *
+ * \param fstyle style for this text
+ * \param string UTF-8 string to measure
+ * \param length length of string, in bytes
+ * \param x width available
+ * \param char_offset updated to offset in string of actual_x, [1..length]
+ * \param actual_x updated to x coordinate of character closest to x
+ * \return true on success, false on error and error reported
+ *
+ * On exit, char_offset indicates first character after split point.
+ *
+ * Note: char_offset of 0 should never be returned.
+ *
+ * Returns:
+ * char_offset giving split point closest to x, where actual_x <= x
+ * else
+ * char_offset giving split point closest to x, where actual_x > x
+ *
+ * Returning char_offset == length means no split possible
+ */
+static nserror
+ro_font_split(const plot_font_style_t *fstyle,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x)
+{
+ const char *font_family;
+ unsigned int font_size;
+ rufl_style font_style;
+ rufl_code code;
+
+ nsfont_read_style(fstyle, &font_family, &font_size, &font_style);
+ if (font_size == 0) {
+ *char_offset = 0;
+ *actual_x = 0;
+ return NSERROR_OK;
+ }
+
+ code = rufl_split(font_family, font_style, font_size,
+ string, length,
+ x * 2, char_offset, actual_x);
+ if (code != rufl_OK) {
+ if (code == rufl_FONT_MANAGER_ERROR) {
+ LOG("rufl_split: rufl_FONT_MANAGER_ERROR: ""0x%x: %s",
+ rufl_fm_error->errnum, rufl_fm_error->errmess);
+ } else {
+ LOG("rufl_split: 0x%x", code);
+ }
+/* ro_warn_user("MiscError", "font error"); */
+ *char_offset = 0;
+ *actual_x = 0;
+ return NSERROR_INVALID;
+ }
+
+ if (*char_offset != length) {
+ /* we found something to split at */
+ size_t orig = *char_offset;
+
+ /* ensure a space at <= the split point we found */
+ while (*char_offset && string[*char_offset] != ' ') {
+ (*char_offset)--;
+ }
+
+ /* nothing valid found <= split point, advance to next space */
+ if (*char_offset == 0) {
+ *char_offset = orig;
+ while ((*char_offset != length) &&
+ (string[*char_offset] != ' ')) {
+ (*char_offset)++;
+ }
+ }
+ }
+
+ code = rufl_width(font_family, font_style, font_size,
+ string, *char_offset,
+ actual_x);
+ if (code != rufl_OK) {
+ if (code == rufl_FONT_MANAGER_ERROR) {
+ LOG("rufl_width: rufl_FONT_MANAGER_ERROR: 0x%x: %s",
+ rufl_fm_error->errnum, rufl_fm_error->errmess);
+ } else {
+ LOG("rufl_width: 0x%x", code);
+ }
+/* ro_warn_user("MiscError", "font error"); */
+ *char_offset = 0;
+ *actual_x = 0;
+ return NSERROR_INVALID;
+ }
+
+ *actual_x /= 2;
+ return NSERROR_OK;
+}
+
+
+/**
+ * Paint a string.
+ *
+ * \param fstyle plot style for this text
+ * \param string UTF-8 string to measure
+ * \param length length of string
+ * \param x x coordinate
+ * \param y y coordinate
+ * \return true on success, false on error and error reported
+ */
+bool nsfont_paint(const plot_font_style_t *fstyle, const char *string,
+ size_t length, int x, int y)
+{
+ const char *font_family;
+ unsigned int font_size;
+ unsigned int flags = rufl_BLEND_FONT;
+ rufl_style font_style;
+ rufl_code code;
+
+ nsfont_read_style(fstyle, &font_family, &font_size, &font_style);
+ if (font_size == 0)
+ return true;
+
+ if (no_font_blending || print_active)
+ flags = 0;
+
+ code = rufl_paint(font_family, font_style, font_size,
+ string, length, x, y, flags);
+ if (code != rufl_OK) {
+ if (code == rufl_FONT_MANAGER_ERROR) {
+ LOG("rufl_paint: rufl_FONT_MANAGER_ERROR: 0x%x: %s", rufl_fm_error->errnum, rufl_fm_error->errmess);
+ } else {
+ LOG("rufl_paint: 0x%x", code);
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Convert a font style to a font family, size and rufl_style.
+ *
+ * \param fstyle plot style for this text
+ * \param font_family updated to font family
+ * \param font_size updated to font size
+ * \param font_style updated to font style
+ */
+void nsfont_read_style(const plot_font_style_t *fstyle,
+ const char **font_family, unsigned int *font_size,
+ rufl_style *font_style)
+{
+ static const rufl_style weight_table[] = {
+ rufl_WEIGHT_100,
+ rufl_WEIGHT_200,
+ rufl_WEIGHT_300,
+ rufl_WEIGHT_400,
+ rufl_WEIGHT_500,
+ rufl_WEIGHT_600,
+ rufl_WEIGHT_700,
+ rufl_WEIGHT_800,
+ rufl_WEIGHT_900
+ };
+
+ *font_size = (fstyle->size * 16) / FONT_SIZE_SCALE;
+ if (1600 < *font_size)
+ *font_size = 1600;
+
+ switch (fstyle->family) {
+ case PLOT_FONT_FAMILY_SANS_SERIF:
+ *font_family = nsoption_charp(font_sans);
+ break;
+ case PLOT_FONT_FAMILY_SERIF:
+ *font_family = nsoption_charp(font_serif);
+ break;
+ case PLOT_FONT_FAMILY_MONOSPACE:
+ *font_family = nsoption_charp(font_mono);
+ break;
+ case PLOT_FONT_FAMILY_CURSIVE:
+ *font_family = nsoption_charp(font_cursive);
+ break;
+ case PLOT_FONT_FAMILY_FANTASY:
+ *font_family = nsoption_charp(font_fantasy);
+ break;
+ default:
+ *font_family = nsoption_charp(font_sans);
+ break;
+ }
+
+ if ((fstyle->flags & FONTF_ITALIC) || (fstyle->flags & FONTF_OBLIQUE)) {
+ *font_style = rufl_SLANTED;
+ } else {
+ *font_style = 0;
+ }
+
+ *font_style |= weight_table[(fstyle->weight / 100) - 1];
+}
+
+
+/**
+ * Looks up the current desktop font and converts that to a family name,
+ * font size and style flags suitable for passing directly to rufl
+ *
+ * \param family buffer to receive font family
+ * \param family_size buffer size
+ * \param psize receives the font size in 1/16 points
+ * \param pstyle receives the style settings to be passed to rufl
+ */
+static void
+ro_gui_wimp_desktop_font(char *family,
+ size_t family_size,
+ int *psize,
+ rufl_style *pstyle)
+{
+ rufl_style style = rufl_WEIGHT_400;
+ os_error *error;
+ int ptx, pty;
+ font_f font_handle;
+ int used;
+
+ assert(family);
+ assert(20 < family_size);
+ assert(psize);
+ assert(pstyle);
+
+ error = xwimpreadsysinfo_font(&font_handle, NULL);
+ if (error) {
+ LOG("xwimpreadsysinfo_font: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ goto failsafe;
+ }
+
+ if (font_handle == font_SYSTEM) {
+ /* Er, yeah; like that's ever gonna work with RUfl */
+ goto failsafe;
+ }
+
+ error = xfont_read_identifier(font_handle, NULL, &used);
+ if (error) {
+ LOG("xfont_read_identifier: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ goto failsafe;
+ }
+
+ if (family_size < (size_t) used + 1) {
+ LOG("desktop font name too long");
+ goto failsafe;
+ }
+
+ error = xfont_read_defn(font_handle, (byte *) family,
+ &ptx, &pty, NULL, NULL, NULL, NULL);
+ if (error) {
+ LOG("xfont_read_defn: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ goto failsafe;
+ }
+
+ for (size_t i = 0; i != (size_t) used; i++) {
+ if (family[i] < ' ') {
+ family[i] = 0;
+ break;
+ }
+ }
+
+ LOG("desktop font \"%s\"", family);
+
+ if (strcasestr(family, ".Medium"))
+ style = rufl_WEIGHT_500;
+ else if (strcasestr(family, ".Bold"))
+ style = rufl_WEIGHT_700;
+ if (strcasestr(family, ".Italic") || strcasestr(family, ".Oblique"))
+ style |= rufl_SLANTED;
+
+ char *dot = strchr(family, '.');
+ if (dot)
+ *dot = 0;
+
+ *psize = max(ptx, pty);
+ *pstyle = style;
+
+ LOG("family \"%s\", size %i, style %i", family, *psize, style);
+
+ return;
+
+failsafe:
+ strcpy(family, "Homerton");
+ *psize = 12*16;
+ *pstyle = rufl_WEIGHT_400;
+}
+
+
+/**
+ * Retrieve the current desktop font family, size and style from
+ * the WindowManager in a form suitable for passing to rufl
+ */
+void ro_gui_wimp_get_desktop_font(void)
+{
+ ro_gui_wimp_desktop_font(ro_gui_desktop_font_family,
+ sizeof(ro_gui_desktop_font_family),
+ &ro_gui_desktop_font_size,
+ &ro_gui_desktop_font_style);
+}
+
+
+static struct gui_layout_table layout_table = {
+ .width = ro_font_width,
+ .position = ro_font_position,
+ .split = ro_font_split,
+};
+
+struct gui_layout_table *riscos_layout_table = &layout_table;
diff --git a/frontends/riscos/font.h b/frontends/riscos/font.h
new file mode 100644
index 000000000..0319a7ee3
--- /dev/null
+++ b/frontends/riscos/font.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file riscos/font.h
+ * RISC OS font interface.
+ */
+
+#ifndef _NETSURF_RISCOS_FONT_H_
+#define _NETSURF_RISCOS_FONT_H_
+
+#include <rufl.h>
+
+struct gui_layout_table *riscos_layout_table;
+
+/** desktop font, size and style being used */
+extern char ro_gui_desktop_font_family[];
+extern int ro_gui_desktop_font_size;
+extern rufl_style ro_gui_desktop_font_style;
+
+void nsfont_init(void);
+bool nsfont_exists(const char *font_family);
+const char *nsfont_fallback_font(void);
+bool nsfont_paint(const plot_font_style_t *fstyle, const char *string,
+ size_t length, int x, int y);
+void nsfont_read_style(const plot_font_style_t *fstyle,
+ const char **font_family, unsigned int *font_size,
+ rufl_style *font_style);
+void ro_gui_wimp_get_desktop_font(void);
+
+#endif
diff --git a/frontends/riscos/global_history.c b/frontends/riscos/global_history.c
new file mode 100644
index 000000000..c469847e0
--- /dev/null
+++ b/frontends/riscos/global_history.c
@@ -0,0 +1,413 @@
+/*
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Global history (implementation).
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "oslib/wimp.h"
+#include "oslib/wimpspriteop.h"
+
+#include "content/urldb.h"
+#include "utils/nsoption.h"
+#include "utils/messages.h"
+#include "utils/log.h"
+#include "desktop/global_history.h"
+#include "desktop/tree.h"
+#include "desktop/gui_window.h"
+
+#include "riscos/dialog.h"
+#include "riscos/global_history.h"
+#include "riscos/gui.h"
+#include "riscos/menus.h"
+#include "riscos/save.h"
+#include "riscos/toolbar.h"
+#include "riscos/treeview.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+
+static void ro_gui_global_history_toolbar_update_buttons(void);
+static void ro_gui_global_history_toolbar_save_buttons(char *config);
+static bool ro_gui_global_history_menu_prepare(wimp_w w, wimp_i i,
+ wimp_menu *menu, wimp_pointer *pointer);
+static void ro_gui_global_history_menu_warning(wimp_w w, wimp_i i,
+ wimp_menu *menu, wimp_selection *selection, menu_action action);
+static bool ro_gui_global_history_menu_select(wimp_w w, wimp_i i,
+ wimp_menu *menu, wimp_selection *selection, menu_action action);
+static void ro_gui_global_history_toolbar_click(button_bar_action action);
+
+struct ro_treeview_callbacks ro_global_history_treeview_callbacks = {
+ ro_gui_global_history_toolbar_click,
+ ro_gui_global_history_toolbar_update_buttons,
+ ro_gui_global_history_toolbar_save_buttons
+};
+
+/* The RISC OS global history window, toolbar and treeview data */
+
+static struct ro_global_history_window {
+ wimp_w window;
+ struct toolbar *toolbar;
+ ro_treeview *tv;
+ wimp_menu *menu;
+} global_history_window;
+
+/**
+ * Pre-Initialise the global history tree. This is called for things that
+ * need to be done at the gui_init() stage, such as loading templates.
+ */
+
+void ro_gui_global_history_preinitialise(void)
+{
+ /* Create our window. */
+
+ global_history_window.window = ro_gui_dialog_create("tree");
+ ro_gui_set_window_title(global_history_window.window,
+ messages_get("GlobalHistory"));
+}
+
+/**
+ * Initialise global history tree, at the gui_init2() stage.
+ */
+
+void ro_gui_global_history_postinitialise(void)
+{
+ /* Create our toolbar. */
+
+ global_history_window.toolbar = ro_toolbar_create(NULL,
+ global_history_window.window,
+ THEME_STYLE_GLOBAL_HISTORY_TOOLBAR, TOOLBAR_FLAGS_NONE,
+ ro_treeview_get_toolbar_callbacks(), NULL,
+ "HelpGHistoryToolbar");
+ if (global_history_window.toolbar != NULL) {
+ ro_toolbar_add_buttons(global_history_window.toolbar,
+ global_history_toolbar_buttons,
+ nsoption_charp(toolbar_history));
+ ro_toolbar_rebuild(global_history_window.toolbar);
+ }
+
+ /* Create the treeview with the window and toolbar. */
+
+ global_history_window.tv =
+ ro_treeview_create(global_history_window.window,
+ global_history_window.toolbar,
+ &ro_global_history_treeview_callbacks,
+ TREE_HISTORY);
+ if (global_history_window.tv == NULL) {
+ LOG("Failed to allocate treeview");
+ return;
+ }
+
+ ro_toolbar_update_client_data(global_history_window.toolbar,
+ global_history_window.tv);
+
+ /* Build the global history window menu. */
+
+ static const struct ns_menu global_history_definition = {
+ "History", {
+ { "History", NO_ACTION, 0 },
+ { "_History.Export", HISTORY_EXPORT, &dialog_saveas },
+ { "History.Expand", TREE_EXPAND_ALL, 0 },
+ { "History.Expand.All", TREE_EXPAND_ALL, 0 },
+ { "History.Expand.Folders", TREE_EXPAND_FOLDERS, 0 },
+ { "History.Expand.Links", TREE_EXPAND_LINKS, 0 },
+ { "History.Collapse", TREE_COLLAPSE_ALL, 0 },
+ { "History.Collapse.All", TREE_COLLAPSE_ALL, 0 },
+ { "History.Collapse.Folders", TREE_COLLAPSE_FOLDERS, 0 },
+ { "History.Collapse.Links", TREE_COLLAPSE_LINKS, 0 },
+ { "History.Toolbars", NO_ACTION, 0 },
+ { "_History.Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 },
+ { "History.Toolbars.EditToolbar",TOOLBAR_EDIT, 0 },
+ { "Selection", TREE_SELECTION, 0 },
+ { "Selection.Launch", TREE_SELECTION_LAUNCH, 0 },
+ { "Selection.Delete", TREE_SELECTION_DELETE, 0 },
+ { "SelectAll", TREE_SELECT_ALL, 0 },
+ { "Clear", TREE_CLEAR_SELECTION, 0 },
+ {NULL, 0, 0}
+ }
+ };
+ global_history_window.menu = ro_gui_menu_define_menu(
+ &global_history_definition);
+
+ ro_gui_wimp_event_register_menu(global_history_window.window,
+ global_history_window.menu, false, false);
+ ro_gui_wimp_event_register_menu_prepare(global_history_window.window,
+ ro_gui_global_history_menu_prepare);
+ ro_gui_wimp_event_register_menu_selection(global_history_window.window,
+ ro_gui_global_history_menu_select);
+ ro_gui_wimp_event_register_menu_warning(global_history_window.window,
+ ro_gui_global_history_menu_warning);
+}
+
+/**
+ * Destroy the global history window.
+ */
+
+void ro_gui_global_history_destroy(void)
+{
+ if (global_history_window.tv == NULL)
+ return;
+
+ ro_treeview_destroy(global_history_window.tv);
+}
+
+/**
+ * Open the global history window.
+ */
+
+void ro_gui_global_history_open(void)
+{
+ ro_gui_global_history_toolbar_update_buttons();
+
+ if (!ro_gui_dialog_open_top(global_history_window.window,
+ global_history_window.toolbar, 600, 800)) {
+ ro_treeview_set_origin(global_history_window.tv, 0,
+ -(ro_toolbar_height(
+ global_history_window.toolbar)));
+ }
+}
+
+/**
+ * Handle toolbar button clicks.
+ *
+ * \param action The action to handle
+ */
+
+void ro_gui_global_history_toolbar_click(button_bar_action action)
+{
+ switch (action) {
+ case TOOLBAR_BUTTON_DELETE:
+ global_history_keypress(NS_KEY_DELETE_LEFT);
+ break;
+
+ case TOOLBAR_BUTTON_EXPAND:
+ global_history_expand(false);
+ break;
+
+ case TOOLBAR_BUTTON_COLLAPSE:
+ global_history_contract(false);
+ break;
+
+ case TOOLBAR_BUTTON_OPEN:
+ global_history_expand(true);
+ break;
+
+ case TOOLBAR_BUTTON_CLOSE:
+ global_history_contract(true);
+ break;
+
+ case TOOLBAR_BUTTON_LAUNCH:
+ global_history_keypress(NS_KEY_CR);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/**
+ * Update the button state in the global history toolbar.
+ */
+
+void ro_gui_global_history_toolbar_update_buttons(void)
+{
+ ro_toolbar_set_button_shaded_state(global_history_window.toolbar,
+ TOOLBAR_BUTTON_DELETE,
+ !global_history_has_selection());
+
+ ro_toolbar_set_button_shaded_state(global_history_window.toolbar,
+ TOOLBAR_BUTTON_LAUNCH,
+ !global_history_has_selection());
+}
+
+
+/**
+ * Save a new button arrangement in the global history toolbar.
+ *
+ * \param *config The new button configuration string.
+ */
+
+void ro_gui_global_history_toolbar_save_buttons(char *config)
+{
+ nsoption_set_charp(toolbar_history, config);
+ ro_gui_save_options();
+}
+
+
+/**
+ * Prepare the global history menu for opening
+ *
+ * \param w The window owning the menu.
+ * \param i The icon owning the menu.
+ * \param *menu The menu about to be opened.
+ * \param *pointer Pointer to the relevant wimp event block, or
+ * NULL for an Adjust click.
+ * \return true if the event was handled; else false.
+ */
+
+bool ro_gui_global_history_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_pointer *pointer)
+{
+ bool selection;
+
+ if (menu != global_history_window.menu)
+ return false;
+
+ selection = global_history_has_selection();
+
+ ro_gui_menu_set_entry_shaded(global_history_window.menu,
+ TREE_SELECTION, !selection);
+ ro_gui_menu_set_entry_shaded(global_history_window.menu,
+ TREE_CLEAR_SELECTION, !selection);
+
+ ro_gui_save_prepare(GUI_SAVE_HISTORY_EXPORT_HTML,
+ NULL, NULL, NULL, NULL);
+
+ ro_gui_menu_set_entry_shaded(menu, TOOLBAR_BUTTONS,
+ ro_toolbar_menu_option_shade(
+ global_history_window.toolbar));
+ ro_gui_menu_set_entry_ticked(menu, TOOLBAR_BUTTONS,
+ ro_toolbar_menu_buttons_tick(
+ global_history_window.toolbar));
+
+ ro_gui_menu_set_entry_shaded(menu, TOOLBAR_EDIT,
+ ro_toolbar_menu_edit_shade(
+ global_history_window.toolbar));
+ ro_gui_menu_set_entry_ticked(menu, TOOLBAR_EDIT,
+ ro_toolbar_menu_edit_tick(
+ global_history_window.toolbar));
+
+ return true;
+}
+
+/**
+ * Handle submenu warnings for the global_hostory menu
+ *
+ * \param w The window owning the menu.
+ * \param i The icon owning the menu.
+ * \param *menu The menu to which the warning applies.
+ * \param *selection The wimp menu selection data.
+ * \param action The selected menu action.
+ */
+
+void ro_gui_global_history_menu_warning(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action)
+{
+ /* Do nothing */
+}
+
+/**
+ * Handle selections from the global history menu
+ *
+ * \param w The window owning the menu.
+ * \param i The icon owning the menu.
+ * \param *menu The menu from which the selection was made.
+ * \param *selection The wimp menu selection data.
+ * \param action The selected menu action.
+ * \return true if action accepted; else false.
+ */
+
+bool ro_gui_global_history_menu_select(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action)
+{
+ switch (action) {
+ case HISTORY_EXPORT:
+ ro_gui_dialog_open_persistent(w, dialog_saveas, true);
+ return true;
+ case TREE_EXPAND_ALL:
+ global_history_expand(false);
+ return true;
+ case TREE_EXPAND_FOLDERS:
+ global_history_expand(true);
+ return true;
+ case TREE_EXPAND_LINKS:
+ global_history_expand(false);
+ return true;
+ case TREE_COLLAPSE_ALL:
+ global_history_contract(true);
+ return true;
+ case TREE_COLLAPSE_FOLDERS:
+ global_history_contract(true);
+ return true;
+ case TREE_COLLAPSE_LINKS:
+ global_history_contract(false);
+ return true;
+ case TREE_SELECTION_LAUNCH:
+ global_history_keypress(NS_KEY_CR);
+ return true;
+ case TREE_SELECTION_DELETE:
+ global_history_keypress(NS_KEY_DELETE_LEFT);
+ return true;
+ case TREE_SELECT_ALL:
+ global_history_keypress(NS_KEY_SELECT_ALL);
+ return true;
+ case TREE_CLEAR_SELECTION:
+ global_history_keypress(NS_KEY_CLEAR_SELECTION);
+ return true;
+ case TOOLBAR_BUTTONS:
+ ro_toolbar_set_display_buttons(global_history_window.toolbar,
+ !ro_toolbar_get_display_buttons(
+ global_history_window.toolbar));
+ return true;
+ case TOOLBAR_EDIT:
+ ro_toolbar_toggle_edit(global_history_window.toolbar);
+ return true;
+ default:
+ return false;
+ }
+
+ return false;
+}
+
+/**
+ * Check if a particular window handle is the global history window
+ *
+ * \param window the window in question
+ * \return true if this window is the global history
+ */
+
+bool ro_gui_global_history_check_window(wimp_w window)
+{
+ if (global_history_window.window == window)
+ return true;
+ else
+ return false;
+}
+
+/**
+ * Check if a particular menu handle is the global history menu
+ *
+ * \param *menu The menu in question.
+ * \return true if this menu is the global history menu
+ */
+
+bool ro_gui_global_history_check_menu(wimp_menu *menu)
+{
+ if (global_history_window.menu == menu)
+ return true;
+ else
+ return false;
+}
+
diff --git a/frontends/riscos/global_history.h b/frontends/riscos/global_history.h
new file mode 100644
index 000000000..6f5ba11eb
--- /dev/null
+++ b/frontends/riscos/global_history.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Global history (interface).
+ */
+
+#ifndef _NETSURF_RISCOS_GLOBALHISTORY_H_
+#define _NETSURF_RISCOS_GLOBALHISTORY_H_
+
+#include "riscos/menus.h"
+
+void ro_gui_global_history_preinitialise(void);
+void ro_gui_global_history_postinitialise(void);
+void ro_gui_global_history_destroy(void);
+void ro_gui_global_history_open(void);
+void ro_gui_global_history_save(void);
+bool ro_gui_global_history_check_window(wimp_w window);
+bool ro_gui_global_history_check_menu(wimp_menu *menu);
+
+#endif
+
diff --git a/frontends/riscos/gui.c b/frontends/riscos/gui.c
new file mode 100644
index 000000000..309f27bdb
--- /dev/null
+++ b/frontends/riscos/gui.c
@@ -0,0 +1,2522 @@
+/*
+ * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
+ * Copyright 2004-2008 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2004 Andrew Timmins <atimmins@blueyonder.co.uk>
+ * Copyright 2004-2009 John Tytgat <joty@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <unixlib/local.h>
+#include <fpu_control.h>
+#include <oslib/help.h>
+#include <oslib/uri.h>
+#include <oslib/inetsuite.h>
+#include <oslib/pdriver.h>
+#include <oslib/osfile.h>
+#include <oslib/hourglass.h>
+#include <oslib/osgbpb.h>
+#include <oslib/osbyte.h>
+#include <oslib/osmodule.h>
+#include <oslib/osfscontrol.h>
+
+#include "utils/utils.h"
+#include "utils/nsoption.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/file.h"
+#include "utils/filename.h"
+#include "utils/url.h"
+#include "utils/corestrings.h"
+#include "desktop/gui_fetch.h"
+#include "desktop/gui_misc.h"
+#include "desktop/save_complete.h"
+#include "desktop/treeview.h"
+#include "desktop/netsurf.h"
+#include "desktop/browser.h"
+#include "content/urldb.h"
+#include "content/hlcache.h"
+#include "content/backing_store.h"
+
+#include "riscos/gui.h"
+#include "riscos/bitmap.h"
+#include "riscos/wimputils.h"
+#include "riscos/hotlist.h"
+#include "riscos/buffer.h"
+#include "riscos/textselection.h"
+#include "riscos/print.h"
+#include "riscos/save.h"
+#include "riscos/dialog.h"
+#include "riscos/wimp.h"
+#include "riscos/message.h"
+#include "riscos/help.h"
+#include "riscos/query.h"
+#include "riscos/window.h"
+#include "riscos/iconbar.h"
+#include "riscos/sslcert.h"
+#include "riscos/global_history.h"
+#include "riscos/cookies.h"
+#include "riscos/wimp_event.h"
+#include "riscos/uri.h"
+#include "riscos/url_protocol.h"
+#include "riscos/mouse.h"
+#include "riscos/ucstables.h"
+#include "riscos/filetype.h"
+#include "riscos/font.h"
+#include "riscos/toolbar.h"
+#include "riscos/content-handlers/artworks.h"
+#include "riscos/content-handlers/draw.h"
+#include "riscos/content-handlers/sprite.h"
+
+bool riscos_done = false;
+
+extern bool ro_plot_patterned_lines;
+
+int os_version = 0;
+
+const char * const __dynamic_da_name = "NetSurf"; /**< For UnixLib. */
+int __dynamic_da_max_size = 128 * 1024 * 1024; /**< For UnixLib. */
+int __feature_imagefs_is_file = 1; /**< For UnixLib. */
+/* default filename handling */
+int __riscosify_control = __RISCOSIFY_NO_SUFFIX |
+ __RISCOSIFY_NO_REVERSE_SUFFIX;
+#ifndef __ELF__
+extern int __dynamic_num;
+#endif
+
+const char * NETSURF_DIR;
+
+static const char *task_name = "NetSurf";
+#define CHOICES_PREFIX "<Choices$Write>.WWW.NetSurf."
+
+ro_gui_drag_type gui_current_drag_type;
+wimp_t task_handle; /**< RISC OS wimp task handle. */
+static clock_t gui_last_poll; /**< Time of last wimp_poll. */
+osspriteop_area *gui_sprites; /**< Sprite area containing pointer and hotlist sprites */
+
+#define DIR_SEP ('.')
+
+/**
+ * Accepted wimp user messages.
+ */
+static ns_wimp_message_list task_messages = {
+ message_HELP_REQUEST,
+ {
+ message_DATA_SAVE,
+ message_DATA_SAVE_ACK,
+ message_DATA_LOAD,
+ message_DATA_LOAD_ACK,
+ message_DATA_OPEN,
+ message_PRE_QUIT,
+ message_SAVE_DESKTOP,
+ message_MENU_WARNING,
+ message_MENUS_DELETED,
+ message_WINDOW_INFO,
+ message_CLAIM_ENTITY,
+ message_DATA_REQUEST,
+ message_DRAGGING,
+ message_DRAG_CLAIM,
+ message_MODE_CHANGE,
+ message_PALETTE_CHANGE,
+ message_FONT_CHANGED,
+ message_URI_PROCESS,
+ message_URI_RETURN_RESULT,
+ message_INET_SUITE_OPEN_URL,
+#ifdef WITH_PLUGIN
+ message_PLUG_IN_OPENING,
+ message_PLUG_IN_CLOSED,
+ message_PLUG_IN_RESHAPE_REQUEST,
+ message_PLUG_IN_FOCUS,
+ message_PLUG_IN_URL_ACCESS,
+ message_PLUG_IN_STATUS,
+ message_PLUG_IN_BUSY,
+ message_PLUG_IN_STREAM_NEW,
+ message_PLUG_IN_STREAM_WRITE,
+ message_PLUG_IN_STREAM_WRITTEN,
+ message_PLUG_IN_STREAM_DESTROY,
+ message_PLUG_IN_OPEN,
+ message_PLUG_IN_CLOSE,
+ message_PLUG_IN_RESHAPE,
+ message_PLUG_IN_STREAM_AS_FILE,
+ message_PLUG_IN_NOTIFY,
+ message_PLUG_IN_ABORT,
+ message_PLUG_IN_ACTION,
+ /* message_PLUG_IN_INFORMED, (not provided by oslib) */
+#endif
+ message_PRINT_SAVE,
+ message_PRINT_ERROR,
+ message_PRINT_TYPE_ODD,
+ message_HOTLIST_ADD_URL,
+ message_HOTLIST_CHANGED,
+ 0
+ }
+};
+
+
+static struct
+{
+ int width; /* in OS units */
+ int height;
+} screen_info;
+
+
+/**
+ * Callback to translate resource to full url for RISC OS.
+ *
+ * Transforms a resource: path into a full URL. The returned URL is
+ * used as the target for a redirect. The caller takes ownership of
+ * the returned nsurl including unrefing it when finished with it.
+ *
+ * \param path The path of the resource to locate.
+ * \return A string containing the full URL of the target object or
+ * NULL if no suitable resource can be found.
+ */
+static nsurl *gui_get_resource_url(const char *path)
+{
+ static const char base_url[] = "file:///NetSurf:/Resources/";
+ size_t path_len, length;
+ char *raw;
+ nsurl *url = NULL;
+
+ /* Map paths first */
+ if (strcmp(path, "adblock.css") == 0) {
+ path = "AdBlock";
+
+ } else if (strcmp(path, "default.css") == 0) {
+ path = "CSS";
+
+ } else if (strcmp(path, "quirks.css") == 0) {
+ path = "Quirks";
+
+ } else if (strcmp(path, "favicon.ico") == 0) {
+ path = "Icons/content.png";
+
+ } else if (strcmp(path, "user.css") == 0) {
+ /* Special case; this file comes from Choices: */
+ nsurl_create("file:///Choices:WWW/NetSurf/User", &url);
+ return url;
+ }
+
+ path_len = strlen(path);
+
+ /* Find max URL length */
+ length = SLEN(base_url) + SLEN("xx/") + path_len + 1;
+
+ raw = malloc(length);
+ if (raw != NULL) {
+ /* Insert base URL */
+ char *ptr = memcpy(raw, base_url, SLEN(base_url));
+ ptr += SLEN(base_url);
+
+ /* Add language directory to URL, for translated files */
+ /* TODO: handle non-en langauages
+ * handle non-html translated files */
+ if (path_len > SLEN(".html") &&
+ strncmp(path + path_len - SLEN(".html"),
+ ".html", SLEN(".html")) == 0) {
+ memcpy(ptr, "en/", SLEN("en/"));
+ ptr += SLEN("en/");
+ }
+
+ /* Add filename to URL */
+ memcpy(ptr, path, path_len);
+ ptr += path_len;
+
+ /* Terminate string */
+ *ptr = '\0';
+
+ nsurl_create(raw, &url);
+ free(raw);
+ }
+
+ return url;
+}
+
+
+/**
+ * Set colour option from wimp.
+ *
+ * \param opts The netsurf options.
+ * \param wimp wimp colour value
+ * \param option the netsurf option enum.
+ * \param def_colour The default colour value to use.
+ * \return NSERROR_OK on success or error code.
+ */
+static nserror
+set_colour_from_wimp(struct nsoption_s *opts,
+ wimp_colour wimp,
+ enum nsoption_e option,
+ colour def_colour)
+{
+ os_error *error;
+ os_PALETTE(20) palette;
+
+ error = xwimp_read_true_palette((os_palette *) &palette);
+ if (error != NULL) {
+ LOG("xwimp_read_palette: 0x%x: %s",
+ error->errnum, error->errmess);
+ } else {
+ /* entries are in B0G0R0LL */
+ def_colour = palette.entries[wimp] >> 8;
+ }
+
+ opts[option].value.c = def_colour;
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Set option defaults for riscos frontend
+ *
+ * @param defaults The option table to update.
+ * @return error status.
+ *
+ * @todo The wimp_COLOUR_... values here map the colour definitions to
+ * parts of the RISC OS desktop palette. In places this is fairly
+ * arbitrary, and could probably do with re-checking.
+ */
+static nserror set_defaults(struct nsoption_s *defaults)
+{
+ /* Set defaults for absent option strings */
+ nsoption_setnull_charp(ca_bundle, strdup("NetSurf:Resources.ca-bundle"));
+ nsoption_setnull_charp(cookie_file, strdup("NetSurf:Cookies"));
+ nsoption_setnull_charp(cookie_jar, strdup(CHOICES_PREFIX "Cookies"));
+
+ if (nsoption_charp(ca_bundle) == NULL ||
+ nsoption_charp(cookie_file) == NULL ||
+ nsoption_charp(cookie_jar) == NULL) {
+ LOG("Failed initialising default options");
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ /* RISC OS platform does not generally benefit from disc cache
+ * so the default should be off.
+ */
+ nsoption_set_uint(disc_cache_size, 0);
+
+ /* set default system colours for riscos ui */
+ set_colour_from_wimp(defaults, wimp_COLOUR_BLACK, NSOPTION_sys_colour_ActiveBorder, 0x00000000);
+ set_colour_from_wimp(defaults, wimp_COLOUR_CREAM, NSOPTION_sys_colour_ActiveCaption, 0x00dddddd);
+ set_colour_from_wimp(defaults, wimp_COLOUR_VERY_LIGHT_GREY, NSOPTION_sys_colour_AppWorkspace, 0x00eeeeee);
+ set_colour_from_wimp(defaults, wimp_COLOUR_VERY_LIGHT_GREY, NSOPTION_sys_colour_Background, 0x00aa0000);/* \TODO -- Check */
+ set_colour_from_wimp(defaults, wimp_COLOUR_VERY_LIGHT_GREY, NSOPTION_sys_colour_ButtonFace, 0x00aaaaaa);
+ set_colour_from_wimp(defaults, wimp_COLOUR_DARK_GREY, NSOPTION_sys_colour_ButtonHighlight, 0x00cccccc);/* \TODO -- Check */
+ set_colour_from_wimp(defaults, wimp_COLOUR_MID_DARK_GREY, NSOPTION_sys_colour_ButtonShadow, 0x00bbbbbb);
+ set_colour_from_wimp(defaults, wimp_COLOUR_BLACK, NSOPTION_sys_colour_ButtonText, 0x00000000);
+ set_colour_from_wimp(defaults, wimp_COLOUR_BLACK, NSOPTION_sys_colour_CaptionText, 0x00000000);
+ set_colour_from_wimp(defaults, wimp_COLOUR_MID_LIGHT_GREY, NSOPTION_sys_colour_GrayText, 0x00777777);/* \TODO -- Check */
+ set_colour_from_wimp(defaults, wimp_COLOUR_BLACK, NSOPTION_sys_colour_Highlight, 0x00ee0000);
+ set_colour_from_wimp(defaults, wimp_COLOUR_WHITE, NSOPTION_sys_colour_HighlightText, 0x00000000);
+ set_colour_from_wimp(defaults, wimp_COLOUR_BLACK, NSOPTION_sys_colour_InactiveBorder, 0x00000000);
+ set_colour_from_wimp(defaults, wimp_COLOUR_LIGHT_GREY, NSOPTION_sys_colour_InactiveCaption, 0x00ffffff);
+ set_colour_from_wimp(defaults, wimp_COLOUR_BLACK, NSOPTION_sys_colour_InactiveCaptionText, 0x00cccccc);
+ set_colour_from_wimp(defaults, wimp_COLOUR_CREAM, NSOPTION_sys_colour_InfoBackground, 0x00aaaaaa);
+ set_colour_from_wimp(defaults, wimp_COLOUR_BLACK, NSOPTION_sys_colour_InfoText, 0x00000000);
+ set_colour_from_wimp(defaults, wimp_COLOUR_WHITE, NSOPTION_sys_colour_Menu, 0x00aaaaaa);
+ set_colour_from_wimp(defaults, wimp_COLOUR_BLACK, NSOPTION_sys_colour_MenuText, 0x00000000);
+ set_colour_from_wimp(defaults, wimp_COLOUR_LIGHT_GREY, NSOPTION_sys_colour_Scrollbar, 0x00aaaaaa);/* \TODO -- Check */
+ set_colour_from_wimp(defaults, wimp_COLOUR_MID_DARK_GREY, NSOPTION_sys_colour_ThreeDDarkShadow, 0x00555555);
+ set_colour_from_wimp(defaults, wimp_COLOUR_VERY_LIGHT_GREY, NSOPTION_sys_colour_ThreeDFace, 0x00dddddd);
+ set_colour_from_wimp(defaults, wimp_COLOUR_WHITE, NSOPTION_sys_colour_ThreeDHighlight, 0x00aaaaaa);
+ set_colour_from_wimp(defaults, wimp_COLOUR_WHITE, NSOPTION_sys_colour_ThreeDLightShadow, 0x00999999);
+ set_colour_from_wimp(defaults, wimp_COLOUR_MID_DARK_GREY, NSOPTION_sys_colour_ThreeDShadow, 0x00777777);
+ set_colour_from_wimp(defaults, wimp_COLOUR_VERY_LIGHT_GREY, NSOPTION_sys_colour_Window, 0x00aaaaaa);
+ set_colour_from_wimp(defaults, wimp_COLOUR_BLACK, NSOPTION_sys_colour_WindowFrame, 0x00000000);
+ set_colour_from_wimp(defaults, wimp_COLOUR_BLACK, NSOPTION_sys_colour_WindowText, 0x00000000);
+
+ return NSERROR_OK;
+}
+
+
+
+
+/**
+ * Create intermediate directories for Choices and User Data files
+ */
+static void ro_gui_create_dirs(void)
+{
+ char buf[256];
+ char *path;
+
+ /* Choices */
+ path = getenv("NetSurf$ChoicesSave");
+ if (!path)
+ die("Failed to find NetSurf Choices save path");
+
+ snprintf(buf, sizeof(buf), "%s", path);
+ netsurf_mkdir_all(buf);
+
+ /* URL */
+ snprintf(buf, sizeof(buf), "%s", nsoption_charp(url_save));
+ netsurf_mkdir_all(buf);
+
+ /* Hotlist */
+ snprintf(buf, sizeof(buf), "%s", nsoption_charp(hotlist_save));
+ netsurf_mkdir_all(buf);
+
+ /* Recent */
+ snprintf(buf, sizeof(buf), "%s", nsoption_charp(recent_save));
+ netsurf_mkdir_all(buf);
+
+ /* Theme */
+ snprintf(buf, sizeof(buf), "%s", nsoption_charp(theme_save));
+ netsurf_mkdir_all(buf);
+ /* and the final directory part (as theme_save is a directory) */
+ xosfile_create_dir(buf, 0);
+}
+
+
+/**
+ * Ensures the gui exits cleanly.
+ */
+static void ro_gui_cleanup(void)
+{
+ ro_gui_buffer_close();
+ xhourglass_off();
+ /* Uninstall NetSurf-specific fonts */
+ xos_cli("FontRemove NetSurf:Resources.Fonts.");
+}
+
+
+/**
+ * Handles a signal
+ */
+static void ro_gui_signal(int sig)
+{
+ static const os_error error = { 1, "NetSurf has detected a serious "
+ "error and must exit. Please submit a bug report, "
+ "attaching the browser log file." };
+ os_colour old_sand, old_glass;
+
+ ro_gui_cleanup();
+
+ xhourglass_on();
+ xhourglass_colours(0x0000ffff, 0x000000ff, &old_sand, &old_glass);
+ nsoption_dump(stderr, NULL);
+ /*rufl_dump_state();*/
+
+#ifndef __ELF__
+ /* save WimpSlot and DA to files if NetSurf$CoreDump exists */
+ int used;
+ xos_read_var_val_size("NetSurf$CoreDump", 0, 0, &used, 0, 0);
+ if (used) {
+ int curr_slot;
+ xwimp_slot_size(-1, -1, &curr_slot, 0, 0);
+ LOG("saving WimpSlot, size 0x%x", curr_slot);
+ xosfile_save("$.NetSurf_Slot", 0x8000, 0,
+ (byte *) 0x8000,
+ (byte *) 0x8000 + curr_slot);
+
+ if (__dynamic_num != -1) {
+ int size;
+ byte *base_address;
+ xosdynamicarea_read(__dynamic_num, &size,
+ &base_address, 0, 0, 0, 0, 0);
+ LOG("saving DA %i, base %p, size 0x%x", __dynamic_num, base_address, size);
+ xosfile_save("$.NetSurf_DA",
+ (bits) base_address, 0,
+ base_address,
+ base_address + size);
+ }
+ }
+#else
+ /* Save WimpSlot and UnixLib managed DAs when UnixEnv$coredump
+ * defines a coredump directory. */
+ const _kernel_oserror *err = __unixlib_write_coredump (NULL);
+ if (err != NULL)
+ LOG("Coredump failed: %s", err->errmess);
+#endif
+
+ xhourglass_colours(old_sand, old_glass, 0, 0);
+ xhourglass_off();
+
+ __write_backtrace(sig);
+
+ xwimp_report_error_by_category(&error,
+ wimp_ERROR_BOX_GIVEN_CATEGORY |
+ wimp_ERROR_BOX_CATEGORY_ERROR <<
+ wimp_ERROR_BOX_CATEGORY_SHIFT,
+ "NetSurf", "!netsurf",
+ (osspriteop_area *) 1, "Quit", 0);
+ xos_cli("Filer_Run <Wimp$ScrapDir>.WWW.NetSurf.Log");
+
+ _Exit(sig);
+}
+
+
+/**
+ * Read a "line" from an Acorn URI file.
+ *
+ * \param fp file pointer to read from
+ * \param b buffer for line, size 400 bytes
+ * \return true on success, false on EOF
+ */
+static bool ro_gui_uri_file_parse_line(FILE *fp, char *b)
+{
+ int c;
+ unsigned int i = 0;
+
+ c = getc(fp);
+ if (c == EOF)
+ return false;
+
+ /* skip comment lines */
+ while (c == '#') {
+ do { c = getc(fp); } while (c != EOF && 32 <= c);
+ if (c == EOF)
+ return false;
+ do { c = getc(fp); } while (c != EOF && c < 32);
+ if (c == EOF)
+ return false;
+ }
+
+ /* read "line" */
+ do {
+ if (i == 399)
+ return false;
+ b[i++] = c;
+ c = getc(fp);
+ } while (c != EOF && 32 <= c);
+
+ /* skip line ending control characters */
+ while (c != EOF && c < 32)
+ c = getc(fp);
+
+ if (c != EOF)
+ ungetc(c, fp);
+
+ b[i] = 0;
+ return true;
+}
+
+
+/**
+ * Parse an Acorn URI file.
+ *
+ * \param file_name file to read
+ * \param uri_title pointer to receive title data, or NULL for no data
+ * \return URL from file, or 0 on error and error reported
+ */
+static char *ro_gui_uri_file_parse(const char *file_name, char **uri_title)
+{
+ /* See the "Acorn URI Handler Functional Specification" for the
+ * definition of the URI file format. */
+ char line[400];
+ char *url = NULL;
+ FILE *fp;
+
+ *uri_title = NULL;
+ fp = fopen(file_name, "rb");
+ if (!fp) {
+ LOG("fopen(\"%s\", \"rb\"): %i: %s", file_name, errno, strerror(errno));
+ ro_warn_user("LoadError", strerror(errno));
+ return 0;
+ }
+
+ /* "URI" */
+ if (!ro_gui_uri_file_parse_line(fp, line) || strcmp(line, "URI") != 0)
+ goto uri_syntax_error;
+
+ /* version */
+ if (!ro_gui_uri_file_parse_line(fp, line) ||
+ strspn(line, "0123456789") != strlen(line))
+ goto uri_syntax_error;
+
+ /* URI */
+ if (!ro_gui_uri_file_parse_line(fp, line))
+ goto uri_syntax_error;
+
+ url = strdup(line);
+ if (!url) {
+ ro_warn_user("NoMemory", 0);
+ fclose(fp);
+ return 0;
+ }
+
+ /* title */
+ if (!ro_gui_uri_file_parse_line(fp, line))
+ goto uri_free;
+ if (uri_title && line[0] && ((line[0] != '*') || line[1])) {
+ *uri_title = strdup(line);
+ if (!*uri_title) /* non-fatal */
+ ro_warn_user("NoMemory", 0);
+ }
+ fclose(fp);
+
+ return url;
+
+uri_free:
+ free(url);
+
+uri_syntax_error:
+ fclose(fp);
+ ro_warn_user("URIError", 0);
+ return 0;
+}
+
+
+/**
+ * Parse an ANT URL file.
+ *
+ * \param file_name file to read
+ * \return URL from file, or 0 on error and error reported
+ */
+static char *ro_gui_url_file_parse(const char *file_name)
+{
+ char line[400];
+ char *url;
+ FILE *fp;
+
+ fp = fopen(file_name, "r");
+ if (!fp) {
+ LOG("fopen(\"%s\", \"r\"): %i: %s", file_name, errno, strerror(errno));
+ ro_warn_user("LoadError", strerror(errno));
+ return 0;
+ }
+
+ if (!fgets(line, sizeof line, fp)) {
+ if (ferror(fp)) {
+ LOG("fgets: %i: %s", errno, strerror(errno));
+ ro_warn_user("LoadError", strerror(errno));
+ } else
+ ro_warn_user("LoadError", messages_get("EmptyError"));
+ fclose(fp);
+ return 0;
+ }
+
+ fclose(fp);
+
+ if (line[strlen(line) - 1] == '\n')
+ line[strlen(line) - 1] = '\0';
+
+ url = strdup(line);
+ if (!url) {
+ ro_warn_user("NoMemory", 0);
+ return 0;
+ }
+
+ return url;
+}
+
+
+/**
+ * Parse an IEURL file.
+ *
+ * \param file_name file to read
+ * \return URL from file, or 0 on error and error reported
+ */
+static char *ro_gui_ieurl_file_parse(const char *file_name)
+{
+ char line[400];
+ char *url = 0;
+ FILE *fp;
+
+ fp = fopen(file_name, "r");
+ if (!fp) {
+ LOG("fopen(\"%s\", \"r\"): %i: %s", file_name, errno, strerror(errno));
+ ro_warn_user("LoadError", strerror(errno));
+ return 0;
+ }
+
+ while (fgets(line, sizeof line, fp)) {
+ if (strncmp(line, "URL=", 4) == 0) {
+ if (line[strlen(line) - 1] == '\n')
+ line[strlen(line) - 1] = '\0';
+ url = strdup(line + 4);
+ if (!url) {
+ fclose(fp);
+ ro_warn_user("NoMemory", 0);
+ return 0;
+ }
+ break;
+ }
+ }
+ if (ferror(fp)) {
+ LOG("fgets: %i: %s", errno, strerror(errno));
+ ro_warn_user("LoadError", strerror(errno));
+ fclose(fp);
+ return 0;
+ }
+
+ fclose(fp);
+
+ if (!url)
+ ro_warn_user("URIError", 0);
+
+ return url;
+}
+
+
+/**
+ * Handle Message_DataOpen (double-click on file in the Filer).
+ *
+ * \param message The wimp message to open.
+ */
+static void ro_msg_dataopen(wimp_message *message)
+{
+ int file_type = message->data.data_xfer.file_type;
+ char *url = 0;
+ os_error *oserror;
+ nsurl *urlns;
+ nserror error;
+ size_t len;
+
+ switch (file_type) {
+ case 0xb28: /* ANT URL file */
+ url = ro_gui_url_file_parse(message->data.data_xfer.file_name);
+ error = nsurl_create(url, &urlns);
+ free(url);
+ break;
+
+ case 0xfaf: /* HTML file */
+ error = netsurf_path_to_nsurl(message->data.data_xfer.file_name,
+ &urlns);
+ break;
+
+ case 0x1ba: /* IEURL file */
+ url = ro_gui_ieurl_file_parse(message->
+ data.data_xfer.file_name);
+ error = nsurl_create(url, &urlns);
+ free(url);
+ break;
+
+ case 0x2000: /* application */
+ len = strlen(message->data.data_xfer.file_name);
+ if (len < 9 || strcmp(".!NetSurf",
+ message->data.data_xfer.file_name + len - 9))
+ return;
+
+ if (nsoption_charp(homepage_url) &&
+ nsoption_charp(homepage_url)[0]) {
+ error = nsurl_create(nsoption_charp(homepage_url),
+ &urlns);
+ } else {
+ error = nsurl_create(NETSURF_HOMEPAGE, &urlns);
+ }
+ break;
+
+ default:
+ return;
+ }
+
+ /* send DataLoadAck */
+ message->action = message_DATA_LOAD_ACK;
+ message->your_ref = message->my_ref;
+ oserror = xwimp_send_message(wimp_USER_MESSAGE, message, message->sender);
+ if (oserror) {
+ LOG("xwimp_send_message: 0x%x: %s", oserror->errnum, oserror->errmess);
+ ro_warn_user("WimpError", oserror->errmess);
+ return;
+ }
+
+ if (error != NSERROR_OK) {
+ ro_warn_user(messages_get_errorcode(error), 0);
+ return;
+ }
+
+ /* create a new window with the file */
+ error = browser_window_create(BW_CREATE_HISTORY,
+ urlns,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(urlns);
+ if (error != NSERROR_OK) {
+ ro_warn_user(messages_get_errorcode(error), 0);
+ }
+}
+
+
+/**
+ * Handle Message_DataLoad (file dragged in).
+ */
+static void ro_msg_dataload(wimp_message *message)
+{
+ int file_type = message->data.data_xfer.file_type;
+ char *urltxt = NULL;
+ char *title = NULL;
+ struct gui_window *g;
+ os_error *oserror;
+ nsurl *url;
+ nserror error;
+
+ g = ro_gui_window_lookup(message->data.data_xfer.w);
+ if (g) {
+ if (ro_gui_window_dataload(g, message))
+ return;
+ }
+ else {
+ g = ro_gui_toolbar_lookup(message->data.data_xfer.w);
+ if (g && ro_gui_toolbar_dataload(g, message))
+ return;
+ }
+
+ switch (file_type) {
+ case FILETYPE_ACORN_URI:
+ urltxt = ro_gui_uri_file_parse(message->data.data_xfer.file_name,
+ &title);
+ error = nsurl_create(urltxt, &url);
+ free(urltxt);
+ break;
+
+ case FILETYPE_ANT_URL:
+ urltxt = ro_gui_url_file_parse(message->data.data_xfer.file_name);
+ error = nsurl_create(urltxt, &url);
+ free(urltxt);
+ break;
+
+ case FILETYPE_IEURL:
+ urltxt = ro_gui_ieurl_file_parse(message->data.data_xfer.file_name);
+ error = nsurl_create(urltxt, &url);
+ free(urltxt);
+ break;
+
+ case FILETYPE_HTML:
+ case FILETYPE_JNG:
+ case FILETYPE_CSS:
+ case FILETYPE_MNG:
+ case FILETYPE_GIF:
+ case FILETYPE_BMP:
+ case FILETYPE_ICO:
+ case osfile_TYPE_DRAW:
+ case FILETYPE_PNG:
+ case FILETYPE_JPEG:
+ case osfile_TYPE_SPRITE:
+ case osfile_TYPE_TEXT:
+ case FILETYPE_ARTWORKS:
+ case FILETYPE_SVG:
+ /* display the actual file */
+ error = netsurf_path_to_nsurl(message->data.data_xfer.file_name, &url);
+ break;
+
+ default:
+ return;
+ }
+
+ /* report error to user */
+ if (error != NSERROR_OK) {
+ ro_warn_user(messages_get_errorcode(error), 0);
+ return;
+ }
+
+
+ if (g) {
+ error = browser_window_navigate(g->bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ } else {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ }
+ nsurl_unref(url);
+ if (error != NSERROR_OK) {
+ ro_warn_user(messages_get_errorcode(error), 0);
+ }
+
+
+ /* send DataLoadAck */
+ message->action = message_DATA_LOAD_ACK;
+ message->your_ref = message->my_ref;
+ oserror = xwimp_send_message(wimp_USER_MESSAGE, message,
+ message->sender);
+ if (oserror) {
+ LOG("xwimp_send_message: 0x%x: %s", oserror->errnum, oserror->errmess);
+ ro_warn_user("WimpError", oserror->errmess);
+ return;
+ }
+
+}
+
+
+/**
+ * Ensure that the filename in a data transfer message is NULL terminated
+ * (some applications, especially BASIC programs use CR)
+ *
+ * \param message message to be corrected
+ */
+static void ro_msg_terminate_filename(wimp_full_message_data_xfer *message)
+{
+ const char *ep = (char*)message + message->size;
+ char *p = message->file_name;
+
+ if ((size_t)message->size >= sizeof(*message))
+ ep = (char*)message + sizeof(*message) - 1;
+
+ while (p < ep && *p >= ' ') p++;
+ *p = '\0';
+}
+
+
+/**
+ * Handle Message_DataSave
+ */
+static void ro_msg_datasave(wimp_message *message)
+{
+ wimp_full_message_data_xfer *dataxfer = (wimp_full_message_data_xfer*)message;
+
+ /* remove ghost caret if drag-and-drop protocol was used */
+// ro_gui_selection_drag_reset();
+
+ ro_msg_terminate_filename(dataxfer);
+
+ if (ro_gui_selection_prepare_paste_datasave(dataxfer))
+ return;
+
+ switch (dataxfer->file_type) {
+ case FILETYPE_ACORN_URI:
+ case FILETYPE_ANT_URL:
+ case FILETYPE_IEURL:
+ case FILETYPE_HTML:
+ case FILETYPE_JNG:
+ case FILETYPE_CSS:
+ case FILETYPE_MNG:
+ case FILETYPE_GIF:
+ case FILETYPE_BMP:
+ case FILETYPE_ICO:
+ case osfile_TYPE_DRAW:
+ case FILETYPE_PNG:
+ case FILETYPE_JPEG:
+ case osfile_TYPE_SPRITE:
+ case osfile_TYPE_TEXT:
+ case FILETYPE_ARTWORKS:
+ case FILETYPE_SVG: {
+ os_error *error;
+
+ dataxfer->your_ref = dataxfer->my_ref;
+ dataxfer->size = offsetof(wimp_full_message_data_xfer, file_name) + 16;
+ dataxfer->action = message_DATA_SAVE_ACK;
+ dataxfer->est_size = -1;
+ memcpy(dataxfer->file_name, "<Wimp$Scrap>", 13);
+
+ error = xwimp_send_message(wimp_USER_MESSAGE, (wimp_message*)dataxfer, message->sender);
+ if (error) {
+ LOG("xwimp_send_message: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ }
+ break;
+ }
+}
+
+
+/**
+ * Handle Message_DataSaveAck.
+ */
+static void ro_msg_datasave_ack(wimp_message *message)
+{
+ ro_msg_terminate_filename((wimp_full_message_data_xfer*)message);
+
+ if (ro_print_ack(message))
+ return;
+
+ switch (gui_current_drag_type) {
+ case GUI_DRAG_DOWNLOAD_SAVE:
+ ro_gui_download_datasave_ack(message);
+ break;
+
+ case GUI_DRAG_SAVE:
+ ro_gui_save_datasave_ack(message);
+ gui_current_drag_type = GUI_DRAG_NONE;
+ break;
+
+ default:
+ break;
+ }
+
+ gui_current_drag_type = GUI_DRAG_NONE;
+}
+
+
+/**
+ * Handle PreQuit message
+ *
+ * \param message PreQuit message from Wimp
+ */
+static void ro_msg_prequit(wimp_message *message)
+{
+ if (!ro_gui_prequit()) {
+ os_error *error;
+
+ /* we're objecting to the close down */
+ message->your_ref = message->my_ref;
+ error = xwimp_send_message(wimp_USER_MESSAGE_ACKNOWLEDGE,
+ message, message->sender);
+ if (error) {
+ LOG("xwimp_send_message: 0x%x:%s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ }
+}
+
+
+/**
+ * Handle SaveDesktop message.
+ *
+ * \param message SaveDesktop message from Wimp.
+ */
+static void ro_msg_save_desktop(wimp_message *message)
+{
+ os_error *error;
+
+ error = xosgbpb_writew(message->data.save_desktopw.file,
+ (const byte*)"Run ", 4, NULL);
+ if (!error) {
+ error = xosgbpb_writew(message->data.save_desktopw.file,
+ (const byte*)NETSURF_DIR, strlen(NETSURF_DIR), NULL);
+ if (!error)
+ error = xos_bputw('\n', message->data.save_desktopw.file);
+ }
+
+ if (error) {
+ LOG("xosgbpb_writew/xos_bputw: 0x%x:%s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+
+ /* we must cancel the save by acknowledging the message */
+ message->your_ref = message->my_ref;
+ error = xwimp_send_message(wimp_USER_MESSAGE_ACKNOWLEDGE,
+ message, message->sender);
+ if (error) {
+ LOG("xwimp_send_message: 0x%x:%s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ }
+}
+
+
+/**
+ * Handle WindowInfo message (part of the iconising protocol)
+ *
+ * \param message WindowInfo message from the Iconiser
+ */
+static void ro_msg_window_info(wimp_message *message)
+{
+ wimp_full_message_window_info *wi;
+ struct gui_window *g;
+
+ /* allow the user to turn off thumbnail icons */
+ if (!nsoption_bool(thumbnail_iconise))
+ return;
+
+ wi = (wimp_full_message_window_info*)message;
+ g = ro_gui_window_lookup(wi->w);
+
+ /* ic_<task name> will suffice for our other windows */
+ if (g) {
+ ro_gui_window_iconise(g, wi);
+ ro_gui_dialog_close_persistent(wi->w);
+ }
+}
+
+
+/**
+ * Get screen properties following a mode change.
+ */
+static void ro_gui_get_screen_properties(void)
+{
+ static const ns_os_vdu_var_list vars = {
+ os_MODEVAR_XWIND_LIMIT,
+ {
+ os_MODEVAR_YWIND_LIMIT,
+ os_MODEVAR_XEIG_FACTOR,
+ os_MODEVAR_YEIG_FACTOR,
+ os_VDUVAR_END_LIST
+ }
+ };
+ os_error *error;
+ int vals[4];
+
+ error = xos_read_vdu_variables(PTR_OS_VDU_VAR_LIST(&vars), vals);
+ if (error) {
+ LOG("xos_read_vdu_variables: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ return;
+ }
+ screen_info.width = (vals[0] + 1) << vals[2];
+ screen_info.height = (vals[1] + 1) << vals[3];
+}
+
+
+/**
+ * Warn the user if Inet$Resolvers is not set.
+ */
+static void ro_gui_check_resolvers(void)
+{
+ char *resolvers;
+ resolvers = getenv("Inet$Resolvers");
+ if (resolvers && resolvers[0]) {
+ LOG("Inet$Resolvers '%s'", resolvers);
+ } else {
+ LOG("Inet$Resolvers not set or empty");
+ ro_warn_user("Resolvers", 0);
+ }
+}
+
+
+/**
+ * Initialise the RISC OS specific GUI.
+ *
+ * \param argc The number of command line arguments.
+ * \param argv The string vector of command line arguments.
+ */
+static nserror gui_init(int argc, char** argv)
+{
+ struct {
+ void (*sigabrt)(int);
+ void (*sigfpe)(int);
+ void (*sigill)(int);
+ void (*sigint)(int);
+ void (*sigsegv)(int);
+ void (*sigterm)(int);
+ void (*sigoserror)(int);
+ } prev_sigs;
+ char path[40];
+ os_error *error;
+ int length;
+ char *nsdir_temp;
+ byte *base;
+ nsurl *url;
+ nserror ret;
+ bool open_window;
+
+ /* re-enable all FPU exceptions/traps except inexact operations,
+ * which we're not interested in, and underflow which is incorrectly
+ * raised when converting an exact value of 0 from double-precision
+ * to single-precision on FPEmulator v4.09-4.11 (MVFD F0,#0:MVFS F0,F0)
+ * - UnixLib disables all FP exceptions by default */
+
+ _FPU_SETCW(_FPU_IEEE & ~(_FPU_MASK_PM | _FPU_MASK_UM));
+
+ xhourglass_start(1);
+
+ /* read OS version for code that adapts to conform to the OS
+ * (remember that it's preferable to check for specific features
+ * being present) */
+ xos_byte(osbyte_IN_KEY, 0, 0xff, &os_version, NULL);
+
+ /* the first release version of the A9home OS is incapable of
+ plotting patterned lines (presumably a fault in the hw acceleration) */
+ if (!xosmodule_lookup("VideoHWSMI", NULL, NULL, &base, NULL, NULL)) {
+#if 0 // this fault still hasn't been fixed, so disable patterned lines for all versions until it has
+ const char *help = (char*)base + ((int*)base)[5];
+ while (*help > 9) help++;
+ while (*help == 9) help++;
+ if (!memcmp(help, "0.55", 4))
+#endif
+ ro_plot_patterned_lines = false;
+ }
+
+ /* Create our choices directories */
+ ro_gui_create_dirs();
+
+ /* Register exit and signal handlers */
+ atexit(ro_gui_cleanup);
+ prev_sigs.sigabrt = signal(SIGABRT, ro_gui_signal);
+ prev_sigs.sigfpe = signal(SIGFPE, ro_gui_signal);
+ prev_sigs.sigill = signal(SIGILL, ro_gui_signal);
+ prev_sigs.sigint = signal(SIGINT, ro_gui_signal);
+ prev_sigs.sigsegv = signal(SIGSEGV, ro_gui_signal);
+ prev_sigs.sigterm = signal(SIGTERM, ro_gui_signal);
+ prev_sigs.sigoserror = signal(SIGOSERROR, ro_gui_signal);
+
+ if (prev_sigs.sigabrt == SIG_ERR || prev_sigs.sigfpe == SIG_ERR ||
+ prev_sigs.sigill == SIG_ERR ||
+ prev_sigs.sigint == SIG_ERR ||
+ prev_sigs.sigsegv == SIG_ERR ||
+ prev_sigs.sigterm == SIG_ERR ||
+ prev_sigs.sigoserror == SIG_ERR)
+ die("Failed registering signal handlers");
+
+ /* Load in UI sprites */
+ gui_sprites = ro_gui_load_sprite_file("NetSurf:Resources.Sprites");
+ if (!gui_sprites)
+ die("Unable to load Sprites.");
+
+ /* Find NetSurf directory */
+ nsdir_temp = getenv("NetSurf$Dir");
+ if (!nsdir_temp)
+ die("Failed to locate NetSurf directory");
+ NETSURF_DIR = strdup(nsdir_temp);
+ if (!NETSURF_DIR)
+ die("Failed duplicating NetSurf directory string");
+
+ /* Initialise filename allocator */
+ filename_initialise();
+
+ /* Initialise save complete functionality */
+ save_complete_init();
+
+ /* Load in visited URLs and Cookies */
+ urldb_load(nsoption_charp(url_path));
+ urldb_load_cookies(nsoption_charp(cookie_file));
+
+ /* Initialise with the wimp */
+ error = xwimp_initialise(wimp_VERSION_RO38, task_name,
+ PTR_WIMP_MESSAGE_LIST(&task_messages), 0,
+ &task_handle);
+ if (error) {
+ LOG("xwimp_initialise: 0x%x: %s", error->errnum, error->errmess);
+ die(error->errmess);
+ }
+ /* Register message handlers */
+ ro_message_register_route(message_HELP_REQUEST,
+ ro_gui_interactive_help_request);
+ ro_message_register_route(message_DATA_OPEN,
+ ro_msg_dataopen);
+ ro_message_register_route(message_DATA_SAVE,
+ ro_msg_datasave);
+ ro_message_register_route(message_DATA_SAVE_ACK,
+ ro_msg_datasave_ack);
+ ro_message_register_route(message_PRE_QUIT,
+ ro_msg_prequit);
+ ro_message_register_route(message_SAVE_DESKTOP,
+ ro_msg_save_desktop);
+ ro_message_register_route(message_DRAGGING,
+ ro_gui_selection_dragging);
+ ro_message_register_route(message_DRAG_CLAIM,
+ ro_gui_selection_drag_claim);
+ ro_message_register_route(message_WINDOW_INFO,
+ ro_msg_window_info);
+
+ /* Initialise the font subsystem */
+ nsfont_init();
+
+ /* Initialise global information */
+ ro_gui_get_screen_properties();
+ ro_gui_wimp_get_desktop_font();
+
+ /* Issue a *Desktop to poke AcornURI into life */
+ if (getenv("NetSurf$Start_URI_Handler"))
+ xwimp_start_task("Desktop", 0);
+
+ /* Open the templates */
+ if ((length = snprintf(path, sizeof(path),
+ "NetSurf:Resources.%s.Templates",
+ nsoption_charp(language))) < 0 || length >= (int)sizeof(path))
+ die("Failed to locate Templates resource.");
+ error = xwimp_open_template(path);
+ if (error) {
+ LOG("xwimp_open_template failed: 0x%x: %s", error->errnum, error->errmess);
+ die(error->errmess);
+ }
+
+ ret = treeview_init(12);
+ if (ret != NSERROR_OK) {
+ die("Failed to initialise treeview");
+ }
+
+ /* Initialise themes before dialogs */
+ ro_gui_theme_initialise();
+
+ /* Initialise dialog windows (must be after UI sprites are loaded) */
+ ro_gui_dialog_init();
+
+ /* Initialise download window */
+ ro_gui_download_init();
+
+ /* Initialise menus */
+ ro_gui_menu_init();
+
+ /* Initialise query windows */
+ ro_gui_query_init();
+
+ /* Initialise the history subsystem */
+ ro_gui_history_init();
+
+ /* Initialise toolbars */
+ ro_toolbar_init();
+
+ /* Initialise url bar module */
+ ro_gui_url_bar_init();
+
+ /* Initialise browser windows */
+ ro_gui_window_initialise();
+
+ /* Done with the templates file */
+ wimp_close_template();
+
+ /* Create Iconbar icon and menus */
+ ro_gui_iconbar_initialise();
+
+ /* Finally, check Inet$Resolvers for sanity */
+ ro_gui_check_resolvers();
+
+ /* certificate verification window */
+ ro_gui_cert_postinitialise();
+
+ /* hotlist window */
+ ro_gui_hotlist_postinitialise();
+
+ /* global history window */
+ ro_gui_global_history_postinitialise();
+
+ /* cookies window */
+ ro_gui_cookies_postinitialise();
+
+ open_window = nsoption_bool(open_browser_at_startup);
+
+ /* parse command-line arguments */
+ if (argc == 2) {
+ LOG("parameters: '%s'", argv[1]);
+ /* this is needed for launching URI files */
+ if (strcasecmp(argv[1], "-nowin") == 0) {
+ return NSERROR_OK;
+ }
+ ret = nsurl_create(NETSURF_HOMEPAGE, &url);
+ }
+ else if (argc == 3) {
+ LOG("parameters: '%s' '%s'", argv[1], argv[2]);
+ open_window = true;
+
+ /* HTML files */
+ if (strcasecmp(argv[1], "-html") == 0) {
+ ret = netsurf_path_to_nsurl(argv[2], &url);
+ }
+ /* URL files */
+ else if (strcasecmp(argv[1], "-urlf") == 0) {
+ char *urlf = ro_gui_url_file_parse(argv[2]);
+ if (!urlf) {
+ LOG("allocation failed");
+ die("Insufficient memory for URL");
+ }
+ ret = nsurl_create(urlf, &url);
+ free(urlf);
+ }
+ /* ANT URL Load */
+ else if (strcasecmp(argv[1], "-url") == 0) {
+ ret = nsurl_create(argv[2], &url);
+ }
+ /* Unknown => exit here. */
+ else {
+ LOG("Unknown parameters: '%s' '%s'", argv[1], argv[2]);
+ return NSERROR_BAD_PARAMETER;
+ }
+ }
+ /* get user's homepage (if configured) */
+ else if (nsoption_charp(homepage_url) &&
+ nsoption_charp(homepage_url)[0]) {
+ ret = nsurl_create(nsoption_charp(homepage_url), &url);
+ }
+ /* default homepage */
+ else {
+ ret = nsurl_create(NETSURF_HOMEPAGE, &url);
+ }
+
+ /* check for url creation error */
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+
+ if (open_window) {
+ ret = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ }
+ nsurl_unref(url);
+
+ return ret;
+}
+
+
+/**
+ * Determine the default language to use.
+ *
+ * RISC OS has no standard way of determining which language the user prefers.
+ * We have to guess from the 'Country' setting.
+ */
+const char *ro_gui_default_language(void)
+{
+ char path[40];
+ const char *lang;
+ int country;
+ os_error *error;
+
+ /* choose a language from the configured country number */
+ error = xosbyte_read(osbyte_VAR_COUNTRY_NUMBER, &country);
+ if (error) {
+ LOG("xosbyte_read failed: 0x%x: %s", error->errnum, error->errmess);
+ country = 1;
+ }
+ switch (country) {
+ case 7: /* Germany */
+ case 30: /* Austria */
+ case 35: /* Switzerland (70% German-speaking) */
+ lang = "de";
+ break;
+ case 6: /* France */
+ case 18: /* Canada2 (French Canada?) */
+ lang = "fr";
+ break;
+ case 34: /* Netherlands */
+ lang = "nl";
+ break;
+ default:
+ lang = "en";
+ break;
+ }
+ sprintf(path, "NetSurf:Resources.%s", lang);
+ if (is_dir(path))
+ return lang;
+ return "en";
+}
+
+
+/**
+ * Create a nsurl from a RISC OS pathname.
+ *
+ * Perform the necessary operations on a path to generate a nsurl.
+ *
+ * @param[in] path The RISC OS pathname to convert.
+ * @param[out] url_out pointer to recive the nsurl, The returned url must be
+ * unreferenced by the caller.
+ * @return NSERROR_OK and the url is placed in \a url or error code on faliure.
+ */
+static nserror ro_path_to_nsurl(const char *path, struct nsurl **url_out)
+{
+ int spare;
+ char *canonical_path; /* canonicalised RISC OS path */
+ char *unix_path; /* unix path */
+ char *escurl;
+ os_error *error;
+ nserror ret;
+ int urllen;
+ char *url; /* resulting url */
+
+ /* calculate the canonical risc os path */
+ error = xosfscontrol_canonicalise_path(path, 0, 0, 0, 0, &spare);
+ if (error) {
+ LOG("xosfscontrol_canonicalise_path failed: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("PathToURL", error->errmess);
+ return NSERROR_NOT_FOUND;
+ }
+
+ canonical_path = malloc(1 - spare);
+ if (canonical_path == NULL) {
+ free(canonical_path);
+ return NSERROR_NOMEM;
+ }
+
+ error = xosfscontrol_canonicalise_path(path, canonical_path, 0, 0, 1 - spare, 0);
+ if (error) {
+ LOG("xosfscontrol_canonicalise_path failed: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("PathToURL", error->errmess);
+ free(canonical_path);
+ return NSERROR_NOT_FOUND;
+ }
+
+ /* create a unix path from the cananocal risc os one */
+ unix_path = __unixify(canonical_path, __RISCOSIFY_NO_REVERSE_SUFFIX, NULL, 0, 0);
+
+ if (unix_path == NULL) {
+ LOG("__unixify failed: %s", canonical_path);
+ free(canonical_path);
+ return NSERROR_BAD_PARAMETER;
+ }
+ free(canonical_path);
+
+ /* convert the unix path into a url */
+ urllen = strlen(unix_path) + FILE_SCHEME_PREFIX_LEN + 1;
+ url = malloc(urllen);
+ if (url == NULL) {
+ LOG("Unable to allocate url");
+ free(unix_path);
+ return NSERROR_NOMEM;
+ }
+
+ if (*unix_path == '/') {
+ snprintf(url, urllen, "%s%s", FILE_SCHEME_PREFIX, unix_path + 1);
+ } else {
+ snprintf(url, urllen, "%s%s", FILE_SCHEME_PREFIX, unix_path);
+ }
+ free(unix_path);
+
+ /* We don't want '/' to be escaped. */
+ ret = url_escape(url, FILE_SCHEME_PREFIX_LEN, false, "/", &escurl);
+ free(url);
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+
+ ret = nsurl_create(escurl, url_out);
+ free(escurl);
+
+ return ret;
+}
+
+
+/**
+ * Create a path from a nsurl using posix file handling.
+ *
+ * @param[in] url The url to encode.
+ * @param[out] path_out A string containing the result path which should
+ * be freed by the caller.
+ * @return NSERROR_OK and the path is written to \a path or error code
+ * on faliure.
+ */
+static nserror ro_nsurl_to_path(struct nsurl *url, char **path_out)
+{
+ lwc_string *urlpath;
+ char *unpath;
+ char *path;
+ bool match;
+ lwc_string *scheme;
+ nserror res;
+ char *r;
+
+ if ((url == NULL) || (path_out == NULL)) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ scheme = nsurl_get_component(url, NSURL_SCHEME);
+
+ if (lwc_string_caseless_isequal(scheme, corestring_lwc_file,
+ &match) != lwc_error_ok)
+ {
+ return NSERROR_BAD_PARAMETER;
+ }
+ lwc_string_unref(scheme);
+ if (match == false) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ urlpath = nsurl_get_component(url, NSURL_PATH);
+ if (urlpath == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ res = url_unescape(lwc_string_data(urlpath), &unpath);
+ lwc_string_unref(urlpath);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ /* RISC OS path should not be more than 100 characters longer */
+ path = malloc(strlen(unpath) + 100);
+ if (path == NULL) {
+ free(unpath);
+ return NSERROR_NOMEM;
+ }
+
+ r = __riscosify(unpath, 0, __RISCOSIFY_NO_SUFFIX,
+ path, strlen(unpath) + 100, 0);
+ free(unpath);
+ if (r == NULL) {
+ free(path);
+ return NSERROR_NOMEM;
+ }
+
+ *path_out = path;
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Ensures output logging stream is correctly configured.
+ */
+static bool nslog_stream_configure(FILE *fptr)
+{
+ /* set log stream to be non-buffering */
+ setbuf(fptr, NULL);
+
+ return true;
+}
+
+
+/**
+ * Close down the gui (RISC OS).
+ */
+static void gui_quit(void)
+{
+ urldb_save_cookies(nsoption_charp(cookie_jar));
+ urldb_save(nsoption_charp(url_save));
+ ro_gui_window_quit();
+ ro_gui_global_history_destroy();
+ ro_gui_hotlist_destroy();
+ ro_gui_cookies_destroy();
+ ro_gui_saveas_quit();
+ ro_gui_url_bar_fini();
+ rufl_quit();
+ free(gui_sprites);
+ xwimp_close_down(task_handle);
+ xhourglass_off();
+}
+
+
+/**
+ * Handle Close_Window_Request events.
+ */
+static void ro_gui_close_window_request(wimp_close *close)
+{
+ if (ro_gui_alt_pressed())
+ ro_gui_window_close_all();
+ else {
+ if (ro_gui_wimp_event_close_window(close->w))
+ return;
+ ro_gui_dialog_close(close->w);
+ }
+}
+
+
+/**
+ * Handle key press paste callback.
+ */
+static void ro_gui_keypress_cb(void *pw)
+{
+ wimp_key *key = (wimp_key *) pw;
+
+ if (ro_gui_wimp_event_keypress(key) == false) {
+ os_error *error = xwimp_process_key(key->c);
+ if (error) {
+ LOG("xwimp_process_key: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ }
+
+ free(key);
+}
+
+
+/**
+ * Handle gui keypress.
+ */
+static void ro_gui_keypress(wimp_key *key)
+{
+ if (key->c == wimp_KEY_ESCAPE &&
+ (gui_current_drag_type == GUI_DRAG_SAVE ||
+ gui_current_drag_type == GUI_DRAG_DOWNLOAD_SAVE)) {
+
+ /* Allow Escape key to be used for cancelling a drag
+ * save (easier than finding somewhere safe to abort
+ * the drag)
+ */
+ ro_gui_drag_box_cancel();
+ gui_current_drag_type = GUI_DRAG_NONE;
+ } else if (key->c == 22 /* Ctrl-V */) {
+ wimp_key *copy;
+
+ /* Must copy the keypress as it's on the stack */
+ copy = malloc(sizeof(wimp_key));
+ if (copy == NULL)
+ return;
+ memcpy(copy, key, sizeof(wimp_key));
+
+ ro_gui_selection_prepare_paste(key->w, ro_gui_keypress_cb, copy);
+ } else if (ro_gui_wimp_event_keypress(key) == false) {
+ os_error *error = xwimp_process_key(key->c);
+ if (error) {
+ LOG("xwimp_process_key: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ }
+}
+
+
+/**
+ * Handle the three User_Message events.
+ */
+static void ro_gui_user_message(wimp_event_no event, wimp_message *message)
+{
+ /* attempt automatic routing */
+ if (ro_message_handle_message(event, message))
+ return;
+
+ switch (message->action) {
+ case message_DATA_LOAD:
+ ro_msg_terminate_filename((wimp_full_message_data_xfer*)message);
+
+ if (event == wimp_USER_MESSAGE_ACKNOWLEDGE) {
+ if (ro_print_current_window)
+ ro_print_dataload_bounce(message);
+ } else if (ro_gui_selection_prepare_paste_dataload(
+ (wimp_full_message_data_xfer *) message) == false) {
+ ro_msg_dataload(message);
+ }
+ break;
+
+ case message_DATA_LOAD_ACK:
+ if (ro_print_current_window)
+ ro_print_cleanup();
+ break;
+
+ case message_MENU_WARNING:
+ ro_gui_menu_warning((wimp_message_menu_warning *)
+ &message->data);
+ break;
+
+ case message_MENUS_DELETED:
+ ro_gui_menu_message_deleted((wimp_message_menus_deleted *)
+ &message->data);
+ break;
+
+ case message_CLAIM_ENTITY:
+ ro_gui_selection_claim_entity((wimp_full_message_claim_entity*)message);
+ break;
+
+ case message_DATA_REQUEST:
+ ro_gui_selection_data_request((wimp_full_message_data_request*)message);
+ break;
+
+ case message_MODE_CHANGE:
+ ro_gui_get_screen_properties();
+ rufl_invalidate_cache();
+ break;
+
+ case message_PALETTE_CHANGE:
+ break;
+
+ case message_FONT_CHANGED:
+ ro_gui_wimp_get_desktop_font();
+ break;
+
+ case message_URI_PROCESS:
+ if (event != wimp_USER_MESSAGE_ACKNOWLEDGE)
+ ro_uri_message_received(message);
+ break;
+ case message_URI_RETURN_RESULT:
+ ro_uri_bounce(message);
+ break;
+ case message_INET_SUITE_OPEN_URL:
+ if (event == wimp_USER_MESSAGE_ACKNOWLEDGE) {
+ ro_url_bounce(message);
+ }
+ else {
+ ro_url_message_received(message);
+ }
+ break;
+#ifdef WITH_PLUGIN
+ case message_PLUG_IN_OPENING:
+ plugin_opening(message);
+ break;
+ case message_PLUG_IN_CLOSED:
+ plugin_closed(message);
+ break;
+ case message_PLUG_IN_RESHAPE_REQUEST:
+ plugin_reshape_request(message);
+ break;
+ case message_PLUG_IN_FOCUS:
+ break;
+ case message_PLUG_IN_URL_ACCESS:
+ plugin_url_access(message);
+ break;
+ case message_PLUG_IN_STATUS:
+ plugin_status(message);
+ break;
+ case message_PLUG_IN_BUSY:
+ break;
+ case message_PLUG_IN_STREAM_NEW:
+ plugin_stream_new(message);
+ break;
+ case message_PLUG_IN_STREAM_WRITE:
+ break;
+ case message_PLUG_IN_STREAM_WRITTEN:
+ plugin_stream_written(message);
+ break;
+ case message_PLUG_IN_STREAM_DESTROY:
+ break;
+ case message_PLUG_IN_OPEN:
+ if (event == wimp_USER_MESSAGE_ACKNOWLEDGE)
+ plugin_open_msg(message);
+ break;
+ case message_PLUG_IN_CLOSE:
+ if (event == wimp_USER_MESSAGE_ACKNOWLEDGE)
+ plugin_close_msg(message);
+ break;
+ case message_PLUG_IN_RESHAPE:
+ case message_PLUG_IN_STREAM_AS_FILE:
+ case message_PLUG_IN_NOTIFY:
+ case message_PLUG_IN_ABORT:
+ case message_PLUG_IN_ACTION:
+ break;
+#endif
+ case message_PRINT_SAVE:
+ if (event == wimp_USER_MESSAGE_ACKNOWLEDGE)
+ ro_print_save_bounce(message);
+ break;
+ case message_PRINT_ERROR:
+ ro_print_error(message);
+ break;
+ case message_PRINT_TYPE_ODD:
+ ro_print_type_odd(message);
+ break;
+ case message_HOTLIST_CHANGED:
+ ro_gui_hotlist_add_cleanup();
+ break;
+ case message_QUIT:
+ riscos_done = true;
+ break;
+ }
+}
+
+
+/**
+ * Process a Wimp_Poll event.
+ *
+ * \param event wimp event number
+ * \param block parameter block
+ */
+static void ro_gui_handle_event(wimp_event_no event, wimp_block *block)
+{
+ switch (event) {
+ case wimp_NULL_REASON_CODE:
+ ro_gui_throb();
+ ro_mouse_poll();
+ break;
+
+ case wimp_REDRAW_WINDOW_REQUEST:
+ ro_gui_wimp_event_redraw_window(&block->redraw);
+ break;
+
+ case wimp_OPEN_WINDOW_REQUEST:
+ ro_gui_open_window_request(&block->open);
+ break;
+
+ case wimp_CLOSE_WINDOW_REQUEST:
+ ro_gui_close_window_request(&block->close);
+ break;
+
+ case wimp_POINTER_LEAVING_WINDOW:
+ ro_mouse_pointer_leaving_window(&block->leaving);
+ break;
+
+ case wimp_POINTER_ENTERING_WINDOW:
+ ro_gui_wimp_event_pointer_entering_window(&block->entering);
+ break;
+
+ case wimp_MOUSE_CLICK:
+ ro_gui_wimp_event_mouse_click(&block->pointer);
+ break;
+
+ case wimp_USER_DRAG_BOX:
+ ro_mouse_drag_end(&block->dragged);
+ break;
+
+ case wimp_KEY_PRESSED:
+ ro_gui_keypress(&(block->key));
+ break;
+
+ case wimp_MENU_SELECTION:
+ ro_gui_menu_selection(&(block->selection));
+ break;
+
+ /* Scroll requests fall back to a generic handler because we
+ * might get these events for any window from a scroll-wheel.
+ */
+
+ case wimp_SCROLL_REQUEST:
+ if (!ro_gui_wimp_event_scroll_window(&(block->scroll)))
+ ro_gui_scroll(&(block->scroll));
+ break;
+
+ case wimp_USER_MESSAGE:
+ case wimp_USER_MESSAGE_RECORDED:
+ case wimp_USER_MESSAGE_ACKNOWLEDGE:
+ ro_gui_user_message(event, &(block->message));
+ break;
+ }
+}
+
+
+/**
+ * Poll the RISC OS wimp for events.
+ */
+static void riscos_poll(void)
+{
+ wimp_event_no event;
+ wimp_block block;
+ const wimp_poll_flags mask = wimp_MASK_LOSE | wimp_MASK_GAIN | wimp_SAVE_FP;
+ os_t track_poll_offset;
+
+ /* Poll wimp. */
+ xhourglass_off();
+ track_poll_offset = ro_mouse_poll_interval();
+ if (sched_active || (track_poll_offset > 0)) {
+ os_t t = os_read_monotonic_time();
+
+ if (track_poll_offset > 0) {
+ t += track_poll_offset;
+ } else {
+ t += 10;
+ }
+
+ if (sched_active && (sched_time - t) < 0) {
+ t = sched_time;
+ }
+
+ event = wimp_poll_idle(mask, &block, t, 0);
+ } else {
+ event = wimp_poll(wimp_MASK_NULL | mask, &block, 0);
+ }
+
+ xhourglass_on();
+ gui_last_poll = clock();
+ ro_gui_handle_event(event, &block);
+
+ /* Only run scheduled callbacks on a null poll
+ * We cannot do this in the null event handler, as that may be called
+ * from gui_multitask(). Scheduled callbacks must only be run from the
+ * top-level.
+ */
+ if (event == wimp_NULL_REASON_CODE) {
+ schedule_run();
+ }
+
+ ro_gui_window_update_boxes();
+}
+
+
+/**
+ * Handle Open_Window_Request events.
+ */
+void ro_gui_open_window_request(wimp_open *open)
+{
+ os_error *error;
+
+ if (ro_gui_wimp_event_open_window(open))
+ return;
+
+ error = xwimp_open_window(open);
+ if (error) {
+ LOG("xwimp_open_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+}
+
+
+/**
+ * source bounce callback.
+ */
+static void ro_gui_view_source_bounce(wimp_message *message)
+{
+ char *filename;
+ os_error *error;
+ char command[256];
+
+ /* run the file as text */
+ filename = ((wimp_full_message_data_xfer *)message)->file_name;
+ sprintf(command, "@RunType_FFF %s", filename);
+ error = xwimp_start_task(command, 0);
+ if (error) {
+ LOG("xwimp_start_task failed: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+}
+
+
+/**
+ * Send the source of a content to a text editor.
+ */
+void ro_gui_view_source(hlcache_handle *c)
+{
+ os_error *error;
+ char *temp_name;
+ wimp_full_message_data_xfer message;
+ int objtype;
+ bool done = false;
+
+ const char *source_data;
+ unsigned long source_size;
+
+ if (!c) {
+ ro_warn_user("MiscError", "No document source");
+ return;
+ }
+
+ source_data = content_get_source_data(c, &source_size);
+
+ if (!source_data) {
+ ro_warn_user("MiscError", "No document source");
+ return;
+ }
+
+ /* try to load local files directly. */
+ if (netsurf_nsurl_to_path(hlcache_handle_get_url(c), &temp_name) == NSERROR_OK) {
+ error = xosfile_read_no_path(temp_name, &objtype, 0, 0, 0, 0);
+ if ((!error) && (objtype == osfile_IS_FILE)) {
+ snprintf(message.file_name, 212, "%s", temp_name);
+ message.file_name[211] = '\0';
+ done = true;
+ }
+ free(temp_name);
+ }
+ if (!done) {
+ /* We cannot release the requested filename until after it
+ * has finished being used. As we can't easily find out when
+ * this is, we simply don't bother releasing it and simply
+ * allow it to be re-used next time NetSurf is started. The
+ * memory overhead from doing this is under 1 byte per
+ * filename. */
+ char *r;
+ char full_name[256];
+ const char *filename = filename_request();
+ if (!filename) {
+ ro_warn_user("NoMemory", 0);
+ return;
+ }
+
+ snprintf(full_name, 256, "%s/%s", TEMP_FILENAME_PREFIX,
+ filename);
+ full_name[255] = '\0';
+ r = __riscosify(full_name, 0, __RISCOSIFY_NO_SUFFIX,
+ message.file_name, 212, 0);
+ if (r == 0) {
+ LOG("__riscosify failed");
+ return;
+ }
+ message.file_name[211] = '\0';
+
+ error = xosfile_save_stamped(message.file_name,
+ ro_content_filetype(c),
+ (byte *) source_data,
+ (byte *) source_data + source_size);
+ if (error) {
+ LOG("xosfile_save_stamped failed: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ return;
+ }
+ }
+
+ /* begin the DataOpen protocol */
+ message.your_ref = 0;
+ message.size = 44 + ((strlen(message.file_name) + 4) & (~3u));
+ message.action = message_DATA_OPEN;
+ message.w = 0;
+ message.i = 0;
+ message.pos.x = 0;
+ message.pos.y = 0;
+ message.est_size = 0;
+ message.file_type = 0xfff;
+ ro_message_send_message(wimp_USER_MESSAGE_RECORDED,
+ (wimp_message*)&message, 0,
+ ro_gui_view_source_bounce);
+}
+
+
+/**
+ * Broadcast an URL that we can't handle.
+ */
+static nserror gui_launch_url(struct nsurl *url)
+{
+ /* Try ant broadcast */
+ ro_url_broadcast(nsurl_access(url));
+ return NSERROR_OK;
+}
+
+
+/**
+ * Choose the language to use.
+ */
+static void ro_gui_choose_language(void)
+{
+ /* if option_language exists and is valid, use that */
+ if (nsoption_charp(language)) {
+ char path[40];
+ if (2 < strlen(nsoption_charp(language)))
+ nsoption_charp(language)[2] = 0;
+ sprintf(path, "NetSurf:Resources.%s", nsoption_charp(language));
+
+ if (is_dir(path)) {
+ nsoption_setnull_charp(accept_language,
+ strdup(nsoption_charp(language)));
+ return;
+ }
+ nsoption_set_charp(language, NULL);
+ }
+
+ nsoption_set_charp(language, strdup(ro_gui_default_language()));
+ if (nsoption_charp(language) == NULL)
+ die("Out of memory");
+ nsoption_set_charp(accept_language, strdup(nsoption_charp(language)));
+ if (nsoption_charp(accept_language) == NULL)
+ die("Out of memory");
+}
+
+
+/**
+ * Display a warning for a serious problem (eg memory exhaustion).
+ *
+ * \param warning message key for warning message
+ * \param detail additional message, or 0
+ */
+nserror ro_warn_user(const char *warning, const char *detail)
+{
+ LOG("%s %s", warning, detail);
+
+ if (dialog_warning) {
+ char warn_buffer[300];
+ snprintf(warn_buffer, sizeof warn_buffer, "%s %s",
+ messages_get(warning),
+ detail ? detail : "");
+ warn_buffer[sizeof warn_buffer - 1] = 0;
+ ro_gui_set_icon_string(dialog_warning, ICON_WARNING_MESSAGE,
+ warn_buffer, true);
+ xwimp_set_icon_state(dialog_warning, ICON_WARNING_HELP,
+ wimp_ICON_DELETED, wimp_ICON_DELETED);
+ ro_gui_dialog_open(dialog_warning);
+ xos_bell();
+ } else {
+ /* probably haven't initialised (properly), use a
+ non-multitasking error box */
+ os_error error;
+ snprintf(error.errmess, sizeof error.errmess, "%s %s",
+ messages_get(warning),
+ detail ? detail : "");
+ error.errmess[sizeof error.errmess - 1] = 0;
+ xwimp_report_error_by_category(&error,
+ wimp_ERROR_BOX_OK_ICON |
+ wimp_ERROR_BOX_GIVEN_CATEGORY |
+ wimp_ERROR_BOX_CATEGORY_ERROR <<
+ wimp_ERROR_BOX_CATEGORY_SHIFT,
+ "NetSurf", "!netsurf",
+ (osspriteop_area *) 1, 0, 0);
+ }
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Display an error and exit.
+ *
+ * Should only be used during initialisation.
+ */
+void die(const char * const error)
+{
+ os_error warn_error;
+
+ LOG("%s", error);
+
+ warn_error.errnum = 1; /* \todo: reasonable ? */
+ strncpy(warn_error.errmess, messages_get(error),
+ sizeof(warn_error.errmess)-1);
+ warn_error.errmess[sizeof(warn_error.errmess)-1] = '\0';
+ xwimp_report_error_by_category(&warn_error,
+ wimp_ERROR_BOX_OK_ICON |
+ wimp_ERROR_BOX_GIVEN_CATEGORY |
+ wimp_ERROR_BOX_CATEGORY_ERROR <<
+ wimp_ERROR_BOX_CATEGORY_SHIFT,
+ "NetSurf", "!netsurf",
+ (osspriteop_area *) 1, 0, 0);
+ exit(EXIT_FAILURE);
+}
+
+
+/**
+ * Test whether it's okay to shutdown, prompting the user if not.
+ *
+ * \return true iff it's okay to shutdown immediately
+ */
+bool ro_gui_prequit(void)
+{
+ return ro_gui_download_prequit();
+}
+
+
+/**
+ * Generate a riscos path from one or more component elemnts.
+ *
+ * Constructs a complete path element from passed components. The
+ * second (and subsequent) components have a slash substituted for all
+ * riscos directory separators.
+ *
+ * If a string is allocated it must be freed by the caller.
+ *
+ * @param[in,out] str pointer to string pointer if this is NULL enough
+ * storage will be allocated for the complete path.
+ * @param[in,out] size The size of the space available if \a str not
+ * NULL on input and if not NULL set to the total
+ * output length on output.
+ * @param[in] nelm The number of elements.
+ * @param[in] ap The elements of the path as string pointers.
+ * @return NSERROR_OK and the complete path is written to str
+ * or error code on faliure.
+ */
+static nserror riscos_mkpath(char **str, size_t *size, size_t nelm, va_list ap)
+{
+ const char *elm[16];
+ size_t elm_len[16];
+ size_t elm_idx;
+ char *fname;
+ size_t fname_len = 0;
+ char *curp;
+ size_t idx;
+
+ /* check the parameters are all sensible */
+ if ((nelm == 0) || (nelm > 16)) {
+ return NSERROR_BAD_PARAMETER;
+ }
+ if ((*str != NULL) && (size == NULL)) {
+ /* if the caller is providing the buffer they must say
+ * how much space is available.
+ */
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ /* calculate how much storage we need for the complete path
+ * with all the elements.
+ */
+ for (elm_idx = 0; elm_idx < nelm; elm_idx++) {
+ elm[elm_idx] = va_arg(ap, const char *);
+ /* check the argument is not NULL */
+ if (elm[elm_idx] == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+ elm_len[elm_idx] = strlen(elm[elm_idx]);
+ fname_len += elm_len[elm_idx];
+ }
+ fname_len += nelm; /* allow for separators and terminator */
+
+ /* ensure there is enough space */
+ fname = *str;
+ if (fname != NULL) {
+ if (fname_len > *size) {
+ return NSERROR_NOSPACE;
+ }
+ } else {
+ fname = malloc(fname_len);
+ if (fname == NULL) {
+ return NSERROR_NOMEM;
+ }
+ }
+
+ /* copy the elements in with directory separator */
+ curp = fname;
+
+ /* first element is not altered */
+ memmove(curp, elm[0], elm_len[0]);
+ curp += elm_len[0];
+ /* ensure there is a delimiter */
+ if (curp[-1] != DIR_SEP) {
+ *curp = DIR_SEP;
+ curp++;
+ }
+
+ /* subsequent elemnts have slashes substituted with directory
+ * separators.
+ */
+ for (elm_idx = 1; elm_idx < nelm; elm_idx++) {
+ for (idx = 0; idx < elm_len[elm_idx]; idx++) {
+ if (elm[elm_idx][idx] == DIR_SEP) {
+ *curp = '/';
+ } else {
+ *curp = elm[elm_idx][idx];
+ }
+ curp++;
+ }
+ *curp = DIR_SEP;
+ curp++;
+ }
+ curp[-1] = 0; /* NULL terminate */
+
+ assert((curp - fname) <= (int)fname_len);
+
+ *str = fname;
+ if (size != NULL) {
+ *size = fname_len;
+ }
+
+ return NSERROR_OK;
+
+}
+
+
+/**
+ * Get the basename of a file using posix path handling.
+ *
+ * This gets the last element of a path and returns it. The returned
+ * element has all forward slashes translated into riscos directory
+ * separators.
+ *
+ * @param[in] path The path to extract the name from.
+ * @param[in,out] str Pointer to string pointer if this is NULL enough
+ * storage will be allocated for the path element.
+ * @param[in,out] size The size of the space available if \a
+ * str not NULL on input and set to the total
+ * output length on output.
+ * @return NSERROR_OK and the complete path is written to str
+ * or error code on faliure.
+ */
+static nserror riscos_basename(const char *path, char **str, size_t *size)
+{
+ const char *leafname;
+ char *fname;
+ char *temp;
+
+ if (path == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ leafname = strrchr(path, DIR_SEP);
+ if (!leafname) {
+ leafname = path;
+ } else {
+ leafname += 1;
+ }
+
+ fname = strdup(leafname);
+ if (fname == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ /** @todo check this leafname translation is actually required */
+ /* and s/\//\./g */
+ for (temp = fname; *temp != 0; temp++) {
+ if (*temp == '/') {
+ *temp = DIR_SEP;
+ }
+ }
+
+ *str = fname;
+ if (size != NULL) {
+ *size = strlen(fname);
+ }
+ return NSERROR_OK;
+}
+
+
+/**
+ * Ensure that all directory elements needed to store a filename exist.
+ *
+ * Given a path of x.y.z directories x and x.y will be created.
+ *
+ * @param fname The filename to ensure the path to exists.
+ * @return NSERROR_OK on success or error code on failure.
+ */
+static nserror riscos_mkdir_all(const char *fname)
+{
+ char *dname;
+ char *cur;
+
+ dname = strdup(fname);
+
+ cur = dname;
+ while ((cur = strchr(cur, '.'))) {
+ *cur = '\0';
+ xosfile_create_dir(dname, 0);
+ *cur++ = '.';
+ }
+
+ free(dname);
+
+ return NSERROR_OK;
+}
+
+/**
+ * Find screen size in OS units.
+ */
+void ro_gui_screen_size(int *width, int *height)
+{
+ *width = screen_info.width;
+ *height = screen_info.height;
+}
+
+
+/**
+ * Send the debug dump of a content to a text editor.
+ */
+void ro_gui_dump_browser_window(struct browser_window *bw)
+{
+ os_error *error;
+
+ /* open file for dump */
+ FILE *stream = fopen("<Wimp$ScrapDir>.WWW.NetSurf.dump", "w");
+ if (!stream) {
+ LOG("fopen: errno %i", errno);
+ ro_warn_user("SaveError", strerror(errno));
+ return;
+ }
+
+ browser_window_debug_dump(bw, stream, CONTENT_DEBUG_RENDER);
+
+ fclose(stream);
+
+ /* launch file in editor */
+ error = xwimp_start_task("Filer_Run <Wimp$ScrapDir>.WWW.NetSurf.dump",
+ 0);
+ if (error) {
+ LOG("xwimp_start_task failed: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+}
+
+
+static struct gui_file_table riscos_file_table = {
+ .mkpath = riscos_mkpath,
+ .basename = riscos_basename,
+ .nsurl_to_path = ro_nsurl_to_path,
+ .path_to_nsurl = ro_path_to_nsurl,
+ .mkdir_all = riscos_mkdir_all,
+};
+
+static struct gui_fetch_table riscos_fetch_table = {
+ .filetype = fetch_filetype,
+
+ .get_resource_url = gui_get_resource_url,
+ .mimetype = fetch_mimetype,
+};
+
+static struct gui_misc_table riscos_misc_table = {
+ .schedule = riscos_schedule,
+ .warning = ro_warn_user,
+
+ .quit = gui_quit,
+ .launch_url = gui_launch_url,
+ .cert_verify = gui_cert_verify,
+ .login = gui_401login_open,
+};
+
+
+static char *get_cachepath(void)
+{
+ char *cachedir;
+ char *cachepath = NULL;
+ nserror ret;
+
+ cachedir = getenv("Cache$Dir");
+ if ((cachedir == NULL) || (cachedir[0] == 0)) {
+ LOG("cachedir was null");
+ return NULL;
+ }
+ ret = netsurf_mkpath(&cachepath, NULL, 2, cachedir, "NetSurf");
+ if (ret != NSERROR_OK) {
+ return NULL;
+ }
+ return cachepath;
+}
+
+/**
+ * Normal entry point from RISC OS.
+ */
+int main(int argc, char** argv)
+{
+ char *cachepath;
+ char path[40];
+ int length;
+ os_var_type type;
+ int used = -1; /* slightly better with older OSLib versions */
+ os_error *error;
+ nserror ret;
+ struct netsurf_table riscos_table = {
+ .misc = &riscos_misc_table,
+ .window = riscos_window_table,
+ .clipboard = riscos_clipboard_table,
+ .download = riscos_download_table,
+ .fetch = &riscos_fetch_table,
+ .file = &riscos_file_table,
+ .utf8 = riscos_utf8_table,
+ .search = riscos_search_table,
+ .llcache = filesystem_llcache_table,
+ .bitmap = riscos_bitmap_table,
+ .layout = riscos_layout_table,
+ };
+
+ ret = netsurf_register(&riscos_table);
+ if (ret != NSERROR_OK) {
+ die("NetSurf operation table failed registration");
+ }
+
+ /* Consult NetSurf$Logging environment variable to decide if logging
+ * is required. */
+ error = xos_read_var_val_size("NetSurf$Logging", 0, os_VARTYPE_STRING,
+ &used, NULL, &type);
+ if (error != NULL || type != os_VARTYPE_STRING || used != -2) {
+ verbose_log = true;
+ } else {
+ char logging_env[2];
+ error = xos_read_var_val("NetSurf$Logging", logging_env,
+ sizeof(logging_env), 0, os_VARTYPE_STRING,
+ &used, NULL, &type);
+ if (error != NULL || logging_env[0] != '0') {
+ verbose_log = true;
+ } else {
+ verbose_log = false;
+ }
+ }
+
+ /* initialise logging. Not fatal if it fails but not much we
+ * can do about it either.
+ */
+ nslog_init(nslog_stream_configure, &argc, argv);
+
+ /* user options setup */
+ ret = nsoption_init(set_defaults, &nsoptions, &nsoptions_default);
+ if (ret != NSERROR_OK) {
+ die("Options failed to initialise");
+ }
+ nsoption_read("NetSurf:Choices", NULL);
+ nsoption_commandline(&argc, argv, NULL);
+
+ /* Choose the interface language to use */
+ ro_gui_choose_language();
+
+ /* select language-specific Messages */
+ if (((length = snprintf(path,
+ sizeof(path),
+ "NetSurf:Resources.%s.Messages",
+ nsoption_charp(language))) < 0) ||
+ (length >= (int)sizeof(path))) {
+ die("Failed to locate Messages resource.");
+ }
+
+ /* initialise messages */
+ messages_add_from_file(path);
+
+ /* obtain cache path */
+ cachepath = get_cachepath();
+
+ /* common initialisation */
+ ret = netsurf_init(cachepath);
+ free(cachepath);
+ if (ret != NSERROR_OK) {
+ die("NetSurf failed to initialise core");
+ }
+
+ artworks_init();
+ draw_init();
+ sprite_init();
+
+ /* Load some extra RISC OS specific Messages */
+ messages_add_from_file("NetSurf:Resources.LangNames");
+
+ ret = gui_init(argc, argv);
+ if (ret != NSERROR_OK) {
+ ro_warn_user(messages_get_errorcode(ret), 0);
+ }
+
+ while (!riscos_done) {
+ riscos_poll();
+ }
+
+ netsurf_exit();
+
+ return 0;
+}
diff --git a/frontends/riscos/gui.h b/frontends/riscos/gui.h
new file mode 100644
index 000000000..624f9e2fb
--- /dev/null
+++ b/frontends/riscos/gui.h
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2004 Andrew Timmins <atimmins@blueyonder.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_RISCOS_GUI_H_
+#define _NETSURF_RISCOS_GUI_H_
+
+#include <oslib/wimp.h>
+
+#define RISCOS5 0xAA
+
+#define THUMBNAIL_WIDTH 100
+#define THUMBNAIL_HEIGHT 86
+
+/* The maximum size for user-editable URLs in the RISC OS GUI. */
+
+#define RO_GUI_MAX_URL_SIZE 2048
+
+extern int os_version;
+
+extern const char * NETSURF_DIR;
+
+struct toolbar;
+struct status_bar;
+struct plotter_table;
+struct gui_window;
+struct tree;
+struct node;
+struct history;
+struct css_style;
+struct ssl_cert_info;
+struct nsurl;
+struct hlcache_handle;
+
+enum gui_pointer_shape;
+
+extern wimp_t task_handle; /**< RISC OS wimp task handle. */
+
+extern wimp_w dialog_info, dialog_saveas, dialog_zoom, dialog_pageinfo,
+ dialog_objinfo, dialog_tooltip, dialog_warning, dialog_openurl,
+ dialog_folder, dialog_entry, dialog_url_complete,
+ dialog_search, dialog_print, dialog_theme_install;
+extern wimp_w current_menu_window;
+extern bool current_menu_open;
+extern wimp_menu *recent_search_menu; /* search.c */
+extern wimp_w history_window;
+extern bool gui_redraw_debug;
+extern osspriteop_area *gui_sprites;
+extern bool dialog_folder_add, dialog_entry_add, hotlist_insert;
+extern bool print_active, print_text_black;
+extern bool no_font_blending;
+
+typedef enum { GUI_DRAG_NONE, GUI_DRAG_DOWNLOAD_SAVE, GUI_DRAG_SAVE }
+ ro_gui_drag_type;
+
+extern ro_gui_drag_type gui_current_drag_type;
+
+extern bool riscos_done;
+
+/** RISC OS data for a browser window. */
+struct gui_window {
+ /** Associated platform-independent browser window data. */
+ struct browser_window *bw;
+
+ struct toolbar *toolbar; /**< Toolbar, or 0 if not present. */
+ struct status_bar *status_bar; /**< Status bar, or 0 if not present. */
+
+ wimp_w window; /**< RISC OS window handle. */
+
+ int old_width; /**< Width when last opened / os units. */
+ int old_height; /**< Height when last opened / os units. */
+ bool update_extent; /**< Update the extent on next opening */
+ bool active; /**< Whether the throbber should be active */
+
+ char title[256]; /**< Buffer for window title. */
+
+ int iconise_icon; /**< ID number of icon when window is iconised */
+
+ char validation[12]; /**< Validation string for colours */
+
+ float scale; /**< Browser window scale */
+
+ /** Options. */
+ struct {
+ bool buffer_animations; /**< Use screen buffering for animations. */
+ bool buffer_everything; /**< Use screen buffering for everything. */
+ } option;
+
+ struct gui_window *prev; /**< Previous in linked list. */
+ struct gui_window *next; /**< Next in linked list. */
+};
+
+
+extern struct gui_window *ro_gui_current_redraw_gui;
+
+
+/* in gui.c */
+void ro_gui_open_window_request(wimp_open *open);
+void ro_gui_screen_size(int *width, int *height);
+void ro_gui_view_source(struct hlcache_handle *c);
+void ro_gui_dump_browser_window(struct browser_window *bw);
+void ro_gui_drag_box_start(wimp_pointer *pointer);
+bool ro_gui_prequit(void);
+const char *ro_gui_default_language(void);
+nserror ro_warn_user(const char *warning, const char *detail);
+
+/**
+ * Cause an abnormal program termination.
+ *
+ * \note This never returns and is intended to terminate without any cleanup.
+ *
+ * \param error The message to display to the user.
+ */
+void die(const char * const error) __attribute__ ((noreturn));
+
+/* in download.c */
+void ro_gui_download_init(void);
+void ro_gui_download_datasave_ack(wimp_message *message);
+bool ro_gui_download_prequit(void);
+extern struct gui_download_table *riscos_download_table;
+
+/* in 401login.c */
+void ro_gui_401login_init(void);
+void gui_401login_open(struct nsurl *url, const char *realm,
+ nserror (*cb)(bool proceed, void *pw), void *cbpw);
+
+/* in window.c */
+void ro_gui_window_set_scale(struct gui_window *g, float scale);
+bool ro_gui_window_dataload(struct gui_window *g, wimp_message *message);
+void ro_gui_window_mouse_at(wimp_pointer *pointer, void *data);
+void ro_gui_window_iconise(struct gui_window *g,
+ wimp_full_message_window_info *wi);
+bool ro_gui_toolbar_dataload(struct gui_window *g, wimp_message *message);
+void ro_gui_window_redraw_all(void);
+void ro_gui_window_update_boxes(void);
+void ro_gui_window_quit(void);
+/* void ro_gui_window_close_all(void); */
+#define ro_gui_window_close_all ro_gui_window_quit /* no need for a separate fn */
+void ro_gui_throb(void);
+void ro_gui_window_default_options(struct gui_window *gui);
+struct gui_window *ro_gui_window_lookup(wimp_w window);
+struct gui_window *ro_gui_toolbar_lookup(wimp_w window);
+bool ro_gui_window_to_window_pos(struct gui_window *g, int x, int y,
+ os_coord *pos);
+bool ro_gui_window_to_screen_pos(struct gui_window *g, int x, int y,
+ os_coord *pos);
+enum browser_mouse_state ro_gui_mouse_click_state(wimp_mouse_state buttons,
+ wimp_icon_flags type);
+enum browser_mouse_state ro_gui_mouse_drag_state(wimp_mouse_state buttons,
+ wimp_icon_flags type);
+bool ro_gui_shift_pressed(void);
+bool ro_gui_ctrl_pressed(void);
+bool ro_gui_alt_pressed(void);
+void gui_window_set_pointer(struct gui_window *g, enum gui_pointer_shape shape);
+
+/* in history.c */
+void ro_gui_history_init(void);
+void ro_gui_history_open(struct gui_window *g, bool pointer);
+
+/* in schedule.c */
+extern bool sched_active;
+extern os_t sched_time;
+
+/**
+ * Process events up to current time.
+ */
+bool schedule_run(void);
+
+/**
+ * Schedule a callback.
+ *
+ * \param t interval before the callback should be made in ms
+ * \param callback callback function
+ * \param p user parameter, passed to callback function
+ *
+ * The callback function will be called as soon as possible after t ms have
+ * passed.
+ */
+nserror riscos_schedule(int t, void (*callback)(void *p), void *p);
+
+/* in search.c */
+void ro_gui_search_init(void);
+void ro_gui_search_prepare(struct browser_window *g);
+struct gui_search_table *riscos_search_table;
+
+/* in print.c */
+void ro_gui_print_init(void);
+void ro_gui_print_prepare(struct gui_window *g);
+
+/* in plotters.c */
+extern const struct plotter_table ro_plotters;
+extern int ro_plot_origin_x;
+extern int ro_plot_origin_y;
+
+/* in theme_install.c */
+bool ro_gui_theme_install_apply(wimp_w w);
+
+/* in sslcert.c */
+void gui_cert_verify(struct nsurl *url,
+ const struct ssl_cert_info *certs, unsigned long num,
+ nserror (*cb)(bool proceed, void *pw), void *cbpw);
+
+/* icon numbers */
+#define ICON_STATUS_RESIZE 0
+#define ICON_STATUS_TEXT 1
+
+#define ICON_SAVE_ICON 0
+#define ICON_SAVE_PATH 1
+#define ICON_SAVE_OK 2
+#define ICON_SAVE_CANCEL 3
+
+#define ICON_PAGEINFO_TITLE 0
+#define ICON_PAGEINFO_URL 1
+#define ICON_PAGEINFO_ENC 2
+#define ICON_PAGEINFO_TYPE 3
+#define ICON_PAGEINFO_ICON 4
+
+#define ICON_OBJINFO_URL 0
+#define ICON_OBJINFO_TARGET 1
+#define ICON_OBJINFO_TYPE 2
+#define ICON_OBJINFO_ICON 3
+
+#define ICON_WARNING_MESSAGE 0
+#define ICON_WARNING_CONTINUE 1
+#define ICON_WARNING_HELP 2
+
+#define ICON_SEARCH_TEXT 0
+#define ICON_SEARCH_CASE_SENSITIVE 1
+#define ICON_SEARCH_FIND_NEXT 2
+#define ICON_SEARCH_FIND_PREV 3
+#define ICON_SEARCH_CANCEL 4
+#define ICON_SEARCH_STATUS 5
+#define ICON_SEARCH_MENU 8
+#define ICON_SEARCH_SHOW_ALL 9
+
+#define ICON_THEME_INSTALL_MESSAGE 0
+#define ICON_THEME_INSTALL_INSTALL 1
+#define ICON_THEME_INSTALL_CANCEL 2
+
+#define ICON_OPENURL_URL 1
+#define ICON_OPENURL_CANCEL 2
+#define ICON_OPENURL_OPEN 3
+#define ICON_OPENURL_MENU 4
+
+#define ICON_ENTRY_NAME 1
+#define ICON_ENTRY_URL 3
+#define ICON_ENTRY_CANCEL 4
+#define ICON_ENTRY_OK 5
+#define ICON_ENTRY_RECENT 6
+
+#define ICON_FOLDER_NAME 1
+#define ICON_FOLDER_CANCEL 2
+#define ICON_FOLDER_OK 3
+
+#endif
diff --git a/frontends/riscos/gui/button_bar.c b/frontends/riscos/gui/button_bar.c
new file mode 100644
index 000000000..6ecd7cffa
--- /dev/null
+++ b/frontends/riscos/gui/button_bar.c
@@ -0,0 +1,1229 @@
+/*
+ * Copyright 2004, 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2011 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Button bars (implementation).
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <oslib/dragasprite.h>
+#include <oslib/os.h>
+#include <oslib/osspriteop.h>
+#include <oslib/wimp.h>
+#include <oslib/wimpspriteop.h>
+
+#include "utils/log.h"
+
+#include "riscos/gui/button_bar.h"
+#include "riscos/gui.h"
+#include "riscos/mouse.h"
+#include "riscos/theme.h"
+#include "riscos/wimp.h"
+
+#define BUTTONBAR_SPRITE_NAME_LENGTH 12
+#define BUTTONBAR_VALIDATION_LENGTH 40
+
+struct button_bar_button {
+ wimp_i icon;
+ bool shaded;
+ bool separator;
+
+ button_bar_action select_action;
+ button_bar_action adjust_action;
+
+ int x_pos, y_pos;
+ int x_size, y_size;
+
+ char sprite[BUTTONBAR_SPRITE_NAME_LENGTH];
+ char validation[BUTTONBAR_VALIDATION_LENGTH];
+ char opt_key;
+ const char *help_suffix;
+
+ struct button_bar_button *bar_next;
+ struct button_bar_button *next;
+};
+
+
+struct button_bar {
+ /** The applied theme (or NULL to use the default) */
+ struct theme_descriptor *theme;
+
+ /** The widget dimensions. */
+ int x_min, y_min;
+ int separator_width;
+ int vertical_offset;
+
+ bool separators;
+
+ /** The window details and bar position. */
+ wimp_w window;
+ os_box extent;
+ osspriteop_area *sprites;
+ int background;
+
+ bool hidden;
+
+ bool edit;
+ struct button_bar *edit_target;
+ struct button_bar *edit_source;
+ void (*edit_refresh)(void *);
+ void *edit_client_data;
+
+ /** The list of all the defined buttons. */
+
+ struct button_bar_button *buttons;
+
+ /** The list of the buttons in the current bar. */
+
+ struct button_bar_button *bar;
+};
+
+static char null_text_string[] = "";
+static char separator_name[] = "separator";
+
+static struct button_bar *drag_start = NULL;
+static char drag_opt = '\0';
+static bool drag_separator = false;
+
+/*
+ * Private function prototypes.
+ */
+
+static bool ro_gui_button_bar_place_buttons(struct button_bar *button_bar);
+static bool ro_gui_button_bar_icon_update(struct button_bar *button_bar);
+static bool ro_gui_button_bar_icon_resize(struct button_bar *button_bar);
+static void ro_gui_button_bar_drag_end(wimp_dragged *drag, void *data);
+static void ro_gui_button_bar_sync_editors(struct button_bar *target,
+ struct button_bar *source);
+static struct button_bar_button *ro_gui_button_bar_find_icon(
+ struct button_bar *button_bar, wimp_i icon);
+static struct button_bar_button *ro_gui_button_bar_find_opt_key(
+ struct button_bar *button_bar, char opt_key);
+static struct button_bar_button *ro_gui_button_bar_find_action(
+ struct button_bar *button_bar, button_bar_action action);
+static struct button_bar_button *ro_gui_button_bar_find_coords(
+ struct button_bar *button_bar, os_coord pos,
+ bool *separator, bool *right);
+
+/* This is an exported interface documented in button_bar.h */
+
+struct button_bar *ro_gui_button_bar_create(struct theme_descriptor *theme,
+ const struct button_bar_buttons buttons[])
+{
+ struct button_bar *button_bar;
+ struct button_bar_button *icon, *new_icon;
+ int def;
+
+ /* Allocate memory. */
+
+ button_bar = malloc(sizeof(struct button_bar));
+ if (button_bar == NULL) {
+ LOG("No memory for malloc()");
+ return NULL;
+ }
+
+ /* Set up default parameters. */
+
+ button_bar->theme = theme;
+ button_bar->sprites = ro_gui_theme_get_sprites(theme);
+ button_bar->background = wimp_COLOUR_VERY_LIGHT_GREY;
+
+ button_bar->x_min = -1;
+ button_bar->y_min = -1;
+ button_bar->separator_width = 0;
+ button_bar->vertical_offset = 0;
+
+ button_bar->separators = false;
+
+ button_bar->window = NULL;
+
+ button_bar->hidden = false;
+
+ button_bar->edit = false;
+ button_bar->edit_target = NULL;
+ button_bar->edit_source = NULL;
+ button_bar->edit_refresh = NULL;
+ button_bar->edit_client_data = NULL;
+
+ button_bar->buttons = NULL;
+
+ /* Process the button icon definitions */
+
+ icon = NULL;
+
+ for (def = 0; buttons[def].icon != NULL; def++) {
+ new_icon = malloc(sizeof(struct button_bar_button));
+ if (new_icon == NULL) {
+ break;
+ }
+
+ if (icon == NULL) {
+ button_bar->buttons = new_icon;
+ button_bar->bar = new_icon;
+ } else {
+ icon->next = new_icon;
+ icon->bar_next = new_icon;
+ }
+ icon = new_icon;
+ icon->next = NULL;
+ icon->bar_next = NULL;
+
+ strncpy(icon->sprite, buttons[def].icon,
+ BUTTONBAR_SPRITE_NAME_LENGTH);
+ snprintf(icon->validation, BUTTONBAR_VALIDATION_LENGTH,
+ "R5;S%s,p%s", icon->sprite, icon->sprite);
+
+ icon->icon = -1;
+ icon->shaded = false;
+ icon->separator = false;
+
+ icon->select_action = buttons[def].select;
+ icon->adjust_action = buttons[def].adjust;
+ icon->opt_key = buttons[def].opt_key;
+ icon->help_suffix = buttons[def].help;
+ }
+
+ /* Add a separator after the last entry. This will be lost if the
+ * buttons are subsequently set, but is used for the edit source bar.
+ */
+
+ if (icon != NULL)
+ icon->separator = true;
+
+ return button_bar;
+}
+
+
+/* This is an exported interface documented in button_bar.h */
+
+bool ro_gui_button_bar_link_editor(struct button_bar *target,
+ struct button_bar *source, void (* refresh)(void *),
+ void *client_data)
+{
+ if (target == NULL || source == NULL ||
+ target->edit_target != NULL ||
+ target->edit_source != NULL ||
+ source->edit_target != NULL ||
+ source->edit_source != NULL)
+ return false;
+
+ target->edit_source = source;
+ source->edit_target = target;
+
+ /* Store the callback data in the editor bar. */
+
+ source->edit_refresh = refresh;
+ source->edit_client_data = client_data;
+
+ return true;
+}
+
+
+/* This is an exported interface documented in button_bar.h */
+
+bool ro_gui_button_bar_rebuild(struct button_bar *button_bar,
+ struct theme_descriptor *theme, theme_style style,
+ wimp_w window, bool edit)
+{
+ struct button_bar_button *button;
+ int height;
+
+
+ if (button_bar == NULL)
+ return false;
+
+ button_bar->theme = theme;
+ button_bar->window = window;
+ button_bar->sprites = ro_gui_theme_get_sprites(theme);
+ button_bar->background = ro_gui_theme_get_style_element(theme, style,
+ THEME_ELEMENT_BACKGROUND);
+
+ button_bar->edit = edit;
+
+ height = 0;
+ button_bar->separator_width = 16;
+ ro_gui_wimp_get_sprite_dimensions(button_bar->sprites, separator_name,
+ &button_bar->separator_width, &height);
+
+ /* If the separator height is 0, then either the sprite really is
+ * zero pixels high or the default was used as no sprite was found.
+ * Either way, we don't have a separator.
+ */
+
+ button_bar->separators = (height == 0) ? false : true;
+
+ button = button_bar->buttons;
+
+ while (button != NULL) {
+ button->x_size = 0;
+ button->y_size = 0;
+ button->icon = -1;
+
+ ro_gui_wimp_get_sprite_dimensions(button_bar->sprites,
+ button->sprite,
+ &button->x_size, &button->y_size);
+
+ button = button->next;
+ }
+
+ if (!ro_gui_button_bar_place_buttons(button_bar))
+ return false;
+
+ if (button_bar->edit && button_bar->edit_target != NULL)
+ ro_gui_button_bar_sync_editors(button_bar->edit_target,
+ button_bar);
+
+ return ro_gui_button_bar_icon_update(button_bar);
+}
+
+
+/* This is an exported interface documented in button_bar.h */
+
+bool ro_gui_button_bar_arrange_buttons(struct button_bar *button_bar,
+ char order[])
+{
+ struct button_bar_button *button, *new;
+ int i;
+
+ if (button_bar == NULL || order == NULL)
+ return false;
+
+ /* Delete any existing button arrangement. */
+
+ button_bar->bar = NULL;
+
+ for (button = button_bar->buttons; button != NULL;
+ button = button->next) {
+ button->bar_next = NULL;
+ button->separator = false;
+ }
+
+ /* Parse the config string and link up the new buttons. */
+
+ button = NULL;
+
+ for (i = 0; order[i] != '\0'; i++) {
+ if (order[i] != '|') {
+ new = ro_gui_button_bar_find_opt_key(button_bar,
+ order[i]);
+
+ if (new != NULL) {
+ if (button == NULL)
+ button_bar->bar = new;
+ else
+ button->bar_next = new;
+
+ button = new;
+ }
+ } else {
+ if (button != NULL)
+ button->separator = true;
+ }
+ }
+
+ if (!ro_gui_button_bar_place_buttons(button_bar))
+ return false;
+
+ return ro_gui_button_bar_place_buttons(button_bar);
+}
+
+/**
+ * Place the buttons on a button bar, taking into account the button arrangement
+ * and the current theme, and update the bar extent details.
+ *
+ * \param *button_bar The button bar to update.
+ * \return true if successful; else false.
+ */
+
+bool ro_gui_button_bar_place_buttons(struct button_bar *button_bar)
+{
+ struct button_bar_button *button;
+ int x_pos, y_pos, height;
+
+ if (button_bar == NULL)
+ return false;
+
+ button = button_bar->bar;
+ x_pos = 0;
+ y_pos = 0;
+ height = 0;
+
+ while (button != NULL) {
+ button->x_pos = x_pos;
+ button->y_pos = y_pos;
+
+ x_pos += button->x_size;
+ if (button->separator)
+ x_pos += button_bar->separator_width;
+
+ if (button->y_size > height)
+ height = button->y_size;
+
+ button = button->bar_next;
+ }
+
+ button_bar->x_min = x_pos;
+ button_bar->y_min = height;
+
+ return true;
+}
+
+
+/* This is an exported interface documented in button_bar.h */
+
+void ro_gui_button_bar_destroy(struct button_bar *button_bar)
+{
+ struct button_bar_button *button;
+
+ if (button_bar == NULL)
+ return;
+
+ /* Free the button definitions. */
+
+ while (button_bar->buttons != NULL) {
+ button = button_bar->buttons;
+ button_bar->buttons = button->next;
+ free(button);
+ }
+
+ free(button_bar);
+}
+
+
+/* This is an exported interface documented in button_bar.h */
+
+bool ro_gui_button_bar_get_dims(struct button_bar *button_bar,
+ int *width, int *height)
+{
+ if (button_bar == NULL)
+ return false;
+
+ if (button_bar->x_min != -1 && button_bar->y_min != -1) {
+ if (width != NULL)
+ *width = button_bar->x_min;
+ if (height != NULL)
+ *height = button_bar->y_min;
+
+ return true;
+ }
+
+ return false;
+}
+
+
+/* This is an exported interface documented in button_bar.h */
+
+bool ro_gui_button_bar_set_extent(struct button_bar *button_bar,
+ int x0, int y0, int x1, int y1)
+{
+ if (button_bar == NULL)
+ return false;
+
+ if ((x1 - x0) < button_bar->x_min || (y1 - y0) < button_bar->y_min)
+ return false;
+
+ if (button_bar->extent.x0 == x0 && button_bar->extent.y0 == y0 &&
+ button_bar->extent.x1 == x1 &&
+ button_bar->extent.y1 == y1)
+ return true;
+
+ /* Redraw the relevant bits of the toolbar. We can't optimise for
+ * stretching the X-extent, as this probably means the button
+ * arrangement has changed which necessitates a full redraw anyway.
+ */
+
+ if (button_bar->window != NULL) {
+ xwimp_force_redraw(button_bar->window,
+ button_bar->extent.x0, button_bar->extent.y0,
+ button_bar->extent.x1, button_bar->extent.y1);
+ xwimp_force_redraw(button_bar->window, x0, y0, x1, y1);
+ }
+
+ button_bar->extent.x0 = x0;
+ button_bar->extent.y0 = y0;
+ button_bar->extent.x1 = x1;
+ button_bar->extent.y1 = y1;
+
+ if ((y1 - y0) > button_bar->y_min)
+ button_bar->vertical_offset =
+ ((y1 - y0) - button_bar->y_min) / 2;
+ else
+ button_bar->vertical_offset = 0;
+
+ return ro_gui_button_bar_icon_resize(button_bar);
+}
+
+
+/**
+ * Update the icons on a button bar, creating or deleting them from the window
+ * as necessary.
+ */
+
+bool ro_gui_button_bar_icon_update(struct button_bar *button_bar)
+{
+ wimp_icon_create icon;
+ struct button_bar_button *button, *b;
+ os_error *error;
+
+
+ if (button_bar == NULL || button_bar->window == NULL)
+ return (button_bar == NULL) ? false : true;
+
+ button = button_bar->buttons;
+
+ while (button != NULL) {
+ bool on_bar = false;
+
+ /* Check if the icon is currently on the bar. */
+
+ for (b = button_bar->bar; b != NULL; b = b->bar_next) {
+ if (b == button) {
+ on_bar = true;
+ break;
+ }
+ }
+
+ if (on_bar && !button_bar->hidden && button->icon == -1) {
+ icon.w = button_bar->window;
+ icon.icon.extent.x0 = 0;
+ icon.icon.extent.y0 = 0;
+ icon.icon.extent.x1 = 0;
+ icon.icon.extent.y1 = 0;
+ icon.icon.flags = wimp_ICON_TEXT | wimp_ICON_SPRITE |
+ wimp_ICON_INDIRECTED |
+ wimp_ICON_HCENTRED |
+ wimp_ICON_VCENTRED |
+ (button_bar->background
+ << wimp_ICON_BG_COLOUR_SHIFT);
+ icon.icon.data.indirected_text.size = 1;
+
+ /* We don't actually shade buttons unless there's no
+ * editor active or this is the source bar.
+ */
+
+ if (button->shaded && (!button_bar->edit ||
+ button_bar->edit_target != NULL))
+ icon.icon.flags |= wimp_ICON_SHADED;
+
+ if (button_bar->edit)
+ icon.icon.flags |= (wimp_BUTTON_CLICK_DRAG <<
+ wimp_ICON_BUTTON_TYPE_SHIFT);
+ else
+ icon.icon.flags |= (wimp_BUTTON_CLICK <<
+ wimp_ICON_BUTTON_TYPE_SHIFT);
+
+ icon.icon.data.indirected_text.text = null_text_string;
+ icon.icon.data.indirected_text.validation =
+ button->validation;
+
+ error = xwimp_create_icon(&icon, &button->icon);
+ if (error) {
+ LOG("xwimp_create_icon: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ button->icon = -1;
+ return false;
+ }
+ } else if ((!on_bar || button_bar->hidden)
+ && button->icon != -1) {
+ error = xwimp_delete_icon(button_bar->window,
+ button->icon);
+ if (error != NULL) {
+ LOG("xwimp_delete_icon: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ button->icon = -1;
+ }
+
+ button = button->next;
+ }
+
+ return ro_gui_button_bar_icon_resize(button_bar);
+}
+
+
+/**
+ * Position the icons in the button bar to take account of the currently
+ * configured extent.
+ *
+ * \param *button_bar The button bar to update.
+ * \return true if successful; else false.
+ */
+
+bool ro_gui_button_bar_icon_resize(struct button_bar *button_bar)
+{
+ os_error *error;
+ struct button_bar_button *button;
+
+ if (button_bar == NULL || button_bar->hidden)
+ return (button_bar == NULL) ? false : true;
+
+ /* Reposition all the icons. */
+
+ button = button_bar->bar;
+
+ while (button != NULL) {
+ if(button->icon != -1) {
+ error = xwimp_resize_icon(button_bar->window,
+ button->icon,
+ button_bar->extent.x0 + button->x_pos,
+ button_bar->extent.y0 +
+ button_bar->vertical_offset +
+ button->y_pos,
+ button_bar->extent.x0 + button->x_pos +
+ button->x_size,
+ button_bar->extent.y0 +
+ button_bar->vertical_offset +
+ button->y_pos +
+ button->y_size);
+ if (error != NULL) {
+ LOG("xwimp_resize_icon: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ button->icon = -1;
+ return false;
+ }
+ }
+
+ button = button->bar_next;
+ }
+
+ return true;
+}
+
+
+/* This is an exported interface documented in button_bar.h */
+
+bool ro_gui_button_bar_hide(struct button_bar *button_bar, bool hide)
+{
+ if (button_bar == NULL || button_bar->hidden == hide)
+ return (button_bar == NULL) ? false : true;
+
+ button_bar->hidden = hide;
+
+ return ro_gui_button_bar_icon_update(button_bar);
+}
+
+
+/* This is an exported interface documented in button_bar.h */
+
+bool ro_gui_button_bar_shade_button(struct button_bar *button_bar,
+ button_bar_action action, bool shaded)
+{
+ struct button_bar_button *button;
+
+ if (button_bar == NULL)
+ return false;
+
+ button = ro_gui_button_bar_find_action(button_bar, action);
+ if (button == NULL)
+ return false;
+
+ if (button->shaded == shaded)
+ return true;
+
+ button->shaded = shaded;
+
+ /* We don't actually shade buttons unless there's no editor active
+ * or this is the source bar.
+ */
+
+ if (button->icon != -1 &&
+ (!button_bar->edit || button_bar->edit_target != NULL))
+ ro_gui_set_icon_shaded_state(button_bar->window, button->icon,
+ shaded);
+
+ return true;
+}
+
+
+/* This is an exported interface documented in button_bar.h */
+
+void ro_gui_button_bar_redraw(struct button_bar *button_bar,
+ wimp_draw *redraw)
+{
+ wimp_icon icon;
+ struct button_bar_button *button;
+
+ /* Test for a valid button bar, and then check that the redraw box
+ * coincides with the bar's extent.
+ */
+
+ if (button_bar == NULL || button_bar->hidden ||
+ (redraw->clip.x0 - (redraw->box.x0 - redraw->xscroll))
+ > button_bar->extent.x1 ||
+ (redraw->clip.y0 - (redraw->box.y1 - redraw->yscroll))
+ > button_bar->extent.y1 ||
+ (redraw->clip.x1 - (redraw->box.x0 - redraw->xscroll))
+ < button_bar->extent.x0 ||
+ (redraw->clip.y1 - (redraw->box.y1 - redraw->yscroll))
+ < button_bar->extent.y0 ||
+ (!button_bar->edit && !button_bar->separators))
+ return;
+
+ icon.flags = wimp_ICON_SPRITE | wimp_ICON_INDIRECTED |
+ wimp_ICON_HCENTRED | wimp_ICON_VCENTRED;
+ if (button_bar->edit)
+ icon.flags |= wimp_ICON_BORDER | wimp_COLOUR_DARK_GREY <<
+ wimp_ICON_FG_COLOUR_SHIFT;
+ if (!button_bar->separators)
+ icon.flags |= wimp_ICON_FILLED | wimp_COLOUR_LIGHT_GREY <<
+ wimp_ICON_BG_COLOUR_SHIFT;
+ icon.data.indirected_sprite.id = (osspriteop_id) separator_name;
+ icon.data.indirected_sprite.area = button_bar->sprites;
+ icon.data.indirected_sprite.size = 12;
+ icon.extent.y0 = button_bar->extent.y0 + button_bar->vertical_offset;
+ icon.extent.y1 = icon.extent.y0 + button_bar->y_min;
+
+ for (button = button_bar->bar; button != NULL;
+ button = button->bar_next) {
+ if (button->separator) {
+ icon.extent.x0 = button_bar->extent.x0 +
+ button->x_pos + button->x_size;
+ icon.extent.x1 = icon.extent.x0 +
+ button_bar->separator_width;
+ xwimp_plot_icon(&icon);
+ }
+ }
+}
+
+
+/* This is an exported interface documented in button_bar.h */
+
+bool ro_gui_button_bar_click(struct button_bar *button_bar,
+ wimp_pointer *pointer, wimp_window_state *state,
+ button_bar_action *action)
+{
+ struct button_bar_button *button;
+ os_coord pos;
+ os_box box;
+
+ if (button_bar == NULL || button_bar->hidden)
+ return false;
+
+ /* Check that the click was within our part of the window. */
+
+ pos.x = pointer->pos.x - state->visible.x0 + state->xscroll;
+ pos.y = pointer->pos.y - state->visible.y1 + state->yscroll;
+
+ if (pos.x < button_bar->extent.x0 || pos.x > button_bar->extent.x1 ||
+ pos.y < button_bar->extent.y0 ||
+ pos.y > button_bar->extent.y1)
+ return false;
+
+ if (button_bar->edit && pointer->buttons == wimp_DRAG_SELECT) {
+ /* This is an editor click, so we need to check for drags on
+ * icons (buttons) and work area (separators).
+ */
+
+ button = ro_gui_button_bar_find_coords(button_bar, pos,
+ &drag_separator, NULL);
+
+ if (button != NULL && (!button->shaded || drag_separator ||
+ button_bar->edit_source != NULL)) {
+ char *sprite;
+ os_error *error;
+
+ drag_start = button_bar;
+ drag_opt = button->opt_key;
+
+ if (drag_separator) {
+ box.x0 = pointer->pos.x -
+ button_bar->separator_width / 2;
+ box.x1 = box.x0 + button_bar->separator_width;
+ sprite = separator_name;
+ } else {
+ box.x0 = pointer->pos.x - button->x_size / 2;
+ box.x1 = box.x0 + button->x_size;
+ sprite = button->sprite;
+ }
+
+ box.y0 = pointer->pos.y - button->y_size / 2;
+ box.y1 = box.y0 + button->y_size;
+
+ error = xdragasprite_start(dragasprite_HPOS_CENTRE |
+ dragasprite_VPOS_CENTRE |
+ dragasprite_BOUND_SPRITE |
+ dragasprite_BOUND_TO_WINDOW |
+ dragasprite_DROP_SHADOW,
+ button_bar->sprites,
+ sprite, &box, NULL);
+ if (error)
+ LOG("xdragasprite_start: 0x%x: %s", error->errnum, error->errmess);
+
+ ro_mouse_drag_start(ro_gui_button_bar_drag_end,
+ NULL, NULL, NULL);
+
+
+ return true;
+ }
+
+ } else if (!button_bar->edit && pointer->i != -1 &&
+ (pointer->buttons == wimp_CLICK_SELECT ||
+ pointer->buttons == wimp_CLICK_ADJUST)) {
+ /* This isn't an editor click, so we're only interested in
+ * Select or Adjust clicks that occur on physical icons.
+ */
+
+ button = ro_gui_button_bar_find_icon(button_bar, pointer->i);
+
+ if (button != NULL) {
+ if (action != NULL) {
+ switch (pointer->buttons) {
+ case wimp_CLICK_SELECT:
+ *action = button->select_action;
+ break;
+ case wimp_CLICK_ADJUST:
+ *action = button->adjust_action;
+ break;
+ default:
+ break;
+ }
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/* This is an exported interface documented in button_bar.h */
+
+bool ro_gui_button_bar_help_suffix(struct button_bar *button_bar, wimp_i i,
+ os_coord *mouse, wimp_window_state *state,
+ wimp_mouse_state buttons, const char **suffix)
+{
+ os_coord pos;
+ struct button_bar_button *button;
+
+ if (button_bar == NULL || button_bar->hidden)
+ return false;
+
+ /* Check that the click was within our part of the window. */
+
+ pos.x = mouse->x - state->visible.x0 + state->xscroll;
+ pos.y = mouse->y - state->visible.y1 + state->yscroll;
+
+ if (pos.x < button_bar->extent.x0 || pos.x > button_bar->extent.x1 ||
+ pos.y < button_bar->extent.y0 ||
+ pos.y > button_bar->extent.y1)
+ return false;
+
+ /* Look up and return the help suffix assocuated with the button. */
+
+ button = ro_gui_button_bar_find_icon(button_bar, i);
+
+ if (button != NULL)
+ *suffix = button->help_suffix;
+ else
+ *suffix = "";
+
+ return true;
+}
+
+
+/**
+ * Terminate a drag event that was initiated by a button bar.
+ *
+ * \param *drag The drag event data.
+ * \param *data NULL data to satisfy callback syntax.
+ */
+
+void ro_gui_button_bar_drag_end(wimp_dragged *drag, void *data)
+{
+ struct button_bar *drag_end = NULL;
+ struct button_bar *source = NULL, *target = NULL;
+ struct button_bar_button *button, *drop, *previous;
+ bool right, separator;
+ wimp_window_state state;
+ wimp_pointer pointer;
+ os_coord pos;
+ os_error *error;
+
+ xdragasprite_stop();
+
+ if (drag_start == NULL)
+ return;
+
+ /* Sort out the window coordinates of the drag end. */
+
+ error = xwimp_get_pointer_info(&pointer);
+ if (error) {
+ LOG("xwimp_get_pointer_info: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ assert(pointer.w = drag_start->window);
+
+ state.w = drag_start->window;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ pos.x = pointer.pos.x - state.visible.x0 + state.xscroll;
+ pos.y = pointer.pos.y - state.visible.y1 + state.yscroll;
+
+ /* Work out the destination bar, and establish source and target. */
+
+ if (drag_start->edit_target != NULL) {
+ source = drag_start;
+ target = drag_start->edit_target;
+ if (pos.x >= target->extent.x0 && pos.x <= target->extent.x1 &&
+ pos.y >= target->extent.y0 &&
+ pos.y <= target->extent.y1)
+ drag_end = target;
+ } else if (drag_start->edit_source != NULL) {
+ source = drag_start->edit_source;
+ target = drag_start;
+ if (pos.x >= target->extent.x0 && pos.x <= target->extent.x1 &&
+ pos.y >= target->extent.y0 &&
+ pos.y <= target->extent.y1)
+ drag_end = target;
+ /* drag_end == source and drag_end == NULL are both equivalent
+ * as far as the following code are concerned, and we don't need
+ * to identify either case. */
+ }
+
+ button = ro_gui_button_bar_find_opt_key(target, drag_opt);
+ assert(button != NULL);
+
+ /* The drag finished in the target bar, so find out where. */
+
+ if (drag_end == target) {
+ drop = ro_gui_button_bar_find_coords(target, pos,
+ &separator, &right);
+ } else {
+ drop = NULL;
+ }
+
+ /* If the button is dropped on itself, there's no change and it's
+ * less messy to get out now.
+ */
+
+ if (drag_start == target && drag_end == target && button == drop) {
+ drag_start = NULL;
+ return;
+ }
+
+ /* The drag started in the target bar, so remove the dragged button. */
+
+ if (drag_start == target) {
+ if (drag_separator) {
+ button->separator = false;
+ } else if (target->bar == button) {
+ target->bar = button->bar_next;
+ } else {
+ for (previous = target->bar; previous != NULL &&
+ previous->bar_next != button;
+ previous = previous->bar_next);
+ assert(previous != NULL);
+ previous->bar_next = button->bar_next;
+ if (button->separator) // ??
+ previous->separator = true; // ??
+ }
+ }
+
+ /* The drag ended in the target bar, so add the dragged button in. */
+
+ if (drop != NULL) {
+ if (right) {
+ if (drag_separator) {
+ drop->separator = true;
+ } else {
+ button->bar_next = drop->bar_next;
+ drop->bar_next = button;
+ if (drop->separator && !separator) {
+ drop->separator = false;
+ button->separator = true;
+ } else {
+ button->separator = false;
+ }
+ }
+ } else if (target->bar == drop && !drag_separator) {
+ button->separator = false;
+ button->bar_next = target->bar;
+ target->bar = button;
+ } else if (target->bar != drop) {
+ for (previous = target->bar; previous != NULL &&
+ previous->bar_next != drop;
+ previous = previous->bar_next);
+ assert(previous != NULL);
+
+ if (drag_separator) {
+ previous->separator = true;
+ } else {
+ if (separator) {
+ previous->separator = false;
+ button->separator = true;
+ } else {
+ button->separator = false;
+ }
+ button->bar_next = previous->bar_next;
+ previous->bar_next = button;
+ }
+ }
+ }
+
+ /* Reposition the buttons and force our client to update. */
+
+ ro_gui_button_bar_place_buttons(target);
+ ro_gui_button_bar_icon_update(target);
+ ro_gui_button_bar_sync_editors(target, source);
+
+ xwimp_force_redraw(target->window,
+ target->extent.x0, target->extent.y0,
+ target->extent.x1, target->extent.y1);
+
+ if (source->edit_refresh != NULL)
+ source->edit_refresh(source->edit_client_data);
+
+ drag_start = NULL;
+}
+
+
+/**
+ * Synchronise the shading of a button bar editor source bar with the currently
+ * defined buttons in its target bar.
+ *
+ * \param *target The editor target bar.
+ * \param *source The editor source bar.
+ */
+
+void ro_gui_button_bar_sync_editors(struct button_bar *target,
+ struct button_bar *source)
+{
+ struct button_bar_button *sb, *tb;
+
+ if (source == NULL || target == NULL)
+ return;
+
+ /* Unshade all of the buttons in the source bar. */
+
+ for (sb = source->bar; sb != NULL; sb = sb->bar_next)
+ sb->shaded = false;
+
+ /* Step through the target bar and shade each corresponding
+ * button in the source.
+ */
+
+ for (tb = target->bar; tb != NULL; tb = tb->bar_next) {
+ sb = ro_gui_button_bar_find_opt_key(source, tb->opt_key);
+
+ if (sb != NULL)
+ sb->shaded = true;
+ }
+
+ /* Phyically shade the necessary buttons in the toolbar. */
+
+ for (sb = source->bar; sb != NULL; sb = sb->bar_next)
+ if (sb->icon != -1)
+ ro_gui_set_icon_shaded_state(source->window, sb->icon,
+ sb->shaded);
+}
+
+
+/* This is an exported interface documented in button_bar.h */
+
+char *ro_gui_button_bar_get_config(struct button_bar *button_bar)
+{
+ struct button_bar_button *button;
+ size_t size;
+ char *config;
+ int i;
+
+ if (button_bar == NULL)
+ return NULL;
+
+ for (size = 1, button = button_bar->bar; button != NULL;
+ button = button->bar_next) {
+ size++;
+ if (button->separator)
+ size++;
+ }
+
+ config = malloc(size);
+ if (config == NULL) {
+ LOG("No memory for malloc()");
+ ro_warn_user("NoMemory", 0);
+ return NULL;
+ }
+
+ for (i = 0, button = button_bar->bar; button != NULL;
+ button = button->bar_next) {
+ config[i++] = button->opt_key;
+ if (button->separator)
+ config[i++] = '|';
+ }
+
+ config[i] = '\0';
+
+ return config;
+}
+
+
+/**
+ * Find a button bar icon definition from an icon handle.
+ *
+ * \param *button_bar The button bar to use.
+ * \param icon The icon handle.
+ * \return Pointer to the button bar icon, or NULL.
+ */
+
+struct button_bar_button *ro_gui_button_bar_find_icon(
+ struct button_bar *button_bar, wimp_i icon)
+{
+ struct button_bar_button *button;
+
+ if (button_bar == NULL || icon == -1)
+ return NULL;
+
+ button = button_bar->buttons;
+
+ while (button != NULL && button->icon != icon)
+ button = button->next;
+
+ return button;
+}
+
+
+/**
+ * Find a button bar icon definition from an options key code.
+ *
+ * \param *button_bar The button bar to use.
+ * \param opt_key The option key character code.
+ * \return Pointer to the button bar icon, or NULL.
+ */
+
+struct button_bar_button *ro_gui_button_bar_find_opt_key(
+ struct button_bar *button_bar, char opt_key)
+{
+ struct button_bar_button *button;
+
+ if (button_bar == NULL)
+ return NULL;
+
+ button = button_bar->buttons;
+
+ while (button != NULL && button->opt_key != opt_key)
+ button = button->next;
+
+ return button;
+}
+
+
+/**
+ * Find a button bar icon definition from an action code.
+ *
+ * \param *button_bar The button bar to use.
+ * \param action The button action to find.
+ * \return Pointer to the button bar icon, or NULL.
+ */
+
+struct button_bar_button *ro_gui_button_bar_find_action(
+ struct button_bar *button_bar, button_bar_action action)
+{
+ struct button_bar_button *button;
+
+ if (button_bar == NULL)
+ return NULL;
+
+ button = button_bar->buttons;
+
+ while (button != NULL &&
+ button->select_action != action &&
+ button->adjust_action != action)
+ button = button->next;
+
+ return button;
+}
+
+
+/**
+ * Find a button bar icon definition from coordinates.
+ *
+ * \param *button_bar The button bar to use.
+ * \param pos The coordinates to find, work area relative.
+ * \param *separator Returns true if the associated separator was
+ * matched; else false.
+ * \param *right Returns true if the coordinates were in the
+ * right hand side of the target; else false.
+ * \return Pointer to the button bar icon, or NULL.
+ */
+
+struct button_bar_button *ro_gui_button_bar_find_coords(
+ struct button_bar *button_bar, os_coord pos,
+ bool *separator, bool *right)
+{
+ struct button_bar_button *button;
+
+ if (button_bar == NULL)
+ return NULL;
+
+ button = button_bar->bar;
+
+ while (button != NULL) {
+ /* Match button extents. */
+ int x0, y0, x1, y1;
+
+ x0 = button_bar->extent.x0 + button->x_pos;
+ y0 = button_bar->extent.y0 + button->y_pos;
+ x1 = x0 + button->x_size;
+ y1 = y0 + button->y_size;
+
+ if (pos.x > x0 && pos.y > y0 && pos.x < x1 && pos.y < y1) {
+ if (separator != NULL)
+ *separator = false;
+
+ if (right != NULL)
+ *right = (pos.x > x0 + button->x_size/2) ?
+ true : false;
+ return button;
+ }
+
+ x0 = x1;
+ x1 = x0 + button_bar->separator_width;
+
+ /* Match separator extents. */
+
+ if (pos.x > x0 && pos.y > y0 && pos.x < x1 && pos.y < y1 &&
+ button->separator) {
+ if (separator != NULL)
+ *separator = true;
+
+ if (right != NULL)
+ *right = (x0 + button_bar->separator_width/2) ?
+ true : false;
+ return button;
+ }
+
+ button = button->bar_next;
+ }
+
+ return NULL;
+}
+
diff --git a/frontends/riscos/gui/button_bar.h b/frontends/riscos/gui/button_bar.h
new file mode 100644
index 000000000..a1f7e8b9f
--- /dev/null
+++ b/frontends/riscos/gui/button_bar.h
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2011 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Button bars (interface).
+ */
+
+#ifndef _NETSURF_RISCOS_BUTTONBAR_H_
+#define _NETSURF_RISCOS_BUTTONBAR_H_
+
+#include <stdbool.h>
+#include "riscos/theme.h"
+
+/* A list of possible toolbar actions. */
+
+typedef enum {
+ TOOLBAR_BUTTON_NONE = 0, /* Special case: no action */
+ TOOLBAR_BUTTON_BACK,
+ TOOLBAR_BUTTON_BACK_NEW,
+ TOOLBAR_BUTTON_UP,
+ TOOLBAR_BUTTON_UP_NEW,
+ TOOLBAR_BUTTON_FORWARD,
+ TOOLBAR_BUTTON_FORWARD_NEW,
+ TOOLBAR_BUTTON_STOP,
+ TOOLBAR_BUTTON_RELOAD,
+ TOOLBAR_BUTTON_RELOAD_ALL,
+ TOOLBAR_BUTTON_HOME,
+ TOOLBAR_BUTTON_HISTORY_LOCAL,
+ TOOLBAR_BUTTON_HISTORY_GLOBAL,
+ TOOLBAR_BUTTON_SAVE_SOURCE,
+ TOOLBAR_BUTTON_SAVE_COMPLETE,
+ TOOLBAR_BUTTON_PRINT,
+ TOOLBAR_BUTTON_BOOKMARK_OPEN,
+ TOOLBAR_BUTTON_BOOKMARK_ADD,
+ TOOLBAR_BUTTON_SCALE,
+ TOOLBAR_BUTTON_SEARCH,
+ TOOLBAR_BUTTON_DELETE,
+ TOOLBAR_BUTTON_EXPAND,
+ TOOLBAR_BUTTON_COLLAPSE,
+ TOOLBAR_BUTTON_OPEN,
+ TOOLBAR_BUTTON_CLOSE,
+ TOOLBAR_BUTTON_LAUNCH,
+ TOOLBAR_BUTTON_CREATE
+} button_bar_action;
+
+/* Button bar button source definitions.
+ *
+ * Help tokens are added to the help prefix for the given toolbar by the
+ * help system, and correspond to the hard-coded icon numbers that were
+ * assigned to the different buttons in the original toolbar implementation.
+ * If the Messages file can be updated, these can change to something more
+ * meaningful.
+ */
+
+struct button_bar_buttons {
+ const char *icon; /**< The sprite used for the icon. */
+ button_bar_action select; /**< The action for select clicks. */
+ button_bar_action adjust; /**< The action for Adjust clicks. */
+ const char opt_key; /**< The char used in option strings. */
+ const char *help; /**< The interactive help token. */
+};
+
+/* \TODO -- Move these to the correct modules.
+ */
+
+static const struct button_bar_buttons brower_toolbar_buttons[] = {
+ {"back", TOOLBAR_BUTTON_BACK, TOOLBAR_BUTTON_BACK_NEW, '0', "0"},
+ {"up", TOOLBAR_BUTTON_UP, TOOLBAR_BUTTON_UP_NEW, 'b', "11"},
+ {"forward", TOOLBAR_BUTTON_FORWARD, TOOLBAR_BUTTON_FORWARD_NEW, '1', "1"},
+ {"stop", TOOLBAR_BUTTON_STOP, TOOLBAR_BUTTON_NONE, '2', "2"},
+ {"reload", TOOLBAR_BUTTON_RELOAD, TOOLBAR_BUTTON_RELOAD_ALL, '3', "3"},
+ {"home", TOOLBAR_BUTTON_HOME, TOOLBAR_BUTTON_NONE, '4', "4"},
+ {"history", TOOLBAR_BUTTON_HISTORY_LOCAL, TOOLBAR_BUTTON_HISTORY_GLOBAL, '5', "5"},
+ {"save", TOOLBAR_BUTTON_SAVE_SOURCE, TOOLBAR_BUTTON_SAVE_COMPLETE, '6', "6"},
+ {"print", TOOLBAR_BUTTON_PRINT, TOOLBAR_BUTTON_NONE, '7', "7"},
+ {"hotlist", TOOLBAR_BUTTON_BOOKMARK_OPEN, TOOLBAR_BUTTON_BOOKMARK_ADD, '8', "8"},
+ {"scale", TOOLBAR_BUTTON_SCALE, TOOLBAR_BUTTON_NONE, '9', "9"},
+ {"search", TOOLBAR_BUTTON_SEARCH, TOOLBAR_BUTTON_NONE, 'a', "10"},
+ {NULL, TOOLBAR_BUTTON_NONE, TOOLBAR_BUTTON_NONE, '\0', ""}
+};
+
+static const struct button_bar_buttons cookies_toolbar_buttons[] = {
+ {"delete", TOOLBAR_BUTTON_DELETE, TOOLBAR_BUTTON_NONE, '0', "0"},
+ {"expand", TOOLBAR_BUTTON_EXPAND, TOOLBAR_BUTTON_COLLAPSE, '1', "1"},
+ {"open", TOOLBAR_BUTTON_OPEN, TOOLBAR_BUTTON_CLOSE, '2', "2"},
+ {NULL, TOOLBAR_BUTTON_NONE, TOOLBAR_BUTTON_NONE, '\0', ""}
+};
+
+static const struct button_bar_buttons global_history_toolbar_buttons[] = {
+ {"delete", TOOLBAR_BUTTON_DELETE, TOOLBAR_BUTTON_NONE, '0', "0"},
+ {"expand", TOOLBAR_BUTTON_EXPAND, TOOLBAR_BUTTON_COLLAPSE, '1', "1"},
+ {"open", TOOLBAR_BUTTON_OPEN, TOOLBAR_BUTTON_CLOSE, '2', "2"},
+ {"launch", TOOLBAR_BUTTON_LAUNCH, TOOLBAR_BUTTON_NONE, '3', "3"},
+ {NULL, TOOLBAR_BUTTON_NONE, TOOLBAR_BUTTON_NONE, '\0', ""}
+};
+
+static const struct button_bar_buttons hotlist_toolbar_buttons[] = {
+ {"delete", TOOLBAR_BUTTON_DELETE, TOOLBAR_BUTTON_NONE, '0', "0"},
+ {"expand", TOOLBAR_BUTTON_EXPAND, TOOLBAR_BUTTON_COLLAPSE, '1', "1"},
+ {"open", TOOLBAR_BUTTON_OPEN, TOOLBAR_BUTTON_CLOSE, '2', "2"},
+ {"launch", TOOLBAR_BUTTON_LAUNCH, TOOLBAR_BUTTON_NONE, '3', "3"},
+ {"create", TOOLBAR_BUTTON_CREATE, TOOLBAR_BUTTON_NONE, '4', "4"},
+ {NULL, TOOLBAR_BUTTON_NONE, TOOLBAR_BUTTON_NONE, '\0', ""}
+};
+
+struct button_bar;
+
+
+/**
+ * Create a new button bar widget.
+ *
+ * \param *theme The theme to apply (or NULL for the default).
+ * \param buttons[] An array of button definitions for the bar.
+ * \return A button bar handle, or NULL on failure.
+ */
+
+struct button_bar *ro_gui_button_bar_create(struct theme_descriptor *theme,
+ const struct button_bar_buttons buttons[]);
+
+
+/**
+ * Link two button bars together
+ *
+ * Join two button bars the target being the active bar, and the
+ * source being the editing bar used to supply valid buttons. The bars are
+ * checked to ensure that they are not already part of an edit pair, but are
+ * not checked for button-compatibility.
+ *
+ * \param target The target button bar.
+ * \param source The source button bar.
+ * \param refresh The refresh callback.
+ * \param client_data context passed to the refresh callback
+ * \return true if successful; else false.
+ */
+
+bool ro_gui_button_bar_link_editor(struct button_bar *target,
+ struct button_bar *source, void (* refresh)(void *),
+ void *client_data);
+
+/**
+ * Place a button bar into a toolbar window and initialise any theme-specific
+ * settings. Any previous incarnation of the bar will be forgotten: this
+ * is for use when a new toolbar is being created, or when a toolbar has been
+ * deleted and rebuilt following a theme change.
+ *
+ * \param *button_bar The button bar to rebuild.
+ * \param *theme The theme to apply (or NULL for current).
+ * \param style The theme style to apply.
+ * \param window The window that the bar is in.
+ * \param edit The edit mode of the button bar.
+ * \return true on success; else false.
+ */
+
+bool ro_gui_button_bar_rebuild(struct button_bar *button_bar,
+ struct theme_descriptor *theme, theme_style style,
+ wimp_w window, bool edit);
+
+
+/**
+ * Arrange buttons on a button bar, using an order string to specify the
+ * required button and separator layout.
+ *
+ * \param *button_bar The button bar to update.
+ * \param order[] The button order configuration string.
+ * \return true if successful; else false.
+ */
+
+bool ro_gui_button_bar_arrange_buttons(struct button_bar *button_bar,
+ char order[]);
+
+
+/**
+ * Destroy a button bar widget.
+ *
+ * \param *button_bar The button bar to destroy.
+ */
+
+void ro_gui_button_bar_destroy(struct button_bar *button_bar);
+
+
+/**
+ * Return the MINIMUM dimensions required by the button bar, in RO units,
+ * allowing for the current theme.
+ *
+ * \param *button_bar The button bar of interest.
+ * \param *width Return the required width.
+ * \param *height Return the required height.
+ * \return true if values are returned; else false.
+ */
+
+bool ro_gui_button_bar_get_dims(struct button_bar *button_bar,
+ int *width, int *height);
+
+
+/**
+ * Set or update the dimensions to be used by the button bar, in RO units.
+ * If these are greater than the minimum required, the button bar will fill
+ * the extended space; if less, the call will fail.
+ *
+ * \param *button_bar The button bar to update.
+ * \param x0 The minimum X window position.
+ * \param y0 The minimum Y window position.
+ * \param x1 The maximum X window position.
+ * \param y1 The maximum Y window position.
+ * \return true if size updated; else false.
+ */
+
+bool ro_gui_button_bar_set_extent(struct button_bar *button_bar,
+ int x0, int y0, int x1, int y1);
+
+
+/**
+ * Show or hide a button bar.
+ *
+ * \param *button_bar The button bar to hide.
+ * \param hide true to hide the bar; false to show it.
+ * \return true if successful; else false.
+ */
+
+bool ro_gui_button_bar_hide(struct button_bar *button_bar, bool hide);
+
+
+/**
+ * Shade or unshade a button in a bar corresponding to the given action.
+ *
+ * \param *button_bar The button bar to update.
+ * \param action The action to update.
+ * \param shaded true to shade the button; false to unshade.
+ * \return true if successful; else false.
+ */
+
+bool ro_gui_button_bar_shade_button(struct button_bar *button_bar,
+ button_bar_action action, bool shaded);
+
+
+/**
+ * Handle redraw event rectangles in a button bar.
+ *
+ * \param *button_bar The button bar to use.
+ * \param *redraw The Wimp redraw rectangle to process.
+ */
+
+void ro_gui_button_bar_redraw(struct button_bar *button_bar,
+ wimp_draw *redraw);
+
+
+/**
+ * Handle mouse clicks in a button bar.
+ *
+ * \param *button_bar The button bar to use.
+ * \param *pointer The Wimp mouse click event data.
+ * \param *state The toolbar window state.
+ * \param *action Returns the selected action, or
+ * TOOLBAR_BUTTON_NONE.
+ * \return true if the event was handled exclusively;
+ * else false.
+ */
+
+bool ro_gui_button_bar_click(struct button_bar *button_bar,
+ wimp_pointer *pointer, wimp_window_state *state,
+ button_bar_action *action);
+
+
+/**
+ * Translate mouse data into an interactive help message for a button bar.
+ *
+ * \param *button_bar The button bar to process.
+ * \param i The wimp icon under the pointer.
+ * \param *mouse The mouse position.
+ * \param *state The toolbar window state.
+ * \param buttons The mouse button state.
+ * \param **suffix Return a help token suffix, or "" for none.
+ * \return true if handled exclusively; else false.
+ */
+
+bool ro_gui_button_bar_help_suffix(struct button_bar *button_bar, wimp_i i,
+ os_coord *mouse, wimp_window_state *state,
+ wimp_mouse_state buttons, const char **suffix);
+
+
+/**
+ * Return a config string reflecting the configured order of buttons
+ * and spacers. The string is allocated with malloc(), and should be
+ * free()d after use.
+ *
+ * \param *button_bar The button bar of interest.
+ * \return Pointer to a config string, or NULL on failure.
+ */
+
+char *ro_gui_button_bar_get_config(struct button_bar *button_bar);
+
+#endif
+
diff --git a/frontends/riscos/gui/progress_bar.c b/frontends/riscos/gui/progress_bar.c
new file mode 100644
index 000000000..3ec6b3aa8
--- /dev/null
+++ b/frontends/riscos/gui/progress_bar.c
@@ -0,0 +1,535 @@
+/*
+ * Copyright 2006 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Progress bar (implementation).
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+#include "swis.h"
+#include "oslib/colourtrans.h"
+#include "oslib/os.h"
+#include "oslib/osspriteop.h"
+#include "oslib/wimp.h"
+#include "oslib/wimpspriteop.h"
+
+#include "desktop/plotters.h"
+#include "utils/log.h"
+#include "utils/utils.h"
+
+#include "riscos/gui.h"
+#include "riscos/tinct.h"
+#include "riscos/wimp_event.h"
+#include "riscos/gui/progress_bar.h"
+
+#define MARGIN 6
+
+struct progress_bar {
+ wimp_w w; /**< progress bar window handle */
+ unsigned int range; /**< progress bar range */
+ unsigned int value; /**< progress bar value */
+ char icon[13]; /**< current icon */
+ int offset; /**< progress bar rotation */
+ os_box visible; /**< progress bar position */
+ int icon_x0; /**< icon x0 */
+ int icon_y0; /**< icon y0 */
+ osspriteop_header *icon_img; /**< icon image */
+ bool animating; /**< progress bar is animating */
+ bool recalculate; /**< recalculation required */
+ int cur_width; /**< current calculated width */
+ int cur_height; /**< current calculated height */
+ bool icon_obscured; /**< icon is partially obscured */
+};
+
+static char progress_animation_sprite[] = "progress";
+static osspriteop_header *progress_icon;
+static unsigned int progress_width;
+static unsigned int progress_height;
+
+struct wimp_window_base progress_bar_definition = {
+ {0, 0, 1, 1},
+ 0,
+ 0,
+ wimp_TOP,
+ wimp_WINDOW_NEW_FORMAT | wimp_WINDOW_MOVEABLE | wimp_WINDOW_NO_BOUNDS,
+ 0xff,
+ wimp_COLOUR_LIGHT_GREY,
+ wimp_COLOUR_LIGHT_GREY,
+ wimp_COLOUR_VERY_LIGHT_GREY,
+ wimp_COLOUR_DARK_GREY,
+ wimp_COLOUR_MID_LIGHT_GREY,
+ wimp_COLOUR_CREAM,
+ wimp_WINDOW_NEVER3D | 0x16u /* RISC OS 5.03+ */,
+ {0, 0, 65535, 65535},
+ 0,
+ 0,
+ wimpspriteop_AREA,
+ 1,
+ 1,
+ {""},
+ 0
+};
+
+
+static void ro_gui_progress_bar_calculate(struct progress_bar *pb, int width,
+ int height);
+static void ro_gui_progress_bar_redraw(wimp_draw *redraw);
+static void ro_gui_progress_bar_redraw_window(wimp_draw *redraw,
+ struct progress_bar *pb);
+static void ro_gui_progress_bar_animate(void *p);
+
+
+/**
+ * Initialise the progress bar
+ *
+ * \param icons the sprite area to use for icons
+ */
+void ro_gui_progress_bar_init(osspriteop_area *icons)
+{
+ const char *name = progress_animation_sprite;
+ os_error *error;
+
+ progress_bar_definition.sprite_area = icons;
+
+ progress_icon = NULL;
+ error = xosspriteop_select_sprite(osspriteop_USER_AREA,
+ progress_bar_definition.sprite_area,
+ (osspriteop_id) name, &progress_icon);
+ if (!error) {
+ xosspriteop_read_sprite_info(osspriteop_USER_AREA,
+ progress_bar_definition.sprite_area,
+ (osspriteop_id) name,
+ (int *) &progress_width, (int *) &progress_height,
+ 0, 0);
+ }
+}
+
+
+/**
+ * Create a new progress bar
+ */
+struct progress_bar *ro_gui_progress_bar_create(void)
+{
+ struct progress_bar *pb;
+ os_error *error;
+
+ pb = calloc(1, sizeof(*pb));
+ if (!pb)
+ return NULL;
+
+ error = xwimp_create_window((wimp_window *)&progress_bar_definition,
+ &pb->w);
+ if (error) {
+ LOG("xwimp_create_window: 0x%x: %s", error->errnum, error->errmess);
+ free(pb);
+ return NULL;
+ }
+
+ ro_gui_wimp_event_register_redraw_window(pb->w,
+ ro_gui_progress_bar_redraw);
+ ro_gui_wimp_event_set_user_data(pb->w, pb);
+ return pb;
+}
+
+
+/**
+ * Destroy a progress bar and free all associated resources
+ *
+ * \param pb the progress bar to destroy
+ */
+void ro_gui_progress_bar_destroy(struct progress_bar *pb)
+{
+ os_error *error;
+ assert(pb);
+
+ if (pb->animating) {
+ riscos_schedule(-1, ro_gui_progress_bar_animate, pb);
+ }
+ ro_gui_wimp_event_finalise(pb->w);
+ error = xwimp_delete_window(pb->w);
+ if (error) {
+ LOG("xwimp_delete_window: 0x%x:%s", error->errnum, error->errmess);
+ }
+
+ free(pb);
+}
+
+
+/**
+ * Get the handle of the window that represents a progress bar
+ *
+ * \param pb the progress bar to get the window handle of
+ * \return the progress bar's window handle
+ */
+wimp_w ro_gui_progress_bar_get_window(struct progress_bar *pb)
+{
+ assert(pb);
+
+ return pb->w;
+}
+
+
+/**
+ * Set the icon for a progress bar
+ *
+ * \param pb the progress bar to set the icon for
+ * \param icon the icon to use, or NULL for no icon
+ */
+void ro_gui_progress_bar_set_icon(struct progress_bar *pb, const char *icon)
+{
+ assert(pb);
+
+ if (!strcmp(icon, pb->icon))
+ return;
+ if (!icon)
+ pb->icon[0] = '\0';
+ else {
+ strncpy(pb->icon, icon, 12);
+ pb->icon[12] = '\0';
+ }
+ pb->recalculate = true;
+ xwimp_force_redraw(pb->w, 0, 0, 32, 32);
+ ro_gui_progress_bar_update(pb, pb->cur_width, pb->cur_height);
+}
+
+
+/**
+ * Set the value of a progress bar
+ *
+ * \param pb the progress bar to set the value for
+ * \param value the value to use
+ */
+void ro_gui_progress_bar_set_value(struct progress_bar *pb, unsigned int value)
+{
+ assert(pb);
+
+ pb->value = value;
+ if (pb->value > pb->range)
+ pb->range = pb->value;
+ ro_gui_progress_bar_update(pb, pb->cur_width, pb->cur_height);
+}
+
+
+/**
+ * Get the value of a progress bar
+ *
+ * \param pb the progress bar to get the value of
+ * \return the current value
+ */
+unsigned int ro_gui_progress_bar_get_value(struct progress_bar *pb)
+{
+ assert(pb);
+
+ return pb->value;
+}
+
+
+/**
+ * Set the range of a progress bar
+ *
+ * \param pb the progress bar to set the range for
+ * \param range the range to use
+ */
+void ro_gui_progress_bar_set_range(struct progress_bar *pb, unsigned int range)
+{
+ assert(pb);
+
+ pb->range = range;
+ if (pb->value > pb->range)
+ pb->value = pb->range;
+ ro_gui_progress_bar_update(pb, pb->cur_width, pb->cur_height);
+}
+
+
+/**
+ * Get the range of a progress bar
+ *
+ * \param pb the progress bar to get the range of
+ * \return the current range
+ */
+unsigned int ro_gui_progress_bar_get_range(struct progress_bar *pb)
+{
+ assert(pb);
+
+ return pb->range;
+}
+
+
+/**
+ * Update the progress bar to a new dimension.
+ *
+ * \param pb the progress bar to update
+ * \param width the new progress bar width
+ * \param height the new progress bar height
+ */
+void ro_gui_progress_bar_update(struct progress_bar *pb, int width, int height)
+{
+ wimp_draw redraw;
+ os_error *error;
+ osbool more;
+ os_box cur;
+
+ /* don't allow negative dimensions */
+ width = max(width, 0);
+ height = max(height, 0);
+
+ /* update the animation state */
+ if ((pb->value == 0) || (pb->value == pb->range)) {
+ if (pb->animating) {
+ riscos_schedule(-1, ro_gui_progress_bar_animate, pb);
+ }
+ pb->animating = false;
+ } else {
+ if (!pb->animating) {
+ riscos_schedule(200, ro_gui_progress_bar_animate, pb);
+ }
+ pb->animating = true;
+ }
+
+ /* get old and new positions */
+ cur = pb->visible;
+ pb->recalculate = true;
+ ro_gui_progress_bar_calculate(pb, width, height);
+
+ /* see if the progress bar hasn't moved. we don't need to consider
+ * the left edge moving as this is handled by the icon setting
+ * function */
+ if (cur.x1 == pb->visible.x1)
+ return;
+
+ /* if size has decreased then we must force a redraw */
+ if (cur.x1 > pb->visible.x1) {
+ xwimp_force_redraw(pb->w, pb->visible.x1, pb->visible.y0,
+ cur.x1, pb->visible.y1);
+ return;
+ }
+
+ /* perform a minimal redraw update */
+ redraw.w = pb->w;
+ redraw.box = pb->visible;
+ redraw.box.x0 = cur.x1;
+ error = xwimp_update_window(&redraw, &more);
+ if (error) {
+ LOG("Error getting update window: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+ if (more)
+ ro_gui_progress_bar_redraw_window(&redraw, pb);
+}
+
+
+/**
+ * Process a WIMP redraw request
+ *
+ * \param redraw the redraw request to process
+ */
+void ro_gui_progress_bar_redraw(wimp_draw *redraw)
+{
+ struct progress_bar *pb;
+ os_error *error;
+ osbool more;
+
+ pb = (struct progress_bar *)ro_gui_wimp_event_get_user_data(redraw->w);
+ assert(pb);
+
+ error = xwimp_redraw_window(redraw, &more);
+ if (error) {
+ LOG("xwimp_redraw_window: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+ if (more)
+ ro_gui_progress_bar_redraw_window(redraw, pb);
+}
+
+
+/**
+ * Animate the progress bar
+ *
+ * \param p the progress bar to animate
+ */
+void ro_gui_progress_bar_animate(void *p)
+{
+ wimp_draw redraw;
+ os_error *error;
+ osbool more;
+ struct progress_bar *pb = p;
+
+ if (!progress_icon)
+ return;
+ pb->offset -= 6;
+ if (pb->offset < 0)
+ pb->offset += progress_width * 2;
+
+ if (pb->animating) {
+ riscos_schedule(200, ro_gui_progress_bar_animate, pb);
+ }
+
+ redraw.w = pb->w;
+ redraw.box = pb->visible;
+ error = xwimp_update_window(&redraw, &more);
+ if (error != NULL) {
+ LOG("Error getting update window: '%s'", error->errmess);
+ return;
+ }
+ if (more)
+ ro_gui_progress_bar_redraw_window(&redraw, pb);
+}
+
+
+/**
+ * Calculate the position of the progress bar
+ *
+ * \param pb the progress bar to recalculate
+ * \param width the width of the progress bar
+ * \param height the height of the progress bar
+ * \return the address of the associated icon, or NULL
+ */
+void ro_gui_progress_bar_calculate(struct progress_bar *pb, int width,
+ int height)
+{
+ int icon_width, icon_height;
+ int icon_x0 = 0, icon_y0 = 0, progress_x0, progress_x1;
+ osspriteop_header *icon = NULL;
+ bool icon_redraw = false;
+
+ /* try to use cached values */
+ if ((!pb->recalculate) && (pb->cur_width == width) &&
+ (pb->cur_height == height))
+ return;
+
+ /* update cache status */
+ pb->recalculate = false;
+ pb->cur_width = width;
+ pb->cur_height = height;
+
+ /* get the window dimensions */
+ width -= MARGIN * 2;
+ icon_width = icon_height = 0;
+ progress_x0 = MARGIN;
+
+ /* get the icon information */
+ if (progress_bar_definition.sprite_area != wimpspriteop_AREA) {
+ int progress_ymid = height / 2;
+ os_error *error;
+ error = xosspriteop_read_sprite_info(osspriteop_USER_AREA,
+ progress_bar_definition.sprite_area,
+ (osspriteop_id)pb->icon,
+ &icon_width, &icon_height, 0, 0);
+ if (!error) {
+ error = xosspriteop_select_sprite(osspriteop_USER_AREA,
+ progress_bar_definition.sprite_area,
+ (osspriteop_id)pb->icon, &icon);
+ }
+ if (!error) {
+ progress_x0 += 32 + MARGIN;
+ width -= 32 + MARGIN;
+ icon_x0 = MARGIN + 16 - icon_width;
+ icon_y0 = progress_ymid - icon_height;
+ if (width < -MARGIN) {
+ icon_x0 += width + MARGIN;
+ icon_redraw = true;
+ }
+ }
+ }
+
+ /* update the icon */
+ if ((pb->icon_obscured) || (icon_redraw)) {
+ if (icon_x0 != pb->icon_x0)
+ xwimp_force_redraw(pb->w, 0, 0, 32 + MARGIN, 65536);
+ }
+ pb->icon_obscured = icon_redraw;
+
+ progress_x1 = progress_x0;
+ if ((pb->range > 0) && (width > 0))
+ progress_x1 += (width * pb->value) / pb->range;
+
+ pb->visible.x0 = progress_x0;
+ pb->visible.y0 = MARGIN;
+ pb->visible.x1 = progress_x1;
+ pb->visible.y1 = height - MARGIN;
+ pb->icon_x0 = icon_x0;
+ pb->icon_y0 = icon_y0;
+ pb->icon_img = icon;
+}
+
+
+/**
+ * Redraw a section of a progress bar window
+ *
+ * \param redraw the section of the window to redraw
+ * \param pb the progress bar to redraw
+ */
+void ro_gui_progress_bar_redraw_window(wimp_draw *redraw,
+ struct progress_bar *pb)
+{
+ osbool more = true;
+ struct rect clip;
+ int progress_ymid;
+
+ /* initialise the plotters */
+ ro_plot_origin_x = 0;
+ ro_plot_origin_y = 0;
+
+ /* recalculate the progress bar */
+ ro_gui_progress_bar_calculate(pb, redraw->box.x1 - redraw->box.x0,
+ redraw->box.y1 - redraw->box.y0);
+ progress_ymid = redraw->box.y0 + pb->visible.y0 +
+ ((pb->visible.y1 - pb->visible.y0) >> 1);
+
+ /* redraw the window */
+ while (more) {
+ os_error *error;
+ if (pb->icon)
+ _swix(Tinct_PlotAlpha, _IN(2) | _IN(3) | _IN(4) | _IN(7),
+ pb->icon_img,
+ redraw->box.x0 + pb->icon_x0,
+ redraw->box.y0 + pb->icon_y0,
+ tinct_ERROR_DIFFUSE);
+ if (!pb->icon_obscured) {
+ clip.x0 = max(redraw->clip.x0,
+ redraw->box.x0 + pb->visible.x0) >> 1;
+ clip.y0 = -min(redraw->clip.y1,
+ redraw->box.y0 + pb->visible.y1) >> 1;
+ clip.x1 = min(redraw->clip.x1,
+ redraw->box.x0 + pb->visible.x1) >> 1;
+ clip.y1 = -max(redraw->clip.y0,
+ redraw->box.y0 + pb->visible.y0) >> 1;
+ if ((clip.x0 < clip.x1) && (clip.y0 < clip.y1)) {
+ if (progress_icon) {
+ ro_plotters.clip(&clip);
+ _swix(Tinct_Plot, _IN(2) | _IN(3) | _IN(4) | _IN(7),
+ progress_icon,
+ redraw->box.x0 - pb->offset,
+ progress_ymid - progress_height,
+ tinct_FILL_HORIZONTALLY);
+ } else {
+ ro_plotters.rectangle(clip.x0, clip.y0,
+ clip.x1, clip.y1,
+ plot_style_fill_red);
+ }
+ }
+ }
+ error = xwimp_get_rectangle(redraw, &more);
+ if (error) {
+ LOG("xwimp_get_rectangle: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+ }
+}
diff --git a/frontends/riscos/gui/progress_bar.h b/frontends/riscos/gui/progress_bar.h
new file mode 100644
index 000000000..e4cec1369
--- /dev/null
+++ b/frontends/riscos/gui/progress_bar.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2006 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Progress bar (interface).
+ */
+
+#ifndef _NETSURF_RISCOS_PROGRESS_BAR_H_
+#define _NETSURF_RISCOS_PROGRESS_BAR_H_
+
+#include <stdbool.h>
+#include "oslib/osspriteop.h"
+#include "oslib/wimp.h"
+
+struct progress_bar;
+
+void ro_gui_progress_bar_init(osspriteop_area *icons);
+
+struct progress_bar *ro_gui_progress_bar_create(void);
+void ro_gui_progress_bar_destroy(struct progress_bar *pb);
+void ro_gui_progress_bar_update(struct progress_bar *pb, int width, int height);
+
+wimp_w ro_gui_progress_bar_get_window(struct progress_bar *pb);
+void ro_gui_progress_bar_set_icon(struct progress_bar *pb, const char *icon);
+void ro_gui_progress_bar_set_value(struct progress_bar *pb, unsigned int value);
+unsigned int ro_gui_progress_bar_get_value(struct progress_bar *pb);
+void ro_gui_progress_bar_set_range(struct progress_bar *pb, unsigned int range);
+unsigned int ro_gui_progress_bar_get_range(struct progress_bar *pb);
+#endif
diff --git a/frontends/riscos/gui/status_bar.c b/frontends/riscos/gui/status_bar.c
new file mode 100644
index 000000000..cbc404658
--- /dev/null
+++ b/frontends/riscos/gui/status_bar.c
@@ -0,0 +1,625 @@
+/*
+ * Copyright 2006 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * UTF8 status bar (implementation).
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+#include "swis.h"
+#include "oslib/colourtrans.h"
+#include "oslib/os.h"
+#include "oslib/wimp.h"
+#include "oslib/wimpspriteop.h"
+#include "desktop/plotters.h"
+#include "utils/log.h"
+#include "utils/utils.h"
+
+#include "riscos/gui.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/wimputils.h"
+#include "riscos/font.h"
+#include "riscos/gui/progress_bar.h"
+#include "riscos/gui/status_bar.h"
+
+#define ICON_WIDGET 0
+#define WIDGET_WIDTH 12
+#define PROGRESS_WIDTH 160
+
+struct status_bar {
+ wimp_w w; /**< status bar window handle */
+ wimp_w parent; /**< parent window handle */
+ const char *text; /**< status bar text */
+ struct progress_bar *pb; /**< progress bar */
+ unsigned int scale; /**< current status bar scale */
+ int width; /**< current status bar width */
+ bool visible; /**< status bar is visible? */
+};
+
+static char status_widget_text[] = "";
+static char status_widget_validation[] = "R5;Pptr_lr,8,6";
+
+wimp_WINDOW(1) status_bar_definition = {
+ {0, 0, 1, 1},
+ 0,
+ 0,
+ wimp_TOP,
+ wimp_WINDOW_NEW_FORMAT | wimp_WINDOW_MOVEABLE |
+ wimp_WINDOW_FURNITURE_WINDOW |
+ wimp_WINDOW_IGNORE_XEXTENT,
+ wimp_COLOUR_BLACK,
+ wimp_COLOUR_LIGHT_GREY,
+ wimp_COLOUR_LIGHT_GREY,
+ wimp_COLOUR_VERY_LIGHT_GREY,
+ wimp_COLOUR_DARK_GREY,
+ wimp_COLOUR_MID_LIGHT_GREY,
+ wimp_COLOUR_CREAM,
+ wimp_WINDOW_NEVER3D | 0x16u /* RISC OS 5.03+ */,
+ {0, 0, 65535, 65535},
+ 0,
+ 0,
+ wimpspriteop_AREA,
+ 1,
+ 1,
+ {""},
+ 1,
+ {
+ {
+ {0, 0, 1, 1},
+ wimp_ICON_TEXT | wimp_ICON_INDIRECTED |
+ wimp_ICON_BORDER | wimp_ICON_FILLED |
+ (wimp_COLOUR_LIGHT_GREY <<
+ wimp_ICON_BG_COLOUR_SHIFT) |
+ (wimp_BUTTON_CLICK_DRAG <<
+ wimp_ICON_BUTTON_TYPE_SHIFT),
+ {
+ .indirected_text = {
+ status_widget_text,
+ status_widget_validation,
+ 1
+ }
+ }
+ }
+ }
+};
+
+static void ro_gui_status_bar_open(wimp_open *open);
+static bool ro_gui_status_bar_click(wimp_pointer *pointer);
+static void ro_gui_status_bar_redraw(wimp_draw *redraw);
+static void ro_gui_status_bar_redraw_callback(void *handle);
+static void ro_gui_status_position_progress_bar(struct status_bar *sb);
+
+
+/**
+ * Create a new status bar
+ *
+ * \param parent the window to contain the status bar
+ * \param width the proportional width to use (0...10,000)
+ */
+struct status_bar *ro_gui_status_bar_create(wimp_w parent, unsigned int width)
+{
+ struct status_bar *sb;
+ os_error *error;
+
+ sb = calloc(1, sizeof(*sb));
+ if (!sb)
+ return NULL;
+
+ sb->pb = ro_gui_progress_bar_create();
+ if (!sb->pb)
+ return NULL;
+
+ error = xwimp_create_window((wimp_window *)&status_bar_definition,
+ &sb->w);
+ if (error) {
+ LOG("xwimp_create_window: 0x%x: %s", error->errnum, error->errmess);
+ free(sb);
+ return NULL;
+ }
+ sb->parent = parent;
+ sb->scale = width;
+ sb->visible = true;
+
+ ro_gui_wimp_event_set_user_data(sb->w, sb);
+ ro_gui_wimp_event_register_open_window(sb->w,
+ ro_gui_status_bar_open);
+ ro_gui_wimp_event_register_mouse_click(sb->w,
+ ro_gui_status_bar_click);
+ ro_gui_wimp_event_register_redraw_window(sb->w,
+ ro_gui_status_bar_redraw);
+ ro_gui_wimp_event_set_help_prefix(sb->w, "HelpStatus");
+ ro_gui_status_bar_resize(sb);
+ return sb;
+}
+
+
+/**
+ * Destroy a status bar and free all associated resources
+ *
+ * \param sb the status bar to destroy
+ */
+void ro_gui_status_bar_destroy(struct status_bar *sb)
+{
+ os_error *error;
+ assert(sb);
+
+ ro_gui_wimp_event_finalise(sb->w);
+ error = xwimp_delete_window(sb->w);
+ if (error) {
+ LOG("xwimp_delete_window: 0x%x:%s", error->errnum, error->errmess);
+ }
+
+ ro_gui_progress_bar_destroy(sb->pb);
+
+ /* Remove any scheduled redraw callbacks */
+ riscos_schedule(-1, ro_gui_status_bar_redraw_callback, (void *) sb);
+
+ free(sb);
+}
+
+
+/**
+ * Get the handle of the window that represents a status bar
+ *
+ * \param sb the status bar to get the window handle of
+ * \return the status bar's window handle
+ */
+wimp_w ro_gui_status_bar_get_window(struct status_bar *sb)
+{
+ assert(sb);
+
+ return sb->w;
+}
+
+
+/**
+ * Get the proportional width the status bar is currently using
+ *
+ * \param sb the status bar to get the width of
+ * \return the status bar's width (0...10,000)
+ */
+unsigned int ro_gui_status_bar_get_width(struct status_bar *sb)
+{
+ assert(sb);
+
+ return sb->scale;
+}
+
+
+/**
+ * Set the visibility status of the status bar
+ *
+ * \param sb the status bar to check the visiblity of
+ * \param visible if the status bar should be shown or not.
+ * \return whether the status bar is visible
+ */
+void ro_gui_status_bar_set_visible(struct status_bar *sb, bool visible)
+{
+ assert(sb);
+
+ sb->visible = visible;
+ if (visible) {
+ ro_gui_status_bar_resize(sb);
+ } else {
+ os_error *error = xwimp_close_window(sb->w);
+ if (error) {
+ LOG("xwimp_close_window: 0x%x:%s", error->errnum, error->errmess);
+ }
+ }
+}
+
+
+/**
+ * Get the visibility status of the status bar
+ *
+ * \param sb the status bar to check the visiblity of
+ * \return whether the status bar is visible
+ */
+bool ro_gui_status_bar_get_visible(struct status_bar *sb)
+{
+ assert(sb);
+
+ return sb->visible;
+}
+
+
+/**
+ * Set the value of the progress bar
+ *
+ * \param sb the status bar to set the progress of
+ * \param value the value to use
+ */
+void ro_gui_status_bar_set_progress_value(struct status_bar *sb,
+ unsigned int value)
+{
+ assert(sb);
+
+ ro_gui_status_bar_set_progress_range(sb,
+ max(value, ro_gui_progress_bar_get_range(sb->pb)));
+ ro_gui_progress_bar_set_value(sb->pb, value);
+}
+
+
+/**
+ * Set the range of the progress bar
+ *
+ * \param sb the status bar to set the range of
+ * \param range the range of the progress bar
+ */
+void ro_gui_status_bar_set_progress_range(struct status_bar *sb,
+ unsigned int range)
+{
+ unsigned int old_range;
+
+ assert(sb);
+
+ old_range = ro_gui_progress_bar_get_range(sb->pb);
+ ro_gui_progress_bar_set_range(sb->pb, range);
+
+ LOG("Ranges are %i vs %i", old_range, range);
+ if ((old_range == 0) && (range != 0)) {
+ ro_gui_status_position_progress_bar(sb);
+ } else if ((old_range != 0) && (range == 0)) {
+ os_error *error = xwimp_close_window(
+ ro_gui_progress_bar_get_window(sb->pb));
+ if (error) {
+ LOG("xwimp_close_window: 0x%x:%s", error->errnum, error->errmess);
+ }
+ }
+}
+
+
+/**
+ * Set the icon for the progress bar
+ *
+ * \param sb the status bar to set the icon for
+ * \param icon the icon to use, or NULL for no icon
+ */
+void ro_gui_status_bar_set_progress_icon(struct status_bar *sb,
+ const char *icon)
+{
+ assert(sb);
+
+ ro_gui_progress_bar_set_icon(sb->pb, icon);
+}
+
+
+/**
+ * Set the text to display in the status bar
+ *
+ * \param sb the status bar to set the text for
+ * \param text the UTF8 text to display, or NULL for none
+ */
+void ro_gui_status_bar_set_text(struct status_bar *sb, const char *text)
+{
+ assert(sb);
+
+ sb->text = text;
+
+ /* Schedule a window redraw for 1cs' time.
+ *
+ * We do this to ensure that redraws as a result of text changes
+ * do not prevent other applications obtaining CPU time.
+ *
+ * The scheduled callback will be run when we receive the first
+ * null poll after 1cs has elapsed. It may then issue a redraw
+ * request to the Wimp.
+ *
+ * The scheduler ensures that only one instance of the
+ * { callback, handle } pair is registered at once.
+ */
+ if (sb->visible && text != NULL) {
+ riscos_schedule(10, ro_gui_status_bar_redraw_callback, sb);
+ }
+}
+
+
+/**
+ * Resize a status bar following a change in the dimensions of the
+ * parent window.
+ *
+ * \param sb the status bar to resize
+ */
+void ro_gui_status_bar_resize(struct status_bar *sb)
+{
+ int window_width;
+ int status_width, status_height;
+ wimp_window_state state;
+ os_error *error;
+ os_box extent;
+
+ if ((!sb) || (!sb->visible))
+ return;
+
+ /* get the window work area dimensions */
+ state.w = sb->parent;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+ window_width = state.visible.x1 - state.visible.x0;
+
+
+ /* recalculate the scaled width */
+ status_width = (window_width * sb->scale) / 10000;
+ if (status_width < WIDGET_WIDTH)
+ status_width = WIDGET_WIDTH;
+ status_height = ro_get_hscroll_height(sb->parent);
+
+ /* resize the status/resize icons */
+ if (status_width != sb->width) {
+ /* update the window extent */
+ int redraw_left, redraw_right;
+
+ extent.x0 = 0;
+ extent.y0 = 0;
+ extent.x1 = status_width;
+ extent.y1 = status_height - 4;
+ error = xwimp_set_extent(sb->w, &extent);
+ if (error) {
+ LOG("xwimp_set_extent: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+
+ /* re-open the nested window */
+ state.w = sb->w;
+ state.xscroll = 0;
+ state.yscroll = 0;
+ state.next = wimp_TOP;
+ state.visible.x0 = state.visible.x0;
+ state.visible.y1 = state.visible.y0 - 2;
+ state.visible.x1 = state.visible.x0 + status_width;
+ state.visible.y0 = state.visible.y1 - status_height + 4;
+ error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state),
+ sb->parent,
+ wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
+ << wimp_CHILD_XORIGIN_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
+ << wimp_CHILD_YORIGIN_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
+ << wimp_CHILD_LS_EDGE_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
+ << wimp_CHILD_BS_EDGE_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
+ << wimp_CHILD_RS_EDGE_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
+ << wimp_CHILD_TS_EDGE_SHIFT);
+ if (error) {
+ LOG("xwimp_open_window_nested: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+ ro_gui_status_position_progress_bar(sb);
+ error = xwimp_resize_icon(sb->w, ICON_WIDGET,
+ status_width - WIDGET_WIDTH, 0,
+ status_width, status_height - 4);
+ if (error) {
+ LOG("xwimp_resize_icon: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+
+ redraw_left = min(status_width, sb->width) - WIDGET_WIDTH - 2;
+ redraw_right = max(status_width, sb->width);
+ xwimp_force_redraw(sb->w, redraw_left, 0,
+ redraw_right, status_height);
+ sb->width = status_width;
+ }
+}
+
+
+/**
+ * Process a WIMP redraw request
+ *
+ * \param redraw the redraw request to process
+ */
+void ro_gui_status_bar_redraw(wimp_draw *redraw)
+{
+ struct status_bar *sb;
+ os_error *error;
+ osbool more;
+ rufl_code code;
+
+ sb = (struct status_bar *)ro_gui_wimp_event_get_user_data(redraw->w);
+ assert(sb);
+
+ /* initialise the plotters */
+ ro_plot_origin_x = 0;
+ ro_plot_origin_y = 0;
+
+ /* redraw the window */
+ error = xwimp_redraw_window(redraw, &more);
+ if (error) {
+ LOG("xwimp_redraw_window: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+ while (more) {
+ /* redraw the status text */
+ if (sb->text) {
+ error = xcolourtrans_set_font_colours(font_CURRENT,
+ 0xeeeeee00, 0x00000000, 14, 0, 0, 0);
+ if (error) {
+ LOG("xcolourtrans_set_font_colours: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+ code = rufl_paint(ro_gui_desktop_font_family,
+ ro_gui_desktop_font_style,
+ ro_gui_desktop_font_size,
+ sb->text, strlen(sb->text),
+ redraw->box.x0 + 6, redraw->box.y0 + 8,
+ rufl_BLEND_FONT);
+ if (code != rufl_OK) {
+ if (code == rufl_FONT_MANAGER_ERROR)
+ LOG("rufl_FONT_MANAGER_ERROR: 0x%x: %s", rufl_fm_error->errnum, rufl_fm_error->errmess);
+ else
+ LOG("rufl_paint: 0x%x", code);
+ }
+ }
+
+ /* separate the widget from the text with a line */
+ ro_plotters.rectangle((redraw->box.x0 + sb->width - WIDGET_WIDTH - 2) >> 1,
+ -redraw->box.y0 >> 1,
+ (redraw->box.x0 + sb->width - WIDGET_WIDTH) >> 1,
+ -redraw->box.y1 >> 1,
+ plot_style_fill_black);
+
+ error = xwimp_get_rectangle(redraw, &more);
+ if (error) {
+ LOG("xwimp_get_rectangle: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+ }
+}
+
+/**
+ * Callback for scheduled redraw
+ *
+ * \param handle Callback handle
+ */
+void ro_gui_status_bar_redraw_callback(void *handle)
+{
+ struct status_bar *sb = handle;
+
+ wimp_force_redraw(sb->w, 0, 0, sb->width - WIDGET_WIDTH, 65536);
+}
+
+
+/**
+ * Process an mouse_click event for a status window.
+ *
+ * \param pointer details of the mouse click
+ */
+bool ro_gui_status_bar_click(wimp_pointer *pointer)
+{
+ wimp_drag drag;
+ os_error *error;
+
+ switch (pointer->i) {
+ case ICON_WIDGET:
+ drag.w = pointer->w;
+ drag.type = wimp_DRAG_SYSTEM_SIZE;
+ drag.initial.x0 = pointer->pos.x;
+ drag.initial.x1 = pointer->pos.x;
+ drag.initial.y0 = pointer->pos.y;
+ drag.initial.y1 = pointer->pos.y;
+ error = xwimp_drag_box(&drag);
+ if (error) {
+ LOG("xwimp_drag_box: 0x%x: %s", error->errnum, error->errmess);
+ }
+ break;
+ }
+ return true;
+}
+
+
+/**
+ * Process an open_window request for a status window.
+ *
+ * \param open the request to process
+ */
+void ro_gui_status_bar_open(wimp_open *open)
+{
+ struct status_bar *sb;
+ int window_width, status_width;
+ wimp_window_state state;
+ os_error *error;
+
+ /* get the parent width for scaling */
+ sb = (struct status_bar *)ro_gui_wimp_event_get_user_data(open->w);
+ state.w = sb->parent;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+ window_width = state.visible.x1 - state.visible.x0;
+ if (window_width == 0)
+ window_width = 1;
+ status_width = open->visible.x1 - open->visible.x0;
+ if (status_width <= 12)
+ status_width = 0;
+
+ /* store the new size */
+ sb->scale = (10000 * status_width) / window_width;
+ if (sb->scale > 10000)
+ sb->scale = 10000;
+ ro_gui_status_bar_resize(sb);
+}
+
+
+/**
+ * Reposition the progress component following a change in the
+ * dimension of the status window.
+ *
+ * \param sb the status bar to update
+ */
+void ro_gui_status_position_progress_bar(struct status_bar *sb)
+{
+ wimp_window_state state;
+ os_error *error;
+ int left, right;
+
+ if (!sb)
+ return;
+ if (ro_gui_progress_bar_get_range(sb->pb) == 0)
+ return;
+
+ /* get the window work area dimensions */
+ state.w = sb->w;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+
+ /* calculate the dimensions */
+ right = state.visible.x1 - WIDGET_WIDTH - 2;
+ left = max(state.visible.x0, right - PROGRESS_WIDTH);
+
+ /* re-open the nested window */
+ state.w = ro_gui_progress_bar_get_window(sb->pb);
+ state.xscroll = 0;
+ state.yscroll = 0;
+ state.next = wimp_TOP;
+ state.visible.x0 = left;
+ state.visible.x1 = right;
+ error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state),
+ sb->w,
+ wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
+ << wimp_CHILD_XORIGIN_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
+ << wimp_CHILD_YORIGIN_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
+ << wimp_CHILD_LS_EDGE_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
+ << wimp_CHILD_BS_EDGE_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
+ << wimp_CHILD_RS_EDGE_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
+ << wimp_CHILD_TS_EDGE_SHIFT);
+ if (error) {
+ LOG("xwimp_open_window: 0x%x: %s", error->errnum, error->errmess);
+ }
+
+ /* update the progress bar display on non-standard width */
+ if ((right - left) != PROGRESS_WIDTH)
+ ro_gui_progress_bar_update(sb->pb, right - left,
+ state.visible.y1 - state.visible.y0);
+}
diff --git a/frontends/riscos/gui/status_bar.h b/frontends/riscos/gui/status_bar.h
new file mode 100644
index 000000000..8b5bb35aa
--- /dev/null
+++ b/frontends/riscos/gui/status_bar.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2006 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * UTF8 status bar (interface).
+ */
+
+#ifndef _NETSURF_RISCOS_STATUS_BAR_H_
+#define _NETSURF_RISCOS_STATUS_BAR_H_
+
+#include <stdbool.h>
+
+struct status_bar;
+
+struct status_bar *ro_gui_status_bar_create(wimp_w parent, unsigned int width);
+void ro_gui_status_bar_destroy(struct status_bar *sb);
+
+wimp_w ro_gui_status_bar_get_window(struct status_bar *sb);
+unsigned int ro_gui_status_bar_get_width(struct status_bar *sb);
+void ro_gui_status_bar_resize(struct status_bar *sb);
+void ro_gui_status_bar_set_visible(struct status_bar *pb, bool visible);
+bool ro_gui_status_bar_get_visible(struct status_bar *sb);
+void ro_gui_status_bar_set_text(struct status_bar *sb, const char *text);
+void ro_gui_status_bar_set_progress_value(struct status_bar *sb,
+ unsigned int value);
+void ro_gui_status_bar_set_progress_range(struct status_bar *sb,
+ unsigned int range);
+void ro_gui_status_bar_set_progress_icon(struct status_bar *sb,
+ const char *icon);
+#endif
diff --git a/frontends/riscos/gui/throbber.c b/frontends/riscos/gui/throbber.c
new file mode 100644
index 000000000..a326f806c
--- /dev/null
+++ b/frontends/riscos/gui/throbber.c
@@ -0,0 +1,418 @@
+/*
+ * Copyright 2004, 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2011 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Throbber (implementation).
+ */
+
+#include <alloca.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include "oslib/os.h"
+#include "oslib/osspriteop.h"
+#include "oslib/wimp.h"
+
+#include "utils/log.h"
+#include "riscos/gui.h"
+
+#include "riscos/gui/throbber.h"
+#include "riscos/theme.h"
+#include "riscos/wimp.h"
+
+#define THROBBER_SPRITE_NAME_LENGTH 12
+#define THROBBER_ANIMATE_INTERVAL 10
+
+struct throbber {
+ /** The applied theme (or NULL to use the default) */
+ struct theme_descriptor *theme;
+
+ /** The widget dimensions. */
+ int x_min, y_min;
+
+ /** The window and icon details. */
+ wimp_w window;
+ wimp_i icon;
+ os_box extent;
+ osspriteop_area *sprites;
+ bool hidden;
+ bool shaded;
+
+ /** The animation details. */
+ int max_frame;
+ int current_frame;
+ os_t last_update;
+ char sprite_name[THROBBER_SPRITE_NAME_LENGTH];
+ bool force_redraw;
+};
+
+/*
+ * Private function prototypes.
+ */
+
+static bool ro_gui_throbber_icon_update(struct throbber *throbber);
+static bool ro_gui_throbber_icon_resize(struct throbber *throbber);
+
+/* This is an exported interface documented in throbber.h */
+
+struct throbber *ro_gui_throbber_create(struct theme_descriptor *theme)
+{
+ struct throbber *throbber;
+
+ /* Allocate memory. */
+
+ throbber = malloc(sizeof(struct throbber));
+ if (throbber == NULL) {
+ LOG("No memory for malloc()");
+ return NULL;
+ }
+
+ /* Set up default parameters. If reading the throbber theme data
+ * fails, we give up and return a failure.
+ */
+
+ if (!ro_gui_theme_get_throbber_data(theme, &throbber->max_frame,
+ &throbber->x_min, &throbber->y_min, NULL,
+ &throbber->force_redraw)) {
+ free(throbber);
+ return NULL;
+ }
+
+ throbber->sprites = ro_gui_theme_get_sprites(theme);
+
+ throbber->theme = theme;
+
+ throbber->extent.x0 = 0;
+ throbber->extent.y0 = 0;
+ throbber->extent.x1 = 0;
+ throbber->extent.y1 = 0;
+
+ throbber->current_frame = 0;
+ throbber->last_update = 0;
+
+ throbber->window = NULL;
+ throbber->icon = -1;
+
+ throbber->hidden = false;
+ throbber->shaded = false;
+
+ return throbber;
+}
+
+
+/* This is an exported interface documented in throbber.h */
+
+bool ro_gui_throbber_rebuild(struct throbber *throbber,
+ struct theme_descriptor *theme, theme_style style,
+ wimp_w window, bool shaded)
+{
+ if (throbber == NULL)
+ return false;
+
+ throbber->theme = theme;
+ throbber->window = window;
+ throbber->sprites = ro_gui_theme_get_sprites(theme);
+
+ throbber->icon = -1;
+
+ throbber->shaded = shaded;
+
+ strcpy(throbber->sprite_name, "throbber0");
+
+ if (!ro_gui_theme_get_throbber_data(theme, &throbber->max_frame,
+ &throbber->x_min, &throbber->y_min, NULL,
+ &throbber->force_redraw)) {
+ free(throbber);
+ return false;
+ }
+
+ return ro_gui_throbber_icon_update(throbber);
+}
+
+
+/* This is an exported interface documented in throbber.h */
+
+void ro_gui_throbber_destroy(struct throbber *throbber)
+{
+ if (throbber == NULL)
+ return;
+
+ free(throbber);
+}
+
+
+/* This is an exported interface documented in throbber.h */
+
+bool ro_gui_throbber_get_dims(struct throbber *throbber,
+ int *width, int *height)
+{
+ if (throbber == NULL)
+ return false;
+
+ if (throbber->x_min != -1 && throbber->y_min != -1) {
+ if (width != NULL)
+ *width = throbber->x_min;
+ if (height != NULL)
+ *height = throbber->y_min;
+
+ return true;
+ }
+
+ return false;
+}
+
+
+/* This is an exported interface documented in throbber.h */
+
+bool ro_gui_throbber_set_extent(struct throbber *throbber,
+ int x0, int y0, int x1, int y1)
+{
+ if (throbber == NULL)
+ return false;
+
+ if ((x1 - x0) < throbber->x_min || (y1 - y0) < throbber->y_min)
+ return false;
+
+ if (throbber->extent.x0 == x0 && throbber->extent.y0 == y0 &&
+ throbber->extent.x1 == x1 &&
+ throbber->extent.y1 == y1)
+ return true;
+
+ /* Redraw the relevant bits of the toolbar. */
+
+ if (throbber->window != NULL && throbber->icon != -1) {
+ xwimp_force_redraw(throbber->window,
+ throbber->extent.x0, throbber->extent.y0,
+ throbber->extent.x1, throbber->extent.y1);
+ xwimp_force_redraw(throbber->window, x0, y0, x1, y1);
+ }
+
+ /* Update the throbber position */
+
+ throbber->extent.x0 = x0;
+ throbber->extent.y0 = y0;
+ throbber->extent.x1 = x1;
+ throbber->extent.y1 = y1;
+
+ return ro_gui_throbber_icon_resize(throbber);
+}
+
+
+/**
+ * Create or delete a throbber's icon if required to bring it into sync with
+ * the current hidden setting.
+ *
+ * \param *throbber The throbber to update.
+ * \return true if successful; else false.
+ */
+
+bool ro_gui_throbber_icon_update(struct throbber *throbber)
+{
+ wimp_icon_create icon;
+ os_error *error;
+
+ if (throbber == NULL || throbber->window == NULL)
+ return false;
+
+ if (!throbber->hidden && throbber->icon == -1) {
+ icon.w = throbber->window;
+ icon.icon.extent.x0 = throbber->extent.x0;
+ icon.icon.extent.y0 = throbber->extent.y0;
+ icon.icon.extent.x1 = throbber->extent.x1;
+ icon.icon.extent.y1 = throbber->extent.y1;
+ icon.icon.flags = wimp_ICON_SPRITE | wimp_ICON_INDIRECTED |
+ wimp_ICON_HCENTRED | wimp_ICON_VCENTRED;
+ icon.icon.data.indirected_sprite.id =
+ (osspriteop_id) throbber->sprite_name;
+ icon.icon.data.indirected_sprite.area = throbber->sprites;
+ icon.icon.data.indirected_sprite.size =
+ THROBBER_SPRITE_NAME_LENGTH;
+
+ error = xwimp_create_icon(&icon, &throbber->icon);
+ if (error != NULL) {
+ LOG("xwimp_create_icon: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ throbber->icon = -1;
+ return false;
+ }
+
+ if (!ro_gui_throbber_icon_resize(throbber))
+ return false;
+ } else if (throbber->hidden && throbber->icon != -1) {
+ error = xwimp_delete_icon(throbber->window, throbber->icon);
+ if (error != NULL) {
+ LOG("xwimp_delete_icon: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ throbber->icon = -1;
+ }
+
+ if (throbber->icon != -1)
+ ro_gui_set_icon_shaded_state(throbber->window,
+ throbber->icon, throbber->shaded);
+
+ return true;
+}
+
+
+/**
+ * Position the icons in the throbber to take account of the currently
+ * configured extent.
+ *
+ * \param *throbber The throbber to update.
+ * \return true if successful; else false.
+ */
+
+bool ro_gui_throbber_icon_resize(struct throbber *throbber)
+{
+
+ if (throbber->window == NULL)
+ return false;
+
+ if (throbber->icon != -1) {
+ os_error *error;
+ error = xwimp_resize_icon(throbber->window, throbber->icon,
+ throbber->extent.x0, throbber->extent.y0,
+ throbber->extent.x1, throbber->extent.y1);
+ if (error != NULL) {
+ LOG("xwimp_resize_icon: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ throbber->icon = -1;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+/* This is an exported interface documented in throbber.h */
+
+bool ro_gui_throbber_hide(struct throbber *throbber, bool hide)
+{
+ if (throbber == NULL || throbber->hidden == hide)
+ return (throbber == NULL) ? false : true;
+
+ throbber->hidden = hide;
+
+ return ro_gui_throbber_icon_update(throbber);
+}
+
+
+/* This is an exported interface documented in throbber.h */
+
+bool ro_gui_throbber_help_suffix(struct throbber *throbber, wimp_i i,
+ os_coord *mouse, wimp_window_state *state,
+ wimp_mouse_state buttons, const char **suffix)
+{
+ os_coord pos;
+
+ if (throbber == NULL || throbber->hidden)
+ return false;
+
+ /* Check that the click was within our part of the window. */
+
+ pos.x = mouse->x - state->visible.x0 + state->xscroll;
+ pos.y = mouse->y - state->visible.y1 + state->yscroll;
+
+ if (pos.x < throbber->extent.x0 || pos.x > throbber->extent.x1 ||
+ pos.y < throbber->extent.y0 ||
+ pos.y > throbber->extent.y1)
+ return false;
+
+ /* Return a hard-coded icon number that matches the one that was
+ * always allocated to the throbber in a previous implementation.
+ * If Messages can be updated, this could be changed.
+ */
+
+ if (i == throbber->icon)
+ *suffix = "16";
+ else
+ *suffix = "";
+
+ return true;
+}
+
+
+/* This is an exported interface documented in throbber.h */
+
+bool ro_gui_throbber_animate(struct throbber *throbber)
+{
+ os_t t;
+ char sprite_name[THROBBER_SPRITE_NAME_LENGTH];
+
+ if (throbber == NULL || throbber->hidden)
+ return (throbber == NULL) ? false : true;
+
+ xos_read_monotonic_time(&t);
+
+ /* Drop out if we're not ready for the next frame, unless this
+ * call is to start animation from a stopped throbber (ie. if
+ * the current frame is 0).
+ */
+
+ if ((t < (throbber->last_update + THROBBER_ANIMATE_INTERVAL)) &&
+ (throbber->current_frame > 0))
+ return true;
+
+ throbber->last_update = t;
+ throbber->current_frame++;
+
+ if (throbber->current_frame > throbber->max_frame)
+ throbber->current_frame = 1;
+
+ snprintf(sprite_name, THROBBER_SPRITE_NAME_LENGTH,
+ "throbber%i", throbber->current_frame);
+ ro_gui_set_icon_string(throbber->window, throbber->icon,
+ sprite_name, true);
+
+ if (throbber->force_redraw)
+ ro_gui_force_redraw_icon(throbber->window, throbber->icon);
+
+ return true;
+}
+
+
+/* This is an exported interface documented in throbber.h */
+
+bool ro_gui_throbber_stop(struct throbber *throbber)
+{
+ char sprite_name[THROBBER_SPRITE_NAME_LENGTH];
+
+ if (throbber == NULL || throbber->hidden ||
+ throbber->current_frame == 0)
+ return (throbber == FALSE) ? false : true;
+
+ throbber->current_frame = 0;
+ throbber->last_update = 0;
+
+ strcpy(sprite_name, "throbber0");
+ ro_gui_set_icon_string(throbber->window, throbber->icon,
+ sprite_name, true);
+
+ if (throbber->force_redraw)
+ ro_gui_force_redraw_icon(throbber->window, throbber->icon);
+
+ return true;
+}
+
diff --git a/frontends/riscos/gui/throbber.h b/frontends/riscos/gui/throbber.h
new file mode 100644
index 000000000..6b2419b6e
--- /dev/null
+++ b/frontends/riscos/gui/throbber.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2011 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Throbber (interface).
+ */
+
+#ifndef _NETSURF_RISCOS_THROBBER_H_
+#define _NETSURF_RISCOS_THROBBER_H_
+
+#include <stdbool.h>
+#include "riscos/theme.h"
+
+struct throbber;
+
+
+/**
+ * Create a new throbber widget.
+ *
+ * \param *theme The theme to apply (or NULL for the default).
+ * \return A throbber handle, or NULL on failure.
+ */
+
+struct throbber *ro_gui_throbber_create(struct theme_descriptor *theme);
+
+/**
+ * Place a throbber into a toolbar window and initialise any theme-specific
+ * settings. Any previous incarnation of the throbber will be forgotten: this
+ * is for use when a new toolbar is being created, or when a toolbar has been
+ * deleted and rebuilt following a theme change.
+ *
+ * \param *throbber The throbber to rebuild.
+ * \param *theme The theme to apply (or NULL for current).
+ * \param style The theme style to apply.
+ * \param window The window that the throbber is in.
+ * \param shaded true if the bar should be throbber; else false.
+ * \return true on success; else false.
+ */
+
+bool ro_gui_throbber_rebuild(struct throbber *throbber,
+ struct theme_descriptor *theme, theme_style style,
+ wimp_w window, bool shaded);
+
+
+/**
+ * Destroy a throbber widget.
+ *
+ * \param *throbber The throbber to destroy.
+ */
+
+void ro_gui_throbber_destroy(struct throbber *throbber);
+
+
+/**
+ * Return the MINIMUM dimensions required by the throbber, in RO units,
+ * allowing for the current theme.
+ *
+ * \param *throbber The throbber of interest.
+ * \param *width Return the required width.
+ * \param *height Return the required height.
+ * \return true if values are returned; else false.
+ */
+
+bool ro_gui_throbber_get_dims(struct throbber *throbber,
+ int *width, int *height);
+
+
+/**
+ * Set or update the dimensions to be used by the throbber in RO units
+ *
+ * If these are greater than the minimum required, the throbber will fill
+ * the extended space; if less, the call will fail.
+ *
+ * \param throbber The throbber to update.
+ * \param x0 top left of bounding box x coordinate
+ * \param y0 top left of bounding box y coordinate
+ * \param x1 bottom right of bounding box x coordinate
+ * \param y1 bottom right of bounding box y coordinate
+ * \return true if size updated; else false.
+ */
+
+bool ro_gui_throbber_set_extent(struct throbber *throbber,
+ int x0, int y0, int x1, int y1);
+
+
+/**
+ * Show or hide a throbber.
+ *
+ * \param *throbber The throbber to hide.
+ * \param hide true to hide the throbber; false to show it.
+ * \return true if successful; else false.
+ */
+
+bool ro_gui_throbber_hide(struct throbber *throbber, bool hide);
+
+
+/**
+ * Translate mouse data into an interactive help message for the throbber.
+ *
+ * \param throbber The throbber to process.
+ * \param i The wimp icon under the pointer.
+ * \param screenpos The screen position.
+ * \param state The toolbar window state.
+ * \param buttons The mouse button state.
+ * \param suffix Return a help token suffix, or "" for none.
+ * \return true if handled exclusively; else false.
+ */
+
+bool ro_gui_throbber_help_suffix(struct throbber *throbber, wimp_i i,
+ os_coord *screenpos, wimp_window_state *state,
+ wimp_mouse_state buttons, const char **suffix);
+
+/**
+ * Start or update the amimation of a throbber.
+ *
+ * \param *throbber The throbber to amimate.
+ */
+
+bool ro_gui_throbber_animate(struct throbber *throbber);
+
+
+/**
+ * Stop the amimation of a throbber.
+ *
+ * \param *throbber The throbber to amimate.
+ */
+
+bool ro_gui_throbber_stop(struct throbber *throbber);
+
+#endif
+
diff --git a/frontends/riscos/gui/url_bar.c b/frontends/riscos/gui/url_bar.c
new file mode 100644
index 000000000..053014784
--- /dev/null
+++ b/frontends/riscos/gui/url_bar.c
@@ -0,0 +1,1344 @@
+/*
+ * Copyright 2004, 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2011-2014 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * URL bars (implementation).
+ */
+
+#include <alloca.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include "oslib/os.h"
+#include "oslib/osspriteop.h"
+#include "oslib/wimp.h"
+
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utf8.h"
+#include "utils/utils.h"
+#include "content/hlcache.h"
+#include "content/content.h"
+#include "desktop/browser.h"
+#include "desktop/plotters.h"
+
+#include "riscos/gui.h"
+#include "riscos/hotlist.h"
+#include "riscos/gui/url_bar.h"
+#include "riscos/theme.h"
+#include "riscos/url_suggest.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/window.h"
+#include "riscos/ucstables.h"
+#include "riscos/filetype.h"
+
+#define URLBAR_HEIGHT 52
+#define URLBAR_FAVICON_SIZE 16
+#define URLBAR_HOTLIST_SIZE 17
+#define URLBAR_FAVICON_WIDTH ((5 + URLBAR_FAVICON_SIZE + 5) * 2)
+#define URLBAR_HOTLIST_WIDTH ((5 + URLBAR_HOTLIST_SIZE + 5) * 2)
+#define URLBAR_MIN_WIDTH 52
+#define URLBAR_GRIGHT_GUTTER 8
+#define URLBAR_FAVICON_NAME_LENGTH 12
+
+struct url_bar {
+ /** The applied theme (or NULL to use the default) */
+ struct theme_descriptor *theme;
+
+ /** The widget dimensions. */
+ int x_min, y_min;
+
+ /** The window and icon details. */
+ wimp_w window;
+ os_box extent;
+
+ wimp_i container_icon;
+
+ char favicon_sprite[URLBAR_FAVICON_NAME_LENGTH];
+ int favicon_type;
+ struct hlcache_handle *favicon_content;
+ os_box favicon_extent;
+ os_coord favicon_offset;
+ int favicon_width, favicon_height;
+
+ wimp_i text_icon;
+ char *text_buffer;
+ size_t text_size;
+ char *text_buffer_utf8;
+
+ wimp_i suggest_icon;
+ int suggest_x, suggest_y;
+
+ bool hidden;
+ bool display;
+ bool shaded;
+
+ struct {
+ bool set;
+ os_box extent;
+ os_coord offset;
+ } hotlist;
+};
+
+static char text_validation[] = "Pptr_write;KN";
+static char suggest_icon[] = "gright";
+static char suggest_validation[] = "R5;Sgright,pgright";
+static char null_text_string[] = "";
+
+struct url_bar_resource {
+ const char *url;
+ struct hlcache_handle *c;
+ int height;
+ bool ready;
+}; /**< Treeview content resource data */
+enum url_bar_resource_id {
+ URLBAR_RES_HOTLIST_ADD = 0,
+ URLBAR_RES_HOTLIST_REMOVE,
+ URLBAR_RES_LAST
+};
+static struct url_bar_resource url_bar_res[URLBAR_RES_LAST] = {
+ { "resource:icons/hotlist-add.png", NULL, 0, false },
+ { "resource:icons/hotlist-rmv.png", NULL, 0, false }
+}; /**< Treeview content resources */
+
+
+static void ro_gui_url_bar_set_hotlist(struct url_bar *url_bar, bool set);
+
+
+/* This is an exported interface documented in url_bar.h */
+
+struct url_bar *ro_gui_url_bar_create(struct theme_descriptor *theme)
+{
+ struct url_bar *url_bar;
+
+ /* Allocate memory. */
+
+ url_bar = malloc(sizeof(struct url_bar));
+ if (url_bar == NULL) {
+ LOG("No memory for malloc()");
+ return NULL;
+ }
+
+ /* Set up default parameters. */
+
+ url_bar->theme = theme;
+
+ url_bar->display = false;
+ url_bar->shaded = false;
+
+ url_bar->x_min = URLBAR_FAVICON_WIDTH + URLBAR_MIN_WIDTH +
+ URLBAR_HOTLIST_WIDTH;
+ url_bar->y_min = URLBAR_HEIGHT;
+
+ url_bar->extent.x0 = 0;
+ url_bar->extent.y0 = 0;
+ url_bar->extent.x1 = 0;
+ url_bar->extent.y1 = 0;
+
+ url_bar->window = NULL;
+ url_bar->container_icon = -1;
+ url_bar->text_icon = -1;
+ url_bar->suggest_icon = -1;
+
+ url_bar->favicon_extent.x0 = 0;
+ url_bar->favicon_extent.y0 = 0;
+ url_bar->favicon_extent.x1 = 0;
+ url_bar->favicon_extent.y1 = 0;
+ url_bar->favicon_width = 0;
+ url_bar->favicon_height = 0;
+ url_bar->favicon_content = NULL;
+ url_bar->favicon_type = 0;
+ strncpy(url_bar->favicon_sprite, "Ssmall_xxx",
+ URLBAR_FAVICON_NAME_LENGTH);
+
+ url_bar->hotlist.set = false;
+ url_bar->hotlist.extent.x0 = 0;
+ url_bar->hotlist.extent.y0 = 0;
+ url_bar->hotlist.extent.x1 = 0;
+ url_bar->hotlist.extent.y1 = 0;
+
+ url_bar->text_size = RO_GUI_MAX_URL_SIZE;
+ url_bar->text_buffer = malloc(url_bar->text_size);
+ if (url_bar->text_buffer == NULL) {
+ free(url_bar);
+ return NULL;
+ }
+ url_bar->text_buffer[0] = 0;
+ url_bar->text_buffer_utf8 = NULL;
+
+ url_bar->hidden = false;
+
+ return url_bar;
+}
+
+
+/**
+ * Position the icons in the URL bar to take account of the currently
+ * configured extent.
+ *
+ * \param *url_bar The URL bar to update.
+ * \param full true to resize everything; false to move only
+ * the right-hand end of the bar.
+ * \return true if successful; else false.
+ */
+
+static bool ro_gui_url_bar_icon_resize(struct url_bar *url_bar, bool full)
+{
+ int x0, y0, x1, y1;
+ int centre;
+ os_error *error;
+ os_coord eig = {1, 1};
+ wimp_caret caret;
+
+ if (url_bar == NULL || url_bar->window == NULL)
+ return false;
+
+ /* calculate 1px in OS units */
+ ro_convert_pixels_to_os_units(&eig, (os_mode) -1);
+
+ /* The vertical centre line of the widget's extent. */
+
+ centre = url_bar->extent.y0 +
+ (url_bar->extent.y1 - url_bar->extent.y0) / 2;
+
+ /* Position the container icon. */
+
+ if (url_bar->container_icon != -1) {
+ x0 = url_bar->extent.x0;
+ x1 = url_bar->extent.x1 -
+ url_bar->suggest_x - URLBAR_GRIGHT_GUTTER;
+
+ y0 = centre - (URLBAR_HEIGHT / 2);
+ y1 = y0 + URLBAR_HEIGHT;
+
+ error = xwimp_resize_icon(url_bar->window,
+ url_bar->container_icon,
+ x0, y0, x1, y1);
+ if (error != NULL) {
+ LOG("xwimp_resize_icon: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ url_bar->container_icon = -1;
+ return false;
+ }
+ }
+
+ /* Position the URL Suggest icon. */
+
+ if (url_bar->suggest_icon != -1) {
+ x0 = url_bar->extent.x1 - url_bar->suggest_x;
+ x1 = url_bar->extent.x1;
+
+ y0 = centre - (url_bar->suggest_y / 2);
+ y1 = y0 + url_bar->suggest_y;
+
+ error = xwimp_resize_icon(url_bar->window,
+ url_bar->suggest_icon,
+ x0, y0, x1, y1);
+ if (error != NULL) {
+ LOG("xwimp_resize_icon: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ url_bar->suggest_icon = -1;
+ return false;
+ }
+ }
+
+ /* Position the Text icon. */
+
+ if (url_bar->text_icon != -1) {
+ x0 = url_bar->extent.x0 + URLBAR_FAVICON_WIDTH;
+ x1 = url_bar->extent.x1 - eig.x - URLBAR_HOTLIST_WIDTH -
+ url_bar->suggest_x - URLBAR_GRIGHT_GUTTER;
+
+ y0 = centre - (URLBAR_HEIGHT / 2) + eig.y;
+ y1 = y0 + URLBAR_HEIGHT - 2 * eig.y;
+
+ error = xwimp_resize_icon(url_bar->window,
+ url_bar->text_icon,
+ x0, y0, x1, y1);
+ if (error != NULL) {
+ LOG("xwimp_resize_icon: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ url_bar->text_icon = -1;
+ return false;
+ }
+
+ if (xwimp_get_caret_position(&caret) == NULL) {
+ if ((caret.w == url_bar->window) &&
+ (caret.i == url_bar->text_icon)) {
+ xwimp_set_caret_position(url_bar->window,
+ url_bar->text_icon, caret.pos.x,
+ caret.pos.y, -1, caret.index);
+ }
+ }
+ }
+
+ /* Position the Favicon icon. */
+
+ url_bar->favicon_extent.x0 = url_bar->extent.x0 + eig.x;
+ url_bar->favicon_extent.x1 = url_bar->extent.x0 + URLBAR_FAVICON_WIDTH;
+ url_bar->favicon_extent.y0 = centre - (URLBAR_HEIGHT / 2) + eig.y;
+ url_bar->favicon_extent.y1 = url_bar->favicon_extent.y0 + URLBAR_HEIGHT
+ - 2 * eig.y;
+
+ /* Position the Hotlist icon. */
+
+ url_bar->hotlist.extent.x0 = url_bar->extent.x1 - eig.x -
+ URLBAR_HOTLIST_WIDTH - url_bar->suggest_x -
+ URLBAR_GRIGHT_GUTTER;
+ url_bar->hotlist.extent.x1 = url_bar->hotlist.extent.x0 +
+ URLBAR_HOTLIST_WIDTH;
+ url_bar->hotlist.extent.y0 = centre - (URLBAR_HEIGHT / 2) + eig.y;
+ url_bar->hotlist.extent.y1 = url_bar->hotlist.extent.y0 + URLBAR_HEIGHT
+ - 2 * eig.y;
+
+ url_bar->hotlist.offset.x = ((url_bar->hotlist.extent.x1 -
+ url_bar->hotlist.extent.x0) -
+ (URLBAR_HOTLIST_SIZE * 2)) / 2;
+ url_bar->hotlist.offset.y = ((url_bar->hotlist.extent.y1 -
+ url_bar->hotlist.extent.y0) -
+ (URLBAR_HOTLIST_SIZE * 2)) / 2 - 1;
+
+ return true;
+}
+
+
+/**
+ * Create or delete a URL bar's icons if required to bring it into sync with
+ * the current hidden setting.
+ *
+ * \param *url_bar The URL bar to update.
+ * \return true if successful; else false.
+ */
+
+static bool ro_gui_url_bar_icon_update(struct url_bar *url_bar)
+{
+ wimp_icon_create icon;
+ os_error *error;
+ bool resize;
+
+ if (url_bar == NULL || url_bar->window == NULL)
+ return false;
+
+ icon.w = url_bar->window;
+ icon.icon.extent.x0 = 0;
+ icon.icon.extent.y0 = 0;
+ icon.icon.extent.x1 = 0;
+ icon.icon.extent.y1 = 0;
+
+ resize = false;
+
+ /* Create or delete the container icon. */
+
+ if (!url_bar->hidden && url_bar->container_icon == -1) {
+ icon.icon.flags = wimp_ICON_BORDER |
+ (wimp_COLOUR_BLACK <<
+ wimp_ICON_FG_COLOUR_SHIFT) |
+ (wimp_BUTTON_DOUBLE_CLICK_DRAG <<
+ wimp_ICON_BUTTON_TYPE_SHIFT);
+ error = xwimp_create_icon(&icon, &url_bar->container_icon);
+ if (error != NULL) {
+ LOG("xwimp_create_icon: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ url_bar->container_icon = -1;
+ return false;
+ }
+
+ resize = true;
+ } else if (url_bar->hidden && url_bar->container_icon != -1){
+ error = xwimp_delete_icon(url_bar->window,
+ url_bar->container_icon);
+ if (error != NULL) {
+ LOG("xwimp_delete_icon: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ url_bar->container_icon = -1;
+ }
+
+ /* Create or delete the text icon. */
+
+ if (!url_bar->hidden && url_bar->text_icon == -1) {
+ icon.icon.data.indirected_text.text = url_bar->text_buffer;
+ icon.icon.data.indirected_text.validation = text_validation;
+ icon.icon.data.indirected_text.size = url_bar->text_size;
+ icon.icon.flags = wimp_ICON_TEXT | wimp_ICON_INDIRECTED |
+ wimp_ICON_VCENTRED | wimp_ICON_FILLED |
+ (wimp_COLOUR_BLACK <<
+ wimp_ICON_FG_COLOUR_SHIFT);
+ if (url_bar->display)
+ icon.icon.flags |= (wimp_BUTTON_NEVER <<
+ wimp_ICON_BUTTON_TYPE_SHIFT);
+ else
+ icon.icon.flags |= (wimp_BUTTON_WRITE_CLICK_DRAG <<
+ wimp_ICON_BUTTON_TYPE_SHIFT);
+ error = xwimp_create_icon(&icon, &url_bar->text_icon);
+ if (error) {
+ LOG("xwimp_create_icon: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ url_bar->text_icon = -1;
+ return false;
+ }
+
+ resize = true;
+ } else if (url_bar->hidden && url_bar->text_icon != -1) {
+ error = xwimp_delete_icon(url_bar->window,
+ url_bar->text_icon);
+ if (error != NULL) {
+ LOG("xwimp_delete_icon: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ url_bar->text_icon = -1;
+ }
+
+ /* Create or delete the suggest icon. */
+
+ if (!url_bar->hidden && url_bar->suggest_icon == -1) {
+ icon.icon.data.indirected_text.text = null_text_string;
+ icon.icon.data.indirected_text.size = 1;
+ icon.icon.data.indirected_text.validation = suggest_validation;
+ icon.icon.flags = wimp_ICON_TEXT | wimp_ICON_SPRITE |
+ wimp_ICON_INDIRECTED | wimp_ICON_HCENTRED |
+ wimp_ICON_VCENTRED | (wimp_BUTTON_CLICK <<
+ wimp_ICON_BUTTON_TYPE_SHIFT);
+ error = xwimp_create_icon(&icon, &url_bar->suggest_icon);
+ if (error) {
+ LOG("xwimp_create_icon: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ if (!url_bar->display)
+ ro_gui_wimp_event_register_menu_gright(url_bar->window,
+ wimp_ICON_WINDOW, url_bar->suggest_icon,
+ ro_gui_url_suggest_menu);
+
+ if (!ro_gui_url_bar_update_urlsuggest(url_bar))
+ return false;
+
+ resize = true;
+ } else if (url_bar->hidden && url_bar->suggest_icon != -1) {
+ ro_gui_wimp_event_deregister(url_bar->window,
+ url_bar->suggest_icon);
+ error = xwimp_delete_icon(url_bar->window,
+ url_bar->suggest_icon);
+ if (error != NULL) {
+ LOG("xwimp_delete_icon: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ url_bar->suggest_icon = -1;
+ }
+
+ /* If any icons were created, resize the bar. */
+
+ if (resize && !ro_gui_url_bar_icon_resize(url_bar, true))
+ return false;
+
+ /* If there are any icons, apply shading as necessary. */
+
+ if (url_bar->container_icon != -1)
+ ro_gui_set_icon_shaded_state(url_bar->window,
+ url_bar->container_icon, url_bar->shaded);
+
+ if (url_bar->text_icon != -1)
+ ro_gui_set_icon_shaded_state(url_bar->window,
+ url_bar->text_icon, url_bar->shaded);
+
+ if (url_bar->suggest_icon != -1)
+ ro_gui_set_icon_shaded_state(url_bar->window,
+ url_bar->suggest_icon, url_bar->shaded);
+
+ return true;
+}
+
+
+/* This is an exported interface documented in url_bar.h */
+
+bool ro_gui_url_bar_rebuild(struct url_bar *url_bar,
+ struct theme_descriptor *theme, theme_style style,
+ wimp_w window, bool display, bool shaded)
+{
+ if (url_bar == NULL)
+ return false;
+
+ url_bar->theme = theme;
+ url_bar->window = window;
+
+ url_bar->display = display;
+ url_bar->shaded = shaded;
+
+ url_bar->container_icon = -1;
+ url_bar->text_icon = -1;
+ url_bar->suggest_icon = -1;
+
+ ro_gui_wimp_get_sprite_dimensions((osspriteop_area *) -1,
+ suggest_icon, &url_bar->suggest_x, &url_bar->suggest_y);
+
+ url_bar->x_min = URLBAR_FAVICON_WIDTH + URLBAR_MIN_WIDTH +
+ URLBAR_HOTLIST_WIDTH + URLBAR_GRIGHT_GUTTER +
+ url_bar->suggest_x;
+ url_bar->y_min = (url_bar->suggest_y > URLBAR_HEIGHT) ?
+ url_bar->suggest_y : URLBAR_HEIGHT;
+
+ return ro_gui_url_bar_icon_update(url_bar);
+}
+
+
+/* This is an exported interface documented in url_bar.h */
+
+void ro_gui_url_bar_destroy(struct url_bar *url_bar)
+{
+ if (url_bar == NULL)
+ return;
+
+ if (url_bar->text_buffer_utf8 != NULL)
+ free(url_bar->text_buffer_utf8);
+
+ if (url_bar->text_buffer != NULL)
+ free(url_bar->text_buffer);
+
+ free(url_bar);
+}
+
+
+/* This is an exported interface documented in url_bar.h */
+
+bool ro_gui_url_bar_get_dims(struct url_bar *url_bar,
+ int *width, int *height)
+{
+ if (url_bar == NULL)
+ return false;
+
+ if (url_bar->x_min != -1 && url_bar->y_min != -1) {
+ if (width != NULL)
+ *width = url_bar->x_min;
+ if (height != NULL)
+ *height = url_bar->y_min;
+
+ return true;
+ }
+
+ return false;
+}
+
+
+/* This is an exported interface documented in url_bar.h */
+
+bool ro_gui_url_bar_set_extent(struct url_bar *url_bar,
+ int x0, int y0, int x1, int y1)
+{
+ bool stretch;
+
+ if (url_bar == NULL)
+ return false;
+
+ if ((x1 - x0) < url_bar->x_min || (y1 - y0) < url_bar->y_min)
+ return false;
+
+ if (url_bar->extent.x0 == x0 && url_bar->extent.y0 == y0 &&
+ url_bar->extent.x1 == x1 &&
+ url_bar->extent.y1 == y1)
+ return true;
+
+ /* If it's only the length that changes, less needs to be updated. */
+
+ stretch = (url_bar->extent.x0 == x0 && url_bar->extent.y0 == y0 &&
+ url_bar->extent.y1 == y1) ? true : false;
+
+ /* Redraw the relevant bits of the toolbar. */
+
+ if (url_bar->window != NULL && !url_bar->hidden) {
+ if (stretch) {
+ xwimp_force_redraw(url_bar->window,
+ x0 + URLBAR_FAVICON_WIDTH, y0,
+ (x1 > url_bar->extent.x1) ?
+ x1 : url_bar->extent.x1, y1);
+ } else {
+ xwimp_force_redraw(url_bar->window,
+ url_bar->extent.x0, url_bar->extent.y0,
+ url_bar->extent.x1, url_bar->extent.y1);
+ xwimp_force_redraw(url_bar->window, x0, y0, x1, y1);
+ }
+ }
+
+ /* Reposition the URL bar icons. */
+
+ url_bar->extent.x0 = x0;
+ url_bar->extent.y0 = y0;
+ url_bar->extent.x1 = x1;
+ url_bar->extent.y1 = y1;
+
+ return ro_gui_url_bar_icon_resize(url_bar, !stretch);
+}
+
+
+/* This is an exported interface documented in url_bar.h */
+
+bool ro_gui_url_bar_hide(struct url_bar *url_bar, bool hide)
+{
+ if (url_bar == NULL || url_bar->hidden == hide)
+ return (url_bar == NULL) ? false : true;
+
+ url_bar->hidden = hide;
+
+ return ro_gui_url_bar_icon_update(url_bar);
+}
+
+
+/* This is an exported interface documented in url_bar.h */
+
+void ro_gui_url_bar_redraw(struct url_bar *url_bar, wimp_draw *redraw)
+{
+ wimp_icon icon;
+ struct rect clip;
+ bool draw_favicon = true;
+ bool draw_hotlist = true;
+
+ /* Test for a valid URL bar */
+ if (url_bar == NULL || url_bar->hidden)
+ return;
+
+ if ((redraw->clip.x0 - (redraw->box.x0 - redraw->xscroll)) >
+ (url_bar->favicon_extent.x1) ||
+ (redraw->clip.y0 - (redraw->box.y1 - redraw->yscroll)) >
+ (url_bar->favicon_extent.y1) ||
+ (redraw->clip.x1 - (redraw->box.x0 - redraw->xscroll)) <
+ (url_bar->favicon_extent.x0) ||
+ (redraw->clip.y1 - (redraw->box.y1 - redraw->yscroll)) <
+ (url_bar->favicon_extent.y0)) {
+ /* Favicon not in redraw area */
+ draw_favicon = false;
+ }
+
+ if ((redraw->clip.x0 - (redraw->box.x0 - redraw->xscroll)) >
+ (url_bar->hotlist.extent.x1) ||
+ (redraw->clip.y0 - (redraw->box.y1 - redraw->yscroll)) >
+ (url_bar->hotlist.extent.y1) ||
+ (redraw->clip.x1 - (redraw->box.x0 - redraw->xscroll)) <
+ (url_bar->hotlist.extent.x0) ||
+ (redraw->clip.y1 - (redraw->box.y1 - redraw->yscroll)) <
+ (url_bar->hotlist.extent.y0)) {
+ /* Hotlist icon not in redraw area */
+ draw_hotlist = false;
+ }
+
+ if (draw_favicon) {
+ if (url_bar->favicon_content == NULL) {
+ icon.data.indirected_text.text = null_text_string;
+ icon.data.indirected_text.validation =
+ url_bar->favicon_sprite;
+ icon.data.indirected_text.size = 1;
+ icon.flags = wimp_ICON_TEXT | wimp_ICON_SPRITE |
+ wimp_ICON_INDIRECTED |
+ wimp_ICON_FILLED |
+ wimp_ICON_HCENTRED |
+ wimp_ICON_VCENTRED;
+ icon.extent.x0 = url_bar->favicon_extent.x0;
+ icon.extent.x1 = url_bar->favicon_extent.x1;
+ icon.extent.y0 = url_bar->favicon_extent.y0;
+ icon.extent.y1 = url_bar->favicon_extent.y1;
+
+ xwimp_plot_icon(&icon);
+ } else {
+ struct content_redraw_data data;
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &ro_plotters
+ };
+
+ xwimp_set_colour(wimp_COLOUR_WHITE);
+ xos_plot(os_MOVE_TO,
+ (redraw->box.x0 - redraw->xscroll) +
+ url_bar->favicon_extent.x0,
+ (redraw->box.y1 - redraw->yscroll) +
+ url_bar->favicon_extent.y0);
+ xos_plot(os_PLOT_TO | os_PLOT_RECTANGLE,
+ (redraw->box.x0 - redraw->xscroll) +
+ url_bar->favicon_extent.x1,
+ (redraw->box.y1 - redraw->yscroll) +
+ url_bar->favicon_extent.y1);
+
+ clip.x0 = (redraw->clip.x0 - ro_plot_origin_x) / 2;
+ clip.y0 = (ro_plot_origin_y - redraw->clip.y0) / 2;
+ clip.x1 = (redraw->clip.x1 - ro_plot_origin_x) / 2;
+ clip.y1 = (ro_plot_origin_y - redraw->clip.y1) / 2;
+
+ data.x = (url_bar->favicon_extent.x0 +
+ url_bar->favicon_offset.x) / 2;
+ data.y = (url_bar->favicon_offset.y -
+ url_bar->favicon_extent.y1) / 2;
+ data.width = url_bar->favicon_width;
+ data.height = url_bar->favicon_height;
+ data.background_colour = 0xFFFFFF;
+ data.scale = 1;
+ data.repeat_x = false;
+ data.repeat_y = false;
+
+ content_redraw(url_bar->favicon_content,
+ &data, &clip, &ctx);
+ }
+ }
+
+ if (draw_hotlist) {
+ struct content_redraw_data data;
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &ro_plotters
+ };
+ struct url_bar_resource *hotlist_icon = url_bar->hotlist.set ?
+ &(url_bar_res[URLBAR_RES_HOTLIST_REMOVE]) :
+ &(url_bar_res[URLBAR_RES_HOTLIST_ADD]);
+
+ xwimp_set_colour(wimp_COLOUR_WHITE);
+ xos_plot(os_MOVE_TO,
+ (redraw->box.x0 - redraw->xscroll) +
+ url_bar->hotlist.extent.x0,
+ (redraw->box.y1 - redraw->yscroll) +
+ url_bar->hotlist.extent.y0);
+ xos_plot(os_PLOT_TO | os_PLOT_RECTANGLE,
+ (redraw->box.x0 - redraw->xscroll) +
+ url_bar->hotlist.extent.x1,
+ (redraw->box.y1 - redraw->yscroll) +
+ url_bar->hotlist.extent.y1);
+
+ if (hotlist_icon->ready == false) {
+ return;
+ }
+
+ clip.x0 = (redraw->clip.x0 - ro_plot_origin_x) / 2;
+ clip.y0 = (ro_plot_origin_y - redraw->clip.y0) / 2;
+ clip.x1 = (redraw->clip.x1 - ro_plot_origin_x) / 2;
+ clip.y1 = (ro_plot_origin_y - redraw->clip.y1) / 2;
+
+ data.x = (url_bar->hotlist.extent.x0 +
+ url_bar->hotlist.offset.x) / 2;
+ data.y = (url_bar->hotlist.offset.y -
+ url_bar->hotlist.extent.y1) / 2;
+ data.width = URLBAR_HOTLIST_SIZE;
+ data.height = URLBAR_HOTLIST_SIZE;
+ data.background_colour = 0xFFFFFF;
+ data.scale = 1;
+ data.repeat_x = false;
+ data.repeat_y = false;
+
+ content_redraw(hotlist_icon->c, &data, &clip, &ctx);
+ }
+}
+
+
+/* This is an exported interface documented in url_bar.h */
+
+bool ro_gui_url_bar_click(struct url_bar *url_bar,
+ wimp_pointer *pointer, wimp_window_state *state,
+ url_bar_action *action)
+{
+ os_coord pos;
+
+ if (url_bar == NULL || url_bar->hidden ||
+ url_bar->display || url_bar->shaded)
+ return false;
+
+ /* Check that the click was within our part of the window. */
+
+ pos.x = pointer->pos.x - state->visible.x0 + state->xscroll;
+ pos.y = pointer->pos.y - state->visible.y1 + state->yscroll;
+
+ if (pos.x < url_bar->extent.x0 || pos.x > url_bar->extent.x1 ||
+ pos.y < url_bar->extent.y0 ||
+ pos.y > url_bar->extent.y1)
+ return false;
+
+ /* If we have a Select or Adjust click, check if it originated on the
+ * hotlist icon; if it did, return an event.
+ */
+
+ if (pointer->buttons == wimp_SINGLE_SELECT ||
+ pointer->buttons == wimp_SINGLE_ADJUST) {
+ if (pos.x >= url_bar->hotlist.extent.x0 &&
+ pos.x <= url_bar->hotlist.extent.x1 &&
+ pos.y >= url_bar->hotlist.extent.y0 &&
+ pos.y <= url_bar->hotlist.extent.y1) {
+ if (pointer->buttons == wimp_SINGLE_SELECT &&
+ action != NULL)
+ *action = TOOLBAR_URL_SELECT_HOTLIST;
+ else if (pointer->buttons == wimp_SINGLE_ADJUST &&
+ action != NULL)
+ *action = TOOLBAR_URL_ADJUST_HOTLIST;
+ return true;
+ }
+ }
+
+ /* If we find a Select or Adjust drag, check if it originated on the
+ * URL bar or over the favicon. If either, then return an event.
+ */
+
+ if (pointer->buttons == wimp_DRAG_SELECT ||
+ pointer->buttons == wimp_DRAG_ADJUST) {
+ if (pointer->i == url_bar->text_icon) {
+ if (action != NULL)
+ *action = TOOLBAR_URL_DRAG_URL;
+ return true;
+ }
+
+ if (pos.x >= url_bar->favicon_extent.x0 &&
+ pos.x <= url_bar->favicon_extent.x1 &&
+ pos.y >= url_bar->favicon_extent.y0 &&
+ pos.y <= url_bar->favicon_extent.y1) {
+ if (action != NULL)
+ *action = TOOLBAR_URL_DRAG_FAVICON;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/* This is an exported interface documented in url_bar.h */
+
+bool ro_gui_url_bar_menu_prepare(struct url_bar *url_bar, wimp_i i,
+ wimp_menu *menu, wimp_pointer *pointer)
+{
+ if (url_bar == NULL || url_bar->suggest_icon != i ||
+ menu != ro_gui_url_suggest_menu)
+ return false;
+
+ if (pointer != NULL)
+ return ro_gui_url_suggest_prepare_menu();
+
+ return true;
+}
+
+
+/* This is an exported interface documented in url_bar.h */
+
+bool ro_gui_url_bar_menu_select(struct url_bar *url_bar, wimp_i i,
+ wimp_menu *menu, wimp_selection *selection, menu_action action)
+{
+ const char *urltxt;
+ struct gui_window *g;
+
+ if (url_bar == NULL || url_bar->suggest_icon != i ||
+ menu != ro_gui_url_suggest_menu)
+ return false;
+
+ urltxt = ro_gui_url_suggest_get_selection(selection);
+ g = ro_gui_toolbar_lookup(url_bar->window);
+
+ if (urltxt != NULL && g != NULL && g->bw != NULL) {
+ nsurl *url;
+ nserror error;
+
+ error = nsurl_create(urltxt, &url);
+ if (error != NSERROR_OK) {
+ ro_warn_user(messages_get_errorcode(error), 0);
+ } else {
+ ro_gui_window_set_url(g, url);
+
+ browser_window_navigate(g->bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ }
+
+ return true;
+}
+
+
+/* This is an exported interface documented in url_bar.h */
+
+bool ro_gui_url_bar_help_suffix(struct url_bar *url_bar, wimp_i i,
+ os_coord *mouse, wimp_window_state *state,
+ wimp_mouse_state buttons, const char **suffix)
+{
+ os_coord pos;
+
+ if (url_bar == NULL || url_bar->hidden)
+ return false;
+
+ /* Check that the click was within our part of the window. */
+
+ pos.x = mouse->x - state->visible.x0 + state->xscroll;
+ pos.y = mouse->y - state->visible.y1 + state->yscroll;
+
+ if (pos.x < url_bar->extent.x0 || pos.x > url_bar->extent.x1 ||
+ pos.y < url_bar->extent.y0 ||
+ pos.y > url_bar->extent.y1)
+ return false;
+
+ /* Return hard-coded icon numbers that match the ones that were
+ * always allocated to the URL bar in a previous implementation.
+ * If Messages can be updated, this could be changed.
+ */
+
+ if (i == url_bar->text_icon)
+ *suffix = "14";
+ else if (i == url_bar->suggest_icon)
+ *suffix = "15";
+ else if (pos.x >= url_bar->hotlist.extent.x0 &&
+ pos.x <= url_bar->hotlist.extent.x1 &&
+ pos.y >= url_bar->hotlist.extent.y0 &&
+ pos.y <= url_bar->hotlist.extent.y1)
+ *suffix = "Hot";
+ else if (pos.x >= url_bar->favicon_extent.x0 &&
+ pos.x <= url_bar->favicon_extent.x1 &&
+ pos.y >= url_bar->favicon_extent.y0 &&
+ pos.y <= url_bar->favicon_extent.y1)
+ *suffix = "Fav";
+ else
+ *suffix = "";
+
+ return true;
+}
+
+
+/* This is an exported interface documented in url_bar.h */
+
+bool ro_gui_url_bar_take_caret(struct url_bar *url_bar)
+{
+ os_error *error;
+
+ if (url_bar == NULL || url_bar->hidden)
+ return false;
+
+ error = xwimp_set_caret_position(url_bar->window, url_bar->text_icon,
+ -1, -1, -1, 0);
+ if (error) {
+ LOG("xwimp_set_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+
+ return false;
+ }
+
+ return true;
+}
+
+
+/* This is an exported interface documented in url_bar.h */
+
+void ro_gui_url_bar_set_url(struct url_bar *url_bar, const char *url,
+ bool is_utf8, bool set_caret)
+{
+ wimp_caret caret;
+ os_error *error;
+ char *local_text = NULL;
+ const char *local_url;
+ nsurl *n;
+
+ if (url_bar == NULL || url_bar->text_buffer == NULL || url == NULL)
+ return;
+
+ /* Before we do anything with the URL, get it into local encoding so
+ * that behaviour is consistant with the rest of the URL Bar module
+ * (which will act on the icon's text buffer, which is always in local
+ * encoding).
+ */
+
+ if (is_utf8) {
+ nserror err;
+
+ err = utf8_to_local_encoding(url, 0, &local_text);
+ if (err != NSERROR_OK) {
+ /* A bad encoding should never happen, so assert this */
+ assert(err != NSERROR_BAD_ENCODING);
+ LOG("utf8_to_enc failed");
+ /* Paranoia */
+ local_text = NULL;
+ }
+ local_url = (local_text != NULL) ? local_text : url;
+ } else {
+ local_url = url;
+ }
+
+ /* Copy the text into the icon buffer. If the text is too long, blank
+ * the buffer and warn the user.
+ */
+
+ if (strlen(local_url) >= url_bar->text_size) {
+ url_bar->text_buffer[0] = '\0';
+ ro_warn_user("LongURL", NULL);
+ LOG("Long URL (%zu chars): %s", strlen(url), url);
+ } else {
+ strncpy(url_bar->text_buffer, local_url,
+ url_bar->text_size - 1);
+ url_bar->text_buffer[url_bar->text_size - 1] = '\0';
+ }
+
+ if (local_text != NULL)
+ free(local_text);
+
+ /* Set the hotlist flag. */
+
+ if (nsurl_create(url_bar->text_buffer, &n) == NSERROR_OK) {
+ ro_gui_url_bar_set_hotlist(url_bar, ro_gui_hotlist_has_page(n));
+ nsurl_unref(n);
+ }
+
+ /* If there's no icon, then there's nothing else to do... */
+
+ if (url_bar->text_icon == -1)
+ return;
+
+ /* ...if there is, redraw the icon and fix the caret's position. */
+
+ ro_gui_redraw_icon(url_bar->window, url_bar->text_icon);
+
+ error = xwimp_get_caret_position(&caret);
+ if (error) {
+ LOG("xwimp_get_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ if (set_caret || (caret.w == url_bar->window &&
+ caret.i == url_bar->text_icon)) {
+ const char *set_url = ro_gui_get_icon_string(url_bar->window,
+ url_bar->text_icon);
+
+ error = xwimp_set_caret_position(url_bar->window,
+ url_bar->text_icon, 0, 0, -1, strlen(set_url));
+ if (error) {
+ LOG("xwimp_set_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ }
+}
+
+
+/* This is an exported interface documented in url_bar.h */
+
+void ro_gui_url_bar_update_hotlist(struct url_bar *url_bar)
+{
+ const char *url;
+ nsurl *n;
+
+ if (url_bar == NULL)
+ return;
+
+ url = (const char *) url_bar->text_buffer;
+ if (url != NULL && nsurl_create(url, &n) == NSERROR_OK) {
+ ro_gui_url_bar_set_hotlist(url_bar, ro_gui_hotlist_has_page(n));
+ nsurl_unref(n);
+ }
+}
+
+
+/**
+ * Set the state of a URL Bar's hotlist icon.
+ *
+ * \param *url_bar The URL Bar to update.
+ * \param set TRUE to set the hotlist icon; FALSE to clear it.
+ */
+
+static void ro_gui_url_bar_set_hotlist(struct url_bar *url_bar, bool set)
+{
+ if (url_bar == NULL || set == url_bar->hotlist.set)
+ return;
+
+ url_bar->hotlist.set = set;
+
+ if (!url_bar->hidden) {
+ xwimp_force_redraw(url_bar->window,
+ url_bar->hotlist.extent.x0,
+ url_bar->hotlist.extent.y0,
+ url_bar->hotlist.extent.x1,
+ url_bar->hotlist.extent.y1);
+ }
+}
+
+
+/* This is an exported interface documented in url_bar.h */
+
+const char *ro_gui_url_bar_get_url(struct url_bar *url_bar)
+{
+ if ((url_bar == NULL) || (url_bar->text_buffer == NULL))
+ return NULL;
+
+ if (url_bar->text_buffer_utf8 != NULL) {
+ free(url_bar->text_buffer_utf8);
+ url_bar->text_buffer_utf8 = NULL;
+ }
+
+ if (url_bar->text_buffer[0] == '\0')
+ return (const char *) url_bar->text_buffer;
+
+ if (utf8_from_local_encoding(url_bar->text_buffer, 0, &url_bar->text_buffer_utf8) == NSERROR_OK) {
+ return (const char *) url_bar->text_buffer_utf8;
+ }
+
+ return (const char *) url_bar->text_buffer;
+}
+
+
+/* This is an exported interface documented in url_bar.h */
+
+bool ro_gui_url_bar_get_url_extent(struct url_bar *url_bar, os_box *extent)
+{
+ wimp_icon_state state;
+ os_error *error;
+
+ if (url_bar == NULL || url_bar->hidden)
+ return false;
+
+ if (extent == NULL)
+ return true;
+
+ state.w = url_bar->window;
+ state.i = url_bar->container_icon;
+ error = xwimp_get_icon_state(&state);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ extent->x0 = state.icon.extent.x0;
+ extent->y0 = state.icon.extent.y0;
+ extent->x1 = state.icon.extent.x1;
+ extent->y1 = state.icon.extent.y1;
+
+ return true;
+}
+
+
+/* This is an exported interface documented in url_bar.h */
+
+bool ro_gui_url_bar_test_for_text_field_click(struct url_bar *url_bar,
+ wimp_pointer *pointer)
+{
+ if (url_bar == NULL || url_bar->hidden || pointer == NULL)
+ return false;
+
+ return (pointer->w == url_bar->window &&
+ pointer->i == url_bar->text_icon) ? true : false;
+}
+
+
+/* This is an exported interface documented in url_bar.h */
+
+bool ro_gui_url_bar_test_for_text_field_keypress(struct url_bar *url_bar,
+ wimp_key *key)
+{
+ const char *url;
+ nsurl *n;
+
+ if (url_bar == NULL || url_bar->hidden || key == NULL)
+ return false;
+
+ if (key->w != url_bar->window || key->i != url_bar->text_icon)
+ return false;
+
+ /* Update hotlist indicator */
+
+ url = (const char *) url_bar->text_buffer;
+ if (url != NULL && nsurl_create(url, &n) == NSERROR_OK) {
+ ro_gui_url_bar_set_hotlist(url_bar, ro_gui_hotlist_has_page(n));
+ nsurl_unref(n);
+ } else if (url_bar->hotlist.set) {
+ ro_gui_url_bar_set_hotlist(url_bar, false);
+ }
+
+ return true;
+}
+
+
+/* This is an exported interface documented in url_bar.h */
+
+bool ro_gui_url_bar_set_site_favicon(struct url_bar *url_bar,
+ struct hlcache_handle *h)
+{
+ content_type type = CONTENT_NONE;
+
+ if (url_bar == NULL)
+ return false;
+
+ if (h != NULL)
+ type = content_get_type(h);
+
+ // \TODO -- Maybe test for CONTENT_ICO ???
+
+ if (type == CONTENT_IMAGE) {
+ url_bar->favicon_content = h;
+ url_bar->favicon_width = content_get_width(h);
+ url_bar->favicon_height = content_get_height(h);
+
+ if (url_bar->favicon_width > URLBAR_FAVICON_SIZE)
+ url_bar->favicon_width = URLBAR_FAVICON_SIZE;
+
+ if (url_bar->favicon_height > URLBAR_FAVICON_SIZE)
+ url_bar->favicon_height = URLBAR_FAVICON_SIZE;
+
+ url_bar->favicon_offset.x = ((url_bar->favicon_extent.x1 -
+ url_bar->favicon_extent.x0) -
+ (url_bar->favicon_width * 2)) / 2;
+ url_bar->favicon_offset.y = ((url_bar->favicon_extent.y1 -
+ url_bar->favicon_extent.y0) -
+ (url_bar->favicon_height * 2)) / 2;
+ } else {
+ url_bar->favicon_content = NULL;
+
+ if (url_bar->favicon_type != 0)
+ snprintf(url_bar->favicon_sprite,
+ URLBAR_FAVICON_NAME_LENGTH,
+ "Ssmall_%.3x", url_bar->favicon_type);
+ else
+ snprintf(url_bar->favicon_sprite,
+ URLBAR_FAVICON_NAME_LENGTH,
+ "Ssmall_xxx");
+ }
+
+ if (!url_bar->hidden)
+ xwimp_force_redraw(url_bar->window,
+ url_bar->favicon_extent.x0,
+ url_bar->favicon_extent.y0,
+ url_bar->favicon_extent.x1,
+ url_bar->favicon_extent.y1);
+
+ return true;
+}
+
+
+/* This is an exported interface documented in url_bar.h */
+
+bool ro_gui_url_bar_set_content_favicon(struct url_bar *url_bar,
+ struct gui_window *g)
+{
+ int type = 0;
+ char sprite[URLBAR_FAVICON_NAME_LENGTH];
+ struct hlcache_handle *h;
+
+ if (url_bar == NULL || g == NULL)
+ return false;
+
+ h = browser_window_get_content(g->bw);
+ if (h != NULL)
+ type = ro_content_filetype(h);
+
+ if (type != 0) {
+ snprintf(sprite, URLBAR_FAVICON_NAME_LENGTH,
+ "small_%.3x", type);
+
+ if (!ro_gui_wimp_sprite_exists(sprite))
+ type = 0;
+ }
+
+ url_bar->favicon_type = type;
+
+ if (url_bar->favicon_content == NULL) {
+ if (type == 0)
+ snprintf(url_bar->favicon_sprite,
+ URLBAR_FAVICON_NAME_LENGTH, "Ssmall_xxx");
+ else
+ snprintf(url_bar->favicon_sprite,
+ URLBAR_FAVICON_NAME_LENGTH, "S%s", sprite);
+
+ if (!url_bar->hidden)
+ xwimp_force_redraw(url_bar->window,
+ url_bar->favicon_extent.x0,
+ url_bar->favicon_extent.y0,
+ url_bar->favicon_extent.x1,
+ url_bar->favicon_extent.y1);
+ }
+
+ return true;
+}
+
+
+/* This is an exported interface documented in url_bar.h */
+
+bool ro_gui_url_bar_update_urlsuggest(struct url_bar *url_bar)
+{
+ if (url_bar == NULL || url_bar->hidden)
+ return (url_bar == NULL) ? false : true;
+
+ if (url_bar->window != NULL && url_bar->suggest_icon != -1)
+ ro_gui_set_icon_shaded_state(url_bar->window,
+ url_bar->suggest_icon,
+ !ro_gui_url_suggest_get_menu_available());
+
+ return true;
+}
+
+
+/**
+ * Callback for hlcache.
+ */
+static nserror ro_gui_url_bar_res_cb(hlcache_handle *handle,
+ const hlcache_event *event, void *pw)
+{
+ struct url_bar_resource *r = pw;
+
+ switch (event->type) {
+ case CONTENT_MSG_READY:
+ case CONTENT_MSG_DONE:
+ r->ready = true;
+ r->height = content_get_height(handle);
+ break;
+
+ default:
+ break;
+ }
+
+ return NSERROR_OK;
+}
+
+
+/* Exported interface, documented in url_bar.h */
+bool ro_gui_url_bar_init(void)
+{
+ int i;
+
+ for (i = 0; i < URLBAR_RES_LAST; i++) {
+ nsurl *url;
+ if (nsurl_create(url_bar_res[i].url, &url) == NSERROR_OK) {
+ hlcache_handle_retrieve(url, 0, NULL, NULL,
+ ro_gui_url_bar_res_cb,
+ &(url_bar_res[i]), NULL,
+ CONTENT_IMAGE, &(url_bar_res[i].c));
+ nsurl_unref(url);
+ }
+ }
+
+ return true;
+}
+
+
+/* Exported interface, documented in url_bar.h */
+void ro_gui_url_bar_fini(void)
+{
+ int i;
+
+ for (i = 0; i < URLBAR_RES_LAST; i++) {
+ hlcache_handle_release(url_bar_res[i].c);
+ }
+}
diff --git a/frontends/riscos/gui/url_bar.h b/frontends/riscos/gui/url_bar.h
new file mode 100644
index 000000000..981afb35f
--- /dev/null
+++ b/frontends/riscos/gui/url_bar.h
@@ -0,0 +1,329 @@
+/*
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2011 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * URL bars (interface).
+ */
+
+#ifndef _NETSURF_RISCOS_URLBAR_H_
+#define _NETSURF_RISCOS_URLBAR_H_
+
+#include <stdbool.h>
+#include "riscos/menus.h"
+#include "riscos/theme.h"
+
+/* A list of possible URL bar actions. */
+
+typedef enum {
+ TOOLBAR_URL_NONE = 0, /* Special case: no action */
+ TOOLBAR_URL_DRAG_URL,
+ TOOLBAR_URL_DRAG_FAVICON,
+ TOOLBAR_URL_SELECT_HOTLIST,
+ TOOLBAR_URL_ADJUST_HOTLIST
+} url_bar_action;
+
+struct url_bar;
+struct hlcache_handle;
+
+/**
+ * Initialise the url bar module.
+ *
+ * \return True iff success, else false.
+ */
+
+bool ro_gui_url_bar_init(void);
+
+/**
+ * Finalise the url bar module
+ */
+
+void ro_gui_url_bar_fini(void);
+
+/**
+ * Create a new url bar widget.
+ *
+ * \param *theme The theme to apply (or NULL for the default).
+ * \return A url bar handle, or NULL on failure.
+ */
+
+struct url_bar *ro_gui_url_bar_create(struct theme_descriptor *theme);
+
+
+/**
+ * Place a URL bar into a toolbar window and initialise any theme-specific
+ * settings. Any previous incarnation of the bar will be forgotten: this
+ * is for use when a new toolbar is being created, or when a toolbar has been
+ * deleted and rebuilt following a theme change.
+ *
+ * \param *url_bar The URL bar to rebuild.
+ * \param *theme The theme to apply (or NULL for current).
+ * \param style The theme style to apply.
+ * \param window The window that the bar is in.
+ * \param display true if the bar should be for display only.
+ * \param shaded true if the bar should be shaded; else false.
+ * \return true on success; else false.
+ */
+
+bool ro_gui_url_bar_rebuild(struct url_bar *url_bar,
+ struct theme_descriptor *theme, theme_style style,
+ wimp_w window, bool display, bool shaded);
+
+
+/**
+ * Destroy a url bar widget.
+ *
+ * \param *url_bar The url bar to destroy.
+ */
+
+void ro_gui_url_bar_destroy(struct url_bar *url_bar);
+
+
+/**
+ * Return the MINIMUM dimensions required by the URL bar, in RO units,
+ * allowing for the current theme.
+ *
+ * \param *url_bar The URL bar of interest.
+ * \param *width Return the required width.
+ * \param *height Return the required height.
+ * \return true if values are returned; else false.
+ */
+
+bool ro_gui_url_bar_get_dims(struct url_bar *url_bar,
+ int *width, int *height);
+
+
+/**
+ * Set or update the dimensions to be used by the URL bar, in RO units.
+ * If these are greater than the minimum required, the URL bar will fill
+ * the extended space; if less, the call will fail.
+ *
+ * \param *url_bar The URL bar to update.
+ * \param x0 The minimum X window position.
+ * \param y0 The minimum Y window position.
+ * \param x1 The maximum X window position.
+ * \param y1 The maximum Y window position.
+ * \return true if size updated; else false.
+ */
+
+bool ro_gui_url_bar_set_extent(struct url_bar *url_bar,
+ int x0, int y0, int x1, int y1);
+
+
+/**
+ * Show or hide a URL bar.
+ *
+ * \param *url_bar The URL bar to hide.
+ * \param hide true to hide the bar; false to show it.
+ * \return true if successful; else false.
+ */
+
+bool ro_gui_url_bar_hide(struct url_bar *url_bar, bool hide);
+
+
+/**
+ * Handle redraw event rectangles in a URL bar.
+ *
+ * \param *url_bar The URL bar to use.
+ * \param *redraw The Wimp redraw rectangle to process.
+ */
+
+void ro_gui_url_bar_redraw(struct url_bar *url_bar, wimp_draw *redraw);
+
+
+/**
+ * Handle mouse clicks in a URL bar.
+ *
+ * \param *url_bar The URL bar to use.
+ * \param *pointer The Wimp mouse click event data.
+ * \param *state The toolbar window state.
+ * \param *action Returns the selected action, or
+ * TOOLBAR_URL_NONE.
+ * \return true if the event was handled exclusively;
+ * else false.
+ */
+
+bool ro_gui_url_bar_click(struct url_bar *url_bar,
+ wimp_pointer *pointer, wimp_window_state *state,
+ url_bar_action *action);
+
+
+/**
+ * Process offered menu prepare events from the parent window.
+ *
+ * \param *url_bar The URL bar in question.
+ * \param i The icon owning the menu.
+ * \param *menu The menu to be prepared.
+ * \param *pointer The Wimp Pointer data from the event.
+ * \return true if the event is claimed; else false.
+ */
+
+bool ro_gui_url_bar_menu_prepare(struct url_bar *url_bar, wimp_i i,
+ wimp_menu *menu, wimp_pointer *pointer);
+
+
+/**
+ * Process offered menu select events from the parent window.
+ *
+ * \param *url_bar The URL bar in question.
+ * \param i The icon owning the menu.
+ * \param *menu The menu to be prepared.
+ * \param *selection The wimp menu selection data.
+ * \param action The selected menu action.
+ * \return true if the event is claimed; else false.
+ */
+
+bool ro_gui_url_bar_menu_select(struct url_bar *url_bar, wimp_i i,
+ wimp_menu *menu, wimp_selection *selection, menu_action action);
+
+
+/**
+ * Translate mouse data into an interactive help message for the URL bar.
+ *
+ * \param *url_bar The URL bar to process.
+ * \param i The wimp icon under the pointer.
+ * \param *mouse The mouse position.
+ * \param *state The toolbar window state.
+ * \param buttons The mouse button state.
+ * \param **suffix Return a help token suffix, or "" for none.
+ * \return true if handled exclusively; else false.
+ */
+
+bool ro_gui_url_bar_help_suffix(struct url_bar *url_bar, wimp_i i,
+ os_coord *mouse, wimp_window_state *state,
+ wimp_mouse_state buttons, const char **suffix);
+
+
+/**
+ * Give a URL bar input focus.
+ *
+ * \param *url_bar The URL bar to give focus to.
+ * \return true if successful; else false.
+ */
+
+bool ro_gui_url_bar_take_caret(struct url_bar *url_bar);
+
+
+/**
+ * Set the content of a URL Bar field.
+ *
+ * \param *url_bar The URL Bar to update.
+ * \param *url The new url to insert.
+ * \param is_utf8 true if the string is in utf8 encoding; false
+ * if it is in local encoding.
+ * \param set_caret true if the caret should be placed in the field;
+ * else false.
+ */
+
+void ro_gui_url_bar_set_url(struct url_bar *url_bar, const char *url,
+ bool is_utf8, bool set_caret);
+
+
+/**
+ * Update the state of a URL Bar's hotlist icon to reflect any changes to the
+ * URL or the contents of the hotlist.
+ *
+ * \param *url_bar The URL Bar to update.
+ */
+
+void ro_gui_url_bar_update_hotlist(struct url_bar *url_bar);
+
+
+/**
+ * Return a pointer to the URL contained in a URL bar.
+ *
+ * \param *url_bar The URL Bar to look up the URL from.
+ * \return Pointer to the URL, or NULL.
+ */
+
+const char *ro_gui_url_bar_get_url(struct url_bar *url_bar);
+
+
+/**
+ * Return the current work area coordinates of the URL and favicon field's
+ * bounding box.
+ *
+ * \param *url_bar The URL bar to check.
+ * \param *extent Returns the field extent.
+ * \return true if successful; else false.
+ */
+
+bool ro_gui_url_bar_get_url_extent(struct url_bar *url_bar, os_box *extent);
+
+
+/**
+ * Test a pointer click to see if it was in the URL bar's text field.
+ *
+ * \param url_bar The URL Bar to test.
+ * \param pointer The pointer event data to test.
+ * \return true if the click was in the field; else false.
+ */
+
+bool ro_gui_url_bar_test_for_text_field_click(struct url_bar *url_bar,
+ wimp_pointer *pointer);
+
+
+/**
+ * Test a keypress to see if it was in the URL bar's text field.
+ *
+ * \param url_bar The URL Bar to test.
+ * \param key The key pressed
+ * \return true if the keypress was in the field; else false.
+ */
+
+bool ro_gui_url_bar_test_for_text_field_keypress(struct url_bar *url_bar,
+ wimp_key *key);
+
+
+/**
+ * Set the favicon to a site supplied favicon image, or remove the image
+ * and return to using filetype-based icons.
+ *
+ * \param *url_bar The URL Bar to update the favicon on.
+ * \param *h The content to use, or NULL to unset.
+ * \return true if successful; else false.
+ */
+
+bool ro_gui_url_bar_set_site_favicon(struct url_bar *url_bar,
+ struct hlcache_handle *h);
+
+
+/**
+ * Set the favicon to a RISC OS filetype sprite based on the type of the
+ * content within the supplied window.
+ *
+ * \param *url_bar The URL Bar to update the favicon on.
+ * \param *g The window with the content to use.
+ * \return true if successful; else false.
+ */
+
+bool ro_gui_url_bar_set_content_favicon(struct url_bar *url_bar,
+ struct gui_window *g);
+
+
+/**
+ * Update the state of the URL suggestion pop-up menu icon on a URL bar.
+ *
+ * \param *url_bar The URL bar to update.
+ * \return true if successful; else false.
+ */
+
+bool ro_gui_url_bar_update_urlsuggest(struct url_bar *url_bar);
+
+#endif
+
diff --git a/frontends/riscos/help.c b/frontends/riscos/help.c
new file mode 100644
index 000000000..73cb6957d
--- /dev/null
+++ b/frontends/riscos/help.c
@@ -0,0 +1,361 @@
+/*
+ * Copyright 2004, 2005 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Interactive help (implementation).
+ */
+
+#include <oslib/wimp.h>
+#include <oslib/taskmanager.h>
+
+#include "utils/nsoption.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utf8.h"
+#include "desktop/mouse.h"
+#include "desktop/gui_window.h"
+
+#include "riscos/treeview.h"
+#include "riscos/help.h"
+#include "riscos/wimp_event.h"
+#include "riscos/hotlist.h"
+#include "riscos/global_history.h"
+#include "riscos/cookies.h"
+#include "riscos/wimp.h"
+#include "riscos/iconbar.h"
+#include "riscos/window.h"
+#include "riscos/ucstables.h"
+#include "riscos/gui.h"
+
+/* Recognised help keys
+ ====================
+ Help keys should be registered using the wimp_event system to be
+ recognised. The only special case help values are:
+
+ HelpIconbar Iconbar (no icon suffix is used)
+ HelpBrowser Browser window [*]
+ HelpHotlist Hotlist window [*]
+ HelpGHistory Global history window [*]
+ HelpCookies Cookies window [*]
+
+ HelpIconMenu Iconbar menu
+ HelpBrowserMenu Browser window menu
+ HelpHotlistMenu Hotlist window menu
+ HelpGHistoryMenu Global history window menu
+ HelpCookiesMenu Cookie window menu
+
+ The prefixes are followed by either the icon number (eg 'HelpToolbar7'),
+ or a series of numbers representing the menu structure (eg
+ 'HelpBrowserMenu3-1-2').
+ If '<key><identifier>' is not available, then simply '<key>' is then
+ used. For example if 'HelpToolbar7' is not available then 'HelpToolbar'
+ is then tried.
+ If an item is greyed out then a suffix of 'g' is added (eg
+ 'HelpToolbar7g'). For this to work, windows must have bit 4 of the
+ window flag byte set and the user must be running RISC OS 5.03 or
+ greater.
+ For items marked with an asterisk [*] a call must be made to determine
+ the required help text as the window does not contain any icons. An
+ example of this is the hotlist window where ro_gui_hotlist_help() is
+ called.
+*/
+
+
+static void ro_gui_interactive_help_broadcast(wimp_message *message,
+ char *token);
+static os_t help_time = 0;
+
+
+/**
+ * Attempts to process an interactive help message request
+ *
+ * \param message the request message
+ */
+void ro_gui_interactive_help_request(wimp_message *message)
+{
+ char message_token[32];
+ char menu_buffer[4];
+ wimp_selection menu_tree;
+ help_full_message_request *message_data;
+ wimp_w window;
+ wimp_i icon;
+ unsigned int index;
+ bool greyed = false;
+ wimp_menu *test_menu;
+ os_error *error;
+ const char *auto_text;
+ int i;
+
+ /* check we aren't turned off */
+ if (!nsoption_bool(interactive_help))
+ return;
+
+ /* only accept help requests */
+ if ((!message) || (message->action != message_HELP_REQUEST))
+ return;
+
+ /* remember the time of the request so we can track them */
+ xos_read_monotonic_time(&help_time);
+
+ /* set up our state */
+ message_token[0] = 0x00;
+ message_data = (help_full_message_request *)message;
+ window = message_data->w;
+ icon = message_data->i;
+
+ /* do the basic window checks */
+ auto_text = ro_gui_wimp_event_get_help_prefix(window);
+ if (auto_text != NULL) {
+ const char *auto_suffix = ro_gui_wimp_event_get_help_suffix(
+ window, icon, &message_data->pos,
+ message_data->buttons);
+
+ if (auto_suffix == NULL)
+ sprintf(message_token, "%s%i", auto_text, (int)icon);
+ else
+ sprintf(message_token, "%s%s", auto_text, auto_suffix);
+ } else if (window == wimp_ICON_BAR)
+ sprintf(message_token, "HelpIconbar");
+ else if (ro_gui_hotlist_check_window(message->data.data_xfer.w)) {
+ i = ro_treeview_get_help(message_data);
+ sprintf(message_token,
+ (i >= 0) ? "HelpTree%i" :"HelpHotlist%i", i);
+ } else if (ro_gui_global_history_check_window(
+ message->data.data_xfer.w)) {
+ i = ro_treeview_get_help(message_data);
+ sprintf(message_token,
+ (i >= 0) ? "HelpTree%i" :"HelpGHistory%i", i);
+ } else if (ro_gui_cookies_check_window(message->data.data_xfer.w)) {
+ i = ro_treeview_get_help(message_data);
+ sprintf(message_token,
+ (i >= 0) ? "HelpTree%i" :"HelpCookies%i", i);
+ } else if (ro_gui_window_lookup(window) != NULL)
+ sprintf(message_token, "HelpBrowser%i", (int)icon);
+
+ /* if we've managed to find something so far then we broadcast it */
+ if (message_token[0]) {
+ if ((icon >= 0) &&
+ (ro_gui_get_icon_shaded_state(window, icon)))
+ strcat(message_token, "g");
+ ro_gui_interactive_help_broadcast(message,
+ (char *)message_token);
+ return;
+ }
+
+ /* if we are not on an icon, we can't be in a menu (which stops
+ * separators giving help for their parent) so we abort */
+ if (icon == wimp_ICON_WINDOW)
+ return;
+
+ /* get the current menu tree */
+ error = xwimp_get_menu_state(wimp_GIVEN_WINDOW_AND_ICON,
+ &menu_tree, window, icon);
+ if (error) {
+ LOG("xwimp_get_menu_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ if (menu_tree.items[0] == -1)
+ return;
+
+ /* get the menu prefix */
+ if (ro_gui_iconbar_check_menu(current_menu))
+ sprintf(message_token, "HelpIconMenu");
+ else if (ro_gui_window_check_menu(current_menu))
+ sprintf(message_token, "HelpBrowserMenu");
+ else if (ro_gui_hotlist_check_menu(current_menu))
+ sprintf(message_token, "HelpHotlistMenu");
+ else if (ro_gui_global_history_check_menu(current_menu))
+ sprintf(message_token, "HelpGHistoryMenu");
+ else if (ro_gui_cookies_check_menu(current_menu))
+ sprintf(message_token, "HelpCookiesMenu");
+ else
+ return;
+
+ /* decode the menu */
+ index = 0;
+ test_menu = current_menu;
+ while (menu_tree.items[index] != -1) {
+ greyed |= test_menu->entries[menu_tree.items[index]].icon_flags
+ & wimp_ICON_SHADED;
+ test_menu = test_menu->entries[menu_tree.items[index]].sub_menu;
+ if (index == 0)
+ sprintf(menu_buffer, "%i", menu_tree.items[index]);
+ else
+ sprintf(menu_buffer, "-%i", menu_tree.items[index]);
+ strcat(message_token, menu_buffer);
+ index++;
+ }
+ if (greyed)
+ strcat(message_token, "g");
+ ro_gui_interactive_help_broadcast(message, (char *)message_token);
+}
+
+
+/**
+ * Broadcasts a help reply
+ *
+ * \param message the original request message
+ * \param token the token to look up
+ */
+static void ro_gui_interactive_help_broadcast(wimp_message *message,
+ char *token)
+{
+ const char *translated_token;
+ help_full_message_reply *reply;
+ char *local_token;
+ os_error *error;
+
+ /* start off with an empty reply */
+ reply = (help_full_message_reply *)message;
+ reply->reply[0] = '\0';
+
+ /* check if the message exists */
+ translated_token = messages_get(token);
+ if (translated_token == token) {
+ /* no default help for 'g' suffix */
+ if (token[strlen(token) - 1] != 'g') {
+ /* find the base key from the token */
+ char *base_token = token;
+ while (base_token[0] != 0x00) {
+ if ((base_token[0] == '-') ||
+ ((base_token[0] >= '0') &&
+ (base_token[0] <= '9')))
+ base_token[0] = 0x00;
+ else
+ ++base_token;
+ }
+ translated_token = messages_get(token);
+ }
+ }
+
+ /* copy our message string */
+ if (translated_token != token) {
+ /* convert to local encoding */
+ nserror err = utf8_to_local_encoding(translated_token, 0,
+ &local_token);
+ if (err != NSERROR_OK) {
+ /* badenc should never happen */
+ assert(err != NSERROR_BAD_ENCODING);
+ /* simply use UTF-8 string */
+ strncpy(reply->reply, translated_token, 235);
+ }
+ else {
+ strncpy(reply->reply, local_token, 235);
+ free(local_token);
+ }
+ reply->reply[235] = '\0';
+ }
+
+ /* broadcast the help reply */
+ reply->size = 256;
+ reply->action = message_HELP_REPLY;
+ reply->your_ref = reply->my_ref;
+ error = xwimp_send_message(wimp_USER_MESSAGE, (wimp_message *)reply,
+ reply->sender);
+ if (error) {
+ LOG("xwimp_send_message: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+}
+
+
+/**
+ * Checks if interactive help is running
+ *
+ * \return non-zero if interactive help is available, or 0 if not available
+ */
+bool ro_gui_interactive_help_available(void)
+{
+ taskmanager_task task;
+ int context = 0;
+ os_t time;
+
+ /* generic test: any help request within the last 100cs */
+ xos_read_monotonic_time(&time);
+ if ((help_time + 100) > time)
+ return true;
+
+ /* special cases: check known application names */
+ do {
+ os_error *error;
+ error = xtaskmanager_enumerate_tasks(context, &task,
+ sizeof(taskmanager_task), &context, 0);
+ if (error) {
+ LOG("xtaskmanager_enumerate_tasks: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ }
+
+ /* we can't just use strcmp due to string termination issues */
+ if (!strncmp(task.name, "Help", 4) &&
+ (task.name[4] < 32))
+ return true;
+ else if (!strncmp(task.name, "Bubble Help", 11) &&
+ (task.name[11] < 32))
+ return true;
+ else if (!strncmp(task.name, "Floating Help", 13) &&
+ (task.name[13] < 32))
+ return true;
+ } while (context >= 0);
+ return false;
+}
+
+
+/**
+ * Launches interactive help.
+ */
+void ro_gui_interactive_help_start(void)
+{
+ char *help_start;
+ wimp_t task = 0;
+ os_error *error;
+
+ /* don't launch a second copy of anything */
+ if (ro_gui_interactive_help_available())
+ return;
+
+ /* launch <Help$Start> */
+ help_start = getenv("Help$Start");
+ if ((help_start) && (help_start[0])) {
+ error = xwimp_start_task("<Help$Start>", &task);
+ if (error) {
+ LOG("xwimp_start_tast: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ }
+
+ /* first attempt failed, launch !Help */
+ if (!task) {
+ error = xwimp_start_task("Resources:$.Apps.!Help", &task);
+ if (error) {
+ LOG("xwimp_start_tast: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ }
+
+ /* pretend we got a help request straight away */
+ if (task) {
+ error = xos_read_monotonic_time(&help_time);
+ if (error) {
+ LOG("xwimp_read_monotonic_time: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ }
+}
diff --git a/frontends/riscos/help.h b/frontends/riscos/help.h
new file mode 100644
index 000000000..b09594ac8
--- /dev/null
+++ b/frontends/riscos/help.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2004, 2005 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Interactive help (interface).
+ */
+
+#ifndef _NETSURF_RISCOS_HELP_H_
+#define _NETSURF_RISCOS_HELP_H_
+
+#include <stdbool.h>
+#include "oslib/wimp.h"
+
+void ro_gui_interactive_help_request(wimp_message *message);
+bool ro_gui_interactive_help_available(void);
+void ro_gui_interactive_help_start(void);
+
+#endif
diff --git a/frontends/riscos/history.c b/frontends/riscos/history.c
new file mode 100644
index 000000000..9d78f6ded
--- /dev/null
+++ b/frontends/riscos/history.c
@@ -0,0 +1,336 @@
+/*
+ * Copyright 2006 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Browser history window (RISC OS implementation).
+ *
+ * There is only one history window, not one per browser window.
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include "oslib/wimp.h"
+
+#include "utils/nsoption.h"
+#include "utils/log.h"
+#include "desktop/browser_history.h"
+#include "desktop/plotters.h"
+
+#include "riscos/dialog.h"
+#include "riscos/gui.h"
+#include "riscos/mouse.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/wimputils.h"
+
+static struct browser_window *history_bw;
+/* Last position of mouse in window. */
+static int mouse_x = 0;
+/* Last position of mouse in window. */
+static int mouse_y = 0;
+wimp_w history_window;
+
+static void ro_gui_history_redraw(wimp_draw *redraw);
+static bool ro_gui_history_click(wimp_pointer *pointer);
+static void ro_gui_history_pointer_entering(wimp_entering *entering);
+static void ro_gui_history_track_end(wimp_leaving *leaving, void *data);
+static void ro_gui_history_mouse_at(wimp_pointer *pointer, void *data);
+
+
+/**
+ * Create history window.
+ */
+
+void ro_gui_history_init(void)
+{
+ history_window = ro_gui_dialog_create("history");
+ ro_gui_wimp_event_register_redraw_window(history_window,
+ ro_gui_history_redraw);
+ ro_gui_wimp_event_register_mouse_click(history_window,
+ ro_gui_history_click);
+ ro_gui_wimp_event_register_pointer_entering_window(history_window,
+ ro_gui_history_pointer_entering);
+ ro_gui_wimp_event_set_help_prefix(history_window, "HelpHistory");
+}
+
+
+/**
+ * Open history window.
+ *
+ * \param g The riscos window to open history for.
+ * \param at_pointer open the window at the pointer.
+ */
+
+void ro_gui_history_open(struct gui_window *g, bool at_pointer)
+{
+ struct browser_window *bw;
+ int width, height;
+ os_box box = {0, 0, 0, 0};
+ wimp_window_state state;
+ os_error *error;
+
+ assert(g != NULL);
+ assert(g->bw != NULL);
+ bw = g->bw;
+ history_bw = bw;
+
+ browser_window_history_size(bw, &width, &height);
+ width *= 2;
+ height *= 2;
+
+ /* set extent */
+ box.x1 = width;
+ box.y0 = -height;
+ error = xwimp_set_extent(history_window, &box);
+ if (error) {
+ LOG("xwimp_set_extent: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* open full size */
+ state.w = history_window;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ state.visible.x0 = 0;
+ state.visible.y0 = 0;
+ state.visible.x1 = width;
+ state.visible.y1 = height;
+ state.next = wimp_HIDDEN;
+ error = xwimp_open_window(PTR_WIMP_OPEN(&state));
+ if (error) {
+ LOG("xwimp_open_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ ro_gui_dialog_open_persistent(g->window, history_window, at_pointer);
+}
+
+
+/**
+ * Redraw history window.
+ */
+
+void ro_gui_history_redraw(wimp_draw *redraw)
+{
+ osbool more;
+ os_error *error;
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &ro_plotters
+ };
+
+ error = xwimp_redraw_window(redraw, &more);
+ if (error) {
+ LOG("xwimp_redraw_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ while (more) {
+ ro_plot_origin_x = redraw->box.x0 - redraw->xscroll;
+ ro_plot_origin_y = redraw->box.y1 - redraw->yscroll;
+ browser_window_history_redraw(history_bw, &ctx);
+ error = xwimp_get_rectangle(redraw, &more);
+ if (error) {
+ LOG("xwimp_get_rectangle: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ }
+}
+
+
+/**
+ * Handle Pointer Entering Window events the history window.
+ *
+ * \param *entering The Wimp_PointerEnteringWindow block.
+ */
+
+void ro_gui_history_pointer_entering(wimp_entering *entering)
+{
+ ro_mouse_track_start(ro_gui_history_track_end,
+ ro_gui_history_mouse_at, NULL);
+}
+
+
+/**
+ * Handle Pointer Leaving Window events the history window. These arrive as the
+ * termination callback handler from ro_mouse's mouse tracking.
+ *
+ * \param *leaving The Wimp_PointerLeavingWindow block.
+ * \param *data NULL data pointer.
+ */
+
+void ro_gui_history_track_end(wimp_leaving *leaving, void *data)
+{
+ ro_gui_dialog_close(dialog_tooltip);
+}
+
+
+/**
+ * Handle mouse movements over the history window.
+ */
+
+void ro_gui_history_mouse_at(wimp_pointer *pointer, void *data)
+{
+ int x, y;
+ int width;
+ const char *url;
+ wimp_window_state state;
+ wimp_icon_state ic;
+ os_box box = {0, 0, 0, 0};
+ os_error *error;
+
+ LOG("Mouse at...");
+
+ /* If the mouse hasn't moved, or if we don't want tooltips, exit */
+ if ((mouse_x == pointer->pos.x && mouse_y == pointer->pos.y) ||
+ !nsoption_bool(history_tooltip))
+ return;
+
+ /* Update mouse position */
+ mouse_x = pointer->pos.x;
+ mouse_y = pointer->pos.y;
+
+ /* Find history tree entry under mouse */
+ state.w = history_window;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ x = (pointer->pos.x - (state.visible.x0 - state.xscroll)) / 2;
+ y = -(pointer->pos.y - (state.visible.y1 - state.yscroll)) / 2;
+ url = browser_window_history_position_url(history_bw, x, y);
+ if (!url) {
+ /* not over a tree entry => close tooltip window. */
+ error = xwimp_close_window(dialog_tooltip);
+ if (error) {
+ LOG("xwimp_close_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ return;
+ }
+
+ /* get width of string */
+ error = xwimptextop_string_width(url,
+ strlen(url) > 256 ? 256 : strlen(url),
+ &width);
+ if (error) {
+ LOG("xwimptextop_string_width: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ ro_gui_set_icon_string(dialog_tooltip, 0, url, true);
+
+ /* resize icon appropriately */
+ ic.w = dialog_tooltip;
+ ic.i = 0;
+ error = xwimp_get_icon_state(&ic);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ error = xwimp_resize_icon(dialog_tooltip, 0,
+ ic.icon.extent.x0, ic.icon.extent.y0,
+ width + 16, ic.icon.extent.y1);
+ if (error) {
+ LOG("xwimp_resize_icon: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ state.w = dialog_tooltip;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* update window extent */
+ box.x1 = width + 16;
+ box.y0 = -36;
+ error = xwimp_set_extent(dialog_tooltip, &box);
+ if (error) {
+ LOG("xwimp_set_extent: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* set visible area */
+ state.visible.x0 = pointer->pos.x + 24;
+ state.visible.y0 = pointer->pos.y - 22 - 36;
+ state.visible.x1 = pointer->pos.x + 24 + width + 16;
+ state.visible.y1 = pointer->pos.y - 22;
+ state.next = wimp_TOP;
+ /* open window */
+ error = xwimp_open_window(PTR_WIMP_OPEN(&state));
+ if (error) {
+ LOG("xwimp_open_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+}
+
+
+/**
+ * Handle mouse clicks in the history window.
+ *
+ * \return true if the event was handled, false to pass it on
+ */
+
+bool ro_gui_history_click(wimp_pointer *pointer)
+{
+ int x, y;
+ wimp_window_state state;
+ os_error *error;
+
+ if (pointer->buttons != wimp_CLICK_SELECT &&
+ pointer->buttons != wimp_CLICK_ADJUST)
+ /* return if not select or adjust click */
+ return true;
+
+ state.w = history_window;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return true;
+ }
+
+ x = (pointer->pos.x - (state.visible.x0 - state.xscroll)) / 2;
+ y = -(pointer->pos.y - (state.visible.y1 - state.yscroll)) / 2;
+ browser_window_history_click(history_bw, x, y,
+ pointer->buttons == wimp_CLICK_ADJUST);
+
+ return true;
+}
diff --git a/frontends/riscos/hotlist.c b/frontends/riscos/hotlist.c
new file mode 100644
index 000000000..381978faf
--- /dev/null
+++ b/frontends/riscos/hotlist.c
@@ -0,0 +1,737 @@
+/*
+ * Copyright 2004, 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2010, 2013 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Hotlist (implementation).
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include "oslib/osfile.h"
+#include "oslib/osmodule.h"
+#include "oslib/wimp.h"
+
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/nsoption.h"
+#include "content/content.h"
+#include "content/hlcache.h"
+#include "content/urldb.h"
+#include "desktop/hotlist.h"
+#include "desktop/tree.h"
+#include "desktop/gui_window.h"
+
+#include "riscos/gui.h"
+#include "riscos/dialog.h"
+#include "riscos/hotlist.h"
+#include "riscos/menus.h"
+#include "riscos/message.h"
+#include "riscos/save.h"
+#include "riscos/toolbar.h"
+#include "riscos/treeview.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/query.h"
+
+static void ro_gui_hotlist_toolbar_update_buttons(void);
+static void ro_gui_hotlist_toolbar_save_buttons(char *config);
+static bool ro_gui_hotlist_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_pointer *pointer);
+static void ro_gui_hotlist_menu_warning(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action);
+static bool ro_gui_hotlist_menu_select(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action);
+static void ro_gui_hotlist_toolbar_click(button_bar_action action);
+static void ro_gui_hotlist_addurl_bounce(wimp_message *message);
+static void ro_gui_hotlist_scheduled_callback(void *p);
+static void ro_gui_hotlist_remove_confirmed(query_id id,
+ enum query_response res, void *p);
+static void ro_gui_hotlist_remove_cancelled(query_id id,
+ enum query_response res, void *p);
+
+static const query_callback remove_funcs = {
+ ro_gui_hotlist_remove_confirmed,
+ ro_gui_hotlist_remove_cancelled
+};
+
+struct ro_treeview_callbacks ro_hotlist_treeview_callbacks = {
+ ro_gui_hotlist_toolbar_click,
+ ro_gui_hotlist_toolbar_update_buttons,
+ ro_gui_hotlist_toolbar_save_buttons
+};
+
+/* Hotlist Protocol Message Blocks, which are currently not in OSLib. */
+
+struct ro_hotlist_message_hotlist_addurl {
+ wimp_MESSAGE_HEADER_MEMBERS /**< The standard message header. */
+ char *url; /**< Pointer to the URL in RMA. */
+ char *title; /**< Pointer to the title in RMA. */
+ char appname[32]; /**< The application name. */
+};
+
+struct ro_hotlist_message_hotlist_changed {
+ wimp_MESSAGE_HEADER_MEMBERS /**< The standard message header. */
+};
+
+static char *hotlist_url = NULL; /**< URL area claimed from RMA. */
+static char *hotlist_title = NULL; /**< Title area claimed from RMA. */
+
+/** Hotlist Query Handler. */
+
+static query_id hotlist_query = QUERY_INVALID;
+static nsurl *hotlist_delete_url = NULL;
+
+/* The RISC OS hotlist window, toolbar and treeview data. */
+
+static struct ro_hotlist {
+ wimp_w window; /**< The hotlist RO window handle. */
+ struct toolbar *toolbar; /**< The hotlist toolbar handle. */
+ ro_treeview *tv; /**< The hotlist treeview handle. */
+ wimp_menu *menu; /**< The hotlist window menu. */
+} hotlist_window;
+
+/**
+ * Pre-Initialise the hotlist tree. This is called for things that need to
+ * be done at the gui_init() stage, such as loading templates.
+ */
+
+void ro_gui_hotlist_preinitialise(void)
+{
+ /* Create our window. */
+
+ hotlist_window.window = ro_gui_dialog_create("tree");
+ ro_gui_set_window_title(hotlist_window.window,
+ messages_get("Hotlist"));
+}
+
+/**
+ * Initialise the hotlist tree, at the gui_init2() stage.
+ */
+
+void ro_gui_hotlist_postinitialise(void)
+{
+ /* Create our toolbar. */
+
+ hotlist_window.toolbar = ro_toolbar_create(NULL, hotlist_window.window,
+ THEME_STYLE_HOTLIST_TOOLBAR, TOOLBAR_FLAGS_NONE,
+ ro_treeview_get_toolbar_callbacks(), NULL,
+ "HelpHotToolbar");
+ if (hotlist_window.toolbar != NULL) {
+ ro_toolbar_add_buttons(hotlist_window.toolbar,
+ hotlist_toolbar_buttons,
+ nsoption_charp(toolbar_hotlist));
+ ro_toolbar_rebuild(hotlist_window.toolbar);
+ }
+
+ /* Create the treeview with the window and toolbar. */
+ tree_hotlist_path = nsoption_charp(hotlist_path);
+ hotlist_window.tv = ro_treeview_create(hotlist_window.window,
+ hotlist_window.toolbar, &ro_hotlist_treeview_callbacks,
+ TREE_HOTLIST);
+ if (hotlist_window.tv == NULL) {
+ LOG("Failed to allocate treeview");
+ return;
+ }
+
+ ro_toolbar_update_client_data(hotlist_window.toolbar,
+ hotlist_window.tv);
+
+ /* Build the hotlist window menu. */
+
+ static const struct ns_menu hotlist_definition = {
+ "Hotlist", {
+ { "Hotlist", NO_ACTION, 0 },
+ { "Hotlist.New", NO_ACTION, 0 },
+ { "Hotlist.New.Folder", TREE_NEW_FOLDER, 0 },
+ { "Hotlist.New.Link", TREE_NEW_LINK, 0 },
+ { "_Hotlist.Export", HOTLIST_EXPORT, &dialog_saveas },
+ { "Hotlist.Expand", TREE_EXPAND_ALL, 0 },
+ { "Hotlist.Expand.All", TREE_EXPAND_ALL, 0 },
+ { "Hotlist.Expand.Folders", TREE_EXPAND_FOLDERS, 0 },
+ { "Hotlist.Expand.Links", TREE_EXPAND_LINKS, 0 },
+ { "Hotlist.Collapse", TREE_COLLAPSE_ALL, 0 },
+ { "Hotlist.Collapse.All", TREE_COLLAPSE_ALL, 0 },
+ { "Hotlist.Collapse.Folders", TREE_COLLAPSE_FOLDERS, 0 },
+ { "Hotlist.Collapse.Links", TREE_COLLAPSE_LINKS, 0 },
+ { "Hotlist.Toolbars", NO_ACTION, 0 },
+ { "_Hotlist.Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 },
+ { "Hotlist.Toolbars.EditToolbar", TOOLBAR_EDIT, 0 },
+ { "Selection", TREE_SELECTION, 0 },
+ { "Selection.Edit", TREE_SELECTION_EDIT, 0 },
+ { "Selection.Launch", TREE_SELECTION_LAUNCH, 0 },
+ { "Selection.Delete", TREE_SELECTION_DELETE, 0 },
+ { "SelectAll", TREE_SELECT_ALL, 0 },
+ { "Clear", TREE_CLEAR_SELECTION, 0 },
+ {NULL, 0, 0}
+ }
+ };
+
+ hotlist_window.menu = ro_gui_menu_define_menu(&hotlist_definition);
+
+ ro_gui_wimp_event_register_menu(hotlist_window.window,
+ hotlist_window.menu, false, false);
+ ro_gui_wimp_event_register_menu_prepare(hotlist_window.window,
+ ro_gui_hotlist_menu_prepare);
+ ro_gui_wimp_event_register_menu_selection(hotlist_window.window,
+ ro_gui_hotlist_menu_select);
+ ro_gui_wimp_event_register_menu_warning(hotlist_window.window,
+ ro_gui_hotlist_menu_warning);
+}
+
+/**
+ * Destroy the hotlist window.
+ */
+
+void ro_gui_hotlist_destroy(void)
+{
+ if (hotlist_window.tv == NULL)
+ return;
+
+ tree_hotlist_path = nsoption_charp(hotlist_save);
+ ro_treeview_destroy(hotlist_window.tv);
+}
+
+
+/**
+ * Open the hotlist window.
+ *
+ */
+
+void ro_gui_hotlist_open(void)
+{
+ if (nsoption_bool(external_hotlists) &&
+ nsoption_charp(external_hotlist_app) != NULL &&
+ *nsoption_charp(external_hotlist_app) != '\0') {
+ char command[2048];
+ os_error *error;
+
+ snprintf(command, sizeof(command), "Filer_Run %s",
+ nsoption_charp(external_hotlist_app));
+ error = xos_cli(command);
+
+ if (error == NULL)
+ return;
+
+ LOG("xos_cli: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("Failed to launch external hotlist: %s",
+ error->errmess);
+ }
+
+ ro_gui_hotlist_toolbar_update_buttons();
+
+ if (!ro_gui_dialog_open_top(hotlist_window.window,
+ hotlist_window.toolbar, 600, 800)) {
+ ro_treeview_set_origin(hotlist_window.tv, 0,
+ -(ro_toolbar_height(hotlist_window.toolbar)));
+ }
+}
+
+/**
+ * Handle toolbar button clicks.
+ *
+ * \param action The action to handle
+ */
+
+void ro_gui_hotlist_toolbar_click(button_bar_action action)
+{
+ switch (action) {
+ case TOOLBAR_BUTTON_DELETE:
+ hotlist_keypress(NS_KEY_DELETE_LEFT);
+ ro_toolbar_update_all_hotlists();
+ break;
+
+ case TOOLBAR_BUTTON_EXPAND:
+ hotlist_expand(false);
+ break;
+
+ case TOOLBAR_BUTTON_COLLAPSE:
+ hotlist_contract(false);
+ break;
+
+ case TOOLBAR_BUTTON_OPEN:
+ hotlist_expand(true);
+ break;
+
+ case TOOLBAR_BUTTON_CLOSE:
+ hotlist_contract(true);
+ break;
+
+ case TOOLBAR_BUTTON_LAUNCH:
+ hotlist_keypress(NS_KEY_CR);
+ break;
+
+ case TOOLBAR_BUTTON_CREATE:
+ hotlist_add_folder(NULL, false, 0);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/**
+ * Update the button state in the hotlist toolbar.
+ */
+
+void ro_gui_hotlist_toolbar_update_buttons(void)
+{
+ ro_toolbar_set_button_shaded_state(hotlist_window.toolbar,
+ TOOLBAR_BUTTON_DELETE,
+ !hotlist_has_selection());
+
+ ro_toolbar_set_button_shaded_state(hotlist_window.toolbar,
+ TOOLBAR_BUTTON_LAUNCH,
+ !hotlist_has_selection());
+}
+
+
+/**
+ * Save a new button arrangement in the hotlist toolbar.
+ *
+ * \param *config The new button configuration string.
+ */
+
+void ro_gui_hotlist_toolbar_save_buttons(char *config)
+{
+ nsoption_set_charp(toolbar_hotlist, config);
+ ro_gui_save_options();
+}
+
+
+/**
+ * Prepare the hotlist menu for opening
+ *
+ * \param w The window owning the menu.
+ * \param i A wimp icon
+ * \param menu The menu about to be opened.
+ * \param pointer Pointer to the relevant wimp event block, or
+ * NULL for an Adjust click.
+ * \return true if the event was handled; else false.
+ */
+
+bool ro_gui_hotlist_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_pointer *pointer)
+{
+ bool selection;
+
+ if (menu != hotlist_window.menu)
+ return false;
+
+ selection = hotlist_has_selection();
+
+ ro_gui_menu_set_entry_shaded(hotlist_window.menu,
+ TREE_SELECTION, !selection);
+ ro_gui_menu_set_entry_shaded(hotlist_window.menu,
+ TREE_CLEAR_SELECTION, !selection);
+
+ ro_gui_save_prepare(GUI_SAVE_HOTLIST_EXPORT_HTML,
+ NULL, NULL, NULL, NULL);
+
+ ro_gui_menu_set_entry_shaded(menu, TOOLBAR_BUTTONS,
+ ro_toolbar_menu_option_shade(hotlist_window.toolbar));
+ ro_gui_menu_set_entry_ticked(menu, TOOLBAR_BUTTONS,
+ ro_toolbar_menu_buttons_tick(hotlist_window.toolbar));
+
+ ro_gui_menu_set_entry_shaded(menu, TOOLBAR_EDIT,
+ ro_toolbar_menu_edit_shade(hotlist_window.toolbar));
+ ro_gui_menu_set_entry_ticked(menu, TOOLBAR_EDIT,
+ ro_toolbar_menu_edit_tick(hotlist_window.toolbar));
+
+ return true;
+}
+
+
+/**
+ * Handle submenu warnings for the hotlist menu
+ *
+ * \param w The window owning the menu.
+ * \param i The icon owning the menu.
+ * \param *menu The menu to which the warning applies.
+ * \param *selection The wimp menu selection data.
+ * \param action The selected menu action.
+ */
+
+void ro_gui_hotlist_menu_warning(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action)
+{
+ /* Do nothing */
+}
+
+/**
+ * Handle selections from the hotlist menu
+ *
+ * \param w The window owning the menu.
+ * \param i The icon owning the menu.
+ * \param *menu The menu from which the selection was made.
+ * \param *selection The wimp menu selection data.
+ * \param action The selected menu action.
+ * \return true if action accepted; else false.
+ */
+
+bool ro_gui_hotlist_menu_select(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action)
+{
+ switch (action) {
+ case HOTLIST_EXPORT:
+ ro_gui_dialog_open_persistent(w, dialog_saveas, true);
+ return true;
+ case TREE_NEW_FOLDER:
+ hotlist_add_folder(NULL, false, 0);
+ return true;
+ case TREE_NEW_LINK:
+ hotlist_add_entry(NULL, NULL, false, 0);
+ return true;
+ case TREE_EXPAND_ALL:
+ hotlist_expand(false);
+ return true;
+ case TREE_EXPAND_FOLDERS:
+ hotlist_expand(true);
+ return true;
+ case TREE_EXPAND_LINKS:
+ hotlist_expand(false);
+ return true;
+ case TREE_COLLAPSE_ALL:
+ hotlist_contract(true);
+ return true;
+ case TREE_COLLAPSE_FOLDERS:
+ hotlist_contract(true);
+ return true;
+ case TREE_COLLAPSE_LINKS:
+ hotlist_contract(false);
+ return true;
+ case TREE_SELECTION_EDIT:
+ hotlist_edit_selection();
+ return true;
+ case TREE_SELECTION_LAUNCH:
+ hotlist_keypress(NS_KEY_CR);
+ return true;
+ case TREE_SELECTION_DELETE:
+ hotlist_keypress(NS_KEY_DELETE_LEFT);
+ ro_toolbar_update_all_hotlists();
+ return true;
+ case TREE_SELECT_ALL:
+ hotlist_keypress(NS_KEY_SELECT_ALL);
+ return true;
+ case TREE_CLEAR_SELECTION:
+ hotlist_keypress(NS_KEY_CLEAR_SELECTION);
+ return true;
+ case TOOLBAR_BUTTONS:
+ ro_toolbar_set_display_buttons(hotlist_window.toolbar,
+ !ro_toolbar_get_display_buttons(
+ hotlist_window.toolbar));
+ return true;
+ case TOOLBAR_EDIT:
+ ro_toolbar_toggle_edit(hotlist_window.toolbar);
+ return true;
+ default:
+ return false;
+ }
+
+ return false;
+}
+
+/**
+ * Check if a particular window handle is the hotlist window
+ *
+ * \param window The window in question
+ * \return true if this window is the hotlist
+ */
+bool ro_gui_hotlist_check_window(wimp_w window)
+{
+ if (hotlist_window.window == window)
+ return true;
+ else
+ return false;
+}
+
+/**
+ * Check if a particular menu handle is the hotlist menu
+ *
+ * \param *menu The menu in question.
+ * \return true if this menu is the hotlist menu
+ */
+
+bool ro_gui_hotlist_check_menu(wimp_menu *menu)
+{
+ if (hotlist_window.menu == menu)
+ return true;
+ else
+ return false;
+}
+
+
+/**
+ * Add a URL to the hotlist. This will be passed on to the core hotlist, then
+ * Message_HotlistAddURL will broadcast to any bookmark applications via the
+ * Hotlist Protocol.
+ *
+ * \param *url The URL to be added.
+ */
+
+void ro_gui_hotlist_add_page(nsurl *url)
+{
+ const struct url_data *data;
+ wimp_message message;
+ struct ro_hotlist_message_hotlist_addurl *add_url =
+ (struct ro_hotlist_message_hotlist_addurl *) &message;
+
+ if (url == NULL)
+ return;
+
+ /* If we're not using external hotlists, add the page to NetSurf's
+ * own hotlist and return...
+ */
+
+ if (!nsoption_bool(external_hotlists)) {
+ hotlist_add_url(url);
+ return;
+ }
+
+ /* ...otherwise try broadcasting the details to any other
+ * interested parties. If no-one answers, we'll fall back to
+ * NetSurf's hotlist anyway when the message bounces.
+ */
+
+ ro_gui_hotlist_add_cleanup();
+
+ data = urldb_get_url_data(url);
+ if (data == NULL)
+ return;
+
+ hotlist_url = osmodule_alloc(nsurl_length(url) + 1);
+ hotlist_title = osmodule_alloc(strlen(data->title) + 1);
+
+ if (hotlist_url == NULL || hotlist_title == NULL) {
+ ro_gui_hotlist_add_cleanup();
+ return;
+ }
+
+ strcpy(hotlist_url, nsurl_access(url));
+ strcpy(hotlist_title, data->title);
+
+ add_url->size = 60;
+ add_url->your_ref = 0;
+ add_url->action = message_HOTLIST_ADD_URL;
+ add_url->url = hotlist_url;
+ add_url->title = hotlist_title;
+ strcpy(add_url->appname, "NetSurf");
+
+ if (!ro_message_send_message(wimp_USER_MESSAGE_RECORDED, &message, 0,
+ ro_gui_hotlist_addurl_bounce))
+ ro_gui_hotlist_add_cleanup();
+
+ /* Listen for the next Null poll, as an indication that the
+ * message didn't bounce.
+ */
+
+ riscos_schedule(0, ro_gui_hotlist_scheduled_callback, NULL);
+}
+
+
+/**
+ * Handle bounced Message_HotlistAddURL, so that RMA storage can be freed.
+ *
+ * \param *message The bounced message content.
+ */
+
+static void ro_gui_hotlist_addurl_bounce(wimp_message *message)
+{
+ if (hotlist_url != NULL) {
+ nsurl *nsurl;
+
+ if (nsurl_create(hotlist_url, &nsurl) != NSERROR_OK)
+ return;
+
+ hotlist_add_url(nsurl);
+ nsurl_unref(nsurl);
+ }
+
+ ro_gui_hotlist_add_cleanup();
+
+ /* There's no longer any need to listen for the next Null poll. */
+
+ riscos_schedule(-1, ro_gui_hotlist_scheduled_callback, NULL);
+}
+
+
+/**
+ * Callback to schedule for the next available Null poll, by which point
+ * a hotlist client will have claimed the Message_HotlistAddURL and any
+ * details in RMA can safely be discarded.
+ *
+ * \param *p Unused data pointer.
+ */
+
+static void ro_gui_hotlist_scheduled_callback(void *p)
+{
+ ro_gui_hotlist_add_cleanup();
+}
+
+
+/**
+ * Clean up RMA storage used by the Message_HotlistAddURL protocol.
+ */
+
+void ro_gui_hotlist_add_cleanup(void)
+{
+ if (hotlist_url != NULL) {
+ osmodule_free(hotlist_url);
+ hotlist_url = NULL;
+ }
+
+ if (hotlist_title != NULL) {
+ osmodule_free(hotlist_title);
+ hotlist_title = NULL;
+ }
+}
+
+
+/**
+ * Remove a URL from the hotlist. This will be passed on to the core hotlist,
+ * unless we're configured to use external hotlists in which case we ignore it.
+ *
+ * \param *url The URL to be removed.
+ */
+
+void ro_gui_hotlist_remove_page(nsurl *url)
+{
+ if (url == NULL || nsoption_bool(external_hotlists) ||
+ !hotlist_has_url(url))
+ return;
+
+ /* Clean up any existing delete attempts before continuing. */
+
+ if (hotlist_query != QUERY_INVALID) {
+ query_close(hotlist_query);
+ hotlist_query = QUERY_INVALID;
+ }
+
+ if (hotlist_delete_url != NULL) {
+ nsurl_unref(hotlist_delete_url);
+ hotlist_delete_url = NULL;
+ }
+
+ /* Check with the user before removing the URL, unless they don't
+ * want us to be careful in which case just do it.
+ */
+
+ if (nsoption_bool(confirm_hotlist_remove)) {
+ hotlist_query = query_user("RemoveHotlist", NULL,
+ &remove_funcs, NULL,
+ messages_get("Remove"),
+ messages_get("DontRemove"));
+
+ hotlist_delete_url = nsurl_ref(url);
+ } else {
+ hotlist_remove_url(url);
+ ro_toolbar_update_all_hotlists();
+ }
+}
+
+
+/**
+ * Callback confirming a URL delete query.
+ *
+ * \param id The ID of the query calling us.
+ * \param res The user's response to the query.
+ * \param *p Callback data (always NULL).
+ */
+
+static void ro_gui_hotlist_remove_confirmed(query_id id,
+ enum query_response res, void *p)
+{
+ hotlist_remove_url(hotlist_delete_url);
+ ro_toolbar_update_all_hotlists();
+
+ nsurl_unref(hotlist_delete_url);
+ hotlist_delete_url = NULL;
+ hotlist_query = QUERY_INVALID;
+}
+
+
+/**
+ * Callback cancelling a URL delete query.
+ *
+ * \param id The ID of the query calling us.
+ * \param res The user's response to the query.
+ * \param *p Callback data (always NULL).
+ */
+
+static void ro_gui_hotlist_remove_cancelled(query_id id,
+ enum query_response res, void *p)
+{
+ nsurl_unref(hotlist_delete_url);
+ hotlist_delete_url = NULL;
+ hotlist_query = QUERY_INVALID;
+}
+
+
+/**
+ * Report whether the hotlist contains a given URL. This will be passed on to
+ * the core hotlist, unless we're configured to use an external hotlist in which
+ * case we always report false.
+ *
+ * \param *url The URL to be tested.
+ * \return true if the hotlist contains the URL; else false.
+ */
+
+bool ro_gui_hotlist_has_page(nsurl *url)
+{
+ if (url == NULL || nsoption_bool(external_hotlists))
+ return false;
+
+ return hotlist_has_url(url);
+}
+
+
+#if 0
+/**
+ * Handle URL dropped on hotlist
+ *
+ * \param message the wimp message we're acting on
+ * \param url the URL to add
+ */
+void ro_gui_hotlist_url_drop(wimp_message *message, const char *url)
+{
+ int x, y;
+ nsurl *nsurl;
+
+ if (hotlist_window.window != message->data.data_xfer.w)
+ return;
+
+ if (url == NULL)
+ return;
+
+ if (nsurl_create(url, &nsurl) != NSERROR_OK)
+ return;
+
+ ro_gui_tree_get_tree_coordinates(hotlist_window.tree,
+ message->data.data_xfer.pos.x,
+ message->data.data_xfer.pos.y,
+ &x, &y);
+
+ hotlist_add_entry(nsurl, NULL, true, y);
+ nsurl_unref(nsurl);
+}
+#endif
diff --git a/frontends/riscos/hotlist.h b/frontends/riscos/hotlist.h
new file mode 100644
index 000000000..0b87a2e96
--- /dev/null
+++ b/frontends/riscos/hotlist.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2006 Richard Wilson <info@tinct.net>
+ * Copyright 2010, 2013 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Hotlist (interface).
+ */
+
+#ifndef _NETSURF_RISCOS_HOTLIST_H_
+#define _NETSURF_RISCOS_HOTLIST_H_
+
+/* Hotlist Protocol Messages, which are currently not in OSLib. */
+
+#ifndef message_HOTLIST_ADD_URL
+#define message_HOTLIST_ADD_URL 0x4af81
+#endif
+
+#ifndef message_HOTLIST_CHANGED
+#define message_HOTLIST_CHANGED 0x4af82
+#endif
+
+#include "riscos/menus.h"
+
+struct nsurl;
+
+void ro_gui_hotlist_preinitialise(void);
+void ro_gui_hotlist_postinitialise(void);
+void ro_gui_hotlist_destroy(void);
+void ro_gui_hotlist_open(void);
+void ro_gui_hotlist_save(void);
+bool ro_gui_hotlist_check_window(wimp_w window);
+bool ro_gui_hotlist_check_menu(wimp_menu *menu);
+void ro_gui_hotlist_add_page(struct nsurl *url);
+void ro_gui_hotlist_add_cleanup(void);
+void ro_gui_hotlist_remove_page(struct nsurl *url);
+bool ro_gui_hotlist_has_page(struct nsurl *url);
+
+#endif
diff --git a/frontends/riscos/iconbar.c b/frontends/riscos/iconbar.c
new file mode 100644
index 000000000..9cff116a1
--- /dev/null
+++ b/frontends/riscos/iconbar.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Iconbar icon and menus (implementation).
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <features.h>
+#include <oslib/os.h>
+#include <oslib/osbyte.h>
+#include <oslib/wimp.h>
+
+#include "utils/nsoption.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/nsurl.h"
+#include "desktop/browser.h"
+
+#include "riscos/gui.h"
+#include "riscos/configure.h"
+#include "riscos/cookies.h"
+#include "riscos/dialog.h"
+#include "riscos/global_history.h"
+#include "riscos/hotlist.h"
+#include "riscos/iconbar.h"
+#include "riscos/wimp_event.h"
+
+static bool ro_gui_iconbar_click(wimp_pointer *pointer);
+
+static bool ro_gui_iconbar_menu_select(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action);
+static void ro_gui_iconbar_menu_warning(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action);
+
+
+static wimp_menu *ro_gui_iconbar_menu = NULL; /**< Iconbar menu handle */
+
+/**
+ * Initialise the iconbar menus, create an icon and register the necessary
+ * handlers to look after them all.
+ */
+
+void ro_gui_iconbar_initialise(void)
+{
+ os_error *error;
+
+ /* Build the iconbar menu */
+
+ static const struct ns_menu iconbar_definition = {
+ "NetSurf", {
+ { "Info", NO_ACTION, &dialog_info },
+ { "AppHelpNoShortcut", HELP_OPEN_CONTENTS, 0 },
+ { "Open", BROWSER_NAVIGATE_URL, 0 },
+ { "Open.OpenURL", BROWSER_NAVIGATE_URL, &dialog_openurl },
+ { "Open.HotlistShowNoShortcut", HOTLIST_SHOW, 0 },
+ { "Open.HistGlobalNoShortcut", HISTORY_SHOW_GLOBAL, 0 },
+ { "Open.ShowCookies", COOKIES_SHOW, 0 },
+ { "Choices", CHOICES_SHOW, 0 },
+ { "Quit", APPLICATION_QUIT, 0 },
+ {NULL, 0, 0}
+ }
+ };
+ ro_gui_iconbar_menu = ro_gui_menu_define_menu(&iconbar_definition);
+
+ /* Create an iconbar icon. */
+
+ wimp_icon_create icon = {
+ wimp_ICON_BAR_RIGHT,
+ { { 0, 0, 68, 68 },
+ wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED |
+ (wimp_BUTTON_CLICK << wimp_ICON_BUTTON_TYPE_SHIFT),
+ { "!netsurf" } } };
+ error = xwimp_create_icon(&icon, 0);
+ if (error) {
+ LOG("xwimp_create_icon: 0x%x: %s", error->errnum, error->errmess);
+ die(error->errmess);
+ }
+
+ /* Register handlers to look after clicks and menu actions. */
+
+ ro_gui_wimp_event_register_mouse_click(wimp_ICON_BAR,
+ ro_gui_iconbar_click);
+
+ ro_gui_wimp_event_register_menu(wimp_ICON_BAR, ro_gui_iconbar_menu,
+ true, true);
+ ro_gui_wimp_event_register_menu_selection(wimp_ICON_BAR,
+ ro_gui_iconbar_menu_select);
+ ro_gui_wimp_event_register_menu_warning(wimp_ICON_BAR,
+ ro_gui_iconbar_menu_warning);
+}
+
+
+/**
+ * Handle Mouse_Click events on the iconbar icon.
+ *
+ * \param *pointer The wimp event block to be processed.
+ * \return true if the event was handled; else false.
+ */
+
+bool ro_gui_iconbar_click(wimp_pointer *pointer)
+{
+ int key_down = 0;
+ nsurl *url;
+ nserror error;
+
+ switch (pointer->buttons) {
+ case wimp_CLICK_SELECT:
+ if (nsoption_charp(homepage_url) != NULL) {
+ error = nsurl_create(nsoption_charp(homepage_url), &url);
+ } else {
+ error = nsurl_create(NETSURF_HOMEPAGE, &url);
+ }
+
+ /* create an initial browser window */
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ ro_warn_user(messages_get_errorcode(error), 0);
+ }
+ break;
+
+ case wimp_CLICK_ADJUST:
+ xosbyte1(osbyte_SCAN_KEYBOARD, 0 ^ 0x80, 0, &key_down);
+ if (key_down == 0)
+ ro_gui_hotlist_open();
+ break;
+ }
+
+ return true;
+}
+
+/**
+ * Handle submenu warnings for the iconbar menu
+ *
+ * \param w The window owning the menu.
+ * \param i The icon owning the menu.
+ * \param *menu The menu to which the warning applies.
+ * \param *selection The wimp menu selection data.
+ * \param action The selected menu action.
+ */
+
+void ro_gui_iconbar_menu_warning(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action)
+{
+ if (w != wimp_ICON_BAR || i != wimp_ICON_WINDOW)
+ return;
+
+ switch (action) {
+ case BROWSER_NAVIGATE_URL:
+ ro_gui_dialog_prepare_open_url();
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * Handle selections from the iconbar menu
+ *
+ * \param w The window owning the menu.
+ * \param i The icon owning the menu.
+ * \param menu The wimp menu
+ * \param selection The wimp menu selection data.
+ * \param action The selected menu action.
+ * \return true if action accepted; else false.
+ */
+
+bool ro_gui_iconbar_menu_select(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action)
+{
+ nsurl *url;
+ nserror error;
+
+ if (w != wimp_ICON_BAR || i != wimp_ICON_WINDOW)
+ return false;
+
+ switch (action) {
+ case HELP_OPEN_CONTENTS:
+ error = nsurl_create("http://www.netsurf-browser.org/documentation/", &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ ro_warn_user(messages_get_errorcode(error), 0);
+ }
+ return true;
+
+ case BROWSER_NAVIGATE_URL:
+ ro_gui_dialog_prepare_open_url();
+ ro_gui_dialog_open_persistent(NULL, dialog_openurl, true);
+ return true;
+ case HOTLIST_SHOW:
+ ro_gui_hotlist_open();
+ return true;
+ case HISTORY_SHOW_GLOBAL:
+ ro_gui_global_history_open();
+ return true;
+ case COOKIES_SHOW:
+ ro_gui_cookies_open();
+ return true;
+ case CHOICES_SHOW:
+ ro_gui_configure_show();
+ return true;
+ case APPLICATION_QUIT:
+ if (ro_gui_prequit()) {
+ LOG("QUIT in response to user request");
+ riscos_done = true;
+ }
+ return true;
+ default:
+ return false;
+ }
+
+ return false;
+}
+
+/**
+ * Check if a particular menu handle is the iconbar menu
+ *
+ * \param *menu The menu in question.
+ * \return true if this menu is the iconbar menu
+ */
+
+bool ro_gui_iconbar_check_menu(wimp_menu *menu)
+{
+ return (ro_gui_iconbar_menu == menu) ? true : false;
+}
+
diff --git a/frontends/riscos/iconbar.h b/frontends/riscos/iconbar.h
new file mode 100644
index 000000000..e40f9acd8
--- /dev/null
+++ b/frontends/riscos/iconbar.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Iconbar icon and menus (interface).
+ */
+
+#include <stdbool.h>
+
+#ifndef _NETSURF_RISCOS_ICONBAR_H_
+#define _NETSURF_RISCOS_ICONBAR_H_
+
+void ro_gui_iconbar_initialise(void);
+bool ro_gui_iconbar_check_menu(wimp_menu *menu);
+
+#endif
+
diff --git a/frontends/riscos/image.c b/frontends/riscos/image.c
new file mode 100644
index 000000000..acbe62d98
--- /dev/null
+++ b/frontends/riscos/image.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include <swis.h>
+#include <stdlib.h>
+#include <oslib/colourtrans.h>
+#include <oslib/osspriteop.h>
+
+#include "utils/nsoption.h"
+#include "utils/log.h"
+
+#include "riscos/image.h"
+#include "riscos/gui.h"
+#include "riscos/tinct.h"
+
+static bool image_redraw_tinct(osspriteop_id header, int x, int y,
+ int req_width, int req_height, int width, int height,
+ colour background_colour, bool repeatx, bool repeaty,
+ bool alpha, unsigned int tinct_options);
+static bool image_redraw_os(osspriteop_id header, int x, int y,
+ int req_width, int req_height, int width, int height);
+
+/**
+ * Plot an image at the given coordinates using the method specified
+ *
+ * \param area The sprite area containing the sprite
+ * \param x Left edge of sprite
+ * \param y Top edge of sprite
+ * \param req_width The requested width of the sprite
+ * \param req_height The requested height of the sprite
+ * \param width The actual width of the sprite
+ * \param height The actual height of the sprite
+ * \param background_colour The background colour to blend to
+ * \param repeatx Repeat the image in the x direction
+ * \param repeaty Repeat the image in the y direction
+ * \param background Use background image settings (otherwise foreground)
+ * \param type The plot method to use
+ * \return true on success, false otherwise
+ */
+bool image_redraw(osspriteop_area *area, int x, int y, int req_width,
+ int req_height, int width, int height,
+ colour background_colour,
+ bool repeatx, bool repeaty, bool background, image_type type)
+{
+ unsigned int tinct_options;
+
+ /* failed decompression/loading can result in no image being present */
+ if (!area)
+ return false;
+
+ osspriteop_id header = (osspriteop_id)
+ ((char*) area + area->first);
+ req_width *= 2;
+ req_height *= 2;
+ width *= 2;
+ height *= 2;
+ tinct_options = background ? nsoption_int(plot_bg_quality) :
+ nsoption_int(plot_fg_quality);
+ switch (type) {
+ case IMAGE_PLOT_TINCT_ALPHA:
+ return image_redraw_tinct(header, x, y,
+ req_width, req_height,
+ width, height,
+ background_colour,
+ repeatx, repeaty, true,
+ tinct_options);
+ case IMAGE_PLOT_TINCT_OPAQUE:
+ return image_redraw_tinct(header, x, y,
+ req_width, req_height,
+ width, height,
+ background_colour,
+ repeatx, repeaty, false,
+ tinct_options);
+ case IMAGE_PLOT_OS:
+ return image_redraw_os(header, x, y, req_width,
+ req_height, width, height);
+ default:
+ break;
+ }
+
+ return false;
+}
+
+/**
+ * Plot an image at the given coordinates using tinct
+ *
+ * \param header The sprite header
+ * \param x Left edge of sprite
+ * \param y Top edge of sprite
+ * \param req_width The requested width of the sprite
+ * \param req_height The requested height of the sprite
+ * \param width The actual width of the sprite
+ * \param height The actual height of the sprite
+ * \param background_colour The background colour to blend to
+ * \param repeatx Repeat the image in the x direction
+ * \param repeaty Repeat the image in the y direction
+ * \param alpha Use the alpha channel
+ * \param tinct_options The base option set to use
+ * \return true on success, false otherwise
+ */
+bool image_redraw_tinct(osspriteop_id header, int x, int y,
+ int req_width, int req_height, int width, int height,
+ colour background_colour, bool repeatx, bool repeaty,
+ bool alpha, unsigned int tinct_options)
+{
+ _kernel_oserror *error;
+
+ /* Set up our flagword
+ */
+ tinct_options |= background_colour << tinct_BACKGROUND_SHIFT;
+ if (print_active)
+ tinct_options |= tinct_USE_OS_SPRITE_OP;
+ if (repeatx)
+ tinct_options |= tinct_FILL_HORIZONTALLY;
+ if (repeaty)
+ tinct_options |= tinct_FILL_VERTICALLY;
+
+ if (alpha) {
+ error = _swix(Tinct_PlotScaledAlpha, _INR(2,7),
+ header, x, y - req_height,
+ req_width, req_height, tinct_options);
+ } else {
+ error = _swix(Tinct_PlotScaled, _INR(2,7),
+ header, x, y - req_height,
+ req_width, req_height, tinct_options);
+ }
+
+ if (error) {
+ LOG("xtinct_plotscaled%s: 0x%x: %s", (alpha ? "alpha" : ""), error->errnum, error->errmess);
+ return false;
+ }
+
+ return true;
+}
+
+
+/**
+ * Plot an image at the given coordinates using os_spriteop
+ *
+ * \param header The sprite header
+ * \param x Left edge of sprite
+ * \param y Top edge of sprite
+ * \param req_width The requested width of the sprite
+ * \param req_height The requested height of the sprite
+ * \param width The actual width of the sprite
+ * \param height The actual height of the sprite
+ * \return true on success, false otherwise
+ */
+bool image_redraw_os(osspriteop_id header, int x, int y, int req_width,
+ int req_height, int width, int height)
+{
+ int size;
+ os_factors f;
+ osspriteop_trans_tab *table;
+ os_error *error;
+
+ error = xcolourtrans_generate_table_for_sprite(
+ (osspriteop_area *)0x100, header,
+ os_CURRENT_MODE,
+ colourtrans_CURRENT_PALETTE,
+ 0, colourtrans_GIVEN_SPRITE, 0, 0, &size);
+ if (error) {
+ LOG("xcolourtrans_generate_table_for_sprite: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+
+ table = calloc(size, sizeof(char));
+ if (!table) {
+ LOG("malloc failed");
+ ro_warn_user("NoMemory", 0);
+ return false;
+ }
+
+ error = xcolourtrans_generate_table_for_sprite(
+ (osspriteop_area *)0x100, header,
+ os_CURRENT_MODE,
+ colourtrans_CURRENT_PALETTE,
+ table, colourtrans_GIVEN_SPRITE, 0, 0, 0);
+ if (error) {
+ LOG("xcolourtrans_generate_table_for_sprite: 0x%x: %s", error->errnum, error->errmess);
+ free(table);
+ return false;
+ }
+
+ f.xmul = req_width;
+ f.ymul = req_height;
+ f.xdiv = width;
+ f.ydiv = height;
+
+ error = xosspriteop_put_sprite_scaled(osspriteop_PTR,
+ (osspriteop_area *)0x100, header,
+ x, (int)(y - req_height),
+ 8, &f, table);
+ if (error) {
+ LOG("xosspriteop_put_sprite_scaled: 0x%x: %s", error->errnum, error->errmess);
+ free(table);
+ return false;
+ }
+
+ free(table);
+
+ return true;
+}
diff --git a/frontends/riscos/image.h b/frontends/riscos/image.h
new file mode 100644
index 000000000..a11388c13
--- /dev/null
+++ b/frontends/riscos/image.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_RISCOS_IMAGE_H_
+#define _NETSURF_RISCOS_IMAGE_H_
+
+#include <stdbool.h>
+#include "desktop/plot_style.h"
+#include "oslib/osspriteop.h"
+
+struct osspriteop_area;
+
+typedef enum {
+ IMAGE_PLOT_TINCT_ALPHA,
+ IMAGE_PLOT_TINCT_OPAQUE,
+ IMAGE_PLOT_OS
+} image_type;
+
+bool image_redraw(osspriteop_area *area, int x, int y, int req_width,
+ int req_height, int width, int height,
+ colour background_colour,
+ bool repeatx, bool repeaty, bool background, image_type type);
+
+#endif
diff --git a/frontends/riscos/menus.c b/frontends/riscos/menus.c
new file mode 100644
index 000000000..37285c9d1
--- /dev/null
+++ b/frontends/riscos/menus.c
@@ -0,0 +1,958 @@
+/*
+ * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * Menu creation and handling implementation.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include "oslib/os.h"
+#include "oslib/osbyte.h"
+#include "oslib/osgbpb.h"
+#include "oslib/territory.h"
+#include "oslib/wimp.h"
+
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utf8.h"
+#include "content/content.h"
+#include "content/hlcache.h"
+#include "content/urldb.h"
+#include "desktop/cookie_manager.h"
+#include "desktop/browser.h"
+#include "desktop/textinput.h"
+
+#include "riscos/dialog.h"
+#include "riscos/configure.h"
+#include "riscos/cookies.h"
+#include "riscos/gui.h"
+#include "riscos/global_history.h"
+#include "riscos/help.h"
+#include "riscos/hotlist.h"
+#include "riscos/menus.h"
+#include "utils/nsoption.h"
+#include "riscos/save.h"
+#include "riscos/tinct.h"
+#include "riscos/toolbar.h"
+#include "riscos/treeview.h"
+#include "riscos/url_suggest.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/ucstables.h"
+
+struct menu_definition_entry {
+ menu_action action; /**< menu action */
+ wimp_menu_entry *menu_entry; /**< corresponding menu entry */
+ const char *entry_key; /**< Messages key for entry text */
+ struct menu_definition_entry *next; /**< next menu entry */
+};
+
+struct menu_definition {
+ wimp_menu *menu; /**< corresponding menu */
+ const char *title_key; /**< Messages key for title text */
+ int current_encoding; /**< Identifier for current text encoding of menu text (as per OS_Byte,71,127) */
+ struct menu_definition_entry *entries; /**< menu entries */
+ struct menu_definition *next; /**< next menu */
+};
+
+static void ro_gui_menu_closed(void);
+static void ro_gui_menu_define_menu_add(struct menu_definition *definition,
+ const struct ns_menu *menu, int depth,
+ wimp_menu_entry *parent_entry,
+ int first, int last, const char *prefix, int prefix_length);
+static struct menu_definition *ro_gui_menu_find_menu(wimp_menu *menu);
+static struct menu_definition_entry *ro_gui_menu_find_entry(wimp_menu *menu,
+ menu_action action);
+static menu_action ro_gui_menu_find_action(wimp_menu *menu,
+ wimp_menu_entry *menu_entry);
+static int ro_gui_menu_get_checksum(void);
+static bool ro_gui_menu_translate(struct menu_definition *menu);
+
+
+/* default menu item flags */
+#define DEFAULT_FLAGS (wimp_ICON_TEXT | wimp_ICON_FILLED | \
+ (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) | \
+ (wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT))
+
+/** The currently defined menus to perform actions for */
+static struct menu_definition *ro_gui_menu_definitions;
+/** The current menu being worked with (may not be open) */
+wimp_menu *current_menu;
+/** Whether a menu is currently open */
+bool current_menu_open = false;
+/** Window that owns the current menu */
+wimp_w current_menu_window;
+/** Icon that owns the current menu (only valid for popup menus) */
+static wimp_i current_menu_icon;
+/** The available menus */
+wimp_menu *image_quality_menu, *proxy_type_menu, *languages_menu;
+
+/* the values given in PRM 3-157 for how to check menus/windows are
+ * incorrect so we use a hack of checking if the sub-menu has bit 0
+ * set which is undocumented but true of window handles on
+ * all target OS versions */
+#define IS_MENU(menu) !((int)(menu) & 1)
+
+/**
+ * Create menu structures.
+ */
+
+void ro_gui_menu_init(void)
+{
+ /* image quality menu */
+ static const struct ns_menu images_definition = {
+ "Display", {
+ { "ImgStyle0", NO_ACTION, 0 },
+ { "ImgStyle1", NO_ACTION, 0 },
+ { "ImgStyle2", NO_ACTION, 0 },
+ { "ImgStyle3", NO_ACTION, 0 },
+ {NULL, 0, 0}
+ }
+ };
+ image_quality_menu = ro_gui_menu_define_menu(&images_definition);
+
+ /* proxy menu */
+ static const struct ns_menu proxy_type_definition = {
+ "ProxyType", {
+ { "ProxyNone", NO_ACTION, 0 },
+ { "ProxyNoAuth", NO_ACTION, 0 },
+ { "ProxyBasic", NO_ACTION, 0 },
+ { "ProxyNTLM", NO_ACTION, 0 },
+ {NULL, 0, 0}
+ }
+ };
+ proxy_type_menu = ro_gui_menu_define_menu(&proxy_type_definition);
+
+ /* special case menus */
+ ro_gui_url_suggest_init();
+
+ /* Note: This table *must* be kept in sync with the LangNames file */
+ static const struct ns_menu lang_definition = {
+ "Languages", {
+ { "lang_af", NO_ACTION, 0 },
+ { "lang_bm", NO_ACTION, 0 },
+ { "lang_ca", NO_ACTION, 0 },
+ { "lang_cs", NO_ACTION, 0 },
+ { "lang_cy", NO_ACTION, 0 },
+ { "lang_da", NO_ACTION, 0 },
+ { "lang_de", NO_ACTION, 0 },
+ { "lang_en", NO_ACTION, 0 },
+ { "lang_es", NO_ACTION, 0 },
+ { "lang_et", NO_ACTION, 0 },
+ { "lang_eu", NO_ACTION, 0 },
+ { "lang_ff", NO_ACTION, 0 },
+ { "lang_fi", NO_ACTION, 0 },
+ { "lang_fr", NO_ACTION, 0 },
+ { "lang_ga", NO_ACTION, 0 },
+ { "lang_gl", NO_ACTION, 0 },
+ { "lang_ha", NO_ACTION, 0 },
+ { "lang_hr", NO_ACTION, 0 },
+ { "lang_hu", NO_ACTION, 0 },
+ { "lang_id", NO_ACTION, 0 },
+ { "lang_is", NO_ACTION, 0 },
+ { "lang_it", NO_ACTION, 0 },
+ { "lang_lt", NO_ACTION, 0 },
+ { "lang_lv", NO_ACTION, 0 },
+ { "lang_ms", NO_ACTION, 0 },
+ { "lang_mt", NO_ACTION, 0 },
+ { "lang_nl", NO_ACTION, 0 },
+ { "lang_no", NO_ACTION, 0 },
+ { "lang_pl", NO_ACTION, 0 },
+ { "lang_pt", NO_ACTION, 0 },
+ { "lang_rn", NO_ACTION, 0 },
+ { "lang_ro", NO_ACTION, 0 },
+ { "lang_rw", NO_ACTION, 0 },
+ { "lang_sk", NO_ACTION, 0 },
+ { "lang_sl", NO_ACTION, 0 },
+ { "lang_so", NO_ACTION, 0 },
+ { "lang_sq", NO_ACTION, 0 },
+ { "lang_sr", NO_ACTION, 0 },
+ { "lang_sv", NO_ACTION, 0 },
+ { "lang_sw", NO_ACTION, 0 },
+ { "lang_tr", NO_ACTION, 0 },
+ { "lang_uz", NO_ACTION, 0 },
+ { "lang_vi", NO_ACTION, 0 },
+ { "lang_wo", NO_ACTION, 0 },
+ { "lang_xs", NO_ACTION, 0 },
+ { "lang_yo", NO_ACTION, 0 },
+ { "lang_zu", NO_ACTION, 0 },
+ { NULL, 0, 0 }
+ }
+ };
+ languages_menu = ro_gui_menu_define_menu(&lang_definition);
+}
+
+
+/**
+ * Display a menu.
+ *
+ * \param *menu Pointer to the menu to be displayed.
+ * \param x The x position.
+ * \param y The y position.
+ * \param w The window that the menu belongs to.
+ */
+
+void ro_gui_menu_create(wimp_menu *menu, int x, int y, wimp_w w)
+{
+ os_error *error;
+ struct menu_definition *definition;
+
+ /* translate menu, if necessary (this returns quickly
+ * if there's nothing to be done) */
+ definition = ro_gui_menu_find_menu(menu);
+ if (definition) {
+ if (!ro_gui_menu_translate(definition)) {
+ ro_warn_user("NoMemory", 0);
+ return;
+ }
+ }
+
+ /* store the menu characteristics */
+ current_menu = menu;
+ current_menu_window = w;
+ current_menu_icon = wimp_ICON_WINDOW;
+
+ /* create the menu */
+ current_menu_open = true;
+ error = xwimp_create_menu(menu, x - 64, y);
+ if (error) {
+ LOG("xwimp_create_menu: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MenuError", error->errmess);
+ ro_gui_menu_closed();
+ }
+}
+
+
+/**
+ * Display a pop-up menu next to the specified icon.
+ *
+ * \param menu menu to open
+ * \param w window handle
+ * \param i icon handle
+ */
+
+void ro_gui_popup_menu(wimp_menu *menu, wimp_w w, wimp_i i)
+{
+ wimp_window_state state;
+ wimp_icon_state icon_state;
+ os_error *error;
+
+ state.w = w;
+ icon_state.w = w;
+ icon_state.i = i;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MenuError", error->errmess);
+ return;
+ }
+
+ error = xwimp_get_icon_state(&icon_state);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MenuError", error->errmess);
+ return;
+ }
+
+ ro_gui_menu_create(menu,
+ state.visible.x0 + icon_state.icon.extent.x1 + 64,
+ state.visible.y1 + icon_state.icon.extent.y1 -
+ state.yscroll, w);
+ current_menu_icon = i;
+}
+
+
+/**
+ * Forcibly close any menu or transient dialogue box that is currently open.
+ */
+
+void ro_gui_menu_destroy(void)
+{
+ os_error *error;
+
+ if (current_menu == NULL)
+ return;
+
+ error = xwimp_create_menu(wimp_CLOSE_MENU, 0, 0);
+ if (error) {
+ LOG("xwimp_create_menu: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MenuError", error->errmess);
+ }
+
+ ro_gui_menu_closed();
+}
+
+
+/**
+ * Allow the current menu window to change, if the window is deleted and
+ * recreated while a menu is active on an Adjust-click.
+ *
+ * \param from The original window handle.
+ * \param to The new replacement window handle.
+ */
+
+void ro_gui_menu_window_changed(wimp_w from, wimp_w to)
+{
+
+ if (from == current_menu_window)
+ current_menu_window = to;
+}
+
+
+/**
+ * Handle menu selection.
+ */
+
+void ro_gui_menu_selection(wimp_selection *selection)
+{
+ int i; //, j;
+ wimp_menu_entry *menu_entry;
+ menu_action action;
+ wimp_pointer pointer;
+ os_error *error;
+ int previous_menu_icon = current_menu_icon;
+
+ /* if we are using gui_multitask then menu selection events
+ * may be delivered after the menu has been closed. As such,
+ * we simply ignore these events. */
+ if (!current_menu)
+ return;
+
+ assert(current_menu_window);
+
+ /* get the menu entry and associated action and definition */
+ menu_entry = &current_menu->entries[selection->items[0]];
+ for (i = 1; selection->items[i] != -1; i++)
+ menu_entry = &menu_entry->sub_menu->
+ entries[selection->items[i]];
+ action = ro_gui_menu_find_action(current_menu, menu_entry);
+
+ /* Deal with the menu action. If this manages to re-prepare the
+ * menu for re-opening, we test for and act on Adjust clicks.
+ */
+
+ if (!ro_gui_wimp_event_menu_selection(current_menu_window,
+ current_menu_icon, current_menu, selection, action))
+ return;
+
+ /* re-open the menu for Adjust clicks */
+ error = xwimp_get_pointer_info(&pointer);
+ if (error) {
+ LOG("xwimp_get_pointer_info: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ ro_gui_menu_closed();
+ return;
+ }
+
+ if (pointer.buttons != wimp_CLICK_ADJUST) {
+ ro_gui_menu_closed();
+ return;
+ }
+
+ ro_gui_menu_create(current_menu, 0, 0, current_menu_window);
+ current_menu_icon = previous_menu_icon;
+}
+
+
+/**
+ * Handle Message_MenuWarning.
+ */
+void ro_gui_menu_warning(wimp_message_menu_warning *warning)
+{
+ int i;
+ menu_action action;
+ wimp_menu_entry *menu_entry;
+ os_error *error;
+
+ assert(current_menu);
+ assert(current_menu_window);
+
+ /* get the sub-menu of the warning */
+ if (warning->selection.items[0] == -1)
+ return;
+ menu_entry = &current_menu->entries[warning->selection.items[0]];
+ for (i = 1; warning->selection.items[i] != -1; i++)
+ menu_entry = &menu_entry->sub_menu->
+ entries[warning->selection.items[i]];
+ action = ro_gui_menu_find_action(current_menu, menu_entry);
+
+ /* Process the warning via Wimp_Event, then register the resulting
+ * submenu with the module.
+ */
+
+ ro_gui_wimp_event_submenu_warning(current_menu_window,
+ current_menu_icon, current_menu, &(warning->selection),
+ action);
+
+ if (IS_MENU(menu_entry->sub_menu)) {
+ ro_gui_wimp_event_register_submenu((wimp_w) 0);
+ } else {
+ ro_gui_wimp_event_register_submenu((wimp_w)
+ menu_entry->sub_menu);
+
+ /* If this is a dialogue box, remove the close and back icons.
+ */
+
+ ro_gui_wimp_update_window_furniture((wimp_w)
+ menu_entry->sub_menu,
+ wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_BACK_ICON,
+ 0);
+ }
+
+ /* open the sub-menu */
+
+ error = xwimp_create_sub_menu(menu_entry->sub_menu,
+ warning->pos.x, warning->pos.y);
+ if (error) {
+ LOG("xwimp_create_sub_menu: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MenuError", error->errmess);
+ }
+}
+
+
+/**
+ * Handle Message_MenusDeleted, removing our current record of an open menu
+ * if it matches the deleted menu handle.
+ *
+ * \param *deleted The message block.
+ */
+
+void ro_gui_menu_message_deleted(wimp_message_menus_deleted *deleted)
+{
+ if (deleted != NULL && deleted->menu == current_menu)
+ ro_gui_menu_closed();
+}
+
+
+/**
+ * Clean up after a menu has been closed, or forcibly close an open menu.
+ */
+
+static void ro_gui_menu_closed(void)
+{
+ if (current_menu != NULL)
+ ro_gui_wimp_event_menus_closed(current_menu_window,
+ current_menu_icon, current_menu);
+
+ current_menu = NULL;
+ current_menu_window = NULL;
+ current_menu_icon = 0;
+ current_menu_open = false;
+}
+
+
+/**
+ * Update the current menu by sending it a Menu Prepare event through wimp_event
+ * and then reopening it if the contents has changed.
+ *
+ * \param *menu The menu to refresh: if 0, the current menu will be
+ * refreshed regardless, otherwise it will be refreshed
+ * only if it matches the supplied handle.
+ */
+
+void ro_gui_menu_refresh(wimp_menu *menu)
+{
+ int checksum = 0;
+
+ if (!current_menu_open)
+ return;
+
+ checksum = ro_gui_menu_get_checksum();
+
+ if (!ro_gui_wimp_event_prepare_menu(current_menu_window,
+ current_menu_icon, current_menu))
+ return;
+
+ /* \TODO -- Call the menu's event handler here. */
+
+ if (checksum != ro_gui_menu_get_checksum()) {
+ os_error *error;
+ error = xwimp_create_menu(current_menu, 0, 0);
+ if (error) {
+ LOG("xwimp_create_menu: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MenuError", error->errmess);
+ }
+ }
+}
+
+
+
+
+/**
+ * Creates a wimp_menu and adds it to the list to handle actions for.
+ *
+ * \param menu The data to create the menu with
+ * \return The menu created, or NULL on failure
+ */
+wimp_menu *ro_gui_menu_define_menu(const struct ns_menu *menu)
+{
+ struct menu_definition *definition;
+ int entry;
+
+ definition = calloc(sizeof(struct menu_definition), 1);
+ if (!definition) {
+ die("No memory to create menu definition.");
+ return NULL; /* For the benefit of scan-build */
+ }
+
+ /* link in the menu to our list */
+ definition->next = ro_gui_menu_definitions;
+ ro_gui_menu_definitions = definition;
+
+ /* count number of menu entries */
+ for (entry = 0; menu->entries[entry].text; entry++)
+ /* do nothing */;
+
+ /* create our definitions */
+ ro_gui_menu_define_menu_add(definition, menu, 0, NULL,
+ 0, entry, NULL, 0);
+
+ /* and translate menu into current encoding */
+ if (!ro_gui_menu_translate(definition))
+ die("No memory to translate menu.");
+
+ return definition->menu;
+}
+
+/**
+ * Create a wimp menu tree from ns_menu data.
+ * This function does *not* deal with the menu textual content - it simply
+ * creates and populates the appropriate structures. Textual content is
+ * generated by ro_gui_menu_translate_menu()
+ *
+ * \param definition Top level menu definition
+ * \param menu Menu declaration data
+ * \param depth Depth of menu we're currently building
+ * \param parent_entry Entry in parent menu, or NULL if root menu
+ * \param first First index in declaration data that is used by this menu
+ * \param last Last index in declaration data that is used by this menu
+ * \param prefix Prefix pf menu declaration string already seen
+ * \param prefix_length Length of prefix
+ */
+void ro_gui_menu_define_menu_add(struct menu_definition *definition,
+ const struct ns_menu *menu, int depth,
+ wimp_menu_entry *parent_entry, int first, int last,
+ const char *prefix, int prefix_length)
+{
+ int entry;
+ int entries = 0;
+ int matches[last - first + 1];
+ const char *text, *menu_text;
+ wimp_menu *new_menu;
+ struct menu_definition_entry *definition_entry;
+
+ /* step 1: store the matches for depth and subset string */
+ for (entry = first; entry < last; entry++) {
+ const char *match;
+ int cur_depth = 0;
+ match = menu->entries[entry].text;
+
+ /* skip specials at start of string */
+ while (!isalnum(*match))
+ match++;
+
+ /* attempt prefix match */
+ if ((prefix) && (strncmp(match, prefix, prefix_length)))
+ continue;
+
+ /* Find depth of this entry */
+ while (*match)
+ if (*match++ == '.')
+ cur_depth++;
+
+ if (depth == cur_depth)
+ matches[entries++] = entry;
+ }
+ matches[entries] = last;
+
+ /* no entries, so exit */
+ if (entries == 0)
+ return;
+
+ /* step 2: build and link the menu. we must use realloc to stop
+ * our memory fragmenting so we can test for sub-menus easily */
+ new_menu = (wimp_menu *)malloc(wimp_SIZEOF_MENU(entries));
+ if (!new_menu)
+ die("No memory to create menu.");
+
+ if (parent_entry) {
+ /* Fix up sub menu pointer */
+ parent_entry->sub_menu = new_menu;
+ } else {
+ /* Root menu => fill in definition struct */
+ definition->title_key = menu->title;
+ definition->current_encoding = 0;
+ definition->menu = new_menu;
+ }
+
+ /* this is fixed up in ro_gui_menu_translate() */
+ new_menu->title_data.indirected_text.text = NULL;
+
+ /* fill in menu flags */
+ ro_gui_menu_init_structure(new_menu, entries);
+
+ /* and then create the entries */
+ for (entry = 0; entry < entries; entry++) {
+ /* add the entry */
+ int id = matches[entry];
+
+ text = menu->entries[id].text;
+
+ /* fill in menu flags from specials at start of string */
+ new_menu->entries[entry].menu_flags = 0;
+ while (!isalnum(*text)) {
+ if (*text == '_')
+ new_menu->entries[entry].menu_flags |=
+ wimp_MENU_SEPARATE;
+ text++;
+ }
+
+ /* get messages key for menu entry */
+ menu_text = strrchr(text, '.');
+ if (!menu_text)
+ /* no '.' => top-level entry */
+ menu_text = text;
+ else
+ menu_text++; /* and move past the '.' */
+
+ /* fill in submenu data */
+ if (menu->entries[id].sub_window)
+ new_menu->entries[entry].sub_menu =
+ (wimp_menu *) (*menu->entries[id].sub_window);
+
+ /* this is fixed up in ro_gui_menu_translate() */
+ new_menu->entries[entry].data.indirected_text.text = NULL;
+
+ /* create definition entry */
+ definition_entry =
+ malloc(sizeof(struct menu_definition_entry));
+ if (!definition_entry)
+ die("Unable to create menu definition entry");
+ definition_entry->action = menu->entries[id].action;
+ definition_entry->menu_entry = &new_menu->entries[entry];
+ definition_entry->entry_key = menu_text;
+ definition_entry->next = definition->entries;
+ definition->entries = definition_entry;
+
+ /* recurse */
+ if (new_menu->entries[entry].sub_menu == wimp_NO_SUB_MENU) {
+ ro_gui_menu_define_menu_add(definition, menu,
+ depth + 1, &new_menu->entries[entry],
+ matches[entry], matches[entry + 1],
+ text, strlen(text));
+ }
+
+ /* give menu warnings */
+ if (new_menu->entries[entry].sub_menu != wimp_NO_SUB_MENU)
+ new_menu->entries[entry].menu_flags |=
+ wimp_MENU_GIVE_WARNING;
+ }
+ new_menu->entries[0].menu_flags |= wimp_MENU_TITLE_INDIRECTED;
+ new_menu->entries[entries - 1].menu_flags |= wimp_MENU_LAST;
+}
+
+
+/**
+ * Initialise the basic state of a menu structure so all entries are
+ * indirected text with no flags, no submenu.
+ */
+void ro_gui_menu_init_structure(wimp_menu *menu, int entries)
+{
+ int i;
+
+ menu->title_fg = wimp_COLOUR_BLACK;
+ menu->title_bg = wimp_COLOUR_LIGHT_GREY;
+ menu->work_fg = wimp_COLOUR_BLACK;
+ menu->work_bg = wimp_COLOUR_WHITE;
+ menu->width = 200;
+ menu->height = wimp_MENU_ITEM_HEIGHT;
+ menu->gap = wimp_MENU_ITEM_GAP;
+
+ for (i = 0; i < entries; i++) {
+ menu->entries[i].menu_flags = 0;
+ menu->entries[i].sub_menu = wimp_NO_SUB_MENU;
+ menu->entries[i].icon_flags =
+ DEFAULT_FLAGS | wimp_ICON_INDIRECTED;
+ menu->entries[i].data.indirected_text.validation =
+ (char *)-1;
+ }
+ menu->entries[0].menu_flags |= wimp_MENU_TITLE_INDIRECTED;
+ menu->entries[i - 1].menu_flags |= wimp_MENU_LAST;
+}
+
+
+/**
+ * Finds the menu_definition corresponding to a wimp_menu.
+ *
+ * \param menu the menu to find the definition for
+ * \return the associated definition, or NULL if one could not be found
+ */
+struct menu_definition *ro_gui_menu_find_menu(wimp_menu *menu)
+{
+ struct menu_definition *definition;
+
+ if (!menu)
+ return NULL;
+
+ for (definition = ro_gui_menu_definitions; definition;
+ definition = definition->next)
+ if (definition->menu == menu)
+ return definition;
+ return NULL;
+}
+
+
+/**
+ * Finds the key associated with a menu entry translation.
+ *
+ * \param menu the menu to search
+ * \param translated the translated text
+ * \return the original message key, or NULL if one could not be found
+ */
+const char *ro_gui_menu_find_menu_entry_key(wimp_menu *menu,
+ const char *translated)
+{
+ struct menu_definition_entry *entry;
+ struct menu_definition *definition = ro_gui_menu_find_menu(menu);
+
+ if (!definition)
+ return NULL;
+
+ for (entry = definition->entries; entry; entry = entry->next)
+ if (!strcmp(entry->menu_entry->data.indirected_text.text, translated))
+ return entry->entry_key;
+ return NULL;
+}
+
+
+/**
+ * Finds the menu_definition_entry corresponding to an action for a wimp_menu.
+ *
+ * \param menu the menu to search for an action within
+ * \param action the action to find
+ * \return the associated menu entry, or NULL if one could not be found
+ */
+struct menu_definition_entry *ro_gui_menu_find_entry(wimp_menu *menu,
+ menu_action action)
+{
+ struct menu_definition_entry *entry;
+ struct menu_definition *definition = ro_gui_menu_find_menu(menu);
+
+ if (!definition)
+ return NULL;
+
+ for (entry = definition->entries; entry; entry = entry->next)
+ if (entry->action == action)
+ return entry;
+ return NULL;
+}
+
+
+/**
+ * Finds the action corresponding to a wimp_menu_entry for a wimp_menu.
+ *
+ * \param menu the menu to search for an action within
+ * \param menu_entry the menu_entry to find
+ * \return the associated action, or 0 if one could not be found
+ */
+menu_action ro_gui_menu_find_action(wimp_menu *menu, wimp_menu_entry *menu_entry)
+{
+ struct menu_definition_entry *entry;
+ struct menu_definition *definition = ro_gui_menu_find_menu(menu);
+
+ if (!definition)
+ return NO_ACTION;
+
+ for (entry = definition->entries; entry; entry = entry->next) {
+ if (entry->menu_entry == menu_entry)
+ return entry->action;
+ }
+ return NO_ACTION;
+}
+
+
+/**
+ * Sets an action within a menu as having a specific ticked status.
+ *
+ * \param menu the menu containing the action
+ * \param action the action to tick/untick
+ * \param shaded whether to set the item as shaded
+ */
+void ro_gui_menu_set_entry_shaded(wimp_menu *menu, menu_action action,
+ bool shaded)
+{
+ struct menu_definition_entry *entry;
+ struct menu_definition *definition = ro_gui_menu_find_menu(menu);
+
+ if (!definition)
+ return;
+
+ /* we can't use find_entry as multiple actions may appear in one menu */
+ for (entry = definition->entries; entry; entry = entry->next)
+ if (entry->action == action) {
+ if (shaded)
+ entry->menu_entry->icon_flags |= wimp_ICON_SHADED;
+ else
+ entry->menu_entry->icon_flags &= ~wimp_ICON_SHADED;
+ }
+}
+
+
+/**
+ * Sets an action within a menu as having a specific ticked status.
+ *
+ * \param menu the menu containing the action
+ * \param action the action to tick/untick
+ * \param ticked whether to set the item as ticked
+ */
+void ro_gui_menu_set_entry_ticked(wimp_menu *menu, menu_action action,
+ bool ticked)
+{
+ struct menu_definition_entry *entry =
+ ro_gui_menu_find_entry(menu, action);
+ if (entry) {
+ if (ticked)
+ entry->menu_entry->menu_flags |= wimp_MENU_TICKED;
+ else
+ entry->menu_entry->menu_flags &= ~wimp_MENU_TICKED;
+ }
+}
+
+
+/**
+ * Calculates a simple checksum for the current menu state
+ */
+int ro_gui_menu_get_checksum(void)
+{
+ wimp_selection menu_tree;
+ int i = 0, j, checksum = 0;
+ os_error *error;
+ wimp_menu *menu;
+
+ if (!current_menu_open)
+ return 0;
+
+ error = xwimp_get_menu_state((wimp_menu_state_flags)0,
+ &menu_tree, 0, 0);
+ if (error) {
+ LOG("xwimp_get_menu_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MenuError", error->errmess);
+ return 0;
+ }
+
+ menu = current_menu;
+ do {
+ j = 0;
+ do {
+ if (menu->entries[j].icon_flags & wimp_ICON_SHADED)
+ checksum ^= (1 << (i + j * 2));
+ if (menu->entries[j].menu_flags & wimp_MENU_TICKED)
+ checksum ^= (2 << (i + j * 2));
+ } while (!(menu->entries[j++].menu_flags & wimp_MENU_LAST));
+
+ j = menu_tree.items[i++];
+ if (j != -1) {
+ menu = menu->entries[j].sub_menu;
+ if ((!menu) || (menu == wimp_NO_SUB_MENU) || (!IS_MENU(menu)))
+ break;
+ }
+ } while (j != -1);
+
+ return checksum;
+}
+
+/**
+ * Translate a menu's textual content into the system local encoding
+ *
+ * \param menu The menu to translate
+ * \return false if out of memory, true otherwise
+ */
+bool ro_gui_menu_translate(struct menu_definition *menu)
+{
+ os_error *error;
+ int alphabet;
+ struct menu_definition_entry *entry;
+ char *translated;
+ nserror err;
+
+ /* read current alphabet */
+ error = xosbyte1(osbyte_ALPHABET_NUMBER, 127, 0, &alphabet);
+ if (error) {
+ LOG("failed reading alphabet: 0x%x: %s", error->errnum, error->errmess);
+ /* assume Latin1 */
+ alphabet = territory_ALPHABET_LATIN1;
+ }
+
+ if (menu->current_encoding == alphabet)
+ /* menu text is already in the correct encoding */
+ return true;
+
+ /* translate root menu title text */
+ free(menu->menu->title_data.indirected_text.text);
+ err = utf8_to_local_encoding(messages_get(menu->title_key),
+ 0, &translated);
+ if (err != NSERROR_OK) {
+ assert(err != NSERROR_BAD_ENCODING);
+ LOG("utf8_to_enc failed");
+ return false;
+ }
+
+ /* and fill in WIMP menu field */
+ menu->menu->title_data.indirected_text.text = translated;
+
+ /* now the menu entries */
+ for (entry = menu->entries; entry; entry = entry->next) {
+ wimp_menu *submenu = entry->menu_entry->sub_menu;
+
+ /* tranlate menu entry text */
+ free(entry->menu_entry->data.indirected_text.text);
+ err = utf8_to_local_encoding(messages_get(entry->entry_key),
+ 0, &translated);
+ if (err != NSERROR_OK) {
+ assert(err != NSERROR_BAD_ENCODING);
+ LOG("utf8_to_enc failed");
+ return false;
+ }
+
+ /* fill in WIMP menu fields */
+ entry->menu_entry->data.indirected_text.text = translated;
+ entry->menu_entry->data.indirected_text.validation =
+ (char *) -1;
+ entry->menu_entry->data.indirected_text.size =
+ strlen(translated);
+
+ /* child menu title - this is the same as the text of
+ * the parent menu entry, so just copy the pointer */
+ if (submenu != wimp_NO_SUB_MENU && IS_MENU(submenu)) {
+ submenu->title_data.indirected_text.text =
+ translated;
+ }
+ }
+
+ /* finally, set the current encoding of the menu */
+ menu->current_encoding = alphabet;
+
+ return true;
+}
+
diff --git a/frontends/riscos/menus.h b/frontends/riscos/menus.h
new file mode 100644
index 000000000..7faa87ed6
--- /dev/null
+++ b/frontends/riscos/menus.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_RISCOS_MENUS_H_
+#define _NETSURF_RISCOS_MENUS_H_
+
+extern wimp_menu *image_quality_menu, *proxy_type_menu, *languages_menu;
+
+extern wimp_menu *current_menu;
+
+typedef enum {
+
+ /* no/unknown actions */
+ NO_ACTION,
+
+ /* help actions */
+ HELP_OPEN_CONTENTS,
+ HELP_OPEN_GUIDE,
+ HELP_OPEN_INFORMATION,
+ HELP_OPEN_CREDITS,
+ HELP_OPEN_LICENCE,
+ HELP_LAUNCH_INTERACTIVE,
+
+ /* history actions */
+ HISTORY_SHOW_LOCAL,
+ HISTORY_SHOW_GLOBAL,
+
+ /* hotlist actions */
+ HOTLIST_ADD_URL,
+ HOTLIST_SHOW,
+
+ /* cookie actions */
+ COOKIES_SHOW,
+ COOKIES_DELETE,
+
+ /* page actions */
+ BROWSER_PAGE,
+ BROWSER_PAGE_INFO,
+ BROWSER_PRINT,
+ BROWSER_NEW_WINDOW,
+ BROWSER_VIEW_SOURCE,
+
+ /* object actions */
+ BROWSER_OBJECT,
+ BROWSER_OBJECT_OBJECT,
+ BROWSER_OBJECT_LINK,
+ BROWSER_OBJECT_INFO,
+ BROWSER_OBJECT_PRINT,
+ BROWSER_OBJECT_RELOAD,
+ BROWSER_LINK_SAVE,
+ BROWSER_LINK_DOWNLOAD,
+ BROWSER_LINK_NEW_WINDOW,
+
+ /* save actions */
+ BROWSER_OBJECT_SAVE,
+ BROWSER_OBJECT_EXPORT,
+ BROWSER_OBJECT_EXPORT_SPRITE,
+ BROWSER_OBJECT_EXPORT_DRAW,
+ BROWSER_OBJECT_SAVE_URL_URI,
+ BROWSER_OBJECT_SAVE_URL_URL,
+ BROWSER_OBJECT_SAVE_URL_TEXT,
+ BROWSER_SAVE,
+ BROWSER_SAVE_COMPLETE,
+ BROWSER_EXPORT_DRAW,
+ BROWSER_EXPORT_PDF,
+ BROWSER_EXPORT_TEXT,
+ BROWSER_SAVE_URL_URI,
+ BROWSER_SAVE_URL_URL,
+ BROWSER_SAVE_URL_TEXT,
+ BROWSER_LINK_SAVE_URI,
+ BROWSER_LINK_SAVE_URL,
+ BROWSER_LINK_SAVE_TEXT,
+ HOTLIST_EXPORT,
+ HISTORY_EXPORT,
+
+ /* selection actions */
+ BROWSER_SELECTION,
+ BROWSER_SELECTION_SAVE,
+ BROWSER_SELECTION_COPY,
+ BROWSER_SELECTION_CUT,
+ BROWSER_SELECTION_PASTE,
+ BROWSER_SELECTION_CLEAR,
+ BROWSER_SELECTION_ALL,
+
+ /* navigation actions */
+ BROWSER_NAVIGATE_HOME,
+ BROWSER_NAVIGATE_BACK,
+ BROWSER_NAVIGATE_FORWARD,
+ BROWSER_NAVIGATE_UP,
+ BROWSER_NAVIGATE_RELOAD,
+ BROWSER_NAVIGATE_RELOAD_ALL,
+ BROWSER_NAVIGATE_STOP,
+ BROWSER_NAVIGATE_URL,
+
+ /* browser window/display actions */
+ BROWSER_SCALE_VIEW,
+ BROWSER_FIND_TEXT,
+ BROWSER_IMAGES_FOREGROUND,
+ BROWSER_IMAGES_BACKGROUND,
+ BROWSER_BUFFER_ANIMS,
+ BROWSER_BUFFER_ALL,
+ BROWSER_SAVE_VIEW,
+ BROWSER_WINDOW_DEFAULT,
+ BROWSER_WINDOW_STAGGER,
+ BROWSER_WINDOW_COPY,
+ BROWSER_WINDOW_RESET,
+
+ /* tree actions */
+ TREE_NEW_FOLDER,
+ TREE_NEW_LINK,
+ TREE_EXPAND_ALL,
+ TREE_EXPAND_FOLDERS,
+ TREE_EXPAND_LINKS,
+ TREE_COLLAPSE_ALL,
+ TREE_COLLAPSE_FOLDERS,
+ TREE_COLLAPSE_LINKS,
+ TREE_SELECTION,
+ TREE_SELECTION_EDIT,
+ TREE_SELECTION_LAUNCH,
+ TREE_SELECTION_DELETE,
+ TREE_SELECT_ALL,
+ TREE_CLEAR_SELECTION,
+
+ /* toolbar actions */
+ TOOLBAR_BUTTONS,
+ TOOLBAR_ADDRESS_BAR,
+ TOOLBAR_THROBBER,
+ TOOLBAR_EDIT,
+
+ /* misc actions */
+ CHOICES_SHOW,
+ APPLICATION_QUIT,
+} menu_action;
+
+
+/* Menu entry structures for use when defining menus. */
+
+struct ns_menu_entry {
+ const char *text; /**< menu text (from messages) */
+ menu_action action; /**< associated action */
+ wimp_w *sub_window; /**< sub-window if any */
+};
+
+struct ns_menu {
+ const char *title;
+ struct ns_menu_entry entries[];
+};
+
+
+void ro_gui_menu_init(void);
+void ro_gui_menu_create(wimp_menu* menu, int x, int y, wimp_w w);
+void ro_gui_menu_destroy(void);
+void ro_gui_popup_menu(wimp_menu *menu, wimp_w w, wimp_i i);
+void ro_gui_menu_window_changed(wimp_w from, wimp_w to);
+void ro_gui_menu_selection(wimp_selection* selection);
+void ro_gui_menu_warning(wimp_message_menu_warning *warning);
+void ro_gui_menu_message_deleted(wimp_message_menus_deleted *deleted);
+void ro_gui_menu_refresh(wimp_menu *menu);
+void ro_gui_menu_init_structure(wimp_menu *menu, int entries);
+const char *ro_gui_menu_find_menu_entry_key(wimp_menu *menu,
+ const char *translated);
+wimp_menu *ro_gui_menu_define_menu(const struct ns_menu *menu);
+void ro_gui_menu_set_entry_shaded(wimp_menu *menu, menu_action action,
+ bool shaded);
+void ro_gui_menu_set_entry_ticked(wimp_menu *menu, menu_action action,
+ bool ticked);
+
+#endif
diff --git a/frontends/riscos/message.c b/frontends/riscos/message.c
new file mode 100644
index 000000000..1c54ea0b7
--- /dev/null
+++ b/frontends/riscos/message.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2006 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Automated RISC OS message routing (implementation).
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include "oslib/os.h"
+#include "oslib/wimp.h"
+
+#include "utils/log.h"
+
+#include "riscos/message.h"
+#include "riscos/gui.h"
+
+struct active_message {
+ unsigned int message_code;
+ int id;
+ void (*callback)(wimp_message *message);
+ struct active_message *next;
+ struct active_message *previous;
+};
+struct active_message *current_messages = NULL;
+
+static struct active_message *ro_message_add(unsigned int message_code,
+ void (*callback)(wimp_message *message));
+static void ro_message_free(int ref);
+
+
+/**
+ * Sends a message and registers a return route for a bounce.
+ *
+ * \param event the message event type
+ * \param message the message to register a route back for
+ * \param task the task to send a message to, or 0 for broadcast
+ * \param callback the code to call on a bounce
+ * \return true on success, false otherwise
+ */
+bool ro_message_send_message(wimp_event_no event, wimp_message *message,
+ wimp_t task, void (*callback)(wimp_message *message))
+{
+ os_error *error;
+
+ assert(message);
+
+ /* send a message */
+ error = xwimp_send_message(event, message, task);
+ if (error) {
+ LOG("xwimp_send_message: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ /* register the default bounce handler */
+ if (callback) {
+ assert(event == wimp_USER_MESSAGE_RECORDED);
+ return ro_message_register_handler(message, message->action,
+ callback);
+ }
+ return true;
+}
+
+
+/**
+ * Sends a message and registers a return route for a bounce.
+ *
+ * \param event the message event type
+ * \param message the message to register a route back for
+ * \param to_w the window to send the message to
+ * \param to_i the icon
+ * \param callback the code to call on a bounce
+ * \param to_t receives the task handle of the window's creator
+ * \return true on success, false otherwise
+ */
+bool ro_message_send_message_to_window(wimp_event_no event, wimp_message *message,
+ wimp_w to_w, wimp_i to_i, void (*callback)(wimp_message *message),
+ wimp_t *to_t)
+{
+ os_error *error;
+
+ assert(message);
+
+ /* send a message */
+ error = xwimp_send_message_to_window(event, message, to_w, to_i, to_t);
+ if (error) {
+ LOG("xwimp_send_message_to_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ /* register the default bounce handler */
+ if (callback) {
+ assert(event == wimp_USER_MESSAGE_RECORDED);
+ return ro_message_register_handler(message, message->action,
+ callback);
+ }
+ return true;
+}
+
+
+/**
+ * Registers a return route for a message.
+ *
+ * This function must be called after wimp_send_message so that a
+ * valid value is present in the my_ref field.
+ *
+ * \param message the message to register a route back for
+ * \param message_code the message action code to route
+ * \param callback the code to call for a matched action
+ * \return true on success, false on memory exhaustion
+ */
+bool ro_message_register_handler(wimp_message *message,
+ unsigned int message_code,
+ void (*callback)(wimp_message *message))
+{
+ struct active_message *add;
+
+ assert(message);
+ assert(callback);
+
+ add = ro_message_add(message_code, callback);
+ if (add)
+ add->id = message->my_ref;
+ return (add != NULL);
+}
+
+
+/**
+ * Registers a route for a message code.
+ *
+ * \param message_code the message action code to route
+ * \param callback the code to call for a matched action
+ * \return true on success, false on memory exhaustion
+ */
+bool ro_message_register_route(unsigned int message_code,
+ void (*callback)(wimp_message *message))
+{
+ assert(callback);
+
+ return (ro_message_add(message_code, callback) != NULL);
+}
+
+struct active_message *ro_message_add(unsigned int message_code,
+ void (*callback)(wimp_message *message))
+{
+ struct active_message *add;
+
+ assert(callback);
+
+ add = (struct active_message *)malloc(sizeof(*add));
+ if (!add)
+ return NULL;
+ add->message_code = message_code;
+ add->id = 0;
+ add->callback = callback;
+ add->next = current_messages;
+ add->previous = NULL;
+ current_messages = add;
+ return add;
+}
+
+
+/**
+ * Attempts to route a message.
+ *
+ * \param event wimp event
+ * \param message the message to attempt to route
+ * \return true if message was routed, false otherwise
+ */
+bool ro_message_handle_message(wimp_event_no event, wimp_message *message)
+{
+ struct active_message *test;
+
+ assert(message);
+
+ if (event == wimp_USER_MESSAGE_ACKNOWLEDGE) {
+ /* handle message acknowledgement */
+ bool handled = false;
+ int ref = message->my_ref;
+
+ if (ref == 0)
+ return false;
+
+ /* handle the message */
+ for (test = current_messages; test; test = test->next) {
+ if ((ref == test->id) &&
+ (message->action == test->message_code)) {
+ handled = true;
+ if (test->callback)
+ test->callback(message);
+ break;
+ }
+ }
+
+ /* remove all handlers for this id */
+ ro_message_free(ref);
+ return handled;
+ } else {
+ /* handle simple routing */
+ for (test = current_messages; test; test = test->next) {
+ if ((test->id == 0) &&
+ (message->action == test->message_code)) {
+ test->callback(message);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+void ro_message_free(int ref)
+{
+ struct active_message *test;
+ struct active_message *next = current_messages;
+
+ while ((test = next)) {
+ next = test->next;
+ if (ref == test->id) {
+ if (test->previous)
+ test->previous->next = test->next;
+ if (test->next)
+ test->next->previous = test->previous;
+ if (current_messages == test)
+ current_messages = test->next;
+ free(test);
+ }
+ }
+}
diff --git a/frontends/riscos/message.h b/frontends/riscos/message.h
new file mode 100644
index 000000000..8c1a515f2
--- /dev/null
+++ b/frontends/riscos/message.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2006 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Automated RISC OS message routing (interface).
+ */
+
+
+#ifndef _NETSURF_RISCOS_MESSAGE_H_
+#define _NETSURF_RISCOS_MESSAGE_H_
+
+#include <stdbool.h>
+#include "oslib/wimp.h"
+
+bool ro_message_send_message(wimp_event_no event, wimp_message *message,
+ wimp_t task, void (*callback)(wimp_message *message));
+bool ro_message_send_message_to_window(wimp_event_no event, wimp_message *message,
+ wimp_w to_w, wimp_i to_i, void (*callback)(wimp_message *message),
+ wimp_t *to_t);
+bool ro_message_register_handler(wimp_message *message,
+ unsigned int message_code,
+ void (*callback)(wimp_message *message));
+bool ro_message_register_route(unsigned int message_code,
+ void (*callback)(wimp_message *message));
+bool ro_message_handle_message(wimp_event_no event, wimp_message *message);
+
+#endif
diff --git a/frontends/riscos/mouse.c b/frontends/riscos/mouse.c
new file mode 100644
index 000000000..a0cc0e7ce
--- /dev/null
+++ b/frontends/riscos/mouse.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2013 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Mouse dragging and tracking support implementation.
+ *
+ * Two different functions are provided:-
+ *
+ * 1. Wimp_DragBox support, allowing clients to start a drag and specify
+ * callbacks to be used
+ *
+ * - on Null Polls while the drag is active,
+ * - when the drag terminates with Event_DragEnd, and
+ * - when the drag terminates with Escape being pressed.
+ *
+ * 2. Mouse tracking support, allowing clients to track the mouse while it
+ * remains in the current window and specify callbacks to be used
+ *
+ * - on Null Polls while the pointer is in the window, and
+ * - when the pointer leaves the window.
+ */
+
+#include <assert.h>
+#include <oslib/wimp.h>
+
+#include "utils/log.h"
+
+#include "riscos/mouse.h"
+#include "riscos/gui.h"
+
+/* Data for the wimp drag handler. */
+
+static void (*ro_mouse_drag_end_callback)(wimp_dragged *dragged, void *data)
+ = NULL;
+static void (*ro_mouse_drag_track_callback)(wimp_pointer *pointer, void *data)
+ = NULL;
+static void (*ro_mouse_drag_cancel_callback)(void *data) = NULL;
+static void *ro_mouse_drag_data = NULL;
+
+static bool ro_mouse_ignore_leaving_event = false;
+
+/* Data for the wimp poll handler. */
+
+static void (*ro_mouse_poll_end_callback)(wimp_leaving *leaving, void *data)
+ = NULL;
+static void (*ro_mouse_poll_track_callback)(wimp_pointer *pointer, void *data)
+ = NULL;
+static void *ro_mouse_poll_data = NULL;
+
+
+/**
+ * Process Null polls for any drags and mouse trackers that are currently
+ * active.
+ */
+
+void ro_mouse_poll(void)
+{
+ wimp_pointer pointer;
+ os_error *error;
+
+ /* If no trackers are active, just exit. */
+
+ if (ro_mouse_drag_track_callback == NULL &&
+ ro_mouse_poll_track_callback == NULL)
+ return;
+
+ error = xwimp_get_pointer_info(&pointer);
+ if (error) {
+ LOG("xwimp_get_pointer_info: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* Process the drag tracker, if one is active. */
+
+ if (ro_mouse_drag_track_callback != NULL)
+ ro_mouse_drag_track_callback(&pointer, ro_mouse_drag_data);
+
+ /* Process the window tracker, if one is active. */
+
+ if (ro_mouse_poll_track_callback != NULL)
+ ro_mouse_poll_track_callback(&pointer, ro_mouse_poll_data);
+}
+
+
+/**
+ * Start a drag, providing a function to be called when the Wimp_DragEnd event
+ * is received and optionally a tracking function to be called on null polls
+ * in between times.
+ *
+ * \param *drag_end Callback for when the drag terminates, or NULL for none.
+ * \param *drag_track Callback for mouse tracking during the drag, or NULL for
+ * none.
+ * \param *drag_cancel Callback for cancelling the drag, or NULL if the drag
+ * can't be cancelled.
+ * \param *data Data to be passed to the callback functions, or NULL.
+ */
+
+void ro_mouse_drag_start(void (*drag_end)(wimp_dragged *dragged, void *data),
+ void (*drag_track)(wimp_pointer *pointer, void *data),
+ void (*drag_cancel)(void *data), void *data)
+{
+ /* A drag should never be started when one is already in progress. */
+
+ assert(ro_mouse_drag_end_callback == NULL &&
+ ro_mouse_drag_track_callback == NULL &&
+ ro_mouse_drag_cancel_callback == NULL &&
+ ro_mouse_drag_data == NULL);
+
+ ro_mouse_drag_end_callback = drag_end;
+ ro_mouse_drag_track_callback = drag_track;
+ ro_mouse_drag_cancel_callback = drag_cancel;
+ ro_mouse_drag_data = data;
+
+ /* The Wimp sends a PointerLeaving event when Wimp_DragBox is called,
+ * so we mask out the next event that will come our way.
+ */
+
+ ro_mouse_ignore_leaving_event = true;
+}
+
+
+/**
+ * Process Wimp_DragEnd events by terminating an active drag track and passing
+ * the details on to any registered event handler.
+ *
+ * \param *dragged The Wimp_DragEnd data block.
+ */
+
+void ro_mouse_drag_end(wimp_dragged *dragged)
+{
+ if (ro_mouse_drag_end_callback != NULL)
+ ro_mouse_drag_end_callback(dragged, ro_mouse_drag_data);
+ else
+ ro_warn_user("WimpError", "No callback");
+
+ /* Wimp_DragEnd is a one-shot event, so clear the data ready for
+ * another claimant.
+ */
+
+ ro_mouse_drag_end_callback = NULL;
+ ro_mouse_drag_track_callback = NULL;
+ ro_mouse_drag_cancel_callback = NULL;
+ ro_mouse_drag_data = NULL;
+}
+
+
+/**
+ * Start tracking the mouse in a window, providing a function to be called on
+ * null polls and optionally one to be called when it leaves the window.
+ *
+ * \param *poll_end Callback for when the pointer leaves the window, or
+ * NULL for none. Claimants can receive *leaving==NULL if
+ * a new tracker is started before a PointerLeaving event
+ * is received.
+ * \param *poll_track Callback for mouse tracking while the pointer remains
+ * in the window, or NULL for none.
+ * \param *data Data to be passed to the callback functions, or NULL.
+ */
+
+void ro_mouse_track_start(void (*poll_end)(wimp_leaving *leaving, void *data),
+ void (*poll_track)(wimp_pointer *pointer, void *data),
+ void *data)
+{
+ /* It should never be possible for the mouse to be in two windows
+ * at the same time! However, some third-party extensions to RISC OS
+ * appear to make this possible (MouseAxess being one), so in the
+ * event that there's still a claimant we tidy them up first and then
+ * log the fact in case there are any unexpected consequences.
+ *
+ * NB: The Poll End callback will get called with *leaving == NULL in
+ * this situation, as there's no PointerLeaving event to pass on.
+ */
+
+ if (ro_mouse_poll_end_callback != NULL ||
+ ro_mouse_poll_track_callback != NULL ||
+ ro_mouse_poll_data != NULL) {
+ if (ro_mouse_poll_end_callback != NULL &&
+ ro_mouse_ignore_leaving_event == false)
+ ro_mouse_poll_end_callback(NULL, ro_mouse_poll_data);
+
+ LOG("Unexpected mouse track termination.");
+
+ ro_mouse_ignore_leaving_event = false;
+ ro_mouse_poll_end_callback = NULL;
+ ro_mouse_poll_track_callback = NULL;
+ ro_mouse_poll_data = NULL;
+ }
+
+ /* Now record details of the new claimant. */
+
+ ro_mouse_poll_end_callback = poll_end;
+ ro_mouse_poll_track_callback = poll_track;
+ ro_mouse_poll_data = data;
+}
+
+
+/**
+ * Process Wimp_PointerLeaving events by terminating an active mouse track and
+ * passing the details on to any registered event handler.
+ *
+ * If the ignore mask is set, we don't pass the event on to the client as it
+ * is assumed that it's a result of starting a Wimp_DragBox operation.
+ *
+ * \param *leaving The Wimp_PointerLeaving data block.
+ */
+
+void ro_mouse_pointer_leaving_window(wimp_leaving *leaving)
+{
+ if (ro_mouse_poll_end_callback != NULL &&
+ ro_mouse_ignore_leaving_event == false)
+ ro_mouse_poll_end_callback(leaving, ro_mouse_poll_data);
+
+ ro_mouse_ignore_leaving_event = false;
+
+ /* Poll tracking is a one-shot event, so clear the data ready for
+ * another claimant.
+ */
+
+ ro_mouse_poll_end_callback = NULL;
+ ro_mouse_poll_track_callback = NULL;
+ ro_mouse_poll_data = NULL;
+}
+
+
+/**
+ * Kill any tracking events if the data pointers match the supplied pointer.
+ *
+ * \param *data The data of the client to be killed.
+ */
+
+void ro_mouse_kill(void *data)
+{
+ if (data == ro_mouse_drag_data) {
+ ro_mouse_drag_end_callback = NULL;
+ ro_mouse_drag_track_callback = NULL;
+ ro_mouse_drag_cancel_callback = NULL;
+ ro_mouse_drag_data = NULL;
+ }
+
+ if (data == ro_mouse_poll_data) {
+ ro_mouse_poll_end_callback = NULL;
+ ro_mouse_poll_track_callback = NULL;
+ ro_mouse_poll_data = NULL;
+ }
+}
+
+
+/**
+ * Return the desired polling interval to allow the mouse tracking to be
+ * carried out.
+ *
+ * \return Desired poll interval (0 for none required).
+ */
+
+os_t ro_mouse_poll_interval(void)
+{
+ if (ro_mouse_drag_track_callback != NULL)
+ return 4;
+
+ if (ro_mouse_poll_track_callback != NULL)
+ return 10;
+
+ return 0;
+
+}
+
diff --git a/frontends/riscos/mouse.h b/frontends/riscos/mouse.h
new file mode 100644
index 000000000..6bc5c13e1
--- /dev/null
+++ b/frontends/riscos/mouse.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2013 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+
+/** \file
+ * Mouse dragging and tracking support interface for RISC OS.
+ */
+
+#ifndef _NETSURF_RISCOS_MOUSE_H_
+#define _NETSURF_RISCOS_MOUSE_H_
+
+
+/**
+ * Process Null polls for any drags and mouse trackers that are currently
+ * active.
+ */
+
+void ro_mouse_poll(void);
+
+
+/**
+ * Start a drag, providing a function to be called when the Wimp_DragEnd event
+ * is received and optionally a tracking function to be called on null polls
+ * in between times.
+ *
+ * \param *drag_end Callback for when the drag terminates, or NULL for none.
+ * \param *drag_track Callback for mouse tracking during the drag, or NULL for
+ * none.
+ * \param *drag_cancel Callback for cancelling the drag, or NULL if the drag
+ * can't be cancelled.
+ * \param *data Data to be passed to the callback functions, or NULL.
+ */
+
+void ro_mouse_drag_start(void (*drag_end)(wimp_dragged *dragged, void *data),
+ void (*drag_track)(wimp_pointer *pointer, void *data),
+ void (*drag_cancel)(void *data), void *data);
+
+
+/**
+ * Process Wimp_DragEnd events by passing the details on to any registered
+ * event handler.
+ *
+ * \param *dragged The Wimp_DragEnd data block.
+ */
+
+void ro_mouse_drag_end(wimp_dragged *dragged);
+
+
+/**
+ * Start tracking the mouse in a window, providing a function to be called on
+ * null polls and optionally one to be called when it leaves the window.
+ *
+ * \param *poll_end Callback for when the pointer leaves the window, or
+ * NULL for none. Claimants can receive *leaving==NULL if
+ * a new tracker is started before a PointerLeaving event
+ * is received.
+ * \param *poll_track Callback for mouse tracking while the pointer remains
+ * in the window, or NULL for none.
+ * \param *data Data to be passed to the callback functions, or NULL.
+ */
+
+void ro_mouse_track_start(void (*poll_end)(wimp_leaving *leaving, void *data),
+ void (*poll_track)(wimp_pointer *pointer, void *data),
+ void *data);
+
+/**
+ * Process Wimp_PointerLeaving events by terminating an active mouse track and
+ * passing the details on to any registered event handler.
+ *
+ * \param *leaving The Wimp_PointerLeaving data block.
+ */
+
+void ro_mouse_pointer_leaving_window(wimp_leaving *leaving);
+
+
+/**
+ * Kill any tracking events if the data pointers match the supplied pointer.
+ *
+ * \param *data The data of the client to be killed.
+ */
+
+void ro_mouse_kill(void *data);
+
+
+/**
+ * Return the desired polling interval to allow the mouse tracking to be
+ * carried out.
+ *
+ * \return Desired poll interval (0 for none required).
+ */
+
+os_t ro_mouse_poll_interval(void);
+
+#endif
+
diff --git a/frontends/riscos/options.h b/frontends/riscos/options.h
new file mode 100644
index 000000000..cb2b78bd8
--- /dev/null
+++ b/frontends/riscos/options.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * RISC OS specific options.
+ */
+
+#ifndef _NETSURF_RISCOS_OPTIONS_H_
+#define _NETSURF_RISCOS_OPTIONS_H_
+
+#include "riscos/tinct.h"
+
+/* setup longer default reflow time */
+#define DEFAULT_REFLOW_PERIOD 100 /* time in cs */
+
+#define CHOICES_PREFIX "<Choices$Write>.WWW.NetSurf."
+
+#endif
+
+NSOPTION_STRING(theme, "Aletheia")
+NSOPTION_STRING(language, NULL)
+NSOPTION_INTEGER(plot_fg_quality, tinct_ERROR_DIFFUSE)
+NSOPTION_INTEGER(plot_bg_quality, tinct_DITHER)
+NSOPTION_BOOL(history_tooltip, true)
+NSOPTION_BOOL(toolbar_show_buttons, true)
+NSOPTION_BOOL(toolbar_show_address, true)
+NSOPTION_BOOL(toolbar_show_throbber, true)
+NSOPTION_STRING(toolbar_browser, "0123|58|9")
+NSOPTION_STRING(toolbar_hotlist, "40|12|3")
+NSOPTION_STRING(toolbar_history, "0|12|3")
+NSOPTION_STRING(toolbar_cookies, "0|12")
+NSOPTION_BOOL(window_stagger, true)
+NSOPTION_BOOL(window_size_clone, true)
+NSOPTION_BOOL(buffer_animations, true)
+NSOPTION_BOOL(buffer_everything, true)
+NSOPTION_BOOL(open_browser_at_startup, false)
+NSOPTION_BOOL(no_plugins, false)
+NSOPTION_BOOL(block_popups, false)
+NSOPTION_BOOL(strip_extensions, false)
+NSOPTION_BOOL(confirm_overwrite, true)
+NSOPTION_BOOL(confirm_hotlist_remove, true)
+NSOPTION_STRING(url_path, "NetSurf:URL")
+NSOPTION_STRING(url_save, CHOICES_PREFIX "URL")
+NSOPTION_STRING(hotlist_path, "NetSurf:Hotlist")
+NSOPTION_STRING(hotlist_save, CHOICES_PREFIX "Hotlist")
+NSOPTION_STRING(recent_path, "NetSurf:Recent")
+NSOPTION_STRING(recent_save, CHOICES_PREFIX "Recent")
+NSOPTION_STRING(theme_path, "NetSurf:Themes")
+NSOPTION_STRING(theme_save, CHOICES_PREFIX "Themes")
+NSOPTION_BOOL(thumbnail_iconise, true)
+NSOPTION_BOOL(interactive_help, true)
+NSOPTION_BOOL(external_hotlists, false)
+NSOPTION_STRING(external_hotlist_app, NULL)
diff --git a/frontends/riscos/oslib_pre7.h b/frontends/riscos/oslib_pre7.h
new file mode 100644
index 000000000..a99bd0349
--- /dev/null
+++ b/frontends/riscos/oslib_pre7.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2008 John Tytgat <joty@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Backward compatible defines to make NetSurf buildable with pre-OSLib 7
+ * releases.
+ */
+
+#ifndef _NETSURF_RISCOS_OSLIB_PRE7_H_
+#define _NETSURF_RISCOS_OSLIB_PRE7_H_
+
+#include "oslib/colourtrans.h"
+
+/**
+ * After OSLib 6.90, there was a rename of colourtrans defines in order
+ * to avoid namespace clashes:
+ * svn diff -c 238 https://ro-oslib.svn.sourceforge.net/svnroot/ro-oslib/trunk/\!OSLib/Source/Core/oslib/ColourTrans.swi
+ * Foresee some backwards compatibility until we've switched to OSLib 7.
+*/
+#ifndef colourtrans_SET_BG_GCOL
+# define colourtrans_SET_BG_GCOL colourtrans_SET_BG
+#endif
+#ifndef colourtrans_USE_ECFS_GCOL
+# define colourtrans_USE_ECFS_GCOL colourtrans_USE_ECFS
+#endif
+
+#endif
diff --git a/frontends/riscos/palettes.c b/frontends/riscos/palettes.c
new file mode 100644
index 000000000..631d8802a
--- /dev/null
+++ b/frontends/riscos/palettes.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2006 Adrian Lees <adrianl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Palette definitions for sprites
+ */
+
+#include "riscos/palettes.h"
+
+
+const os_colour default_palette1[] =
+{
+ 0xffffff00, 0xffffff00,
+ 0x00000000, 0x00000000
+};
+
+
+const os_colour default_palette2[] =
+{
+ 0xffffff00, 0xffffff00,
+ 0xbbbbbb00, 0xbbbbbb00,
+ 0x77777700, 0x77777700,
+ 0x00000000, 0x00000000
+};
+
+
+const os_colour wimp_palette[] =
+{
+ 0xffffff00, 0xffffff00,
+ 0xdddddd00, 0xdddddd00,
+ 0xbbbbbb00, 0xbbbbbb00,
+ 0x99999900, 0x99999900,
+ 0x77777700, 0x77777700,
+ 0x55555500, 0x55555500,
+ 0x33333300, 0x33333300,
+ 0x00000000, 0x00000000,
+ 0x99440000, 0x99440000,
+ 0x00eeee00, 0x00eeee00,
+ 0x00cc0000, 0x00cc0000,
+ 0x0000dd00, 0x0000dd00,
+ 0xbbeeee00, 0xbbeeee00,
+ 0x00885500, 0x00885500,
+ 0x00bbff00, 0x00bbff00,
+ 0xffbb0000, 0xffbb0000
+};
+
+
+const os_colour default_palette8[] =
+{
+ 0x00000010, 0x00000010,
+ 0x11111110, 0x11111110,
+ 0x22222210, 0x22222210,
+ 0x33333310, 0x33333310,
+ 0x00004410, 0x00004410,
+ 0x11115510, 0x11115510,
+ 0x22226610, 0x22226610,
+ 0x33337710, 0x33337710,
+ 0x44000010, 0x44000010,
+ 0x55111110, 0x55111110,
+ 0x66222210, 0x66222210,
+ 0x77333310, 0x77333310,
+ 0x44004410, 0x44004410,
+ 0x55115510, 0x55115510,
+ 0x66226610, 0x66226610,
+ 0x77337710, 0x77337710,
+ 0x00008810, 0x00008810,
+ 0x11119910, 0x11119910,
+ 0x2222AA10, 0x2222AA10,
+ 0x3333BB10, 0x3333BB10,
+ 0x0000CC10, 0x0000CC10,
+ 0x1111DD10, 0x1111DD10,
+ 0x2222EE10, 0x2222EE10,
+ 0x3333FF10, 0x3333FF10,
+ 0x44008810, 0x44008810,
+ 0x55119910, 0x55119910,
+ 0x6622AA10, 0x6622AA10,
+ 0x7733BB10, 0x7733BB10,
+ 0x4400CC10, 0x4400CC10,
+ 0x5511DD10, 0x5511DD10,
+ 0x6622EE10, 0x6622EE10,
+ 0x7733FF10, 0x7733FF10,
+ 0x00440010, 0x00440010,
+ 0x11551110, 0x11551110,
+ 0x22662210, 0x22662210,
+ 0x33773310, 0x33773310,
+ 0x00444410, 0x00444410,
+ 0x11555510, 0x11555510,
+ 0x22666610, 0x22666610,
+ 0x33777710, 0x33777710,
+ 0x44440010, 0x44440010,
+ 0x55551110, 0x55551110,
+ 0x66662210, 0x66662210,
+ 0x77773310, 0x77773310,
+ 0x44444410, 0x44444410,
+ 0x55555510, 0x55555510,
+ 0x66666610, 0x66666610,
+ 0x77777710, 0x77777710,
+ 0x00448810, 0x00448810,
+ 0x11559910, 0x11559910,
+ 0x2266AA10, 0x2266AA10,
+ 0x3377BB10, 0x3377BB10,
+ 0x0044CC10, 0x0044CC10,
+ 0x1155DD10, 0x1155DD10,
+ 0x2266EE10, 0x2266EE10,
+ 0x3377FF10, 0x3377FF10,
+ 0x44448810, 0x44448810,
+ 0x55559910, 0x55559910,
+ 0x6666AA10, 0x6666AA10,
+ 0x7777BB10, 0x7777BB10,
+ 0x4444CC10, 0x4444CC10,
+ 0x5555DD10, 0x5555DD10,
+ 0x6666EE10, 0x6666EE10,
+ 0x7777FF10, 0x7777FF10,
+ 0x00880010, 0x00880010,
+ 0x11991110, 0x11991110,
+ 0x22AA2210, 0x22AA2210,
+ 0x33BB3310, 0x33BB3310,
+ 0x00884410, 0x00884410,
+ 0x11995510, 0x11995510,
+ 0x22AA6610, 0x22AA6610,
+ 0x33BB7710, 0x33BB7710,
+ 0x44880010, 0x44880010,
+ 0x55991110, 0x55991110,
+ 0x66AA2210, 0x66AA2210,
+ 0x77BB3310, 0x77BB3310,
+ 0x44884410, 0x44884410,
+ 0x55995510, 0x55995510,
+ 0x66AA6610, 0x66AA6610,
+ 0x77BB7710, 0x77BB7710,
+ 0x00888810, 0x00888810,
+ 0x11999910, 0x11999910,
+ 0x22AAAA10, 0x22AAAA10,
+ 0x33BBBB10, 0x33BBBB10,
+ 0x0088CC10, 0x0088CC10,
+ 0x1199DD10, 0x1199DD10,
+ 0x22AAEE10, 0x22AAEE10,
+ 0x33BBFF10, 0x33BBFF10,
+ 0x44888810, 0x44888810,
+ 0x55999910, 0x55999910,
+ 0x66AAAA10, 0x66AAAA10,
+ 0x77BBBB10, 0x77BBBB10,
+ 0x4488CC10, 0x4488CC10,
+ 0x5599DD10, 0x5599DD10,
+ 0x66AAEE10, 0x66AAEE10,
+ 0x77BBFF10, 0x77BBFF10,
+ 0x00CC0010, 0x00CC0010,
+ 0x11DD1110, 0x11DD1110,
+ 0x22EE2210, 0x22EE2210,
+ 0x33FF3310, 0x33FF3310,
+ 0x00CC4410, 0x00CC4410,
+ 0x11DD5510, 0x11DD5510,
+ 0x22EE6610, 0x22EE6610,
+ 0x33FF7710, 0x33FF7710,
+ 0x44CC0010, 0x44CC0010,
+ 0x55DD1110, 0x55DD1110,
+ 0x66EE2210, 0x66EE2210,
+ 0x77FF3310, 0x77FF3310,
+ 0x44CC4410, 0x44CC4410,
+ 0x55DD5510, 0x55DD5510,
+ 0x66EE6610, 0x66EE6610,
+ 0x77FF7710, 0x77FF7710,
+ 0x00CC8810, 0x00CC8810,
+ 0x11DD9910, 0x11DD9910,
+ 0x22EEAA10, 0x22EEAA10,
+ 0x33FFBB10, 0x33FFBB10,
+ 0x00CCCC10, 0x00CCCC10,
+ 0x11DDDD10, 0x11DDDD10,
+ 0x22EEEE10, 0x22EEEE10,
+ 0x33FFFF10, 0x33FFFF10,
+ 0x44CC8810, 0x44CC8810,
+ 0x55DD9910, 0x55DD9910,
+ 0x66EEAA10, 0x66EEAA10,
+ 0x77FFBB10, 0x77FFBB10,
+ 0x44CCCC10, 0x44CCCC10,
+ 0x55DDDD10, 0x55DDDD10,
+ 0x66EEEE10, 0x66EEEE10,
+ 0x77FFFF10, 0x77FFFF10,
+ 0x88000010, 0x88000010,
+ 0x99111110, 0x99111110,
+ 0xAA222210, 0xAA222210,
+ 0xBB333310, 0xBB333310,
+ 0x88004410, 0x88004410,
+ 0x99115510, 0x99115510,
+ 0xAA226610, 0xAA226610,
+ 0xBB337710, 0xBB337710,
+ 0xCC000010, 0xCC000010,
+ 0xDD111110, 0xDD111110,
+ 0xEE222210, 0xEE222210,
+ 0xFF333310, 0xFF333310,
+ 0xCC004410, 0xCC004410,
+ 0xDD115510, 0xDD115510,
+ 0xEE226610, 0xEE226610,
+ 0xFF337710, 0xFF337710,
+ 0x88008810, 0x88008810,
+ 0x99119910, 0x99119910,
+ 0xAA22AA10, 0xAA22AA10,
+ 0xBB33BB10, 0xBB33BB10,
+ 0x8800CC10, 0x8800CC10,
+ 0x9911DD10, 0x9911DD10,
+ 0xAA22EE10, 0xAA22EE10,
+ 0xBB33FF10, 0xBB33FF10,
+ 0xCC008810, 0xCC008810,
+ 0xDD119910, 0xDD119910,
+ 0xEE22AA10, 0xEE22AA10,
+ 0xFF33BB10, 0xFF33BB10,
+ 0xCC00CC10, 0xCC00CC10,
+ 0xDD11DD10, 0xDD11DD10,
+ 0xEE22EE10, 0xEE22EE10,
+ 0xFF33FF10, 0xFF33FF10,
+ 0x88440010, 0x88440010,
+ 0x99551110, 0x99551110,
+ 0xAA662210, 0xAA662210,
+ 0xBB773310, 0xBB773310,
+ 0x88444410, 0x88444410,
+ 0x99555510, 0x99555510,
+ 0xAA666610, 0xAA666610,
+ 0xBB777710, 0xBB777710,
+ 0xCC440010, 0xCC440010,
+ 0xDD551110, 0xDD551110,
+ 0xEE662210, 0xEE662210,
+ 0xFF773310, 0xFF773310,
+ 0xCC444410, 0xCC444410,
+ 0xDD555510, 0xDD555510,
+ 0xEE666610, 0xEE666610,
+ 0xFF777710, 0xFF777710,
+ 0x88448810, 0x88448810,
+ 0x99559910, 0x99559910,
+ 0xAA66AA10, 0xAA66AA10,
+ 0xBB77BB10, 0xBB77BB10,
+ 0x8844CC10, 0x8844CC10,
+ 0x9955DD10, 0x9955DD10,
+ 0xAA66EE10, 0xAA66EE10,
+ 0xBB77FF10, 0xBB77FF10,
+ 0xCC448810, 0xCC448810,
+ 0xDD559910, 0xDD559910,
+ 0xEE66AA10, 0xEE66AA10,
+ 0xFF77BB10, 0xFF77BB10,
+ 0xCC44CC10, 0xCC44CC10,
+ 0xDD55DD10, 0xDD55DD10,
+ 0xEE66EE10, 0xEE66EE10,
+ 0xFF77FF10, 0xFF77FF10,
+ 0x88880010, 0x88880010,
+ 0x99991110, 0x99991110,
+ 0xAAAA2210, 0xAAAA2210,
+ 0xBBBB3310, 0xBBBB3310,
+ 0x88884410, 0x88884410,
+ 0x99995510, 0x99995510,
+ 0xAAAA6610, 0xAAAA6610,
+ 0xBBBB7710, 0xBBBB7710,
+ 0xCC880010, 0xCC880010,
+ 0xDD991110, 0xDD991110,
+ 0xEEAA2210, 0xEEAA2210,
+ 0xFFBB3310, 0xFFBB3310,
+ 0xCC884410, 0xCC884410,
+ 0xDD995510, 0xDD995510,
+ 0xEEAA6610, 0xEEAA6610,
+ 0xFFBB7710, 0xFFBB7710,
+ 0x88888810, 0x88888810,
+ 0x99999910, 0x99999910,
+ 0xAAAAAA10, 0xAAAAAA10,
+ 0xBBBBBB10, 0xBBBBBB10,
+ 0x8888CC10, 0x8888CC10,
+ 0x9999DD10, 0x9999DD10,
+ 0xAAAAEE10, 0xAAAAEE10,
+ 0xBBBBFF10, 0xBBBBFF10,
+ 0xCC888810, 0xCC888810,
+ 0xDD999910, 0xDD999910,
+ 0xEEAAAA10, 0xEEAAAA10,
+ 0xFFBBBB10, 0xFFBBBB10,
+ 0xCC88CC10, 0xCC88CC10,
+ 0xDD99DD10, 0xDD99DD10,
+ 0xEEAAEE10, 0xEEAAEE10,
+ 0xFFBBFF10, 0xFFBBFF10,
+ 0x88CC0010, 0x88CC0010,
+ 0x99DD1110, 0x99DD1110,
+ 0xAAEE2210, 0xAAEE2210,
+ 0xBBFF3310, 0xBBFF3310,
+ 0x88CC4410, 0x88CC4410,
+ 0x99DD5510, 0x99DD5510,
+ 0xAAEE6610, 0xAAEE6610,
+ 0xBBFF7710, 0xBBFF7710,
+ 0xCCCC0010, 0xCCCC0010,
+ 0xDDDD1110, 0xDDDD1110,
+ 0xEEEE2210, 0xEEEE2210,
+ 0xFFFF3310, 0xFFFF3310,
+ 0xCCCC4410, 0xCCCC4410,
+ 0xDDDD5510, 0xDDDD5510,
+ 0xEEEE6610, 0xEEEE6610,
+ 0xFFFF7710, 0xFFFF7710,
+ 0x88CC8810, 0x88CC8810,
+ 0x99DD9910, 0x99DD9910,
+ 0xAAEEAA10, 0xAAEEAA10,
+ 0xBBFFBB10, 0xBBFFBB10,
+ 0x88CCCC10, 0x88CCCC10,
+ 0x99DDDD10, 0x99DDDD10,
+ 0xAAEEEE10, 0xAAEEEE10,
+ 0xBBFFFF10, 0xBBFFFF10,
+ 0xCCCC8810, 0xCCCC8810,
+ 0xDDDD9910, 0xDDDD9910,
+ 0xEEEEAA10, 0xEEEEAA10,
+ 0xFFFFBB10, 0xFFFFBB10,
+ 0xCCCCCC10, 0xCCCCCC10,
+ 0xDDDDDD10, 0xDDDDDD10,
+ 0xEEEEEE10, 0xEEEEEE10,
+ 0xFFFFFF10, 0xFFFFFF10
+};
diff --git a/frontends/riscos/palettes.h b/frontends/riscos/palettes.h
new file mode 100644
index 000000000..82fccb467
--- /dev/null
+++ b/frontends/riscos/palettes.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2006 Adrian Lees <adrianl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Palette definitions for sprites
+ */
+
+#ifndef __NETSURF_RISCOS_PALETTES_H_
+#define __NETSURF_RISCOS_PALETTES_H_
+#include "oslib/os.h"
+
+extern const os_colour default_palette1[];
+extern const os_colour default_palette2[];
+extern const os_colour wimp_palette[];
+extern const os_colour default_palette8[];
+extern const os_colour captured_palette8[];
+
+#endif
diff --git a/frontends/riscos/plotters.c b/frontends/riscos/plotters.c
new file mode 100644
index 000000000..38fd9d74a
--- /dev/null
+++ b/frontends/riscos/plotters.c
@@ -0,0 +1,525 @@
+/*
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Target independent plotting (RISC OS screen implementation).
+ */
+
+#include <stdbool.h>
+#include <math.h>
+#include "oslib/colourtrans.h"
+#include "oslib/draw.h"
+#include "oslib/os.h"
+
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "desktop/plotters.h"
+
+#include "riscos/bitmap.h"
+#include "riscos/image.h"
+#include "riscos/gui.h"
+#include "riscos/font.h"
+#include "riscos/oslib_pre7.h"
+
+static bool ro_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style);
+static bool ro_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *style);
+static bool ro_plot_draw_path(const draw_path * const path, int width,
+ colour c, bool dotted, bool dashed);
+static bool ro_plot_polygon(const int *p, unsigned int n, const plot_style_t *style);
+static bool ro_plot_path(const float *p, unsigned int n, colour fill, float width,
+ colour c, const float transform[6]);
+static bool ro_plot_clip(const struct rect *clip);
+static bool ro_plot_text(int x, int y, const char *text, size_t length,
+ const plot_font_style_t *fstyle);
+static bool ro_plot_disc(int x, int y, int radius, const plot_style_t *style);
+static bool ro_plot_arc(int x, int y, int radius, int angle1, int angle2,
+ const plot_style_t *style);
+static bool ro_plot_bitmap(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg,
+ bitmap_flags_t flags);
+
+
+struct plotter_table plot;
+
+const struct plotter_table ro_plotters = {
+ .rectangle = ro_plot_rectangle,
+ .line = ro_plot_line,
+ .polygon = ro_plot_polygon,
+ .clip = ro_plot_clip,
+ .text = ro_plot_text,
+ .disc = ro_plot_disc,
+ .arc = ro_plot_arc,
+ .bitmap = ro_plot_bitmap,
+ .path = ro_plot_path,
+ .option_knockout = true,
+};
+
+int ro_plot_origin_x = 0;
+int ro_plot_origin_y = 0;
+
+/** One version of the A9home OS is incapable of drawing patterned lines */
+bool ro_plot_patterned_lines = true;
+
+
+
+bool ro_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ if (style->fill_type != PLOT_OP_TYPE_NONE) {
+ os_error *error;
+ error = xcolourtrans_set_gcol(style->fill_colour << 8,
+ colourtrans_USE_ECFS_GCOL,
+ os_ACTION_OVERWRITE, 0, 0);
+ if (error) {
+ LOG("xcolourtrans_set_gcol: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+
+ error = xos_plot(os_MOVE_TO,
+ ro_plot_origin_x + x0 * 2,
+ ro_plot_origin_y - y0 * 2 - 1);
+ if (error) {
+ LOG("xos_plot: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+
+ error = xos_plot(os_PLOT_RECTANGLE | os_PLOT_TO,
+ ro_plot_origin_x + x1 * 2 - 1,
+ ro_plot_origin_y - y1 * 2);
+ if (error) {
+ LOG("xos_plot: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+ }
+
+ if (style->stroke_type != PLOT_OP_TYPE_NONE) {
+ bool dotted = false;
+ bool dashed = false;
+
+ const int path[] = { draw_MOVE_TO,
+ (ro_plot_origin_x + x0 * 2) * 256,
+ (ro_plot_origin_y - y0 * 2 - 1) * 256,
+ draw_LINE_TO,
+ (ro_plot_origin_x + (x1) * 2) * 256,
+ (ro_plot_origin_y - y0 * 2 - 1) * 256,
+ draw_LINE_TO,
+ (ro_plot_origin_x + (x1) * 2) * 256,
+ (ro_plot_origin_y - (y1) * 2 - 1) * 256,
+ draw_LINE_TO,
+ (ro_plot_origin_x + x0 * 2) * 256,
+ (ro_plot_origin_y - (y1) * 2 - 1) * 256,
+ draw_CLOSE_LINE,
+ (ro_plot_origin_x + x0 * 2) * 256,
+ (ro_plot_origin_y - y0 * 2 - 1) * 256,
+ draw_END_PATH };
+
+ if (style->stroke_type == PLOT_OP_TYPE_DOT)
+ dotted = true;
+
+ if (style->stroke_type == PLOT_OP_TYPE_DASH)
+ dashed = true;
+
+ ro_plot_draw_path((const draw_path *)path,
+ style->stroke_width,
+ style->stroke_colour,
+ dotted, dashed);
+ }
+
+ return true;
+}
+
+
+bool ro_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ if (style->stroke_type != PLOT_OP_TYPE_NONE) {
+ const int path[] = { draw_MOVE_TO,
+ (ro_plot_origin_x + x0 * 2) * 256,
+ (ro_plot_origin_y - y0 * 2 - 1) * 256,
+ draw_LINE_TO,
+ (ro_plot_origin_x + x1 * 2) * 256,
+ (ro_plot_origin_y - y1 * 2 - 1) * 256,
+ draw_END_PATH };
+ bool dotted = false;
+ bool dashed = false;
+
+ if (style->stroke_type == PLOT_OP_TYPE_DOT)
+ dotted = true;
+
+ if (style->stroke_type == PLOT_OP_TYPE_DASH)
+ dashed = true;
+
+ return ro_plot_draw_path((const draw_path *)path,
+ style->stroke_width,
+ style->stroke_colour,
+ dotted, dashed);
+ }
+ return true;
+}
+
+
+bool ro_plot_draw_path(const draw_path * const path, int width,
+ colour c, bool dotted, bool dashed)
+{
+ static const draw_line_style line_style = { draw_JOIN_MITRED,
+ draw_CAP_BUTT, draw_CAP_BUTT, 0, 0x7fffffff,
+ 0, 0, 0, 0 };
+ draw_dash_pattern dash = { 0, 1, { 512 } };
+ const draw_dash_pattern *dash_pattern = 0;
+ os_error *error;
+
+ if (width < 1)
+ width = 1;
+
+ if (ro_plot_patterned_lines) {
+ if (dotted) {
+ dash.elements[0] = 512 * width;
+ dash_pattern = &dash;
+ } else if (dashed) {
+ dash.elements[0] = 1536 * width;
+ dash_pattern = &dash;
+ }
+ }
+
+ error = xcolourtrans_set_gcol(c << 8, 0, os_ACTION_OVERWRITE, 0, 0);
+ if (error) {
+ LOG("xcolourtrans_set_gcol: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+
+ error = xdraw_stroke(path, 0, 0, 0, width * 2 * 256,
+ &line_style, dash_pattern);
+ if (error) {
+ LOG("xdraw_stroke: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+
+ return true;
+}
+
+
+bool ro_plot_polygon(const int *p, unsigned int n, const plot_style_t *style)
+{
+ int path[n * 3 + 2];
+ unsigned int i;
+ os_error *error;
+
+ for (i = 0; i != n; i++) {
+ path[i * 3 + 0] = draw_LINE_TO;
+ path[i * 3 + 1] = (ro_plot_origin_x + p[i * 2 + 0] * 2) * 256;
+ path[i * 3 + 2] = (ro_plot_origin_y - p[i * 2 + 1] * 2) * 256;
+ }
+ path[0] = draw_MOVE_TO;
+ path[n * 3] = draw_END_PATH;
+ path[n * 3 + 1] = 0;
+
+ error = xcolourtrans_set_gcol(style->fill_colour << 8, 0, os_ACTION_OVERWRITE, 0, 0);
+ if (error) {
+ LOG("xcolourtrans_set_gcol: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+ error = xdraw_fill((draw_path *) path, 0, 0, 0);
+ if (error) {
+ LOG("xdraw_fill: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+
+ return true;
+}
+
+
+bool ro_plot_path(const float *p, unsigned int n, colour fill, float width,
+ colour c, const float transform[6])
+{
+ static const draw_line_style line_style = { draw_JOIN_MITRED,
+ draw_CAP_BUTT, draw_CAP_BUTT, 0, 0x7fffffff,
+ 0, 0, 0, 0 };
+ int *path = 0;
+ unsigned int i;
+ os_trfm trfm;
+ os_error *error;
+
+ if (n == 0)
+ return true;
+
+ if (p[0] != PLOTTER_PATH_MOVE) {
+ LOG("path doesn't start with a move");
+ goto error;
+ }
+
+ path = malloc(sizeof *path * (n + 10));
+ if (!path) {
+ LOG("out of memory");
+ goto error;
+ }
+
+ for (i = 0; i < n; ) {
+ if (p[i] == PLOTTER_PATH_MOVE) {
+ path[i] = draw_MOVE_TO;
+ path[i + 1] = p[i + 1] * 2 * 256;
+ path[i + 2] = -p[i + 2] * 2 * 256;
+ i += 3;
+ } else if (p[i] == PLOTTER_PATH_CLOSE) {
+ path[i] = draw_CLOSE_LINE;
+ i++;
+ } else if (p[i] == PLOTTER_PATH_LINE) {
+ path[i] = draw_LINE_TO;
+ path[i + 1] = p[i + 1] * 2 * 256;
+ path[i + 2] = -p[i + 2] * 2 * 256;
+ i += 3;
+ } else if (p[i] == PLOTTER_PATH_BEZIER) {
+ path[i] = draw_BEZIER_TO;
+ path[i + 1] = p[i + 1] * 2 * 256;
+ path[i + 2] = -p[i + 2] * 2 * 256;
+ path[i + 3] = p[i + 3] * 2 * 256;
+ path[i + 4] = -p[i + 4] * 2 * 256;
+ path[i + 5] = p[i + 5] * 2 * 256;
+ path[i + 6] = -p[i + 6] * 2 * 256;
+ i += 7;
+ } else {
+ LOG("bad path command %f", p[i]);
+ goto error;
+ }
+ }
+ path[i] = draw_END_PATH;
+ path[i + 1] = 0;
+
+ trfm.entries[0][0] = transform[0] * 0x10000;
+ trfm.entries[0][1] = transform[1] * 0x10000;
+ trfm.entries[1][0] = transform[2] * 0x10000;
+ trfm.entries[1][1] = transform[3] * 0x10000;
+ trfm.entries[2][0] = (ro_plot_origin_x + transform[4] * 2) * 256;
+ trfm.entries[2][1] = (ro_plot_origin_y - transform[5] * 2) * 256;
+
+ if (fill != NS_TRANSPARENT) {
+ error = xcolourtrans_set_gcol(fill << 8, 0,
+ os_ACTION_OVERWRITE, 0, 0);
+ if (error) {
+ LOG("xcolourtrans_set_gcol: 0x%x: %s", error->errnum, error->errmess);
+ goto error;
+ }
+
+ error = xdraw_fill((draw_path *) path, 0, &trfm, 0);
+ if (error) {
+ LOG("xdraw_stroke: 0x%x: %s", error->errnum, error->errmess);
+ goto error;
+ }
+ }
+
+ if (c != NS_TRANSPARENT) {
+ error = xcolourtrans_set_gcol(c << 8, 0,
+ os_ACTION_OVERWRITE, 0, 0);
+ if (error) {
+ LOG("xcolourtrans_set_gcol: 0x%x: %s", error->errnum, error->errmess);
+ goto error;
+ }
+
+ error = xdraw_stroke((draw_path *) path, 0, &trfm, 0,
+ width * 2 * 256, &line_style, 0);
+ if (error) {
+ LOG("xdraw_stroke: 0x%x: %s", error->errnum, error->errmess);
+ goto error;
+ }
+ }
+
+ free(path);
+ return true;
+
+error:
+ free(path);
+ return false;
+}
+
+
+
+
+bool ro_plot_clip(const struct rect *clip)
+{
+ os_error *error;
+ char buf[12];
+
+ int clip_x0 = ro_plot_origin_x + clip->x0 * 2;
+ int clip_y0 = ro_plot_origin_y - clip->y0 * 2 - 1;
+ int clip_x1 = ro_plot_origin_x + clip->x1 * 2 - 1;
+ int clip_y1 = ro_plot_origin_y - clip->y1 * 2;
+
+ if (clip_x1 < clip_x0 || clip_y0 < clip_y1) {
+ LOG("bad clip rectangle %i %i %i %i", clip_x0, clip_y0, clip_x1, clip_y1);
+ return false;
+ }
+
+ buf[0] = os_VDU_SET_GRAPHICS_WINDOW;
+ buf[1] = clip_x0;
+ buf[2] = clip_x0 >> 8;
+ buf[3] = clip_y1;
+ buf[4] = clip_y1 >> 8;
+ buf[5] = clip_x1;
+ buf[6] = clip_x1 >> 8;
+ buf[7] = clip_y0;
+ buf[8] = clip_y0 >> 8;
+
+ error = xos_writen(buf, 9);
+ if (error) {
+ LOG("xos_writen: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+
+ return true;
+}
+
+
+bool ro_plot_text(int x, int y, const char *text, size_t length,
+ const plot_font_style_t *fstyle)
+{
+ os_error *error;
+
+ error = xcolourtrans_set_font_colours(font_CURRENT,
+ fstyle->background << 8, fstyle->foreground << 8,
+ 14, 0, 0, 0);
+ if (error) {
+ LOG("xcolourtrans_set_font_colours: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+
+ return nsfont_paint(fstyle, text, length,
+ ro_plot_origin_x + x * 2,
+ ro_plot_origin_y - y * 2);
+}
+
+
+bool ro_plot_disc(int x, int y, int radius, const plot_style_t *style)
+{
+ os_error *error;
+ if (style->fill_type != PLOT_OP_TYPE_NONE) {
+ error = xcolourtrans_set_gcol(style->fill_colour << 8, 0,
+ os_ACTION_OVERWRITE, 0, 0);
+ if (error) {
+ LOG("xcolourtrans_set_gcol: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+ error = xos_plot(os_MOVE_TO,
+ ro_plot_origin_x + x * 2,
+ ro_plot_origin_y - y * 2);
+ if (error) {
+ LOG("xos_plot: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+ error = xos_plot(os_PLOT_CIRCLE | os_PLOT_BY, radius * 2, 0);
+ if (error) {
+ LOG("xos_plot: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+ }
+
+ if (style->stroke_type != PLOT_OP_TYPE_NONE) {
+
+ error = xcolourtrans_set_gcol(style->stroke_colour << 8, 0,
+ os_ACTION_OVERWRITE, 0, 0);
+ if (error) {
+ LOG("xcolourtrans_set_gcol: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+ error = xos_plot(os_MOVE_TO,
+ ro_plot_origin_x + x * 2,
+ ro_plot_origin_y - y * 2);
+ if (error) {
+ LOG("xos_plot: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+ error = xos_plot(os_PLOT_CIRCLE_OUTLINE | os_PLOT_BY,
+ radius * 2, 0);
+
+ if (error) {
+ LOG("xos_plot: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ro_plot_arc(int x, int y, int radius, int angle1, int angle2, const plot_style_t *style)
+{
+ os_error *error;
+ int sx, sy, ex, ey;
+ double t;
+
+ x = ro_plot_origin_x + x * 2;
+ y = ro_plot_origin_y - y * 2;
+ radius <<= 1;
+
+ error = xcolourtrans_set_gcol(style->fill_colour << 8, 0,
+ os_ACTION_OVERWRITE, 0, 0);
+
+ if (error) {
+ LOG("xcolourtrans_set_gcol: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+
+ t = ((double)angle1 * M_PI) / 180.0;
+ sx = (x + (int)(radius * cos(t)));
+ sy = (y + (int)(radius * sin(t)));
+
+ t = ((double)angle2 * M_PI) / 180.0;
+ ex = (x + (int)(radius * cos(t)));
+ ey = (y + (int)(radius * sin(t)));
+
+ error = xos_plot(os_MOVE_TO, x, y); /* move to centre */
+ if (error) {
+ LOG("xos_plot: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+
+ error = xos_plot(os_MOVE_TO, sx, sy); /* move to start */
+ if (error) {
+ LOG("xos_plot: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+
+ error = xos_plot(os_PLOT_ARC | os_PLOT_TO, ex, ey); /* arc to end */
+ if (error) {
+ LOG("xos_plot: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+
+ return true;
+}
+
+
+
+bool ro_plot_bitmap(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg,
+ bitmap_flags_t flags)
+{
+ const uint8_t *buffer;
+
+ buffer = riscos_bitmap_get_buffer(bitmap);
+ if (!buffer) {
+ LOG("bitmap_get_buffer failed");
+ return false;
+ }
+
+ return image_redraw(bitmap->sprite_area,
+ ro_plot_origin_x + x * 2,
+ ro_plot_origin_y - y * 2,
+ width, height,
+ bitmap->width,
+ bitmap->height,
+ bg,
+ flags & BITMAPF_REPEAT_X, flags & BITMAPF_REPEAT_Y,
+ flags & BITMAPF_REPEAT_X || flags & BITMAPF_REPEAT_Y,
+ riscos_bitmap_get_opaque(bitmap) ? IMAGE_PLOT_TINCT_OPAQUE :
+ IMAGE_PLOT_TINCT_ALPHA);
+}
diff --git a/frontends/riscos/print.c b/frontends/riscos/print.c
new file mode 100644
index 000000000..95730d6be
--- /dev/null
+++ b/frontends/riscos/print.c
@@ -0,0 +1,976 @@
+/*
+ * Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk>
+ * Copyright 2006 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "utils/config.h"
+
+#include <assert.h>
+#include <string.h>
+#include <swis.h>
+#include <oslib/font.h>
+#include <oslib/hourglass.h>
+#include <oslib/osfile.h>
+#include <oslib/osfind.h>
+#include <oslib/pdriver.h>
+#include <oslib/wimp.h>
+#include <rufl.h>
+#include <limits.h>
+
+#include "utils/config.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "utils/nsoption.h"
+#include "content/content.h"
+#include "content/hlcache.h"
+#include "desktop/browser.h"
+#include "desktop/plotters.h"
+
+#include "riscos/gui.h"
+#include "riscos/dialog.h"
+#include "riscos/menus.h"
+#include "riscos/print.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/filetype.h"
+#include "riscos/font.h"
+
+
+#define ICON_PRINT_TO_BOTTOM 1
+#define ICON_PRINT_SHEETS 2
+#define ICON_PRINT_SHEETS_VALUE 3
+#define ICON_PRINT_SHEETS_DOWN 4
+#define ICON_PRINT_SHEETS_UP 5
+#define ICON_PRINT_SHEETS_TEXT 6
+#define ICON_PRINT_FG_IMAGES 7
+#define ICON_PRINT_BG_IMAGES 8
+#define ICON_PRINT_IN_BACKGROUND 9
+#define ICON_PRINT_UPRIGHT 10
+#define ICON_PRINT_SIDEWAYS 11
+#define ICON_PRINT_COPIES 12
+#define ICON_PRINT_COPIES_DOWN 13
+#define ICON_PRINT_COPIES_UP 14
+#define ICON_PRINT_CANCEL 15
+#define ICON_PRINT_PRINT 16
+#define ICON_PRINT_TEXT_BLACK 20
+
+
+/** \todo landscape format pages
+ * \todo be somewhat more intelligent and try not to crop pages
+ * half way up a line of text
+ * \todo make use of print stylesheets
+ */
+
+struct gui_window *ro_print_current_window = NULL;
+bool print_text_black = false;
+bool print_active = false;
+
+/* 1 millipoint == 1/400 OS unit == 1/800 browser units */
+
+static int print_prev_message = 0;
+static bool print_in_background = false;
+static float print_scale = 1.0;
+static int print_num_copies = 1;
+static bool print_bg_images = false;
+static int print_max_sheets = -1;
+static bool print_sideways = false;
+/** List of fonts in current print. */
+static char **print_fonts_list = 0;
+/** Number of entries in print_fonts_list. */
+static unsigned int print_fonts_count;
+/** Error in print_fonts_plot_text() or print_fonts_callback(). */
+static const char *print_fonts_error;
+
+void gui_window_redraw_window(struct gui_window *g);
+
+static bool ro_gui_print_click(wimp_pointer *pointer);
+static bool ro_gui_print_apply(wimp_w w);
+static void print_update_sheets_shaded_state(bool on);
+static void print_send_printsave(hlcache_handle *h);
+static bool print_send_printtypeknown(wimp_message *m);
+static bool print_document(struct gui_window *g, const char *filename);
+static const char *print_declare_fonts(hlcache_handle *h);
+static bool print_fonts_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style);
+static bool print_fonts_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *style);
+static bool print_fonts_plot_polygon(const int *p, unsigned int n, const plot_style_t *style);
+static bool print_fonts_plot_clip(const struct rect *clip);
+static bool print_fonts_plot_text(int x, int y, const char *text, size_t length,
+ const plot_font_style_t *fstyle);
+static bool print_fonts_plot_disc(int x, int y, int radius, const plot_style_t *style);
+static bool print_fonts_plot_arc(int x, int y, int radius, int angle1, int angle2, const plot_style_t *style);
+static bool print_fonts_plot_bitmap(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg,
+ bitmap_flags_t flags);
+static bool print_fonts_plot_path(const float *p, unsigned int n, colour fill, float width,
+ colour c, const float transform[6]);
+static void print_fonts_callback(void *context,
+ const char *font_name, unsigned int font_size,
+ const char *s8, unsigned short *s16, unsigned int n,
+ int x, int y);
+
+
+/** Plotter for print_declare_fonts(). All the functions do nothing except for
+ * print_fonts_plot_text, which records the fonts used. */
+static const struct plotter_table print_fonts_plotters = {
+ .rectangle = print_fonts_plot_rectangle,
+ .line = print_fonts_plot_line,
+ .polygon = print_fonts_plot_polygon,
+ .clip = print_fonts_plot_clip,
+ .text = print_fonts_plot_text,
+ .disc = print_fonts_plot_disc,
+ .arc = print_fonts_plot_arc,
+ .bitmap = print_fonts_plot_bitmap,
+ .path = print_fonts_plot_path,
+ .option_knockout = false,
+};
+
+
+/**
+ * Initialise the print dialog.
+ */
+
+void ro_gui_print_init(void)
+{
+ wimp_i radio_print_type[] = {ICON_PRINT_TO_BOTTOM, ICON_PRINT_SHEETS,
+ -1};
+ wimp_i radio_print_orientation[] = {ICON_PRINT_UPRIGHT,
+ ICON_PRINT_SIDEWAYS, -1};
+
+ dialog_print = ro_gui_dialog_create("print");
+ ro_gui_wimp_event_register_radio(dialog_print, radio_print_type);
+ ro_gui_wimp_event_register_radio(dialog_print, radio_print_orientation);
+ ro_gui_wimp_event_register_checkbox(dialog_print, ICON_PRINT_FG_IMAGES);
+ ro_gui_wimp_event_register_checkbox(dialog_print, ICON_PRINT_BG_IMAGES);
+ ro_gui_wimp_event_register_checkbox(dialog_print,
+ ICON_PRINT_IN_BACKGROUND);
+ ro_gui_wimp_event_register_checkbox(dialog_print,
+ ICON_PRINT_TEXT_BLACK);
+ ro_gui_wimp_event_register_text_field(dialog_print,
+ ICON_PRINT_SHEETS_TEXT);
+ ro_gui_wimp_event_register_numeric_field(dialog_print,
+ ICON_PRINT_COPIES, ICON_PRINT_COPIES_UP,
+ ICON_PRINT_COPIES_DOWN, 1, 99, 1, 0);
+ ro_gui_wimp_event_register_numeric_field(dialog_print,
+ ICON_PRINT_SHEETS_VALUE, ICON_PRINT_SHEETS_UP,
+ ICON_PRINT_SHEETS_DOWN, 1, 99, 1, 0);
+ ro_gui_wimp_event_register_cancel(dialog_print, ICON_PRINT_CANCEL);
+ ro_gui_wimp_event_register_mouse_click(dialog_print,
+ ro_gui_print_click);
+ ro_gui_wimp_event_register_ok(dialog_print, ICON_PRINT_PRINT,
+ ro_gui_print_apply);
+ ro_gui_wimp_event_set_help_prefix(dialog_print, "HelpPrint");
+}
+
+
+/**
+ * Prepares all aspects of the print dialog prior to opening.
+ *
+ * \param g parent window
+ */
+
+void ro_gui_print_prepare(struct gui_window *g)
+{
+ char *desc;
+ bool printers_exists = true;
+ os_error *error;
+
+ assert(g);
+
+ ro_print_current_window = g;
+ print_prev_message = 0;
+
+ /* Read Printer Driver name */
+ error = xpdriver_info(0, 0, 0, 0, &desc, 0, 0, 0);
+ if (error) {
+ LOG("xpdriver_info: 0x%x: %s", error->errnum, error->errmess);
+ printers_exists = false;
+ }
+
+ ro_gui_set_icon_selected_state(dialog_print, ICON_PRINT_TO_BOTTOM,
+ true);
+
+ ro_gui_set_icon_selected_state(dialog_print, ICON_PRINT_SHEETS, false);
+ ro_gui_set_icon_integer(dialog_print, ICON_PRINT_SHEETS_VALUE, 1);
+ print_update_sheets_shaded_state(true);
+
+ ro_gui_set_icon_selected_state(dialog_print, ICON_PRINT_FG_IMAGES,
+ true);
+ ro_gui_set_icon_shaded_state(dialog_print, ICON_PRINT_FG_IMAGES, true);
+
+ ro_gui_set_icon_selected_state(dialog_print, ICON_PRINT_BG_IMAGES,
+ print_bg_images);
+
+ ro_gui_set_icon_selected_state(dialog_print, ICON_PRINT_IN_BACKGROUND,
+ false);
+
+ ro_gui_set_icon_selected_state(dialog_print, ICON_PRINT_UPRIGHT, true);
+ ro_gui_set_icon_selected_state(dialog_print, ICON_PRINT_SIDEWAYS,
+ false);
+
+ ro_gui_set_icon_selected_state(dialog_print, ICON_PRINT_TEXT_BLACK,
+ false);
+
+ ro_gui_set_icon_integer(dialog_print, ICON_PRINT_COPIES, 1);
+
+ ro_gui_set_icon_shaded_state(dialog_print, ICON_PRINT_PRINT,
+ !printers_exists);
+ if (printers_exists)
+ ro_gui_set_window_title(dialog_print, desc);
+
+ ro_gui_wimp_event_memorise(dialog_print);
+}
+
+
+/**
+ * Handle mouse clicks in print dialog
+ *
+ * \param pointer wimp_pointer block
+ */
+
+bool ro_gui_print_click(wimp_pointer *pointer)
+{
+ if (pointer->buttons == wimp_CLICK_MENU)
+ return true;
+
+ switch (pointer->i) {
+ case ICON_PRINT_TO_BOTTOM:
+ case ICON_PRINT_SHEETS:
+ print_update_sheets_shaded_state(pointer->i !=
+ ICON_PRINT_SHEETS);
+ break;
+ }
+ return false;
+}
+
+
+/**
+ * Handle click on the Print button in the print dialog.
+ */
+
+bool ro_gui_print_apply(wimp_w w)
+{
+ int copies = atoi(ro_gui_get_icon_string(dialog_print,
+ ICON_PRINT_COPIES));
+ int sheets = atoi(ro_gui_get_icon_string(dialog_print,
+ ICON_PRINT_SHEETS_VALUE));
+
+ print_in_background = ro_gui_get_icon_selected_state(dialog_print,
+ ICON_PRINT_IN_BACKGROUND);
+ print_text_black = ro_gui_get_icon_selected_state(dialog_print,
+ ICON_PRINT_TEXT_BLACK);
+ print_sideways = ro_gui_get_icon_selected_state(dialog_print,
+ ICON_PRINT_SIDEWAYS);
+ print_num_copies = copies;
+ if (ro_gui_get_icon_selected_state(dialog_print, ICON_PRINT_SHEETS))
+ print_max_sheets = sheets;
+ else
+ print_max_sheets = -1;
+ print_bg_images = ro_gui_get_icon_selected_state(dialog_print,
+ ICON_PRINT_BG_IMAGES);
+
+ print_send_printsave(browser_window_get_content(
+ ro_print_current_window->bw));
+
+ return true;
+}
+
+
+/**
+ * Set shaded state of sheets
+ *
+ * \param on whether to turn shading on or off
+ */
+
+void print_update_sheets_shaded_state(bool on)
+{
+ ro_gui_set_icon_shaded_state(dialog_print, ICON_PRINT_SHEETS_VALUE, on);
+ ro_gui_set_icon_shaded_state(dialog_print, ICON_PRINT_SHEETS_DOWN, on);
+ ro_gui_set_icon_shaded_state(dialog_print, ICON_PRINT_SHEETS_UP, on);
+ ro_gui_set_icon_shaded_state(dialog_print, ICON_PRINT_SHEETS_TEXT, on);
+ ro_gui_set_caret_first(dialog_print);
+}
+
+
+/**
+ * Send a message_PRINT_SAVE
+ *
+ * \param h handle to content to print.
+ */
+
+void print_send_printsave(hlcache_handle *h)
+{
+ wimp_full_message_data_xfer m;
+ os_error *e;
+ int len;
+
+ len = strlen(content_get_title(h)) + 1;
+ if (212 < len)
+ len = 212;
+
+ m.size = ((44+len+3) & ~3);
+ m.your_ref = 0;
+ m.action = message_PRINT_SAVE;
+ m.w = (wimp_w)0;
+ m.i = m.pos.x = m.pos.y = 0;
+ m.est_size = 1024; /* arbitrary value - it really doesn't matter */
+ m.file_type = ro_content_filetype(h);
+ strncpy(m.file_name, content_get_title(h), 211);
+ m.file_name[211] = 0;
+ e = xwimp_send_message(wimp_USER_MESSAGE_RECORDED,
+ (wimp_message *)&m, 0);
+ if (e) {
+ LOG("xwimp_send_message: 0x%x: %s", e->errnum, e->errmess);
+ ro_warn_user("WimpError", e->errmess);
+ ro_print_cleanup();
+ }
+ print_prev_message = m.my_ref;
+}
+
+
+/**
+ * Send a message_PRINT_TYPE_KNOWN
+ *
+ * \param m message to reply to
+ * \return true on success, false otherwise
+ */
+
+bool print_send_printtypeknown(wimp_message *m)
+{
+ os_error *e;
+
+ m->size = 20;
+ m->your_ref = m->my_ref;
+ m->action = message_PRINT_TYPE_KNOWN;
+ e = xwimp_send_message(wimp_USER_MESSAGE, m, m->sender);
+ if (e) {
+ LOG("xwimp_send_message: 0x%x: %s", e->errnum, e->errmess);
+ ro_warn_user("WimpError", e->errmess);
+ return false;
+ }
+
+ return true;
+}
+
+
+/**
+ * Handle a bounced message_PRINT_SAVE
+ *
+ * \param m the bounced message
+ */
+
+void ro_print_save_bounce(wimp_message *m)
+{
+ if (m->my_ref == 0 || m->my_ref != print_prev_message)
+ return;
+
+ /* try to print anyway (we're graphics printing) */
+ if (ro_print_current_window) {
+ print_document(ro_print_current_window, "printer:");
+ }
+ ro_print_cleanup();
+}
+
+
+/**
+ * Handle message_PRINT_ERROR
+ *
+ * \param m the message containing the error
+ */
+
+void ro_print_error(wimp_message *m)
+{
+ pdriver_message_print_error *p = (pdriver_message_print_error*)&m->data;
+ if (m->your_ref == 0 || m->your_ref != print_prev_message)
+ return;
+
+ if (m->size == 20)
+ ro_warn_user("PrintErrorRO2", 0);
+ else
+ ro_warn_user("PrintError", p->errmess);
+
+ ro_print_cleanup();
+}
+
+
+/**
+ * Handle message_PRINT_TYPE_ODD
+ *
+ * \param m the message to handle
+ */
+
+void ro_print_type_odd(wimp_message *m)
+{
+ if ((m->your_ref == 0 || m->your_ref == print_prev_message) &&
+ !print_in_background) {
+ /* reply to a previous message (ie printsave) */
+ if (ro_print_current_window && print_send_printtypeknown(m)) {
+ print_document(ro_print_current_window, "printer:");
+ }
+ ro_print_cleanup();
+ }
+ else {
+ /* broadcast message */
+ /* no need to do anything */
+ }
+
+}
+
+
+/**
+ * Handle message_DATASAVE_ACK for the printing protocol.
+ *
+ * \param m the message to handle
+ * \return true if message successfully handled, false otherwise
+ *
+ * We cheat here and, instead of giving Printers what it asked for (a copy of
+ * the file so it can poke us later via a broadcast of PrintTypeOdd), we give
+ * it a file that it can print itself without having to bother us further. For
+ * PostScript printers (type 0) we give it a PostScript file. Otherwise, we give
+ * it a PrintOut file.
+ *
+ * This method has a couple of advantages:
+ * - we can reuse this code for background printing (we simply ignore the
+ * PrintTypeOdd reply)
+ * - there's no need to ensure all components of a page queued to be printed
+ * still exist when it reaches the top of the queue. (which reduces complexity
+ * a fair bit)
+ */
+
+bool ro_print_ack(wimp_message *m)
+{
+ pdriver_info_type info_type;
+ pdriver_type type;
+ os_error *error;
+
+ if (m->your_ref == 0 || m->your_ref != print_prev_message ||
+ !ro_print_current_window)
+ return false;
+
+ /* read printer driver type */
+ error = xpdriver_info(&info_type, 0, 0, 0, 0, 0, 0, 0);
+ if (error) {
+ LOG("xpdriver_info: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("PrintError", error->errmess);
+ ro_print_cleanup();
+ return true;
+ }
+ type = info_type >> 16;
+
+ /* print to file */
+ if (!print_document(ro_print_current_window,
+ m->data.data_xfer.file_name)) {
+ ro_print_cleanup();
+ return true;
+ }
+
+ /* send dataload */
+ m->your_ref = m->my_ref;
+ m->action = message_DATA_LOAD;
+
+ if (type == pdriver_TYPE_PS)
+ m->data.data_xfer.file_type = osfile_TYPE_POSTSCRIPT;
+ else
+ m->data.data_xfer.file_type = osfile_TYPE_PRINTOUT;
+
+ error = xwimp_send_message(wimp_USER_MESSAGE_RECORDED, m, m->sender);
+ if (error) {
+ LOG("xwimp_send_message: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ /* and delete temporary file */
+ xosfile_delete(m->data.data_xfer.file_name,
+ 0, 0, 0, 0, 0);
+ }
+ print_prev_message = m->my_ref;
+
+ ro_print_cleanup();
+ return true;
+}
+
+
+/**
+ * Handle a bounced dataload message
+ *
+ * \param m the message to handle
+ */
+
+void ro_print_dataload_bounce(wimp_message *m)
+{
+ if (m->your_ref == 0 || m->your_ref != print_prev_message)
+ return;
+
+ xosfile_delete(m->data.data_xfer.file_name, 0, 0, 0, 0, 0);
+ ro_print_cleanup();
+}
+
+
+/**
+ * Cleanup after printing
+ */
+
+void ro_print_cleanup(void)
+{
+ ro_print_current_window = NULL;
+ print_text_black = false;
+ print_prev_message = 0;
+ print_max_sheets = -1;
+ ro_gui_menu_destroy();
+ ro_gui_dialog_close(dialog_print);
+}
+
+
+/**
+ * Print a document.
+ *
+ * \param g gui_window containing the document to print
+ * \param filename name of file to print to
+ * \return true on success, false on error and error reported
+ */
+
+bool print_document(struct gui_window *g, const char *filename)
+{
+ int left, right, top, bottom, width, height;
+ int saved_width, saved_height;
+ int yscroll = 0, sheets = print_max_sheets;
+ hlcache_handle *h = browser_window_get_content(g->bw);
+ const char *error_message;
+ pdriver_features features;
+ os_fw fhandle, old_job = 0;
+ os_error *error;
+
+ /* no point printing a blank page */
+ if (!h) {
+ ro_warn_user("PrintError", "nothing to print");
+ return false;
+ }
+
+ /* read printer driver features */
+ error = xpdriver_info(0, 0, 0, &features, 0, 0, 0, 0);
+ if (error) {
+ LOG("xpdriver_info: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("PrintError", error->errmess);
+ return false;
+ }
+
+ /* read page size */
+ error = xpdriver_page_size(0, 0, &left, &bottom, &right, &top);
+ if (error) {
+ LOG("xpdriver_page_size: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("PrintError", error->errmess);
+ return false;
+ }
+
+ if (print_sideways) {
+ width = (top - bottom) / 800;
+ height = (right - left) / 800;
+ } else {
+ width = (right - left) / 800;
+ height = (top - bottom) / 800;
+ }
+
+ /* layout the document to the correct width */
+ saved_width = content_get_width(h);
+ saved_height = content_get_height(h);
+ if (content_get_type(h) == CONTENT_HTML)
+ content_reformat(h, false, width, height);
+
+ /* open printer file */
+ error = xosfind_openoutw(osfind_NO_PATH | osfind_ERROR_IF_DIR |
+ osfind_ERROR_IF_ABSENT, filename, 0, &fhandle);
+ if (error) {
+ LOG("xosfind_openoutw: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("PrintError", error->errmess);
+ return false;
+ }
+
+ /* select print job */
+ error = xpdriver_select_jobw(fhandle, "NetSurf", &old_job);
+ if (error) {
+ LOG("xpdriver_select_jobw: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("PrintError", error->errmess);
+ xosfind_closew(fhandle);
+ return false;
+ }
+
+ rufl_invalidate_cache();
+
+ /* declare fonts, if necessary */
+ if (features & pdriver_FEATURE_DECLARE_FONT) {
+ if ((error_message = print_declare_fonts(h)))
+ goto error;
+ }
+
+ ro_gui_current_redraw_gui = g;
+
+ /* print is now active */
+ print_active = true;
+
+ do {
+ struct rect clip;
+ os_box b;
+ os_hom_trfm t;
+ os_coord p;
+ osbool more;
+
+ if (print_sideways) {
+ b.x0 = bottom / 400 -2;
+ b.y0 = left / 400 - 2;
+ b.x1 = top / 400 + 2;
+ b.y1 = right / 400 + 2;
+ t.entries[0][0] = 0;
+ t.entries[0][1] = 65536;
+ t.entries[1][0] = -65536;
+ t.entries[1][1] = 0;
+ p.x = right;
+ p.y = bottom;
+ ro_plot_origin_x = bottom / 400;
+ ro_plot_origin_y = right / 400 + yscroll * 2;
+ } else {
+ b.x0 = left / 400 -2;
+ b.y0 = bottom / 400 - 2;
+ b.x1 = right / 400 + 2;
+ b.y1 = top / 400 + 2;
+ t.entries[0][0] = 65536;
+ t.entries[0][1] = 0;
+ t.entries[1][0] = 0;
+ t.entries[1][1] = 65536;
+ p.x = left;
+ p.y = bottom;
+ ro_plot_origin_x = left / 400;
+ ro_plot_origin_y = top / 400 + yscroll * 2;
+ }
+
+ xhourglass_percentage((int) (yscroll * 100 /
+ content_get_height(h)));
+
+ /* give page rectangle */
+ error = xpdriver_give_rectangle(0, &b, &t, &p, os_COLOUR_WHITE);
+ if (error) {
+ LOG("xpdriver_give_rectangle: 0x%x: %s", error->errnum, error->errmess);
+ error_message = error->errmess;
+ goto error;
+ }
+
+ LOG("given rectangle: [(%d, %d), (%d, %d)]", b.x0, b.y0, b.x1, b.y1);
+
+ /* and redraw the document */
+ error = xpdriver_draw_page(print_num_copies, &b, 0, 0,
+ &more, 0);
+ if (error) {
+ LOG("xpdriver_draw_page: 0x%x: %s", error->errnum, error->errmess);
+ error_message = error->errmess;
+ goto error;
+ }
+
+ while (more) {
+ struct content_redraw_data data;
+ /* TODO: turn knockout off for print */
+ struct redraw_context ctx = {
+ .interactive = false,
+ .background_images = print_bg_images,
+ .plot = &ro_plotters
+ };
+
+ LOG("redrawing area: [(%d, %d), (%d, %d)]", b.x0, b.y0, b.x1, b.y1);
+ clip.x0 = (b.x0 - ro_plot_origin_x) / 2;
+ clip.y0 = (ro_plot_origin_y - b.y1) / 2;
+ clip.x1 = (b.x1 - ro_plot_origin_x) / 2;
+ clip.y1 = (ro_plot_origin_y - b.y0) / 2;
+
+ data.x = 0;
+ data.y = 0;
+ data.width = content_get_width(h);
+ data.height = content_get_height(h);
+ data.background_colour = 0xFFFFFF;
+ data.scale = print_scale;
+ data.repeat_x = false;
+ data.repeat_y = false;
+
+ if (!content_redraw(h, &data, &clip, &ctx)) {
+ error_message = "redraw error";
+ goto error;
+ }
+
+ error = xpdriver_get_rectangle(&b, &more, 0);
+ if (error) {
+ LOG("xpdriver_get_rectangle: 0x%x: %s", error->errnum, error->errmess);
+ error_message = error->errmess;
+ goto error;
+ }
+ }
+
+ yscroll += height;
+ } while (yscroll <= content_get_height(h) && --sheets != 0);
+
+ /* make print inactive */
+ print_active = false;
+ ro_gui_current_redraw_gui = 0;
+
+ /* clean up
+ *
+ * Call PDriver_EndJob via _swix() so that r9 is preserved. This
+ * prevents a crash if the SWI corrupts it on exit (as seems to
+ * happen on some versions of RISC OS 6).
+ */
+
+ error = (os_error *) _swix(PDriver_EndJob, _IN(0), (int) fhandle);
+ if (error) {
+ LOG("xpdriver_end_jobw: 0x%x: %s", error->errnum, error->errmess);
+ error_message = error->errmess;
+ goto error;
+ }
+
+ error = xosfind_closew(fhandle);
+ if (error) {
+ LOG("xosfind_closew: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("PrintError", error->errmess);
+ return false;
+ }
+
+ if (old_job) {
+ error = xpdriver_select_jobw(old_job, 0, 0);
+ if (error) {
+ LOG("xpdriver_select_jobw: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("PrintError", error->errmess);
+ /* the printing succeeded anyway */
+ return true;
+ }
+ }
+
+ rufl_invalidate_cache();
+
+ /* restore document layout and redraw browser window */
+ if (content_get_type(h) == CONTENT_HTML)
+ content_reformat(h, false, saved_width, saved_height);
+
+ gui_window_redraw_window(g);
+
+ return true;
+
+error:
+ xpdriver_abort_job(fhandle);
+ xosfind_closew(fhandle);
+ if (old_job)
+ xpdriver_select_jobw(old_job, 0, 0);
+ print_active = false;
+ ro_gui_current_redraw_gui = 0;
+
+ ro_warn_user("PrintError", error_message);
+
+ rufl_invalidate_cache();
+
+ /* restore document layout */
+ if (content_get_type(h) == CONTENT_HTML)
+ content_reformat(h, false, saved_width, saved_height);
+
+ return false;
+}
+
+
+/**
+ * Declare fonts to the printer driver.
+ *
+ * \param h handle to content being printed
+ * \return 0 on success, error message on error
+ */
+
+const char *print_declare_fonts(hlcache_handle *h)
+{
+ unsigned int i;
+ struct rect clip;
+ struct content_redraw_data data;
+ const char *error_message = 0;
+ os_error *error;
+ struct redraw_context ctx = {
+ .interactive = false,
+ .background_images = false,
+ .plot = &print_fonts_plotters
+ };
+
+ free(print_fonts_list);
+ print_fonts_list = 0;
+ print_fonts_count = 0;
+ print_fonts_error = 0;
+
+ clip.x0 = clip.y0 = INT_MIN;
+ clip.x1 = clip.y1 = INT_MAX;
+
+ data.x = 0;
+ data.y = 0;
+ data.width = content_get_width(h);
+ data.height = content_get_height(h);
+ data.background_colour = 0xFFFFFF;
+ data.scale = 1;
+ data.repeat_x = false;
+ data.repeat_y = false;
+
+ if (!content_redraw(h, &data, &clip, &ctx)) {
+ if (print_fonts_error)
+ return print_fonts_error;
+ return "Declaring fonts failed.";
+ }
+
+ for (i = 0; i != print_fonts_count; ++i) {
+ LOG("%u %s", i, print_fonts_list[i]);
+ error = xpdriver_declare_font(0, print_fonts_list[i],
+ pdriver_KERNED);
+ if (error) {
+ LOG("xpdriver_declare_font: 0x%x: %s", error->errnum, error->errmess);
+ error_message = error->errmess;
+ goto end;
+ }
+ }
+ error = xpdriver_declare_font(0, 0, 0);
+ if (error) {
+ LOG("xpdriver_declare_font: 0x%x: %s", error->errnum, error->errmess);
+ error_message = error->errmess;
+ goto end;
+ }
+
+end:
+ for (i = 0; i != print_fonts_count; i++)
+ free(print_fonts_list[i]);
+ free(print_fonts_list);
+ print_fonts_list = 0;
+
+ return error_message;
+}
+
+
+bool print_fonts_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ return true;
+}
+
+
+bool print_fonts_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ return true;
+}
+
+bool print_fonts_plot_polygon(const int *p, unsigned int n, const plot_style_t *style)
+{
+ return true;
+}
+
+
+bool print_fonts_plot_clip(const struct rect *clip)
+{
+ return true;
+}
+
+bool print_fonts_plot_disc(int x, int y, int radius, const plot_style_t *style)
+{
+ return true;
+}
+
+bool print_fonts_plot_arc(int x, int y, int radius, int angle1, int angle2,
+ const plot_style_t *style)
+{
+ return true;
+}
+
+bool print_fonts_plot_bitmap(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg, bitmap_flags_t flags)
+{
+ return true;
+}
+
+bool print_fonts_plot_path(const float *p, unsigned int n, colour fill, float width,
+ colour c, const float transform[6])
+{
+ return true;
+}
+
+
+/**
+ * Plotter for text plotting during font listing.
+ */
+
+bool print_fonts_plot_text(int x, int y, const char *text, size_t length,
+ const plot_font_style_t *fstyle)
+{
+ const char *font_family;
+ unsigned int font_size;
+ rufl_style font_style;
+ rufl_code code;
+
+ nsfont_read_style(fstyle, &font_family, &font_size, &font_style);
+
+ code = rufl_paint_callback(font_family, font_style, font_size,
+ text, length, 0, 0, print_fonts_callback, 0);
+ if (code != rufl_OK) {
+ if (code == rufl_FONT_MANAGER_ERROR) {
+ LOG("rufl_paint_callback: rufl_FONT_MANAGER_ERROR: ""0x%x: %s", rufl_fm_error->errnum, rufl_fm_error->errmess);
+ print_fonts_error = rufl_fm_error->errmess;
+ } else {
+ LOG("rufl_paint_callback: 0x%x", code);
+ }
+ return false;
+ }
+ if (print_fonts_error)
+ return false;
+
+ return true;
+}
+
+
+/**
+ * Callback for print_fonts_plot_text().
+ *
+ * The font name is added to print_fonts_list.
+ */
+
+void print_fonts_callback(void *context,
+ const char *font_name, unsigned int font_size,
+ const char *s8, unsigned short *s16, unsigned int n,
+ int x, int y)
+{
+ unsigned int i;
+ char **fonts_list;
+
+ (void) context; /* unused */
+ (void) font_size; /* unused */
+ (void) x; /* unused */
+ (void) y; /* unused */
+
+ assert(s8 || s16);
+
+ /* check if the font name is new */
+ for (i = 0; i != print_fonts_count &&
+ strcmp(print_fonts_list[i], font_name) != 0; i++)
+ ;
+ if (i != print_fonts_count)
+ return;
+
+ /* add to list of fonts */
+ fonts_list = realloc(print_fonts_list,
+ sizeof print_fonts_list[0] *
+ (print_fonts_count + 1));
+ if (!fonts_list) {
+ print_fonts_error = messages_get("NoMemory");
+ return;
+ }
+ fonts_list[print_fonts_count] = strdup(font_name);
+ if (!fonts_list[print_fonts_count]) {
+ print_fonts_error = messages_get("NoMemory");
+ return;
+ }
+ print_fonts_list = fonts_list;
+ print_fonts_count++;
+}
+
diff --git a/frontends/riscos/print.h b/frontends/riscos/print.h
new file mode 100644
index 000000000..d997dce99
--- /dev/null
+++ b/frontends/riscos/print.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_RISCOS_PRINT_H_
+#define _NETSURF_RISCOS_PRINT_H_
+
+struct gui_window;
+
+extern struct gui_window *ro_print_current_window;
+
+void ro_print_save_bounce(wimp_message *m);
+void ro_print_error(wimp_message *m);
+void ro_print_type_odd(wimp_message *m);
+bool ro_print_ack(wimp_message *m);
+void ro_print_dataload_bounce(wimp_message *m);
+void ro_print_cleanup(void);
+
+#endif
diff --git a/frontends/riscos/query.c b/frontends/riscos/query.c
new file mode 100644
index 000000000..1d7cf5120
--- /dev/null
+++ b/frontends/riscos/query.c
@@ -0,0 +1,381 @@
+/*
+ * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utf8.h"
+#include "utils/utils.h"
+
+#include "riscos/gui.h"
+#include "riscos/query.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/ucstables.h"
+#include "riscos/dialog.h"
+
+#define ICON_QUERY_MESSAGE 0
+#define ICON_QUERY_YES 1
+#define ICON_QUERY_NO 2
+#define ICON_QUERY_HELP 3
+
+/** Data for a query window */
+struct gui_query_window
+{
+ struct gui_query_window *prev; /** Previous query in list */
+ struct gui_query_window *next; /** Next query in list */
+
+ query_id id; /** unique ID number for this query */
+ wimp_w window; /** RISC OS window handle */
+
+ const query_callback *cb; /** Table of callback functions */
+ void *pw; /** Handle passed to callback functions */
+
+ bool default_confirm; /** Default action is to confirm */
+};
+
+
+/** Next unallocated query id */
+static query_id next_id = (query_id)1;
+
+/** List of all query windows. */
+static struct gui_query_window *gui_query_window_list = 0;
+
+/** Template for a query window. */
+static struct wimp_window *query_template;
+
+/** Widths of Yes and No buttons */
+static int query_yes_width = 0;
+static int query_no_width = 0;
+
+static struct gui_query_window *ro_gui_query_window_lookup_id(query_id id);
+
+static bool ro_gui_query_click(wimp_pointer *pointer);
+static void ro_gui_query_close(wimp_w w);
+static bool ro_gui_query_apply(wimp_w w);
+
+
+void ro_gui_query_init(void)
+{
+ query_template = ro_gui_dialog_load_template("query");
+}
+
+
+/**
+ * Lookup a query window using its ID number
+ *
+ * \param id id to search for
+ * \return pointer to query window or NULL
+ */
+
+struct gui_query_window *ro_gui_query_window_lookup_id(query_id id)
+{
+ struct gui_query_window *qw = gui_query_window_list;
+ while (qw && qw->id != id)
+ qw = qw->next;
+ return qw;
+}
+
+
+/**
+ * Display a query to the user, requesting a response, near the current
+ * pointer position to keep the required mouse travel small, but also
+ * protecting against spurious mouse clicks.
+ *
+ * \param query message token of query
+ * \param detail parameter used in expanding tokenised message
+ * \param cb table of callback functions to be called when user responds
+ * \param pw handle to be passed to callback functions
+ * \param yes text to use for 'Yes' button' (or NULL for default)
+ * \param no text to use for 'No' button (or NULL for default)
+ * \return id number of the query (or QUERY_INVALID if it failed)
+ */
+
+query_id query_user(const char *query, const char *detail,
+ const query_callback *cb, void *pw,
+ const char *yes, const char *no)
+{
+ wimp_pointer pointer;
+ if (xwimp_get_pointer_info(&pointer))
+ pointer.pos.y = pointer.pos.x = -1;
+
+ return query_user_xy(query, detail, cb, pw, yes, no,
+ pointer.pos.x, pointer.pos.y);
+}
+
+
+/**
+ * Display a query to the user, requesting a response, at a specified
+ * screen position (x,y). The window is positioned relative to the given
+ * location such that the required mouse travel is small, but non-zero
+ * for protection spurious double-clicks.
+ *
+ * \param query message token of query
+ * \param detail parameter used in expanding tokenised message
+ * \param cb table of callback functions to be called when user responds
+ * \param pw handle to be passed to callback functions
+ * \param yes text to use for 'Yes' button' (or NULL for default)
+ * \param no text to use for 'No' button (or NULL for default)
+ * \param x x position in screen coordinates (-1 = centred on screen)
+ * \param y y position in screen coordinates (-1 = centred on screen)
+ * \return id number of the query (or QUERY_INVALID if it failed)
+ */
+
+query_id query_user_xy(const char *query, const char *detail,
+ const query_callback *cb, void *pw,
+ const char *yes, const char *no,
+ int x, int y)
+{
+ struct gui_query_window *qw;
+ char query_buffer[300];
+ os_error *error;
+ wimp_icon *icn;
+ int width;
+ int len;
+ int tx;
+ char *local_text = NULL;
+ nserror err;
+
+ qw = malloc(sizeof(struct gui_query_window));
+ if (!qw) {
+ ro_warn_user("NoMemory", NULL);
+ return QUERY_INVALID;
+ }
+
+ qw->cb = cb;
+ qw->pw = pw;
+ qw->id = next_id++;
+ qw->default_confirm = false;
+
+ if (next_id == QUERY_INVALID)
+ next_id++;
+
+ if (!yes) yes = messages_get("Yes");
+ if (!no) no = messages_get("No");
+
+ /* set the text of the 'Yes' button and size accordingly */
+ err = utf8_to_local_encoding(yes, 0, &local_text);
+ if (err != NSERROR_OK) {
+ assert(err != NSERROR_BAD_ENCODING);
+ LOG("utf8_to_local_encoding_failed");
+ local_text = NULL;
+ }
+
+ icn = &query_template->icons[ICON_QUERY_YES];
+ len = strlen(local_text ? local_text : yes);
+ len = max(len, icn->data.indirected_text.size - 1);
+ memcpy(icn->data.indirected_text.text,
+ local_text ? local_text: yes, len);
+ icn->data.indirected_text.text[len] = '\0';
+
+ free(local_text);
+ local_text = NULL;
+
+ error = xwimptextop_string_width(icn->data.indirected_text.text, len, &width);
+ if (error) {
+ LOG("xwimptextop_string_width: 0x%x:%s", error->errnum, error->errmess);
+ width = len * 16;
+ }
+ if (!query_yes_width) query_yes_width = icn->extent.x1 - icn->extent.x0;
+ width += 44;
+ if (width < query_yes_width)
+ width = query_yes_width;
+ icn->extent.x0 = tx = icn->extent.x1 - width;
+
+ /* set the text of the 'No' button and size accordingly */
+ err = utf8_to_local_encoding(no, 0, &local_text);
+ if (err != NSERROR_OK) {
+ assert(err != NSERROR_BAD_ENCODING);
+ LOG("utf8_to_local_encoding_failed");
+ local_text = NULL;
+ }
+
+ icn = &query_template->icons[ICON_QUERY_NO];
+ len = strlen(local_text ? local_text : no);
+ len = max(len, icn->data.indirected_text.size - 1);
+ memcpy(icn->data.indirected_text.text,
+ local_text ? local_text : no, len);
+ icn->data.indirected_text.text[len] = '\0';
+
+ free(local_text);
+ local_text = NULL;
+
+ if (!query_no_width) query_no_width = icn->extent.x1 - icn->extent.x0;
+ icn->extent.x1 = tx - 16;
+ error = xwimptextop_string_width(icn->data.indirected_text.text, len, &width);
+ if (error) {
+ LOG("xwimptextop_string_width: 0x%x:%s", error->errnum, error->errmess);
+ width = len * 16;
+ }
+ width += 28;
+ if (width < query_no_width)
+ width = query_no_width;
+ icn->extent.x0 = icn->extent.x1 - width;
+
+ error = xwimp_create_window(query_template, &qw->window);
+ if (error) {
+ ro_warn_user("WimpError", error->errmess);
+ free(qw);
+ return QUERY_INVALID;
+ }
+
+ snprintf(query_buffer, sizeof query_buffer, "%s %s",
+ messages_get(query), detail ? detail : "");
+ query_buffer[sizeof query_buffer - 1] = 0;
+
+ ro_gui_set_icon_string(qw->window, ICON_QUERY_MESSAGE,
+ query_buffer, true);
+
+ xwimp_set_icon_state(qw->window, ICON_QUERY_HELP,
+ wimp_ICON_DELETED, wimp_ICON_DELETED);
+
+ if (x >= 0 && y >= 0) {
+ x -= tx - 8;
+ y += (query_template->visible.y1 - query_template->visible.y0) / 2;
+ ro_gui_dialog_open_xy(qw->window, x, y);
+ }
+ else
+ ro_gui_dialog_open(qw->window);
+
+ ro_gui_wimp_event_set_user_data(qw->window, qw);
+ ro_gui_wimp_event_register_mouse_click(qw->window, ro_gui_query_click);
+ ro_gui_wimp_event_register_cancel(qw->window, ICON_QUERY_NO);
+ ro_gui_wimp_event_register_ok(qw->window, ICON_QUERY_YES, ro_gui_query_apply);
+ ro_gui_wimp_event_register_close_window(qw->window, ro_gui_query_close);
+
+ error = xwimp_set_caret_position(qw->window, (wimp_i)-1, 0, 0, 1 << 25, -1);
+ if (error) {
+ LOG("xwimp_get_caret_position: 0x%x : %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ /* put this query window at the head of our list */
+ if (gui_query_window_list)
+ gui_query_window_list->prev = qw;
+
+ qw->prev = NULL;
+ qw->next = gui_query_window_list;
+ gui_query_window_list = qw;
+
+ return qw->id;
+}
+
+
+/**
+ * Close a query window without waiting for a response from the user.
+ * (should normally only be called if the user has responded in some other
+ * way of which the query window in unaware.)
+ *
+ * \param id id of query window to close
+ */
+
+void query_close(query_id id)
+{
+ struct gui_query_window *qw = ro_gui_query_window_lookup_id(id);
+ if (!qw)
+ return;
+ ro_gui_query_close(qw->window);
+
+}
+
+
+void ro_gui_query_window_bring_to_front(query_id id)
+{
+ struct gui_query_window *qw = ro_gui_query_window_lookup_id(id);
+ if (qw) {
+ os_error *error;
+
+ ro_gui_dialog_open(qw->window);
+
+ error = xwimp_set_caret_position(qw->window, (wimp_i)-1, 0, 0, 1 << 25, -1);
+ if (error) {
+ LOG("xwimp_get_caret_position: 0x%x : %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ }
+}
+
+
+/**
+ * Handle closing of query dialog
+ */
+void ro_gui_query_close(wimp_w w)
+{
+ struct gui_query_window *qw;
+ os_error *error;
+
+ qw = (struct gui_query_window *)ro_gui_wimp_event_get_user_data(w);
+
+ ro_gui_dialog_close(w);
+ error = xwimp_delete_window(qw->window);
+ if (error) {
+ LOG("xwimp_delete_window: 0x%x:%s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ ro_gui_wimp_event_finalise(w);
+
+ /* remove from linked-list of query windows and release memory */
+ if (qw->prev)
+ qw->prev->next = qw->next;
+ else
+ gui_query_window_list = qw->next;
+
+ if (qw->next)
+ qw->next->prev = qw->prev;
+ free(qw);
+}
+
+
+/**
+ * Handle acceptance of query dialog
+ */
+bool ro_gui_query_apply(wimp_w w)
+{
+ struct gui_query_window *qw;
+ const query_callback *cb;
+
+ qw = (struct gui_query_window *)ro_gui_wimp_event_get_user_data(w);
+ cb = qw->cb;
+ cb->confirm(qw->id, QUERY_YES, qw->pw);
+ return true;
+}
+
+
+/**
+ * Handle clicks in query dialog
+ */
+bool ro_gui_query_click(wimp_pointer *pointer)
+{
+ struct gui_query_window *qw;
+ const query_callback *cb;
+
+ qw = (struct gui_query_window *)ro_gui_wimp_event_get_user_data(pointer->w);
+ cb = qw->cb;
+
+ switch (pointer->i) {
+ case ICON_QUERY_NO:
+ cb->cancel(qw->id, QUERY_NO, qw->pw);
+ break;
+ default:
+ return false;
+ }
+ return false;
+}
diff --git a/frontends/riscos/query.h b/frontends/riscos/query.h
new file mode 100644
index 000000000..c60a8bbbb
--- /dev/null
+++ b/frontends/riscos/query.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_RISCOS_QUERY_H
+#define _NETSURF_RISCOS_QUERY_H
+
+#include <stdbool.h>
+#include "oslib/wimp.h"
+
+enum query_response {
+ QUERY_CONTINUE,
+ QUERY_YES,
+ QUERY_NO,
+ QUERY_ESCAPE
+};
+
+typedef int query_id;
+
+#define QUERY_INVALID ((query_id)-1)
+
+typedef struct
+{
+ void (*confirm)(query_id id, enum query_response res, void *pw);
+ void (*cancel)(query_id, enum query_response res, void *pw);
+} query_callback;
+
+
+query_id query_user_xy(const char *query, const char *detail,
+ const query_callback *cb, void *pw, const char *yes, const char *no,
+ int x, int y);
+void ro_gui_query_init(void);
+void ro_gui_query_window_bring_to_front(query_id id);
+
+query_id query_user(const char *query, const char *detail,
+ const query_callback *cb, void *pw, const char *yes, const char *no);
+void query_close(query_id);
+
+#endif
diff --git a/frontends/riscos/save.c b/frontends/riscos/save.c
new file mode 100644
index 000000000..27330700c
--- /dev/null
+++ b/frontends/riscos/save.c
@@ -0,0 +1,1401 @@
+/*
+ * Copyright 2004-2007 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Save dialog and drag and drop saving (implementation).
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "oslib/dragasprite.h"
+#include "oslib/osbyte.h"
+#include "oslib/osfile.h"
+#include "oslib/osmodule.h"
+#include "oslib/osspriteop.h"
+#include "oslib/wimp.h"
+#include "oslib/wimpspriteop.h"
+
+#include "utils/config.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utf8.h"
+#include "content/content.h"
+#include "content/hlcache.h"
+#include "desktop/browser.h"
+#include "desktop/hotlist.h"
+#include "desktop/global_history.h"
+#include "desktop/version.h"
+#include "desktop/save_complete.h"
+#include "desktop/save_text.h"
+#include "desktop/gui_window.h"
+#include "image/bitmap.h"
+#include "render/form.h"
+
+#include "riscos/bitmap.h"
+#include "riscos/dialog.h"
+#include "riscos/gui.h"
+#include "riscos/menus.h"
+#include "riscos/message.h"
+#include "riscos/mouse.h"
+#include "utils/nsoption.h"
+#include "riscos/query.h"
+#include "riscos/save.h"
+#include "riscos/save_draw.h"
+#include "riscos/save_pdf.h"
+#include "riscos/textselection.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/ucstables.h"
+#include "riscos/filetype.h"
+
+//typedef enum
+//{
+// QueryRsn_Quit,
+// QueryRsn_Abort,
+// QueryRsn_Overwrite
+//} query_reason;
+
+
+/**todo - much of the state information for a save should probably be moved into a structure
+ now since we could have multiple saves outstanding */
+
+static gui_save_type gui_save_current_type;
+static hlcache_handle *gui_save_content = NULL;
+static char *gui_save_selection = NULL;
+static const char *gui_save_url = NULL;
+static const char *gui_save_title = NULL;
+static int gui_save_filetype;
+static query_id gui_save_query;
+static bool gui_save_send_dataload;
+static wimp_message gui_save_message;
+static bool gui_save_close_after = true;
+
+static bool dragbox_active = false; /** in-progress Wimp_DragBox/DragASprite op */
+static bool using_dragasprite = true;
+static bool saving_from_dialog = true;
+static osspriteop_area *saveas_area = NULL;
+static wimp_w gui_save_sourcew = (wimp_w)-1;
+#define LEAFNAME_MAX 200
+static char save_leafname[LEAFNAME_MAX];
+
+/** Current save directory (updated by and used for dialog-based saving) */
+static char *save_dir = NULL;
+static size_t save_dir_len;
+
+typedef enum { LINK_ACORN, LINK_ANT, LINK_TEXT } link_format;
+
+static bool ro_gui_save_complete(hlcache_handle *h, char *path);
+static bool ro_gui_save_content(hlcache_handle *h, char *path, bool force_overwrite);
+static void ro_gui_save_done(void);
+static void ro_gui_save_bounced(wimp_message *message);
+static bool ro_gui_save_object_native(hlcache_handle *h, char *path);
+static bool ro_gui_save_link(const char *url, const char *title, link_format format, char *path);
+static void ro_gui_save_set_state(hlcache_handle *h, gui_save_type save_type,
+ const nsurl *url, char *leaf_buf, size_t leaf_len,
+ char *icon_buf, size_t icon_len);
+static void ro_gui_save_drag_end(wimp_dragged *drag, void *data);
+static bool ro_gui_save_create_thumbnail(hlcache_handle *h, const char *name);
+static void ro_gui_save_overwrite_confirmed(query_id, enum query_response res, void *p);
+static void ro_gui_save_overwrite_cancelled(query_id, enum query_response res, void *p);
+
+static const query_callback overwrite_funcs =
+{
+ ro_gui_save_overwrite_confirmed,
+ ro_gui_save_overwrite_cancelled
+};
+
+
+/** An entry in gui_save_table. */
+struct gui_save_table_entry {
+ int filetype;
+ const char *name;
+};
+
+/** Table of filetypes and default filenames. Must be in sync with
+ * gui_save_type (riscos/gui.h). A filetype of 0 indicates the content should
+ * be used.
+ */
+static const struct gui_save_table_entry gui_save_table[] = {
+ /* GUI_SAVE_SOURCE, */ { 0, "SaveSource" },
+ /* GUI_SAVE_DRAW, */ { 0xaff, "SaveDraw" },
+ /* GUI_SAVE_PDF, */ { 0xadf, "SavePDF" },
+ /* GUI_SAVE_TEXT, */ { 0xfff, "SaveText" },
+ /* GUI_SAVE_COMPLETE, */ { 0xfaf, "SaveComplete" },
+ /* GUI_SAVE_OBJECT_ORIG, */ { 0, "SaveObject" },
+ /* GUI_SAVE_OBJECT_NATIVE, */ { 0, "SaveObject" },
+ /* GUI_SAVE_LINK_URI, */ { 0xf91, "SaveLink" },
+ /* GUI_SAVE_LINK_URL, */ { 0xb28, "SaveLink" },
+ /* GUI_SAVE_LINK_TEXT, */ { 0xfff, "SaveLink" },
+ /* GUI_SAVE_HOTLIST_EXPORT_HTML, */ { 0xfaf, "Hotlist" },
+ /* GUI_SAVE_HISTORY_EXPORT_HTML, */ { 0xfaf, "History" },
+ /* GUI_SAVE_TEXT_SELECTION, */ { 0xfff, "SaveSelection" },
+};
+
+
+/**
+ * Create the saveas dialogue from the given template, and the sprite area
+ * necessary for our thumbnail (full page save)
+ *
+ * \param template_name name of template to be used
+ * \return window handle of created dialogue
+ */
+
+wimp_w ro_gui_saveas_create(const char *template_name)
+{
+ const int sprite_size = (68 * 68 * 4) + ((68 * 68) / 8); /* 32bpp with mask */
+ int area_size = sizeof(osspriteop_area) + sizeof(osspriteop_header) +
+ 256 * 8 + sprite_size;
+ void *area = NULL;
+ wimp_window *window;
+ os_error *error;
+ wimp_icon *icons;
+ wimp_w w;
+
+ window = ro_gui_dialog_load_template(template_name);
+ assert(window);
+
+ icons = window->icons;
+
+ error = xosmodule_alloc(area_size, (void **) &area);
+ if (error) {
+ LOG("xosmodule_alloc: 0x%x: %s", error->errnum, error->errmess);
+ xwimp_close_template();
+ die(error->errmess);
+ } else {
+ saveas_area = area;
+ saveas_area->size = area_size;
+ saveas_area->first = 16;
+
+ error = xosspriteop_clear_sprites(osspriteop_USER_AREA, saveas_area);
+ if (error) {
+ LOG("xosspriteop_clear_sprites: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+
+ xosmodule_free(saveas_area);
+ saveas_area = NULL;
+ }
+ }
+
+ assert((icons[ICON_SAVE_ICON].flags &
+ (wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_INDIRECTED)) ==
+ (wimp_ICON_SPRITE | wimp_ICON_INDIRECTED));
+ icons[ICON_SAVE_ICON].data.indirected_sprite.area = saveas_area;
+
+ /* create window */
+ error = xwimp_create_window(window, &w);
+ if (error) {
+ LOG("xwimp_create_window: 0x%x: %s", error->errnum, error->errmess);
+ xwimp_close_template();
+ die(error->errmess);
+ }
+
+ /* the window definition is copied by the wimp and may be freed */
+ free(window);
+
+ return w;
+}
+
+
+/**
+ * Clean-up function that releases our sprite area and memory.
+ */
+
+void ro_gui_saveas_quit(void)
+{
+ if (saveas_area) {
+ os_error *error = xosmodule_free(saveas_area);
+ if (error) {
+ LOG("xosmodule_free: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ }
+ saveas_area = NULL;
+ }
+
+ free(save_dir);
+ save_dir = NULL;
+}
+
+/**
+ * Prepares the save box to reflect gui_save_type and a content, and
+ * opens it.
+ *
+ * \param save_type type of save
+ * \param h content to save
+ * \param s selection to save
+ * \param url url to be saved (link types)
+ * \param title title (if any), when saving links
+ */
+
+void ro_gui_save_prepare(gui_save_type save_type, hlcache_handle *h,
+ char *s, const nsurl *url, const char *title)
+{
+ char name_buf[FILENAME_MAX];
+ size_t leaf_offset = 0;
+ char icon_buf[20];
+
+ assert( (save_type == GUI_SAVE_LINK_URI) ||
+ (save_type == GUI_SAVE_LINK_URL) ||
+ (save_type == GUI_SAVE_LINK_TEXT) ||
+ (save_type == GUI_SAVE_HOTLIST_EXPORT_HTML) ||
+ (save_type == GUI_SAVE_HISTORY_EXPORT_HTML) ||
+ (save_type == GUI_SAVE_TEXT_SELECTION) || h);
+
+ if (gui_save_selection == NULL)
+ free(gui_save_selection);
+
+ gui_save_selection = s;
+ if (url != NULL) {
+ gui_save_url = nsurl_access(url);
+ } else {
+ gui_save_url = NULL;
+ }
+ gui_save_title = title;
+
+ if (save_dir) {
+ leaf_offset = save_dir_len;
+ memcpy(name_buf, save_dir, leaf_offset);
+ name_buf[leaf_offset++] = '.';
+ }
+
+ if (h != NULL) {
+ url = hlcache_handle_get_url(h);
+ }
+
+ ro_gui_save_set_state(h, save_type, url,
+ name_buf + leaf_offset, FILENAME_MAX - leaf_offset,
+ icon_buf, sizeof(icon_buf));
+
+ ro_gui_set_icon_sprite(dialog_saveas, ICON_SAVE_ICON, saveas_area,
+ icon_buf);
+
+ ro_gui_set_icon_string(dialog_saveas, ICON_SAVE_PATH, name_buf, true);
+ ro_gui_wimp_event_memorise(dialog_saveas);
+}
+
+/**
+ * Starts a drag for the save dialog
+ *
+ * \param pointer mouse position info from Wimp
+ */
+void ro_gui_save_start_drag(wimp_pointer *pointer)
+{
+ if (pointer->buttons & (wimp_DRAG_SELECT | wimp_DRAG_ADJUST)) {
+ const char *sprite = ro_gui_get_icon_string(pointer->w, pointer->i);
+ int x = pointer->pos.x, y = pointer->pos.y;
+ wimp_window_state wstate;
+ wimp_icon_state istate;
+ /* start the drag from the icon's exact location, rather than the pointer */
+ istate.w = wstate.w = pointer->w;
+ istate.i = pointer->i;
+ if (!xwimp_get_window_state(&wstate) && !xwimp_get_icon_state(&istate)) {
+ x = (istate.icon.extent.x1 + istate.icon.extent.x0)/2 +
+ wstate.visible.x0 - wstate.xscroll;
+ y = (istate.icon.extent.y1 + istate.icon.extent.y0)/2 +
+ wstate.visible.y1 - wstate.yscroll;
+ }
+ ro_mouse_drag_start(ro_gui_save_drag_end, NULL, NULL, NULL);
+ gui_save_sourcew = pointer->w;
+ saving_from_dialog = true;
+ gui_save_close_after = !(pointer->buttons & wimp_DRAG_ADJUST);
+ ro_gui_drag_icon(x, y, sprite);
+ }
+}
+
+
+/**
+ * Handle OK click/keypress in the save dialog.
+ *
+ * \param w window handle of save dialog
+ * \return true on success, false on failure
+ */
+bool ro_gui_save_ok(wimp_w w)
+{
+ const char *name = ro_gui_get_icon_string(w, ICON_SAVE_PATH);
+ wimp_pointer pointer;
+ char path[256];
+
+ if (!strrchr(name, '.')) {
+ ro_warn_user("NoPathError", NULL);
+ return false;
+ }
+
+ ro_gui_convert_save_path(path, sizeof path, name);
+ gui_save_sourcew = w;
+ saving_from_dialog = true;
+ gui_save_send_dataload = false;
+ gui_save_close_after = xwimp_get_pointer_info(&pointer)
+ || !(pointer.buttons & wimp_CLICK_ADJUST);
+ memcpy(&gui_save_message.data.data_xfer.file_name, path, 1 + strlen(path));
+
+ if (ro_gui_save_content(gui_save_content, path, !nsoption_bool(confirm_overwrite))) {
+ ro_gui_save_done();
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Initiates drag saving of an object directly from a browser window
+ *
+ * \param save_type type of save
+ * \param c content to save
+ * \param g gui window
+ */
+
+void gui_drag_save_object(struct gui_window *g, hlcache_handle *c,
+ gui_save_type save_type)
+{
+ wimp_pointer pointer;
+ char icon_buf[20];
+ os_error *error;
+
+ /* Close the save window because otherwise we need two contexts
+ */
+ xwimp_create_menu(wimp_CLOSE_MENU, 0, 0);
+ ro_gui_dialog_close(dialog_saveas);
+
+ gui_save_sourcew = g->window;
+ saving_from_dialog = false;
+
+ error = xwimp_get_pointer_info(&pointer);
+ if (error) {
+ LOG("xwimp_get_pointer_info: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ ro_gui_save_set_state(c, save_type, hlcache_handle_get_url(c),
+ save_leafname, LEAFNAME_MAX,
+ icon_buf, sizeof(icon_buf));
+
+ ro_mouse_drag_start(ro_gui_save_drag_end, NULL, NULL, NULL);
+
+ ro_gui_drag_icon(pointer.pos.x, pointer.pos.y, icon_buf);
+}
+
+
+/**
+ * Initiates drag saving of a selection from a browser window
+ *
+ * \param g gui window
+ * \param selection selection object
+ */
+
+void gui_drag_save_selection(struct gui_window *g, const char *selection)
+{
+ wimp_pointer pointer;
+ char icon_buf[20];
+ os_error *error;
+
+ /* Close the save window because otherwise we need two contexts
+ */
+ xwimp_create_menu(wimp_CLOSE_MENU, 0, 0);
+ ro_gui_dialog_close(dialog_saveas);
+
+ gui_save_sourcew = g->window;
+ saving_from_dialog = false;
+
+ error = xwimp_get_pointer_info(&pointer);
+ if (error) {
+ LOG("xwimp_get_pointer_info: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+
+ if (gui_save_selection == NULL)
+ free(gui_save_selection);
+
+ if (selection == NULL)
+ gui_save_selection = strdup("");
+ else
+ gui_save_selection = strdup(selection);
+
+ ro_gui_save_set_state(NULL, GUI_SAVE_TEXT_SELECTION, NULL,
+ save_leafname, LEAFNAME_MAX,
+ icon_buf, sizeof(icon_buf));
+
+ ro_mouse_drag_start(ro_gui_save_drag_end, NULL, NULL, NULL);
+
+ ro_gui_drag_icon(pointer.pos.x, pointer.pos.y, icon_buf);
+}
+
+
+/**
+ * Initiates drag saving of a link/URL file
+ *
+ * \param save_type format in which URL should be saved
+ * \param url url to be saved
+ * \param title title to be included in URI format, if any
+ * \param g gui window to save from
+ * \
+ */
+
+void ro_gui_drag_save_link(gui_save_type save_type, const nsurl *url,
+ const char *title, struct gui_window *g)
+{
+ wimp_pointer pointer;
+ char icon_buf[20];
+ os_error *error;
+
+ /* Close the save window because otherwise we need two contexts
+ */
+ xwimp_create_menu(wimp_CLOSE_MENU, 0, 0);
+ ro_gui_dialog_close(dialog_saveas);
+
+ gui_save_url = nsurl_access(url);
+ gui_save_title = title;
+ gui_save_sourcew = g->window;
+ saving_from_dialog = false;
+
+ error = xwimp_get_pointer_info(&pointer);
+ if (error) {
+ LOG("xwimp_get_pointer_info: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ ro_gui_save_set_state(NULL, save_type, url, save_leafname,
+ LEAFNAME_MAX, icon_buf, sizeof(icon_buf));
+
+ ro_mouse_drag_start(ro_gui_save_drag_end, NULL, NULL, NULL);
+
+ ro_gui_drag_icon(pointer.pos.x, pointer.pos.y, icon_buf);
+}
+
+
+/**
+ * Start drag of icon under the pointer.
+ */
+
+void ro_gui_drag_icon(int x, int y, const char *sprite)
+{
+ os_error *error;
+ wimp_drag drag;
+ int r2;
+
+ drag.initial.x0 = x - 34;
+ drag.initial.y0 = y - 34;
+ drag.initial.x1 = x + 34;
+ drag.initial.y1 = y + 34;
+
+ if (sprite && (xosbyte2(osbyte_READ_CMOS, 28, 0, &r2) || (r2 & 2))) {
+ osspriteop_area *area = (osspriteop_area*)1;
+
+ /* first try our local sprite area in case it's a thumbnail sprite */
+ if (saveas_area) {
+ error = xosspriteop_select_sprite(osspriteop_USER_AREA,
+ saveas_area, (osspriteop_id)sprite, NULL);
+ if (error) {
+ if (error->errnum != error_SPRITE_OP_DOESNT_EXIST) {
+ LOG("xosspriteop_select_sprite: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ }
+ }
+ else
+ area = saveas_area;
+ }
+
+ error = xdragasprite_start(dragasprite_HPOS_CENTRE |
+ dragasprite_VPOS_CENTRE |
+ dragasprite_BOUND_POINTER |
+ dragasprite_DROP_SHADOW,
+ area, sprite, &drag.initial, 0);
+
+ if (!error) {
+ using_dragasprite = true;
+ dragbox_active = true;
+ return;
+ }
+
+ LOG("xdragasprite_start: 0x%x: %s", error->errnum, error->errmess);
+ }
+
+ drag.type = wimp_DRAG_USER_FIXED;
+ drag.bbox.x0 = -0x8000;
+ drag.bbox.y0 = -0x8000;
+ drag.bbox.x1 = 0x7fff;
+ drag.bbox.y1 = 0x7fff;
+
+ using_dragasprite = false;
+ error = xwimp_drag_box(&drag);
+
+ if (error) {
+ LOG("xwimp_drag_box: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("DragError", error->errmess);
+ }
+ else
+ dragbox_active = true;
+}
+
+
+/**
+ * Convert a ctrl-char terminated pathname possibly containing spaces
+ * to a NUL-terminated one containing only hard spaces.
+ *
+ * \param dp destination buffer to receive pathname
+ * \param len size of destination buffer
+ * \param p source pathname, ctrl-char terminated
+ */
+
+void ro_gui_convert_save_path(char *dp, size_t len, const char *p)
+{
+ char *ep = dp + len - 1; /* leave room for NUL */
+
+ assert(p <= dp || p > ep); /* in-situ conversion /is/ allowed */
+
+ while (dp < ep && *p >= ' ') /* ctrl-char terminated */
+ {
+ *dp++ = (*p == ' ') ? 160 : *p;
+ p++;
+ }
+ *dp = '\0';
+}
+
+
+void ro_gui_drag_box_cancel(void)
+{
+ if (dragbox_active) {
+ os_error *error;
+ if (using_dragasprite) {
+ error = xdragasprite_stop();
+ if (error) {
+ LOG("xdragasprite_stop: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ }
+ else {
+ error = xwimp_drag_box(NULL);
+ if (error) {
+ LOG("xwimp_drag_box: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ }
+ dragbox_active = false;
+ }
+}
+
+
+/**
+ * Handle User_Drag_Box event for a drag from the save dialog or browser window.
+ *
+ * \param *drag The Wimp_DragEnd data block.
+ * \param *data NULL, as function is used as a callback from ro_mouse.
+ */
+
+static void ro_gui_save_drag_end(wimp_dragged *drag, void *data)
+{
+ const char *name;
+ wimp_pointer pointer;
+ wimp_message message;
+ os_error *error;
+ char *dp, *ep;
+ char *local_name = NULL;
+
+ if (dragbox_active)
+ ro_gui_drag_box_cancel();
+
+ error = xwimp_get_pointer_info(&pointer);
+ if (error) {
+ LOG("xwimp_get_pointer_info: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* perform hit-test if the destination is the same as the source window;
+ we want to allow drag-saving from a page into the input fields within
+ the page, but avoid accidental replacements of the current page */
+ if (gui_save_sourcew != (wimp_w)-1 && pointer.w == gui_save_sourcew) {
+ int dx = (drag->final.x1 + drag->final.x0)/2;
+ int dy = (drag->final.y1 + drag->final.y0)/2;
+ struct gui_window *g;
+ bool dest_ok = false;
+ os_coord pos;
+
+ g = ro_gui_window_lookup(gui_save_sourcew);
+
+ if (g && ro_gui_window_to_window_pos(g, dx, dy, &pos)) {
+ dest_ok = browser_window_drop_file_at_point(g->bw,
+ pos.x, pos.y, NULL);
+ }
+ if (!dest_ok)
+ return;
+ }
+
+ if (!saving_from_dialog) {
+ /* saving directly from browser window, choose a
+ * name based upon the URL */
+ nserror err;
+ err = utf8_to_local_encoding(save_leafname, 0, &local_name);
+ if (err != NSERROR_OK) {
+ /* badenc should never happen */
+ assert(err != NSERROR_BAD_ENCODING);
+ local_name = NULL;
+ }
+ name = local_name ? local_name : save_leafname;
+ }
+ else {
+ char *dot;
+
+ /* saving from dialog, grab leafname from icon */
+ name = ro_gui_get_icon_string(gui_save_sourcew, ICON_SAVE_PATH);
+ dot = strrchr(name, '.');
+ if (dot)
+ name = dot + 1;
+ }
+
+ dp = message.data.data_xfer.file_name;
+ ep = dp + sizeof message.data.data_xfer.file_name;
+
+ if (gui_save_current_type == GUI_SAVE_COMPLETE) {
+ message.data.data_xfer.file_type = 0x2000;
+ if (*name != '!') *dp++ = '!';
+ } else
+ message.data.data_xfer.file_type = gui_save_filetype;
+
+ ro_gui_convert_save_path(dp, ep - dp, name);
+
+/* \todo - we're supposed to set this if drag-n-drop used */
+ message.your_ref = 0;
+
+ message.action = message_DATA_SAVE;
+ message.data.data_xfer.w = pointer.w;
+ message.data.data_xfer.i = pointer.i;
+ message.data.data_xfer.pos.x = pointer.pos.x;
+ message.data.data_xfer.pos.y = pointer.pos.y;
+ message.data.data_xfer.est_size = 1000;
+ message.size = 44 + ((strlen(message.data.data_xfer.file_name) + 4) &
+ (~3u));
+
+ ro_message_send_message_to_window(wimp_USER_MESSAGE_RECORDED, &message,
+ pointer.w, pointer.i, ro_gui_save_bounced, NULL);
+
+ gui_current_drag_type = GUI_DRAG_SAVE;
+
+ free(local_name);
+}
+
+
+
+/**
+ * Send DataSave message on behalf of clipboard code and remember that it's the
+ * clipboard contents we're being asked for when the DataSaveAck reply arrives
+ */
+
+void ro_gui_send_datasave(gui_save_type save_type,
+ wimp_full_message_data_xfer *message, wimp_t to)
+{
+ /* Close the save window because otherwise we need two contexts
+ */
+
+ ro_gui_dialog_close(dialog_saveas);
+
+ if (ro_message_send_message(wimp_USER_MESSAGE_RECORDED, (wimp_message*)message,
+ to, ro_gui_save_bounced)) {
+ gui_save_current_type = save_type;
+ gui_save_sourcew = (wimp_w)-1;
+ saving_from_dialog = false;
+
+ gui_current_drag_type = GUI_DRAG_SAVE;
+ }
+}
+
+
+/**
+ * Handle lack of Message_DataSaveAck for drags, saveas dialogs and clipboard code
+ */
+
+void ro_gui_save_bounced(wimp_message *message)
+{
+ gui_current_drag_type = GUI_DRAG_NONE;
+}
+
+
+/**
+ * Handle Message_DataSaveAck for a drag from the save dialog or browser window,
+ * or Clipboard protocol.
+ */
+
+void ro_gui_save_datasave_ack(wimp_message *message)
+{
+ char *path = message->data.data_xfer.file_name;
+ hlcache_handle *h = gui_save_content;
+ bool force_overwrite;
+
+ switch (gui_save_current_type) {
+ case GUI_SAVE_LINK_URI:
+ case GUI_SAVE_LINK_URL:
+ case GUI_SAVE_LINK_TEXT:
+ case GUI_SAVE_HOTLIST_EXPORT_HTML:
+ case GUI_SAVE_HISTORY_EXPORT_HTML:
+ case GUI_SAVE_TEXT_SELECTION:
+ case GUI_SAVE_CLIPBOARD_CONTENTS:
+ break;
+
+ default:
+ if (!gui_save_content) {
+ LOG("unexpected DataSaveAck: gui_save_content not set");
+ return;
+ }
+ break;
+ }
+
+ if (saving_from_dialog)
+ ro_gui_set_icon_string(gui_save_sourcew, ICON_SAVE_PATH,
+ path, true);
+
+ gui_save_send_dataload = true;
+ memcpy(&gui_save_message, message, sizeof(gui_save_message));
+
+ /* if saving/pasting to another application, don't request user
+ confirmation; a ScrapFile almost certainly exists already */
+ if (message->data.data_xfer.est_size == -1)
+ force_overwrite = true;
+ else
+ force_overwrite = !nsoption_bool(confirm_overwrite);
+
+ if (ro_gui_save_content(h, path, force_overwrite))
+ ro_gui_save_done();
+}
+
+
+
+/**
+ * Does the actual saving
+ *
+ * \param h handle to content to save (or NULL for other)
+ * \param path path to save as
+ * \param force_overwrite true iff required to overwrite without prompting
+ * \return true on success,
+ * false on (i) error and error reported
+ * or (ii) deferred awaiting user confirmation
+ */
+
+bool ro_gui_save_content(hlcache_handle *h, char *path, bool force_overwrite)
+{
+ os_error *error;
+ const char *source_data;
+ unsigned long source_size;
+
+ /* does the user want to check for collisions when saving? */
+ if (!force_overwrite) {
+ fileswitch_object_type obj_type;
+ /* check whether the destination file/dir already exists */
+ error = xosfile_read_stamped(path, &obj_type,
+ NULL, NULL, NULL, NULL, NULL);
+ if (error) {
+ LOG("xosfile_read_stamped: 0x%x:%s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ return false;
+ }
+
+ switch (obj_type) {
+ case osfile_NOT_FOUND:
+ break;
+
+ case osfile_IS_FILE:
+ gui_save_query = query_user("OverwriteFile", NULL, &overwrite_funcs, NULL,
+ messages_get("Replace"), messages_get("DontReplace"));
+// gui_save_query_rsn = QueryRsn_Overwrite;
+ return false;
+
+ default:
+ error = xosfile_make_error(path, obj_type);
+ assert(error);
+ ro_warn_user("SaveError", error->errmess);
+ return false;
+ }
+ }
+
+ switch (gui_save_current_type) {
+#ifdef WITH_DRAW_EXPORT
+ case GUI_SAVE_DRAW:
+ return save_as_draw(h, path);
+#endif
+#ifdef WITH_PDF_EXPORT
+ case GUI_SAVE_PDF:
+ return save_as_pdf(h, path);
+#endif
+ case GUI_SAVE_TEXT:
+ save_as_text(h, path);
+ xosfile_set_type(path, 0xfff);
+ break;
+ case GUI_SAVE_COMPLETE:
+ assert(h);
+ if (content_get_type(h) == CONTENT_HTML) {
+ if (strcmp(path, "<Wimp$Scrap>"))
+ return ro_gui_save_complete(h, path);
+
+ /* we can't send a whole directory to another
+ * application, so just send the HTML source */
+ gui_save_current_type = GUI_SAVE_SOURCE;
+ }
+ else
+ gui_save_current_type = GUI_SAVE_OBJECT_ORIG; /* \todo do this earlier? */
+ /* no break */
+ case GUI_SAVE_SOURCE:
+ case GUI_SAVE_OBJECT_ORIG:
+ source_data = content_get_source_data(h, &source_size);
+ error = xosfile_save_stamped(path,
+ ro_content_filetype(h),
+ (byte *) source_data,
+ (byte *) source_data + source_size);
+ if (error) {
+ LOG("xosfile_save_stamped: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ return false;
+ }
+ break;
+
+ case GUI_SAVE_OBJECT_NATIVE:
+ return ro_gui_save_object_native(h, path);
+
+ case GUI_SAVE_LINK_URI:
+ return ro_gui_save_link(gui_save_url, gui_save_title,
+ LINK_ACORN, path);
+
+ case GUI_SAVE_LINK_URL:
+ return ro_gui_save_link(gui_save_url, gui_save_title,
+ LINK_ANT, path);
+
+ case GUI_SAVE_LINK_TEXT:
+ return ro_gui_save_link(gui_save_url, gui_save_title,
+ LINK_TEXT, path);
+
+ case GUI_SAVE_HOTLIST_EXPORT_HTML:
+ if (hotlist_export(path, NULL) != NSERROR_OK)
+ return false;
+ error = xosfile_set_type(path, 0xfaf);
+ if (error)
+ LOG("xosfile_set_type: 0x%x: %s", error->errnum, error->errmess);
+ break;
+ case GUI_SAVE_HISTORY_EXPORT_HTML:
+ if (global_history_export(path, NULL) != NSERROR_OK)
+ return false;
+ error = xosfile_set_type(path, 0xfaf);
+ if (error)
+ LOG("xosfile_set_type: 0x%x: %s", error->errnum, error->errmess);
+ break;
+
+ case GUI_SAVE_TEXT_SELECTION:
+ if (gui_save_selection == NULL)
+ return false;
+ if (!utf8_save_text(gui_save_selection, path)) {
+ free(gui_save_selection);
+ gui_save_selection = NULL;
+ return false;
+ }
+ free(gui_save_selection);
+ gui_save_selection = NULL;
+ xosfile_set_type(path, 0xfff);
+ break;
+
+ case GUI_SAVE_CLIPBOARD_CONTENTS:
+ return ro_gui_save_clipboard(path);
+
+ default:
+ LOG("Unexpected content type: %d, path %s", gui_save_current_type, path);
+ return false;
+ }
+ return true;
+}
+
+
+/**
+ * Save completed, inform recipient and close our 'save as' dialog.
+ */
+
+void ro_gui_save_done(void)
+{
+ os_error *error;
+
+ if (gui_save_send_dataload) {
+ /* Ack successful save with message_DATA_LOAD */
+ wimp_message *message = &gui_save_message;
+ message->action = message_DATA_LOAD;
+ message->your_ref = message->my_ref;
+ error = xwimp_send_message(wimp_USER_MESSAGE, message,
+ message->sender);
+ if (error) {
+ LOG("xwimp_send_message: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ }
+ }
+
+ if (saving_from_dialog) {
+ /* remember the save directory if saving to the Filer */
+ if (!gui_save_send_dataload ||
+ gui_save_message.data.data_xfer.est_size != -1) {
+ char *sp = gui_save_message.data.data_xfer.file_name;
+ char *ep = sp + sizeof(gui_save_message.data.data_xfer.file_name);
+ char *lastdot = NULL;
+ char *p = sp;
+
+ while (p < ep && *p >= 0x20) {
+ if (*p == '.') {
+ /* don't remember the directory if it's a temporary file */
+ if (!lastdot && p == sp + 12 &&
+ !memcmp(sp, "<Wimp$Scrap>", 12)) break;
+ lastdot = p;
+ }
+ p++;
+ }
+ if (lastdot) {
+ /* remember the directory */
+ char *new_dir = realloc(save_dir, (lastdot+1)-sp);
+ if (new_dir) {
+ save_dir_len = lastdot - sp;
+ memcpy(new_dir, sp, save_dir_len);
+ new_dir[save_dir_len] = '\0';
+ save_dir = new_dir;
+ }
+ }
+ }
+
+ if (gui_save_close_after) {
+ /* Close the save window */
+ ro_gui_dialog_close(dialog_saveas);
+ error = xwimp_create_menu(wimp_CLOSE_MENU, 0, 0);
+ if (error) {
+ LOG("xwimp_create_menu: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MenuError", error->errmess);
+ }
+ }
+ }
+
+ if (!saving_from_dialog || gui_save_close_after)
+ gui_save_content = 0;
+}
+
+static void ro_gui_save_set_file_type(const char *path, lwc_string *mime_type)
+{
+ int rotype = ro_content_filetype_from_mime_type(mime_type);
+ os_error *error;
+
+ error = xosfile_set_type(path, rotype);
+ if (error != NULL) {
+ LOG("xosfile_set_type: 0x%x: %s", error->errnum, error->errmess);
+ }
+}
+
+/**
+ * Prepare an application directory and save_complete() to it.
+ *
+ * \param h content of type CONTENT_HTML to save
+ * \param path path to save as
+ * \return true on success, false on error and error reported
+ */
+
+bool ro_gui_save_complete(hlcache_handle *h, char *path)
+{
+ void *spr = ((byte *) saveas_area) + saveas_area->first;
+ osspriteop_header *sprite = (osspriteop_header *) spr;
+ char name[12];
+ char buf[256];
+ FILE *fp;
+ os_error *error;
+ size_t len;
+ char *dot;
+ int i;
+
+ /* Create dir */
+ error = xosfile_create_dir(path, 0);
+ if (error) {
+ LOG("xosfile_create_dir: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ return false;
+ }
+
+ /* Save !Run file */
+ snprintf(buf, sizeof buf, "%s.!Run", path);
+ fp = fopen(buf, "w");
+ if (!fp) {
+ LOG("fopen(): errno = %i", errno);
+ ro_warn_user("SaveError", strerror(errno));
+ return false;
+ }
+ fprintf(fp, "IconSprites <Obey$Dir>.!Sprites\n");
+ fprintf(fp, "Filer_Run <Obey$Dir>.index\n");
+ fclose(fp);
+ error = xosfile_set_type(buf, 0xfeb);
+ if (error) {
+ LOG("xosfile_set_type: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ return false;
+ }
+
+ /* create an empty !Runimage so the date gets correctly set */
+ snprintf(buf, sizeof buf, "%s.!RunImage", path);
+ fp = fopen(buf, "w");
+ if (!fp) {
+ LOG("Creating !RunImage failed: errno = %i", errno);
+ } else {
+ fclose(fp);
+ }
+
+ /* Make sure the sprite name matches the directory name, because
+ the user may have renamed the directory since we created the
+ thumbnail sprite */
+
+ dot = strrchr(path, '.');
+ if (dot) dot++; else dot = path;
+ len = strlen(dot);
+ if (len >= 12) len = 12;
+
+ memcpy(name, sprite->name, 12); /* remember original name */
+ memcpy(sprite->name, dot, len);
+ memset(sprite->name + len, 0, 12 - len);
+ for (i = 0; i < 12; i++) /* convert to lower case */
+ if (sprite->name[i] != '\0')
+ sprite->name[i] = tolower(sprite->name[i]);
+
+ /* Create !Sprites */
+ snprintf(buf, sizeof buf, "%s.!Sprites", path);
+
+ error = xosspriteop_save_sprite_file(osspriteop_NAME, saveas_area, buf);
+ if (error) {
+ LOG("xosspriteop_save_sprite_file: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ return false;
+ }
+
+ /* restore sprite name in case the save fails and we need to try again */
+ memcpy(sprite->name, name, 12);
+
+ /* save URL file with original URL */
+ snprintf(buf, sizeof buf, "%s.URL", path);
+ if (!ro_gui_save_link(nsurl_access(hlcache_handle_get_url(h)),
+ content_get_title(h), LINK_ANT, buf))
+ return false;
+
+ return save_complete(h, path, ro_gui_save_set_file_type);
+}
+
+bool ro_gui_save_object_native(hlcache_handle *h, char *path)
+{
+ int file_type = ro_content_filetype(h);
+
+ if (file_type == osfile_TYPE_SPRITE || file_type == osfile_TYPE_DRAW) {
+ /* Native sprite or drawfile */
+ const char *source_data;
+ unsigned long source_size;
+ os_error *error;
+
+ source_data = content_get_source_data(h, &source_size);
+ error = xosfile_save_stamped(path, file_type,
+ (byte *) source_data,
+ (byte *) source_data + source_size);
+ if (error != NULL) {
+ LOG("xosfile_save_stamped: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ return false;
+ }
+ } else {
+ /* Non-native type: export */
+ switch (ro_content_native_type(h)) {
+ case osfile_TYPE_SPRITE:
+ {
+ unsigned flags = (os_version == 0xA9) ?
+ BITMAP_SAVE_FULL_ALPHA : 0;
+ riscos_bitmap_save(content_get_bitmap(h), path, flags);
+ }
+ break;
+ case osfile_TYPE_DRAW:
+ /* Must be SVG */
+ return save_as_draw(h, path);
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Save a link file.
+ *
+ * \param url url to be saved
+ * \param title corresponding title, if any
+ * \param format format of link file
+ * \param path pathname for link file
+ * \return true on success, false on failure and reports the error
+ */
+
+bool ro_gui_save_link(const char *url, const char *title, link_format format,
+ char *path)
+{
+ FILE *fp = fopen(path, "w");
+
+ if (!fp) {
+ ro_warn_user("SaveError", strerror(errno));
+ return false;
+ }
+
+ switch (format) {
+ case LINK_ACORN: /* URI */
+ fprintf(fp, "%s\t%s\n", "URI", "100");
+ fprintf(fp, "\t# NetSurf %s\n\n", netsurf_version);
+ fprintf(fp, "\t%s\n", url);
+ if (title)
+ fprintf(fp, "\t%s\n", title);
+ else
+ fprintf(fp, "\t*\n");
+ break;
+ case LINK_ANT: /* URL */
+ case LINK_TEXT: /* Text */
+ fprintf(fp, "%s\n", url);
+ break;
+ }
+
+ fclose(fp);
+
+ switch (format) {
+ case LINK_ACORN: /* URI */
+ xosfile_set_type(path, 0xf91);
+ break;
+ case LINK_ANT: /* URL */
+ xosfile_set_type(path, 0xb28);
+ break;
+ case LINK_TEXT: /* Text */
+ xosfile_set_type(path, 0xfff);
+ break;
+ }
+
+ return true;
+}
+
+
+/**
+ * Suggest a leafname and sprite name for the given content.
+ *
+ * \param h content being saved
+ * \param save_type type of save operation being performed
+ * \param url used to determine leafname
+ * \param leaf_buf buffer to receive suggested leafname.
+ * \param leaf_len size of buffer to receive suggested leafname.
+ * \param icon_buf buffer to receive sprite name.
+ * \param icon_len size of buffer to receive icon name.
+ */
+
+void ro_gui_save_set_state(hlcache_handle *h, gui_save_type save_type,
+ const nsurl *url, char *leaf_buf, size_t leaf_len,
+ char *icon_buf, size_t icon_len)
+{
+ /* filename */
+ const char *name = gui_save_table[save_type].name;
+ bool done = false;
+ char *nice = NULL;
+ nserror err;
+ char *local_name;
+
+ assert(icon_len >= 13);
+
+ /* parameters that we need to remember */
+ gui_save_current_type = save_type;
+ gui_save_content = h;
+
+ /* suggest a filetype based upon the content */
+ gui_save_filetype = gui_save_table[save_type].filetype;
+ if (!gui_save_filetype && h) {
+ if (save_type == GUI_SAVE_OBJECT_NATIVE) {
+ switch (ro_content_native_type(h)) {
+ case osfile_TYPE_SPRITE:
+ gui_save_filetype = osfile_TYPE_SPRITE;
+ break;
+ case osfile_TYPE_DRAW:
+ gui_save_filetype = osfile_TYPE_DRAW;
+ break;
+ default:
+ break;
+ }
+ }
+ if (!gui_save_filetype)
+ gui_save_filetype = ro_content_filetype(h);
+ }
+
+ /* leafname */
+ if ((url != NULL) &&
+ (nsurl_nice(url, &nice, nsoption_bool(strip_extensions)) ==
+ NSERROR_OK)) {
+ size_t i;
+ for (i = 0; nice[i]; i++) {
+ if (nice[i] == '.')
+ nice[i] = '/';
+ else if (nice[i] <= ' ' ||
+ strchr(":*#$&@^%\\", nice[i]))
+ nice[i] = '_';
+ }
+ name = nice;
+ } else {
+ name = messages_get(name);
+ }
+
+ /* filename is utf8 */
+ if (save_type == GUI_SAVE_COMPLETE && leaf_len > 0) {
+ leaf_buf[0] = '!';
+ leaf_buf++;
+ leaf_len--;
+ }
+ strncpy(leaf_buf, name, leaf_len);
+ leaf_buf[leaf_len - 1] = 0;
+
+ err = utf8_to_local_encoding(name, 0, &local_name);
+ if (err != NSERROR_OK) {
+ /* badenc should never happen */
+ assert(err != NSERROR_BAD_ENCODING);
+ local_name = NULL;
+ }
+
+ if (local_name != NULL)
+ name = local_name;
+
+ /* sprite name used for icon and dragging */
+ if (save_type == GUI_SAVE_COMPLETE) {
+ int index;
+
+ /* Paint gets confused with uppercase characters and we need to
+ convert spaces to hard spaces */
+ icon_buf[0] = '!';
+ for (index = 0; index < 11 && name[index]; ) {
+ char ch = name[index];
+ if (ch == ' ')
+ icon_buf[++index] = 0xa0;
+ else
+ icon_buf[++index] = tolower(ch);
+ }
+ memset(&icon_buf[index + 1], 0, 11 - index);
+ icon_buf[12] = '\0';
+
+ if (ro_gui_save_create_thumbnail(h, icon_buf))
+ done = true;
+ }
+
+ if (!done) {
+ osspriteop_header *sprite;
+ os_error *error;
+
+ sprintf(icon_buf, "file_%.3x", gui_save_filetype);
+
+ error = ro_gui_wimp_get_sprite(icon_buf, &sprite);
+ if (error && error->errnum == error_SPRITE_OP_DOESNT_EXIST) {
+ /* try the 'unknown' filetype sprite as a fallback */
+ memcpy(icon_buf, "file_xxx", 9);
+ error = ro_gui_wimp_get_sprite(icon_buf, &sprite);
+ }
+
+ if (error) {
+ LOG("ro_gui_wimp_get_sprite: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ } else {
+ /* the sprite area should always be large enough for
+ * file_xxx sprites */
+ assert(sprite->size <= saveas_area->size -
+ saveas_area->first);
+
+ memcpy((byte*)saveas_area + saveas_area->first,
+ sprite,
+ sprite->size);
+
+ saveas_area->sprite_count = 1;
+ saveas_area->used = saveas_area->first + sprite->size;
+ }
+ }
+
+ free(local_name);
+ free(nice);
+}
+
+
+
+/**
+ * Create a thumbnail sprite for the page being saved.
+ *
+ * \param h content to be converted
+ * \param name sprite name to use
+ * \return true iff successful
+ */
+
+bool ro_gui_save_create_thumbnail(hlcache_handle *h, const char *name)
+{
+ osspriteop_header *sprite_header;
+ struct bitmap *bitmap;
+ osspriteop_area *area;
+
+ bitmap = riscos_bitmap_create(34, 34, BITMAP_NEW | BITMAP_OPAQUE | BITMAP_CLEAR_MEMORY);
+ if (!bitmap) {
+ LOG("Thumbnail initialisation failed.");
+ return false;
+ }
+ riscos_bitmap_render(bitmap, h);
+ area = riscos_bitmap_convert_8bpp(bitmap);
+ riscos_bitmap_destroy(bitmap);
+ if (!area) {
+ LOG("Thumbnail conversion failed.");
+ return false;
+ }
+
+ sprite_header = (osspriteop_header *)(area + 1);
+ strncpy(sprite_header->name, name, 12);
+
+
+ /* we can't resize the saveas sprite area because it may move and we have
+ no elegant way to update the window definition on all OS versions */
+ assert(sprite_header->size <= saveas_area->size - saveas_area->first);
+
+ memcpy((byte*)saveas_area + saveas_area->first,
+ sprite_header, sprite_header->size);
+
+ saveas_area->sprite_count = 1;
+ saveas_area->used = saveas_area->first + sprite_header->size;
+
+ free(area);
+
+ return true;
+}
+
+
+/**
+ * User has opted not to overwrite the existing file.
+ */
+
+void ro_gui_save_overwrite_cancelled(query_id id, enum query_response res, void *p)
+{
+ if (!saving_from_dialog) {
+// ro_gui_save_prepare(gui_save_current_type, gui_save_content);
+// ro_gui_dialog_open_persistent(g->window, dialog_saveas, true);
+ }
+}
+
+
+/**
+ * Overwrite of existing file confirmed, proceed with the save.
+ */
+
+void ro_gui_save_overwrite_confirmed(query_id id, enum query_response res, void *p)
+{
+ if (ro_gui_save_content(gui_save_content, gui_save_message.data.data_xfer.file_name, true))
+ ro_gui_save_done();
+}
diff --git a/frontends/riscos/save.h b/frontends/riscos/save.h
new file mode 100644
index 000000000..dba09a984
--- /dev/null
+++ b/frontends/riscos/save.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2006 Adrian Lees <adrianl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * File/object/selection saving (Interface).
+ */
+
+#ifndef _NETSURF_RISCOS_SAVE_H_
+#define _NETSURF_RISCOS_SAVE_H_
+
+#include <stdbool.h>
+#include "oslib/wimp.h"
+
+enum gui_save_type;
+struct nsurl;
+
+void gui_drag_save_object(struct gui_window *g, struct hlcache_handle *c, enum gui_save_type save_type);
+void gui_drag_save_selection(struct gui_window *g, const char *selection);
+
+wimp_w ro_gui_saveas_create(const char *template_name);
+void ro_gui_saveas_quit(void);
+void ro_gui_save_prepare(enum gui_save_type save_type, struct hlcache_handle *h,
+ char *s, const struct nsurl *url,
+ const char *title);
+void ro_gui_save_start_drag(wimp_pointer *pointer);
+void ro_gui_drag_save_link(enum gui_save_type save_type, const struct nsurl *url,
+ const char *title, struct gui_window *g);
+void ro_gui_drag_icon(int x, int y, const char *sprite);
+void ro_gui_drag_box_cancel(void);
+void ro_gui_send_datasave(enum gui_save_type save_type, wimp_full_message_data_xfer *message, wimp_t to);
+void ro_gui_save_datasave_ack(wimp_message *message);
+bool ro_gui_save_ok(wimp_w w);
+void ro_gui_convert_save_path(char *dp, size_t len, const char *p);
+
+#endif
diff --git a/frontends/riscos/save_draw.c b/frontends/riscos/save_draw.c
new file mode 100644
index 000000000..50febf3b2
--- /dev/null
+++ b/frontends/riscos/save_draw.c
@@ -0,0 +1,474 @@
+/*
+ * Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk>
+ * Copyright 2004-2008 John Tytgat <joty@netsurf-browser.org>
+ * Copyright 2007 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Export a content as a DrawFile (implementation).
+ */
+
+#ifdef WITH_DRAW_EXPORT
+
+#include <assert.h>
+#include <limits.h>
+#include <oslib/draw.h>
+#include <oslib/osfile.h>
+#include <pencil.h>
+
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "content/content.h"
+#include "content/hlcache.h"
+#include "desktop/plotters.h"
+
+#include "riscos/bitmap.h"
+#include "riscos/gui.h"
+#include "riscos/save_draw.h"
+#include "riscos/font.h"
+
+static bool ro_save_draw_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style);
+static bool ro_save_draw_line(int x0, int y0, int x1, int y1, const plot_style_t *style);
+static bool ro_save_draw_polygon(const int *p, unsigned int n, const plot_style_t *style);
+static bool ro_save_draw_path(const float *p, unsigned int n, colour fill,
+ float width, colour c, const float transform[6]);
+static bool ro_save_draw_clip(const struct rect *clip);
+static bool ro_save_draw_text(int x, int y, const char *text, size_t length,
+ const plot_font_style_t *fstyle);
+static bool ro_save_draw_disc(int x, int y, int radius, const plot_style_t *style);
+static bool ro_save_draw_arc(int x, int y, int radius, int angle1, int angle2,
+ const plot_style_t *style);
+static bool ro_save_draw_bitmap(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg, bitmap_flags_t flags);
+static bool ro_save_draw_group_start(const char *name);
+static bool ro_save_draw_group_end(void);
+static bool ro_save_draw_error(pencil_code code);
+
+
+static const struct plotter_table ro_save_draw_plotters = {
+ .rectangle = ro_save_draw_rectangle,
+ .line = ro_save_draw_line,
+ .polygon = ro_save_draw_polygon,
+ .clip = ro_save_draw_clip,
+ .text = ro_save_draw_text,
+ .disc = ro_save_draw_disc,
+ .arc = ro_save_draw_arc,
+ .bitmap = ro_save_draw_bitmap,
+ .group_start = ro_save_draw_group_start,
+ .group_end = ro_save_draw_group_end,
+ .path = ro_save_draw_path,
+ .option_knockout = false,
+};
+
+static struct pencil_diagram *ro_save_draw_diagram;
+static int ro_save_draw_width;
+static int ro_save_draw_height;
+
+
+/**
+ * Export a content as a DrawFile.
+ *
+ * \param h content to export
+ * \param path path to save DrawFile as
+ * \return true on success, false on error and error reported
+ */
+
+bool save_as_draw(hlcache_handle *h, const char *path)
+{
+ pencil_code code;
+ char *drawfile_buffer;
+ struct rect clip;
+ struct content_redraw_data data;
+ size_t drawfile_size;
+ os_error *error;
+ struct redraw_context ctx = {
+ .interactive = false,
+ .background_images = true,
+ .plot = &ro_save_draw_plotters
+ };
+
+ ro_save_draw_diagram = pencil_create();
+ if (!ro_save_draw_diagram) {
+ ro_warn_user("NoMemory", 0);
+ return false;
+ }
+
+ ro_save_draw_width = content_get_width(h);
+ ro_save_draw_height = content_get_height(h);
+
+ clip.x0 = clip.y0 = INT_MIN;
+ clip.x1 = clip.y1 = INT_MAX;
+
+ data.x = 0;
+ data.y = -ro_save_draw_height;
+ data.width = ro_save_draw_width;
+ data.height = ro_save_draw_height;
+ data.background_colour = 0xFFFFFF;
+ data.scale = 1;
+ data.repeat_x = false;
+ data.repeat_y = false;
+
+ if (!content_redraw(h, &data, &clip, &ctx)) {
+ pencil_free(ro_save_draw_diagram);
+ return false;
+ }
+
+ /*pencil_dump(ro_save_draw_diagram);*/
+
+ code = pencil_save_drawfile(ro_save_draw_diagram, "NetSurf",
+ &drawfile_buffer, &drawfile_size);
+ if (code != pencil_OK) {
+ ro_warn_user("SaveError", 0);
+ pencil_free(ro_save_draw_diagram);
+ return false;
+ }
+ assert(drawfile_buffer);
+
+ error = xosfile_save_stamped(path, osfile_TYPE_DRAW,
+ (byte *) drawfile_buffer,
+ (byte *) drawfile_buffer + drawfile_size);
+ if (error) {
+ LOG("xosfile_save_stamped failed: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ pencil_free(ro_save_draw_diagram);
+ return false;
+ }
+
+ pencil_free(ro_save_draw_diagram);
+
+ return true;
+}
+
+bool ro_save_draw_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ pencil_code code;
+ const int path[] = { draw_MOVE_TO, x0 * 2, -y0 * 2 - 1,
+ draw_LINE_TO, x1 * 2, -y0 * 2 - 1,
+ draw_LINE_TO, x1 * 2, -y1 * 2 - 1,
+ draw_LINE_TO, x0 * 2, -y1 * 2 - 1,
+ draw_CLOSE_LINE,
+ draw_END_PATH };
+
+ if (style->fill_type != PLOT_OP_TYPE_NONE) {
+
+ code = pencil_path(ro_save_draw_diagram,
+ path,
+ sizeof path / sizeof path[0],
+ style->fill_colour << 8,
+ pencil_TRANSPARENT,
+ 0,
+ pencil_JOIN_MITRED,
+ pencil_CAP_BUTT,
+ pencil_CAP_BUTT,
+ 0,
+ 0,
+ false,
+ pencil_SOLID);
+ if (code != pencil_OK)
+ return ro_save_draw_error(code);
+ }
+
+ if (style->stroke_type != PLOT_OP_TYPE_NONE) {
+
+ code = pencil_path(ro_save_draw_diagram,
+ path,
+ sizeof path / sizeof path[0],
+ pencil_TRANSPARENT,
+ style->stroke_colour << 8,
+ style->stroke_width,
+ pencil_JOIN_MITRED,
+ pencil_CAP_BUTT,
+ pencil_CAP_BUTT,
+ 0,
+ 0,
+ false,
+ pencil_SOLID);
+
+ if (code != pencil_OK)
+ return ro_save_draw_error(code);
+ }
+ return true;
+}
+
+
+bool ro_save_draw_line(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ pencil_code code;
+ const int path[] = { draw_MOVE_TO, x0 * 2, -y0 * 2 - 1,
+ draw_LINE_TO, x1 * 2, -y1 * 2 - 1,
+ draw_END_PATH };
+
+ code = pencil_path(ro_save_draw_diagram,
+ path,
+ sizeof path / sizeof path[0],
+ pencil_TRANSPARENT,
+ style->stroke_colour << 8,
+ style->stroke_width,
+ pencil_JOIN_MITRED,
+ pencil_CAP_BUTT,
+ pencil_CAP_BUTT,
+ 0, 0, false,
+ pencil_SOLID);
+ if (code != pencil_OK)
+ return ro_save_draw_error(code);
+
+ return true;
+}
+
+
+bool ro_save_draw_polygon(const int *p, unsigned int n, const plot_style_t *style)
+{
+ pencil_code code;
+ int path[n * 3 + 1];
+ unsigned int i;
+
+ for (i = 0; i != n; i++) {
+ path[i * 3 + 0] = draw_LINE_TO;
+ path[i * 3 + 1] = p[i * 2 + 0] * 2;
+ path[i * 3 + 2] = -p[i * 2 + 1] * 2;
+ }
+ path[0] = draw_MOVE_TO;
+ path[n * 3] = draw_END_PATH;
+
+ code = pencil_path(ro_save_draw_diagram,
+ path, n * 3 + 1,
+ style->fill_colour << 8,
+ pencil_TRANSPARENT,
+ 0,
+ pencil_JOIN_MITRED,
+ pencil_CAP_BUTT,
+ pencil_CAP_BUTT,
+ 0,
+ 0,
+ false,
+ pencil_SOLID);
+ if (code != pencil_OK)
+ return ro_save_draw_error(code);
+
+ return true;
+}
+
+
+bool ro_save_draw_path(const float *p, unsigned int n, colour fill,
+ float width, colour c, const float transform[6])
+{
+ if (n == 0)
+ return true;
+
+ if (p[0] != PLOTTER_PATH_MOVE) {
+ LOG("path doesn't start with a move");
+ return false;
+ }
+
+ int *path = malloc(sizeof *path * (n + 10));
+ if (!path) {
+ LOG("out of memory");
+ return false;
+ }
+
+ unsigned int i;
+ bool empty_path = true;
+ for (i = 0; i < n; ) {
+ if (p[i] == PLOTTER_PATH_MOVE) {
+ path[i] = draw_MOVE_TO;
+ path[i + 1] = (transform[0] * p[i + 1] +
+ transform[2] * -p[i + 2] +
+ transform[4]) * 2;
+ path[i + 2] = (transform[1] * p[i + 1] +
+ transform[3] * -p[i + 2] +
+ -transform[5]) * 2;
+ i += 3;
+ } else if (p[i] == PLOTTER_PATH_CLOSE) {
+ path[i] = draw_CLOSE_LINE;
+ i++;
+ } else if (p[i] == PLOTTER_PATH_LINE) {
+ path[i] = draw_LINE_TO;
+ path[i + 1] = (transform[0] * p[i + 1] +
+ transform[2] * -p[i + 2] +
+ transform[4]) * 2;
+ path[i + 2] = (transform[1] * p[i + 1] +
+ transform[3] * -p[i + 2] +
+ -transform[5]) * 2;
+ i += 3;
+ empty_path = false;
+ } else if (p[i] == PLOTTER_PATH_BEZIER) {
+ path[i] = draw_BEZIER_TO;
+ path[i + 1] = (transform[0] * p[i + 1] +
+ transform[2] * -p[i + 2] +
+ transform[4]) * 2;
+ path[i + 2] = (transform[1] * p[i + 1] +
+ transform[3] * -p[i + 2] +
+ -transform[5]) * 2;
+ path[i + 3] = (transform[0] * p[i + 3] +
+ transform[2] * -p[i + 4] +
+ transform[4]) * 2;
+ path[i + 4] = (transform[1] * p[i + 3] +
+ transform[3] * -p[i + 4] +
+ -transform[5]) * 2;
+ path[i + 5] = (transform[0] * p[i + 5] +
+ transform[2] * -p[i + 6] +
+ transform[4]) * 2;
+ path[i + 6] = (transform[1] * p[i + 5] +
+ transform[3] * -p[i + 6] +
+ -transform[5]) * 2;
+ i += 7;
+ empty_path = false;
+ } else {
+ LOG("bad path command %f", p[i]);
+ free(path);
+ return false;
+ }
+ }
+ path[i] = draw_END_PATH;
+
+ if (empty_path) {
+ free(path);
+ return true;
+ }
+
+ pencil_code code = pencil_path(ro_save_draw_diagram, path, i + 1,
+ fill == NS_TRANSPARENT ? pencil_TRANSPARENT : fill << 8,
+ c == NS_TRANSPARENT ? pencil_TRANSPARENT : c << 8,
+ width, pencil_JOIN_MITRED,
+ pencil_CAP_BUTT, pencil_CAP_BUTT, 0, 0, false,
+ pencil_SOLID);
+ free(path);
+ if (code != pencil_OK)
+ return ro_save_draw_error(code);
+
+ return true;
+}
+
+
+
+
+bool ro_save_draw_clip(const struct rect *clip)
+{
+ return true;
+}
+
+
+bool ro_save_draw_text(int x, int y, const char *text, size_t length,
+ const plot_font_style_t *fstyle)
+{
+ pencil_code code;
+ const char *font_family;
+ unsigned int font_size;
+ rufl_style font_style;
+
+ nsfont_read_style(fstyle, &font_family, &font_size, &font_style);
+
+ code = pencil_text(ro_save_draw_diagram, x * 2, -y * 2, font_family,
+ font_style, font_size, text, length,
+ fstyle->foreground << 8);
+ if (code != pencil_OK)
+ return ro_save_draw_error(code);
+
+ return true;
+}
+
+
+bool ro_save_draw_disc(int x, int y, int radius, const plot_style_t *style)
+{
+ return true;
+}
+
+bool ro_save_draw_arc(int x, int y, int radius, int angle1, int angle2,
+ const plot_style_t *style)
+{
+ return true;
+}
+
+bool ro_save_draw_bitmap(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg, bitmap_flags_t flags)
+{
+ pencil_code code;
+ const uint8_t *buffer;
+
+ buffer = riscos_bitmap_get_buffer(bitmap);
+ if (!buffer) {
+ ro_warn_user("NoMemory", 0);
+ return false;
+ }
+
+ code = pencil_sprite(ro_save_draw_diagram, x * 2, (-y - height) * 2,
+ width * 2, height * 2,
+ ((char *) bitmap->sprite_area) +
+ bitmap->sprite_area->first);
+ if (code != pencil_OK)
+ return ro_save_draw_error(code);
+
+ return true;
+}
+
+
+bool ro_save_draw_group_start(const char *name)
+{
+ pencil_code code;
+
+ code = pencil_group_start(ro_save_draw_diagram, name);
+ if (code != pencil_OK)
+ return ro_save_draw_error(code);
+
+ return true;
+}
+
+
+bool ro_save_draw_group_end(void)
+{
+ pencil_code code;
+
+ code = pencil_group_end(ro_save_draw_diagram);
+ if (code != pencil_OK)
+ return ro_save_draw_error(code);
+
+ return true;
+}
+
+
+/**
+ * Report an error from pencil.
+ *
+ * \param code error code
+ * \return false
+ */
+
+bool ro_save_draw_error(pencil_code code)
+{
+ LOG("code %i", code);
+
+ switch (code) {
+ case pencil_OK:
+ assert(0);
+ break;
+ case pencil_OUT_OF_MEMORY:
+ ro_warn_user("NoMemory", 0);
+ break;
+ case pencil_FONT_MANAGER_ERROR:
+ ro_warn_user("SaveError", rufl_fm_error->errmess);
+ break;
+ case pencil_FONT_NOT_FOUND:
+ case pencil_IO_ERROR:
+ case pencil_IO_EOF:
+ ro_warn_user("SaveError", "generating the DrawFile failed");
+ break;
+ }
+
+ return false;
+}
+
+#endif
diff --git a/frontends/riscos/save_draw.h b/frontends/riscos/save_draw.h
new file mode 100644
index 000000000..7ae447790
--- /dev/null
+++ b/frontends/riscos/save_draw.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_RISCOS_SAVE_DRAW_H_
+#define _NETSURF_RISCOS_SAVE_DRAW_H_
+
+#ifdef WITH_DRAW_EXPORT
+
+#include <stdbool.h>
+struct hlcache_handle;
+
+bool save_as_draw(struct hlcache_handle *h, const char *path);
+
+#endif
+
+#endif
diff --git a/frontends/riscos/save_pdf.c b/frontends/riscos/save_pdf.c
new file mode 100644
index 000000000..3d6395629
--- /dev/null
+++ b/frontends/riscos/save_pdf.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2008 John Tytgat <joty@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Export a content as a PDF file (implementation).
+ */
+
+#include "utils/config.h"
+#ifdef WITH_PDF_EXPORT
+
+#include <stdbool.h>
+#include "oslib/osfile.h"
+#include "content/content.h"
+#include "content/hlcache.h"
+#include "desktop/print.h"
+#include "desktop/save_pdf/font_haru.h"
+#include "desktop/save_pdf/pdf_plotters.h"
+#include "riscos/save_pdf.h"
+#include "utils/log.h"
+#include "utils/config.h"
+
+/**
+ * Export a content as a PDF file.
+ *
+ * \param h content to export
+ * \param path path to save PDF as
+ * \return true on success, false on error and error reported
+ */
+bool save_as_pdf(hlcache_handle *h, const char *path)
+{
+ struct print_settings *psettings;
+
+ psettings = print_make_settings(PRINT_DEFAULT, path, &haru_nsfont);
+ if (psettings == NULL)
+ return false;
+
+ if (!print_basic_run(h, &pdf_printer, psettings))
+ return false;
+
+ xosfile_set_type(path, 0xadf);
+
+ return true;
+}
+
+#endif
diff --git a/frontends/riscos/save_pdf.h b/frontends/riscos/save_pdf.h
new file mode 100644
index 000000000..ad4599dea
--- /dev/null
+++ b/frontends/riscos/save_pdf.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2008 John Tytgat <joty@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_RISCOS_SAVE_PDF_H_
+#define _NETSURF_RISCOS_SAVE_PDF_H_
+
+#include "utils/config.h"
+#ifdef WITH_PDF_EXPORT
+
+struct hlcache_handle;
+
+bool save_as_pdf(struct hlcache_handle *h, const char *path);
+
+#endif /* WITH_PDF_EXPORT */
+
+#endif
diff --git a/frontends/riscos/schedule.c b/frontends/riscos/schedule.c
new file mode 100644
index 000000000..54308b7a9
--- /dev/null
+++ b/frontends/riscos/schedule.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Scheduled callback queue (implementation).
+ *
+ * The queue is simply implemented as a linked list.
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "oslib/os.h"
+#include "utils/log.h"
+
+#include "riscos/gui.h"
+
+
+/** Entry in the queue of scheduled callbacks. */
+struct sched_entry {
+ /** Preferred time for callback. */
+ os_t time;
+ /** Function to call at the specified time. */
+ void (*callback)(void *p);
+ /** User parameter for callback. */
+ void *p;
+ /** Next (later) entry in queue. */
+ struct sched_entry *next;
+};
+
+/** Queue of scheduled callbacks (sentinel at head). */
+static struct sched_entry sched_queue = { 0, 0, 0, 0 };
+
+/** Items have been scheduled. */
+bool sched_active = false;
+/** Time of soonest scheduled event (valid only if sched_active is true). */
+os_t sched_time;
+
+/**
+ * Unschedule a callback.
+ *
+ * \param callback callback function
+ * \param p user parameter, passed to callback function
+ *
+ * All scheduled callbacks matching both callback and p are removed.
+ */
+
+static nserror schedule_remove(void (*callback)(void *p), void *p)
+{
+ struct sched_entry *entry, *prev;
+
+ prev = &sched_queue;
+
+ for (entry = prev->next; entry; entry = prev->next) {
+ if (entry->callback != callback || entry->p != p) {
+ prev = entry;
+ continue;
+ }
+
+ prev->next = entry->next;
+ free(entry);
+
+ /* There can only ever be one match, and we've found it */
+ break;
+ }
+
+ if (sched_queue.next) {
+ sched_active = true;
+ sched_time = sched_queue.next->time;
+ } else {
+ sched_active = false;
+ }
+
+ return NSERROR_OK;
+}
+
+/* exported function documented in riscos/gui.h */
+nserror riscos_schedule(int t, void (*callback)(void *p), void *p)
+{
+ struct sched_entry *entry;
+ struct sched_entry *queue;
+ os_t time;
+ nserror ret;
+
+ ret = schedule_remove(callback, p);
+ if ((t < 0) || (ret != NSERROR_OK)) {
+ return ret;
+ }
+
+ t = t / 10; /* convert to centiseconds */
+
+ time = os_read_monotonic_time() + t;
+
+ entry = malloc(sizeof *entry);
+ if (!entry) {
+ LOG("malloc failed");
+ return NSERROR_NOMEM;
+ }
+
+ entry->time = time;
+ entry->callback = callback;
+ entry->p = p;
+
+ for (queue = &sched_queue;
+ queue->next && queue->next->time <= time;
+ queue = queue->next)
+ ;
+ entry->next = queue->next;
+ queue->next = entry;
+
+ sched_active = true;
+ sched_time = sched_queue.next->time;
+
+ return NSERROR_OK;
+}
+
+
+/* exported function documented in riscos/gui.h */
+bool schedule_run(void)
+{
+ struct sched_entry *entry;
+ os_t now;
+
+ now = os_read_monotonic_time();
+
+ while (sched_queue.next && sched_queue.next->time <= now) {
+ void (*callback)(void *p);
+ void *p;
+
+ entry = sched_queue.next;
+ callback = entry->callback;
+ p = entry->p;
+ sched_queue.next = entry->next;
+ free(entry);
+ /* The callback may call riscos_schedule(), so leave
+ * the queue in a safe state.
+ */
+ callback(p);
+ }
+
+ if (sched_queue.next) {
+ sched_active = true;
+ sched_time = sched_queue.next->time;
+ } else
+ sched_active = false;
+
+ return sched_active;
+}
diff --git a/frontends/riscos/scripts/Help b/frontends/riscos/scripts/Help
new file mode 100644
index 000000000..9116926a3
--- /dev/null
+++ b/frontends/riscos/scripts/Help
@@ -0,0 +1,12 @@
+| Help file for NetSurf. ( $Revision$ )
+
+| Set system variables and application sprites
+Set NetSurf$ForceVars 1
+/<Obey$Dir>.!Boot
+UnSet NetSurf$ForceVars
+
+| Resource Locations
+SetMacro NetSurf$Path Choices:WWW.NetSurf.,<NetSurf$Dir>.
+
+WimpSlot -min 8k -max 8k
+Run <NetSurf$Dir>.OpenHelp
diff --git a/frontends/riscos/scripts/Run b/frontends/riscos/scripts/Run
new file mode 100644
index 000000000..4a51d7838
--- /dev/null
+++ b/frontends/riscos/scripts/Run
@@ -0,0 +1,109 @@
+| Run file for NetSurf.
+|
+| This file ensures that the system resources required by NetSurf are
+| present. Additionally, it forces setting of system variables related
+| to NetSurf.
+
+| Set system variables and application sprites
+Set NetSurf$ForceVars 1
+/<Obey$Dir>.!Boot
+UnSet NetSurf$ForceVars
+
+| Configure logging. Set 1 to enable, or 0 to suppress.
+Set NetSurf$Logging 1
+
+| Detect if NetSurf is already running and, if so, force the
+| current instance to open a new window. Then stop this script.
+Set Alias$NetSurfRunning UnSet Alias$NetSurfRunning|mUnSet NetSurf$Running|mObey
+Set NetSurf$Running 0
+WimpSlot -min 64k -max 64k
+/<NetSurf$Dir>.KickNS
+| If not running, then unset system variables and continue
+If "<NetSurf$Running>" = "0" Then Set Alias$NetSurfRunning UnSet Alias$NetSurfRunning|mUnSet NetSurf$Running
+| Invoke our alias to clean up
+NetSurfRunning
+
+| Resource Locations
+| The following are read-only locations
+SetMacro NetSurf$Path Choices:WWW.NetSurf.,<NetSurf$Dir>.
+| The following are write-only locations
+SetMacro NetSurf$ChoicesSave <Choices$Write>.WWW.NetSurf.Choices
+
+| We need RISC OS 3
+RMEnsure UtilityModule 3.00 Error NetSurf needs RISC OS 3 or later
+
+| Ensure Nested WIMP is installed
+| http://acorn.riscos.com/ (in the universal boot archive)
+RMEnsure WindowManager 3.80 Error NetSurf requires the Nested Window Manager. This can be obtained by downloading the Universal Boot sequence from http://acorn.riscos.com/
+
+| Check for various key resources - can't do much if they don't exist
+If "<System$Path>" = "" Then Set System$Path_Message System resources not found.
+If "<Wimp$ScrapDir>" = "" Then Error Scrap resource not found.
+If "<InetDBase$Path>" = "" Then Error Internet resources can not be found
+If "<Unicode$Path>" = "" Then Error NetSurf requires the !Unicode resource. This can be found, along with the Iconv module, at http://www.netsurf-browser.org/projects/iconv/
+If "<Inet$MimeMappings>" = "" Then Set Inet$MimeMappings InetDBase:MimeMap
+
+| Define this alias for clarity
+| Syntax: NetSurfRMLoad <Path to module>
+Set Alias$NetSurfRMLoad IfThere %%*0 Then RMLoad %%*0
+
+| Ensure a 32bit SharedCLibrary is installed
+| (5.17 == first 32bit SCL, 5.43 == C99 features)
+RMEnsure SharedCLibrary 5.17 NetSurfRMLoad System:Modules.CLib
+RMEnsure SharedCLibrary 5.43 Error NetSurf requires SharedCLibrary 5.43 or later. This can be downloaded from https://www.riscosopen.org/content/downloads/common
+
+| Ensure CallASWI is installed
+RMEnsure UtilityModule 3.70 RMEnsure CallASWI 0.02 NetSurfRMLoad System:Modules.CallASWI
+RMEnsure UtilityModule 3.70 RMEnsure CallASWI 0.02 Error NetSurf requires the CallASWI module. This can be downloaded from https://www.riscosopen.org/content/downloads/common
+
+| Ensure DrawFile module is installed
+| Should be installed in !System.310.Modules
+RMEnsure DrawFile 1.30 NetSurfRMLoad System:Modules.DrawFile
+RMEnsure DrawFile 1.30 Error NetSurf requires the DrawFile module. This can be downloaded from https://www.riscosopen.org/content/downloads/common
+
+| Ensure SharedUnixLibrary is installed
+RMEnsure SharedUnixLibrary 1.07 NetSurfRMLoad System:Modules.SharedULib
+RMEnsure SharedUnixLibrary 1.07 Error NetSurf requires SharedUnixLibrary 1.07 or later. Please use the RISC OS Configure app to update the computer's !System directory from the NetSurf archive.
+
+| Load AcornURI if it isn't already
+Unset NetSurf$Start_URI_Handler
+RMEnsure AcornURI 0.12 Set NetSurf$Start_URI_Handler 1
+RMEnsure AcornURI 0.12 NetSurfRMLoad System:Modules.Network.URI
+RMEnsure AcornURI 0.12 Error NetSurf requires AcornURI 0.12 or later. Please use the RISC OS Configure app to update the computer's !System directory from the NetSurf archive.
+RMEnsure AcornURI 0.12 Unset NetSurf$Start_URI_Handler
+
+| Check for mime map module
+RMEnsure MimeMap 0.10 NetSurfRMLoad System:Modules.Network.MimeMap
+RMEnsure MimeMap 0.10 Error NetSurf requires MimeMap 0.10 or later
+
+| Ensure Tinct is loaded
+RMEnsure Tinct 0.13 NetSurfRMLoad System:Modules.Tinct
+RMEnsure Tinct 0.13 Error NetSurf requires Tinct 0.13 or later. Please use the RISC OS Configure app to update the computer's !System directory from the NetSurf archive.
+
+| Ensure Iconv
+RMEnsure Iconv 0.12 NetSurfRMLoad System:Modules.Iconv
+RMEnsure Iconv 0.12 Error NetSurf requires Iconv 0.12 or later. Please use the RISC OS Configure app to update the computer's !System directory from the NetSurf archive.
+
+| Ensure CryptRandom
+RMEnsure CryptRandom 0.13 NetSurfRMLoad System:Modules.CryptRand
+RMEnsure CryptRandom 0.13 Error NetSurf requires CryptRandom 0.13 or later. Please use the RISC OS Configure app to update the computer's !System directory from the NetSurf archive.
+
+| Disable SpecialFX, if present
+Set NetSurf$SpecialFX 1
+RMEnsure SpecialFX 1.00 Set NetSurf$SpecialFX 0
+If <NetSurf$SpecialFX> Then SpecialFX ~B~G~L NetSurf
+Unset NetSurf$SpecialFX
+
+| No longer need this alias
+Unset Alias$NetSurfRMLoad
+
+| Now attempt to create Scrap directories
+CDir <Wimp$ScrapDir>.WWW
+CDir <Wimp$ScrapDir>.WWW.NetSurf
+
+| Install NetSurf-specific fonts
+| NB: trailing dot is required
+FontInstall NetSurf:Resources.Fonts.
+
+WIMPSLOT
+Run <NetSurf$Dir>.!RunImage %*0 2><Wimp$ScrapDir>.WWW.NetSurf.Log
diff --git a/frontends/riscos/search.c b/frontends/riscos/search.c
new file mode 100644
index 000000000..989c9aa9e
--- /dev/null
+++ b/frontends/riscos/search.c
@@ -0,0 +1,475 @@
+/*
+ * Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk>
+ * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * Free text search implementation
+ */
+
+#include "utils/config.h"
+
+#include <ctype.h>
+#include <string.h>
+#include "oslib/hourglass.h"
+#include "oslib/wimp.h"
+
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "content/content.h"
+#include "content/hlcache.h"
+#include "desktop/browser.h"
+#include "desktop/gui_search.h"
+#include "desktop/browser.h"
+#include "desktop/search.h"
+
+#include "riscos/gui.h"
+#include "riscos/dialog.h"
+#include "riscos/menus.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+
+#define RECENT_SEARCHES 8
+
+struct search_static_data {
+ char *recent_searches[RECENT_SEARCHES];
+ bool search_insert;
+ struct browser_window *search_window;
+};
+
+static struct search_static_data search_data =
+ { { NULL }, false, NULL };
+
+static wimp_MENU(RECENT_SEARCHES) menu_recent;
+wimp_menu *recent_search_menu = (wimp_menu *)&menu_recent;
+#define DEFAULT_FLAGS (wimp_ICON_TEXT | wimp_ICON_FILLED | \
+ (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) | \
+ (wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT))
+
+
+static void ro_gui_search_end(wimp_w w);
+static bool ro_gui_search_next(wimp_w w);
+static bool ro_gui_search_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_pointer *pointer);
+static bool ro_gui_search_prepare_menu(void);
+static bool ro_gui_search_click(wimp_pointer *pointer);
+static bool ro_gui_search_keypress(wimp_key *key);
+static search_flags_t ro_gui_search_update_flags(void);
+static void ro_gui_search_set_forward_state(bool active, void *p);
+static void ro_gui_search_set_back_state(bool active, void *p);
+static void ro_gui_search_set_status(bool found, void *p);
+static void ro_gui_search_set_hourglass(bool active, void *p);
+static void ro_gui_search_add_recent(const char *string, void *p);
+
+static struct gui_search_table search_table = {
+ ro_gui_search_set_status,
+ ro_gui_search_set_hourglass,
+ ro_gui_search_add_recent,
+ ro_gui_search_set_forward_state,
+ ro_gui_search_set_back_state,
+};
+
+struct gui_search_table *riscos_search_table = &search_table;
+
+void ro_gui_search_init(void)
+{
+ dialog_search = ro_gui_dialog_create("search");
+ ro_gui_wimp_event_register_keypress(dialog_search,
+ ro_gui_search_keypress);
+ ro_gui_wimp_event_register_close_window(dialog_search,
+ ro_gui_search_end);
+ ro_gui_wimp_event_register_menu_prepare(dialog_search,
+ ro_gui_search_menu_prepare);
+ ro_gui_wimp_event_register_menu_gright(dialog_search,
+ ICON_SEARCH_TEXT, ICON_SEARCH_MENU,
+ recent_search_menu);
+ ro_gui_wimp_event_register_text_field(dialog_search,
+ ICON_SEARCH_STATUS);
+ ro_gui_wimp_event_register_checkbox(dialog_search,
+ ICON_SEARCH_CASE_SENSITIVE);
+ ro_gui_wimp_event_register_mouse_click(dialog_search,
+ ro_gui_search_click);
+ ro_gui_wimp_event_register_ok(dialog_search, ICON_SEARCH_FIND_NEXT,
+ ro_gui_search_next);
+ ro_gui_wimp_event_register_cancel(dialog_search, ICON_SEARCH_CANCEL);
+ ro_gui_wimp_event_set_help_prefix(dialog_search, "HelpSearch");
+
+ recent_search_menu->title_data.indirected_text.text =
+ (char*)messages_get("Search");
+ ro_gui_menu_init_structure(recent_search_menu, RECENT_SEARCHES);
+}
+
+/**
+ * Wrapper for the pressing of an OK button for wimp_event.
+ *
+ * \return false, to indicate the window should not be closed
+ */
+bool ro_gui_search_next(wimp_w w)
+{
+ search_data.search_insert = true;
+ search_flags_t flags = SEARCH_FLAG_FORWARDS |
+ ro_gui_search_update_flags();
+ browser_window_search(search_data.search_window, NULL, flags,
+ ro_gui_get_icon_string(dialog_search,
+ ICON_SEARCH_TEXT));
+ return false;
+}
+
+
+/**
+ * Callback to prepare menus in the Search dialog. At present, this
+ * only has to handle the previous search pop-up.
+ *
+ * \param w The window handle owning the menu.
+ * \param i The icon handle owning the menu.
+ * \param *menu The menu to be prepared.
+ * \param *pointer The associated mouse click event block, or NULL
+ * on an Adjust-click re-opening.
+ * \return true if the event was handled; false if not.
+ */
+
+bool ro_gui_search_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_pointer *pointer)
+{
+ if (menu != recent_search_menu || i != ICON_SEARCH_MENU)
+ return false;
+
+ if (pointer != NULL)
+ return ro_gui_search_prepare_menu();
+
+ return true;
+}
+
+
+bool ro_gui_search_click(wimp_pointer *pointer)
+{
+ search_flags_t flags;
+ switch (pointer->i) {
+ case ICON_SEARCH_FIND_PREV:
+ search_data.search_insert = true;
+ flags = ~SEARCH_FLAG_FORWARDS &
+ ro_gui_search_update_flags();
+ browser_window_search(search_data.search_window, NULL,
+ flags,
+ ro_gui_get_icon_string(dialog_search,
+ ICON_SEARCH_TEXT));
+ return true;
+ case ICON_SEARCH_CASE_SENSITIVE:
+ flags = SEARCH_FLAG_FORWARDS |
+ ro_gui_search_update_flags();
+ browser_window_search_clear(
+ search_data.search_window);
+ browser_window_search(search_data.search_window, NULL,
+ flags,
+ ro_gui_get_icon_string(dialog_search,
+ ICON_SEARCH_TEXT));
+ return true;
+ case ICON_SEARCH_SHOW_ALL:
+ flags = ro_gui_get_icon_selected_state(
+ pointer->w, pointer->i) ?
+ SEARCH_FLAG_SHOWALL : SEARCH_FLAG_NONE;
+ browser_window_search(search_data.search_window, NULL,
+ flags,
+ ro_gui_get_icon_string(dialog_search,
+ ICON_SEARCH_TEXT));
+ return true;
+ }
+ return false;
+}
+
+/**
+ * add search string to recent searches list
+ *
+ * front is at liberty how to implement the bare notification
+ * should normally store a strdup() of the string in
+ * search_global_data.recent[];
+ * core gives no guarantee of the integrity of the const char *
+ *
+ * \param search string search pattern
+ * \param p the pointer sent to search_verify_new()
+ */
+
+void ro_gui_search_add_recent(const char *search, void *p)
+{
+ char *tmp;
+ int i;
+
+ if ((search == NULL) || (search[0] == '\0'))
+ return;
+
+ if (!search_data.search_insert) {
+ free(search_data.recent_searches[0]);
+ search_data.recent_searches[0] = strdup(search);
+ ro_gui_search_prepare_menu();
+ return;
+ }
+
+ if ((search_data.recent_searches[0] != NULL) &&
+ (!strcmp(search_data.recent_searches[0], search)))
+ return;
+
+ tmp = strdup(search);
+ if (!tmp) {
+ ro_warn_user("NoMemory", 0);
+ return;
+ }
+ free(search_data.recent_searches[RECENT_SEARCHES - 1]);
+ for (i = RECENT_SEARCHES - 1; i > 0; i--)
+ search_data.recent_searches[i] = search_data.recent_searches[i - 1];
+ search_data.recent_searches[0] = tmp;
+ search_data.search_insert = false;
+
+ ro_gui_set_icon_shaded_state(dialog_search, ICON_SEARCH_MENU, false);
+ ro_gui_search_prepare_menu();
+}
+
+bool ro_gui_search_prepare_menu(void)
+{
+ int i;
+ int suggestions = 0;
+
+ for (i = 0; i < RECENT_SEARCHES; i++)
+ if (search_data.recent_searches[i] != NULL)
+ suggestions++;
+
+ if (suggestions == 0)
+ return false;
+
+ for (i = 0; i < suggestions; i++) {
+ recent_search_menu->entries[i].menu_flags &= ~wimp_MENU_LAST;
+ recent_search_menu->entries[i].data.indirected_text.text =
+ search_data.recent_searches[i];
+ recent_search_menu->entries[i].data.indirected_text.size =
+ strlen(search_data.recent_searches[i]) + 1;
+ }
+ recent_search_menu->entries[suggestions - 1].menu_flags |=
+ wimp_MENU_LAST;
+
+ return true;
+}
+
+/**
+ * Determine of the browser window is searchable.
+ *
+ * \param bw The browser window to check.
+ */
+static bool ro_gui_search_bw_searchable(struct browser_window *bw)
+{
+ hlcache_handle *h;
+
+ assert(bw != NULL);
+
+ h = browser_window_get_content(bw);
+
+ /* only handle html/textplain contents */
+ /** \todo Should have content_is_searchable() api */
+ if ((!h) || (content_get_type(h) != CONTENT_HTML &&
+ content_get_type(h) != CONTENT_TEXTPLAIN))
+ return false;
+
+ return true;
+}
+
+
+/**
+ * Open the search dialog
+ *
+ * \param bw the browser window to search
+ */
+void ro_gui_search_prepare(struct browser_window *bw)
+{
+ /* only handle searchable contents */
+ if (!ro_gui_search_bw_searchable(bw))
+ return;
+
+ /* if the search dialogue is reopened over a new window, we may
+ need to cancel the previous search */
+ ro_gui_search_set_forward_state(true, bw);
+ ro_gui_search_set_back_state(true, bw);
+
+ search_data.search_window = bw;
+
+ ro_gui_set_icon_string(dialog_search, ICON_SEARCH_TEXT, "", true);
+ ro_gui_set_icon_selected_state(dialog_search,
+ ICON_SEARCH_CASE_SENSITIVE, false);
+ ro_gui_set_icon_selected_state(dialog_search,
+ ICON_SEARCH_SHOW_ALL, false);
+
+ ro_gui_search_set_status(true, NULL);
+
+ ro_gui_wimp_event_memorise(dialog_search);
+ search_data.search_insert = true;
+}
+
+/**
+ * Handle keypresses in the search dialog
+ *
+ * \param key wimp_key block
+ * \return true if keypress handled, false otherwise
+ */
+bool ro_gui_search_keypress(wimp_key *key)
+{
+ bool state;
+ search_flags_t flags;
+
+ switch (key->c) {
+ case 1: {
+ flags = ro_gui_search_update_flags()
+ ^ SEARCH_FLAG_SHOWALL;
+ browser_window_search(search_data.search_window, NULL,
+ flags,
+ ro_gui_get_icon_string(dialog_search,
+ ICON_SEARCH_TEXT));
+ }
+ break;
+ case 9: /* ctrl i */
+ state = ro_gui_get_icon_selected_state(dialog_search,
+ ICON_SEARCH_CASE_SENSITIVE);
+ ro_gui_set_icon_selected_state(dialog_search,
+ ICON_SEARCH_CASE_SENSITIVE, !state);
+ flags = SEARCH_FLAG_FORWARDS |
+ ro_gui_search_update_flags();
+ browser_window_search(search_data.search_window, NULL,
+ flags,
+ ro_gui_get_icon_string(dialog_search,
+ ICON_SEARCH_TEXT));
+ return true;
+ case IS_WIMP_KEY | wimp_KEY_UP:
+ search_data.search_insert = true;
+ flags = ~SEARCH_FLAG_FORWARDS &
+ ro_gui_search_update_flags();
+ browser_window_search(search_data.search_window, NULL,
+ flags,
+ ro_gui_get_icon_string(dialog_search,
+ ICON_SEARCH_TEXT));
+ return true;
+ case IS_WIMP_KEY | wimp_KEY_DOWN:
+ search_data.search_insert = true;
+ flags = SEARCH_FLAG_FORWARDS |
+ ro_gui_search_update_flags();
+ browser_window_search(search_data.search_window, NULL,
+ flags,
+ ro_gui_get_icon_string(dialog_search,
+ ICON_SEARCH_TEXT));
+ return true;
+
+ default:
+ if (key->c == 21) {
+ /* ctrl+u means the user's starting
+ * a new search */
+ browser_window_search_clear(
+ search_data.search_window);
+ search_data.search_insert = true;
+ }
+ if (key->c == 8 || /* backspace */
+ key->c == 21 || /* ctrl u */
+ (key->c >= 0x20 && key->c <= 0x7f)) {
+ flags = SEARCH_FLAG_FORWARDS |
+ ro_gui_search_update_flags();
+ ro_gui_search_set_forward_state(true,
+ search_data.search_window);
+ ro_gui_search_set_back_state(true,
+ search_data.search_window);
+ browser_window_search(search_data.search_window,
+ NULL,
+ flags,
+ ro_gui_get_icon_string(
+ dialog_search,
+ ICON_SEARCH_TEXT));
+ return true;
+ }
+ break;
+ }
+
+ return false;
+}
+
+/**
+ * Ends the search
+ * \param w the search window handle (not used)
+ */
+void ro_gui_search_end(wimp_w w)
+{
+ browser_window_search_clear(search_data.search_window);
+}
+
+/**
+* Change the displayed search status.
+* \param found search pattern matched in text
+* \param p the pointer sent to search_verify_new() / search_create_context()
+*/
+
+void ro_gui_search_set_status(bool found, void *p)
+{
+ ro_gui_set_icon_string(dialog_search, ICON_SEARCH_STATUS, found ? "" :
+ messages_get("NotFound"), true);
+}
+
+/**
+* display hourglass while searching
+* \param active start/stop indicator
+* \param p the pointer sent to search_verify_new() / search_create_context()
+*/
+
+void ro_gui_search_set_hourglass(bool active, void *p)
+{
+ if (active)
+ xhourglass_on();
+
+ else
+ xhourglass_off();
+}
+
+/**
+* activate search forwards button in gui
+* \param active activate/inactivate
+* \param p the pointer sent to search_verify_new() / search_create_context()
+*/
+
+void ro_gui_search_set_forward_state(bool active, void *p)
+{
+ ro_gui_set_icon_shaded_state(dialog_search, ICON_SEARCH_FIND_NEXT,
+ !active);
+}
+
+/**
+* activate search forwards button in gui
+* \param active activate/inactivate
+* \param p the pointer sent to search_verify_new() / search_create_context()
+*/
+
+void ro_gui_search_set_back_state(bool active, void *p)
+{
+ ro_gui_set_icon_shaded_state(dialog_search, ICON_SEARCH_FIND_PREV,
+ !active);
+}
+
+/**
+* retrieve state of 'case sensitive', 'show all' checks in gui
+*/
+search_flags_t ro_gui_search_update_flags(void)
+{
+ search_flags_t flags;
+ flags = 0 | (ro_gui_get_icon_selected_state(dialog_search,
+ ICON_SEARCH_CASE_SENSITIVE) ?
+ SEARCH_FLAG_CASE_SENSITIVE : 0) |
+ (ro_gui_get_icon_selected_state(dialog_search,
+ ICON_SEARCH_SHOW_ALL) ? SEARCH_FLAG_SHOWALL : 0);
+ return flags;
+}
+
diff --git a/frontends/riscos/searchweb.c b/frontends/riscos/searchweb.c
new file mode 100644
index 000000000..14246d228
--- /dev/null
+++ b/frontends/riscos/searchweb.c
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
diff --git a/frontends/riscos/sslcert.c b/frontends/riscos/sslcert.c
new file mode 100644
index 000000000..9e43f2db1
--- /dev/null
+++ b/frontends/riscos/sslcert.c
@@ -0,0 +1,348 @@
+/*
+ * Copyright 2006 John M Bell <jmb202@ecs.soton.ac.uk>
+ * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * SSL Certificate verification UI (implementation)
+ */
+
+#include "utils/config.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include "oslib/wimp.h"
+
+#include "utils/log.h"
+#include "content/content.h"
+#include "content/hlcache.h"
+#include "content/fetch.h"
+#include "content/urldb.h"
+#include "desktop/browser.h"
+#include "desktop/sslcert_viewer.h"
+#include "desktop/tree.h"
+
+#include "riscos/dialog.h"
+#include "riscos/sslcert.h"
+#include "riscos/textarea.h"
+#include "riscos/treeview.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/wimputils.h"
+#include "riscos/gui.h"
+
+#define ICON_SSL_PANE 1
+#define ICON_SSL_REJECT 3
+#define ICON_SSL_ACCEPT 4
+
+static wimp_window *ro_gui_cert_dialog_template;
+static wimp_window *ro_gui_cert_tree_template;
+
+struct ro_sslcert
+{
+ wimp_w window;
+ wimp_w pane;
+ ro_treeview *tv;
+ struct sslcert_session_data *data;
+};
+
+static void ro_gui_cert_accept(wimp_pointer *pointer);
+static void ro_gui_cert_reject(wimp_pointer *pointer);
+static void ro_gui_cert_close_window(wimp_w w);
+static void ro_gui_cert_release_window(struct ro_sslcert *s);
+
+/**
+ * Load and initialise the certificate window template
+ */
+
+void ro_gui_cert_preinitialise(void)
+{
+ /* Load templates for the SSL windows and adjust the tree window
+ * flags to suit.
+ */
+
+ ro_gui_cert_dialog_template = ro_gui_dialog_load_template("sslcert");
+ ro_gui_cert_tree_template = ro_gui_dialog_load_template("tree");
+
+ ro_gui_cert_tree_template->flags &= ~(wimp_WINDOW_MOVEABLE |
+ wimp_WINDOW_BACK_ICON |
+ wimp_WINDOW_CLOSE_ICON |
+ wimp_WINDOW_TITLE_ICON |
+ wimp_WINDOW_SIZE_ICON |
+ wimp_WINDOW_TOGGLE_ICON);
+}
+
+/**
+ * Load and initialise the certificate window template
+ */
+
+void ro_gui_cert_postinitialise(void)
+{
+ /* Initialise the SSL module. */
+}
+
+/**
+ * Prompt the user to verify a certificate with issuse.
+ *
+ * \param url The URL being verified.
+ * \param certs The certificate to be verified
+ * \param num The number of certificates to be verified.
+ * \param cb Callback upon user decision.
+ * \param cbpw Context pointer passed to cb
+ */
+void gui_cert_verify(nsurl *url,
+ const struct ssl_cert_info *certs, unsigned long num,
+ nserror (*cb)(bool proceed, void *pw), void *cbpw)
+{
+ struct ro_sslcert *sslcert_window;
+ wimp_window_state state;
+ wimp_icon_state istate;
+ wimp_window_info info;
+ os_error *error;
+ bool set_extent;
+
+ assert(certs);
+
+ sslcert_window = malloc(sizeof(struct ro_sslcert));
+ if (sslcert_window == NULL) {
+ LOG("Failed to allocate memory for SSL Cert Dialog");
+ return;
+ }
+
+ /* Create the SSL window and its pane. */
+
+ error = xwimp_create_window(ro_gui_cert_dialog_template,
+ &(sslcert_window->window));
+ if (error) {
+ LOG("xwimp_create_window: 0x%x: %s", error->errnum, error->errmess);
+ free(sslcert_window);
+ return;
+ }
+
+ error = xwimp_create_window(ro_gui_cert_tree_template,
+ &(sslcert_window->pane));
+ if (error) {
+ LOG("xwimp_create_window: 0x%x: %s", error->errnum, error->errmess);
+ free(sslcert_window);
+ return;
+ }
+
+ /* Create the SSL data and build a tree from it. */
+ sslcert_viewer_create_session_data(num, url,
+ cb, cbpw, certs, &sslcert_window->data);
+ ssl_current_session = sslcert_window->data;
+
+ sslcert_window->tv = ro_treeview_create(sslcert_window->pane,
+ NULL, NULL, TREE_SSLCERT);
+ if (sslcert_window->tv == NULL) {
+ LOG("Failed to allocate treeview");
+ free(sslcert_window);
+ return;
+ }
+
+ /* Set up the certificate window event handling.
+ *
+ * (The action buttons are registered as button events, not OK and
+ * Cancel, as both need to carry out actions.)
+ */
+
+ ro_gui_wimp_event_set_user_data(sslcert_window->window, sslcert_window);
+ ro_gui_wimp_event_register_close_window(sslcert_window->window,
+ ro_gui_cert_close_window);
+ ro_gui_wimp_event_register_button(sslcert_window->window,
+ ICON_SSL_REJECT, ro_gui_cert_reject);
+ ro_gui_wimp_event_register_button(sslcert_window->window,
+ ICON_SSL_ACCEPT, ro_gui_cert_accept);
+
+ ro_gui_dialog_open_persistent(NULL, sslcert_window->window, false);
+
+ /* Nest the tree window inside the pane window. To do this, we:
+ * - Get the current pane extent,
+ * - Get the parent window position and the location of the pane-
+ * locating icon inside it,
+ * - Set the visible area of the pane to suit,
+ * - Check that the pane extents are OK for this visible area, and
+ * increase them if necessary,
+ * - Before finally opening the pane as a nested part of the parent.
+ */
+
+ info.w = sslcert_window->pane;
+ error = xwimp_get_window_info_header_only(&info);
+ if (error) {
+ ro_gui_cert_release_window(sslcert_window);
+ LOG("xwimp_get_window_info: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+
+ state.w = sslcert_window->window;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ ro_gui_cert_release_window(sslcert_window);
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+
+ istate.w = sslcert_window->window;
+ istate.i = ICON_SSL_PANE;
+ error = xwimp_get_icon_state(&istate);
+ if (error) {
+ ro_gui_cert_release_window(sslcert_window);
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+
+ state.w = sslcert_window->pane;
+ state.visible.x1 = state.visible.x0 + istate.icon.extent.x1 - 20 -
+ ro_get_vscroll_width(sslcert_window->pane);
+ state.visible.x0 += istate.icon.extent.x0 + 20;
+ state.visible.y0 = state.visible.y1 + istate.icon.extent.y0 + 20 +
+ ro_get_hscroll_height(sslcert_window->pane);
+ state.visible.y1 += istate.icon.extent.y1 - 32;
+
+ set_extent = false;
+
+ if ((info.extent.x1 - info.extent.x0) <
+ (state.visible.x1 - state.visible.x0)) {
+ info.extent.x0 = 0;
+ info.extent.x1 = state.visible.x1 - state.visible.x0;
+ set_extent = true;
+ }
+ if ((info.extent.y1 - info.extent.y0) <
+ (state.visible.y1 - state.visible.y0)) {
+ info.extent.y1 = 0;
+ info.extent.x1 = state.visible.y0 - state.visible.y1;
+ set_extent = true;
+ }
+
+ if (set_extent) {
+ error = xwimp_set_extent(sslcert_window->pane, &(info.extent));
+ if (error) {
+ ro_gui_cert_release_window(sslcert_window);
+ LOG("xwimp_set_extent: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+ }
+
+ error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state),
+ sslcert_window->window,
+ wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
+ << wimp_CHILD_XORIGIN_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
+ << wimp_CHILD_YORIGIN_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
+ << wimp_CHILD_LS_EDGE_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
+ << wimp_CHILD_RS_EDGE_SHIFT);
+ if (error) {
+ ro_gui_cert_release_window(sslcert_window);
+ LOG("xwimp_open_window_nested: 0x%x: %s", error->errnum, error->errmess);
+ ro_gui_cert_release_window(sslcert_window);
+ return;
+ }
+
+ ro_treeview_set_origin(sslcert_window->tv, 0, 0);
+}
+
+/**
+ * Handle acceptance of certificate via event callback.
+ *
+ * \param *pointer The wimp pointer block.
+ */
+
+void ro_gui_cert_accept(wimp_pointer *pointer)
+{
+ struct ro_sslcert *s;
+
+ s = (struct ro_sslcert *) ro_gui_wimp_event_get_user_data(pointer->w);
+
+ if (s != NULL) {
+ sslcert_viewer_accept(s->data);
+ ro_gui_dialog_close(s->window);
+ ro_gui_cert_release_window(s);
+ }
+}
+
+/**
+ * Handle rejection of certificate via event callback.
+ *
+ * \param pointer The wimp pointer block.
+ */
+
+void ro_gui_cert_reject(wimp_pointer *pointer)
+{
+ struct ro_sslcert *s;
+
+ s = (struct ro_sslcert *) ro_gui_wimp_event_get_user_data(pointer->w);
+
+ if (s != NULL) {
+ sslcert_viewer_reject(s->data);
+ ro_gui_dialog_close(s->window);
+ ro_gui_cert_release_window(s);
+ }
+}
+
+/**
+ * Callback to handle the closure of the SSL dialogue by other means.
+ *
+ * \param w The window being closed.
+ */
+
+static void ro_gui_cert_close_window(wimp_w w)
+{
+ struct ro_sslcert *s;
+
+ s = (struct ro_sslcert *) ro_gui_wimp_event_get_user_data(w);
+
+ if (s != NULL)
+ ro_gui_cert_release_window(s);
+}
+
+/**
+ * Handle closing of the RISC OS certificate verification dialog, deleting
+ * the windows and freeing up the treeview and data block.
+ *
+ * \param *s The data block associated with the dialogue.
+ */
+
+void ro_gui_cert_release_window(struct ro_sslcert *s)
+{
+ os_error *error;
+
+ if (s == NULL)
+ return;
+
+ LOG("Releasing SSL data: 0x%x", (unsigned)s);
+
+ ro_gui_wimp_event_finalise(s->window);
+ ro_treeview_destroy(s->tv);
+
+ error = xwimp_delete_window(s->window);
+ if (error) {
+ LOG("xwimp_delete_window: 0x%x:%s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ error = xwimp_delete_window(s->pane);
+ if (error) {
+ LOG("xwimp_delete_window: 0x%x:%s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ free(s);
+}
+
diff --git a/frontends/riscos/sslcert.h b/frontends/riscos/sslcert.h
new file mode 100644
index 000000000..17fce5552
--- /dev/null
+++ b/frontends/riscos/sslcert.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2006 Richard Wilson <info@tinct.net>
+ * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * SSL certificate viewer (interface).
+ */
+
+#ifndef _NETSURF_RISCOS_SSLCERT_H_
+#define _NETSURF_RISCOS_SSLCERT_H_
+
+struct node;
+
+void ro_gui_cert_preinitialise(void);
+void ro_gui_cert_postinitialise(void);
+void ro_gui_cert_open(struct tree *tree, struct node *node);
+
+#endif
+
diff --git a/frontends/riscos/templates/de b/frontends/riscos/templates/de
new file mode 100644
index 000000000..0fb4a9de7
--- /dev/null
+++ b/frontends/riscos/templates/de
@@ -0,0 +1,3845 @@
+Template:
+
+wimp_window {
+ template_name:"configure"
+ visible:378,820,1046,1126
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_TOGGLE_ICON | wimp_WINDOW_VSCROLL | wimp_WINDOW_SIZE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_LIGHT_GREY
+ extra_flags:
+ extent:0,-880,1236,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:4
+ ymin:156
+ text.text:"NetSurf Konfiguration"
+ text.size:*
+ text.validation:""
+}
+
+wimp_window {
+ template_name:"con_cache"
+ visible:500,672,1088,1100
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-428,588,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:588
+ ymin:240
+ text_only:"Cache"
+ wimp_icon {
+ extent:16,-124,568,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,268,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Memory cache "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:44,-100,164,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Größe"
+ text.size:13
+ text.validation:""
+ }
+ wimp_icon {
+ extent:168,-104,336,-52
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"512.0"
+ text.size:10
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:352,-96,384,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:384,-96,416,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:424,-100,480,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"MB"
+ }
+ wimp_icon {
+ extent:16,-312,568,-152
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-180,268,-136
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Disc cache "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:44,-232,164,-188
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Größe"
+ text.size:13
+ text.validation:""
+ }
+ wimp_icon {
+ extent:168,-236,336,-184
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"512"
+ text.size:10
+ text.validation:"Pptr_write;Kta;A0-9"
+ }
+ wimp_icon {
+ extent:352,-228,384,-196
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:384,-228,416,-196
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:424,-232,480,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"MB"
+ }
+ wimp_icon {
+ extent:44,-292,164,-248
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Expiry"
+ text.size:13
+ text.validation:""
+ }
+ wimp_icon {
+ extent:168,-296,336,-244
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"512"
+ text.size:10
+ text.validation:"Pptr_write;Kta;A0-9"
+ }
+ wimp_icon {
+ extent:352,-288,384,-256
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:384,-288,416,-256
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:424,-292,500,-252
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"days"
+ }
+ wimp_icon {
+ extent:24,-396,188,-344
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Standard"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:204,-396,368,-344
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Abbruch"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:384,-404,568,-336
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"OK"
+ text.size:4
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_fonts"
+ visible:744,374,1456,1098
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-724,712,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:712
+ ymin:724
+ text.text:"Schriftarten"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:16,-424,696,-28
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,236,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Font faces "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:26,-104,198,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Sans-serif"
+ }
+ wimp_icon {
+ extent:200,-108,624,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:632,-104,676,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:106,-164,198,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Serif"
+ }
+ wimp_icon {
+ extent:200,-168,624,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:632,-164,676,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:34,-224,198,-180
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Monospace"
+ }
+ wimp_icon {
+ extent:200,-228,624,-176
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:632,-224,676,-180
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:74,-284,198,-240
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Cursive"
+ }
+ wimp_icon {
+ extent:200,-288,624,-236
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:632,-284,676,-240
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:74,-344,198,-300
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Fantasy"
+ }
+ wimp_icon {
+ extent:200,-348,624,-296
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:632,-344,676,-300
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:42,-404,198,-360
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Standard"
+ }
+ wimp_icon {
+ extent:200,-408,624,-356
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:632,-404,676,-360
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:16,-616,696,-456
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-480,220,-436
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Font size "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:64,-532,192,-488
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Normal"
+ }
+ wimp_icon {
+ extent:200,-536,368,-484
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"12.3"
+ text.size:10
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:380,-528,412,-496
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:412,-528,444,-496
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:452,-532,492,-488
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"pt"
+ }
+ wimp_icon {
+ extent:60,-592,192,-548
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Minimum"
+ }
+ wimp_icon {
+ extent:200,-596,368,-544
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"12.3"
+ text.size:10
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:380,-588,412,-556
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:412,-588,444,-556
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:452,-592,492,-548
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"pt"
+ }
+ wimp_icon {
+ extent:24,-696,188,-644
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Standard"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:332,-696,496,-644
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Abbruch"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:512,-704,696,-636
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"OK"
+ text.size:4
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_home"
+ visible:400,304,1200,600
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-296,800,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:800
+ ymin:296
+ text_only:"Homepage"
+ wimp_icon {
+ extent:16,-184,784,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,284,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Home page "
+ text_and_sprite.size:16
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:48,-108,120,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"URL"
+ }
+ wimp_icon {
+ extent:124,-112,712,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:128
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:720,-108,764,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:124,-164,676,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Fenster beim Start öffnen"
+ text_and_sprite.size:32
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:24,-264,188,-212
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Standard"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:416,-264,580,-212
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Abbruch"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:596,-272,780,-204
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"OK"
+ text.size:4
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_image"
+ visible:828,926,1504,1514
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-588,676,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:676
+ ymin:588
+ text_only:"Bilder"
+ wimp_icon {
+ extent:16,-292,660,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,284,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Image quality "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-108,224,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Vordergrund"
+ }
+ wimp_icon {
+ extent:232,-112,592,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Error diffused"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:600,-108,644,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:32,-168,224,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Hintergrund"
+ }
+ wimp_icon {
+ extent:232,-172,592,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Error diffused"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:600,-168,644,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:32,-276,644,-180
+ icon_flags:wimp_ICON_BORDER
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ }
+ wimp_icon {
+ extent:16,-476,660,-320
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-348,284,-304
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Animations "
+ text_and_sprite.size:16
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:20,-404,208,-360
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Speedlimit"
+ text.size:12
+ text.validation:""
+ }
+ wimp_icon {
+ extent:212,-408,380,-356
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"12.34"
+ text.size:*
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:396,-400,428,-368
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:428,-400,460,-368
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:468,-404,612,-360
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Sekunden"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:212,-460,640,-416
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Animationen abschalten"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:24,-560,188,-508
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Standard"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:296,-560,460,-508
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Abbruch"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:476,-568,660,-500
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"OK"
+ text.size:4
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_lang"
+ visible:770,1102,1514,1402
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-300,744,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:744
+ ymin:300
+ text.text:"Spracheinstellungen"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:16,-188,728,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,284,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Language "
+ text_and_sprite.size:16
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-108,204,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Oberfläche"
+ }
+ wimp_icon {
+ extent:208,-112,660,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"1337"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:668,-108,712,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:40,-168,204,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Webseiten"
+ }
+ wimp_icon {
+ extent:208,-172,660,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"8008135"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:668,-168,712,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:24,-268,188,-216
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Standard"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:360,-268,524,-216
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Abbruch"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:540,-276,724,-208
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"OK"
+ text.size:4
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_theme"
+ visible:408,370,1316,974
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-604,908,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:640
+ ymin:604
+ text_only:"Themen"
+ wimp_icon {
+ extent:16,-492,892,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,332,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Available themes "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:24,-576,188,-524
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Standard"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:524,-576,688,-524
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Abbruch"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:704,-584,888,-516
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"OK"
+ text.size:4
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"download"
+ visible:486,610,1394,890
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-280,908,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:796
+ ymin:280
+ text.text:"NetSurf Download"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:420,-84,488,-16
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite.id:"file_ddc"
+ sprite.size:*
+ sprite.area:&1
+ }
+ wimp_icon {
+ extent:204,-152,900,-100
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"http://netsurf.sourceforge.net/netsurf.zip"
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:204,-212,900,-160
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"netsurf"
+ text.size:*
+ text.validation:"Pptr_write"
+ }
+ wimp_icon {
+ extent:204,-212,900,-160
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"ADFS::A7000+.$.netsurf"
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:8,-272,900,-220
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:12,-268,296,-224
+ icon_flags:wimp_ICON_FILLED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_CREAM
+ }
+ wimp_icon {
+ extent:12,-268,896,-224
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"84.4KB of 1.1MB 26.6KB/s 0:39 remaining"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:92,-148,200,-104
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Quelle"
+ }
+ wimp_icon {
+ extent:12,-208,200,-164
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Zielort"
+ }
+}
+
+wimp_window {
+ template_name:"history"
+ visible:252,388,1152,808
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_SCROLL_REPEAT | wimp_WINDOW_BACK_ICON | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_TOGGLE_ICON | wimp_WINDOW_VSCROLL | wimp_WINDOW_SIZE_ICON | wimp_WINDOW_HSCROLL | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_WHITE
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-880,1236,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:wimp_BUTTON_CLICK
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:"History"
+ text.size:*
+ text.validation:""
+}
+
+wimp_window {
+ template_name:"info"
+ visible:268,838,888,1086
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_CREAM
+ scroll_inner:wimp_COLOUR_ORANGE
+ highlight_bg:wimp_COLOUR_LIGHT_GREY
+ extra_flags:
+ extent:0,-248,620,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:wimp_BUTTON_CLICK
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:"Über dieses Programm"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:676,-204,852,-156
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_RED
+ text_only:"OK"
+ }
+ wimp_icon {
+ extent:152,-60,612,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"NetSurf"
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:152,-120,612,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Open source web browser"
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:152,-180,612,-128
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"© NetSurf developers"
+ text.size:40
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:152,-240,612,-188
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"CVS test build"
+ text.size:40
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:60,-56,148,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Name"
+ }
+ wimp_icon {
+ extent:48,-116,148,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Zweck"
+ }
+ wimp_icon {
+ extent:56,-176,148,-132
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Autor"
+ }
+ wimp_icon {
+ extent:24,-236,148,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Version"
+ }
+}
+
+wimp_window {
+ template_name:"login"
+ visible:710,422,1386,758
+ xscroll:0
+ yscroll:-8
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-344,676,-8
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:676
+ ymin:336
+ text.text:"Authentifizierung"
+ text.size:20
+ text.validation:""
+ wimp_icon {
+ extent:532,-332,664,-264
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Login"
+ text.size:8
+ text.validation:"R6,3;Nok"
+ }
+ wimp_icon {
+ extent:376,-324,508,-272
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Abbruch"
+ text.size:*
+ text.validation:"R5,3;Ncancel"
+ }
+ wimp_icon {
+ extent:168,-68,668,-16
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"moo.yoo.com"
+ text.size:255
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:168,-128,668,-76
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"my sekr3t area"
+ text.size:255
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:168,-188,668,-136
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:255
+ text.validation:"Pptr_write;Kta;N401username"
+ }
+ wimp_icon {
+ extent:168,-248,668,-196
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:255
+ text.validation:"Pptr_write;Kta;D*"
+ }
+ wimp_icon {
+ extent:88,-64,164,-20
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Host"
+ }
+ wimp_icon {
+ extent:16,-184,164,-140
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Username"
+ }
+ wimp_icon {
+ extent:24,-244,164,-200
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Passwort"
+ }
+ wimp_icon {
+ extent:68,-124,164,-80
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Realm"
+ }
+}
+
+wimp_window {
+ template_name:"new_entry"
+ visible:1120,590,1720,810
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-220,600,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:600
+ ymin:220
+ text.text:""
+ text.size:32
+ text.validation:""
+ wimp_icon {
+ extent:12,-56,108,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Name"
+ }
+ wimp_icon {
+ extent:112,-60,588,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:128
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:12,-116,108,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"URL"
+ }
+ wimp_icon {
+ extent:112,-120,532,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:1024
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:304,-200,436,-148
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Abbruch"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:456,-208,588,-140
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"OK"
+ text.size:8
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:540,-116,584,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+}
+
+wimp_window {
+ template_name:"new_folder"
+ visible:480,954,1080,1114
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-160,600,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:600
+ ymin:160
+ text.text:""
+ text.size:21
+ text.validation:""
+ wimp_icon {
+ extent:12,-56,108,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Name"
+ }
+ wimp_icon {
+ extent:112,-60,588,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:128
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:304,-140,436,-88
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Abbruch"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:456,-148,588,-80
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"OK"
+ text.size:8
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"objectinfo"
+ visible:272,900,1060,1088
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-188,788,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:wimp_BUTTON_CLICK
+ sprite_area:&1
+ xmin:788
+ ymin:188
+ text.text:"Über das Objekt"
+ text.size:20
+ text.validation:""
+ wimp_icon {
+ extent:204,-60,780,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:204,-120,780,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:204,-180,780,-128
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:12,-80,84,-12
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite.id:"file_faf"
+ sprite.size:*
+ sprite.area:&1
+ }
+ wimp_icon {
+ extent:124,-116,200,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Ziel"
+ }
+ wimp_icon {
+ extent:136,-176,200,-132
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Typ"
+ }
+ wimp_icon {
+ extent:128,-56,200,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"URL"
+ }
+}
+
+wimp_window {
+ template_name:"open_url"
+ visible:438,126,1238,282
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-156,800,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:"Webseite öffnen"
+ text.size:25
+ text.validation:""
+ wimp_icon {
+ extent:20,-56,92,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"URL"
+ }
+ wimp_icon {
+ extent:96,-60,736,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:1
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:504,-136,632,-84
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Abbruch"
+ text.size:21
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:656,-144,788,-76
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Öffnen"
+ text.size:8
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:744,-56,788,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+}
+
+wimp_window {
+ template_name:"pageinfo"
+ visible:576,224,1368,472
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-248,792,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:wimp_BUTTON_CLICK
+ sprite_area:&1
+ xmin:792
+ ymin:248
+ text.text:"Über das Dokument"
+ text.size:20
+ text.validation:""
+ wimp_icon {
+ extent:208,-60,784,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:208,-120,784,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:208,-180,784,-128
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:208,-240,784,-188
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:12,-80,84,-12
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite.id:"file_faf"
+ sprite.size:*
+ sprite.area:&1
+ }
+ wimp_icon {
+ extent:132,-116,204,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"URL"
+ }
+ wimp_icon {
+ extent:48,-176,204,-132
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Kodierung"
+ }
+ wimp_icon {
+ extent:140,-236,204,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Typ"
+ }
+ wimp_icon {
+ extent:112,-56,204,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Titel"
+ }
+}
+
+wimp_window {
+ template_name:"print"
+ visible:1204,134,1804,706
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-572,600,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:600
+ ymin:572
+ text.text:"kein Drucker"
+ text.size:20
+ text.validation:""
+ wimp_icon {
+ extent:12,-176,588,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-100,504,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:1
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Ende der Web-Seite"
+ text_and_sprite.size:27
+ text_and_sprite.validation:"Sradiooff,radioon"
+ }
+ wimp_icon {
+ extent:32,-152,492,-108
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:1
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:6
+ text_and_sprite.validation:"Sradiooff,radioon"
+ }
+ wimp_icon {
+ extent:84,-156,164,-104
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"1"
+ text.size:3
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:176,-148,208,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:212,-148,244,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:248,-152,476,-108
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Seiten"
+ text.size:18
+ text.validation:""
+ }
+ wimp_icon {
+ extent:12,-236,452,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Vordergrundbilder zeigen"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:12,-288,452,-244
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Hintergrundbilder zeigen"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:12,-340,420,-296
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"im Hintergrund drucken"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:12,-404,196,-360
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Portrait"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Sradiooff,radioon"
+ }
+ wimp_icon {
+ extent:12,-452,228,-408
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Landschaft"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Sradiooff,radioon"
+ }
+ wimp_icon {
+ extent:432,-456,512,-404
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"1"
+ text.size:3
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:524,-448,556,-416
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:556,-448,588,-416
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:284,-552,416,-500
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Abbruch"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:440,-560,588,-492
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Drucken"
+ text.size:*
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:320,-452,428,-408
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Kopien"
+ }
+ wimp_icon {
+ extent:-4,-480,608,-472
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:32,-52,364,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Druck beenden nach "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:252,-400,596,-356
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Text immer schwarz"
+ text_and_sprite.size:24
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+}
+
+wimp_window {
+ template_name:"query"
+ visible:426,1082,1226,1326
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_LIGHT_GREY
+ extra_flags:
+ extent:0,-244,800,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:"Sicherheitsabfrage von NetSurf"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:92,-148,792,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Message"
+ text.size:300
+ text.validation:"R2;L"
+ }
+ wimp_icon {
+ extent:636,-232,788,-164
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"012345678901234567"
+ text.size:*
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:504,-224,620,-172
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"012345678901234567"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:16,-224,132,-172
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Hilfe"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:12,-80,80,-12
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite_only:"!netsurf"
+ }
+}
+
+wimp_window {
+ template_name:"saveas"
+ visible:618,322,950,570
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-248,332,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text_only:"Abspeichern"
+ wimp_icon {
+ extent:132,-84,200,-16
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite.id:"file_faf"
+ sprite.size:13
+ sprite.area:&1
+ }
+ wimp_icon {
+ extent:8,-152,324,-96
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:256
+ text.validation:"Pptr_write"
+ }
+ wimp_icon {
+ extent:172,-236,320,-168
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Sichern"
+ text.size:*
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:20,-228,148,-176
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Abbruch"
+ text.size:*
+ text.validation:"R5,3"
+ }
+}
+
+wimp_window {
+ template_name:"search"
+ visible:384,780,1028,1024
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-244,644,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:644
+ ymin:244
+ text.text:"Text suchen"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:96,-60,580,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:32
+ text.validation:"KN;Pptr_write"
+ }
+ wimp_icon {
+ extent:96,-116,352,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Groß/klein"
+ text_and_sprite.size:15
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:500,-228,632,-160
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"vor"
+ text.size:10
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:348,-220,484,-168
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"zurück"
+ text.size:9
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:204,-220,332,-168
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Abbruch"
+ text.size:12
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:16,-216,196,-172
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Not found"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:-8,-148,652,-140
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:16,-56,92,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Text"
+ }
+ wimp_icon {
+ extent:588,-56,632,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_ICON_SHADED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:352,-116,580,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Alle zeigen"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+}
+
+wimp_window {
+ template_name:"theme_inst"
+ visible:384,134,1184,378
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-244,800,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:"NetSurf Themen-Installation"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:92,-148,792,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Message"
+ text.size:300
+ text.validation:"R2;L"
+ }
+ wimp_icon {
+ extent:540,-232,788,-164
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Installieren"
+ text.size:*
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:394,-224,522,-172
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Abbruch"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:12,-80,80,-12
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite_only:"!netsurf"
+ }
+}
+
+wimp_window {
+ template_name:"tooltip"
+ visible:884,620,1148,656
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-36,2000,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text_only:"<Untitled>"
+ wimp_icon {
+ extent:0,-40,300,4
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_ICON_SELECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Kommentar"
+ text.size:256
+ text.validation:""
+ }
+}
+
+wimp_window {
+ template_name:"tree"
+ visible:530,654,1042,954
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_SCROLL_REPEAT | wimp_WINDOW_IGNORE_XEXTENT | wimp_WINDOW_IGNORE_YEXTENT | wimp_WINDOW_BACK_ICON | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_TOGGLE_ICON | wimp_WINDOW_VSCROLL | wimp_WINDOW_SIZE_ICON | wimp_WINDOW_HSCROLL | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_TRANSPARENT
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-880,1236,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:wimp_BUTTON_DOUBLE_CLICK_DRAG
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:""
+ text.size:64
+ text.validation:""
+}
+
+wimp_window {
+ template_name:"url_suggest"
+ visible:320,390,1102,684
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_NO_BOUNDS | wimp_WINDOW_IGNORE_XEXTENT | wimp_WINDOW_IGNORE_YEXTENT | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_TRANSPARENT
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-65536,65536,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:wimp_BUTTON_CLICK
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text_only:"<Untitled>"
+}
+
+wimp_window {
+ template_name:"warning"
+ visible:320,524,1120,768
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-244,800,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:"Warnung von NetSurf"
+ text.size:21
+ text.validation:""
+ wimp_icon {
+ extent:92,-148,792,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Message"
+ text.size:300
+ text.validation:"R2;L"
+ }
+ wimp_icon {
+ extent:624,-232,788,-164
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Weiter"
+ text.size:9
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:480,-224,600,-172
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Hilfe"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:12,-80,80,-12
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite_only:"!netsurf"
+ }
+}
+
+wimp_window {
+ template_name:"zoom"
+ visible:694,830,1142,1054
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-224,448,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:448
+ ymin:224
+ text.text:"Ansicht skalieren"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:8,-56,104,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Größe"
+ }
+ wimp_icon {
+ extent:108,-60,208,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"100"
+ text.size:5
+ text.validation:"Pptr_write;KTA;A0-9."
+ }
+ wimp_icon {
+ extent:220,-52,252,-20
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:252,-52,284,-20
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sup,pup"
+ }
+ wimp_icon {
+ extent:288,-56,328,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_HCENTRED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"%"
+ }
+ wimp_icon {
+ extent:108,-112,420,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Frames skalieren"
+ text_and_sprite.size:20
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:-4,-132,548,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:84,-204,216,-152
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Abbruch"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:240,-212,436,-144
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Skalieren"
+ text.size:*
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_inter"
+ visible:1094,146,1838,918
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-772,744,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:744
+ ymin:584
+ text_only:"Nützliches"
+ wimp_icon {
+ extent:16,-168,728,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,492,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Downloading / saving files "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-92,696,-48
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Erweiterungen beim Speichern entfernen"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:32,-152,676,-108
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"vor Überschreiben von Dateien fragen"
+ text_and_sprite.size:46
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:16,-344,728,-200
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:36,-224,496,-180
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Interactive features "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-268,744,-224
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"aktuellste URLs beim Tippen anzeigen"
+ text_and_sprite.size:42
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:32,-328,872,-284
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"URLs in lokaler History einblenden"
+ text_and_sprite.size:44
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:16,-468,728,-372
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:36,-400,240,-356
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Thumbnails "
+ text_and_sprite.size:23
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-452,648,-408
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Thumbnails zeigen beim Iconisieren"
+ text_and_sprite.size:42
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:24,-740,188,-688
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Standard"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:360,-740,524,-688
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Abbruch"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:540,-748,724,-680
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"OK"
+ text.size:4
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:16,-660,728,-492
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:36,-520,240,-476
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Hotlist"
+ text_and_sprite.size:23
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-572,728,-528
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Use external hotlist apps when available"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:28,-640,232,-588
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Hotlist path"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:232,-640,708,-588
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"Writable icon"
+ text.size:256
+ text.validation:"Pptr_write;Kta"
+ }
+}
+
+wimp_window {
+ template_name:"con_secure"
+ visible:440,540,1032,904
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-364,592,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:592
+ ymin:364
+ text_only:"Sicherheit"
+ wimp_icon {
+ extent:16,-120,576,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,364,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Cross-site privacy "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-96,568,-52
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Seitenreferenzen senden"
+ text_and_sprite.size:31
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:16,-252,576,-148
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-176,316,-132
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Site history "
+ text_and_sprite.size:18
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-224,172,-180
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"behalten"
+ }
+ wimp_icon {
+ extent:184,-228,344,-176
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"12"
+ text.size:4
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:358,-216,390,-184
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:390,-220,422,-188
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:434,-224,510,-180
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Tage"
+ }
+ wimp_icon {
+ extent:24,-336,188,-284
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Standard"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:208,-336,372,-284
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Abbruch"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:388,-344,572,-276
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"OK"
+ text.size:4
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_connect"
+ visible:902,402,1562,1070
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-668,660,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:660
+ ymin:668
+ text_only:"Verbindung"
+ wimp_icon {
+ extent:16,-304,644,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:28,-52,264,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" HTTP Proxy "
+ text_and_sprite.size:15
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:12,-104,184,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Proxytyp"
+ }
+ wimp_icon {
+ extent:188,-108,572,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:32
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:580,-104,624,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:108,-164,184,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Host"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:188,-168,504,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"myhost.proxy"
+ text.size:255
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:500,-164,528,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_HCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:":"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:524,-168,628,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"8080"
+ text.size:8
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:36,-224,184,-180
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Username"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:188,-228,628,-176
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"k1dd13"
+ text.size:64
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:36,-284,184,-240
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Passwort"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:188,-288,628,-236
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"1337"
+ text.size:64
+ text.validation:"Pptr_write;Kta;D*"
+ }
+ wimp_icon {
+ extent:16,-552,644,-332
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-360,268,-316
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Fetching "
+ text_and_sprite.size:15
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:52,-412,340,-368
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Fetches maximal"
+ text.size:18
+ text.validation:""
+ }
+ wimp_icon {
+ extent:344,-416,552,-364
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"99"
+ text.size:*
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:560,-408,592,-376
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:592,-408,624,-376
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:56,-472,340,-428
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Fetches pro Host"
+ text.size:18
+ text.validation:""
+ }
+ wimp_icon {
+ extent:344,-476,552,-424
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"99"
+ text.size:4
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:560,-468,596,-436
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:592,-468,624,-436
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:32,-532,340,-488
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Verbindungen cachen"
+ text.size:22
+ text.validation:""
+ }
+ wimp_icon {
+ extent:344,-536,552,-484
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"99"
+ text.size:4
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:560,-528,596,-496
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:592,-528,624,-496
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:24,-640,188,-588
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Standard"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:276,-640,440,-588
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Abbruch"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:456,-648,640,-580
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"OK"
+ text.size:4
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"ssldisplay"
+ visible:212,142,1172,682
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-540,960,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:960
+ ymin:76
+ text.text:"SSL Zertifikat"
+ text.size:16
+ text.validation:""
+ wimp_icon {
+ extent:432,-168,928,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:16,-520,944,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:24,-108,148,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Version"
+ }
+ wimp_icon {
+ extent:152,-108,264,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:260,-104,432,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"gültig von"
+ }
+ wimp_icon {
+ extent:432,-108,928,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:68,-168,148,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Typ"
+ }
+ wimp_icon {
+ extent:152,-168,264,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:244,-164,432,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"gültig bis"
+ }
+ wimp_icon {
+ extent:32,-52,380,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Certificate details "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:40,-228,148,-184
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Serial"
+ }
+ wimp_icon {
+ extent:152,-228,928,-176
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:40,-288,148,-244
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Issuer"
+ }
+ wimp_icon {
+ extent:152,-376,928,-236
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2;L"
+ }
+ wimp_icon {
+ extent:24,-432,148,-388
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Subject"
+ }
+ wimp_icon {
+ extent:152,-504,928,-384
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2;L"
+ }
+}
+
+wimp_window {
+ template_name:"sslcert"
+ visible:348,306,1136,898
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-592,788,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:788
+ ymin:592
+ text.text:"SSL Zertifizierungsproblem"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:16,-108,772,-16
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"NetSurf konnte ein SSL Zertifikat nicht prüfen. Bitte die Details unten beachten."
+ text.size:150
+ text.validation:"R2;L"
+ }
+ wimp_icon {
+ extent:16,-484,772,-136
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-164,380,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Certificate chain "
+ text_and_sprite.size:22
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:404,-564,568,-512
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Ablehnen"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:588,-572,772,-504
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annehmen"
+ text.size:12
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_content"
+ visible:1404,424,2044,924
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-500,640,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:640
+ ymin:452
+ text_only:"Content"
+ wimp_icon {
+ extent:16,-272,624,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,492,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Content blocking "
+ text_and_sprite.size:29
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-100,436,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Werbung unterdrücken"
+ text_and_sprite.size:39
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:32,-152,528,-108
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Pop-Up Fenster abschalten"
+ text_and_sprite.size:46
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:32,-256,496,-212
+#ifdef WITH_PLUGIN
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+#else
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_DELETED | wimp_BUTTON_RADIO
+#endif
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Plugins nicht benutzen"
+ text_and_sprite.size:42
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:16,-384,624,-292
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-320,492,-276
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Link targets "
+ text_and_sprite.size:29
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-368,612,-324
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Links dürfen neue Fenster öffnen"
+ text_and_sprite.size:35
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:24,-468,188,-416
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Standard"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:256,-468,420,-416
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Abbruch"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:436,-476,620,-408
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"OK"
+ text.size:4
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:32,-204,376,-160
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Disable JavaScript"
+ text_and_sprite.size:19
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+}
diff --git a/frontends/riscos/templates/en b/frontends/riscos/templates/en
new file mode 100644
index 000000000..7746b86cc
--- /dev/null
+++ b/frontends/riscos/templates/en
@@ -0,0 +1,3837 @@
+Template:
+
+wimp_window {
+ template_name:"configure"
+ visible:378,966,1050,1126
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_TOGGLE_ICON | wimp_WINDOW_VSCROLL | wimp_WINDOW_SIZE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_LIGHT_GREY
+ extra_flags:
+ extent:0,-880,1236,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:4
+ ymin:160
+ text.text:"NetSurf configuration"
+ text.size:*
+ text.validation:""
+}
+
+wimp_window {
+ template_name:"con_cache"
+ visible:184,682,772,1110
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-428,588,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:588
+ ymin:240
+ text_only:"Cache"
+ wimp_icon {
+ extent:16,-124,568,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,268,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Memory cache "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:44,-104,164,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Size"
+ text.size:13
+ text.validation:""
+ }
+ wimp_icon {
+ extent:168,-108,336,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"512.0"
+ text.size:10
+ text.validation:"Pptr_write;Kta;A0-9."
+ }
+ wimp_icon {
+ extent:352,-100,384,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:384,-100,416,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:424,-104,480,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"MB"
+ }
+ wimp_icon {
+ extent:16,-312,568,-152
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-180,268,-136
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Disc cache "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:44,-232,164,-188
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Size"
+ text.size:13
+ text.validation:""
+ }
+ wimp_icon {
+ extent:168,-236,336,-184
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"512"
+ text.size:10
+ text.validation:"Pptr_write;Kta;A0-9"
+ }
+ wimp_icon {
+ extent:352,-228,384,-196
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:384,-228,416,-196
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:424,-232,480,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"MB"
+ }
+ wimp_icon {
+ extent:44,-292,164,-248
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Expiry"
+ text.size:13
+ text.validation:""
+ }
+ wimp_icon {
+ extent:168,-296,336,-244
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"512"
+ text.size:10
+ text.validation:"Pptr_write;Kta;A0-9"
+ }
+ wimp_icon {
+ extent:352,-288,384,-256
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:384,-288,416,-256
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:424,-292,500,-252
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"days"
+ }
+ wimp_icon {
+ extent:24,-396,188,-344
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Default"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:204,-396,368,-344
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Cancel"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:384,-404,568,-336
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Set"
+ text.size:*
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_fonts"
+ visible:558,802,1282,1526
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-724,724,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:712
+ ymin:724
+ text_only:"Fonts"
+ wimp_icon {
+ extent:16,-424,704,-28
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,236,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Font faces "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:28,-104,200,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Sans-serif"
+ }
+ wimp_icon {
+ extent:204,-108,628,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:640,-104,684,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:108,-164,200,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Serif"
+ }
+ wimp_icon {
+ extent:204,-168,628,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:640,-164,684,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:36,-224,200,-180
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Monospace"
+ }
+ wimp_icon {
+ extent:204,-228,628,-176
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:640,-224,684,-180
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:76,-284,200,-240
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Cursive"
+ }
+ wimp_icon {
+ extent:204,-288,628,-236
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:640,-284,684,-240
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:72,-344,200,-300
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Fantasy"
+ }
+ wimp_icon {
+ extent:204,-348,628,-296
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:640,-344,684,-300
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:76,-404,200,-360
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Default"
+ }
+ wimp_icon {
+ extent:204,-408,628,-356
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:640,-404,684,-360
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:16,-612,704,-452
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-480,220,-436
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Font size "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:76,-532,200,-488
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Default"
+ }
+ wimp_icon {
+ extent:204,-536,372,-484
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"12.3"
+ text.size:10
+ text.validation:"Pptr_write;Kta;A0-9."
+ }
+ wimp_icon {
+ extent:388,-528,420,-496
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:420,-528,452,-496
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:460,-532,500,-488
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"pt"
+ }
+ wimp_icon {
+ extent:52,-592,200,-548
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Minimum"
+ }
+ wimp_icon {
+ extent:204,-596,372,-544
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"12.3"
+ text.size:10
+ text.validation:"Pptr_write;Kta;A0-9."
+ }
+ wimp_icon {
+ extent:388,-588,420,-556
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:420,-588,452,-556
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:460,-592,500,-548
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"pt"
+ }
+ wimp_icon {
+ extent:24,-696,188,-644
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Default"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:340,-696,504,-644
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Cancel"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:520,-704,704,-636
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Set"
+ text.size:*
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_home"
+ visible:808,592,1608,888
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-296,800,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:800
+ ymin:296
+ text_only:"Home page"
+ wimp_icon {
+ extent:16,-184,784,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,284,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Home page "
+ text_and_sprite.size:16
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:40,-108,120,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"URL"
+ }
+ wimp_icon {
+ extent:124,-112,712,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:128
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:720,-108,764,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:124,-164,676,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_SELECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Open browser window on start-up"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:24,-264,188,-212
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Default"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:420,-264,584,-212
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Cancel"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:600,-272,784,-204
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Set"
+ text.size:*
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_image"
+ visible:1488,822,2164,1410
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-588,676,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:676
+ ymin:588
+ text_only:"Images"
+ wimp_icon {
+ extent:16,-292,660,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,284,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Image quality "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:28,-108,204,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Foreground"
+ }
+ wimp_icon {
+ extent:208,-112,592,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Error diffused"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:600,-108,644,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:24,-168,204,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Background"
+ }
+ wimp_icon {
+ extent:208,-172,592,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Error diffused"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:600,-168,644,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:32,-276,644,-180
+ icon_flags:wimp_ICON_BORDER
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ }
+ wimp_icon {
+ extent:16,-476,660,-320
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-348,236,-304
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Animations "
+ text_and_sprite.size:16
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:20,-404,208,-360
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Speed limit"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:212,-408,380,-356
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"12.34"
+ text.size:*
+ text.validation:"Pptr_write;Kta;A0-9."
+ }
+ wimp_icon {
+ extent:396,-400,428,-368
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:428,-400,460,-368
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:468,-404,592,-360
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"seconds"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:212,-460,556,-416
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Disable animations"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:24,-560,188,-508
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Default"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:292,-560,456,-508
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Cancel"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:472,-568,656,-500
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Set"
+ text.size:*
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_inter"
+ visible:2320,436,3164,1220
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-784,844,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:744
+ ymin:584
+ text_only:"Interface"
+ wimp_icon {
+ extent:16,-168,820,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,492,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Downloading / saving files "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-100,680,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Strip filename extensions when saving"
+ text_and_sprite.size:39
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:32,-152,808,-108
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Request confirmation before overwriting files"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:16,-344,820,-200
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:36,-224,496,-180
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Interactive features "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-276,744,-232
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Display recently visited URLs as you type"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:32,-328,776,-284
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Hover URLs by the pointer for local history"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:16,-468,820,-372
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:36,-400,240,-356
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Thumbnails "
+ text_and_sprite.size:23
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-452,648,-408
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Use thumbnails for iconised windows"
+ text_and_sprite.size:42
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:24,-748,188,-696
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Default"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:456,-748,620,-696
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Cancel"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:636,-756,820,-688
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Set"
+ text.size:*
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:16,-664,820,-496
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:36,-524,240,-480
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Hotlist"
+ text_and_sprite.size:23
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-576,728,-532
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Use external hotlist apps when available"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:28,-644,232,-592
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Hotlist path"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:232,-644,800,-592
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"Writable icon"
+ text.size:256
+ text.validation:"Pptr_write;Kta"
+ }
+}
+
+wimp_window {
+ template_name:"con_lang"
+ visible:1574,956,2318,1256
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-300,744,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:744
+ ymin:300
+ text_only:"Language"
+ wimp_icon {
+ extent:16,-188,728,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,284,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Language "
+ text_and_sprite.size:16
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:48,-108,204,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Interface"
+ }
+ wimp_icon {
+ extent:208,-112,660,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"1337"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:668,-108,712,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:44,-168,204,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Web pages"
+ }
+ wimp_icon {
+ extent:208,-172,660,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"8008135"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:668,-168,712,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:24,-272,188,-220
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Default"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:360,-272,524,-220
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Cancel"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:540,-280,724,-212
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Set"
+ text.size:*
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_theme"
+ visible:410,38,1318,642
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-604,908,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:640
+ ymin:604
+ text_only:"Themes"
+ wimp_icon {
+ extent:16,-492,892,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,332,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Available themes "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:24,-576,188,-524
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Default"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:524,-576,688,-524
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Cancel"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:704,-584,888,-516
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Set"
+ text.size:*
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"download"
+ visible:486,610,1394,890
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-280,908,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:80
+ ymin:28
+ text.text:"NetSurf Download"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:420,-84,488,-16
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite.id:"file_ddc"
+ sprite.size:*
+ sprite.area:&1
+ }
+ wimp_icon {
+ extent:204,-152,900,-100
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"http://netsurf.sourceforge.net/netsurf.zip"
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:204,-212,900,-160
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"netsurf"
+ text.size:*
+ text.validation:"Pptr_write"
+ }
+ wimp_icon {
+ extent:204,-212,900,-160
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"ADFS::A7000+.$.netsurf"
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:8,-272,900,-220
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:12,-268,296,-224
+ icon_flags:wimp_ICON_FILLED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_CREAM
+ }
+ wimp_icon {
+ extent:12,-268,896,-224
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"84.4KB of 1.1MB 26.6KB/s 0:39 remaining"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:92,-148,200,-104
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Source"
+ }
+ wimp_icon {
+ extent:12,-208,200,-164
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Destination"
+ }
+}
+
+wimp_window {
+ template_name:"history"
+ visible:252,388,1152,808
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_SCROLL_REPEAT | wimp_WINDOW_BACK_ICON | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_TOGGLE_ICON | wimp_WINDOW_VSCROLL | wimp_WINDOW_SIZE_ICON | wimp_WINDOW_HSCROLL | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_WHITE
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-880,1236,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:wimp_BUTTON_CLICK
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:"History"
+ text.size:*
+ text.validation:""
+}
+
+wimp_window {
+ template_name:"info"
+ visible:752,452,1372,700
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_CREAM
+ scroll_inner:wimp_COLOUR_ORANGE
+ highlight_bg:wimp_COLOUR_LIGHT_GREY
+ extra_flags:
+ extent:0,-248,620,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ work_flags:wimp_BUTTON_CLICK
+ sprite_area:&1
+ xmin:620
+ ymin:248
+ text.text:"About this program"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:672,-200,848,-152
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_RED
+ text_only:"OK"
+ }
+ wimp_icon {
+ extent:152,-60,612,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"NetSurf"
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:152,-120,612,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Open source web browser"
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:152,-180,612,-128
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"© NetSurf developers"
+ text.size:40
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:152,-240,612,-188
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"CVS test build"
+ text.size:40
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:52,-56,148,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Name"
+ }
+ wimp_icon {
+ extent:24,-116,148,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Purpose"
+ }
+ wimp_icon {
+ extent:24,-176,148,-132
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Authors"
+ }
+ wimp_icon {
+ extent:24,-236,148,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Version"
+ }
+}
+
+wimp_window {
+ template_name:"login"
+ visible:582,400,1258,736
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-336,676,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:676
+ ymin:336
+ text.text:"Site Authentication"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:532,-324,664,-256
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Login"
+ text.size:8
+ text.validation:"R6,3;Nok"
+ }
+ wimp_icon {
+ extent:380,-316,508,-264
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Cancel"
+ text.size:*
+ text.validation:"R5,3;Ncancel"
+ }
+ wimp_icon {
+ extent:168,-60,668,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"moo.yoo.com"
+ text.size:255
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:168,-120,668,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"my sekr3t area"
+ text.size:255
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:168,-180,668,-128
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:255
+ text.validation:"Pptr_write;Kta;N401username"
+ }
+ wimp_icon {
+ extent:168,-240,668,-188
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:255
+ text.validation:"Pptr_write;Kta;D*"
+ }
+ wimp_icon {
+ extent:84,-56,164,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Host"
+ }
+ wimp_icon {
+ extent:8,-176,164,-132
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Username"
+ }
+ wimp_icon {
+ extent:20,-236,164,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Password"
+ }
+ wimp_icon {
+ extent:64,-116,164,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Realm"
+ }
+}
+
+wimp_window {
+ template_name:"new_entry"
+ visible:480,660,1080,880
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-220,600,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:600
+ ymin:220
+ text.text:""
+ text.size:32
+ text.validation:""
+ wimp_icon {
+ extent:12,-56,108,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Name"
+ }
+ wimp_icon {
+ extent:112,-60,588,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:128
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:12,-116,108,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"URL"
+ }
+ wimp_icon {
+ extent:112,-120,532,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:1024
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:304,-200,432,-148
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Cancel"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:456,-208,588,-140
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"OK"
+ text.size:8
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:540,-116,584,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+}
+
+wimp_window {
+ template_name:"new_folder"
+ visible:480,954,1080,1114
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-160,600,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:600
+ ymin:160
+ text.text:""
+ text.size:21
+ text.validation:""
+ wimp_icon {
+ extent:12,-56,108,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Name"
+ }
+ wimp_icon {
+ extent:112,-60,588,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:128
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:304,-140,432,-88
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Cancel"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:456,-148,588,-80
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"OK"
+ text.size:8
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"objectinfo"
+ visible:428,292,1216,480
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-188,788,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:wimp_BUTTON_CLICK
+ sprite_area:&1
+ xmin:788
+ ymin:188
+ text.text:"About this object"
+ text.size:20
+ text.validation:""
+ wimp_icon {
+ extent:204,-60,780,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:204,-120,780,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:204,-180,780,-128
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:12,-80,84,-12
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite.id:"file_faf"
+ sprite.size:*
+ sprite.area:&1
+ }
+ wimp_icon {
+ extent:92,-116,200,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Target"
+ }
+ wimp_icon {
+ extent:120,-176,200,-132
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Type"
+ }
+ wimp_icon {
+ extent:120,-56,200,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"URL"
+ }
+}
+
+wimp_window {
+ template_name:"open_url"
+ visible:248,266,1048,422
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-156,800,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text_only:"Open URL"
+ wimp_icon {
+ extent:12,-56,92,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"URL"
+ }
+ wimp_icon {
+ extent:96,-60,736,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:1
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:504,-136,632,-84
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Cancel"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:656,-144,788,-76
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Open"
+ text.size:8
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:744,-56,788,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+}
+
+wimp_window {
+ template_name:"pageinfo"
+ visible:310,528,1102,776
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-248,792,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:792
+ ymin:248
+ text.text:"About this document"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:208,-60,784,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:208,-120,784,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:208,-180,784,-128
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:208,-240,784,-188
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:12,-80,84,-12
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite.id:"file_faf"
+ sprite.size:*
+ sprite.area:&1
+ }
+ wimp_icon {
+ extent:124,-116,204,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"URL"
+ }
+ wimp_icon {
+ extent:60,-176,204,-132
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Encoding"
+ }
+ wimp_icon {
+ extent:124,-236,204,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Type"
+ }
+ wimp_icon {
+ extent:112,-56,204,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Title"
+ }
+}
+
+wimp_window {
+ template_name:"print"
+ visible:472,136,1132,708
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-572,700,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:600
+ ymin:572
+ text.text:"No Printer"
+ text.size:20
+ text.validation:""
+ wimp_icon {
+ extent:12,-176,652,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-100,504,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:1
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"the bottom of the web page"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Sradiooff,radioon"
+ }
+ wimp_icon {
+ extent:32,-152,492,-108
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:1
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:6
+ text_and_sprite.validation:"Sradiooff,radioon"
+ }
+ wimp_icon {
+ extent:84,-156,164,-104
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"1"
+ text.size:3
+ text.validation:"Pptr_write;Kta;A0-9"
+ }
+ wimp_icon {
+ extent:176,-148,208,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:208,-148,240,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:244,-152,528,-108
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"sheets are filled"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:12,-236,420,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Show foreground images"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:12,-288,420,-244
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Show background images"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:12,-340,372,-296
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Print in background"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:12,-404,180,-360
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_SELECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Upright"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Sradiooff,radioon"
+ }
+ wimp_icon {
+ extent:12,-452,196,-408
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Sideways"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Sradiooff,radioon"
+ }
+ wimp_icon {
+ extent:480,-456,560,-404
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"1"
+ text.size:3
+ text.validation:"Pptr_write;Kta;A0-9"
+ }
+ wimp_icon {
+ extent:572,-448,604,-416
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:604,-448,636,-416
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:364,-552,492,-500
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Cancel"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:516,-560,648,-492
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Print"
+ text.size:8
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:368,-452,476,-408
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Copies"
+ }
+ wimp_icon {
+ extent:-16,-480,676,-472
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:32,-48,364,-4
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" End printing after "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:232,-400,656,-356
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Print all text in black"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+}
+
+wimp_window {
+ template_name:"query"
+ visible:142,562,942,806
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_LIGHT_GREY
+ extra_flags:
+ extent:0,-244,800,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:"Query from NetSurf"
+ text.size:21
+ text.validation:""
+ wimp_icon {
+ extent:92,-148,792,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Message"
+ text.size:300
+ text.validation:"R2;L"
+ }
+ wimp_icon {
+ extent:604,-232,788,-164
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"012345678901234567"
+ text.size:*
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:424,-224,588,-172
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"012345678901234567"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:16,-224,132,-172
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Help"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:12,-80,80,-12
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite_only:"!netsurf"
+ }
+}
+
+wimp_window {
+ template_name:"saveas"
+ visible:824,676,1140,924
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-248,316,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:308
+ ymin:244
+ text_only:"Save as"
+ wimp_icon {
+ extent:124,-84,192,-16
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite.id:"file_faf"
+ sprite.size:13
+ sprite.area:&1
+ }
+ wimp_icon {
+ extent:8,-152,308,-100
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:256
+ text.validation:"Pptr_write"
+ }
+ wimp_icon {
+ extent:172,-236,304,-168
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Save"
+ text.size:*
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:20,-228,148,-176
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Cancel"
+ text.size:*
+ text.validation:"R5,3"
+ }
+}
+
+wimp_window {
+ template_name:"search"
+ visible:1036,684,1680,928
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-244,644,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:644
+ ymin:244
+ text.text:"Find text"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:96,-60,580,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:32
+ text.validation:"KN;Pptr_write"
+ }
+ wimp_icon {
+ extent:96,-116,376,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Case sensitive"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:500,-228,632,-160
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Next"
+ text.size:10
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:348,-220,484,-168
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Previous"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:204,-220,332,-168
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Cancel"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:16,-216,196,-172
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Not found"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:-8,-148,652,-140
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:16,-56,92,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Find"
+ }
+ wimp_icon {
+ extent:588,-56,632,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_ICON_SHADED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:408,-116,592,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Show all"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+}
+
+wimp_window {
+ template_name:"theme_inst"
+ visible:374,590,1174,834
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-244,800,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:800
+ ymin:244
+ text.text:"NetSurf theme installer"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:92,-148,792,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Message"
+ text.size:300
+ text.validation:"R2;L"
+ }
+ wimp_icon {
+ extent:640,-232,788,-164
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Install"
+ text.size:*
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:488,-224,616,-172
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Cancel"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:12,-80,80,-12
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite_only:"!netsurf"
+ }
+}
+
+wimp_window {
+ template_name:"tooltip"
+ visible:884,620,1148,656
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-36,2000,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text_only:"<Untitled>"
+ wimp_icon {
+ extent:0,-40,300,4
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Comment"
+ text.size:256
+ text.validation:""
+ }
+}
+
+wimp_window {
+ template_name:"tree"
+ visible:530,654,1042,954
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_SCROLL_REPEAT | wimp_WINDOW_IGNORE_XEXTENT | wimp_WINDOW_IGNORE_YEXTENT | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_TOGGLE_ICON | wimp_WINDOW_VSCROLL | wimp_WINDOW_SIZE_ICON | wimp_WINDOW_HSCROLL | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_TRANSPARENT
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-880,1236,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:wimp_BUTTON_DOUBLE_CLICK_DRAG
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:""
+ text.size:64
+ text.validation:""
+}
+
+wimp_window {
+ template_name:"url_suggest"
+ visible:320,390,1102,684
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_NO_BOUNDS | wimp_WINDOW_IGNORE_XEXTENT | wimp_WINDOW_IGNORE_YEXTENT | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_TRANSPARENT
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-65536,65536,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:wimp_BUTTON_CLICK
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text_only:"<Untitled>"
+}
+
+wimp_window {
+ template_name:"warning"
+ visible:320,850,1120,1094
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-244,800,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:"Warning from NetSurf"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:92,-148,792,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Message"
+ text.size:300
+ text.validation:"R2;L"
+ }
+ wimp_icon {
+ extent:608,-232,788,-164
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Continue"
+ text.size:*
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:484,-224,584,-172
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Help"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:12,-80,80,-12
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite_only:"!netsurf"
+ }
+}
+
+wimp_window {
+ template_name:"zoom"
+ visible:182,356,630,578
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-224,448,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:448
+ ymin:220
+ text_only:"Scale view"
+ wimp_icon {
+ extent:8,-56,100,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Scale"
+ }
+ wimp_icon {
+ extent:104,-60,204,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"100"
+ text.size:5
+ text.validation:"Pptr_write;KTA;A0-9."
+ }
+ wimp_icon {
+ extent:216,-52,248,-20
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:248,-52,280,-20
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sup,pup"
+ }
+ wimp_icon {
+ extent:284,-56,324,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_HCENTRED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"%"
+ }
+ wimp_icon {
+ extent:104,-112,416,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Scale all frames"
+ text_and_sprite.size:20
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:-4,-132,716,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:164,-204,292,-152
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Cancel"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:308,-212,440,-144
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Scale"
+ text.size:*
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"ssldisplay"
+ visible:862,768,1822,1308
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-540,960,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:960
+ ymin:76
+ text.text:"SSL certificate"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:16,-520,944,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,380,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Certificate details "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:24,-108,148,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Version"
+ }
+ wimp_icon {
+ extent:152,-108,264,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:284,-104,456,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Valid from"
+ }
+ wimp_icon {
+ extent:460,-108,928,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:68,-168,148,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Type"
+ }
+ wimp_icon {
+ extent:152,-168,264,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:268,-164,456,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Valid until"
+ }
+ wimp_icon {
+ extent:460,-168,928,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:40,-228,148,-184
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Serial"
+ }
+ wimp_icon {
+ extent:152,-228,928,-176
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:40,-288,148,-244
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Issuer"
+ }
+ wimp_icon {
+ extent:152,-376,928,-236
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2;L"
+ }
+ wimp_icon {
+ extent:24,-432,148,-388
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Subject"
+ }
+ wimp_icon {
+ extent:152,-504,928,-384
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2;L"
+ }
+}
+
+wimp_window {
+ template_name:"con_secure"
+ visible:1590,788,2182,1152
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-364,592,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:592
+ ymin:364
+ text_only:"Security"
+ wimp_icon {
+ extent:16,-120,576,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,364,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Cross-site privacy "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-104,568,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Send site referral information"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:16,-252,576,-148
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-176,316,-132
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Site history "
+ text_and_sprite.size:18
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:24,-228,164,-184
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Duration"
+ }
+ wimp_icon {
+ extent:168,-232,336,-180
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"12"
+ text.size:4
+ text.validation:"Pptr_write;Kta;A0-9"
+ }
+ wimp_icon {
+ extent:352,-224,384,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:384,-224,416,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:424,-228,500,-184
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"days"
+ }
+ wimp_icon {
+ extent:24,-336,188,-284
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Default"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:208,-336,372,-284
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Cancel"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:388,-344,572,-276
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Set"
+ text.size:*
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_content"
+ visible:1248,854,1912,1354
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-500,664,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:640
+ ymin:452
+ text_only:"Content"
+ wimp_icon {
+ extent:16,-272,644,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,492,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Content blocking "
+ text_and_sprite.size:29
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-100,392,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Hide advertisements"
+ text_and_sprite.size:39
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:32,-152,440,-108
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Disable pop-up windows"
+ text_and_sprite.size:46
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:32,-256,344,-212
+#ifdef WITH_PLUGIN
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+#else
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_DELETED | wimp_BUTTON_RADIO
+#endif
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Disable plug-ins"
+ text_and_sprite.size:42
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:16,-384,644,-292
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-320,492,-276
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Link targets "
+ text_and_sprite.size:29
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-368,632,-324
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Allow links to open in new windows"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:24,-468,188,-416
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Default"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:280,-468,444,-416
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Cancel"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:460,-476,644,-408
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Set"
+ text.size:*
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:32,-204,376,-160
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Disable JavaScript"
+ text_and_sprite.size:19
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+}
+
+wimp_window {
+ template_name:"con_connect"
+ visible:1328,566,2008,1234
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-668,680,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:660
+ ymin:668
+ text_only:"Connection"
+ wimp_icon {
+ extent:16,-304,664,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:28,-52,232,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" HTTP Proxy "
+ text_and_sprite.size:15
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-104,204,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Proxy type"
+ }
+ wimp_icon {
+ extent:208,-108,592,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:32
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:600,-104,644,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:124,-164,204,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Host"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:208,-168,524,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"myhost.proxy"
+ text.size:255
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:520,-164,548,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_HCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:":"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:544,-168,648,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"8080"
+ text.size:8
+ text.validation:"Pptr_write;Kta;A0-9"
+ }
+ wimp_icon {
+ extent:48,-224,204,-180
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Username"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:208,-228,648,-176
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"k1dd13"
+ text.size:64
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:60,-284,204,-240
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Password"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:208,-288,648,-236
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"1337"
+ text.size:64
+ text.validation:"Pptr_write;Kta;D*"
+ }
+ wimp_icon {
+ extent:16,-552,664,-332
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-360,204,-316
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Fetching "
+ text_and_sprite.size:15
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:80,-412,340,-368
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Maximum fetches"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:344,-416,552,-364
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"99"
+ text.size:*
+ text.validation:"Pptr_write;Kta;A0-9"
+ }
+ wimp_icon {
+ extent:568,-408,600,-376
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:600,-408,632,-376
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:72,-472,340,-428
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Fetches per host"
+ text.size:18
+ text.validation:""
+ }
+ wimp_icon {
+ extent:344,-476,552,-424
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"99"
+ text.size:4
+ text.validation:"Pptr_write;Kta;A0-9"
+ }
+ wimp_icon {
+ extent:568,-468,604,-436
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:600,-468,632,-436
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:40,-532,340,-488
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Cached connections"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:344,-536,552,-484
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"99"
+ text.size:4
+ text.validation:"Pptr_write;Kta;A0-9"
+ }
+ wimp_icon {
+ extent:568,-528,604,-496
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:600,-528,632,-496
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:24,-640,188,-588
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Default"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:296,-640,460,-588
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Cancel"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:476,-648,660,-580
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Set"
+ text.size:*
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"sslcert"
+ visible:348,306,1136,898
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-592,788,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:788
+ ymin:592
+ text.text:"SSL certificate problem"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:16,-108,772,-16
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"NetSurf failed to verify the authenticity of an SSL certificate. Please verify the details presented below."
+ text.size:150
+ text.validation:"R2;L"
+ }
+ wimp_icon {
+ extent:16,-484,772,-136
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-164,380,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Certificate chain "
+ text_and_sprite.size:22
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:404,-564,568,-512
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Reject"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:588,-572,772,-504
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Accept"
+ text.size:8
+ text.validation:"R6,3"
+ }
+}
diff --git a/frontends/riscos/templates/fr b/frontends/riscos/templates/fr
new file mode 100644
index 000000000..67792642c
--- /dev/null
+++ b/frontends/riscos/templates/fr
@@ -0,0 +1,3862 @@
+Template:
+
+wimp_window {
+ template_name:"configure"
+ visible:378,820,1046,1126
+ xscroll:0
+ yscroll:-574
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_TOGGLE_ICON | wimp_WINDOW_VSCROLL | wimp_WINDOW_SIZE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_LIGHT_GREY
+ extra_flags:
+ extent:0,-880,1236,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:4
+ ymin:156
+ text.text:"NetSurf configuration"
+ text.size:*
+ text.validation:""
+}
+
+wimp_window {
+ template_name:"con_cache"
+ visible:564,536,1152,964
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-428,588,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:588
+ ymin:240
+ text_only:"Cache"
+ wimp_icon {
+ extent:16,-124,568,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,284,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Cache mémoire "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:44,-104,164,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Taille"
+ text.size:13
+ text.validation:""
+ }
+ wimp_icon {
+ extent:168,-108,336,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"512.0"
+ text.size:10
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:352,-100,384,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:384,-100,416,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:424,-104,480,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"MB"
+ }
+ wimp_icon {
+ extent:16,-312,568,-152
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-180,268,-136
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Disc cache "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:44,-232,164,-188
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Taille"
+ text.size:13
+ text.validation:""
+ }
+ wimp_icon {
+ extent:168,-236,336,-184
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"512"
+ text.size:10
+ text.validation:"Pptr_write;Kta;A0-9"
+ }
+ wimp_icon {
+ extent:352,-228,384,-196
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:384,-228,416,-196
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:424,-232,480,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"MB"
+ }
+ wimp_icon {
+ extent:44,-292,164,-248
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Expiry"
+ text.size:13
+ text.validation:""
+ }
+ wimp_icon {
+ extent:168,-296,336,-244
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"512"
+ text.size:10
+ text.validation:"Pptr_write;Kta;A0-9"
+ }
+ wimp_icon {
+ extent:352,-288,384,-256
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:384,-288,416,-256
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:424,-292,500,-252
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"days"
+ }
+ wimp_icon {
+ extent:24,-396,188,-344
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Par défaut"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:204,-396,368,-344
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuler"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:384,-404,568,-336
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Valider"
+ text.size:*
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_fonts"
+ visible:316,250,1028,974
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-724,712,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:712
+ ymin:724
+ text_only:"Fontes"
+ wimp_icon {
+ extent:16,-424,696,-28
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,236,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Polices "
+ text_and_sprite.size:13
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:20,-104,192,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Sans-serif"
+ }
+ wimp_icon {
+ extent:196,-108,620,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:632,-104,676,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:100,-164,192,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Serif"
+ }
+ wimp_icon {
+ extent:196,-168,620,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:632,-164,676,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:28,-224,192,-180
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Monospace"
+ }
+ wimp_icon {
+ extent:196,-228,620,-176
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:632,-224,676,-180
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:68,-284,192,-240
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Cursive"
+ }
+ wimp_icon {
+ extent:196,-288,620,-236
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:632,-284,676,-240
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:28,-344,192,-300
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Fantaisiste"
+ }
+ wimp_icon {
+ extent:196,-348,620,-296
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:632,-344,676,-300
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:48,-404,192,-360
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Par défaut"
+ }
+ wimp_icon {
+ extent:196,-408,620,-356
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:632,-404,676,-360
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:16,-612,696,-452
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-480,316,-436
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Taille de fonte "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:28,-532,192,-488
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Par défaut"
+ }
+ wimp_icon {
+ extent:196,-536,364,-484
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"12.3"
+ text.size:10
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:380,-528,412,-496
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:412,-528,444,-496
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:452,-532,492,-488
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"pt"
+ }
+ wimp_icon {
+ extent:60,-592,192,-548
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Minimum"
+ }
+ wimp_icon {
+ extent:196,-596,364,-544
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"12.3"
+ text.size:10
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:380,-588,412,-556
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:412,-588,444,-556
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:452,-592,492,-548
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"pt"
+ }
+ wimp_icon {
+ extent:24,-696,188,-644
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Par défaut"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:332,-696,496,-644
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuler"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:512,-704,696,-636
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Valider"
+ text.size:*
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_home"
+ visible:288,150,1088,446
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-296,800,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:800
+ ymin:296
+ text.text:"Page d'accueil"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:16,-184,784,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,300,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Page d'accueil "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:48,-108,120,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"URL"
+ }
+ wimp_icon {
+ extent:124,-112,712,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:128
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:720,-108,764,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:124,-164,676,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Ouvrir une fenêtre au démarrage"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:24,-264,188,-212
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Par défaut"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:416,-264,580,-212
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuler"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:596,-272,780,-204
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Valider"
+ text.size:*
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_image"
+ visible:828,908,1504,1496
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-588,676,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:676
+ ymin:588
+ text_only:"Images"
+ wimp_icon {
+ extent:16,-292,660,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,332,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Qualité d'images "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:24,-108,204,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Premier plan"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:208,-112,592,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Error diffused"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:600,-108,644,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:32,-168,204,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Fond"
+ }
+ wimp_icon {
+ extent:208,-172,592,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Error diffused"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:600,-168,644,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:32,-276,644,-180
+ icon_flags:wimp_ICON_BORDER
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ }
+ wimp_icon {
+ extent:16,-476,660,-320
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-348,284,-304
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Animations "
+ text_and_sprite.size:16
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:20,-404,208,-360
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Vitesse limite"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:212,-408,380,-356
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"12.34"
+ text.size:*
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:396,-400,428,-368
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:428,-400,460,-368
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:468,-404,592,-360
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"secondes"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:212,-460,628,-416
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Désactiver les animations"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:24,-560,188,-508
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Par défaut"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:296,-560,460,-508
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuler"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:476,-568,660,-500
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Valider"
+ text.size:*
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_lang"
+ visible:770,1102,1514,1402
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-300,744,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:744
+ ymin:300
+ text_only:"Langue"
+ wimp_icon {
+ extent:16,-188,728,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,284,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Langue "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:48,-108,204,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Interface"
+ }
+ wimp_icon {
+ extent:208,-112,660,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"1337"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:668,-108,712,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:40,-168,204,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Pages web"
+ }
+ wimp_icon {
+ extent:208,-172,660,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"8008135"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:668,-168,712,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:24,-268,188,-216
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Par défaut"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:360,-268,524,-216
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuler"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:540,-276,724,-208
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Valider"
+ text.size:*
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_theme"
+ visible:408,370,1316,974
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-604,908,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:640
+ ymin:604
+ text_only:"Thèmes"
+ wimp_icon {
+ extent:16,-492,892,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,364,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Thèmes disponibles "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:24,-576,188,-524
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Par défaut"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:524,-576,688,-524
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuler"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:704,-584,888,-516
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Valider"
+ text.size:*
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"download"
+ visible:486,610,1394,890
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-280,908,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:80
+ ymin:28
+ text.text:"Téléchargement de Netsurf"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:420,-84,488,-16
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite.id:"file_ddc"
+ sprite.size:*
+ sprite.area:&1
+ }
+ wimp_icon {
+ extent:204,-152,900,-100
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"http://netsurf.sourceforge.net/netsurf.zip"
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:204,-212,900,-160
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"netsurf"
+ text.size:*
+ text.validation:"Pptr_write"
+ }
+ wimp_icon {
+ extent:204,-212,900,-160
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"ADFS::A7000+.$.netsurf"
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:8,-272,900,-220
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:12,-268,296,-224
+ icon_flags:wimp_ICON_FILLED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_CREAM
+ }
+ wimp_icon {
+ extent:12,-268,896,-224
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"84.4KB of 1.1MB 26.6KB/s 0:39 remaining"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:92,-148,200,-104
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Source"
+ }
+ wimp_icon {
+ extent:12,-208,200,-164
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Destination"
+ }
+}
+
+wimp_window {
+ template_name:"history"
+ visible:252,388,1152,808
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_SCROLL_REPEAT | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_TOGGLE_ICON | wimp_WINDOW_VSCROLL | wimp_WINDOW_SIZE_ICON | wimp_WINDOW_HSCROLL | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_WHITE
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-880,1236,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:wimp_BUTTON_CLICK
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:"Historique"
+ text.size:*
+ text.validation:""
+}
+
+wimp_window {
+ template_name:"info"
+ visible:506,58,1126,306
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_CREAM
+ scroll_inner:wimp_COLOUR_ORANGE
+ highlight_bg:wimp_COLOUR_LIGHT_GREY
+ extra_flags:
+ extent:0,-248,620,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ work_flags:wimp_BUTTON_CLICK
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:"A propos de ce programme"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:676,-204,852,-156
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_RED
+ text_only:"OK"
+ }
+ wimp_icon {
+ extent:152,-60,612,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"NetSurf"
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:152,-120,612,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Navigateur web libre"
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:152,-180,612,-128
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"© Développeurs NetSurf"
+ text.size:40
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:152,-240,612,-188
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"CVS test build"
+ text.size:40
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:76,-56,148,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Nom"
+ }
+ wimp_icon {
+ extent:20,-116,148,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Fonction"
+ }
+ wimp_icon {
+ extent:24,-176,148,-132
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Auteurs"
+ }
+ wimp_icon {
+ extent:24,-236,148,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Version"
+ }
+ wimp_icon {
+ extent:4,-8,4,262328
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_DELETED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text_only:"<Deleted>"
+ }
+}
+
+wimp_window {
+ template_name:"login"
+ visible:566,258,1242,594
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-336,676,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:676
+ ymin:336
+ text.text:"Authentification du Site"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:532,-324,664,-256
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Entrer"
+ text.size:8
+ text.validation:"R6,3;Nok"
+ }
+ wimp_icon {
+ extent:376,-316,508,-264
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuler"
+ text.size:*
+ text.validation:"R5,3;Ncancel"
+ }
+ wimp_icon {
+ extent:200,-60,668,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"moo.yoo.com"
+ text.size:255
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:200,-120,668,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"my sekr3t area"
+ text.size:255
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:200,-180,668,-128
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:255
+ text.validation:"Pptr_write;Kta;N401username"
+ }
+ wimp_icon {
+ extent:200,-240,668,-188
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:255
+ text.validation:"Pptr_write;Kta;D*"
+ }
+ wimp_icon {
+ extent:120,-56,196,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Hôte"
+ }
+ wimp_icon {
+ extent:8,-176,196,-132
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Identifiant"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:8,-236,196,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Code secret"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:68,-116,196,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Domaine"
+ }
+}
+
+wimp_window {
+ template_name:"new_entry"
+ visible:1120,590,1720,810
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-220,600,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:600
+ ymin:220
+ text.text:""
+ text.size:32
+ text.validation:""
+ wimp_icon {
+ extent:12,-56,108,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Nom"
+ }
+ wimp_icon {
+ extent:112,-60,588,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:128
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:12,-116,108,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"URL"
+ }
+ wimp_icon {
+ extent:112,-120,532,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:1024
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:304,-200,436,-148
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuler"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:456,-208,588,-140
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"OK"
+ text.size:8
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:540,-116,584,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+}
+
+wimp_window {
+ template_name:"new_folder"
+ visible:596,278,1196,438
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-160,600,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:600
+ ymin:160
+ text.text:""
+ text.size:25
+ text.validation:""
+ wimp_icon {
+ extent:12,-56,108,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Nom"
+ }
+ wimp_icon {
+ extent:112,-60,588,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:128
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:304,-140,436,-88
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuler"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:456,-148,588,-80
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"OK"
+ text.size:8
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"objectinfo"
+ visible:276,892,1064,1084
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-192,788,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:wimp_BUTTON_CLICK
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:"À propos de cet objet"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:208,-60,784,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:208,-120,784,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:208,-180,784,-128
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:12,-80,84,-12
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite.id:"file_faf"
+ sprite.size:*
+ sprite.area:&1
+ }
+ wimp_icon {
+ extent:112,-116,204,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Cible"
+ }
+ wimp_icon {
+ extent:124,-176,204,-132
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Type"
+ }
+ wimp_icon {
+ extent:132,-56,204,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"URL"
+ }
+}
+
+wimp_window {
+ template_name:"open_url"
+ visible:440,134,1240,290
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-156,800,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:"Ouvrir l'URL"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:20,-56,92,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"URL"
+ }
+ wimp_icon {
+ extent:96,-60,736,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:1
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:504,-136,632,-84
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuler"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:656,-144,788,-76
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Ouvrir"
+ text.size:8
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:744,-56,788,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+}
+
+wimp_window {
+ template_name:"pageinfo"
+ visible:348,298,1140,546
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-248,792,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:wimp_BUTTON_CLICK
+ sprite_area:&1
+ xmin:792
+ ymin:248
+ text.text:"À propos de ce document"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:208,-60,784,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:208,-120,784,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:208,-180,784,-128
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:208,-240,784,-188
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:12,-80,84,-12
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite.id:"file_faf"
+ sprite.size:*
+ sprite.area:&1
+ }
+ wimp_icon {
+ extent:132,-116,204,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"URL"
+ }
+ wimp_icon {
+ extent:60,-176,204,-132
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Encodage"
+ }
+ wimp_icon {
+ extent:124,-236,204,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Type"
+ }
+ wimp_icon {
+ extent:112,-56,204,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Titre"
+ }
+}
+
+wimp_window {
+ template_name:"print"
+ visible:464,144,1064,716
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-572,600,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:"Pas d'imprimante"
+ text.size:20
+ text.validation:""
+ wimp_icon {
+ extent:12,-176,588,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:1
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-100,504,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:1
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"le bas de la page web"
+ text_and_sprite.size:27
+ text_and_sprite.validation:"Sradiooff,radioon"
+ }
+ wimp_icon {
+ extent:32,-152,492,-108
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:1
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:6
+ text_and_sprite.validation:"Sradiooff,radioon"
+ }
+ wimp_icon {
+ extent:84,-156,164,-104
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"1"
+ text.size:3
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:176,-148,208,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:208,-148,240,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:244,-152,528,-108
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"feuilles remplies"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:12,-236,580,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Imprimer les images d'avant-plan"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:12,-288,500,-244
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Imprimer les images de fond"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:12,-340,468,-296
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Imprimer en tâche de fond"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:12,-404,180,-360
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Portrait"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Sradiooff,radioon"
+ }
+ wimp_icon {
+ extent:12,-452,196,-408
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Paysage"
+ text_and_sprite.size:9
+ text_and_sprite.validation:"Sradiooff,radioon"
+ }
+ wimp_icon {
+ extent:428,-456,508,-404
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"1"
+ text.size:3
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:524,-448,556,-416
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:556,-448,588,-416
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:268,-552,400,-500
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuler"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:424,-560,588,-492
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Imprimer"
+ text.size:*
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:284,-452,424,-408
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Copies"
+ }
+ wimp_icon {
+ extent:-12,-480,604,-472
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:32,-52,460,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Finir l'impression après "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:252,-400,676,-356
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Tout le texte en noir"
+ text_and_sprite.size:24
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+}
+
+wimp_window {
+ template_name:"query"
+ visible:426,1082,1226,1326
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_LIGHT_GREY
+ extra_flags:
+ extent:0,-244,800,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:"Question de NetSurf"
+ text.size:21
+ text.validation:""
+ wimp_icon {
+ extent:92,-148,792,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Message"
+ text.size:300
+ text.validation:"R2;L"
+ }
+ wimp_icon {
+ extent:636,-232,788,-164
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"012345678901234567"
+ text.size:*
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:504,-224,620,-172
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"012345678901234567"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:8,-224,124,-172
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Aide"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:12,-80,80,-12
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite_only:"!netsurf"
+ }
+}
+
+wimp_window {
+ template_name:"saveas"
+ visible:682,168,998,416
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-248,316,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text_only:"Sauver sous"
+ wimp_icon {
+ extent:124,-84,192,-16
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite.id:"file_faf"
+ sprite.size:13
+ sprite.area:&1
+ }
+ wimp_icon {
+ extent:8,-152,308,-100
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:256
+ text.validation:"Pptr_write"
+ }
+ wimp_icon {
+ extent:172,-236,304,-168
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Sauver"
+ text.size:*
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:20,-228,148,-176
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuler"
+ text.size:*
+ text.validation:"R5,3"
+ }
+}
+
+wimp_window {
+ template_name:"search"
+ visible:170,720,814,964
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-244,644,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:644
+ ymin:244
+ text.text:"Recherche de texte"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:192,-60,580,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:32
+ text.validation:"KN;Pptr_write"
+ }
+ wimp_icon {
+ extent:32,-116,364,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Sensible à la casse"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:500,-228,632,-160
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Suivant"
+ text.size:10
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:340,-220,492,-168
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Précédent"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:204,-220,332,-168
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuler"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:16,-216,196,-172
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Non trouvé"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:-8,-148,652,-140
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:16,-56,184,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Chercher"
+ }
+ wimp_icon {
+ extent:588,-56,632,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_ICON_SHADED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:400,-116,628,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Tout montrer"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+}
+
+wimp_window {
+ template_name:"theme_inst"
+ visible:458,234,1258,478
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-244,800,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:"Installateur de Thèmes de NetSurf"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:92,-148,792,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Message"
+ text.size:300
+ text.validation:"R2;L"
+ }
+ wimp_icon {
+ extent:608,-232,788,-164
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Installer"
+ text.size:*
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:452,-224,584,-172
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuler"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:12,-80,80,-12
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite_only:"!netsurf"
+ }
+}
+
+wimp_window {
+ template_name:"tooltip"
+ visible:884,620,1148,656
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-36,2000,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text_only:"<Untitled>"
+ wimp_icon {
+ extent:0,-40,300,4
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Comment"
+ text.size:256
+ text.validation:""
+ }
+}
+
+wimp_window {
+ template_name:"tree"
+ visible:530,654,1042,954
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_SCROLL_REPEAT | wimp_WINDOW_IGNORE_XEXTENT | wimp_WINDOW_IGNORE_YEXTENT | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_TOGGLE_ICON | wimp_WINDOW_VSCROLL | wimp_WINDOW_SIZE_ICON | wimp_WINDOW_HSCROLL | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_TRANSPARENT
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-880,1236,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:wimp_BUTTON_DOUBLE_CLICK_DRAG
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:""
+ text.size:64
+ text.validation:""
+}
+
+wimp_window {
+ template_name:"url_suggest"
+ visible:320,390,1102,684
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_NO_BOUNDS | wimp_WINDOW_IGNORE_XEXTENT | wimp_WINDOW_IGNORE_YEXTENT | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_TRANSPARENT
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-65536,65536,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:wimp_BUTTON_CLICK
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text_only:"<Untitled>"
+}
+
+wimp_window {
+ template_name:"warning"
+ visible:320,524,1120,768
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-244,800,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:"Alerte de NetSurf"
+ text.size:21
+ text.validation:""
+ wimp_icon {
+ extent:92,-148,792,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Message"
+ text.size:300
+ text.validation:"R2;L"
+ }
+ wimp_icon {
+ extent:624,-232,788,-164
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Continuer"
+ text.size:*
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:480,-224,600,-172
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Aide"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:12,-80,80,-12
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite_only:"!netsurf"
+ }
+}
+
+wimp_window {
+ template_name:"zoom"
+ visible:828,402,1276,626
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-224,448,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ work_flags:
+ sprite_area:&1
+ xmin:448
+ ymin:224
+ text.text:"Ajuster la vue"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:8,-56,132,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Echelle"
+ }
+ wimp_icon {
+ extent:136,-60,236,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"100"
+ text.size:5
+ text.validation:"Pptr_write;KTA;A0-9."
+ }
+ wimp_icon {
+ extent:248,-52,280,-20
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:280,-52,312,-20
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sup,pup"
+ }
+ wimp_icon {
+ extent:316,-56,356,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_HCENTRED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"%"
+ }
+ wimp_icon {
+ extent:136,-112,448,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Scale all frames"
+ text_and_sprite.size:20
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:-12,-132,564,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:20,-204,152,-152
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuler"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:176,-212,436,-144
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Redimensionner"
+ text.size:*
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_secure"
+ visible:872,490,1464,854
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-364,592,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:592
+ ymin:364
+ text_only:"Sécurité"
+ wimp_icon {
+ extent:16,-120,576,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,492,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Confidentialité inter-site "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-104,568,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Envoyer l'info de renvoi de site"
+ text_and_sprite.size:40
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:16,-252,576,-148
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-176,380,-132
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Historique de sites "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:24,-228,164,-184
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Durée"
+ }
+ wimp_icon {
+ extent:168,-232,336,-180
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"12"
+ text.size:4
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:352,-224,384,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:384,-224,416,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:424,-228,500,-184
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"jours"
+ }
+ wimp_icon {
+ extent:24,-336,188,-284
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Par défaut"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:208,-336,372,-284
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuler"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:388,-344,572,-276
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Valider"
+ text.size:*
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_content"
+ visible:598,836,1350,1336
+ xscroll:0
+ yscroll:0
+ next:
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_OPEN | wimp_WINDOW_HAS_FOCUS | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-500,752,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:640
+ ymin:448
+ text_only:"Contenu"
+ wimp_icon {
+ extent:16,-272,732,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,492,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Blocage de contenu "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-100,392,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Cacher les pubs"
+ text_and_sprite.size:39
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:32,-152,516,-108
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Désactiver les fenêtres pop-up"
+ text_and_sprite.size:46
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:32,-256,404,-212
+#ifdef WITH_PLUGIN
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+#else
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_DELETED | wimp_BUTTON_RADIO
+#endif
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Désactiver les plug-ins"
+ text_and_sprite.size:42
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:16,-384,732,-292
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-320,492,-276
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Cibles de liens "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-372,716,-324
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Autoriser les liens à ouvrir une nouvelle fenêtre"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:24,-468,188,-416
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Par défaut"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:372,-468,536,-416
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuler"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:544,-476,728,-408
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Valider"
+ text.size:*
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:32,-204,376,-160
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Disable JavaScript"
+ text_and_sprite.size:19
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+}
+
+wimp_window {
+ template_name:"con_connect"
+ visible:446,482,1106,1150
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-668,660,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:660
+ ymin:668
+ text_only:"Connection"
+ wimp_icon {
+ extent:16,-304,644,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:28,-52,264,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Proxy HTTP "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-104,240,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Type de proxy"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:244,-108,572,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Display field"
+ text.size:32
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:580,-104,624,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:164,-164,240,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Hôte"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:244,-168,504,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"myhost.proxy"
+ text.size:255
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:500,-164,528,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_HCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:":"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:524,-168,628,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"8080"
+ text.size:8
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:92,-224,240,-180
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Identifiant"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:244,-228,628,-176
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"k1dd13"
+ text.size:64
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:52,-284,240,-240
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Mot de passe"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:244,-288,628,-236
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"1337"
+ text.size:64
+ text.validation:"Pptr_write;Kta;D*"
+ }
+ wimp_icon {
+ extent:16,-552,644,-332
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-360,316,-316
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Téléchargements "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:68,-412,320,-368
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Nombre maximum"
+ text.size:16
+ text.validation:""
+ }
+ wimp_icon {
+ extent:324,-416,532,-364
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"99"
+ text.size:*
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:548,-408,580,-376
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:580,-408,612,-376
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:36,-472,320,-428
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Nombre par hôte"
+ text.size:18
+ text.validation:""
+ }
+ wimp_icon {
+ extent:324,-476,532,-424
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"99"
+ text.size:4
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:548,-468,584,-436
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:580,-468,612,-436
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:20,-532,320,-488
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Connexions en cache"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:324,-536,532,-484
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"99"
+ text.size:4
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:548,-528,584,-496
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:580,-528,612,-496
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:*
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:24,-640,188,-588
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Par défaut"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:276,-640,440,-588
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuler"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:456,-648,640,-580
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Valider"
+ text.size:*
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_inter"
+ visible:264,290,1008,1066
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-844,744,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:744
+ ymin:704
+ text_only:"Interface"
+ wimp_icon {
+ extent:16,-168,728,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,732,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Téléchargements / sauvegardes de fichiers "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-100,676,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Supprimer l'extension lors des sauvegardes"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:32,-152,676,-108
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Demander confirmation avant écrasement"
+ text_and_sprite.size:46
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:16,-344,728,-200
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:36,-224,496,-180
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Fonctions interactives "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-276,744,-232
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Afficher les URLs visitées récemment"
+ text_and_sprite.size:42
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:32,-328,872,-284
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"URLs flottantes dans l'historique local"
+ text_and_sprite.size:44
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:16,-468,728,-372
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:36,-400,240,-356
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Vignettes "
+ text_and_sprite.size:23
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-452,716,-408
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Utiliser les vignettes pour les fenêtres iconisées"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:24,-744,188,-692
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Par défaut"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:360,-744,524,-692
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuler"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:540,-752,724,-684
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Valider"
+ text.size:*
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:16,-660,728,-492
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:36,-520,240,-476
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Hotlist"
+ text_and_sprite.size:23
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-572,728,-528
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Use external hotlist apps when available"
+ text_and_sprite.size:*
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:28,-640,232,-588
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Hotlist path"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:232,-640,708,-588
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"Writable icon"
+ text.size:256
+ text.validation:"Pptr_write;Kta"
+ }
+
+}
+
+wimp_window {
+ template_name:"ssldisplay"
+ visible:282,178,1242,718
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-540,960,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:960
+ ymin:76
+ text.text:"Certificat SSL"
+ text.size:16
+ text.validation:""
+ wimp_icon {
+ extent:16,-520,944,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,412,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Détails de certificat "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:68,-108,192,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Version"
+ }
+ wimp_icon {
+ extent:200,-108,312,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:384,-104,524,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Valide de"
+ }
+ wimp_icon {
+ extent:524,-108,928,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:112,-168,192,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Type"
+ }
+ wimp_icon {
+ extent:200,-168,312,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:328,-164,524,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Valide jusqu'à"
+ text.size:*
+ text.validation:""
+ }
+ wimp_icon {
+ extent:524,-168,928,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:84,-228,192,-184
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Série"
+ }
+ wimp_icon {
+ extent:200,-228,928,-176
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:32,-288,200,-244
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Fournisseur"
+ }
+ wimp_icon {
+ extent:200,-376,928,-236
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2;L"
+ }
+ wimp_icon {
+ extent:68,-432,192,-388
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Sujet"
+ }
+ wimp_icon {
+ extent:200,-504,928,-384
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R2;L"
+ }
+}
+
+wimp_window {
+ template_name:"sslcert"
+ visible:348,306,1136,898
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-592,788,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:788
+ ymin:592
+ text.text:"Problème de certificat SSL"
+ text.size:*
+ text.validation:""
+ wimp_icon {
+ extent:16,-108,772,-16
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"NetSurf n'a pas pu vérifier l'authenticité d'un certificat SSL. Vérifiez SVP les détails présentés ci-dessous."
+ text.size:150
+ text.validation:"R2;L"
+ }
+ wimp_icon {
+ extent:16,-484,772,-136
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:*
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-164,380,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Chaîne de certificat "
+ text_and_sprite.size:*
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:404,-564,568,-512
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Rejeter"
+ text.size:*
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:588,-572,772,-504
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Accepter"
+ text.size:*
+ text.validation:"R6,3"
+ }
+}
diff --git a/frontends/riscos/templates/nl b/frontends/riscos/templates/nl
new file mode 100644
index 000000000..704206fbd
--- /dev/null
+++ b/frontends/riscos/templates/nl
@@ -0,0 +1,3887 @@
+Template:
+
+wimp_window {
+ template_name:"configure"
+ visible:378,966,1050,1126
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_TOGGLE_ICON | wimp_WINDOW_VSCROLL | wimp_WINDOW_SIZE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_LIGHT_GREY
+ extra_flags:
+ extent:0,-880,1236,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:4
+ ymin:160
+ text.text:"NetSurf-instellingen"
+ text.size:22
+ text.validation:""
+}
+
+wimp_window {
+ template_name:"con_cache"
+ visible:184,682,772,1110
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-428,588,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:588
+ ymin:240
+ text_only:"Buffer"
+ wimp_icon {
+ extent:16,-124,568,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,300,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Geheugenbuffer "
+ text_and_sprite.size:17
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:60,-104,212,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Grootte"
+ text.size:13
+ text.validation:""
+ }
+ wimp_icon {
+ extent:216,-108,380,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"512.0"
+ text.size:10
+ text.validation:"Pptr_write;Kta;A0-9."
+ }
+ wimp_icon {
+ extent:392,-100,424,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:424,-100,456,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:464,-104,528,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"MB"
+ }
+ wimp_icon {
+ extent:16,-312,568,-152
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-180,268,-136
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Schijfbuffer "
+ text_and_sprite.size:15
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:60,-232,212,-188
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Grootte"
+ text.size:13
+ text.validation:""
+ }
+ wimp_icon {
+ extent:216,-236,380,-184
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"512"
+ text.size:10
+ text.validation:"Pptr_write;Kta;A0-9"
+ }
+ wimp_icon {
+ extent:392,-228,424,-196
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:424,-228,456,-196
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:464,-232,528,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"MB"
+ }
+ wimp_icon {
+ extent:12,-292,212,-248
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Bewaar max."
+ text.size:12
+ text.validation:""
+ }
+ wimp_icon {
+ extent:216,-296,380,-244
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"512"
+ text.size:10
+ text.validation:"Pptr_write;Kta;A0-9"
+ }
+ wimp_icon {
+ extent:392,-288,424,-256
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:424,-288,456,-256
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:464,-292,560,-252
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"dagen"
+ }
+ wimp_icon {
+ extent:24,-396,200,-344
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Standaard"
+ text.size:10
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:216,-396,376,-344
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuleer"
+ text.size:9
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:392,-404,568,-336
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Stel in"
+ text.size:8
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_fonts"
+ visible:558,802,1282,1526
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-724,724,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:712
+ ymin:724
+ text_only:"Lettertypen"
+ wimp_icon {
+ extent:16,-424,704,-28
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,256,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Lettertypen "
+ text_and_sprite.size:14
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:24,-104,216,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Schreefloos"
+ }
+ wimp_icon {
+ extent:220,-108,628,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Weergaveveld"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:640,-104,684,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:24,-164,216,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Met schreef"
+ }
+ wimp_icon {
+ extent:220,-168,628,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Weergaveveld"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:640,-164,684,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:36,-224,216,-180
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Monospace"
+ }
+ wimp_icon {
+ extent:220,-228,628,-176
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Weergaveveld"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:640,-224,684,-180
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:88,-284,216,-240
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Cursief"
+ }
+ wimp_icon {
+ extent:220,-288,628,-236
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Weergaveveld"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:640,-284,684,-240
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:72,-344,216,-300
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Fantasie"
+ }
+ wimp_icon {
+ extent:220,-348,628,-296
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Weergaveveld"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:640,-344,684,-300
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:56,-404,216,-360
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Standaard"
+ }
+ wimp_icon {
+ extent:220,-408,628,-356
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Weergaveveld"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:640,-404,684,-360
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:16,-612,704,-452
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-480,296,-436
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Lettergrootte "
+ text_and_sprite.size:16
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-532,200,-488
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Standaard"
+ }
+ wimp_icon {
+ extent:204,-536,372,-484
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"12.3"
+ text.size:10
+ text.validation:"Pptr_write;Kta;A0-9."
+ }
+ wimp_icon {
+ extent:388,-528,420,-496
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:420,-528,452,-496
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:460,-532,500,-488
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"pt"
+ }
+ wimp_icon {
+ extent:52,-592,200,-548
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Minimum"
+ }
+ wimp_icon {
+ extent:204,-596,372,-544
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"12.3"
+ text.size:10
+ text.validation:"Pptr_write;Kta;A0-9."
+ }
+ wimp_icon {
+ extent:388,-588,420,-556
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:420,-588,452,-556
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:460,-592,500,-548
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"pt"
+ }
+ wimp_icon {
+ extent:24,-696,200,-644
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Standaard"
+ text.size:10
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:340,-696,504,-644
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuleer"
+ text.size:9
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:520,-704,704,-636
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Stel in"
+ text.size:8
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_home"
+ visible:808,592,1608,888
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-296,800,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:800
+ ymin:296
+ text_only:"Startpagina"
+ wimp_icon {
+ extent:16,-184,784,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,284,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Startpagina "
+ text_and_sprite.size:16
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:24,-108,120,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Adres"
+ }
+ wimp_icon {
+ extent:124,-112,712,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:128
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:720,-108,764,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:124,-164,732,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_SELECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Browservenster openen na opstarten"
+ text_and_sprite.size:35
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:24,-264,200,-212
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Standaard"
+ text.size:10
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:420,-264,584,-212
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuleer"
+ text.size:9
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:600,-272,784,-204
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Stel in"
+ text.size:8
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_image"
+ visible:1488,822,2164,1410
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-588,676,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ work_flags:
+ sprite_area:&1
+ xmin:676
+ ymin:588
+ text.text:"Afbeeldingen"
+ text.size:13
+ text.validation:""
+ wimp_icon {
+ extent:16,-292,660,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,396,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Afbeeldingskwaliteit "
+ text_and_sprite.size:23
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:40,-108,216,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Voorgrond"
+ }
+ wimp_icon {
+ extent:220,-112,592,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"RISC OS-routines"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:600,-108,644,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:24,-168,216,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Achtergrond"
+ }
+ wimp_icon {
+ extent:220,-172,592,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"RISC OS-routines"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:600,-168,644,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:32,-276,644,-180
+ icon_flags:wimp_ICON_BORDER
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ }
+ wimp_icon {
+ extent:16,-476,660,-320
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-348,236,-304
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Animaties "
+ text_and_sprite.size:16
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:20,-404,232,-360
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Animatietijd"
+ text.size:15
+ text.validation:""
+ }
+ wimp_icon {
+ extent:236,-408,404,-356
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"12.34"
+ text.size:6
+ text.validation:"Pptr_write;Kta;A0-9."
+ }
+ wimp_icon {
+ extent:420,-400,452,-368
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:452,-400,484,-368
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:492,-404,644,-360
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"seconden"
+ text.size:9
+ text.validation:""
+ }
+ wimp_icon {
+ extent:236,-460,616,-416
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Animaties uitzetten"
+ text_and_sprite.size:20
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:24,-560,200,-508
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Standaard"
+ text.size:10
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:292,-560,456,-508
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuleer"
+ text.size:9
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:472,-568,656,-500
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Stel in"
+ text.size:8
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_inter"
+ visible:2320,436,3164,1220
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-784,844,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:744
+ ymin:584
+ text.text:"Gebruikersinterface"
+ text.size:20
+ text.validation:""
+ wimp_icon {
+ extent:16,-168,820,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,576,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Ophalen / opslaan van bestanden "
+ text_and_sprite.size:34
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-100,748,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Bestand opslaan zonder bestandsextensie"
+ text_and_sprite.size:40
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:32,-152,808,-108
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Toestemming vragen bij overschrijven"
+ text_and_sprite.size:37
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:16,-344,820,-200
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:36,-224,496,-180
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Interactieve kenmerken "
+ text_and_sprite.size:25
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-276,804,-232
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Recent bezochte adressen tonen tijdens typen"
+ text_and_sprite.size:45
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:32,-328,776,-284
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Adressen tonen bij de venstergeschiedenis"
+ text_and_sprite.size:42
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:16,-468,820,-372
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:36,-400,240,-356
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Miniaturen "
+ text_and_sprite.size:23
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-452,708,-408
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Miniaturen gebruiken bij symboliseren"
+ text_and_sprite.size:42
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:24,-748,200,-696
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Standaard"
+ text.size:10
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:456,-748,620,-696
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuleer"
+ text.size:9
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:636,-756,820,-688
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Stel in"
+ text.size:8
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:16,-664,820,-496
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:36,-524,240,-480
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Favorieten"
+ text_and_sprite.size:23
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-576,728,-532
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Extern programma voor favorietenlijst"
+ text_and_sprite.size:38
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:24,-644,256,-592
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Programmapad"
+ text.size:13
+ text.validation:""
+ }
+ wimp_icon {
+ extent:260,-644,800,-592
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"Schrijfbaar veld"
+ text.size:256
+ text.validation:"Pptr_write;Kta"
+ }
+}
+
+wimp_window {
+ template_name:"con_lang"
+ visible:1574,956,2318,1256
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-300,744,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:744
+ ymin:300
+ text_only:"Taal"
+ wimp_icon {
+ extent:16,-188,728,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,284,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Taal "
+ text_and_sprite.size:16
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:64,-108,224,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Interface"
+ }
+ wimp_icon {
+ extent:228,-112,660,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"1337"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:668,-108,712,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:32,-168,224,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Webpagina's"
+ }
+ wimp_icon {
+ extent:228,-172,660,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"8008135"
+ text.size:256
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:668,-168,712,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:24,-272,200,-220
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Standaard"
+ text.size:10
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:360,-272,524,-220
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuleer"
+ text.size:9
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:540,-280,724,-212
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Stel in"
+ text.size:8
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_theme"
+ visible:410,38,1318,642
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-604,908,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:640
+ ymin:604
+ text_only:"Thema's"
+ wimp_icon {
+ extent:16,-492,892,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,392,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Beschikbare thema's "
+ text_and_sprite.size:22
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:24,-576,200,-524
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Standaard"
+ text.size:10
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:524,-576,688,-524
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuleer"
+ text.size:9
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:704,-584,888,-516
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Stel in"
+ text.size:8
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"download"
+ visible:486,610,1394,890
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-280,908,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:88
+ ymin:28
+ text.text:"NetSurf-ophaalproces"
+ text.size:21
+ text.validation:""
+ wimp_icon {
+ extent:420,-84,488,-16
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite.id:"file_ddc"
+ sprite.size:9
+ sprite.area:&1
+ }
+ wimp_icon {
+ extent:204,-152,900,-100
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"http://netsurf.sourceforge.net/netsurf.zip"
+ text.size:43
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:204,-212,900,-160
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"netsurf"
+ text.size:8
+ text.validation:"Pptr_write"
+ }
+ wimp_icon {
+ extent:204,-212,900,-160
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"ADFS::A7000+.$.netsurf"
+ text.size:23
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:8,-272,900,-220
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:12,-268,296,-224
+ icon_flags:wimp_ICON_FILLED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_CREAM
+ }
+ wimp_icon {
+ extent:12,-268,896,-224
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"999 KB compleet gemiddeld 926.6 KB/s 0:39 resterend"
+ text.size:72
+ text.validation:""
+ }
+ wimp_icon {
+ extent:92,-148,200,-104
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Bron"
+ }
+ wimp_icon {
+ extent:12,-208,200,-164
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Bestemming"
+ }
+}
+
+wimp_window {
+ template_name:"history"
+ visible:252,388,1152,808
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_SCROLL_REPEAT | wimp_WINDOW_BACK_ICON | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_TOGGLE_ICON | wimp_WINDOW_VSCROLL | wimp_WINDOW_SIZE_ICON | wimp_WINDOW_HSCROLL | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_WHITE
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-880,1236,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:wimp_BUTTON_CLICK
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:" Venstergeschiedenis "
+ text.size:22
+ text.validation:""
+}
+
+wimp_window {
+ template_name:"info"
+ visible:752,332,1412,700
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_CREAM
+ scroll_inner:wimp_COLOUR_ORANGE
+ highlight_bg:wimp_COLOUR_LIGHT_GREY
+ extra_flags:
+ extent:0,-368,660,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ work_flags:wimp_BUTTON_CLICK
+ sprite_area:&1
+ xmin:660
+ ymin:248
+ text.text:"Programma-informatie"
+ text.size:21
+ text.validation:""
+ wimp_icon {
+ extent:672,-200,848,-152
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_RED
+ text_only:"OK"
+ }
+ wimp_icon {
+ extent:168,-60,652,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"NetSurf"
+ text.size:8
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:168,-120,652,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Webbrowser met open broncode"
+ text.size:29
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:168,-180,652,-128
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"© NetSurf-ontwikkelaars"
+ text.size:40
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:168,-360,652,-308
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"CVS-testuitgave"
+ text.size:40
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:72,-56,164,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Naam"
+ }
+ wimp_icon {
+ extent:40,-116,164,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Functie"
+ }
+ wimp_icon {
+ extent:40,-176,164,-132
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Auteurs"
+ }
+ wimp_icon {
+ extent:40,-356,164,-312
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Versie"
+ }
+ wimp_icon {
+ extent:168,-300,652,-248
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"g.vankatwijk@freeler.nl"
+ text.size:50
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:8,-296,164,-252
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Contact"
+ text.size:20
+ text.validation:""
+ }
+ wimp_icon {
+ extent:168,-240,652,-188
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"NetSurf-vertalers"
+ text.size:40
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:4,-236,164,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Vertaling"
+ }
+}
+
+wimp_window {
+ template_name:"login"
+ visible:582,400,1322,736
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-336,740,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:740
+ ymin:336
+ text.text:"Website-authenticatie"
+ text.size:22
+ text.validation:""
+ wimp_icon {
+ extent:596,-324,728,-256
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Login"
+ text.size:8
+ text.validation:"R6,3;Nok"
+ }
+ wimp_icon {
+ extent:408,-316,572,-264
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuleer"
+ text.size:9
+ text.validation:"R5,3;Ncancel"
+ }
+ wimp_icon {
+ extent:252,-60,732,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"moo.yoo.com"
+ text.size:255
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:252,-120,732,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"my sekr3t area"
+ text.size:255
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:252,-180,732,-128
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:255
+ text.validation:"Pptr_write;Kta;N401username"
+ }
+ wimp_icon {
+ extent:252,-240,732,-188
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:255
+ text.validation:"Pptr_write;Kta;D*"
+ }
+ wimp_icon {
+ extent:100,-56,248,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Website"
+ }
+ wimp_icon {
+ extent:4,-176,248,-132
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Gebruikersnaam"
+ text.size:15
+ text.validation:""
+ }
+ wimp_icon {
+ extent:16,-236,248,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Wachtwoord"
+ }
+ wimp_icon {
+ extent:108,-116,248,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Gebied"
+ }
+}
+
+wimp_window {
+ template_name:"new_entry"
+ visible:480,660,1080,880
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-220,600,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:600
+ ymin:220
+ text.text:""
+ text.size:32
+ text.validation:""
+ wimp_icon {
+ extent:12,-56,108,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Naam"
+ }
+ wimp_icon {
+ extent:112,-60,588,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:128
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:12,-116,108,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Adres"
+ }
+ wimp_icon {
+ extent:112,-120,532,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:1024
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:268,-200,432,-148
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuleer"
+ text.size:9
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:456,-208,588,-140
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"OK"
+ text.size:8
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:540,-116,584,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+}
+
+wimp_window {
+ template_name:"new_folder"
+ visible:480,954,1080,1114
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-160,600,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:600
+ ymin:160
+ text.text:""
+ text.size:21
+ text.validation:""
+ wimp_icon {
+ extent:12,-56,108,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Naam"
+ }
+ wimp_icon {
+ extent:112,-60,588,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:128
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:268,-140,432,-88
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuleer"
+ text.size:9
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:456,-148,588,-80
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"OK"
+ text.size:8
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"objectinfo"
+ visible:428,292,1216,480
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-188,788,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:wimp_BUTTON_CLICK
+ sprite_area:&1
+ xmin:788
+ ymin:188
+ text.text:"Objectinformatie"
+ text.size:20
+ text.validation:""
+ wimp_icon {
+ extent:204,-60,780,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Weergaveveld"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:204,-120,780,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Weergaveveld"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:204,-180,780,-128
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Weergaveveld"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:12,-80,84,-12
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite.id:"file_faf"
+ sprite.size:9
+ sprite.area:&1
+ }
+ wimp_icon {
+ extent:92,-116,200,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Doel"
+ }
+ wimp_icon {
+ extent:120,-176,200,-132
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Type"
+ }
+ wimp_icon {
+ extent:104,-56,200,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Adres"
+ }
+}
+
+wimp_window {
+ template_name:"open_url"
+ visible:248,266,1048,422
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-156,800,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:"Weblocatie openen"
+ text.size:18
+ text.validation:""
+ wimp_icon {
+ extent:8,-56,104,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Adres"
+ }
+ wimp_icon {
+ extent:108,-60,736,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:1
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:468,-136,632,-84
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuleer"
+ text.size:9
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:656,-144,788,-76
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Open"
+ text.size:8
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:744,-56,788,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+}
+
+wimp_window {
+ template_name:"pageinfo"
+ visible:310,528,1102,776
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-248,792,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:792
+ ymin:248
+ text.text:"Documentinformatie"
+ text.size:19
+ text.validation:""
+ wimp_icon {
+ extent:208,-60,784,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Weergaveveld"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:208,-120,784,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Weergaveveld"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:208,-180,784,-128
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Weergaveveld"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:208,-240,784,-188
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Weergaveveld"
+ text.size:64
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:12,-80,84,-12
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite.id:"file_faf"
+ sprite.size:9
+ sprite.area:&1
+ }
+ wimp_icon {
+ extent:108,-116,204,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Adres"
+ }
+ wimp_icon {
+ extent:60,-176,204,-132
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Codering"
+ }
+ wimp_icon {
+ extent:116,-236,204,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Type"
+ }
+ wimp_icon {
+ extent:112,-56,204,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Titel"
+ }
+}
+
+wimp_window {
+ template_name:"print"
+ visible:472,136,1132,708
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-572,700,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:600
+ ymin:572
+ text.text:"Geen stuurprogramma aanwezig"
+ text.size:29
+ text.validation:""
+ wimp_icon {
+ extent:12,-176,652,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-100,504,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:1
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"einde van de webpagina"
+ text_and_sprite.size:23
+ text_and_sprite.validation:"Sradiooff,radioon"
+ }
+ wimp_icon {
+ extent:32,-152,492,-108
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:1
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:6
+ text_and_sprite.validation:"Sradiooff,radioon"
+ }
+ wimp_icon {
+ extent:84,-156,164,-104
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"1"
+ text.size:3
+ text.validation:"Pptr_write;Kta;A0-9"
+ }
+ wimp_icon {
+ extent:176,-148,208,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:208,-148,240,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:244,-152,528,-108
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"pagina('s)"
+ text.size:11
+ text.validation:""
+ }
+ wimp_icon {
+ extent:12,-236,512,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Voorgrondafbeeldingen"
+ text_and_sprite.size:22
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:12,-288,528,-244
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Achtergrondafbeeldingen"
+ text_and_sprite.size:24
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:12,-340,492,-296
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Afdrukken in achtergrond"
+ text_and_sprite.size:25
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:12,-404,180,-360
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_SELECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Staand"
+ text_and_sprite.size:7
+ text_and_sprite.validation:"Sradiooff,radioon"
+ }
+ wimp_icon {
+ extent:12,-452,196,-408
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Liggend"
+ text_and_sprite.size:8
+ text_and_sprite.validation:"Sradiooff,radioon"
+ }
+ wimp_icon {
+ extent:480,-456,560,-404
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"1"
+ text.size:3
+ text.validation:"Pptr_write;Kta;A0-9"
+ }
+ wimp_icon {
+ extent:572,-448,604,-416
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:604,-448,636,-416
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:292,-552,456,-500
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuleer"
+ text.size:9
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:480,-560,648,-492
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Druk af"
+ text.size:8
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:344,-452,476,-408
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Kopieën"
+ }
+ wimp_icon {
+ extent:-16,-480,676,-472
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:32,-48,448,-4
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Afdrukken stoppen na "
+ text_and_sprite.size:23
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:232,-400,656,-356
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Alle tekst in zwart"
+ text_and_sprite.size:20
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+}
+
+wimp_window {
+ template_name:"query"
+ visible:142,526,942,806
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_LIGHT_GREY
+ extra_flags:
+ extent:0,-280,800,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:"Vraag van NetSurf"
+ text.size:21
+ text.validation:""
+ wimp_icon {
+ extent:92,-184,792,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Bericht"
+ text.size:300
+ text.validation:"R2;L"
+ }
+ wimp_icon {
+ extent:604,-268,788,-200
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"012345678901234567"
+ text.size:19
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:424,-260,588,-208
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"0123456789012345678901"
+ text.size:23
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:16,-260,132,-208
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Hulp"
+ text.size:5
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:12,-80,80,-12
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite_only:"!netsurf"
+ }
+}
+
+wimp_window {
+ template_name:"saveas"
+ visible:824,676,1164,924
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-248,340,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:308
+ ymin:244
+ text_only:"Bewaar als"
+ wimp_icon {
+ extent:144,-84,212,-16
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK_DRAG
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite.id:"file_faf"
+ sprite.size:13
+ sprite.area:&1
+ }
+ wimp_icon {
+ extent:8,-152,332,-100
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:256
+ text.validation:"Pptr_write"
+ }
+ wimp_icon {
+ extent:184,-236,332,-168
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Bewaar"
+ text.size:7
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:8,-228,168,-176
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuleer"
+ text.size:9
+ text.validation:"R5,3"
+ }
+}
+
+wimp_window {
+ template_name:"search"
+ visible:1036,684,1792,928
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-244,756,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:756
+ ymin:244
+ text.text:"Tekst zoeken"
+ text.size:13
+ text.validation:""
+ wimp_icon {
+ extent:96,-60,692,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:""
+ text.size:32
+ text.validation:"KN;Pptr_write"
+ }
+ wimp_icon {
+ extent:96,-116,476,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Hoofdlettergevoelig"
+ text_and_sprite.size:20
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:568,-228,744,-160
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Volgende"
+ text.size:10
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:424,-220,552,-168
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Vorige"
+ text.size:7
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:248,-220,408,-168
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuleer"
+ text.size:9
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:12,-216,232,-172
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Niet gevonden"
+ text.size:14
+ text.validation:""
+ }
+ wimp_icon {
+ extent:-8,-148,772,-140
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:16,-56,92,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Zoek"
+ }
+ wimp_icon {
+ extent:700,-56,744,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_ICON_SHADED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:13
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:472,-116,748,-72
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Alles markeren"
+ text_and_sprite.size:15
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+}
+
+wimp_window {
+ template_name:"theme_inst"
+ visible:374,554,1174,834
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-280,800,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:800
+ ymin:280
+ text.text:"NetSurf-thema-intallatie"
+ text.size:25
+ text.validation:""
+ wimp_icon {
+ extent:92,-184,792,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Bericht"
+ text.size:300
+ text.validation:"R2;L"
+ }
+ wimp_icon {
+ extent:588,-268,788,-200
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Installeer"
+ text.size:11
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:404,-260,564,-208
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuleer"
+ text.size:9
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:12,-80,80,-12
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite_only:"!netsurf"
+ }
+}
+
+wimp_window {
+ template_name:"tooltip"
+ visible:884,620,1148,656
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-36,2000,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text_only:"<Naamloos>"
+ wimp_icon {
+ extent:0,-40,300,4
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Commentaar"
+ text.size:256
+ text.validation:""
+ }
+}
+
+wimp_window {
+ template_name:"tree"
+ visible:530,654,1042,954
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_SCROLL_REPEAT | wimp_WINDOW_IGNORE_XEXTENT | wimp_WINDOW_IGNORE_YEXTENT | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_TOGGLE_ICON | wimp_WINDOW_VSCROLL | wimp_WINDOW_SIZE_ICON | wimp_WINDOW_HSCROLL | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_TRANSPARENT
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-880,1236,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:wimp_BUTTON_DOUBLE_CLICK_DRAG
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:""
+ text.size:64
+ text.validation:""
+}
+
+wimp_window {
+ template_name:"url_suggest"
+ visible:320,390,1102,684
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_NO_BOUNDS | wimp_WINDOW_IGNORE_XEXTENT | wimp_WINDOW_IGNORE_YEXTENT | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_TRANSPARENT
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-65536,65536,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:wimp_BUTTON_CLICK
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text_only:"<Naamloos>"
+}
+
+wimp_window {
+ template_name:"warning"
+ visible:320,814,1120,1094
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-280,800,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:0
+ ymin:0
+ text.text:"Waarschuwing van NetSurf"
+ text.size:25
+ text.validation:""
+ wimp_icon {
+ extent:92,-184,792,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Bericht"
+ text.size:300
+ text.validation:"R2;L"
+ }
+ wimp_icon {
+ extent:608,-268,788,-200
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Verder"
+ text.size:7
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:484,-260,584,-208
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Hulp"
+ text.size:5
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:12,-80,80,-12
+ icon_flags:wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ sprite_only:"!netsurf"
+ }
+}
+
+wimp_window {
+ template_name:"zoom"
+ visible:182,356,650,578
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-224,468,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ work_flags:
+ sprite_area:&1
+ xmin:468
+ ymin:220
+ text.text:"Pagina schalen"
+ text.size:15
+ text.validation:""
+ wimp_icon {
+ extent:0,-56,112,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Schaal"
+ }
+ wimp_icon {
+ extent:116,-60,212,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"100"
+ text.size:5
+ text.validation:"Pptr_write;KTA;A0-9."
+ }
+ wimp_icon {
+ extent:224,-52,256,-20
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"R5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:256,-52,288,-20
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"R5;sup,pup"
+ }
+ wimp_icon {
+ extent:292,-56,332,-12
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_HCENTRED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"%"
+ }
+ wimp_icon {
+ extent:116,-112,460,-68
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Frames meeschalen"
+ text_and_sprite.size:20
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:-4,-132,716,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:148,-204,308,-152
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuleer"
+ text.size:9
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:324,-212,460,-144
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Schaal"
+ text.size:7
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"ssldisplay"
+ visible:862,768,1870,1308
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-540,1008,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:1008
+ ymin:76
+ text.text:"SSL-certificaat"
+ text.size:16
+ text.validation:""
+ wimp_icon {
+ extent:16,-520,992,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,380,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Certificaatedetails "
+ text_and_sprite.size:22
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:72,-108,196,-64
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Versie"
+ }
+ wimp_icon {
+ extent:200,-108,312,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:320,-104,528,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Geldig vanaf"
+ text.size:13
+ text.validation:""
+ }
+ wimp_icon {
+ extent:532,-108,976,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:116,-168,196,-124
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Type"
+ }
+ wimp_icon {
+ extent:200,-168,312,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:340,-164,528,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Geldig tot"
+ }
+ wimp_icon {
+ extent:532,-168,976,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:68,-228,196,-184
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Serienr"
+ }
+ wimp_icon {
+ extent:200,-228,976,-176
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:56,-288,196,-244
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Uitgever"
+ }
+ wimp_icon {
+ extent:200,-376,976,-236
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R2;L"
+ }
+ wimp_icon {
+ extent:24,-432,196,-388
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Onderwerp"
+ }
+ wimp_icon {
+ extent:200,-504,976,-384
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R2;L"
+ }
+}
+
+wimp_window {
+ template_name:"con_secure"
+ visible:1590,788,2206,1152
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-364,616,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ work_flags:
+ sprite_area:&1
+ xmin:616
+ ymin:364
+ text.text:"Privacy & veiligheid"
+ text.size:21
+ text.validation:""
+ wimp_icon {
+ extent:16,-120,600,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,364,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Cross-site privacy "
+ text_and_sprite.size:21
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-104,592,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Sitegerelateerde info verzenden"
+ text_and_sprite.size:32
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:16,-252,600,-148
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-176,316,-132
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Sitegeschiedenis "
+ text_and_sprite.size:18
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:28,-228,112,-184
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Max."
+ }
+ wimp_icon {
+ extent:116,-232,240,-180
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"12"
+ text.size:4
+ text.validation:"Pptr_write;Kta;A0-9"
+ }
+ wimp_icon {
+ extent:252,-224,284,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:284,-224,316,-192
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:324,-228,596,-184
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"dagen onthouden"
+ text.size:16
+ text.validation:""
+ }
+ wimp_icon {
+ extent:24,-336,200,-284
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Standaard"
+ text.size:10
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:232,-336,396,-284
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuleer"
+ text.size:9
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:412,-344,596,-276
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Stel in"
+ text.size:8
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"con_content"
+ visible:1248,854,1928,1354
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FULL_SIZE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-500,680,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:656
+ ymin:452
+ text_only:"Browsen"
+ wimp_icon {
+ extent:16,-272,660,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-52,492,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Paginaverwerking "
+ text_and_sprite.size:29
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-100,460,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Advertenties verbergen"
+ text_and_sprite.size:39
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:32,-152,592,-108
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Pop-up vensters verhinderen"
+ text_and_sprite.size:46
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:32,-256,540,-212
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_DELETED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"Plug-ins uitschakelen"
+ text_and_sprite.size:42
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:16,-384,660,-292
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-320,492,-276
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Koppelingen "
+ text_and_sprite.size:29
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:32,-368,644,-324
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"In nieuw venster openen toestaan"
+ text_and_sprite.size:36
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+ wimp_icon {
+ extent:24,-468,200,-416
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Standaard"
+ text.size:10
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:296,-468,460,-416
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuleer"
+ text.size:9
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:476,-476,660,-408
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Stel in"
+ text.size:8
+ text.validation:"R6,3"
+ }
+ wimp_icon {
+ extent:32,-204,532,-160
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_RADIO
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:"JavaScript uitschakelen"
+ text_and_sprite.size:24
+ text_and_sprite.validation:"Soptoff,opton"
+ }
+}
+
+wimp_window {
+ template_name:"con_connect"
+ visible:1328,566,2068,1234
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BACK_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-668,740,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED
+ work_flags:
+ sprite_area:&1
+ xmin:740
+ ymin:668
+ text_only:"Verbinding"
+ wimp_icon {
+ extent:16,-304,724,-24
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:28,-52,232,-8
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" HTTP-proxy "
+ text_and_sprite.size:15
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:96,-104,268,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_only:"Proxy-type"
+ }
+ wimp_icon {
+ extent:272,-108,652,-56
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Weergaveveld"
+ text.size:32
+ text.validation:"R2"
+ }
+ wimp_icon {
+ extent:660,-104,704,-60
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"R5;sgright,pgright"
+ }
+ wimp_icon {
+ extent:156,-164,268,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Server"
+ text.size:7
+ text.validation:""
+ }
+ wimp_icon {
+ extent:272,-168,584,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"myhost.proxy"
+ text.size:255
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:580,-164,608,-120
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_HCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:":"
+ text.size:2
+ text.validation:""
+ }
+ wimp_icon {
+ extent:604,-168,708,-116
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"8080"
+ text.size:8
+ text.validation:"Pptr_write;Kta;A0-9"
+ }
+ wimp_icon {
+ extent:12,-224,268,-180
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Gebruikersnaam"
+ text.size:15
+ text.validation:""
+ }
+ wimp_icon {
+ extent:272,-228,708,-176
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"k1dd13"
+ text.size:64
+ text.validation:"Pptr_write;Kta"
+ }
+ wimp_icon {
+ extent:44,-284,268,-240
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Wachtwoord"
+ text.size:11
+ text.validation:""
+ }
+ wimp_icon {
+ extent:272,-288,708,-236
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"1337"
+ text.size:64
+ text.validation:"Pptr_write;Kta;D*"
+ }
+ wimp_icon {
+ extent:16,-552,724,-332
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-360,332,-316
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Ophaalopdrachten "
+ text_and_sprite.size:19
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:148,-412,408,-368
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Maximum aantal"
+ text.size:15
+ text.validation:""
+ }
+ wimp_icon {
+ extent:412,-416,620,-364
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"99"
+ text.size:3
+ text.validation:"Pptr_write;Kta;A0-9"
+ }
+ wimp_icon {
+ extent:636,-408,668,-376
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:668,-408,700,-376
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:116,-472,408,-428
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Aantal per server"
+ text.size:18
+ text.validation:""
+ }
+ wimp_icon {
+ extent:412,-476,620,-424
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"99"
+ text.size:4
+ text.validation:"Pptr_write;Kta;A0-9"
+ }
+ wimp_icon {
+ extent:636,-468,672,-436
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:668,-468,700,-436
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:24,-532,408,-488
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_ICON_RJUSTIFIED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Gebufferde verbindingen"
+ text.size:24
+ text.validation:""
+ }
+ wimp_icon {
+ extent:412,-536,620,-484
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_WRITABLE
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_WHITE
+ text.text:"99"
+ text.size:4
+ text.validation:"Pptr_write;Kta;A0-9"
+ }
+ wimp_icon {
+ extent:636,-528,672,-496
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sdown,pdown"
+ }
+ wimp_icon {
+ extent:668,-528,700,-496
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED | wimp_BUTTON_REPEAT
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:""
+ text_and_sprite.size:1
+ text_and_sprite.validation:"r5;sup,pup"
+ }
+ wimp_icon {
+ extent:24,-640,200,-588
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Standaard"
+ text.size:10
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:360,-640,524,-588
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Annuleer"
+ text.size:9
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:540,-648,724,-580
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Stel in"
+ text.size:8
+ text.validation:"R6,3"
+ }
+}
+
+wimp_window {
+ template_name:"sslcert"
+ visible:348,250,1136,898
+ xscroll:0
+ yscroll:0
+ next:wimp_TOP
+ window_flags:wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_BOUNDED_ONCE | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_NEW_FORMAT
+ title_fg:wimp_COLOUR_BLACK
+ title_bg:wimp_COLOUR_LIGHT_GREY
+ work_fg:wimp_COLOUR_BLACK
+ work_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ scroll_outer:wimp_COLOUR_MID_LIGHT_GREY
+ scroll_inner:wimp_COLOUR_VERY_LIGHT_GREY
+ highlight_bg:wimp_COLOUR_CREAM
+ extra_flags:
+ extent:0,-648,788,0
+ title_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | 0x27000000
+ work_flags:
+ sprite_area:&1
+ xmin:788
+ ymin:648
+ text.text:"SSL-certificaatprobleem"
+ text.size:24
+ text.validation:""
+ wimp_icon {
+ extent:16,-148,772,-16
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"NetSurf kan de rechtmatigheid van een SSL-certificaat niet verifiëren. Verifieer de details hieronder."
+ text.size:150
+ text.validation:"R2;L"
+ }
+ wimp_icon {
+ extent:16,-548,772,-176
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:""
+ text.size:1
+ text.validation:"R4"
+ }
+ wimp_icon {
+ extent:32,-204,380,-160
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text_and_sprite.text:" Certificaatketen "
+ text_and_sprite.size:22
+ text_and_sprite.validation:""
+ }
+ wimp_icon {
+ extent:404,-624,568,-572
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Verwerp"
+ text.size:8
+ text.validation:"R5,3"
+ }
+ wimp_icon {
+ extent:588,-632,772,-564
+ icon_flags:wimp_ICON_TEXT | wimp_ICON_BORDER | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_INDIRECTED | wimp_BUTTON_CLICK
+ icon_esg:0
+ icon_fg:wimp_COLOUR_BLACK
+ icon_bg:wimp_COLOUR_VERY_LIGHT_GREY
+ text.text:"Accepteer"
+ text.size:10
+ text.validation:"R6,3"
+ }
+}
diff --git a/frontends/riscos/textarea.c b/frontends/riscos/textarea.c
new file mode 100644
index 000000000..ecf3e0c3d
--- /dev/null
+++ b/frontends/riscos/textarea.c
@@ -0,0 +1,1160 @@
+/*
+ * Copyright 2006 John-Mark Bell <jmb202@ecs.soton.ac.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Single/Multi-line UTF-8 text area (implementation)
+ */
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <swis.h>
+#include <oslib/colourtrans.h>
+#include <oslib/osbyte.h>
+#include <oslib/serviceinternational.h>
+#include <oslib/wimp.h>
+#include <oslib/wimpspriteop.h>
+
+#include "utils/log.h"
+#include "utils/utf8.h"
+#include "desktop/browser.h"
+
+#include "riscos/gui.h"
+#include "riscos/oslib_pre7.h"
+#include "riscos/textarea.h"
+#include "riscos/ucstables.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/wimputils.h"
+
+#define MARGIN_LEFT 8
+#define MARGIN_RIGHT 8
+
+struct line_info {
+ unsigned int b_start; /**< Byte offset of line start */
+ unsigned int b_length; /**< Byte length of line */
+};
+
+struct text_area {
+#define MAGIC (('T'<<24) | ('E'<<16) | ('X'<<8) | 'T')
+ unsigned int magic; /**< Magic word, for sanity */
+
+ unsigned int flags; /**< Textarea flags */
+ unsigned int vis_width; /**< Visible width, in pixels */
+ unsigned int vis_height; /**< Visible height, in pixels */
+ wimp_w window; /**< Window handle */
+
+ char *text; /**< UTF-8 text */
+ unsigned int text_alloc; /**< Size of allocated text */
+ unsigned int text_len; /**< Length of text, in bytes */
+ struct {
+ unsigned int line; /**< Line caret is on */
+ unsigned int char_off; /**< Character index of caret */
+ } caret_pos;
+// unsigned int selection_start; /**< Character index of sel start */
+// unsigned int selection_end; /**< Character index of sel end */
+
+ wimp_w parent; /**< Parent window handle */
+ wimp_i icon; /**< Parent icon handle */
+
+ char *font_family; /**< Font family of text */
+ unsigned int font_size; /**< Font size (16ths/pt) */
+ rufl_style font_style; /**< Font style (rufl) */
+ int line_height; /**< Total height of a line, given font size */
+ int line_spacing; /**< Height of line spacing, given font size */
+
+ unsigned int line_count; /**< Count of lines */
+#define LINE_CHUNK_SIZE 256
+ struct line_info *lines; /**< Line info array */
+
+ struct text_area *next; /**< Next text area in list */
+ struct text_area *prev; /**< Prev text area in list */
+};
+
+static wimp_window text_area_definition = {
+ {0, 0, 16, 16},
+ 0,
+ 0,
+ wimp_TOP,
+ wimp_WINDOW_NEW_FORMAT | wimp_WINDOW_NO_BOUNDS,
+ wimp_COLOUR_BLACK,
+ wimp_COLOUR_LIGHT_GREY,
+ wimp_COLOUR_LIGHT_GREY,
+ wimp_COLOUR_VERY_LIGHT_GREY,
+ wimp_COLOUR_DARK_GREY,
+ wimp_COLOUR_MID_LIGHT_GREY,
+ wimp_COLOUR_CREAM,
+ 0,
+ {0, -16384, 16384, 0},
+ wimp_ICON_TEXT | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED,
+ wimp_BUTTON_CLICK << wimp_ICON_BUTTON_TYPE_SHIFT,
+ wimpspriteop_AREA,
+ 1,
+ 1,
+ {""},
+ 0,
+ {}
+};
+
+static void ro_textarea_reflow(struct text_area *ta, unsigned int line);
+static bool ro_textarea_mouse_click(wimp_pointer *pointer);
+static bool ro_textarea_key_press(wimp_key *key);
+static void ro_textarea_redraw(wimp_draw *redraw);
+static void ro_textarea_redraw_internal(wimp_draw *redraw, bool update);
+static void ro_textarea_open(wimp_open *open);
+
+/**
+ * Create a text area
+ *
+ * \param parent Parent window
+ * \param icon Icon in parent window to replace
+ * \param flags Text area flags
+ * \param font_family RUfl font family to use, or NULL for default
+ * \param font_size Font size to use (pt * 16), or 0 for default
+ * \param font_style Font style to use, or 0 for default
+ * \return Opaque handle for textarea or 0 on error
+ */
+uintptr_t ro_textarea_create(wimp_w parent, wimp_i icon, unsigned int flags,
+ const char *font_family, unsigned int font_size,
+ rufl_style font_style)
+{
+ struct text_area *ret;
+ os_error *error;
+
+ ret = malloc(sizeof(struct text_area));
+ if (!ret) {
+ LOG("malloc failed");
+ return 0;
+ }
+
+ ret->parent = parent;
+ ret->icon = icon;
+ ret->magic = MAGIC;
+ ret->flags = flags;
+ ret->text = malloc(64);
+ if (!ret->text) {
+ LOG("malloc failed");
+ free(ret);
+ return 0;
+ }
+ ret->text[0] = '\0';
+ ret->text_alloc = 64;
+ ret->text_len = 1;
+ ret->caret_pos.line = ret->caret_pos.char_off = (unsigned int)-1;
+// ret->selection_start = (unsigned int)-1;
+// ret->selection_end = (unsigned int)-1;
+ ret->font_family = strdup(font_family ? font_family : "Corpus");
+ if (!ret->font_family) {
+ LOG("strdup failed");
+ free(ret->text);
+ free(ret);
+ return 0;
+ }
+ ret->font_size = font_size ? font_size : 192 /* 12pt */;
+ ret->font_style = font_style ? font_style : rufl_WEIGHT_400;
+
+ /** \todo Better line height calculation */
+ ret->line_height = (int)(((ret->font_size * 1.3) / 16) * 2.0) + 1;
+ ret->line_spacing = ret->line_height / 8;
+
+ ret->line_count = 0;
+ ret->lines = 0;
+
+ if (flags & TEXTAREA_READONLY)
+ text_area_definition.title_fg = 0xff;
+ else
+ text_area_definition.title_fg = wimp_COLOUR_BLACK;
+ error = xwimp_create_window(&text_area_definition, &ret->window);
+ if (error) {
+ LOG("xwimp_create_window: 0x%x: %s", error->errnum, error->errmess);
+ free(ret->font_family);
+ free(ret->text);
+ free(ret);
+ return 0;
+ }
+
+ /* set the window dimensions */
+ if (!ro_textarea_update((uintptr_t)ret)) {
+ ro_textarea_destroy((uintptr_t)ret);
+ return 0;
+ }
+
+ /* and register our event handlers */
+ ro_gui_wimp_event_set_user_data(ret->window, ret);
+ ro_gui_wimp_event_register_mouse_click(ret->window,
+ ro_textarea_mouse_click);
+ ro_gui_wimp_event_register_keypress(ret->window,
+ ro_textarea_key_press);
+ ro_gui_wimp_event_register_redraw_window(ret->window,
+ ro_textarea_redraw);
+ ro_gui_wimp_event_register_open_window(ret->window,
+ ro_textarea_open);
+
+ return (uintptr_t)ret;
+}
+
+/**
+ * Update the a text area following a change in the parent icon
+ *
+ * \param self Text area to update
+ */
+bool ro_textarea_update(uintptr_t self)
+{
+ struct text_area *ta;
+ wimp_window_state state;
+ wimp_icon_state istate;
+ os_box extent;
+ os_error *error;
+
+ ta = (struct text_area *)self;
+ if (!ta || ta->magic != MAGIC)
+ return false;
+
+ state.w = ta->parent;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+
+ istate.w = ta->parent;
+ istate.i = ta->icon;
+ error = xwimp_get_icon_state(&istate);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+
+ state.w = ta->window;
+ state.visible.x1 = state.visible.x0 + istate.icon.extent.x1 -
+ ro_get_vscroll_width(ta->window) - state.xscroll;
+ state.visible.x0 += istate.icon.extent.x0 + 2 - state.xscroll;
+ state.visible.y0 = state.visible.y1 + istate.icon.extent.y0 +
+ ro_get_hscroll_height(ta->window) - state.yscroll;
+ state.visible.y1 += istate.icon.extent.y1 - 2 - state.yscroll;
+
+ if (ta->flags & TEXTAREA_READONLY) {
+ state.visible.x0 += 2;
+ state.visible.x1 -= 4;
+ state.visible.y0 += 2;
+ state.visible.y1 -= 4;
+ }
+
+ /* set our width/height */
+ ta->vis_width = state.visible.x1 - state.visible.x0;
+ ta->vis_height = state.visible.y1 - state.visible.y0;
+
+ /* Set window extent to visible area */
+ extent.x0 = 0;
+ extent.y0 = -ta->vis_height;
+ extent.x1 = ta->vis_width;
+ extent.y1 = 0;
+
+ error = xwimp_set_extent(ta->window, &extent);
+ if (error) {
+ LOG("xwimp_set_extent: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+
+ /* and open the window */
+ error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state), ta->parent,
+ wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
+ << wimp_CHILD_XORIGIN_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
+ << wimp_CHILD_YORIGIN_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
+ << wimp_CHILD_LS_EDGE_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
+ << wimp_CHILD_RS_EDGE_SHIFT);
+ if (error) {
+ LOG("xwimp_open_window_nested: 0x%x: %s", error->errnum, error->errmess);
+ return false;
+ }
+
+ /* reflow the text */
+ ro_textarea_reflow(ta, 0);
+ return true;
+}
+
+/**
+ * Destroy a text area
+ *
+ * \param self Text area to destroy
+ */
+void ro_textarea_destroy(uintptr_t self)
+{
+ struct text_area *ta;
+ os_error *error;
+
+ ta = (struct text_area *)self;
+ if (!ta || ta->magic != MAGIC)
+ return;
+
+ error = xwimp_delete_window(ta->window);
+ if (error) {
+ LOG("xwimp_delete_window: 0x%x: %s", error->errnum, error->errmess);
+ }
+
+ ro_gui_wimp_event_finalise(ta->window);
+
+ free(ta->font_family);
+ free(ta->text);
+ free(ta);
+}
+
+/**
+ * Set the text in a text area, discarding any current text
+ *
+ * \param self Text area
+ * \param text UTF-8 text to set text area's contents to
+ * \return true on success, false on memory exhaustion
+ */
+bool ro_textarea_set_text(uintptr_t self, const char *text)
+{
+ struct text_area *ta;
+ unsigned int len = strlen(text) + 1;
+
+ ta = (struct text_area *)self;
+ if (!ta || ta->magic != MAGIC) {
+ LOG("magic doesn't match");
+ return true;
+ }
+
+ if (len >= ta->text_alloc) {
+ char *temp = realloc(ta->text, len + 64);
+ if (!temp) {
+ LOG("realloc failed");
+ return false;
+ }
+ ta->text = temp;
+ ta->text_alloc = len+64;
+ }
+
+ memcpy(ta->text, text, len);
+ ta->text_len = len;
+
+ ro_textarea_reflow(ta, 0);
+
+ return true;
+}
+
+/**
+ * Extract the text from a text area
+ *
+ * \param self Text area
+ * \param buf Pointer to buffer to receive data, or NULL
+ * to read length required
+ * \param len Length (bytes) of buffer pointed to by buf, or 0 to read length
+ * \return Length (bytes) written/required or -1 on error
+ */
+int ro_textarea_get_text(uintptr_t self, char *buf, unsigned int len)
+{
+ struct text_area *ta;
+
+ ta = (struct text_area *)self;
+ if (!ta || ta->magic != MAGIC) {
+ LOG("magic doesn't match");
+ return -1;
+ }
+
+ if (buf == NULL && len == 0) {
+ /* want length */
+ return ta->text_len;
+ }
+
+ if (len < ta->text_len) {
+ LOG("buffer too small");
+ return -1;
+ }
+
+ memcpy(buf, ta->text, ta->text_len);
+
+ return ta->text_len;
+}
+
+/**
+ * Insert text into the text area
+ *
+ * \param self Text area
+ * \param index 0-based character index to insert at
+ * \param text UTF-8 text to insert
+ */
+void ro_textarea_insert_text(uintptr_t self, unsigned int index,
+ const char *text)
+{
+ struct text_area *ta;
+ unsigned int b_len = strlen(text);
+ size_t b_off, c_len;
+
+ ta = (struct text_area *)self;
+ if (!ta || ta->magic != MAGIC) {
+ LOG("magic doesn't match");
+ return;
+ }
+
+ c_len = utf8_length(ta->text);
+
+ /* Find insertion point */
+ if (index > c_len)
+ index = c_len;
+
+ for (b_off = 0; index-- > 0;
+ b_off = utf8_next(ta->text, ta->text_len, b_off))
+ ; /* do nothing */
+
+ if (b_len + ta->text_len >= ta->text_alloc) {
+ char *temp = realloc(ta->text, b_len + ta->text_len + 64);
+ if (!temp) {
+ LOG("realloc failed");
+ return;
+ }
+
+ ta->text = temp;
+ ta->text_alloc = b_len + ta->text_len + 64;
+ }
+
+ /* Shift text following up */
+ memmove(ta->text + b_off + b_len, ta->text + b_off,
+ ta->text_len - b_off);
+ /* Insert new text */
+ memcpy(ta->text + b_off, text, b_len);
+
+ ta->text_len += b_len;
+
+ /** \todo calculate line to reflow from */
+ ro_textarea_reflow(ta, 0);
+}
+
+/**
+ * Replace text in a text area
+ *
+ * \param self Text area
+ * \param start Start character index of replaced section (inclusive)
+ * \param end End character index of replaced section (exclusive)
+ * \param text UTF-8 text to insert
+ */
+void ro_textarea_replace_text(uintptr_t self, unsigned int start,
+ unsigned int end, const char *text)
+{
+ struct text_area *ta;
+ int b_len = strlen(text);
+ size_t b_start, b_end, c_len, diff;
+
+ ta = (struct text_area *)self;
+ if (!ta || ta->magic != MAGIC) {
+ LOG("magic doesn't match");
+ return;
+ }
+
+ c_len = utf8_length(ta->text);
+
+ if (start > c_len)
+ start = c_len;
+ if (end > c_len)
+ end = c_len;
+
+ if (start == end)
+ return ro_textarea_insert_text(self, start, text);
+
+ if (start > end) {
+ int temp = end;
+ end = start;
+ start = temp;
+ }
+
+ diff = end - start;
+
+ for (b_start = 0; start-- > 0;
+ b_start = utf8_next(ta->text, ta->text_len, b_start))
+ ; /* do nothing */
+
+ for (b_end = b_start; diff-- > 0;
+ b_end = utf8_next(ta->text, ta->text_len, b_end))
+ ; /* do nothing */
+
+ if (b_len + ta->text_len - (b_end - b_start) >= ta->text_alloc) {
+ char *temp = realloc(ta->text,
+ b_len + ta->text_len - (b_end - b_start) + 64);
+ if (!temp) {
+ LOG("realloc failed");
+ return;
+ }
+
+ ta->text = temp;
+ ta->text_alloc =
+ b_len + ta->text_len - (b_end - b_start) + 64;
+ }
+
+ /* Shift text following to new position */
+ memmove(ta->text + b_start + b_len, ta->text + b_end,
+ ta->text_len - b_end);
+
+ /* Insert new text */
+ memcpy(ta->text + b_start, text, b_len);
+
+ ta->text_len += b_len - (b_end - b_start);
+
+ /** \todo calculate line to reflow from */
+ ro_textarea_reflow(ta, 0);
+}
+
+/**
+ * Set the caret's position
+ *
+ * \param self Text area
+ * \param caret 0-based character index to place caret at
+ */
+void ro_textarea_set_caret(uintptr_t self, unsigned int caret)
+{
+ struct text_area *ta;
+ size_t c_len, b_off;
+ unsigned int i;
+ size_t index;
+ int x;
+ os_coord os_line_height;
+ rufl_code code;
+ os_error *error;
+
+ ta = (struct text_area *)self;
+ if (!ta || ta->magic != MAGIC) {
+ LOG("magic doesn't match");
+ return;
+ }
+
+ c_len = utf8_length(ta->text);
+
+ if (caret > c_len)
+ caret = c_len;
+
+ /* Find byte offset of caret position */
+ for (b_off = 0; caret > 0; caret--)
+ b_off = utf8_next(ta->text, ta->text_len, b_off);
+
+ /* Now find line in which byte offset appears */
+ for (i = 0; i < ta->line_count - 1; i++)
+ if (ta->lines[i + 1].b_start > b_off)
+ break;
+
+ ta->caret_pos.line = i;
+
+ /* Now calculate the char. offset of the caret in this line */
+ for (c_len = 0, ta->caret_pos.char_off = 0;
+ c_len < b_off - ta->lines[i].b_start;
+ c_len = utf8_next(ta->text + ta->lines[i].b_start,
+ ta->lines[i].b_length, c_len))
+ ta->caret_pos.char_off++;
+
+
+ /* Finally, redraw the WIMP caret */
+ index = ro_textarea_get_caret(self);
+ os_line_height.x = 0;
+ os_line_height.y = (int)((float)(ta->line_height - ta->line_spacing) * 0.62) + 1;
+ ro_convert_pixels_to_os_units(&os_line_height, (os_mode)-1);
+
+ for (b_off = 0; index-- > 0; b_off = utf8_next(ta->text, ta->text_len, b_off))
+ ; /* do nothing */
+
+ code = rufl_width(ta->font_family, ta->font_style, ta->font_size,
+ ta->text + ta->lines[ta->caret_pos.line].b_start,
+ b_off - ta->lines[ta->caret_pos.line].b_start, &x);
+ if (code != rufl_OK) {
+ if (code == rufl_FONT_MANAGER_ERROR)
+ LOG("rufl_width: 0x%x: %s", rufl_fm_error->errnum, rufl_fm_error->errmess);
+ else
+ LOG("rufl_width: 0x%x", code);
+ return;
+ }
+
+ error = xwimp_set_caret_position(ta->window, -1, x + MARGIN_LEFT,
+ -((ta->caret_pos.line + 1) * ta->line_height) -
+ ta->line_height / 4 + ta->line_spacing,
+ os_line_height.y, -1);
+ if (error) {
+ LOG("xwimp_set_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+}
+
+/**
+ * Set the caret's position
+ *
+ * \param self Text area
+ * \param x X position of caret on the screen
+ * \param y Y position of caret on the screen
+ */
+void ro_textarea_set_caret_xy(uintptr_t self, int x, int y)
+{
+ struct text_area *ta;
+ wimp_window_state state;
+ size_t b_off, c_off, temp;
+ int line;
+ os_coord os_line_height;
+ rufl_code code;
+ os_error *error;
+
+ ta = (struct text_area *)self;
+ if (!ta || ta->magic != MAGIC) {
+ LOG("magic doesn't match");
+ return;
+ }
+
+ if (ta->flags & TEXTAREA_READONLY)
+ return;
+
+ os_line_height.x = 0;
+ os_line_height.y = (int)((float)(ta->line_height - ta->line_spacing) * 0.62) + 1;
+ ro_convert_pixels_to_os_units(&os_line_height, (os_mode)-1);
+
+ state.w = ta->window;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+
+ x = x - (state.visible.x0 - state.xscroll) - MARGIN_LEFT;
+ y = (state.visible.y1 - state.yscroll) - y;
+
+ line = y / ta->line_height;
+
+ if (line < 0)
+ line = 0;
+ if (ta->line_count - 1 < (unsigned)line)
+ line = ta->line_count - 1;
+
+ code = rufl_x_to_offset(ta->font_family, ta->font_style,
+ ta->font_size,
+ ta->text + ta->lines[line].b_start,
+ ta->lines[line].b_length,
+ x, &b_off, &x);
+ if (code != rufl_OK) {
+ if (code == rufl_FONT_MANAGER_ERROR)
+ LOG("rufl_x_to_offset: 0x%x: %s", rufl_fm_error->errnum, rufl_fm_error->errmess);
+ else
+ LOG("rufl_x_to_offset: 0x%x", code);
+ return;
+ }
+
+ for (temp = 0, c_off = 0; temp < b_off + ta->lines[line].b_start;
+ temp = utf8_next(ta->text, ta->text_len, temp))
+ c_off++;
+
+ ro_textarea_set_caret((uintptr_t)ta, c_off);
+}
+
+/**
+ * Get the caret's position
+ *
+ * \param self Text area
+ * \return 0-based character index of caret location, or -1 on error
+ */
+unsigned int ro_textarea_get_caret(uintptr_t self)
+{
+ struct text_area *ta;
+ size_t c_off = 0, b_off;
+
+ ta = (struct text_area *)self;
+ if (!ta || ta->magic != MAGIC) {
+ LOG("magic doesn't match");
+ return -1;
+ }
+
+ /* Calculate character offset of this line's start */
+ for (b_off = 0; b_off < ta->lines[ta->caret_pos.line].b_start;
+ b_off = utf8_next(ta->text, ta->text_len, b_off))
+ c_off++;
+
+ return c_off + ta->caret_pos.char_off;
+}
+
+/** \todo Selection handling */
+
+/**
+ * Reflow a text area from the given line onwards
+ *
+ * \param ta Text area to reflow
+ * \param line Line number to begin reflow on
+ */
+void ro_textarea_reflow(struct text_area *ta, unsigned int line)
+{
+ rufl_code code;
+ char *text;
+ unsigned int len;
+ size_t b_off;
+ int x;
+ char *space;
+ unsigned int line_count = 0;
+ os_box extent;
+ os_error *error;
+
+ /** \todo pay attention to line parameter */
+ /** \todo create horizontal scrollbar if needed */
+
+ ta->line_count = 0;
+
+ if (!ta->lines) {
+ ta->lines =
+ malloc(LINE_CHUNK_SIZE * sizeof(struct line_info));
+ if (!ta->lines) {
+ LOG("malloc failed");
+ return;
+ }
+ }
+
+ if (!(ta->flags & TEXTAREA_MULTILINE)) {
+ /* Single line */
+ ta->lines[line_count].b_start = 0;
+ ta->lines[line_count++].b_length = ta->text_len - 1;
+
+ ta->line_count = line_count;
+
+ return;
+ }
+
+ for (len = ta->text_len - 1, text = ta->text; len > 0;
+ len -= b_off, text += b_off) {
+ code = rufl_split(ta->font_family, ta->font_style,
+ ta->font_size, text, len,
+ ta->vis_width - MARGIN_LEFT - MARGIN_RIGHT,
+ &b_off, &x);
+ if (code != rufl_OK) {
+ if (code == rufl_FONT_MANAGER_ERROR)
+ LOG("rufl_x_to_offset: 0x%x: %s", rufl_fm_error->errnum, rufl_fm_error->errmess);
+ else
+ LOG("rufl_x_to_offset: 0x%x", code);
+ return;
+ }
+
+ if (line_count > 0 && line_count % LINE_CHUNK_SIZE == 0) {
+ struct line_info *temp = realloc(ta->lines,
+ (line_count + LINE_CHUNK_SIZE) *
+ sizeof(struct line_info));
+ if (!temp) {
+ LOG("realloc failed");
+ return;
+ }
+
+ ta->lines = temp;
+ }
+
+ /* handle CR/LF */
+ for (space = text; space < text + b_off; space++) {
+ if (*space == '\r' || *space == '\n')
+ break;
+ }
+
+ if (space != text + b_off) {
+ /* Found newline; use it */
+ ta->lines[line_count].b_start = text - ta->text;
+ ta->lines[line_count++].b_length = space - text;
+
+ /* CRLF / LFCR pair */
+ if (*space == '\r' && *(space + 1) == '\n')
+ space++;
+ else if (*space == '\n' && *(space + 1) == '\r')
+ space++;
+
+ b_off = space + 1 - text;
+
+ if (len - b_off == 0) {
+ /* reached end of input => add last line */
+ ta->lines[line_count].b_start =
+ text + b_off - ta->text;
+ ta->lines[line_count++].b_length = 0;
+ }
+
+ continue;
+ }
+
+ if (len - b_off > 0) {
+ /* find last space (if any) */
+ for (space = text + b_off; space > text; space--)
+ if (*space == ' ')
+ break;
+
+ if (space != text)
+ b_off = space + 1 - text;
+ }
+
+ ta->lines[line_count].b_start = text - ta->text;
+ ta->lines[line_count++].b_length = b_off;
+ }
+
+ ta->line_count = line_count;
+
+ /* and now update extent */
+ extent.x0 = 0;
+ extent.y1 = 0;
+ extent.x1 = ta->vis_width;
+ extent.y0 = -ta->line_height * line_count - ta->line_spacing;
+
+ if (extent.y0 > (int)-ta->vis_height)
+ /* haven't filled window yet */
+ return;
+
+ error = xwimp_set_extent(ta->window, &extent);
+ if (error) {
+ LOG("xwimp_set_extent: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+
+ /* Create vertical scrollbar if we don't already have one */
+ if (!ro_gui_wimp_check_window_furniture(ta->window,
+ wimp_WINDOW_VSCROLL)) {
+ wimp_window_state state;
+ wimp_w parent;
+ bits linkage;
+ unsigned int vscroll_width;
+
+ /* Save window parent & linkage flags */
+ state.w = ta->window;
+ error = xwimp_get_window_state_and_nesting(&state,
+ &parent, &linkage);
+ if (error) {
+ LOG("xwimp_get_window_state_and_nesting: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+
+ /* Now, attempt to create vertical scrollbar */
+ ro_gui_wimp_update_window_furniture(ta->window,
+ wimp_WINDOW_VSCROLL,
+ wimp_WINDOW_VSCROLL);
+
+ /* Get new window state */
+ state.w = ta->window;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+
+ /* Get scroll width */
+ vscroll_width = ro_get_vscroll_width(NULL);
+
+ /* Shrink width by difference */
+ state.visible.x1 -= vscroll_width;
+
+ /* and reopen window */
+ error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state),
+ parent, linkage);
+ if (error) {
+ LOG("xwimp_open_window_nested: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+
+ /* finally, update visible width */
+ ta->vis_width -= vscroll_width;
+
+ /* Now we've done that, we have to reflow the text area */
+ ro_textarea_reflow(ta, 0);
+ }
+}
+
+/**
+ * Handle mouse clicks in a text area
+ *
+ * \param pointer Mouse click state block
+ * \return true if click handled, false otherwise
+ */
+bool ro_textarea_mouse_click(wimp_pointer *pointer)
+{
+ struct text_area *ta;
+
+ ta = (struct text_area *)ro_gui_wimp_event_get_user_data(pointer->w);
+
+ ro_textarea_set_caret_xy((uintptr_t)ta, pointer->pos.x, pointer->pos.y);
+ return true;
+}
+
+/**
+ * Handle key presses in a text area
+ *
+ * \param key Key pressed state block
+ * \return true if press handled, false otherwise
+ */
+bool ro_textarea_key_press(wimp_key *key)
+{
+ uint32_t c = (uint32_t) key->c;
+ wimp_key keypress;
+ struct text_area *ta;
+ bool redraw = false;
+ unsigned int c_pos;
+
+ ta = (struct text_area *)ro_gui_wimp_event_get_user_data(key->w);
+
+ if (ta->flags & TEXTAREA_READONLY)
+ return true;
+
+ if (!(c & IS_WIMP_KEY ||
+ (c <= 0x001f || (0x007f <= c && c <= 0x009f)))) {
+ /* normal character - insert */
+ char utf8[7];
+ size_t utf8_len;
+
+ utf8_len = utf8_from_ucs4(c, utf8);
+ utf8[utf8_len] = '\0';
+
+ c_pos = ro_textarea_get_caret((uintptr_t)ta);
+ ro_textarea_insert_text((uintptr_t)ta, c_pos, utf8);
+ ro_textarea_set_caret((uintptr_t)ta, ++c_pos);
+
+ redraw = true;
+ } else {
+ os_error *error;
+ /** \todo handle command keys */
+ switch (c & ~IS_WIMP_KEY) {
+ case 8: /* Backspace */
+ c_pos = ro_textarea_get_caret((uintptr_t)ta);
+ if (c_pos > 0) {
+ ro_textarea_replace_text((uintptr_t)ta,
+ c_pos - 1, c_pos, "");
+ ro_textarea_set_caret((uintptr_t)ta, c_pos - 1);
+ redraw = true;
+ }
+ break;
+ case 21: /* Ctrl + U */
+ ro_textarea_set_text((uintptr_t)ta, "");
+ ro_textarea_set_caret((uintptr_t)ta, 0);
+ redraw = true;
+ break;
+ case wimp_KEY_DELETE:
+ c_pos = ro_textarea_get_caret((uintptr_t)ta);
+ if (os_version < RISCOS5 && c_pos > 0) {
+ ro_textarea_replace_text((uintptr_t)ta,
+ c_pos - 1, c_pos, "");
+ ro_textarea_set_caret((uintptr_t)ta, c_pos - 1);
+ } else {
+ ro_textarea_replace_text((uintptr_t)ta, c_pos,
+ c_pos + 1, "");
+ }
+ redraw = true;
+ break;
+
+ case wimp_KEY_LEFT:
+ c_pos = ro_textarea_get_caret((uintptr_t)ta);
+ if (c_pos > 0)
+ ro_textarea_set_caret((uintptr_t)ta, c_pos - 1);
+ break;
+ case wimp_KEY_RIGHT:
+ c_pos = ro_textarea_get_caret((uintptr_t)ta);
+ ro_textarea_set_caret((uintptr_t)ta, c_pos + 1);
+ break;
+ case wimp_KEY_UP:
+ /** \todo Move caret up a line */
+ break;
+ case wimp_KEY_DOWN:
+ /** \todo Move caret down a line */
+ break;
+
+ case wimp_KEY_HOME:
+ case wimp_KEY_CONTROL | wimp_KEY_LEFT:
+ /** \todo line start */
+ break;
+ case wimp_KEY_CONTROL | wimp_KEY_RIGHT:
+ /** \todo line end */
+ break;
+ case wimp_KEY_CONTROL | wimp_KEY_UP:
+ ro_textarea_set_caret((uintptr_t)ta, 0);
+ break;
+ case wimp_KEY_CONTROL | wimp_KEY_DOWN:
+ ro_textarea_set_caret((uintptr_t)ta,
+ utf8_length(ta->text));
+ break;
+
+ case wimp_KEY_COPY:
+ if (os_version < RISCOS5) {
+ c_pos = ro_textarea_get_caret((uintptr_t)ta);
+ ro_textarea_replace_text((uintptr_t)ta, c_pos,
+ c_pos + 1, "");
+ } else {
+ /** \todo line end */
+ }
+ break;
+
+ /** pass on RETURN and ESCAPE to the parent icon */
+ case wimp_KEY_RETURN:
+ if (ta->flags & TEXTAREA_MULTILINE) {
+ /* Insert newline */
+ c_pos = ro_textarea_get_caret((uintptr_t)ta);
+ ro_textarea_insert_text((uintptr_t)ta, c_pos,
+ "\n");
+ ro_textarea_set_caret((uintptr_t)ta, ++c_pos);
+
+ redraw = true;
+
+ break;
+ }
+ /* fall through */
+ case wimp_KEY_ESCAPE:
+ keypress = *key;
+ keypress.w = ta->parent;
+ keypress.i = ta->icon;
+ keypress.index = 0; /* undefined if not in an icon */
+ error = xwimp_send_message_to_window(wimp_KEY_PRESSED,
+ (wimp_message*)&keypress, ta->parent,
+ ta->icon, 0);
+ if (error) {
+ LOG("xwimp_send_message: 0x%x:%s", error->errnum, error->errmess);
+ }
+ break;
+ }
+ }
+
+ if (redraw) {
+ wimp_draw update;
+
+ update.w = ta->window;
+ update.box.x0 = 0;
+ update.box.y1 = 0;
+ update.box.x1 = ta->vis_width;
+ update.box.y0 = -ta->line_height * (ta->line_count + 1);
+ ro_textarea_redraw_internal(&update, true);
+ }
+
+ return true;
+}
+
+/**
+ * Handle WIMP redraw requests for text areas
+ *
+ * \param redraw Redraw request block
+ */
+void ro_textarea_redraw(wimp_draw *redraw)
+{
+ ro_textarea_redraw_internal(redraw, false);
+}
+
+/**
+ * Internal textarea redraw routine
+ *
+ * \param redraw Redraw/update request block
+ * \param update True if update, false if full redraw
+ */
+void ro_textarea_redraw_internal(wimp_draw *redraw, bool update)
+{
+ struct text_area *ta;
+ int line;
+ osbool more;
+ rufl_code code;
+ os_error *error;
+
+ ta = (struct text_area *)ro_gui_wimp_event_get_user_data(redraw->w);
+
+ if (update)
+ error = xwimp_update_window(redraw, &more);
+ else
+ error = xwimp_redraw_window(redraw, &more);
+ if (error) {
+ LOG("xwimp_redraw_window: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+
+ while (more) {
+ int line0, line1;
+ int clip_y0, clip_y1;
+ clip_y0 = (redraw->box.y1-redraw->yscroll) - redraw->clip.y1;
+ clip_y1 = (redraw->box.y1-redraw->yscroll) - redraw->clip.y0;
+
+ error = xcolourtrans_set_gcol(
+ (ta->flags & TEXTAREA_READONLY) ? 0xD9D9D900
+ : 0xFFFFFF00,
+ colourtrans_SET_BG_GCOL | colourtrans_USE_ECFS_GCOL,
+ os_ACTION_OVERWRITE, 0, 0);
+ if (error) {
+ LOG("xcolourtrans_set_gcol: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+
+ error = xos_clg();
+ if (error) {
+ LOG("xos_clg: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+
+ if (!ta->lines)
+ /* Nothing to redraw */
+ return;
+
+ line0 = clip_y0 / ta->line_height - 1;
+ line1 = clip_y1 / ta->line_height + 1;
+
+ if (line0 < 0)
+ line0 = 0;
+ if (line1 < 0)
+ line1 = 0;
+ if (ta->line_count - 1 < (unsigned)line0)
+ line0 = ta->line_count - 1;
+ if (ta->line_count - 1 < (unsigned)line1)
+ line1 = ta->line_count - 1;
+ if (line1 < line0)
+ line1 = line0;
+
+ for (line = line0; line <= line1; line++) {
+ if (ta->lines[line].b_length == 0)
+ continue;
+
+ error = xcolourtrans_set_font_colours(font_CURRENT,
+ (ta->flags & TEXTAREA_READONLY) ?
+ 0xD9D9D900 : 0xFFFFFF00,
+ 0x00000000, 14, 0, 0, 0);
+ if (error) {
+ LOG("xcolourtrans_set_font_colours: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+
+ code = rufl_paint(ta->font_family, ta->font_style,
+ ta->font_size,
+ ta->text + ta->lines[line].b_start,
+ ta->lines[line].b_length,
+ redraw->box.x0 - redraw->xscroll + MARGIN_LEFT,
+ redraw->box.y1 - redraw->yscroll -
+ ((line + 1) *
+ ta->line_height - ta->line_spacing),
+ rufl_BLEND_FONT);
+ if (code != rufl_OK) {
+ if (code == rufl_FONT_MANAGER_ERROR)
+ LOG("rufl_paint: rufl_FONT_MANAGER_ERROR: 0x%x: %s", rufl_fm_error->errnum, rufl_fm_error->errmess);
+ else
+ LOG("rufl_paint: 0x%x", code);
+ }
+ }
+
+ error = xwimp_get_rectangle(redraw, &more);
+ if (error) {
+ LOG("xwimp_get_rectangle: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+ }
+}
+
+/**
+ * Handle a WIMP open window request
+ *
+ * \param open OpenWindow block
+ */
+void ro_textarea_open(wimp_open *open)
+{
+ os_error *error;
+
+ error = xwimp_open_window(open);
+ if (error) {
+ LOG("xwimp_open_window: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+}
diff --git a/frontends/riscos/textarea.h b/frontends/riscos/textarea.h
new file mode 100644
index 000000000..c726a0e78
--- /dev/null
+++ b/frontends/riscos/textarea.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2006 John-Mark Bell <jmb202@ecs.soton.ac.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Single/Multi-line UTF-8 text area (interface)
+ */
+
+#ifndef _NETSURF_RISCOS_TEXTAREA_H_
+#define _NETSURF_RISCOS_TEXTAREA_H_
+#include <stdbool.h>
+#include <stdint.h>
+#include "rufl.h"
+#include "oslib/wimp.h"
+
+/* Text area flags */
+#define TEXTAREA_MULTILINE 0x01 /**< Text area is multiline */
+#define TEXTAREA_READONLY 0x02 /**< Text area is read only */
+
+uintptr_t ro_textarea_create(wimp_w parent, wimp_i icon, unsigned int flags,
+ const char *font_family, unsigned int font_size,
+ rufl_style font_style);
+bool ro_textarea_update(uintptr_t self);
+void ro_textarea_destroy(uintptr_t self);
+bool ro_textarea_set_text(uintptr_t self, const char *text);
+int ro_textarea_get_text(uintptr_t self, char *buf, unsigned int len);
+void ro_textarea_insert_text(uintptr_t self, unsigned int index,
+ const char *text);
+void ro_textarea_replace_text(uintptr_t self, unsigned int start,
+ unsigned int end, const char *text);
+void ro_textarea_set_caret(uintptr_t self, unsigned int caret);
+void ro_textarea_set_caret_xy(uintptr_t self, int x, int y);
+unsigned int ro_textarea_get_caret(uintptr_t self);
+
+
+#endif
diff --git a/frontends/riscos/textselection.c b/frontends/riscos/textselection.c
new file mode 100644
index 000000000..718171db0
--- /dev/null
+++ b/frontends/riscos/textselection.c
@@ -0,0 +1,657 @@
+/*
+ * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Text selection code (platform-dependent implementation)
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <oslib/osfile.h>
+#include <oslib/wimp.h>
+
+#include "utils/log.h"
+#include "utils/utf8.h"
+#include "utils/utils.h"
+#include "content/hlcache.h"
+#include "desktop/gui_clipboard.h"
+#include "desktop/gui_window.h"
+#include "desktop/textinput.h"
+#include "desktop/browser.h"
+
+#include "riscos/gui.h"
+#include "riscos/menus.h"
+#include "riscos/message.h"
+#include "riscos/mouse.h"
+#include "riscos/save.h"
+#include "riscos/textselection.h"
+#include "riscos/ucstables.h"
+
+
+#ifndef wimp_DRAG_CLAIM_SUPPRESS_DRAGBOX
+#define wimp_DRAG_CLAIM_SUPPRESS_DRAGBOX ((wimp_drag_claim_flags) 0x2u)
+#endif
+
+
+/** Receive of Dragging message has claimed it */
+static bool dragging_claimed = false;
+static wimp_t dragging_claimant;
+static os_box dragging_box = { -34, -34, 34, 34 }; /* \todo - size properly */
+static wimp_drag_claim_flags last_claim_flags = 0;
+static struct gui_window *last_start_window;
+
+static bool drag_claimed = false;
+
+static bool owns_clipboard = false;
+static bool owns_caret_and_selection = false;
+
+/* Current clipboard contents if we own the clipboard
+ * Current paste buffer if we don't
+ */
+static char *clipboard = NULL;
+static size_t clip_length = 0;
+
+/* Paste context */
+static ro_gui_selection_prepare_paste_cb paste_cb = NULL;
+static void *paste_cb_pw = NULL;
+static int paste_prev_message = 0;
+
+static void ro_gui_selection_drag_end(wimp_dragged *drag, void *g);
+static void ro_gui_discard_clipboard_contents(void);
+static void ro_gui_dragging_bounced(wimp_message *message);
+
+
+/**
+ * Start drag-selecting text within a browser window (RO-dependent part)
+ *
+ * \param g gui window
+ */
+
+void gui_start_selection(struct gui_window *g)
+{
+ wimp_full_message_claim_entity msg;
+ wimp_auto_scroll_info scroll;
+ wimp_window_state state;
+ wimp_drag drag;
+ os_error *error;
+
+ LOG("starting text_selection drag");
+
+ state.w = g->window;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* claim caret and selection */
+ msg.size = sizeof(msg);
+ msg.your_ref = 0;
+ msg.action = message_CLAIM_ENTITY;
+ msg.flags = wimp_CLAIM_CARET_OR_SELECTION;
+
+ error = xwimp_send_message(wimp_USER_MESSAGE,
+ (wimp_message*)&msg, wimp_BROADCAST);
+ if (error) {
+ LOG("xwimp_send_message: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ owns_caret_and_selection = true;
+
+ scroll.w = g->window;
+ scroll.pause_zone_sizes.x0 = 80;
+ scroll.pause_zone_sizes.y0 = 80;
+ scroll.pause_zone_sizes.x1 = 80;
+ scroll.pause_zone_sizes.y1 = 80;
+ scroll.pause_duration = 0;
+ scroll.state_change = (void *)0;
+ error = xwimp_auto_scroll(wimp_AUTO_SCROLL_ENABLE_VERTICAL |
+ wimp_AUTO_SCROLL_ENABLE_HORIZONTAL,
+ &scroll, 0);
+ if (error)
+ LOG("xwimp_auto_scroll: 0x%x: %s", error->errnum, error->errmess);
+
+ ro_mouse_drag_start(ro_gui_selection_drag_end, ro_gui_window_mouse_at,
+ NULL, g);
+
+ drag.type = wimp_DRAG_USER_POINT;
+ /* Don't constrain mouse pointer during drags */
+ drag.bbox.x0 = -16384;
+ drag.bbox.y0 = -16384;
+ drag.bbox.x1 = 16384;
+ drag.bbox.y1 = 16384;
+
+ error = xwimp_drag_box(&drag);
+ if (error) {
+ LOG("xwimp_drag_box: 0x%x : %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ last_start_window = g;
+}
+
+
+/**
+ * End of text selection drag operation
+ *
+ * \param *drag position of pointer at conclusion of drag
+ * \param *data gui window pointer.
+ */
+
+static void ro_gui_selection_drag_end(wimp_dragged *drag, void *data)
+{
+ wimp_auto_scroll_info scroll;
+ wimp_pointer pointer;
+ os_error *error;
+ os_coord pos;
+ struct gui_window *g = (struct gui_window *) data;
+
+ scroll.w = g->window;
+ error = xwimp_auto_scroll(0, &scroll, 0);
+ if (error)
+ LOG("xwimp_auto_scroll: 0x%x: %s", error->errnum, error->errmess);
+
+ error = xwimp_drag_box((wimp_drag*)-1);
+ if (error) {
+ LOG("xwimp_drag_box: 0x%x : %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ error = xwimp_get_pointer_info(&pointer);
+ if (error) {
+ LOG("xwimp_get_pointer_info 0x%x : %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ if (ro_gui_window_to_window_pos(g, drag->final.x0, drag->final.y0, &pos))
+ browser_window_mouse_track(g->bw, 0, pos.x, pos.y);
+}
+
+/**
+ * Core tells front end to put given text in clipboard
+ *
+ * \param buffer UTF-8 text, owned by core
+ * \param length Byte length of UTF-8 text in buffer
+ * \param styles Array of styles given to text runs, owned by core, or NULL
+ * \param n_styles Number of text run styles in array
+ */
+static void gui_set_clipboard(const char *buffer, size_t length,
+ nsclipboard_styles styles[], int n_styles)
+{
+ char *new_cb;
+
+ if (length == 0)
+ return;
+
+ new_cb = malloc(length);
+ if (new_cb == NULL)
+ return;
+
+ memcpy(new_cb, buffer, length);
+
+ /* Replace existing clipboard contents */
+ free(clipboard);
+ clipboard = new_cb;
+ clip_length = length;
+
+ if (!owns_clipboard) {
+ /* Tell RO we now own clipboard */
+ wimp_full_message_claim_entity msg;
+ os_error *error;
+
+ LOG("claiming clipboard");
+
+ msg.size = sizeof(msg);
+ msg.your_ref = 0;
+ msg.action = message_CLAIM_ENTITY;
+ msg.flags = wimp_CLAIM_CLIPBOARD;
+
+ error = xwimp_send_message(wimp_USER_MESSAGE,
+ (wimp_message*)&msg, wimp_BROADCAST);
+ if (error) {
+ LOG("xwimp_send_message: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ owns_clipboard = true;
+ }
+
+ LOG("clipboard now holds %zd bytes", clip_length);
+}
+
+
+/**
+ * Core asks front end for clipboard contents.
+ *
+ * \param buffer UTF-8 text, allocated by front end, ownership yielded to core
+ * \param length Byte length of UTF-8 text in buffer
+ */
+static void gui_get_clipboard(char **buffer, size_t *length)
+{
+ *buffer = NULL;
+ *length = 0;
+
+ if (clip_length > 0) {
+ char *cb = malloc(clip_length);
+ if (cb != NULL) {
+ memcpy(cb, clipboard, clip_length);
+ *buffer = cb;
+ *length = clip_length;
+ }
+ }
+}
+
+
+/**
+ * Discard the current contents of the clipboard, if any, releasing the
+ * memory it uses.
+ */
+
+void ro_gui_discard_clipboard_contents(void)
+{
+ free(clipboard);
+ clipboard = NULL;
+ clip_length = 0;
+}
+
+
+static void ro_gui_selection_prepare_paste_complete(void)
+{
+ ro_gui_selection_prepare_paste_cb cb = paste_cb;
+ void *pw = paste_cb_pw;
+
+ paste_cb = NULL;
+ paste_cb_pw = NULL;
+ paste_prev_message = 0;
+
+ cb(pw);
+}
+
+static void ro_gui_selection_prepare_paste_bounced(wimp_message *message)
+{
+ ro_gui_selection_prepare_paste_complete();
+}
+
+/**
+ * Prepare to paste data from another application
+ *
+ * \param w Window being pasted into
+ * \param cb Callback to call once preparation is complete
+ * \param pw Private data for callback
+ */
+
+void ro_gui_selection_prepare_paste(wimp_w w,
+ ro_gui_selection_prepare_paste_cb cb, void *pw)
+{
+ if (owns_clipboard) {
+ /* We own the clipboard: we're already prepared */
+ cb(pw);
+ } else {
+ /* Someone else owns the clipboard: request its contents */
+ wimp_full_message_data_request msg;
+ bool success;
+
+ ro_gui_discard_clipboard_contents();
+
+ msg.size = 48; /* There's only one filetype listed. */
+ msg.your_ref = 0;
+ msg.action = message_DATA_REQUEST;
+ msg.w = w;
+ msg.i = -1;
+ msg.pos.x = 0;
+ msg.pos.y = 0;
+ msg.flags = wimp_DATA_REQUEST_CLIPBOARD;
+ msg.file_types[0] = osfile_TYPE_TEXT;
+ msg.file_types[1] = ~0;
+
+ success = ro_message_send_message(wimp_USER_MESSAGE_RECORDED,
+ (wimp_message *) &msg, wimp_BROADCAST,
+ ro_gui_selection_prepare_paste_bounced);
+ if (success == false) {
+ /* Ensure key is handled, anyway */
+ cb(pw);
+ } else {
+ /* Set up paste context */
+ paste_cb = cb;
+ paste_cb_pw = pw;
+ paste_prev_message = msg.my_ref;
+ }
+ }
+}
+
+/**
+ * Prepare to paste data from another application (step 2)
+ *
+ * \param dataxfer DataSave message
+ * \return True if message was handled, false otherwise
+ */
+bool ro_gui_selection_prepare_paste_datasave(
+ wimp_full_message_data_xfer *dataxfer)
+{
+ bool success;
+
+ /* Ignore messages that aren't for us */
+ if (dataxfer->your_ref == 0 || dataxfer->your_ref != paste_prev_message)
+ return false;
+
+ /* We're done if the paste data isn't text */
+ if (dataxfer->file_type != osfile_TYPE_TEXT) {
+ ro_gui_selection_prepare_paste_complete();
+ return true;
+ }
+
+ /* Generate and send DataSaveAck */
+ dataxfer->your_ref = dataxfer->my_ref;
+ dataxfer->size = offsetof(wimp_full_message_data_xfer, file_name) + 16;
+ dataxfer->action = message_DATA_SAVE_ACK;
+ dataxfer->est_size = -1;
+ memcpy(dataxfer->file_name, "<Wimp$Scrap>", SLEN("<Wimp$Scrap>") + 1);
+
+ success = ro_message_send_message(wimp_USER_MESSAGE_RECORDED,
+ (wimp_message *) dataxfer, dataxfer->sender,
+ ro_gui_selection_prepare_paste_bounced);
+ if (success == false) {
+ ro_gui_selection_prepare_paste_complete();
+ } else {
+ paste_prev_message = dataxfer->my_ref;
+ }
+
+ return true;
+}
+
+
+/**
+ * Prepare to paste data from another application (step 3)
+ *
+ * \param dataxfer DataLoad message
+ * \return True if message was handled, false otherwise
+ */
+bool ro_gui_selection_prepare_paste_dataload(
+ wimp_full_message_data_xfer *dataxfer)
+{
+ FILE *fp;
+
+ /* Ignore messages that aren't for us */
+ if (dataxfer->your_ref == 0 || dataxfer->your_ref != paste_prev_message)
+ return false;
+
+ fp = fopen(dataxfer->file_name, "r");
+ if (fp != NULL) {
+ long size;
+ fseek(fp, 0, SEEK_END);
+ size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ if (size > 0) {
+ char *local_cb = malloc(size);
+ if (local_cb != NULL) {
+ nserror ret;
+ fread(local_cb, 1, size, fp);
+
+ ret = utf8_from_local_encoding(local_cb, size,
+ &clipboard);
+ if (ret == NSERROR_OK) {
+ clip_length = strlen(clipboard);
+ }
+
+ free(local_cb);
+ }
+ }
+
+ fclose(fp);
+ }
+
+ /* Send DataLoadAck */
+ dataxfer->action = message_DATA_LOAD_ACK;
+ dataxfer->your_ref = dataxfer->my_ref;
+ ro_message_send_message(wimp_USER_MESSAGE,
+ (wimp_message *) dataxfer, dataxfer->sender, NULL);
+
+ ro_gui_selection_prepare_paste_complete();
+ return true;
+}
+
+
+/**
+ * Responds to CLAIM_ENTITY message notifying us that the caret
+ * and selection or clipboard have been claimed by another application.
+ *
+ * \param claim CLAIM_ENTITY message
+ */
+
+void ro_gui_selection_claim_entity(wimp_full_message_claim_entity *claim)
+{
+ /* ignore our own broadcasts! */
+ if (claim->sender != task_handle) {
+
+ LOG("%x", claim->flags);
+
+ if (claim->flags & wimp_CLAIM_CARET_OR_SELECTION) {
+ owns_caret_and_selection = false;
+ }
+
+ if (claim->flags & wimp_CLAIM_CLIPBOARD) {
+ ro_gui_discard_clipboard_contents();
+ owns_clipboard = false;
+ }
+ }
+}
+
+
+/**
+ * Responds to DATA_REQUEST message, returning information about the
+ * clipboard contents if we own the clipboard.
+ *
+ * \param req DATA_REQUEST message
+ */
+
+void ro_gui_selection_data_request(wimp_full_message_data_request *req)
+{
+ if (owns_clipboard && clip_length > 0 &&
+ (req->flags & wimp_DATA_REQUEST_CLIPBOARD)) {
+ wimp_full_message_data_xfer message;
+ int size;
+// int i;
+
+// for(i = 0; i < NOF_ELEMENTS(req->file_types); i++) {
+// bits ftype = req->file_types[i];
+// if (ftype == ~0U) break; /* list terminator */
+//
+// LOG("type %x", ftype);
+// i++;
+// }
+
+ /* we can only supply text at the moment, so that's what you're getting! */
+ size = offsetof(wimp_full_message_data_xfer, file_name) + 9;
+ message.size = (size + 3) & ~3;
+ message.your_ref = req->my_ref;
+ message.action = message_DATA_SAVE;
+ message.w = req->w;
+ message.i = req->i;
+ message.pos = req->pos;
+ message.file_type = osfile_TYPE_TEXT;
+ message.est_size = clip_length;
+ memcpy(message.file_name, "TextFile", 9);
+
+ ro_gui_send_datasave(GUI_SAVE_CLIPBOARD_CONTENTS,
+ &message, req->sender);
+ }
+}
+
+
+/**
+ * Save the clipboard contents to a file.
+ *
+ * \param path the pathname of the file
+ * \return true iff success, otherwise reporting the error before returning false
+ */
+
+bool ro_gui_save_clipboard(const char *path)
+{
+ char *local_cb;
+ nserror ret;
+ os_error *error;
+
+ assert(clip_length > 0 && clipboard);
+
+ ret = utf8_to_local_encoding(clipboard, clip_length, &local_cb);
+ if (ret != NSERROR_OK) {
+ ro_warn_user("SaveError", "Could not convert");
+ return false;
+ }
+
+ error = xosfile_save_stamped(path, osfile_TYPE_TEXT,
+ (byte*) local_cb,
+ (byte*) local_cb + strlen(local_cb));
+
+ free(local_cb);
+
+ if (error) {
+ LOG("xosfile_save_stamped: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ return false;
+ }
+
+ return true;
+}
+
+
+/**
+ * Handler for Message_Dragging, used to implement auto-scrolling and
+ * ghost caret when a drag is in progress.
+ */
+
+void ro_gui_selection_dragging(wimp_message *message)
+{
+ wimp_full_message_dragging *drag = (wimp_full_message_dragging*)message;
+ struct gui_window *g;
+ os_coord pos;
+
+ /* with autoscrolling, we will probably need to remember the
+ * gui_window and override the drag->w window handle which
+ * could be any window on the desktop */
+ g = ro_gui_window_lookup(drag->w);
+
+ if ((drag->flags & wimp_DRAGGING_TERMINATE_DRAG) || !g) {
+
+ drag_claimed = false;
+ return;
+ }
+
+ if (!ro_gui_window_to_window_pos(g, drag->pos.x, drag->pos.y, &pos))
+ return;
+
+ drag_claimed = false;
+}
+
+
+
+/**
+ * Reset drag-and-drop state when drag completes (DataSave received)
+ */
+
+void ro_gui_selection_drag_reset(void)
+{
+ drag_claimed = false;
+}
+
+
+/**
+ *
+ */
+
+void ro_gui_selection_drag_claim(wimp_message *message)
+{
+ wimp_full_message_drag_claim *claim = (wimp_full_message_drag_claim*)message;
+
+ dragging_claimant = message->sender;
+ dragging_claimed = true;
+
+ /* have we been asked to remove the drag box/sprite? */
+ if (claim->flags & wimp_DRAG_CLAIM_SUPPRESS_DRAGBOX) {
+ ro_gui_drag_box_cancel();
+ }
+ else {
+ /* \todo - restore it here? */
+ }
+
+ /* do we need to restore the default pointer shape? */
+ if ((last_claim_flags & wimp_DRAG_CLAIM_POINTER_CHANGED) &&
+ !(claim->flags & wimp_DRAG_CLAIM_POINTER_CHANGED)) {
+ gui_window_set_pointer(last_start_window, GUI_POINTER_DEFAULT);
+ }
+
+ last_claim_flags = claim->flags;
+}
+
+
+void ro_gui_selection_send_dragging(wimp_pointer *pointer)
+{
+ wimp_full_message_dragging dragmsg;
+
+ LOG("sending DRAGGING to %p, %d", pointer->w, pointer->i);
+
+ dragmsg.size = offsetof(wimp_full_message_dragging, file_types) + 8;
+ dragmsg.your_ref = 0;
+ dragmsg.action = message_DRAGGING;
+ dragmsg.w = pointer->w;
+ dragmsg.i = pointer->i;
+ dragmsg.pos = pointer->pos;
+/* \todo - this is interesting because it depends upon not just the state of the
+ shift key, but also whether it /can/ be deleted, ie. from text area/input
+ rather than page contents */
+ dragmsg.flags = wimp_DRAGGING_FROM_SELECTION;
+ dragmsg.box = dragging_box;
+ dragmsg.file_types[0] = osfile_TYPE_TEXT;
+ dragmsg.file_types[1] = ~0;
+
+ /* if the message_dragmsg messages have been claimed we must address them
+ to the claimant task, which is not necessarily the task that owns whatever
+ window happens to be under the pointer */
+
+ if (dragging_claimed) {
+ ro_message_send_message(wimp_USER_MESSAGE_RECORDED,
+ (wimp_message*)&dragmsg, dragging_claimant, ro_gui_dragging_bounced);
+ }
+ else {
+ ro_message_send_message_to_window(wimp_USER_MESSAGE_RECORDED,
+ (wimp_message*)&dragmsg, pointer->w, pointer->i,
+ ro_gui_dragging_bounced, &dragging_claimant);
+ }
+}
+
+
+/**
+ * Our message_DRAGGING message was bounced, ie. the intended recipient does not
+ * support the drag-and-drop protocol or cannot receive the data at the pointer
+ * position.
+ */
+
+void ro_gui_dragging_bounced(wimp_message *message)
+{
+ dragging_claimed = false;
+}
+
+static struct gui_clipboard_table clipboard_table = {
+ .get = gui_get_clipboard,
+ .set = gui_set_clipboard,
+};
+
+struct gui_clipboard_table *riscos_clipboard_table = &clipboard_table;
diff --git a/frontends/riscos/textselection.h b/frontends/riscos/textselection.h
new file mode 100644
index 000000000..400e3dd26
--- /dev/null
+++ b/frontends/riscos/textselection.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2006 Adrian Lees <adrianl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Text selection import/export (interface).
+ */
+
+#ifndef _NETSURF_RISCOS_TEXTSELECTION_H_
+#define _NETSURF_RISCOS_TEXTSELECTION_H_
+
+#include "oslib/wimp.h"
+
+struct gui_clipboard_table *riscos_clipboard_table;
+
+void gui_start_selection(struct gui_window *g);
+
+typedef void (*ro_gui_selection_prepare_paste_cb)(void *pw);
+
+void ro_gui_selection_prepare_paste(wimp_w w,
+ ro_gui_selection_prepare_paste_cb cb, void *pw);
+bool ro_gui_selection_prepare_paste_datasave(
+ wimp_full_message_data_xfer *dataxfer);
+bool ro_gui_selection_prepare_paste_dataload(
+ wimp_full_message_data_xfer *dataxfer);
+
+void ro_gui_selection_claim_entity(wimp_full_message_claim_entity *claim);
+void ro_gui_selection_data_request(wimp_full_message_data_request *req);
+bool ro_gui_save_clipboard(const char *path);
+
+/* drag-and-drop, receiving */
+void ro_gui_selection_dragging(wimp_message *message);
+void ro_gui_selection_drag_reset(void);
+
+/* drag-and-drop, sending */
+void ro_gui_selection_send_dragging(wimp_pointer *pointer);
+void ro_gui_selection_drag_claim(wimp_message *message);
+
+#endif
diff --git a/frontends/riscos/theme.c b/frontends/riscos/theme.c
new file mode 100644
index 000000000..714b9e5a1
--- /dev/null
+++ b/frontends/riscos/theme.c
@@ -0,0 +1,741 @@
+/*
+ * Copyright 2004, 2005 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Window themes (implementation).
+ */
+
+#include <alloca.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include "oslib/dragasprite.h"
+#include "oslib/os.h"
+#include "oslib/osgbpb.h"
+#include "oslib/osfile.h"
+#include "oslib/osfind.h"
+#include "oslib/osspriteop.h"
+#include "oslib/wimpspriteop.h"
+#include "oslib/squash.h"
+#include "oslib/wimp.h"
+#include "oslib/wimpextend.h"
+#include "oslib/wimpspriteop.h"
+
+#include "utils/nsoption.h"
+#include "utils/log.h"
+#include "content/content.h"
+
+#include "riscos/cookies.h"
+#include "riscos/dialog.h"
+#include "riscos/global_history.h"
+#include "riscos/gui.h"
+#include "riscos/hotlist.h"
+#include "riscos/menus.h"
+#include "riscos/theme.h"
+#include "riscos/treeview.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/wimputils.h"
+
+/** @todo provide a proper interface for these and make them static again! */
+
+static struct theme_descriptor *theme_current = NULL;
+static struct theme_descriptor *theme_descriptors = NULL;
+
+static bool ro_gui_theme_add_descriptor(const char *folder, const char *leafname);
+static void ro_gui_theme_get_available_in_dir(const char *directory);
+static void ro_gui_theme_free(struct theme_descriptor *descriptor);
+
+/**
+ * Initialise the theme handler
+ */
+void ro_gui_theme_initialise(void)
+{
+ struct theme_descriptor *descriptor;
+
+ theme_descriptors = ro_gui_theme_get_available();
+ descriptor = ro_gui_theme_find(nsoption_charp(theme));
+ if (!descriptor)
+ descriptor = ro_gui_theme_find("Aletheia");
+ ro_gui_theme_apply(descriptor);
+}
+
+
+/**
+ * Finalise the theme handler
+ */
+void ro_gui_theme_finalise(void)
+{
+ ro_gui_theme_close(theme_current, false);
+ ro_gui_theme_free(theme_descriptors);
+}
+
+
+/**
+ * Finds a theme from the cached values.
+ *
+ * The returned theme is only guaranteed to be valid until the next call
+ * to ro_gui_theme_get_available() unless it has been opened using
+ * ro_gui_theme_open().
+ *
+ * \param leafname the filename of the theme_descriptor to return
+ * \return the requested theme_descriptor, or NULL if not found
+ */
+struct theme_descriptor *ro_gui_theme_find(const char *leafname)
+{
+ struct theme_descriptor *descriptor;
+
+ if (!leafname)
+ return NULL;
+
+ for (descriptor = theme_descriptors; descriptor;
+ descriptor = descriptor->next)
+ if (!strcmp(leafname, descriptor->leafname))
+ return descriptor;
+ /* fallback for 10 chars on old filesystems */
+ for (descriptor = theme_descriptors; descriptor;
+ descriptor = descriptor->next)
+ if (!strncmp(leafname, descriptor->leafname, 10))
+ return descriptor;
+ return NULL;
+}
+
+
+/**
+ * Reads and caches the currently available themes.
+ *
+ * \return the requested theme_descriptor, or NULL if not found
+ */
+struct theme_descriptor *ro_gui_theme_get_available(void)
+{
+ struct theme_descriptor *current;
+ struct theme_descriptor *test;
+
+ /* close any unused descriptors */
+ ro_gui_theme_free(theme_descriptors);
+
+ /* add our default 'Aletheia' theme */
+ ro_gui_theme_add_descriptor("NetSurf:Resources", "Aletheia");
+
+ /* scan our choices directory */
+ ro_gui_theme_get_available_in_dir(nsoption_charp(theme_path));
+
+ /* sort alphabetically in a very rubbish way */
+ if ((theme_descriptors) && (theme_descriptors->next)) {
+ current = theme_descriptors;
+ while ((test = current->next)) {
+ if (strcmp(current->name, test->name) > 0) {
+ current->next->previous = current->previous;
+ if (current->previous)
+ current->previous->next = current->next;
+ current->next = test->next;
+ test->next = current;
+ current->previous = test;
+ if (current->next)
+ current->next->previous = current;
+
+ current = test->previous;
+ if (!current) current = test;
+ } else {
+ current = current->next;
+ }
+ }
+ while (theme_descriptors->previous)
+ theme_descriptors = theme_descriptors->previous;
+ }
+
+ return theme_descriptors;
+}
+
+
+/**
+ * Adds the themes in a directory to the global cache.
+ *
+ * \param directory the directory to scan
+ */
+static void ro_gui_theme_get_available_in_dir(const char *directory)
+{
+ int context = 0;
+ int read_count;
+ osgbpb_INFO(100) info;
+
+ while (context != -1) {
+ /* read some directory info */
+ os_error *error = xosgbpb_dir_entries_info(directory,
+ (osgbpb_info_list *) &info, 1, context,
+ sizeof(info), 0, &read_count, &context);
+ if (error) {
+ LOG("xosgbpb_dir_entries_info: 0x%x: %s",
+ error->errnum, error->errmess);
+ if (error->errnum == 0xd6) /* no such dir */
+ return;
+ ro_warn_user("MiscError", error->errmess);
+ break;
+ }
+
+ /* only process files */
+ if ((read_count != 0) && (info.obj_type == fileswitch_IS_FILE))
+ ro_gui_theme_add_descriptor(directory, info.name);
+ }
+}
+
+
+/**
+ * Returns the current theme handle, or NULL if none is set.
+ *
+ * \return The theme descriptor handle, or NULL.
+ */
+
+struct theme_descriptor *ro_gui_theme_get_current(void)
+{
+ return theme_current;
+}
+
+
+/**
+ * Returns a sprite area for use with the given theme. This may return a
+ * pointer to the wimp sprite pool if a theme area isn't available.
+ *
+ * \param *descriptor The theme to use, or NULL for the current.
+ * \return A pointer to the theme sprite area.
+ */
+
+osspriteop_area *ro_gui_theme_get_sprites(struct theme_descriptor *descriptor)
+{
+ osspriteop_area *area;
+
+ if (descriptor == NULL)
+ descriptor = theme_current;
+
+ if (descriptor != NULL && descriptor->theme != NULL)
+ area = descriptor->theme->sprite_area;
+ else
+ area = (osspriteop_area *) 1;
+
+ return area;
+}
+
+
+/**
+ * Returns an interger element from the specified theme, or the current theme
+ * if the descriptor is NULL.
+ *
+ * This is an attempt to abstract the theme data from its clients: it should
+ * simplify the task of expanding the theme system in the future should this
+ * be necessary to include other parts of the RISC OS GUI in the theme system.
+ *
+ * \param *descriptor The theme to use, or NULL for the current.
+ * \param style The style to use.
+ * \param element The style element to return.
+ * \return The requested value, or 0.
+ */
+
+int ro_gui_theme_get_style_element(struct theme_descriptor *descriptor,
+ theme_style style, theme_element element)
+{
+ if (descriptor == NULL)
+ descriptor = theme_current;
+
+ if (descriptor == NULL)
+ return 0;
+
+ switch (style) {
+ case THEME_STYLE_NONE:
+ switch(element) {
+ case THEME_ELEMENT_FOREGROUND:
+ return wimp_COLOUR_BLACK;
+ case THEME_ELEMENT_BACKGROUND:
+ return wimp_COLOUR_VERY_LIGHT_GREY;
+ default:
+ return 0;
+ }
+ break;
+
+ case THEME_STYLE_BROWSER_TOOLBAR:
+ switch (element) {
+ case THEME_ELEMENT_FOREGROUND:
+ return wimp_COLOUR_BLACK;
+ case THEME_ELEMENT_BACKGROUND:
+ return descriptor->browser_background;
+ default:
+ return 0;
+ }
+ break;
+
+ case THEME_STYLE_HOTLIST_TOOLBAR:
+ case THEME_STYLE_COOKIES_TOOLBAR:
+ case THEME_STYLE_GLOBAL_HISTORY_TOOLBAR:
+ switch (element) {
+ case THEME_ELEMENT_FOREGROUND:
+ return wimp_COLOUR_BLACK;
+ case THEME_ELEMENT_BACKGROUND:
+ return descriptor->hotlist_background;
+ default:
+ return 0;
+ }
+ break;
+
+ case THEME_STYLE_STATUS_BAR:
+ switch (element) {
+ case THEME_ELEMENT_FOREGROUND:
+ return descriptor->status_foreground;
+ case THEME_ELEMENT_BACKGROUND:
+ return descriptor->status_background;
+ default:
+ return 0;
+ }
+ break;
+
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Returns details of the throbber as defined in a theme.
+ *
+ * \param *descriptor The theme of interest (NULL for current).
+ * \param *frames Return the number of animation frames.
+ * \param *width Return the throbber width.
+ * \param *height Return the throbber height.
+ * \param *right Return the 'locate on right' flag.
+ * \param *redraw Return the 'forcible redraw' flag.
+ * \return true if meaningful data has been returned;
+ * else false.
+ */
+
+bool ro_gui_theme_get_throbber_data(struct theme_descriptor *descriptor,
+ int *frames, int *width, int *height,
+ bool *right, bool *redraw)
+{
+ if (descriptor == NULL)
+ descriptor = theme_current;
+
+ if (descriptor == NULL || descriptor->theme == NULL)
+ return false;
+
+ if (frames != NULL)
+ *frames = descriptor->theme->throbber_frames;
+ if (width != NULL)
+ *width = descriptor->theme->throbber_width;
+ if (height != NULL)
+ *height = descriptor->theme->throbber_height;
+ if (right != NULL)
+ *right = descriptor->throbber_right;
+ if (redraw != NULL)
+ *redraw = descriptor->throbber_redraw;
+
+ return true;
+}
+
+
+/**
+ * Checks a theme is valid and adds it to the current list
+ *
+ * \param folder the theme folder
+ * \param leafname the theme leafname
+ * \return whether the theme was added
+ */
+bool ro_gui_theme_add_descriptor(const char *folder, const char *leafname)
+{
+ struct theme_file_header file_header;
+ struct theme_descriptor *current;
+ struct theme_descriptor *test;
+ int output_left;
+ os_fw file_handle;
+ os_error *error;
+ char *filename;
+
+ /* create a full filename */
+ filename = malloc(strlen(folder) + strlen(leafname) + 2);
+ if (!filename) {
+ LOG("No memory for malloc");
+ ro_warn_user("NoMemory", 0);
+ return false;
+ }
+ sprintf(filename, "%s.%s", folder, leafname);
+
+ /* get the header */
+ error = xosfind_openinw(osfind_NO_PATH, filename, 0,
+ &file_handle);
+ if (error) {
+ LOG("xosfind_openinw: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("FileError", error->errmess);
+ free(filename);
+ return false;
+ }
+ if (file_handle == 0) {
+ free(filename);
+ return false;
+ }
+ error = xosgbpb_read_atw(file_handle,
+ (byte *) &file_header,
+ sizeof (struct theme_file_header),
+ 0, &output_left);
+ xosfind_closew(file_handle);
+ if (error) {
+ LOG("xosbgpb_read_atw: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("FileError", error->errmess);
+ free(filename);
+ return false;
+ }
+ if (output_left > 0) { /* should try to read more? */
+ free(filename);
+ return false;
+ }
+
+ /* create a new theme descriptor */
+ current = (struct theme_descriptor *)calloc(1,
+ sizeof(struct theme_descriptor));
+ if (!current) {
+ LOG("calloc failed");
+ ro_warn_user("NoMemory", 0);
+ free(filename);
+ return false;
+ }
+ if (!ro_gui_theme_read_file_header(current, &file_header)) {
+ free(filename);
+ free(current);
+ return false;
+ }
+ current->filename = filename;
+ current->leafname = current->filename + strlen(folder) + 1;
+
+ /* don't add duplicates */
+ for (test = theme_descriptors; test; test = test->next) {
+ if (!strcmp(current->name, test->name)) {
+ free(current->filename);
+ free(current);
+ return false;
+ }
+ }
+
+ /* link in our new descriptor at the head*/
+ if (theme_descriptors) {
+ current->next = theme_descriptors;
+ theme_descriptors->previous = current;
+ }
+ theme_descriptors = current;
+ return true;
+
+}
+
+
+/**
+ * Fills in the basic details for a descriptor from a file header.
+ * The filename string is not set.
+ *
+ * \param descriptor the descriptor to set up
+ * \param file_header the header to read from
+ * \return false for a badly formed theme, true otherwise
+ */
+bool ro_gui_theme_read_file_header(struct theme_descriptor *descriptor,
+ struct theme_file_header *file_header)
+{
+ if ((file_header->magic_value != 0x4d54534e) ||
+ (file_header->parser_version > 2))
+ return false;
+
+ strcpy(descriptor->name, file_header->name);
+ strcpy(descriptor->author, file_header->author);
+ descriptor->browser_background = file_header->browser_bg;
+ descriptor->hotlist_background = file_header->hotlist_bg;
+ descriptor->status_background = file_header->status_bg;
+ descriptor->status_foreground = file_header->status_fg;
+ descriptor->decompressed_size = file_header->decompressed_sprite_size;
+ descriptor->compressed_size = file_header->compressed_sprite_size;
+ if (file_header->parser_version >= 2) {
+ descriptor->throbber_right =
+ !(file_header->theme_flags & (1 << 0));
+ descriptor->throbber_redraw =
+ file_header->theme_flags & (1 << 1);
+ } else {
+ descriptor->throbber_right =
+ (file_header->theme_flags == 0x00);
+ descriptor->throbber_redraw = true;
+ }
+ return true;
+}
+
+
+/**
+ * Opens a theme ready for use.
+ *
+ * \param descriptor the theme_descriptor to open
+ * \param list whether to open all themes in the list
+ * \return whether the operation was successful
+ */
+bool ro_gui_theme_open(struct theme_descriptor *descriptor, bool list)
+{
+ fileswitch_object_type obj_type;
+ squash_output_status status;
+ os_coord dimensions;
+ os_mode mode;
+ os_error *error;
+ struct theme_descriptor *next_descriptor;
+ char sprite_name[16];
+ const char *name = sprite_name;
+ bool result = true;
+ int i, n;
+ int workspace_size, file_size;
+ char *raw_data, *workspace;
+ osspriteop_area *decompressed;
+
+ /* If we are freeing the whole of the list then we need to
+ start at the first descriptor.
+ */
+ if (list && descriptor)
+ while (descriptor->previous) descriptor = descriptor->previous;
+
+ /* Open the themes
+ */
+ for (; descriptor; descriptor = next_descriptor) {
+ /* see if we should iterate through the entire list */
+ if (list)
+ next_descriptor = descriptor->next;
+ else
+ next_descriptor = NULL;
+
+ /* if we are already loaded, increase the usage count */
+ if (descriptor->theme) {
+ descriptor->theme->users = descriptor->theme->users + 1;
+ continue;
+ }
+
+ /* create a new theme */
+ descriptor->theme = (struct theme *)calloc(1,
+ sizeof(struct theme));
+ if (!descriptor->theme) {
+ LOG("calloc() failed");
+ ro_warn_user("NoMemory", 0);
+ continue;
+ }
+ descriptor->theme->users = 1;
+
+ /* try to load the associated file */
+ error = xosfile_read_stamped_no_path(descriptor->filename,
+ &obj_type, 0, 0, &file_size, 0, 0);
+ if (error) {
+ LOG("xosfile_read_stamped_no_path: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("FileError", error->errmess);
+ continue;
+ }
+ if (obj_type != fileswitch_IS_FILE)
+ continue;
+ raw_data = malloc(file_size);
+ if (!raw_data) {
+ LOG("malloc() failed");
+ ro_warn_user("NoMemory", 0);
+ continue;
+ }
+ error = xosfile_load_stamped_no_path(descriptor->filename,
+ (byte *)raw_data, 0, 0, 0, 0, 0);
+ if (error) {
+ free(raw_data);
+ LOG("xosfile_load_stamped_no_path: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("FileError", error->errmess);
+ continue;
+ }
+
+ /* decompress the new data */
+ error = xsquash_decompress_return_sizes(-1, &workspace_size, 0);
+ if (error) {
+ free(raw_data);
+ LOG("xsquash_decompress_return_sizes: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ continue;
+ }
+ decompressed = (osspriteop_area *)malloc(
+ descriptor->decompressed_size);
+ workspace = malloc(workspace_size);
+ if ((!decompressed) || (!workspace)) {
+ free(decompressed);
+ free(raw_data);
+ LOG("malloc() failed");
+ ro_warn_user("NoMemory", 0);
+ continue;
+ }
+ error = xsquash_decompress(squash_INPUT_ALL_PRESENT, workspace,
+ (byte *)(raw_data + sizeof(
+ struct theme_file_header)),
+ descriptor->compressed_size,
+ (byte *)decompressed,
+ descriptor->decompressed_size,
+ &status, 0, 0, 0, 0);
+ free(workspace);
+ free(raw_data);
+ if (error) {
+ free(decompressed);
+ LOG("xsquash_decompress: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ continue;
+ }
+ if (status != 0) {
+ free(decompressed);
+ continue;
+ }
+ descriptor->theme->sprite_area = decompressed;
+
+ /* find the highest sprite called 'throbber%i', and get the
+ * maximum dimensions for all 'thobber%i' icons. */
+ for (i = 1; i <= descriptor->theme->sprite_area->sprite_count;
+ i++) {
+ error = xosspriteop_return_name(osspriteop_USER_AREA,
+ descriptor->theme->sprite_area,
+ sprite_name, 16, i, 0);
+ if (error) {
+ LOG("xosspriteop_return_name: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ continue;
+ }
+ if (strncmp(sprite_name, "throbber", 8))
+ continue;
+
+ /* get the max sprite width/height */
+ error = xosspriteop_read_sprite_info(
+ osspriteop_USER_AREA,
+ descriptor->theme->sprite_area,
+ (osspriteop_id) name,
+ &dimensions.x, &dimensions.y,
+ (osbool *) 0, &mode);
+ if (error) {
+ LOG("xosspriteop_read_sprite_info: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ continue;
+ }
+ ro_convert_pixels_to_os_units(&dimensions, mode);
+ if (descriptor->theme->throbber_width < dimensions.x)
+ descriptor->theme->throbber_width =
+ dimensions.x;
+ if (descriptor->theme->throbber_height < dimensions.y)
+ descriptor->theme->throbber_height =
+ dimensions.y;
+
+ /* get the throbber number */
+ n = atoi(sprite_name + 8);
+ if (descriptor->theme->throbber_frames < n)
+ descriptor->theme->throbber_frames = n;
+ }
+ }
+ return result;
+}
+
+
+/**
+ * Applies the theme to all current windows and subsequent ones.
+ *
+ * \param descriptor the theme_descriptor to open
+ * \return whether the operation was successful
+ */
+bool ro_gui_theme_apply(struct theme_descriptor *descriptor)
+{
+ struct theme_descriptor *theme_previous;
+
+ /* check if the theme is already applied */
+ if (descriptor == theme_current)
+ return true;
+
+ /* re-open the new-theme and release the current theme */
+ if (!ro_gui_theme_open(descriptor, false))
+ return false;
+ theme_previous = theme_current;
+ theme_current = descriptor;
+
+ /* apply the theme to all the current toolbar-ed windows */
+ ro_toolbar_theme_update();
+
+ ro_gui_theme_close(theme_previous, false);
+ return true;
+}
+
+
+/**
+ * Closes a theme after use.
+ *
+ * \param descriptor the theme_descriptor to close
+ * \param list whether to open all themes in the list
+ * \return whether the operation was successful
+ */
+void ro_gui_theme_close(struct theme_descriptor *descriptor, bool list)
+{
+
+ if (!descriptor)
+ return;
+
+ /* move to the start of the list */
+ while (list && descriptor->previous)
+ descriptor = descriptor->previous;
+
+ /* close the themes */
+ while (descriptor) {
+ if (descriptor->theme) {
+ descriptor->theme->users = descriptor->theme->users - 1;
+ if (descriptor->theme->users <= 0) {
+ free(descriptor->theme->sprite_area);
+ free(descriptor->theme);
+ descriptor->theme = NULL;
+ }
+ }
+ if (!list)
+ return;
+ descriptor = descriptor->next;
+ }
+}
+
+
+/**
+ * Frees any unused theme descriptors.
+ *
+ * \param descriptor the theme_descriptor to free
+ */
+void ro_gui_theme_free(struct theme_descriptor *descriptor)
+{
+ struct theme_descriptor *next_descriptor;
+
+ if (!descriptor)
+ return;
+
+ /* move to the start of the list */
+ while (descriptor->previous)
+ descriptor = descriptor->previous;
+
+ /* free closed themes */
+ for (; descriptor; descriptor = next_descriptor) {
+ next_descriptor = descriptor->next;
+
+ /* no theme? no descriptor */
+ if (!descriptor->theme) {
+ if (descriptor->previous)
+ descriptor->previous->next = descriptor->next;
+ if (descriptor->next)
+ descriptor->next->previous =
+ descriptor->previous;
+
+ /* keep the cached list in sync */
+ if (theme_descriptors == descriptor)
+ theme_descriptors = next_descriptor;
+
+ /* release any memory */
+ free(descriptor->filename);
+ free(descriptor);
+ }
+ }
+}
+
+
diff --git a/frontends/riscos/theme.h b/frontends/riscos/theme.h
new file mode 100644
index 000000000..4a4ba1cb2
--- /dev/null
+++ b/frontends/riscos/theme.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Window themes(interface).
+ */
+
+#include <stdbool.h>
+#include "oslib/osspriteop.h"
+
+#ifndef _NETSURF_RISCOS_THEME_H_
+#define _NETSURF_RISCOS_THEME_H_
+
+/** Theme styles, collecting groups of attributes for different locations. */
+
+typedef enum {
+ THEME_STYLE_NONE = 0,
+ THEME_STYLE_BROWSER_TOOLBAR,
+ THEME_STYLE_HOTLIST_TOOLBAR,
+ THEME_STYLE_COOKIES_TOOLBAR,
+ THEME_STYLE_GLOBAL_HISTORY_TOOLBAR,
+ THEME_STYLE_STATUS_BAR
+} theme_style;
+
+/** Theme elements, which belong to styles. */
+
+typedef enum {
+ THEME_ELEMENT_FOREGROUND,
+ THEME_ELEMENT_BACKGROUND
+} theme_element;
+
+struct theme_file_header {
+ unsigned int magic_value;
+ unsigned int parser_version;
+ char name[32];
+ char author[64];
+ char browser_bg;
+ char hotlist_bg;
+ char status_bg;
+ char status_fg;
+ char theme_flags;
+ char future_expansion_1;
+ char future_expansion_2;
+ char future_expansion_3;
+ unsigned int compressed_sprite_size;
+ unsigned int decompressed_sprite_size;
+};
+
+struct theme {
+ osspriteop_area *sprite_area; /**< sprite area for theme */
+ int throbber_width; /**< width of the throbber */
+ int throbber_height; /**< height of the throbber */
+ int throbber_frames; /**< frames of animation for the throbber */
+ int users; /**< number of users for the theme */
+};
+
+struct theme_descriptor {
+ char *leafname; /**< theme leafname */
+ char *filename; /**< theme filename */
+ char name[32]; /**< theme name */
+ char author[64]; /**< theme author */
+ int browser_background; /**< background colour of browser toolbar */
+ int hotlist_background; /**< background colour of hotlist toolbar */
+ int status_background; /**< background colour of status window */
+ int status_foreground; /**< colour of status window text */
+ bool throbber_right; /**< throbber is on the right (left otherwise) */
+ bool throbber_redraw; /**< throbber requires forcible updating */
+ unsigned int decompressed_size; /**< decompressed sprite size */
+ unsigned int compressed_size; /**< compressed sprite size */
+ struct theme *theme; /**< corresponding theme (must be opened) */
+ struct theme_descriptor *previous; /**< previous descriptor in the list */
+ struct theme_descriptor *next; /**< next descriptor in the list */
+};
+
+void ro_gui_theme_initialise(void);
+void ro_gui_theme_finalise(void);
+struct theme_descriptor *ro_gui_theme_find(const char *leafname);
+struct theme_descriptor *ro_gui_theme_get_available(void);
+struct theme_descriptor *ro_gui_theme_get_current(void);
+osspriteop_area *ro_gui_theme_get_sprites(struct theme_descriptor *descriptor);
+int ro_gui_theme_get_style_element(struct theme_descriptor *descriptor,
+ theme_style style, theme_element element);
+bool ro_gui_theme_get_throbber_data(struct theme_descriptor *descriptor,
+ int *frames, int *width, int *height,
+ bool *right, bool *redraw);
+
+bool ro_gui_theme_read_file_header(struct theme_descriptor *descriptor,
+ struct theme_file_header *file_header);
+
+bool ro_gui_theme_open(struct theme_descriptor *descriptor, bool list);
+bool ro_gui_theme_apply(struct theme_descriptor *descriptor);
+void ro_gui_theme_close(struct theme_descriptor *descriptor, bool list);
+#endif
+
diff --git a/frontends/riscos/theme_install.c b/frontends/riscos/theme_install.c
new file mode 100644
index 000000000..5c11ffb83
--- /dev/null
+++ b/frontends/riscos/theme_install.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Theme auto-installing.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <oslib/osfile.h>
+#include <oslib/wimp.h>
+
+#include "utils/nsoption.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "content/content.h"
+#include "content/hlcache.h"
+#include "desktop/browser.h"
+#include "desktop/theme.h"
+
+#include "riscos/dialog.h"
+#include "riscos/gui.h"
+#include "riscos/theme.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+
+
+static hlcache_handle *theme_install_content = NULL;
+static struct theme_descriptor theme_install_descriptor;
+wimp_w dialog_theme_install;
+
+
+static void theme_install_close(wimp_w w);
+static nserror theme_install_callback(hlcache_handle *handle,
+ const hlcache_event *event, void *pw);
+static bool theme_install_read(const char *source_data,
+ unsigned long source_size);
+
+
+/**
+ * Handle a CONTENT_THEME that has started loading.
+ */
+
+void theme_install_start(hlcache_handle *c)
+{
+ assert(c != NULL);
+ assert(content_get_type(c) == CONTENT_THEME);
+
+ if (ro_gui_dialog_open_top(dialog_theme_install, NULL, 0, 0)) {
+ ro_warn_user("ThemeInstActive", 0);
+ return;
+ }
+
+ /* stop theme sitting in memory cache */
+ content_invalidate_reuse_data(c);
+
+ hlcache_handle_replace_callback(c, theme_install_callback, NULL);
+
+ ro_gui_set_icon_string(dialog_theme_install, ICON_THEME_INSTALL_MESSAGE,
+ messages_get("ThemeInstDown"), true);
+ ro_gui_set_icon_shaded_state(dialog_theme_install,
+ ICON_THEME_INSTALL_INSTALL, true);
+ ro_gui_wimp_event_register_close_window(dialog_theme_install,
+ theme_install_close);
+}
+
+
+/**
+ * Callback for fetchcache() for theme install fetches.
+ */
+
+nserror theme_install_callback(hlcache_handle *handle,
+ const hlcache_event *event, void *pw)
+{
+ switch (event->type) {
+
+ case CONTENT_MSG_DONE:
+ {
+ const char *source_data;
+ unsigned long source_size;
+ int author_indent = 0;
+ char buffer[256];
+
+ theme_install_content = handle;
+
+ source_data = content_get_source_data(handle, &source_size);
+
+ if (!theme_install_read(source_data, source_size)) {
+ ro_warn_user("ThemeInvalid", 0);
+ theme_install_close(dialog_theme_install);
+ break;
+ }
+
+ /* remove '© ' from the start of the data */
+ if (theme_install_descriptor.author[0] == '©')
+ author_indent++;
+ while (theme_install_descriptor.author[author_indent] == ' ')
+ author_indent++;
+ snprintf(buffer, sizeof buffer, messages_get("ThemeInstall"),
+ theme_install_descriptor.name,
+ &theme_install_descriptor.author[author_indent]);
+ buffer[sizeof buffer - 1] = '\0';
+ ro_gui_set_icon_string(dialog_theme_install,
+ ICON_THEME_INSTALL_MESSAGE,
+ buffer, true);
+ ro_gui_set_icon_shaded_state(dialog_theme_install,
+ ICON_THEME_INSTALL_INSTALL, false);
+ }
+ break;
+
+ case CONTENT_MSG_ERROR:
+ theme_install_close(dialog_theme_install);
+ ro_warn_user(event->data.error, 0);
+ break;
+
+ default:
+ break;
+ }
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Fill in theme_install_descriptor from received theme data.
+ *
+ * \param source_data received data
+ * \param source_size size of data
+ * \return true if data is a correct theme, false on error
+ *
+ * If the data is a correct theme, theme_install_descriptor is filled in.
+ */
+
+bool theme_install_read(const char *source_data, unsigned long source_size)
+{
+ const void *data = source_data;
+
+ if (source_size < sizeof(struct theme_file_header))
+ return false;
+ if (!ro_gui_theme_read_file_header(&theme_install_descriptor,
+ (struct theme_file_header *) data))
+ return false;
+ if (source_size - sizeof(struct theme_file_header) !=
+ theme_install_descriptor.compressed_size)
+ return false;
+ return true;
+}
+
+
+/**
+ * Install the downloaded theme.
+ *
+ * \param w the theme install window handle
+ */
+
+bool ro_gui_theme_install_apply(wimp_w w)
+{
+ char theme_save[256];
+ char *theme_file;
+ struct theme_descriptor *theme_install;
+ os_error *error;
+ char *fix;
+ const char *source_data;
+ unsigned long source_size;
+
+ assert(theme_install_content);
+
+ /* convert spaces to hard spaces */
+ theme_file = strdup(theme_install_descriptor.name);
+ if (!theme_file) {
+ LOG("malloc failed");
+ ro_warn_user("NoMemory", 0);
+ return false;
+ }
+ for (fix = theme_file; *fix != '\0'; fix++)
+ if (*fix == ' ')
+ *fix = 160; /* hard space */
+
+ /* simply overwrite previous theme versions */
+ snprintf(theme_save, sizeof theme_save, "%s.%s",
+ nsoption_charp(theme_save), theme_file);
+
+ theme_save[sizeof theme_save - 1] = '\0';
+
+ source_data = content_get_source_data(theme_install_content,
+ &source_size);
+
+ error = xosfile_save_stamped(theme_save, 0xffd,
+ (byte *) source_data,
+ (byte *) source_data + source_size);
+ if (error) {
+ LOG("xosfile_save_stamped: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("ThemeInstallErr", 0);
+ free(theme_file);
+ return false;
+ }
+
+ /* apply the new theme */
+ ro_gui_theme_get_available();
+ theme_install = ro_gui_theme_find(theme_file);
+ if (!theme_install || !ro_gui_theme_apply(theme_install)) {
+ ro_warn_user("ThemeApplyErr", 0);
+ } else {
+ nsoption_set_charp(theme, strdup(theme_install->leafname));
+ }
+ free(theme_file);
+ ro_gui_save_options();
+ return true;
+}
+
+
+/**
+ * Close the theme installer and free resources.
+ */
+
+void theme_install_close(wimp_w w)
+{
+ if (theme_install_content)
+ hlcache_handle_release(theme_install_content);
+
+ theme_install_content = NULL;
+}
diff --git a/frontends/riscos/tinct.h b/frontends/riscos/tinct.h
new file mode 100644
index 000000000..e02dcdece
--- /dev/null
+++ b/frontends/riscos/tinct.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/*
+ * Complete details on using Tinct are available from http://www.tinct.net.
+ */
+
+/** \file
+ * Tinct SWI numbers and flags for version 0.11
+ */
+
+#ifndef _NETSURF_RISCOS_TINCT_H_
+#define _NETSURF_RISCOS_TINCT_H_
+
+
+/**
+ * Plots an alpha-blended sprite at the specified coordinates.
+ *
+ * -> R2 Sprite pointer
+ * R3 X coordinate
+ * R4 Y coordinate
+ * R7 Flag word
+*/
+#define Tinct_PlotAlpha 0x57240
+
+
+/**
+ * Plots a scaled alpha-blended sprite at the specified coordinates.
+ *
+ * -> R2 Sprite pointer
+ * R3 X coordinate
+ * R4 Y coordinate
+ * R5 Scaled sprite width
+ * R6 Scaled sprite height
+ * R7 Flag word
+ */
+#define Tinct_PlotScaledAlpha 0x57241
+
+
+/**
+ * Plots a sprite at the specified coordinates with a constant 0xff value for
+ * the alpha channel, ie without a mask.
+ *
+ * -> R2 Sprite pointer
+ * R3 X coordinate
+ * R4 Y coordinate
+ * R7 Flag word
+ */
+#define Tinct_Plot 0x57242
+
+/**
+ * Plots a scaled sprite at the specified coordinates with a constant 0xff value
+ * for the alpha channel, ie without a mask.
+ *
+ * -> R2 Sprite pointer
+ * R3 X coordinate
+ * R4 Y coordinate
+ * R5 Scaled sprite width
+ * R6 Scaled sprite height
+ * R7 Flag word
+ */
+#define Tinct_PlotScaled 0x57243
+
+
+/**
+ * Converts a paletted sprite into its 32bpp equivalent. Sufficient memory must
+ * have previously been allocated for the sprite (44 + width * height * 4).
+ * As sprites with 16bpp or 32bpp do not have palettes, conversion cannot be
+ * performed on these variants. All sprites must be supplied with a full palette,
+ * eg 8bpp must have 256 palette entries.
+ *
+ * -> R2 Source sprite pointer
+ * R3 Destination sprite pointer
+ */
+#define Tinct_ConvertSprite 0x57244
+
+
+/**
+ * Returns the features available to the caller by specifying bits in the flag
+ * word. The features available are unique for each mode, although the current
+ * version of Tinct supports the same subset of features for all modes.
+ *
+ * -> R0 Feature to test for, or 0 for all features
+ * <- R0 Features available
+ */
+#define Tinct_AvailableFeatures 0x57245
+
+
+/**
+ * Compresses an image using a fast algorithm. Sufficient memory must have been
+ * previously allocated for the maximum possible compressed size. This value is
+ * equal to 28 + (width * height * 4) * 33 / 32.
+ *
+ * -> R0 Source sprite pointer
+ * R2 Output data buffer
+ * R3 Output bytes available
+ * R7 Flag word (currently 0)
+ * <- R0 Size of compressed data
+ */
+#define Tinct_Compress 0x57246
+
+
+/**
+ * Decompresses an image previously compressed. Sufficient memory must have been
+ * previously allocated for the decompressed data (44 + width * height * 4) where
+ * width and height are available at +0 and +4 of the compressed data respectively.
+ *
+ * -> R0 Input data buffer
+ * R2 Output data buffer
+ * R7 Flag word (currently 0)
+ * <- R0 Size of decompressed data
+ */
+#define Tinct_Decompress 0x57247
+
+
+/* Plotting flags
+*/
+#define tinct_READ_SCREEN_BASE 0x01 /** <-- Use when hardware scrolling */
+#define tinct_BILINEAR_FILTER 0x02 /** <-- Perform bi-linear filtering */
+#define tinct_DITHER 0x04 /** <-- Perform dithering */
+#define tinct_ERROR_DIFFUSE 0x08 /** <-- Perform error diffusion */
+#define tinct_DITHER_INVERTED 0x0C /** <-- Perform dithering with inverted pattern */
+#define tinct_FILL_HORIZONTALLY 0x10 /** <-- Horizontally fill clipping region with image */
+#define tinct_FILL_VERTICALLY 0x20 /** <-- Vertically fill clipping region with image */
+#define tinct_FORCE_PALETTE_READ 0x40 /** <-- Use after a palette change when out of the desktop */
+#define tinct_USE_OS_SPRITE_OP 0x80 /** <-- Use when printing */
+
+/* Compression flags
+*/
+#define tinct_OPAQUE_IMAGE 0x01 /** <-- Image is opaque, compress further */
+
+/* Shifts
+*/
+#define tinct_BACKGROUND_SHIFT 0x08
+
+/* Sprite mode
+*/
+#define tinct_SPRITE_MODE (os_mode)0x301680b5
+#endif
diff --git a/frontends/riscos/toolbar.c b/frontends/riscos/toolbar.c
new file mode 100644
index 000000000..83751a7b4
--- /dev/null
+++ b/frontends/riscos/toolbar.c
@@ -0,0 +1,1788 @@
+/*
+ * Copyright 2004, 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2010, 2011 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Window toolbars (implementation).
+ */
+
+#include <alloca.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include "oslib/dragasprite.h"
+#include "oslib/os.h"
+#include "oslib/osgbpb.h"
+#include "oslib/osfile.h"
+#include "oslib/osfind.h"
+#include "oslib/osspriteop.h"
+#include "oslib/wimpspriteop.h"
+#include "oslib/squash.h"
+#include "oslib/wimp.h"
+#include "oslib/wimpextend.h"
+#include "oslib/wimpspriteop.h"
+
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "content/content.h"
+#include "desktop/plotters.h"
+
+#include "riscos/cookies.h"
+#include "riscos/dialog.h"
+#include "riscos/global_history.h"
+#include "riscos/gui.h"
+#include "riscos/gui/button_bar.h"
+#include "riscos/gui/throbber.h"
+#include "riscos/gui/url_bar.h"
+#include "riscos/hotlist.h"
+#include "riscos/menus.h"
+#include "riscos/save.h"
+#include "riscos/theme.h"
+#include "riscos/toolbar.h"
+#include "riscos/treeview.h"
+#include "riscos/url_complete.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/wimputils.h"
+#include "riscos/window.h"
+
+
+#define TOOLBAR_WIDGET_GUTTER 8
+#define TOOLBAR_DEFAULT_WIDTH 16384
+
+/* Toolbar rows used to index into the arrays of row-specific data.
+ */
+
+#define TOOLBAR_ROW_TOP 0
+#define TOOLBAR_ROW_DIV1 1
+#define TOOLBAR_ROW_EDIT 2
+#define TOOLBAR_MAX_ROWS 3
+
+/* The toolbar data structure.
+ */
+
+struct toolbar {
+ /** Bar details. */
+ struct theme_descriptor *theme;
+ theme_style style;
+ toolbar_flags flags;
+
+ int current_width, current_height;
+ int full_width, full_height;
+ int clip_width, clip_height;
+
+ /** Toolbar and parent window handles. */
+ wimp_w toolbar_handle;
+ wimp_w parent_handle;
+
+ /** Row locations and sizes. */
+ int row_y0[TOOLBAR_MAX_ROWS];
+ int row_y1[TOOLBAR_MAX_ROWS];
+
+ /** Details for the button bar. */
+ struct button_bar *buttons;
+ bool buttons_display;
+ os_coord buttons_size;
+
+ /** Details for the URL bar. */
+ struct url_bar *url;
+ bool url_display;
+ os_coord url_size;
+
+ /** Details for the throbber. */
+ struct throbber *throbber;
+ bool throbber_display;
+ bool throbber_right;
+ os_coord throbber_size;
+
+ /** Client callback data. */
+ const struct toolbar_callbacks *callbacks;
+ void *client_data;
+
+ /** Details for the toolbar editor. */
+ wimp_i editor_div1;
+ struct button_bar *editor;
+ os_coord editor_size;
+
+ bool editing;
+
+ /** Interactive help data. */
+
+ const char *help_prefix;
+
+ /** The next bar in the toolbar list. */
+ struct toolbar *next;
+};
+
+
+/* Global variables for the toolbar module.
+ */
+
+/** The list of defined toolbars. */
+static struct toolbar *ro_toolbar_bars = NULL;
+
+/** The Toolber Menu */
+wimp_menu *toolbar_menu;
+
+
+/* A basic window definition for the toolbar and status bar.
+ */
+
+static wimp_window ro_toolbar_window = {
+ {0, 0, 1, 1},
+ 0,
+ 0,
+ wimp_TOP,
+ wimp_WINDOW_NEW_FORMAT | wimp_WINDOW_MOVEABLE | wimp_WINDOW_NO_BOUNDS |
+ wimp_WINDOW_FURNITURE_WINDOW |
+ wimp_WINDOW_IGNORE_XEXTENT | wimp_WINDOW_IGNORE_YEXTENT,
+ wimp_COLOUR_BLACK,
+ wimp_COLOUR_LIGHT_GREY,
+ wimp_COLOUR_LIGHT_GREY,
+ wimp_COLOUR_VERY_LIGHT_GREY,
+ wimp_COLOUR_DARK_GREY,
+ wimp_COLOUR_MID_LIGHT_GREY,
+ wimp_COLOUR_CREAM,
+ wimp_WINDOW_NEVER3D | 0x16u /* RISC OS 5.03+ */,
+ {0, 0, TOOLBAR_DEFAULT_WIDTH, 16384},
+ 0,
+ wimp_BUTTON_DOUBLE_CLICK_DRAG << wimp_ICON_BUTTON_TYPE_SHIFT,
+ wimpspriteop_AREA,
+ 1,
+ 1,
+ {""},
+ 0,
+ { }
+};
+
+static char ro_toolbar_null_string[] = "";
+static char ro_toolbar_line_validation[] = "R2";
+
+/*
+ * Private function prototypes.
+ */
+
+static void ro_toolbar_update_current_widgets(struct toolbar *toolbar);
+static void ro_toolbar_refresh_widget_dimensions(struct toolbar *toolbar);
+static void ro_toolbar_reformat_widgets(struct toolbar *toolbar);
+
+static void ro_toolbar_redraw(wimp_draw *redraw);
+static bool ro_toolbar_click(wimp_pointer *pointer);
+static bool ro_toolbar_keypress(wimp_key *key);
+static bool ro_toolbar_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_pointer *pointer);
+static void ro_toolbar_menu_warning(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action);
+static bool ro_toolbar_menu_select(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action);
+static const char *ro_toolbar_get_help_suffix(wimp_w w, wimp_i i, os_coord *pos,
+ wimp_mouse_state buttons);
+
+static void ro_toolbar_update_buttons(struct toolbar *toolbar);
+
+
+/* This is an exported interface documented in toolbar.h */
+
+void ro_toolbar_init(void)
+{
+ /* browser toolbar menu */
+ static const struct ns_menu toolbar_definition = {
+ "Toolbar", {
+ { "Toolbars", NO_ACTION, 0 },
+ { "Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 },
+ { "Toolbars.ToolAddress", TOOLBAR_ADDRESS_BAR, 0 },
+ { "Toolbars.ToolThrob", TOOLBAR_THROBBER, 0 },
+ { "EditToolbar", TOOLBAR_EDIT, 0 },
+ {NULL, 0, 0}
+ }
+ };
+ toolbar_menu = ro_gui_menu_define_menu(
+ &toolbar_definition);
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+struct toolbar *ro_toolbar_create(struct theme_descriptor *descriptor,
+ wimp_w parent, theme_style style, toolbar_flags bar_flags,
+ const struct toolbar_callbacks *callbacks, void *client_data,
+ const char *help)
+{
+ struct toolbar *toolbar;
+
+ /* Allocate memory for the bar and link it into the list of bars. */
+
+ toolbar = calloc(sizeof(struct toolbar), 1);
+ if (toolbar == NULL) {
+ LOG("No memory for malloc()");
+ ro_warn_user("NoMemory", 0);
+ return NULL;
+ }
+
+ toolbar->next = ro_toolbar_bars;
+ ro_toolbar_bars = toolbar;
+
+ /* Store the supplied settings. */
+
+ toolbar->flags = bar_flags;
+ toolbar->theme = descriptor;
+ toolbar->style = style;
+ toolbar->parent_handle = parent;
+ toolbar->callbacks = callbacks;
+ toolbar->client_data = client_data;
+
+ /* Set up the internal widgets: initially, there are none. */
+
+ toolbar->buttons = NULL;
+ toolbar->buttons_display = false;
+
+ toolbar->url = NULL;
+ toolbar->url_display = false;
+
+ toolbar->throbber = NULL;
+ toolbar->throbber_display = false;
+
+ /* Set up the bar editor. */
+
+ toolbar->editor = NULL;
+ toolbar->editor_div1 = -1;
+
+ toolbar->editing = false;
+
+ toolbar->help_prefix = help;
+
+ return toolbar;
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+bool ro_toolbar_add_buttons(struct toolbar *toolbar,
+ const struct button_bar_buttons buttons[], char *button_order)
+{
+ if (toolbar == NULL)
+ return false;
+
+ if (toolbar->buttons != NULL)
+ return false;
+
+ toolbar->buttons = ro_gui_button_bar_create(toolbar->theme, buttons);
+ if (toolbar->buttons != NULL) {
+ toolbar->buttons_display = true;
+ ro_gui_button_bar_arrange_buttons(toolbar->buttons,
+ button_order);
+ }
+
+ toolbar->editor = ro_gui_button_bar_create(toolbar->theme, buttons);
+ if (toolbar->editor != NULL)
+ ro_gui_button_bar_hide(toolbar->editor, !toolbar->editing);
+
+ if (toolbar->buttons != NULL && toolbar->editor != NULL)
+ if (!ro_gui_button_bar_link_editor(toolbar->buttons,
+ toolbar->editor,
+ (void (*)(void *))
+ ro_toolbar_update_current_widgets,
+ toolbar))
+ return false;
+
+ return (toolbar->buttons == NULL || toolbar->editor == NULL) ?
+ false : true;
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+bool ro_toolbar_add_throbber(struct toolbar *toolbar)
+{
+ if (toolbar == NULL)
+ return false;
+
+ if (toolbar->throbber != NULL)
+ return false;
+
+ toolbar->throbber = ro_gui_throbber_create(toolbar->theme);
+
+ if (toolbar->throbber != NULL)
+ toolbar->throbber_display = true;
+
+ return (toolbar->throbber == NULL) ? false : true;
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+bool ro_toolbar_add_url(struct toolbar *toolbar)
+{
+ if (toolbar == NULL)
+ return false;
+
+ if (toolbar->url != NULL)
+ return false;
+
+ toolbar->url = ro_gui_url_bar_create(toolbar->theme);
+
+ if (toolbar->url != NULL)
+ toolbar->url_display = true;
+
+ return (toolbar->url == NULL) ? false : true;
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+bool ro_toolbar_rebuild(struct toolbar *toolbar)
+{
+ os_error *error;
+ wimp_icon_create icon;
+ wimp_w old_window = NULL;
+
+ if (toolbar == NULL)
+ return false;
+
+ /* Start to set up the toolbar window. */
+
+ ro_toolbar_window.sprite_area =
+ ro_gui_theme_get_sprites(toolbar->theme);
+ ro_toolbar_window.work_bg =
+ ro_gui_theme_get_style_element(toolbar->theme,
+ toolbar->style, THEME_ELEMENT_BACKGROUND);
+
+ /* Delete any existing toolbar window... */
+
+ if (toolbar->toolbar_handle != NULL) {
+ old_window = toolbar->toolbar_handle;
+ error = xwimp_delete_window(toolbar->toolbar_handle);
+ if (error)
+ LOG("xwimp_delete_window: 0x%x: %s", error->errnum, error->errmess);
+ toolbar->toolbar_handle = NULL;
+ }
+
+ /* ...and create a new window. */
+
+ error = xwimp_create_window(&ro_toolbar_window,
+ &toolbar->toolbar_handle);
+ if (error) {
+ LOG("xwimp_create_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ /* Set up the toolbar's event handlers. Only set the user activity-
+ * related callbacks if the bar isn't for display purposes. If the
+ * toolbar is being recreated, simply transfer the handlers across
+ * from the old, now-deleted window.
+ */
+
+ if (old_window == NULL) {
+ ro_gui_wimp_event_register_redraw_window(
+ toolbar->toolbar_handle, ro_toolbar_redraw);
+ ro_gui_wimp_event_set_user_data(toolbar->toolbar_handle,
+ toolbar);
+
+ if (!(toolbar->flags & TOOLBAR_FLAGS_DISPLAY)) {
+ ro_gui_wimp_event_register_mouse_click(
+ toolbar->toolbar_handle,
+ ro_toolbar_click);
+ ro_gui_wimp_event_register_keypress(
+ toolbar->toolbar_handle,
+ ro_toolbar_keypress);
+ ro_gui_wimp_event_register_menu_prepare(
+ toolbar->toolbar_handle,
+ ro_toolbar_menu_prepare);
+ ro_gui_wimp_event_register_menu_warning(
+ toolbar->toolbar_handle,
+ ro_toolbar_menu_warning);
+ ro_gui_wimp_event_register_menu_selection(
+ toolbar->toolbar_handle,
+ ro_toolbar_menu_select);
+ ro_gui_wimp_event_register_menu(toolbar->toolbar_handle,
+ toolbar_menu, true, false);
+ ro_gui_wimp_event_register_help_suffix(
+ toolbar->toolbar_handle,
+ ro_toolbar_get_help_suffix);
+ }
+ } else {
+ ro_gui_wimp_event_transfer(old_window, toolbar->toolbar_handle);
+ }
+
+ /* The help prefix changes from edit to non-edit more. */
+
+ ro_gui_wimp_event_set_help_prefix(toolbar->toolbar_handle,
+ (toolbar->editing) ?
+ "HelpEditToolbar" : toolbar->help_prefix);
+
+ /* Place the widgets into the new bar, using the new theme.
+ *
+ * \TODO -- If any widgets fail to rebuild, then we currently just
+ * carry on without them. Not sure if the whole bar
+ * rebuild should fail here?
+ */
+
+ if (toolbar->throbber != NULL) {
+ if (!ro_gui_throbber_rebuild(toolbar->throbber, toolbar->theme,
+ toolbar->style, toolbar->toolbar_handle,
+ toolbar->editing)) {
+ ro_gui_throbber_destroy(toolbar->throbber);
+ toolbar->throbber = NULL;
+ }
+
+ ro_gui_theme_get_throbber_data(toolbar->theme, NULL, NULL, NULL,
+ &toolbar->throbber_right, NULL);
+ }
+
+ if (toolbar->buttons != NULL) {
+ if (!ro_gui_button_bar_rebuild(toolbar->buttons, toolbar->theme,
+ toolbar->style, toolbar->toolbar_handle,
+ toolbar->editing)) {
+ ro_gui_button_bar_destroy(toolbar->buttons);
+ toolbar->buttons = NULL;
+ }
+ }
+
+ if (toolbar->editor != NULL) {
+ if (!ro_gui_button_bar_rebuild(toolbar->editor, toolbar->theme,
+ toolbar->style, toolbar->toolbar_handle,
+ toolbar->editing)) {
+ ro_gui_button_bar_destroy(toolbar->editor);
+ toolbar->editor = NULL;
+ }
+ }
+
+ if (toolbar->url != NULL) {
+ if (!ro_gui_url_bar_rebuild(toolbar->url, toolbar->theme,
+ toolbar->style, toolbar->toolbar_handle,
+ toolbar->flags & TOOLBAR_FLAGS_DISPLAY,
+ toolbar->editing)) {
+ ro_gui_url_bar_destroy(toolbar->url);
+ toolbar->url = NULL;
+ }
+ }
+
+ /* If this is an editor, add in a divider icon and the editor
+ * button bar.
+ */
+
+ if (toolbar->editing) {
+ icon.w = toolbar->toolbar_handle;
+ icon.icon.flags = wimp_ICON_TEXT | wimp_ICON_INDIRECTED |
+ wimp_ICON_VCENTRED | wimp_ICON_BORDER |
+ (wimp_COLOUR_BLACK <<
+ wimp_ICON_FG_COLOUR_SHIFT) |
+ (wimp_COLOUR_VERY_LIGHT_GREY <<
+ wimp_ICON_BG_COLOUR_SHIFT);
+ icon.icon.extent.x0 = 0;
+ icon.icon.extent.x1 = 0;
+ icon.icon.extent.y1 = 0;
+ icon.icon.extent.y0 = 0;
+ icon.icon.data.indirected_text.text = ro_toolbar_null_string;
+ icon.icon.data.indirected_text.validation =
+ ro_toolbar_line_validation;
+ icon.icon.data.indirected_text.size = 1;
+ error = xwimp_create_icon(&icon, &toolbar->editor_div1);
+ if (error) {
+ LOG("xwimp_create_icon: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ toolbar->editor_div1 = -1;
+ }
+ }
+
+ /* Establish the required dimensions to fit the widgets, then
+ * reflow the bar contents.
+ */
+
+ ro_toolbar_refresh_widget_dimensions(toolbar);
+
+ ro_toolbar_process(toolbar, -1, true);
+
+ if (toolbar->parent_handle != NULL)
+ ro_toolbar_attach(toolbar, toolbar->parent_handle);
+
+ ro_toolbar_update_buttons(toolbar);
+
+ return true;
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+bool ro_toolbar_attach(struct toolbar *toolbar, wimp_w parent)
+{
+ wimp_outline outline;
+ wimp_window_state state;
+ os_error *error;
+
+ if (toolbar == NULL || toolbar->toolbar_handle == NULL)
+ return false;
+
+ toolbar->parent_handle = parent;
+
+ /* Only try to attach the toolbar if there's any of it visible to
+ * matter.
+ */
+
+ if (toolbar->current_height > 0) {
+ outline.w = parent;
+ xwimp_get_window_outline(&outline);
+ state.w = parent;
+ xwimp_get_window_state(&state);
+ state.w = toolbar->toolbar_handle;
+ state.visible.x1 = outline.outline.x1 - 2;
+ state.visible.y0 = state.visible.y1 + 2 -
+ toolbar->current_height;
+ state.xscroll = 0;
+ state.yscroll = 0;
+ error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state), parent,
+ wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
+ << wimp_CHILD_XORIGIN_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
+ << wimp_CHILD_YORIGIN_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
+ << wimp_CHILD_LS_EDGE_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
+ << wimp_CHILD_BS_EDGE_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
+ << wimp_CHILD_RS_EDGE_SHIFT |
+ wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
+ << wimp_CHILD_TS_EDGE_SHIFT);
+ if (error) {
+ LOG("xwimp_open_window_nested: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ return true;
+ }
+
+ error = xwimp_close_window(toolbar->toolbar_handle);
+ if (error) {
+ LOG("xwimp_close_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+ return true;
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+bool ro_toolbar_process(struct toolbar *toolbar, int width, bool reformat)
+{
+ os_error *error;
+ wimp_outline outline;
+ wimp_window_state state;
+ os_box extent;
+ int old_height, old_width;
+
+ if (!toolbar)
+ return false;
+
+ old_height = toolbar->current_height;
+ old_width = toolbar->current_width;
+
+ /* Measure the parent window width if the caller has asked us to
+ * calculate the clip width ourselves. Otherwise, if a clip width
+ * has been specified, set the clip to that.
+ */
+
+ if ((toolbar->parent_handle != NULL) && (width == -1)) {
+ outline.w = toolbar->parent_handle;
+ error = xwimp_get_window_outline(&outline);
+ if (error) {
+ LOG("xwimp_get_window_outline: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ toolbar->clip_width = outline.outline.x1 -
+ outline.outline.x0 - 2;
+ toolbar->current_width = toolbar->clip_width;
+ } else if (width != -1) {
+ toolbar->clip_width = width;
+ toolbar->current_width = toolbar->clip_width;
+ }
+
+ /* Find the parent visible height to clip our toolbar height to
+ */
+
+ if (toolbar->parent_handle != NULL) {
+ state.w = toolbar->parent_handle;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ toolbar->clip_height = state.visible.y1 - state.visible.y0 + 2;
+
+ /* We can't obscure the height of the scroll bar as we
+ * lose the resize icon if we do.
+ */
+
+ if (toolbar->clip_height >= toolbar->full_height)
+ toolbar->current_height = toolbar->full_height;
+ else
+ toolbar->current_height = toolbar->clip_height;
+
+ /* Resize the work area extent and update our position. */
+
+ if (old_height != toolbar->current_height) {
+ extent.x0 = 0;
+ extent.y0 = 0;
+ extent.x1 = TOOLBAR_DEFAULT_WIDTH;
+ extent.y1 = toolbar->current_height - 2;
+ error = xwimp_set_extent(toolbar->toolbar_handle,
+ &extent);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ ro_toolbar_attach(toolbar, toolbar->parent_handle);
+ }
+ } else {
+ toolbar->clip_height = toolbar->full_height;
+ toolbar->current_height = toolbar->full_height;
+ }
+
+ /* Reflow the widgets into the toolbar if the dimensions have
+ * changed or we have been asked to anyway. */
+
+ if (toolbar->current_width != old_width || reformat)
+ ro_toolbar_reformat_widgets(toolbar);
+
+ return true;
+}
+
+
+/**
+ * Update the widgets currently on view in a toolbar. This can be used
+ * generally, but is primarily offered to widgets as a way for them
+ * to force an update.
+ *
+ * \param *toolbar The toolbar to update.
+ */
+
+void ro_toolbar_update_current_widgets(struct toolbar *toolbar)
+{
+ int old_height;
+
+ if (toolbar == NULL)
+ return;
+
+ old_height = toolbar->full_height;
+
+ ro_toolbar_refresh_widget_dimensions(toolbar);
+ ro_toolbar_reformat_widgets(toolbar);
+
+ /* If the toolbar height has changed, we need to tell the client. */
+
+ if (toolbar->full_height != old_height)
+ ro_toolbar_refresh(toolbar);
+}
+
+
+/**
+ * Get the minimum dimenstions required by the toolbar widgets after
+ * these have changed. The minimum dimensions are assumed not to change
+ * unless we change theme (ie. we rebuild the bar) or we knowingly
+ * alter a widget (eg. we add or remove button-bar buttons).
+ *
+ *
+ * \param *toolbar The toolbar to refresh.
+ */
+
+void ro_toolbar_refresh_widget_dimensions(struct toolbar *toolbar)
+{
+ int width, height;
+ int row_width, row_height;
+
+ if (toolbar == NULL)
+ return;
+
+ /* Process the toolbar editor and any associated divider rows.
+ */
+
+ if (toolbar->editor != NULL && toolbar->editing) {
+ width = 0;
+ height = 0;
+ ro_gui_button_bar_get_dims(toolbar->editor, &width, &height);
+
+ toolbar->editor_size.x = width;
+ toolbar->editor_size.y = height;
+
+ toolbar->row_y0[TOOLBAR_ROW_EDIT] = TOOLBAR_WIDGET_GUTTER;
+ toolbar->row_y1[TOOLBAR_ROW_EDIT] = TOOLBAR_WIDGET_GUTTER
+ + height;
+
+ toolbar->row_y0[TOOLBAR_ROW_DIV1] = TOOLBAR_WIDGET_GUTTER +
+ toolbar->row_y1[TOOLBAR_ROW_EDIT];
+ toolbar->row_y1[TOOLBAR_ROW_DIV1] = 8 +
+ toolbar->row_y0[TOOLBAR_ROW_DIV1];
+ } else {
+ toolbar->editor_size.x = 0;
+ toolbar->editor_size.y = 0;
+
+ toolbar->row_y0[TOOLBAR_ROW_EDIT] = 0;
+ toolbar->row_y1[TOOLBAR_ROW_EDIT] = 0;
+ toolbar->row_y0[TOOLBAR_ROW_DIV1] = 0;
+ toolbar->row_y1[TOOLBAR_ROW_DIV1] = 0;
+ }
+
+ /* Process the top row icons. */
+
+ row_width = 0;
+ row_height = 0;
+
+ /* If the editor is active, any button bar if forced into view. */
+
+ if (toolbar->buttons != NULL &&
+ (toolbar->buttons_display || toolbar->editing)) {
+ width = 0;
+ height = 0;
+ ro_gui_button_bar_get_dims(toolbar->buttons, &width, &height);
+
+ row_width += width;
+ toolbar->buttons_size.x = width;
+ toolbar->buttons_size.y = height;
+
+ if (height > row_height)
+ row_height = height;
+ } else {
+ toolbar->buttons_size.x = 0;
+ toolbar->buttons_size.y = 0;
+ }
+
+ if (toolbar->url != NULL && toolbar->url_display) {
+ width = 0;
+ height = 0;
+ ro_gui_url_bar_get_dims(toolbar->url, &width, &height);
+
+ if (row_width > 0)
+ row_width += TOOLBAR_WIDGET_GUTTER;
+ row_width += width;
+
+ toolbar->url_size.x = width;
+ toolbar->url_size.y = height;
+
+ if (height > row_height)
+ row_height = height;
+ } else {
+ toolbar->url_size.x = 0;
+ toolbar->url_size.y = 0;
+ }
+
+ if (toolbar->throbber != NULL && toolbar->throbber_display) {
+ width = 0;
+ height = 0;
+ ro_gui_throbber_get_dims(toolbar->throbber, &width, &height);
+
+ if (row_width > 0)
+ row_width += TOOLBAR_WIDGET_GUTTER;
+ row_width += width;
+
+ toolbar->throbber_size.x = width;
+ toolbar->throbber_size.y = height;
+
+ if (height > row_height)
+ row_height = height;
+ } else {
+ toolbar->throbber_size.x = 0;
+ toolbar->throbber_size.y = 0;
+ }
+
+ if (row_height > 0) {
+ toolbar->row_y0[TOOLBAR_ROW_TOP] = TOOLBAR_WIDGET_GUTTER +
+ toolbar->row_y1[TOOLBAR_ROW_DIV1];
+ toolbar->row_y1[TOOLBAR_ROW_TOP] = row_height +
+ toolbar->row_y0[TOOLBAR_ROW_TOP];
+ } else {
+ toolbar->row_y0[TOOLBAR_ROW_TOP] = 0;
+ toolbar->row_y1[TOOLBAR_ROW_TOP] = 0;
+ }
+
+ /* Establish the full dimensions of the bar.
+ *
+ * \TODO -- This currently assumes an "all or nothing" approach to
+ * the editor bar, and will need reworking once we have to
+ * worry about tab bars.
+ */
+
+ if (toolbar->row_y1[TOOLBAR_ROW_TOP] > 0) {
+ toolbar->full_height = toolbar->row_y1[TOOLBAR_ROW_TOP] +
+ TOOLBAR_WIDGET_GUTTER;
+ } else {
+ toolbar->full_height = 0;
+ }
+ toolbar->full_width = 2 * TOOLBAR_WIDGET_GUTTER +
+ ((row_width > toolbar->editor_size.x) ?
+ row_width : toolbar->editor_size.x);
+}
+
+
+/**
+ * Reformat (reflow) the widgets into the toolbar, based on the toolbar size
+ * and the previously calculated widget dimensions.
+ *
+ * \param *toolbar The toolbar to reformat.
+ */
+
+void ro_toolbar_reformat_widgets(struct toolbar *toolbar)
+{
+ int left_margin, right_margin;
+
+ left_margin = TOOLBAR_WIDGET_GUTTER;
+ right_margin = toolbar->clip_width - TOOLBAR_WIDGET_GUTTER;
+
+ /* Flow the toolbar editor row, which will be a fixed with and
+ * may alter the right margin.
+ */
+
+ if (toolbar->editor != NULL && toolbar->editing) {
+ if (right_margin < left_margin + toolbar->editor_size.x)
+ right_margin = left_margin + toolbar->editor_size.x;
+
+ ro_gui_button_bar_set_extent(toolbar->editor,
+ left_margin,
+ toolbar->row_y0[TOOLBAR_ROW_EDIT],
+ left_margin + toolbar->editor_size.x,
+ toolbar->row_y1[TOOLBAR_ROW_EDIT]);
+
+ if (toolbar->editor_div1 != -1)
+ xwimp_resize_icon(toolbar->toolbar_handle,
+ toolbar->editor_div1, -8,
+ toolbar->row_y0[TOOLBAR_ROW_DIV1],
+ toolbar->clip_width + 8,
+ toolbar->row_y1[TOOLBAR_ROW_DIV1]);
+ }
+
+ /* Flow the top row. */
+
+ if (toolbar->throbber != NULL && toolbar->throbber_display) {
+ if (toolbar->throbber_right) {
+ right_margin -= (toolbar->throbber_size.x +
+ TOOLBAR_WIDGET_GUTTER);
+ } else {
+ ro_gui_throbber_set_extent(toolbar->throbber,
+ left_margin,
+ toolbar->row_y0[TOOLBAR_ROW_TOP],
+ left_margin + toolbar->throbber_size.x,
+ toolbar->row_y1[TOOLBAR_ROW_TOP]);
+ left_margin += (toolbar->throbber_size.x +
+ TOOLBAR_WIDGET_GUTTER);
+ }
+ }
+
+ if (toolbar->buttons != NULL &&
+ (toolbar->buttons_display || toolbar->editing)) {
+ if (right_margin < left_margin + toolbar->buttons_size.x)
+ right_margin = left_margin + toolbar->buttons_size.x;
+
+ ro_gui_button_bar_set_extent(toolbar->buttons,
+ left_margin,
+ toolbar->row_y0[TOOLBAR_ROW_TOP],
+ left_margin + toolbar->buttons_size.x,
+ toolbar->row_y1[TOOLBAR_ROW_TOP]);
+ left_margin += (toolbar->buttons_size.x +
+ TOOLBAR_WIDGET_GUTTER);
+ }
+
+ if (toolbar->url != NULL && toolbar->url_display) {
+ if (right_margin < left_margin + toolbar->url_size.x)
+ right_margin = left_margin + toolbar->url_size.x;
+
+ ro_gui_url_bar_set_extent(toolbar->url,
+ left_margin,
+ toolbar->row_y0[TOOLBAR_ROW_TOP],
+ right_margin,
+ toolbar->row_y1[TOOLBAR_ROW_TOP]);
+
+ left_margin = right_margin + TOOLBAR_WIDGET_GUTTER;
+ }
+
+ if (toolbar->throbber != NULL && toolbar->throbber_display &&
+ toolbar->throbber_right) {
+ left_margin = right_margin + TOOLBAR_WIDGET_GUTTER;
+ ro_gui_throbber_set_extent(toolbar->throbber,
+ left_margin,
+ toolbar->row_y0[TOOLBAR_ROW_TOP],
+ left_margin + toolbar->throbber_size.x,
+ toolbar->row_y1[TOOLBAR_ROW_TOP]);
+ }
+
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+void ro_toolbar_destroy(struct toolbar *toolbar)
+{
+ struct toolbar *bar;
+
+ if (toolbar == NULL)
+ return;
+
+ LOG("Destroying toolbar 0x%x", (unsigned int)toolbar);
+
+ /* Destroy the widgets. */
+
+ if (toolbar->buttons != NULL)
+ ro_gui_button_bar_destroy(toolbar->buttons);
+
+ if (toolbar->editor != NULL)
+ ro_gui_button_bar_destroy(toolbar->editor);
+
+ if (toolbar->url != NULL)
+ ro_gui_url_bar_destroy(toolbar->url);
+
+ if (toolbar->throbber != NULL)
+ ro_gui_throbber_destroy(toolbar->throbber);
+
+ /* Delete the toolbar window. */
+
+ if (toolbar->toolbar_handle != NULL) {
+ xwimp_delete_window(toolbar->toolbar_handle);
+ ro_gui_wimp_event_finalise(toolbar->toolbar_handle);
+ }
+
+ /* Remove the bar from the list and free the memory.
+ */
+
+ if (ro_toolbar_bars == toolbar) {
+ ro_toolbar_bars = toolbar->next;
+ } else {
+ for (bar = ro_toolbar_bars; bar != NULL && bar->next != toolbar;
+ bar = bar->next);
+
+ if (bar->next == toolbar)
+ bar->next = toolbar->next;
+ }
+
+ free(toolbar);
+
+}
+
+
+/**
+ * Handle redraw request events for a toolbar workarea.
+ *
+ * \param *redraw The redraw block for the event.
+ */
+
+void ro_toolbar_redraw(wimp_draw *redraw)
+{
+ struct toolbar *toolbar;
+ osbool more;
+ os_error *error;
+
+ toolbar = (struct toolbar *)ro_gui_wimp_event_get_user_data(redraw->w);
+
+ assert(toolbar != NULL);
+
+ error = xwimp_redraw_window(redraw, &more);
+ if (error) {
+ LOG("xwimp_redraw_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ while (more) {
+ ro_plot_origin_x = redraw->box.x0 - redraw->xscroll;
+ ro_plot_origin_y = redraw->box.y1 - redraw->yscroll;
+
+ if (toolbar->buttons != NULL && toolbar->buttons_display)
+ ro_gui_button_bar_redraw(toolbar->buttons, redraw);
+
+ if (toolbar->editor != NULL && toolbar->editing)
+ ro_gui_button_bar_redraw(toolbar->editor, redraw);
+
+ if (toolbar->url != NULL && toolbar->url_display)
+ ro_gui_url_bar_redraw(toolbar->url, redraw);
+
+ error = xwimp_get_rectangle(redraw, &more);
+ if (error) {
+ LOG("xwimp_get_rectangle: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ }
+}
+
+
+/**
+ * Process clicks on a toolbar, passing details on to clients where necessary.
+ *
+ * \param *pointer The wimp mouse click event.
+ * \return True if the event was handled; else false.
+ */
+
+bool ro_toolbar_click(wimp_pointer *pointer)
+{
+ struct toolbar *toolbar;
+ union toolbar_action action;
+ wimp_window_state state;
+ os_error *error;
+
+ toolbar = (struct toolbar *)
+ ro_gui_wimp_event_get_user_data(pointer->w);
+
+ if (toolbar == NULL)
+ return false;
+
+ assert(pointer->w == toolbar->toolbar_handle);
+
+ state.w = toolbar->toolbar_handle;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ /* If the click wasn't in the URL Bar's text field, then it will
+ * need to close any URL Complete window that is open.
+ *
+ * \TODO -- This should really move into the URL Bar module, as
+ * URL Complete is really an extension to that.
+ */
+
+ if (toolbar->url != NULL && toolbar->url_display &&
+ !ro_gui_url_bar_test_for_text_field_click(toolbar->url,
+ pointer))
+ ro_gui_url_complete_close();
+
+ /* Pass the click around the toolbar widgets. */
+
+ if (toolbar->buttons != NULL &&
+ (toolbar->buttons_display || toolbar->editing) &&
+ ro_gui_button_bar_click(toolbar->buttons, pointer,
+ &state, &action.button)) {
+ if (action.button != TOOLBAR_BUTTON_NONE &&
+ !toolbar->editing &&
+ toolbar->callbacks != NULL &&
+ toolbar->callbacks->user_action != NULL)
+ toolbar->callbacks->user_action(toolbar->client_data,
+ TOOLBAR_ACTION_BUTTON, action);
+ return true;
+ }
+
+ if (toolbar->url != NULL && toolbar->url_display &&
+ ro_gui_url_bar_click(toolbar->url, pointer,
+ &state, &action.url)) {
+ if (action.url != TOOLBAR_URL_NONE &&
+ !toolbar->editing &&
+ toolbar->callbacks != NULL &&
+ toolbar->callbacks->user_action != NULL)
+ toolbar->callbacks->user_action(toolbar->client_data,
+ TOOLBAR_ACTION_URL, action);
+ return true;
+ }
+
+ if (toolbar->editor != NULL && toolbar->editing &&
+ ro_gui_button_bar_click(toolbar->editor, pointer,
+ &state, &action.button)) {
+ return true;
+ }
+
+ /* Nothing else has handled this, so try passing it to the
+ * URL Complete module.
+ *
+ * \TODO -- This should really move into the URL Bar module, as
+ * URL Complete is really an extension to that.
+ */
+
+ if (toolbar->url != NULL && toolbar->url_display &&
+ ro_gui_url_bar_test_for_text_field_click(toolbar->url,
+ pointer)) {
+ ro_gui_url_complete_start(toolbar);
+ return true;
+ }
+
+ return false;
+}
+
+
+/**
+ * Process keypresses in a toolbar, passing details on to clients where
+ * necessary.
+ *
+ * \param *key The wimp key press event.
+ * \return True if the event was handled; else false.
+ */
+
+bool ro_toolbar_keypress(wimp_key *key)
+{
+ struct toolbar *toolbar;
+
+ toolbar = (struct toolbar *) ro_gui_wimp_event_get_user_data(key->w);
+
+ if (toolbar == NULL)
+ return false;
+
+ /* Pass the keypress on to the client and stop if they handle it. */
+
+ if (toolbar->callbacks->key_press != NULL &&
+ toolbar->callbacks->key_press(toolbar->client_data, key))
+ return true;
+
+ /* If the caret is in the URL bar, ask the URL Complete module if it
+ * wants to handle the keypress.
+ *
+ * \TODO -- This should really move into the URL Bar module, as
+ * URL Complete is really an extension to that.
+ */
+
+ if (toolbar->url != NULL && toolbar->url_display &&
+ ro_gui_url_bar_test_for_text_field_keypress(
+ toolbar->url, key) &&
+ ro_gui_url_complete_keypress(toolbar, key->c))
+ return true;
+
+ return false;
+}
+
+
+/**
+ * Prepare the toolbar menu for (re-)opening
+ *
+ * \param w The window owning the menu.
+ * \param i The icon owning the menu.
+ * \param *menu The menu about to be opened.
+ * \param *pointer Pointer to the relevant wimp event block, or
+ * NULL for an Adjust click.
+ * \return true if the event was handled; else false.
+ */
+
+bool ro_toolbar_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_pointer *pointer)
+{
+ struct toolbar *toolbar;
+
+ toolbar = (struct toolbar *) ro_gui_wimp_event_get_user_data(w);
+
+ if (toolbar == NULL)
+ return false;
+
+ /* Pass the event on to potentially interested widgets. */
+
+ if (toolbar->url != NULL && ro_gui_url_bar_menu_prepare(toolbar->url,
+ i, menu, pointer))
+ return true;
+
+ /* Try to process the event as a toolbar menu. */
+
+ if (menu != toolbar_menu)
+ return false;
+
+ /* Shade menu entries according to the state of the window and object
+ * under the pointer.
+ */
+
+ /* Toolbar (Sub)Menu */
+
+ ro_gui_menu_set_entry_shaded(menu, TOOLBAR_EDIT,
+ ro_toolbar_menu_edit_shade(toolbar));
+ ro_gui_menu_set_entry_ticked(menu, TOOLBAR_EDIT,
+ ro_toolbar_menu_edit_tick(toolbar));
+
+ ro_gui_menu_set_entry_shaded(menu, TOOLBAR_BUTTONS,
+ ro_toolbar_menu_option_shade(toolbar) ||
+ toolbar->buttons == NULL);
+ ro_gui_menu_set_entry_ticked(menu, TOOLBAR_BUTTONS,
+ ro_toolbar_menu_buttons_tick(toolbar) &&
+ toolbar->buttons != NULL);
+
+ ro_gui_menu_set_entry_shaded(menu, TOOLBAR_ADDRESS_BAR,
+ ro_toolbar_menu_edit_shade(toolbar) ||
+ toolbar->url == NULL);
+ ro_gui_menu_set_entry_ticked(menu, TOOLBAR_ADDRESS_BAR,
+ ro_toolbar_menu_url_tick(toolbar) &&
+ toolbar->url != NULL);
+
+ ro_gui_menu_set_entry_shaded(menu, TOOLBAR_THROBBER,
+ ro_toolbar_menu_edit_shade(toolbar) ||
+ toolbar->throbber == NULL);
+ ro_gui_menu_set_entry_ticked(menu, TOOLBAR_THROBBER,
+ ro_toolbar_menu_throbber_tick(toolbar) &&
+ toolbar->throbber != NULL);
+
+ return true;
+}
+
+
+/**
+ * Handle submenu warnings for the toolbar menu
+ *
+ * \param w The window owning the menu.
+ * \param i The icon owning the menu.
+ * \param *menu The menu to which the warning applies.
+ * \param *selection The wimp menu selection data.
+ * \param action The selected menu action.
+ */
+
+void ro_toolbar_menu_warning(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action)
+{
+ /* Do nothing */
+}
+
+
+/**
+ * Handle selections from the toolbar menu
+ *
+ * \param w The window owning the menu.
+ * \param i The icon owning the menu.
+ * \param *menu The menu from which the selection was made.
+ * \param *selection The wimp menu selection data.
+ * \param action The selected menu action.
+ * \return true if action accepted; else false.
+ */
+
+bool ro_toolbar_menu_select(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action)
+{
+ struct toolbar *toolbar;
+
+ toolbar = (struct toolbar *) ro_gui_wimp_event_get_user_data(w);
+
+ if (toolbar == NULL)
+ return false;
+
+ /* Pass the event on to potentially interested widgets. */
+
+ if (toolbar->url != NULL && ro_gui_url_bar_menu_select(toolbar->url,
+ i, menu, selection, action))
+ return true;
+
+ /* Try to process the event as a toolbar menu. */
+
+ if (menu != toolbar_menu)
+ return false;
+
+ switch (action) {
+ case TOOLBAR_BUTTONS:
+ ro_toolbar_set_display_buttons(toolbar,
+ !ro_toolbar_get_display_buttons(toolbar));
+ break;
+ case TOOLBAR_ADDRESS_BAR:
+ ro_toolbar_set_display_url(toolbar,
+ !ro_toolbar_get_display_url(toolbar));
+ if (ro_toolbar_get_display_url(toolbar))
+ ro_toolbar_take_caret(toolbar);
+ break;
+ case TOOLBAR_THROBBER:
+ ro_toolbar_set_display_throbber(toolbar,
+ !ro_toolbar_get_display_throbber(toolbar));
+ break;
+ case TOOLBAR_EDIT:
+ ro_toolbar_toggle_edit(toolbar);
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+
+/**
+ * Translate the contents of a message_HELP_REQUEST into a suffix for a
+ * NetSurf message token. The help system will then add this to whatever
+ * prefix the current toolbar has registered with WimpEvent.
+ *
+ * \param w The window handle under the mouse.
+ * \param i The icon handle under the mouse.
+ * \param *pos The mouse position.
+ * \param buttons The mouse button state.
+ * \return The required help token suffix.
+ */
+
+const char *ro_toolbar_get_help_suffix(wimp_w w, wimp_i i, os_coord *pos,
+ wimp_mouse_state buttons)
+{
+ struct toolbar *toolbar;
+ wimp_window_state state;
+ os_error *error;
+ const char *suffix;
+
+ toolbar = (struct toolbar *) ro_gui_wimp_event_get_user_data(w);
+
+ if (toolbar == NULL || toolbar->toolbar_handle != w)
+ return NULL;
+
+ state.w = toolbar->toolbar_handle;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return NULL;
+ }
+
+ /* Pass the help request around the toolbar widgets. */
+
+ if (toolbar->throbber != NULL && toolbar->throbber_display &&
+ ro_gui_throbber_help_suffix(toolbar->throbber, i,
+ pos, &state, buttons, &suffix))
+ return suffix;
+
+ if (toolbar->url != NULL && toolbar->url_display &&
+ ro_gui_url_bar_help_suffix(toolbar->url, i,
+ pos, &state, buttons, &suffix))
+ return suffix;
+
+ if (toolbar->buttons != NULL && toolbar->buttons_display &&
+ ro_gui_button_bar_help_suffix(toolbar->buttons, i,
+ pos, &state, buttons, &suffix))
+ return suffix;
+
+ return "";
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+void ro_toolbar_update_client_data(struct toolbar *toolbar, void *client_data)
+{
+ if (toolbar != NULL)
+ toolbar->client_data = client_data;
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+void ro_toolbar_update_all_buttons(void)
+{
+ struct toolbar *bar;
+
+ bar = ro_toolbar_bars;
+ while (bar != NULL) {
+ ro_toolbar_update_buttons(bar);
+
+ bar = bar->next;
+ }
+}
+
+
+/**
+ * Update the state of a toolbar's buttons.
+ *
+ * \param toolbar the toolbar to update
+ */
+
+void ro_toolbar_update_buttons(struct toolbar *toolbar)
+{
+ assert(toolbar != NULL);
+
+ if (toolbar->callbacks != NULL &&
+ toolbar->callbacks->update_buttons != NULL)
+ toolbar->callbacks->update_buttons(toolbar->client_data);
+}
+
+/* This is an exported interface documented in toolbar.h */
+
+void ro_toolbar_refresh(struct toolbar *toolbar)
+{
+ assert(toolbar != NULL);
+
+ ro_toolbar_process(toolbar, -1, true);
+ if (toolbar->callbacks != NULL &&
+ toolbar->callbacks->change_size != NULL)
+ toolbar->callbacks->change_size(toolbar->client_data);
+
+ if (toolbar->toolbar_handle != NULL)
+ xwimp_force_redraw(toolbar->toolbar_handle, 0, 0,
+ toolbar->current_width,
+ toolbar->current_height);
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+void ro_toolbar_theme_update(void)
+{
+ struct toolbar *bar, *next;
+ bool ok;
+
+ bar = ro_toolbar_bars;
+ while (bar != NULL) {
+ /* Take the next bar address now, as *bar may become invalid
+ * during the update process (if an update fails and
+ * ro_toolbar_destroy() is called) and we don't want to lose
+ * the link to the rest of the chain.
+ */
+
+ next = bar->next;
+
+ /* Only process the bar if the theme is set to the default.
+ * Otherwise, it's up to the owner to do whatever they need
+ * to do for themselves.
+ */
+
+ if (bar->theme == NULL) {
+ ok = ro_toolbar_rebuild(bar);
+
+ if (!ok)
+ ro_toolbar_destroy(bar);
+ } else {
+ ok = true;
+ }
+
+ if (bar->callbacks != NULL &&
+ bar->callbacks->theme_update != NULL)
+ bar->callbacks->theme_update(bar->client_data, ok);
+
+ bar = next;
+ }
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+struct toolbar *ro_toolbar_parent_window_lookup(wimp_w w)
+{
+ struct toolbar *toolbar;
+
+ toolbar = ro_toolbar_bars;
+ while (toolbar != NULL && toolbar->parent_handle != w)
+ toolbar = toolbar->next;
+
+ return toolbar;
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+struct toolbar *ro_toolbar_window_lookup(wimp_w w)
+{
+ struct toolbar *toolbar;
+
+ toolbar = ro_toolbar_bars;
+ while (toolbar != NULL && toolbar->toolbar_handle != w)
+ toolbar = toolbar->next;
+
+ return toolbar;
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+wimp_w ro_toolbar_get_parent_window(struct toolbar *toolbar)
+{
+ return (toolbar != NULL) ? toolbar->parent_handle : 0;
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+wimp_w ro_toolbar_get_window(struct toolbar *toolbar)
+{
+ return (toolbar != NULL) ? toolbar->toolbar_handle : 0;
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+int ro_toolbar_height(struct toolbar *toolbar)
+{
+ return (toolbar == NULL) ? 0 : toolbar->current_height;
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+int ro_toolbar_full_height(struct toolbar *toolbar)
+{
+ return (toolbar == NULL) ? 0 : toolbar->full_height;
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+void ro_toolbar_start_throbbing(struct toolbar *toolbar)
+{
+ if (toolbar != NULL && toolbar->throbber != NULL)
+ ro_gui_throbber_animate(toolbar->throbber);
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+void ro_toolbar_stop_throbbing(struct toolbar *toolbar)
+{
+ if (toolbar != NULL && toolbar->throbber != NULL)
+ ro_gui_throbber_stop(toolbar->throbber);
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+void ro_toolbar_throb(struct toolbar *toolbar)
+{
+ if (toolbar != NULL && toolbar->throbber != NULL)
+ ro_gui_throbber_animate(toolbar->throbber);
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+bool ro_toolbar_set_button_order(struct toolbar *toolbar, char order[])
+{
+ if (toolbar == NULL || toolbar->buttons == NULL)
+ return false;
+
+ if (!ro_gui_button_bar_arrange_buttons(toolbar->buttons, order))
+ return false;
+
+ ro_toolbar_refresh_widget_dimensions(toolbar);
+
+ return ro_toolbar_process(toolbar, -1, true);
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+void ro_toolbar_set_button_shaded_state(struct toolbar *toolbar,
+ button_bar_action action, bool shaded)
+{
+ if (toolbar == NULL || toolbar->buttons == NULL)
+ return;
+
+ ro_gui_button_bar_shade_button(toolbar->buttons, action, shaded);
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+bool ro_toolbar_take_caret(struct toolbar *toolbar)
+{
+ if (toolbar == NULL || toolbar->url == NULL || !toolbar->url_display)
+ return false;
+
+ return ro_gui_url_bar_take_caret(toolbar->url);
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+void ro_toolbar_set_url(struct toolbar *toolbar, const char *url,
+ bool is_utf8, bool set_caret)
+{
+ if (toolbar != NULL && toolbar->url != NULL)
+ ro_gui_url_bar_set_url(toolbar->url, url, is_utf8, set_caret);
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+const char *ro_toolbar_get_url(struct toolbar *toolbar)
+{
+ if (toolbar == NULL || toolbar->url == NULL)
+ return NULL;
+
+ return ro_gui_url_bar_get_url(toolbar->url);
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+void ro_toolbar_update_all_hotlists(void)
+{
+ struct toolbar *bar;
+
+ bar = ro_toolbar_bars;
+ while (bar != NULL) {
+ ro_toolbar_update_hotlist(bar);
+
+ bar = bar->next;
+ }
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+void ro_toolbar_update_hotlist(struct toolbar *toolbar)
+{
+ if (toolbar == NULL || toolbar->url == NULL)
+ return;
+
+ ro_gui_url_bar_update_hotlist(toolbar->url);
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+bool ro_toolbar_get_url_field_extent(struct toolbar *toolbar, os_box *extent)
+{
+ if (toolbar == NULL || toolbar->url == NULL)
+ return false;
+
+ if (extent == NULL)
+ return true;
+
+ return ro_gui_url_bar_get_url_extent(toolbar->url, extent);
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+void ro_toolbar_set_site_favicon(struct toolbar *toolbar,
+ struct hlcache_handle *h)
+{
+ if (toolbar == NULL || toolbar->url == NULL)
+ return;
+
+ ro_gui_url_bar_set_site_favicon(toolbar->url, h);
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+void ro_toolbar_set_content_favicon(struct toolbar *toolbar,
+ struct gui_window *g)
+{
+ if (toolbar == NULL || toolbar->url == NULL)
+ return;
+
+ ro_gui_url_bar_set_content_favicon(toolbar->url, g);
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+void ro_toolbar_update_urlsuggest(struct toolbar *toolbar)
+{
+ if (toolbar == NULL || toolbar->url == NULL)
+ return;
+
+ ro_gui_url_bar_update_urlsuggest(toolbar->url);
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+void ro_toolbar_set_display_buttons(struct toolbar *toolbar, bool display)
+{
+ if (toolbar == NULL || toolbar->buttons == NULL)
+ return;
+
+ toolbar->buttons_display = display;
+ ro_gui_button_bar_hide(toolbar->buttons, !display);
+ ro_toolbar_refresh_widget_dimensions(toolbar);
+ ro_toolbar_refresh(toolbar);
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+void ro_toolbar_set_display_url(struct toolbar *toolbar, bool display)
+{
+ if (toolbar == NULL || toolbar->url == NULL)
+ return;
+
+ toolbar->url_display = display;
+ ro_gui_url_bar_hide(toolbar->url, !display);
+ ro_toolbar_refresh_widget_dimensions(toolbar);
+ ro_toolbar_refresh(toolbar);
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+void ro_toolbar_set_display_throbber(struct toolbar *toolbar, bool display)
+{
+ if (toolbar == NULL || toolbar->throbber == NULL)
+ return;
+
+ toolbar->throbber_display = display;
+ ro_gui_throbber_hide(toolbar->throbber, !display);
+ ro_toolbar_refresh_widget_dimensions(toolbar);
+ ro_toolbar_refresh(toolbar);
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+bool ro_toolbar_get_display_buttons(struct toolbar *toolbar)
+{
+ return (toolbar == NULL || toolbar->buttons == NULL) ?
+ false : toolbar->buttons_display;
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+bool ro_toolbar_get_display_url(struct toolbar *toolbar)
+{
+ return (toolbar == NULL || toolbar->url == NULL) ?
+ false : toolbar->url_display;
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+bool ro_toolbar_get_display_throbber(struct toolbar *toolbar)
+{
+ return (toolbar == NULL || toolbar->throbber == NULL) ?
+ false : toolbar->throbber_display;
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+bool ro_toolbar_get_editing(struct toolbar *toolbar)
+{
+ return (toolbar == NULL || !toolbar->editing) ? false : true;
+}
+
+
+/* This is an exported interface documented in toolbar.h */
+
+bool ro_toolbar_toggle_edit(struct toolbar *toolbar)
+{
+ if (toolbar == NULL || toolbar->editor == NULL)
+ return false;
+
+ toolbar->editing = !toolbar->editing;
+
+ ro_gui_button_bar_hide(toolbar->editor, !toolbar->editing);
+ ro_gui_button_bar_hide(toolbar->buttons,
+ !toolbar->buttons_display && !toolbar->editing);
+
+ if (!ro_toolbar_rebuild(toolbar)) {
+ ro_toolbar_destroy(toolbar);
+ return false;
+ }
+
+ ro_toolbar_refresh(toolbar);
+
+ /* If there's a callback registered and an edit has finished,
+ * tell out client what the new button state is.
+ */
+
+ if (!toolbar->editing && toolbar->buttons != NULL &&
+ toolbar->callbacks != NULL &&
+ toolbar->callbacks->save_buttons != NULL) {
+ char *new_buttons;
+ new_buttons = ro_gui_button_bar_get_config(toolbar->buttons);
+ toolbar->callbacks->save_buttons(toolbar->client_data,
+ new_buttons);
+ }
+
+ return true;
+}
+
diff --git a/frontends/riscos/toolbar.h b/frontends/riscos/toolbar.h
new file mode 100644
index 000000000..41f1af728
--- /dev/null
+++ b/frontends/riscos/toolbar.h
@@ -0,0 +1,542 @@
+/*
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2010, 2011 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Window toolbars (interface).
+ */
+
+#include <stdbool.h>
+#include "riscos/theme.h"
+#include "riscos/gui/button_bar.h"
+#include "riscos/gui/throbber.h"
+#include "riscos/gui/url_bar.h"
+
+#ifndef _NETSURF_RISCOS_TOOLBAR_H_
+#define _NETSURF_RISCOS_TOOLBAR_H_
+
+typedef enum {
+ TOOLBAR_FLAGS_NONE = 0x00,
+ TOOLBAR_FLAGS_DISPLAY = 0x01,
+ TOOLBAR_FLAGS_EDIT = 0x02,
+} toolbar_flags;
+
+/**
+ * Widget action types that the toolbar can pass on to clients.
+ */
+
+typedef enum {
+ TOOLBAR_ACTION_NONE = 0,
+ TOOLBAR_ACTION_BUTTON,
+ TOOLBAR_ACTION_URL
+} toolbar_action_type;
+
+/**
+ * Union to hold the different widget action data that can be passed
+ * from widget via toolbar to client.
+ */
+
+union toolbar_action {
+ button_bar_action button;
+ url_bar_action url;
+};
+
+struct toolbar;
+
+struct toolbar_callbacks {
+ /** Call on theme update */
+ void (*theme_update)(void *, bool);
+
+ /** Call on bar size change */
+ void (*change_size)(void *);
+
+ /** Call to update button states */
+ void (*update_buttons)(void *);
+
+ /** Call to handle user actions */
+ void (*user_action)(void *, toolbar_action_type, union toolbar_action);
+
+ /** Call to handle keypresses. */
+ bool (*key_press)(void *, wimp_key *);
+
+ /** Call on change to button order. */
+ void (*save_buttons)(void *, char *);
+};
+
+
+#define ro_toolbar_menu_option_shade(toolbar) \
+ (((toolbar) == NULL) || ro_toolbar_get_editing(toolbar))
+
+#define ro_toolbar_menu_buttons_tick(toolbar) \
+ (ro_toolbar_get_display_buttons(toolbar) || \
+ ro_toolbar_get_editing(toolbar))
+
+#define ro_toolbar_menu_url_tick(toolbar) \
+ (ro_toolbar_get_display_url(toolbar))
+
+#define ro_toolbar_menu_throbber_tick(toolbar) \
+ (ro_toolbar_get_display_throbber(toolbar))
+
+#define ro_toolbar_menu_edit_shade(toolbar) ((toolbar) == NULL)
+
+#define ro_toolbar_menu_edit_tick(toolbar) (ro_toolbar_get_editing(toolbar))
+
+
+/* The new toolbar API */
+
+
+/**
+ * Initialise the RISC OS toolbar widget.
+ */
+
+void ro_toolbar_init(void);
+
+
+/**
+ * Create a new toolbar, ready to have widgets added and to be attached to
+ * a window. If a parent window is supplied, then the toolbar module will
+ * handle the window attachments; if NULL, it is up to the client to sort this
+ * out for itself.
+ *
+ * \param *descriptor The theme to apply, or NULL for the default.
+ * \param parent The window to attach the toolbar to, or NULL.
+ * \param style The theme style to apply.
+ * \param bar_flags Toolbar flags for the new bar.
+ * \param *callbacks A client callback block, or NULL for none.
+ * \param *client_data A data pointer to pass to callbacks, or NULL.
+ * \param *help The Help token prefix for interactive help.
+ * \return The handle of the new bar, or NULL on failure.
+ */
+
+struct toolbar *ro_toolbar_create(struct theme_descriptor *descriptor,
+ wimp_w parent, theme_style style, toolbar_flags bar_flags,
+ const struct toolbar_callbacks *callbacks, void *client_data,
+ const char *help);
+
+
+/**
+ * Add a button bar to a toolbar, and configure the buttons.
+ *
+ * \param *toolbar The toolbar to take the button bar.
+ * \param buttons[] The button definitions.
+ * \param *button_order The initial button order to use.
+ * \return true if the action completed; else false.
+ */
+
+bool ro_toolbar_add_buttons(struct toolbar *toolbar,
+ const struct button_bar_buttons buttons[], char *button_order);
+
+
+/**
+ * Add a throbber to a toolbar.
+ *
+ * \param *toolbar The toolbar to take the throbber.
+ * \return true if the action completed; else false.
+ */
+
+bool ro_toolbar_add_throbber(struct toolbar *toolbar);
+
+
+/**
+ * Add a URL bar to a toolbar.
+ *
+ * \param *toolbar The toolbar to take the URL bar.
+ * \return true if the action completed; else false.
+ */
+
+bool ro_toolbar_add_url(struct toolbar *toolbar);
+
+
+/**
+ * (Re-)build a toolbar to use the specified (or current) theme. If false
+ * is returned, the toolbar may not be complete and should be deleted.
+ *
+ * \param *toolbar The toolbar to rebuild.
+ * \return true if the action was successful; else false.
+ */
+
+bool ro_toolbar_rebuild(struct toolbar *toolbar);
+
+
+/**
+ * Attach or re-attach a toolbar to its parent window.
+ *
+ * \param *toolbar The toolbar to attach.
+ * \param parent The window to attach the toolbar to.
+ * \return true if the operation succeeded; else false.
+ */
+
+bool ro_toolbar_attach(struct toolbar *toolbar, wimp_w parent);
+
+
+/**
+ * Process a toolbar, updating its contents for a size or content change.
+ *
+ * \param *toolbar The toolbar to update.
+ * \param width The width to reformat to, or -1 to use parent.
+ * \param reformat true to force a widget reflow; else false.
+ * \return true if the operation succeeded; else false.
+ */
+
+bool ro_toolbar_process(struct toolbar *toolbar, int width, bool reformat);
+
+
+/**
+ * Destroy a toolbar after use.
+ *
+ * \param *toolbar The toolbar to destroy.
+ */
+
+void ro_toolbar_destroy(struct toolbar *toolbar);
+
+
+/**
+ * Change the client data associated with a toolbar's callbacks.
+ *
+ * \param *toolbar the toolbar whose data is to be updated.
+ * \param *client_data the new client data, or NULL for none.
+ */
+
+void ro_toolbar_update_client_data(struct toolbar *toolbar, void *client_data);
+
+
+/**
+ * Force the update of all toolbars buttons to reflect the current state.
+ */
+
+void ro_toolbar_update_all_buttons(void);
+
+
+/**
+ * Refresh a toolbar after it has been updated
+ *
+ * \param toolbar the toolbar to update
+ */
+
+void ro_toolbar_refresh(struct toolbar *toolbar);
+
+
+/**
+ * Force the update of all toolbars to reflect the application of a new theme.
+ */
+
+void ro_toolbar_theme_update(void);
+
+
+/**
+ * Find the toolbar associated with a given RO window handle.
+ *
+ * \param w the window handle to look up.
+ * \return the toolbar handle, or NULL if a match wasn't found.
+ */
+
+struct toolbar *ro_toolbar_parent_window_lookup(wimp_w w);
+
+
+/**
+ * Find the toolbar using a given RO window handle for its pane.
+ *
+ * \param w the window (pane) handle to look up.
+ * \return the toolbar handle, or NULL if a match wasn't found.
+ */
+
+struct toolbar *ro_toolbar_window_lookup(wimp_w w);
+
+
+/**
+ * Return the RO window handle of the parent window for a toolbar.
+ *
+ * \param *toolbar the toolbar to look up.
+ * \return the RO window handle of the parent.
+ */
+
+wimp_w ro_toolbar_get_parent_window(struct toolbar *toolbar);
+
+
+/**
+ * Return the RO window handle of a toolbar.
+ *
+ * \param *toolbar the toolbar to look up.
+ * \return the RO window handle of the bar.
+ */
+
+wimp_w ro_toolbar_get_window(struct toolbar *toolbar);
+
+
+/**
+ * Return the current height of a toolbar, allowing for available window
+ * space.
+ *
+ * \param *toolbar The toolbar of interest.
+ * \return The current toolbar height in OS units.
+ */
+
+int ro_toolbar_height(struct toolbar *toolbar);
+
+
+/**
+ * Return the full height that a toolbar could grow to, if space is available.
+ *
+ * \param *toolbar The toolbar of interest.
+ * \return The full toolbar height in OS units.
+ */
+
+int ro_toolbar_full_height(struct toolbar *toolbar);
+
+
+/**
+ * Starts a toolbar throbber, if there is one active.
+ *
+ * \param *toolbar the toolbar to start throbbing.
+ */
+
+void ro_toolbar_start_throbbing(struct toolbar *toolbar);
+
+
+/**
+ * Stops a toolbar throbber, if there is one active.
+ *
+ * \param *toolbar the toolbar to stop throbbing.
+ */
+
+void ro_toolbar_stop_throbbing(struct toolbar *toolbar);
+
+
+/**
+ * Animate a toolbar throbber, if there is one active.
+ *
+ * \param *toolbar the toolbar to throb.
+ */
+
+void ro_toolbar_throb(struct toolbar *toolbar);
+
+/**
+ * Change the arrangement of buttons and spacers on a button bar within a
+ * toolbar.
+ *
+ * \param *toolbar The toolbar to change.
+ * \param order[] The new button configuration.
+ * \return true of the order was updated; else false.
+ */
+
+bool ro_toolbar_set_button_order(struct toolbar *toolbar, char order[]);
+
+
+/**
+ * Set the shaded state of a toolbar button.
+ *
+ * \param *toolbar the toolbar to update.
+ * \param action the button action to update.
+ * \param shaded true if the button should be shaded; else false.
+ */
+
+void ro_toolbar_set_button_shaded_state(struct toolbar *toolbar,
+ button_bar_action action, bool shaded);
+
+/**
+ * Give a toolbar input focus, placing the caret into the URL bar if one is
+ * present. Currently a toolbar can only accept focus if it has a URL bar.
+ *
+ * \param *toolbar The toolbar to take the caret.
+ * \return true if the caret was taken; else false.
+ */
+
+bool ro_toolbar_take_caret(struct toolbar *toolbar);
+
+
+/**
+ * Set the content of a toolbar's URL field.
+ *
+ * \param *toolbar the toolbar to update.
+ * \param *url the new url to insert.
+ * \param is_utf8 true if the string is in utf8 encoding; false
+ * if it is in local encoding.
+ * \param set_caret true if the caret should be placed in the field;
+ * else false.
+ */
+
+void ro_toolbar_set_url(struct toolbar *toolbar, const char *url,
+ bool is_utf8, bool set_caret);
+
+
+/**
+ * Return a pointer to the URL contained in a browser toolbar. If the toolbar
+ * doesn't have a URL field, then NULL is returned instead.
+ *
+ * \param *toolbar The toolbar to look up the URL from.
+ * \return pointer to the URL, or NULL.
+ */
+
+const char *ro_toolbar_get_url(struct toolbar *toolbar);
+
+
+/**
+ * Update the state of the URL Bar hotlist icons in all open toolbars.
+ */
+
+void ro_toolbar_update_all_hotlists(void);
+
+
+/**
+ * Update the state of a toolbar's URL Bar hotlist icon to reflect any changes
+ * to the URL or the hotlist contents.
+ *
+ * \param *toolbar The toolbar to update.
+ */
+
+void ro_toolbar_update_hotlist(struct toolbar *toolbar);
+
+
+/**
+ * Return the current work area coordinates of the URL and favicon field's
+ * bounding box.
+ *
+ * \param *toolbar The toolbar to look up.
+ * \param *extent Return the coordinates.
+ * \return true if successful; else false.
+ */
+
+bool ro_toolbar_get_url_field_extent(struct toolbar *toolbar, os_box *extent);
+
+
+/**
+ * Update the favicon in a browser window toolbar to the supplied content, or
+ * revert to using filetype-based icons.
+ *
+ * \param *toolbar The toolbar to refresh.
+ * \param *h The new favicon to use, or NULL for none.
+ */
+
+void ro_toolbar_set_site_favicon(struct toolbar *toolbar,
+ struct hlcache_handle *h);
+
+
+/**
+ * Update the favicon in a browser window toolbar to reflect the RISC OS
+ * filetype of the content within the supplied window. If the toolbar
+ * currently has a site favicon set, then this call will be ignored.
+ *
+ * \param *toolbar The toolbar to refresh.
+ * \param *g The gui window to set content favicon for.
+ */
+
+void ro_toolbar_set_content_favicon(struct toolbar *toolbar,
+ struct gui_window *g);
+
+
+/**
+ * Update the state of the URL suggestion pop-up menu icon on a toolbar.
+ *
+ * \param *toolbar The toolbar to update.
+ */
+
+void ro_toolbar_update_urlsuggest(struct toolbar *toolbar);
+
+
+/**
+ * Set the display button bar state for a toolbar.
+ *
+ * \param *toolbar the toolbar to update.
+ * \param display true to display the button bar; else false.
+ */
+
+void ro_toolbar_set_display_buttons(struct toolbar *toolbar, bool display);
+
+
+/**
+ * Set the display URL bar state for a toolbar.
+ *
+ * \param *toolbar the toolbar to update.
+ * \param display true to display the URL bar; else false.
+ */
+
+void ro_toolbar_set_display_url(struct toolbar *toolbar, bool display);
+
+
+/**
+ * Set the display throbber state for a toolbar.
+ *
+ * \param *toolbar the toolbar to update.
+ * \param display true to display the throbber; else false.
+ */
+
+void ro_toolbar_set_display_throbber(struct toolbar *toolbar, bool display);
+
+
+/**
+ * Return true or false depending on whether the given toolbar is set to
+ * display the button bar.
+ *
+ * \param *toolbar the toolbar of interest.
+ * \return true if the toolbar exists and the button bar is
+ * shown; else false.
+ */
+
+bool ro_toolbar_get_display_buttons(struct toolbar *toolbar);
+
+
+/**
+ * Return true or false depending on whether the given toolbar is set to
+ * display the URL bar.
+ *
+ * \param *toolbar the toolbar of interest.
+ * \return true if the toolbar exists and the URL bar is
+ * shown; else false.
+ */
+
+bool ro_toolbar_get_display_url(struct toolbar *toolbar);
+
+
+/**
+ * Return true or false depending on whether the given toolbar is set to
+ * display the throbber.
+ *
+ * \param *toolbar the toolbar of interest.
+ * \return true if the toolbar exists and the throbber is
+ * shown; else false.
+ */
+
+bool ro_toolbar_get_display_throbber(struct toolbar *toolbar);
+
+
+/**
+ * Return true or false depending on whether the given toolbar is currently
+ * being edited.
+ *
+ * \param *toolbar the toolbar of interest.
+ * \return true if the toolbar exists and is beng edited;
+ * else false.
+ */
+
+bool ro_toolbar_get_editing(struct toolbar *toolbar);
+
+
+/**
+ * Toggle toolbar edit mode on the given toolbar. Only a button bar can be
+ * edited, so edit mode can only be toggled if there's an editor button
+ * bar defined.
+ *
+ * \param *toolbar The toolbar to be toggled.
+ * \return true if the action was successful; false if
+ * the action failed and the toolbar was destroyed.
+ */
+
+bool ro_toolbar_toggle_edit(struct toolbar *toolbar);
+
+#endif
+
diff --git a/frontends/riscos/treeview.c b/frontends/riscos/treeview.c
new file mode 100644
index 000000000..3428ad3d4
--- /dev/null
+++ b/frontends/riscos/treeview.c
@@ -0,0 +1,1279 @@
+/*
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Generic tree handling (implementation).
+ */
+
+#include <oslib/os.h>
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <swis.h>
+#include <time.h>
+#include "oslib/colourtrans.h"
+#include "oslib/dragasprite.h"
+#include "oslib/osbyte.h"
+#include "oslib/osspriteop.h"
+#include "oslib/wimp.h"
+
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "content/urldb.h"
+#include "desktop/browser.h"
+#include "desktop/plotters.h"
+#include "desktop/textinput.h"
+#include "desktop/tree.h"
+
+#include "riscos/bitmap.h"
+#include "riscos/dialog.h"
+#include "riscos/gui.h"
+#include "riscos/image.h"
+#include "riscos/menus.h"
+#include "riscos/mouse.h"
+#include "riscos/toolbar.h"
+#include "riscos/tinct.h"
+#include "riscos/textarea.h"
+#include "riscos/treeview.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/wimputils.h"
+
+#ifndef wimp_KEY_END
+#define wimp_KEY_END wimp_KEY_COPY
+#endif
+
+struct ro_treeview
+{
+ struct tree *tree; /*< Pointer to treeview tree block. */
+ wimp_w w; /*< RO Window Handle for tree window. */
+ struct toolbar *tb; /*< Pointer to toolbar block. */
+ struct {
+ int x; /*< X origin of tree, to RO work area. */
+ int y; /*< Y origin of tree, to RO work area. */
+ } origin;
+ struct {
+ int x; /*< X dimension of the tree, in RO units. */
+ int y; /*< Y dimension of the tree, in RO units. */
+ } size; /* (Dimensions are 0 until set correctly). */
+ struct {
+ int x; /*< X extent of the window, in RO units. */
+ int y; /*< Y extent of the window, in RO units. */
+ } extent; /* (Extents are 0 until set correctly). */
+ struct {
+ int x; /*< X coordinate of drag start */
+ int y; /*< Y coordinate of drag start */
+ } drag_start;
+ tree_drag_type drag; /*< The current drag type for the tree */
+ struct ro_treeview_callbacks *callbacks; /*< Callback handlers */
+};
+
+static void ro_treeview_redraw_request(int x, int y, int width, int height,
+ void *pw);
+static void ro_treeview_resized(struct tree *tree, int width, int height,
+ void *pw);
+static void ro_treeview_scroll_visible(int y, int height, void *pw);
+static void ro_treeview_get_window_dimensions(int *width, int *height,
+ void *pw);
+
+static void ro_treeview_redraw(wimp_draw *redraw);
+static void ro_treeview_scroll(wimp_scroll *scroll);
+static void ro_treeview_redraw_loop(wimp_draw *redraw, ro_treeview *tv,
+ osbool more);
+static void ro_treeview_open(wimp_open *open);
+static bool ro_treeview_mouse_click(wimp_pointer *pointer);
+static void ro_treeview_pointer_entering(wimp_entering *entering);
+static void ro_treeview_drag_start(ro_treeview *tv, wimp_pointer *pointer,
+ wimp_window_state *state);
+static void ro_treeview_drag_end(wimp_dragged *drag, void *data);
+static bool ro_treeview_keypress(wimp_key *key);
+
+static void ro_treeview_set_window_extent(ro_treeview *tv,
+ int width, int height);
+
+static void ro_treeview_update_theme(void *data, bool ok);
+static void ro_treeview_update_toolbar(void *data);
+static void ro_treeview_button_update(void *data);
+static void ro_treeview_save_toolbar_buttons(void *data, char *config);
+static void ro_treeview_button_click(void *data,
+ toolbar_action_type action_type, union toolbar_action action);
+
+static const struct treeview_table ro_tree_callbacks = {
+ ro_treeview_redraw_request,
+ ro_treeview_resized,
+ ro_treeview_scroll_visible,
+ ro_treeview_get_window_dimensions
+};
+
+static const struct toolbar_callbacks ro_treeview_toolbar_callbacks = {
+ ro_treeview_update_theme,
+ ro_treeview_update_toolbar,
+ ro_treeview_button_update,
+ ro_treeview_button_click,
+ NULL, /* No toolbar keypress handler */
+ ro_treeview_save_toolbar_buttons
+};
+
+
+/**
+ * Create a RISC OS GUI implementation of a treeview tree.
+ *
+ * \param window The window to create the tree in.
+ * \param *toolbar A toolbar to attach to the window.
+ * \param *callbacks Callbacks to service the treeview.
+ * \param flags The treeview flags.
+ *
+ * \return The RISC OS treeview pointer.
+ */
+
+ro_treeview *ro_treeview_create(wimp_w window, struct toolbar *toolbar,
+ struct ro_treeview_callbacks *callbacks, unsigned int flags)
+{
+ ro_treeview *tv;
+
+ /* Claim memory for the treeview block, and create a tree. */
+
+ tv = malloc(sizeof(ro_treeview));
+ if (tv == NULL)
+ return NULL;
+
+ tv->w = window;
+ tv->tb = toolbar;
+
+ /* Set the tree redraw origin at a default 0,0 RO units. */
+
+ tv->origin.x = 0;
+ tv->origin.y = 0;
+
+ /* Set the tree size as 0,0 to indicate that we don't know. */
+
+ tv->size.x = 0;
+ tv->size.y = 0;
+
+ /* Set the tree window extent to 0,0, to indicate that we
+ * don't know. */
+
+ tv->extent.x = 0;
+ tv->extent.y = 0;
+
+ /* Set that there is no drag opperation at the moment */
+
+ tv->drag = TREE_NO_DRAG;
+
+ tv->tree = tree_create(flags, &ro_tree_callbacks, tv);
+ if (tv->tree == NULL) {
+ free(tv);
+ return NULL;
+ }
+
+ /* Record the callback info. */
+
+ tv->callbacks = callbacks;
+
+ /* Register wimp events to handle the supplied window. */
+
+ ro_gui_wimp_event_register_redraw_window(tv->w, ro_treeview_redraw);
+ ro_gui_wimp_event_register_scroll_window(tv->w, ro_treeview_scroll);
+ ro_gui_wimp_event_register_pointer_entering_window(tv->w,
+ ro_treeview_pointer_entering);
+ ro_gui_wimp_event_register_open_window(tv->w, ro_treeview_open);
+ ro_gui_wimp_event_register_mouse_click(tv->w, ro_treeview_mouse_click);
+ ro_gui_wimp_event_register_keypress(tv->w, ro_treeview_keypress);
+ ro_gui_wimp_event_set_user_data(tv->w, tv);
+
+ return tv;
+}
+
+/**
+ * Delete a RISC OS GUI implementation of a treeview tree. The window is
+ * *not* destroyed -- this must be done by the caller.
+ *
+ * \param tv The RISC OS treeview to delete.
+ */
+
+void ro_treeview_destroy(ro_treeview *tv)
+{
+ ro_gui_wimp_event_finalise(tv->w);
+
+ tree_delete(tv->tree);
+
+ free(tv);
+}
+
+/**
+ * Return a pointer to a toolbar callbacks structure with the handlers to be
+ * used by any treeview window toolbars.
+ *
+ * \return A pointer to the callback structure.
+ */
+
+const struct toolbar_callbacks *ro_treeview_get_toolbar_callbacks(void)
+{
+ return &ro_treeview_toolbar_callbacks;
+}
+
+/**
+ * Change the redraw origin of a treeview tree in RISC OS graphics units.
+ *
+ * \param *tv The ro_treeview object to update.
+ * \param x The X position, in terms of the RO window work area.
+ * \param y The Y position, in terms of the RO window work area.
+ *
+ * \todo -- this probably needs a rework.
+ */
+
+void ro_treeview_set_origin(ro_treeview *tv, int x, int y)
+{
+ if (tv != NULL) {
+ tv->origin.x = x;
+ tv->origin.y = y;
+
+ /* Assuming that we know how big the tree currently is, then
+ * adjust the window work area extent to match. If we don't,
+ * then presumably the tree isn't in an open window yet and
+ * a subsequent Open Window Event should pick it up.
+ */
+
+ if (tv->size.x != 0 && tv->size.y != 0)
+ ro_treeview_set_window_extent(tv,
+ tv->origin.x + tv->size.x,
+ tv->origin.y + tv->size.y);
+ }
+}
+
+/**
+ * Return details of the tree block associated with an ro_treeview object.
+ *
+ * \param *tv The ro_treeview object of interest.
+ * \return A pointer to the associated tree block.
+ */
+
+struct tree *ro_treeview_get_tree(ro_treeview *tv)
+{
+ return (tv != NULL) ? (tv->tree) : (NULL);
+}
+
+/**
+ * Return details of the RISC OS window handle associated with an
+ * ro_treeview object.
+ *
+ * \param *tv The ro_treeview object of interest.
+ * \return The associated RISC OS window handle.
+ */
+
+wimp_w ro_treeview_get_window(ro_treeview *tv)
+{
+ return (tv != NULL) ? (tv->w) : (NULL);
+}
+
+/**
+ * Callback to force a redraw of part of the treeview window.
+ *
+ * \param x Min X Coordinate of area to be redrawn.
+ * \param y Min Y Coordinate of area to be redrawn.
+ * \param width Width of area to be redrawn.
+ * \param height Height of area to be redrawn.
+ * \param pw The treeview object to be redrawn.
+ */
+
+void ro_treeview_redraw_request(int x, int y, int width, int height,
+ void *pw)
+{
+ if (pw != NULL) {
+ ro_treeview *tv = (ro_treeview *) pw;
+ os_error *error;
+ wimp_draw update;
+ osbool more;
+
+ update.w = tv->w;
+ update.box.x0 = (2 * x) + tv->origin.x;
+ update.box.y0 = (-2 * (y + height)) + tv->origin.y;
+ update.box.x1 = (2 * (x + width)) + tv->origin.x;
+ update.box.y1 = (-2 * y) + tv->origin.y;
+
+ error = xwimp_update_window(&update, &more);
+ if (error) {
+ LOG("xwimp_update_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ ro_treeview_redraw_loop(&update, tv, more);
+ }
+}
+
+/**
+ * Pass RISC OS redraw events on to the treeview widget.
+ *
+ * \param *redraw Pointer to Redraw Event block.
+ */
+
+void ro_treeview_redraw(wimp_draw *redraw)
+{
+ osbool more;
+ os_error *error;
+ ro_treeview *tv;
+
+ tv = (ro_treeview *) ro_gui_wimp_event_get_user_data(redraw->w);
+ if (tv == NULL) {
+ LOG("NULL treeview block for window: 0x%x", (unsigned int)redraw->w);
+ /* Don't return, as not servicing redraw events isn't a good
+ * idea. The following code must handle (tv == NULL)
+ * gracefully while clearing the redraw queue.
+ */
+ }
+
+ error = xwimp_redraw_window(redraw, &more);
+ if (error) {
+ LOG("xwimp_redraw_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ ro_treeview_redraw_loop(redraw, tv, more);
+}
+
+/**
+ * Handle scroll events in treeview windows.
+ *
+ * \param *scroll Pointer to Scroll Event block.
+ */
+
+void ro_treeview_scroll(wimp_scroll *scroll)
+{
+ os_error *error;
+ int x = scroll->visible.x1 - scroll->visible.x0 - 32;
+ int y = scroll->visible.y1 - scroll->visible.y0 - 32;
+ ro_treeview *tv;
+
+ tv = (ro_treeview *) ro_gui_wimp_event_get_user_data(scroll->w);
+ if (tv == NULL)
+ return;
+
+ if (tv->tb != NULL)
+ y -= ro_toolbar_full_height(tv->tb);
+
+ switch (scroll->xmin) {
+ case wimp_SCROLL_PAGE_LEFT:
+ scroll->xscroll -= x;
+ break;
+ case wimp_SCROLL_COLUMN_LEFT:
+ scroll->xscroll -= 32;
+ break;
+ case wimp_SCROLL_COLUMN_RIGHT:
+ scroll->xscroll += 32;
+ break;
+ case wimp_SCROLL_PAGE_RIGHT:
+ scroll->xscroll += x;
+ break;
+ default:
+ scroll->xscroll += (x * (scroll->xmin>>2)) >> 2;
+ break;
+ }
+
+ switch (scroll->ymin) {
+ case wimp_SCROLL_PAGE_UP:
+ scroll->yscroll += y;
+ break;
+ case wimp_SCROLL_LINE_UP:
+ scroll->yscroll += 32;
+ break;
+ case wimp_SCROLL_LINE_DOWN:
+ scroll->yscroll -= 32;
+ break;
+ case wimp_SCROLL_PAGE_DOWN:
+ scroll->yscroll -= y;
+ break;
+ default:
+ scroll->yscroll += (y * (scroll->ymin>>2)) >> 2;
+ break;
+ }
+
+ error = xwimp_open_window((wimp_open *) scroll);
+ if (error) {
+ LOG("xwimp_open_window: 0x%x: %s", error->errnum, error->errmess);
+ }
+}
+
+
+/**
+ * Redraw a treeview window, once the initial readraw block has been collected.
+ *
+ * /param *redraw Pointer to redraw block.
+ * /param *tv The treeview object being redrawn.
+ * /param more Flag to show if more actions are required.
+ */
+
+void ro_treeview_redraw_loop(wimp_draw *redraw, ro_treeview *tv, osbool more)
+{
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &ro_plotters
+ };
+
+ while (more) {
+ os_error *error;
+
+ if (tv != NULL && tv->tree != NULL) {
+ struct rect clip;
+
+ ro_plot_origin_x = redraw->box.x0 + tv->origin.x -
+ redraw->xscroll;
+ ro_plot_origin_y = redraw->box.y1 + tv->origin.y -
+ redraw->yscroll;
+
+ clip.x0 = (redraw->clip.x0 - ro_plot_origin_x) / 2;
+ clip.y0 = (ro_plot_origin_y - redraw->clip.y1) / 2;
+
+ /* Treeview text alwyas has flat background colour,
+ * so disable unnecessary background blending */
+ no_font_blending = true;
+ tree_draw(tv->tree, 0, 0,
+ clip.x0, clip.y0,
+ (redraw->clip.x1 - redraw->clip.x0)/2,
+ (redraw->clip.y1 - redraw->clip.y0)/2,
+ &ctx);
+ no_font_blending = false;
+ }
+
+ error = xwimp_get_rectangle(redraw, &more);
+ if (error) {
+ LOG("xwimp_redraw_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ }
+}
+
+/**
+ * Callback to notify us of a new overall tree size.
+ *
+ * \param tree The tree being resized.
+ * \param width The new width of the window.
+ * \param height The new height of the window.
+ * \param *pw The treeview object to be resized.
+ */
+
+void ro_treeview_resized(struct tree *tree, int width, int height,
+ void *pw)
+{
+ if (pw != NULL) {
+ ro_treeview *tv = (ro_treeview *) pw;
+
+ /* Store the width and height in terms of RISC OS work area. */
+
+ tv->size.x = width * 2;
+ tv->size.y = -(height * 2);
+
+ /* Resize the window. */
+
+ ro_treeview_set_window_extent(tv, tv->size.x, tv->size.y);
+ }
+}
+
+/**
+ * Callback to request that a section of the tree is scrolled into view.
+ *
+ * \param y The Y coordinate of top of the area in NS units.
+ * \param height The height of the area in NS units.
+ * \param *pw The treeview object affected.
+ */
+
+void ro_treeview_scroll_visible(int y, int height, void *pw)
+{
+ if (pw != NULL) {
+ ro_treeview *tv = (ro_treeview *) pw;
+ os_error *error;
+ wimp_window_state state;
+ int visible_t, visible_b;
+ int request_t, request_b;
+
+ state.w = tv->w;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* Work out top and bottom of both the currently visible and
+ * the required areas, in terms of the RO work area.
+ */
+
+ visible_t = state.yscroll;
+ visible_b = state.yscroll
+ - (state.visible.y1 - state.visible.y0);
+
+ request_t = -(2 * y);// - tv->origin.y;
+ request_b = -(2 * (y + height));// - tv->origin.y;
+
+ /* If the area is outside the visible window, then scroll it
+ * in to view.
+ */
+
+ if (request_t > visible_t || request_b < visible_b) {
+ if (request_t > visible_t) {
+ state.yscroll = request_t;
+ } else if (request_b < visible_b) {
+ state.yscroll = request_b + tv->origin.y
+ + (state.visible.y1 - state.visible.y0);
+
+ /* If the required area is bigger than the
+ * visible extent, then align to the top and
+ * let the bottom disappear out of view.
+ */
+
+ if (state.yscroll < request_t)
+ state.yscroll = request_t;
+ }
+
+ error = xwimp_open_window((wimp_open *) &state);
+ if (error) {
+ LOG("xwimp_open_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ }
+ }
+}
+
+/**
+ * Callback to return the tree window dimensions to the treeview system.
+ *
+ * \param *width Return the window width.
+ * \param *height Return the window height.
+ * \param *pw The treeview object to use.
+ */
+
+void ro_treeview_get_window_dimensions(int *width, int *height,
+ void *pw)
+{
+ if (pw != NULL && (width != NULL || height != NULL)) {
+ ro_treeview *tv = (ro_treeview *) pw;
+ os_error *error;
+ wimp_window_state state;
+
+ state.w = tv->w;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ if (width != NULL)
+ *width = (state.visible.x1 - state.visible.x0) / 2;
+
+ if (height != NULL)
+ *height = (state.visible.y1 - state.visible.y0) / 2;
+ }
+}
+
+/**
+ * Resize the RISC OS window extent of a treeview.
+ *
+ * \param *tv The RISC OS treeview object to resize.
+ * \param width The new width of the work area, in RO units.
+ * \param height The new height of the work area, in RO units.
+ */
+
+void ro_treeview_set_window_extent(ro_treeview *tv, int width, int height)
+{
+ if (tv != NULL) {
+ os_error *error;
+ os_box extent;
+ wimp_window_state state;
+ int new_x, new_y;
+ int visible_x, visible_y;
+
+ /* Calculate the new window extents, in RISC OS units. */
+
+ new_x = width + tv->origin.x;
+ new_y = height + tv->origin.y;
+
+ /* Get details of the existing window, and start to sanity
+ * check the new extents.
+ */
+
+ state.w = tv->w;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* If the extent is smaller than the current visible area,
+ * then extend it so that it matches the visible area.
+ */
+
+ if (new_x < (state.visible.x1 - state.visible.x0))
+ new_x = state.visible.x1 - state.visible.x0;
+
+ if (new_y > (state.visible.y0 - state.visible.y1))
+ new_y = state.visible.y0 - state.visible.y1;
+
+ /* Calculate the maximum visible coordinates of the existing
+ * window.
+ */
+
+ visible_x = state.xscroll +
+ (state.visible.x1 - state.visible.x0);
+ visible_y = state.yscroll +
+ (state.visible.y0 - state.visible.y1);
+
+ /* If the window is currently open, and the exising visible
+ * area is bigger than the new extent, then we need to reopen
+ * the window in an appropriare position before setting the
+ * new extent.
+ */
+
+ if ((state.flags & wimp_WINDOW_OPEN) &&
+ (visible_x > new_x || visible_y < new_y)) {
+ int new_x_scroll = state.xscroll;
+ int new_y_scroll = state.yscroll;
+
+ if (visible_x > new_x)
+ new_x_scroll = new_x - (state.visible.x1
+ - state.visible.x0);
+
+ if (visible_y < new_y)
+ new_y_scroll = new_y - (state.visible.y0
+ - state.visible.y1);
+
+ if (new_x_scroll < 0) {
+ state.visible.x1 -= new_x_scroll;
+ state.xscroll = 0;
+ } else {
+ state.xscroll = new_x_scroll;
+ }
+
+ if (new_y_scroll > 0) {
+ state.visible.y0 += new_y_scroll;
+ state.yscroll = 0;
+ } else {
+ state.yscroll = new_y_scroll;
+ }
+
+ error = xwimp_open_window((wimp_open *) &state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* \todo -- Not sure if we need to reattach the
+ * toolbar here: the nested wimp seems to take care
+ * of it for us?
+ */
+ }
+
+ /* Now that the new extent fits into the visible window, we
+ * can resize the work area. If we succeed, the values are
+ * recorded to save having to ask the Wimp for them
+ * each time.
+ */
+
+ extent.x0 = 0;
+ extent.y0 = new_y;
+ extent.x1 = new_x;
+ extent.y1 = 0;
+
+ error = xwimp_set_extent(tv->w, &extent);
+ if (error) {
+ LOG("xwimp_set_extent: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ tv->extent.x = new_x;
+ tv->extent.y = new_y;
+ }
+}
+
+/**
+ * Handle RISC OS Window Open events for a treeview window.
+ *
+ * \param *open Pointer to the Window Open Event block.
+ */
+
+static void ro_treeview_open(wimp_open *open)
+{
+ ro_treeview *tv;
+ os_error *error;
+ os_box extent;
+ int width, height;
+
+ tv = (ro_treeview *) ro_gui_wimp_event_get_user_data(open->w);
+ if (tv == NULL) {
+ LOG("NULL treeview block for window: ox%x", (unsigned int)open->w);
+ return;
+ }
+
+ /* Calculate the window work area. It must be at least the same as
+ * the current visible area of the window, and needs to contain the
+ * tree as defined by size.x + offset.x and size.y + offset.y (note
+ * that the offset.y should be set to cover any toolbar, so we can
+ * ignore the size of that).
+ */
+
+ width = open->visible.x1 - open->visible.x0;
+ height = open->visible.y0 - open->visible.y1;
+
+ if (tv->size.x != 0 && width < (tv->origin.x + tv->size.x))
+ width = (tv->origin.x + tv->size.x);
+
+ if (tv->size.y != 0 && height > (tv->size.y + tv->origin.y))
+ height = (tv->size.y + tv->origin.y);
+
+ if (width != tv->extent.x || height != tv->extent.y) {
+ extent.x0 = 0;
+ extent.y0 = height;
+ extent.x1 = width;
+ extent.y1 = 0;
+
+ error = xwimp_set_extent(tv->w, &extent);
+ if (error) {
+ LOG("xwimp_set_extent: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ tv->extent.x = width;
+ tv->extent.y = height;
+ }
+
+ /* \todo -- Might need to add vertical scrollbar hiding back in here? */
+
+ error = xwimp_open_window(open);
+ if (error) {
+ LOG("xwimp_open_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ if (tv->tb)
+ ro_toolbar_process(tv->tb, -1, false);
+}
+
+
+/**
+ * Pass RISC OS Mouse Click events on to the treeview widget.
+ *
+ * \param *pointer Pointer to the Mouse Click Event block.
+ * \return Return true if click handled; else false.
+ */
+
+static bool ro_treeview_mouse_click(wimp_pointer *pointer)
+{
+ os_error *error;
+ ro_treeview *tv;
+ wimp_window_state state;
+ int xpos, ypos;
+ browser_mouse_state mouse;
+ bool handled = false;
+
+ tv = (ro_treeview *) ro_gui_wimp_event_get_user_data(pointer->w);
+ if (tv == NULL) {
+ LOG("NULL treeview block for window: 0x%x", (unsigned int)pointer->w);
+ return false;
+ }
+
+ state.w = tv->w;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ /* Convert the returned mouse coordinates into NetSurf's internal
+ * units.
+ */
+
+ xpos = ((pointer->pos.x - state.visible.x0) +
+ state.xscroll - tv->origin.x) / 2;
+ ypos = ((state.visible.y1 - pointer->pos.y) -
+ state.yscroll + tv->origin.y) / 2;
+
+ /* Start to process the mouse click.
+ *
+ * Select and Adjust are processed normally. To get filer-like operation
+ * with selections, Menu clicks are passed to the treeview first as
+ * Select if there are no selected nodes in the tree.
+ */
+
+ mouse = 0;
+
+ if (pointer->buttons == wimp_CLICK_MENU) {
+ /* TODO: test for no selection, and pass click to select node */
+ /* mouse |= BROWSER_MOUSE_CLICK_1; */
+ } else {
+ mouse = ro_gui_mouse_click_state(pointer->buttons,
+ wimp_BUTTON_DOUBLE_CLICK_DRAG);
+
+ /* Give the window input focus on Select-clicks. This wouldn't
+ * be necessary if the core used the RISC OS caret.
+ */
+
+ if (mouse & BROWSER_MOUSE_CLICK_1)
+ xwimp_set_caret_position(tv->w, -1, -100, -100, 32, -1);
+ }
+
+ if (mouse != 0) {
+ handled = tree_mouse_action(tv->tree, mouse, xpos, ypos);
+
+ tv->drag = tree_drag_status(tv->tree);
+ if (tv->drag != TREE_NO_DRAG) {
+ tv->drag_start.x = xpos;
+ tv->drag_start.y = ypos;
+ }
+
+ /* If it's a visible drag, start the RO side of the visible
+ * effects.
+ */
+
+ if (tv->drag == TREE_SELECT_DRAG ||
+ tv->drag == TREE_MOVE_DRAG)
+ ro_treeview_drag_start(tv, pointer, &state);
+
+
+ if (tv->callbacks != NULL &&
+ tv->callbacks->toolbar_button_update != NULL)
+ tv->callbacks->toolbar_button_update();
+ }
+
+ /* Special actions for some mouse buttons. Adjust closes the dialog;
+ * Menu opens a menu. For the latter, we assume that the owning module
+ * will have attached a window menu to our parent window with the auto
+ * flag unset (so that we can fudge the selection above). If it hasn't,
+ * the call will quietly fail.
+ *
+ * \TODO -- Adjust-click close isn't a perfect copy of what the RO
+ * version did: adjust clicks anywhere close the tree, and
+ * selections persist.
+ */
+
+ switch(pointer->buttons) {
+ case wimp_CLICK_ADJUST:
+ if (handled)
+ ro_gui_dialog_close(tv->w);
+ break;
+
+ case wimp_CLICK_MENU:
+ ro_gui_wimp_event_process_window_menu_click(pointer);
+ break;
+ }
+
+ return true;
+}
+
+
+/**
+ * Handle Pointer Entering Window events for treeview windows.
+ *
+ * \param *entering The Wimp_PointerEnteringWindow block.
+ */
+
+void ro_treeview_pointer_entering(wimp_entering *entering)
+{
+ ro_treeview *tv;
+
+ tv = (ro_treeview *) ro_gui_wimp_event_get_user_data(entering->w);
+ if (tv == NULL)
+ return;
+
+ ro_mouse_track_start(NULL, ro_treeview_mouse_at, NULL);
+}
+
+/**
+ * Track the mouse under Null Polls from the wimp, to support dragging.
+ *
+ * \param *pointer Pointer to a Wimp Pointer block.
+ * \param *data NULL to allow use as a ro_mouse callback.
+ */
+
+void ro_treeview_mouse_at(wimp_pointer *pointer, void *data)
+{
+ os_error *error;
+ ro_treeview *tv;
+ wimp_window_state state;
+ int xpos, ypos;
+ browser_mouse_state mouse;
+
+ if (pointer->buttons & (wimp_CLICK_MENU))
+ return;
+
+ tv = (ro_treeview *) ro_gui_wimp_event_get_user_data(pointer->w);
+ if (tv == NULL) {
+ LOG("NULL treeview block for window: 0x%x", (unsigned int)pointer->w);
+ return;
+ }
+
+ if (tv->drag == TREE_NO_DRAG)
+ return;
+
+ /* We know now that it's not a Menu click and the treeview thinks
+ * that a drag is in progress.
+ */
+
+ state.w = tv->w;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* Convert the returned mouse coordinates into NetSurf's internal
+ * units.
+ */
+
+ xpos = ((pointer->pos.x - state.visible.x0) +
+ state.xscroll - tv->origin.x) / 2;
+ ypos = ((state.visible.y1 - pointer->pos.y) -
+ state.yscroll + tv->origin.y) / 2;
+
+ /* Start to process the mouse click. */
+
+ mouse = ro_gui_mouse_drag_state(pointer->buttons,
+ wimp_BUTTON_DOUBLE_CLICK_DRAG);
+
+ tree_mouse_action(tv->tree, mouse, xpos, ypos);
+
+ if (!(mouse & BROWSER_MOUSE_DRAG_ON)) {
+ tree_drag_end(tv->tree, mouse, tv->drag_start.x,
+ tv->drag_start.y, xpos, ypos);
+ tv->drag = TREE_NO_DRAG;
+ }
+
+ if (tv->callbacks != NULL &&
+ tv->callbacks->toolbar_button_update != NULL)
+ tv->callbacks->toolbar_button_update();
+}
+
+
+/**
+ * Start a RISC OS drag event to reflect on screen what is happening
+ * during the core tree drag.
+ *
+ * \param *tv The RO treeview to which the drag is attached.
+ * \param *pointer The RO pointer event data block starting the drag.
+ * \param *state The RO window state block for the treeview window.
+ */
+
+static void ro_treeview_drag_start(ro_treeview *tv, wimp_pointer *pointer,
+ wimp_window_state *state)
+{
+ os_error *error;
+ wimp_drag drag;
+ wimp_auto_scroll_info auto_scroll;
+
+ drag.w = tv->w;
+ drag.bbox.x0 = state->visible.x0;
+ drag.bbox.y0 = state->visible.y0;
+ drag.bbox.x1 = state->visible.x1;
+ drag.bbox.y1 = state->visible.y1 - ro_toolbar_height(tv->tb) - 2;
+
+ switch (tv->drag) {
+ case TREE_SELECT_DRAG:
+ drag.type = wimp_DRAG_USER_RUBBER;
+
+ drag.initial.x0 = pointer->pos.x;
+ drag.initial.y0 = pointer->pos.y;
+ drag.initial.x1 = pointer->pos.x;
+ drag.initial.y1 = pointer->pos.y;
+ break;
+
+ case TREE_MOVE_DRAG:
+ drag.type = wimp_DRAG_USER_POINT;
+
+ drag.initial.x0 = pointer->pos.x - 4;
+ drag.initial.y0 = pointer->pos.y - 48;
+ drag.initial.x1 = pointer->pos.x + 48;
+ drag.initial.y1 = pointer->pos.y + 4;
+ break;
+
+ default:
+ /* No other drag types are supported. */
+ break;
+ }
+
+ LOG("Drag start...");
+
+ error = xwimp_drag_box_with_flags(&drag,
+ wimp_DRAG_BOX_KEEP_IN_LINE | wimp_DRAG_BOX_CLIP);
+ if (error) {
+ LOG("xwimp_drag_box: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ } else {
+ auto_scroll.w = tv->w;
+ auto_scroll.pause_zone_sizes.x0 = 80;
+ auto_scroll.pause_zone_sizes.y0 = 80;
+ auto_scroll.pause_zone_sizes.x1 = 80;
+ auto_scroll.pause_zone_sizes.y1 = 80 +
+ ro_toolbar_height(tv->tb);
+ auto_scroll.pause_duration = 0;
+ auto_scroll.state_change = (void *) 1;
+
+ error = xwimp_auto_scroll(wimp_AUTO_SCROLL_ENABLE_VERTICAL,
+ &auto_scroll, NULL);
+ if (error) {
+ LOG("xwimp_auto_scroll: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ ro_mouse_drag_start(ro_treeview_drag_end, ro_treeview_mouse_at,
+ NULL, NULL);
+ }
+}
+
+
+/**
+ * Process RISC OS User Drag Box events which relate to us: in effect, drags
+ * started by ro_treeview_drag_start().
+ *
+ * \param *drag Pointer to the User Drag Box Event block.
+ * \param *data NULL to allow use as a ro_mouse callback.
+ */
+
+static void ro_treeview_drag_end(wimp_dragged *drag, void *data)
+{
+ os_error *error;
+
+ error = xwimp_drag_box((wimp_drag *) -1);
+ if (error) {
+ LOG("xwimp_drag_box: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ error = xwimp_auto_scroll(0, NULL, NULL);
+ if (error) {
+ LOG("xwimp_auto_scroll: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+}
+
+
+/**
+ * Pass RISC OS keypress events on to the treeview widget.
+ *
+ * \param *key Pointer to the Key Pressed Event block.
+ * \return Return true if keypress handled; else false.
+ */
+
+static bool ro_treeview_keypress(wimp_key *key)
+{
+ ro_treeview *tv;
+ uint32_t c;
+
+ tv = (ro_treeview *) ro_gui_wimp_event_get_user_data(key->w);
+ if (tv == NULL) {
+ LOG("NULL treeview block for window: 0x%x", (unsigned int)key->w);
+ return false;
+ }
+
+ c = (uint32_t) key->c;
+
+ if ((unsigned)c < 0x20 || (0x7f <= c && c <= 0x9f) ||
+ (c & IS_WIMP_KEY)) {
+ /* Munge control keys into unused control chars */
+ /* We can't map onto 1->26 (reserved for ctrl+<qwerty>
+ That leaves 27->31 and 128->159 */
+ switch (c & ~IS_WIMP_KEY) {
+ case wimp_KEY_TAB: c = 9; break;
+ case wimp_KEY_SHIFT | wimp_KEY_TAB: c = 11; break;
+
+ /* cursor movement keys */
+ case wimp_KEY_HOME:
+ case wimp_KEY_CONTROL | wimp_KEY_LEFT:
+ c = NS_KEY_LINE_START;
+ break;
+ case wimp_KEY_END:
+ if (os_version >= RISCOS5)
+ c = NS_KEY_LINE_END;
+ else
+ c = NS_KEY_DELETE_RIGHT;
+ break;
+ case wimp_KEY_CONTROL | wimp_KEY_RIGHT: c = NS_KEY_LINE_END; break;
+ case wimp_KEY_CONTROL | wimp_KEY_UP: c = NS_KEY_TEXT_START; break;
+ case wimp_KEY_CONTROL | wimp_KEY_DOWN: c = NS_KEY_TEXT_END; break;
+ case wimp_KEY_SHIFT | wimp_KEY_LEFT: c = NS_KEY_WORD_LEFT ; break;
+ case wimp_KEY_SHIFT | wimp_KEY_RIGHT: c = NS_KEY_WORD_RIGHT; break;
+ case wimp_KEY_SHIFT | wimp_KEY_UP: c = NS_KEY_PAGE_UP; break;
+ case wimp_KEY_SHIFT | wimp_KEY_DOWN: c = NS_KEY_PAGE_DOWN; break;
+ case wimp_KEY_LEFT: c = NS_KEY_LEFT; break;
+ case wimp_KEY_RIGHT: c = NS_KEY_RIGHT; break;
+ case wimp_KEY_UP: c = NS_KEY_UP; break;
+ case wimp_KEY_DOWN: c = NS_KEY_DOWN; break;
+
+ /* editing */
+ case wimp_KEY_CONTROL | wimp_KEY_END:
+ c = NS_KEY_DELETE_LINE_END;
+ break;
+ case wimp_KEY_DELETE:
+ if (ro_gui_ctrl_pressed())
+ c = NS_KEY_DELETE_LINE_START;
+ else if (os_version < RISCOS5)
+ c = NS_KEY_DELETE_LEFT;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (!(c & IS_WIMP_KEY)) {
+ if (tree_keypress(tv->tree, c)) {
+ if (tv->callbacks &&
+ tv->callbacks->toolbar_button_update
+ != NULL)
+ tv->callbacks->toolbar_button_update();
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/**
+ * Update a treeview to use a new theme.
+ *
+ * \param *data Pointer to the treeview to update.
+ * \param ok true if the bar still exists; else false.
+ */
+
+void ro_treeview_update_theme(void *data, bool ok)
+{
+ ro_treeview *tv = (ro_treeview *) data;
+
+ if (tv != NULL && tv->tb != NULL){
+ if (ok) {
+ ro_treeview_update_toolbar(tv);
+ } else {
+ tv->tb = NULL;
+ }
+ }
+}
+
+
+/**
+ * Change the size of a treeview's toolbar and redraw the window.
+ *
+ * \param *data The treeview to update.
+ */
+
+void ro_treeview_update_toolbar(void *data)
+{
+ ro_treeview *tv = (ro_treeview *) data;
+
+ if (tv != NULL && tv->tb != NULL) {
+ ro_treeview_set_origin(tv, 0,
+ -(ro_toolbar_height(tv->tb)));
+
+ xwimp_force_redraw(tv->w, 0, tv->extent.y, tv->extent.x, 0);
+ }
+}
+
+
+/**
+ * Update the toolbar icons in a treeview window's toolbar. As we're just
+ * an intermediate widget, we pass the details on down the chain.
+ *
+ * \param *data The treeview owning the toolbar.
+ */
+
+void ro_treeview_button_update(void *data)
+{
+ ro_treeview *tv = (ro_treeview *) data;
+
+ if (tv == NULL || tv->callbacks == NULL)
+ return;
+
+ if (tv->callbacks->toolbar_button_update != NULL)
+ tv->callbacks->toolbar_button_update();
+}
+
+
+/**
+ * Save a new button configuration from a treeview window's toolbar. As
+ * we're just an intermediate widget, we pass the details on.
+ *
+ * \param *data The treeview owning the toolbar.
+ * \param *config The new button config string.
+ */
+
+void ro_treeview_save_toolbar_buttons(void *data, char *config)
+{
+ ro_treeview *tv = (ro_treeview *) data;
+
+ if (tv == NULL || tv->callbacks == NULL)
+ return;
+
+ if (tv->callbacks->toolbar_button_save != NULL)
+ tv->callbacks->toolbar_button_save(config);
+}
+
+
+/**
+ * Process clicks on buttons in a treeview window's toolbar. As we're just
+ * an intermediate widget, we just pass the details on down the chain.
+ *
+ * \param *data The treeview owning the click.
+ * \param action_type The action type to be handled.
+ * \param action The action to handle.
+ */
+
+void ro_treeview_button_click(void *data,
+ toolbar_action_type action_type, union toolbar_action action)
+{
+ ro_treeview *tv = (ro_treeview *) data;
+
+ if (tv == NULL || tv->callbacks == NULL ||
+ action_type != TOOLBAR_ACTION_BUTTON)
+ return;
+
+ if (tv->callbacks->toolbar_button_click != NULL)
+ tv->callbacks->toolbar_button_click(action.button);
+
+ if (tv->callbacks->toolbar_button_update != NULL)
+ tv->callbacks->toolbar_button_update();
+}
+
+
+/**
+ * Return a token identifying the interactive help message for a given cursor
+ * position.
+ *
+ * Currently this is inimplemented.
+ *
+ * \param *message_data Pointer to the Wimp's help message block.
+ * \return Token value (-1 indicates no help available).
+ */
+
+int ro_treeview_get_help(help_full_message_request *message_data)
+{
+ return -1;
+}
+
diff --git a/frontends/riscos/treeview.h b/frontends/riscos/treeview.h
new file mode 100644
index 000000000..80ff7660f
--- /dev/null
+++ b/frontends/riscos/treeview.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
+ * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Generic tree handling (interface).
+ */
+
+#ifndef _NETSURF_RISCOS_TREEVIEW_H_
+#define _NETSURF_RISCOS_TREEVIEW_H_
+
+#include <stdbool.h>
+#include <oslib/help.h>
+#include <oslib/wimp.h>
+
+#include "desktop/tree.h"
+#include "riscos/toolbar.h"
+
+typedef struct ro_treeview ro_treeview;
+
+struct ro_treeview_callbacks {
+ void (*toolbar_button_click)(button_bar_action action);
+ void (*toolbar_button_update)(void);
+ void (*toolbar_button_save)(char *);
+};
+
+ro_treeview *ro_treeview_create(wimp_w window, struct toolbar *toolbar,
+ struct ro_treeview_callbacks *callbacks, unsigned int flags);
+void ro_treeview_destroy(ro_treeview *tv);
+const struct toolbar_callbacks *ro_treeview_get_toolbar_callbacks(void);
+
+struct tree *ro_treeview_get_tree(ro_treeview *tv);
+wimp_w ro_treeview_get_window(ro_treeview *tv);
+
+void ro_treeview_set_origin(ro_treeview *tv, int x, int y);
+void ro_treeview_mouse_at(wimp_pointer *pointer, void *data);
+int ro_treeview_get_help(help_full_message_request *message_data);
+
+#endif
+
diff --git a/frontends/riscos/ucstables.c b/frontends/riscos/ucstables.c
new file mode 100644
index 000000000..7ac685df2
--- /dev/null
+++ b/frontends/riscos/ucstables.c
@@ -0,0 +1,697 @@
+/*
+ * Copyright 2005 John M Bell <jmb202@ecs.soton.ac.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * UCS conversion tables and RISC OS-specific UTF-8 text handling
+ */
+
+#include <assert.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <oslib/osbyte.h>
+#include <oslib/territory.h>
+
+#include "utils/config.h"
+#include "utils/errors.h"
+#include "utils/log.h"
+#include "utils/utf8.h"
+#include "utils/utils.h"
+#include "desktop/gui_utf8.h"
+
+#include "riscos/ucstables.h"
+
+/* Common values (ASCII) */
+#define common \
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, \
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, \
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, \
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, \
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, \
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, \
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, \
+ 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127 \
+
+/* 0x8c->0x9F, used by many of the encodings */
+#define common2 \
+ 0x2026, 0x2122, 0x2030, 0x2022, 0x2018, 0x2019, 0x2039, 0x203a, \
+ 0x201c, 0x201d, 0x201e, 0x2013, 0x2014, 0x2212, 0x0152, 0x0153, \
+ 0x2020, 0x2021, 0xfb01, 0xfb02
+
+static const int latin1_table[256] =
+{
+ common,
+ 0x20ac, 0x0174, 0x0175, -1, -1, 0x0176, 0x0177, -1, -1, -1, -1, -1,
+ common2,
+ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
+ 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
+ 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
+ 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
+ 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
+ 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
+};
+
+static const int latin2_table[256] =
+{
+ common,
+ 0x20ac, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ common2,
+ 0x00A0, 0x0104, 0x02D8, 0x0141, 0x00A4, 0x013D, 0x015A, 0x00A7,
+ 0x00A8, 0x0160, 0x015E, 0x0164, 0x0179, 0x00AD, 0x017D, 0x017B,
+ 0x00B0, 0x0105, 0x02DB, 0x0142, 0x00B4, 0x013E, 0x015B, 0x02C7,
+ 0x00B8, 0x0161, 0x015F, 0x0165, 0x017A, 0x02DD, 0x017E, 0x017C,
+ 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7,
+ 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E,
+ 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7,
+ 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF,
+ 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7,
+ 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F,
+ 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7,
+ 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9
+};
+
+static const int latin3_table[256] =
+{
+ common,
+ 0x20ac, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ common2,
+ 0x00A0, 0x0126, 0x02D8, 0x00A3, 0x00A4, -1, 0x0124, 0x00A7,
+ 0x00A8, 0x0130, 0x015E, 0x011E, 0x0134, 0x00AD, -1, 0x017B,
+ 0x00B0, 0x0127, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x0125, 0x00B7,
+ 0x00B8, 0x0131, 0x015F, 0x011F, 0x0135, 0x00BD, -1, 0x017C,
+ 0x00C0, 0x00C1, 0x00C2, -1, 0x00C4, 0x010A, 0x0108, 0x00C7,
+ 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+ -1, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x0120, 0x00D6, 0x00D7,
+ 0x011C, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x016C, 0x015C, 0x00DF,
+ 0x00E0, 0x00E1, 0x00E2, -1, 0x00E4, 0x010B, 0x0109, 0x00E7,
+ 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ -1, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x0121, 0x00F6, 0x00F7,
+ 0x011D, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x016D, 0x015D, 0x02D9
+};
+
+static const int latin4_table[256] =
+{
+ common,
+ 0x20ac, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ common2,
+ 0x00A0, 0x0104, 0x0138, 0x0156, 0x00A4, 0x0128, 0x013B, 0x00A7,
+ 0x00A8, 0x0160, 0x0112, 0x0122, 0x0166, 0x00AD, 0x017D, 0x00AF,
+ 0x00B0, 0x0105, 0x02DB, 0x0157, 0x00B4, 0x0129, 0x013C, 0x02C7,
+ 0x00B8, 0x0161, 0x0113, 0x0123, 0x0167, 0x014A, 0x017E, 0x014B,
+ 0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E,
+ 0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x012A,
+ 0x0110, 0x0145, 0x014C, 0x0136, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
+ 0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x0168, 0x016A, 0x00DF,
+ 0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F,
+ 0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x012B,
+ 0x0111, 0x0146, 0x014D, 0x0137, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
+ 0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x0169, 0x016B, 0x02D9
+};
+
+static const int latin5_table[256] =
+{
+ common,
+ 0x20ac, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ common2,
+ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+ 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+ 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
+ 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+ 0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
+ 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0130, 0x015E, 0x00DF,
+ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+ 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
+ 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF
+};
+
+static const int latin6_table[256] =
+{
+ common,
+ 0x20ac, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ common2,
+ 0x00A0, 0x0104, 0x0112, 0x0122, 0x012A, 0x0128, 0x0136, 0x00A7,
+ 0x013B, 0x0110, 0x0160, 0x0166, 0x017D, 0x00AD, 0x016A, 0x014A,
+ 0x00B0, 0x0105, 0x0113, 0x0123, 0x012B, 0x0129, 0x0137, 0x00B7,
+ 0x013C, 0x0111, 0x0161, 0x0167, 0x017E, 0x2015, 0x016B, 0x014B,
+ 0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E,
+ 0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x00CF,
+ 0x00D0, 0x0145, 0x014C, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x0168,
+ 0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
+ 0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F,
+ 0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x00EF,
+ 0x00F0, 0x0146, 0x014D, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x0169,
+ 0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x0138
+};
+
+static const int latin7_table[256] =
+{
+ common,
+ 0x20ac, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0x2026, 0x2122, 0x2030, 0x2022, 0x2018, -1, 0x2039, 0x203a,
+ -1, -1, -1, 0x2013, 0x2014, 0x2212, 0x0152, 0x0153,
+ 0x2020, 0x2021, 0xfb01, 0xfb02,
+ 0x00A0, 0x201D, 0x00A2, 0x00A3, 0x00A4, 0x201E, 0x00A6, 0x00A7,
+ 0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00C6,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x201C, 0x00B5, 0x00B6, 0x00B7,
+ 0x00F8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6,
+ 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112,
+ 0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B,
+ 0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7,
+ 0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF,
+ 0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113,
+ 0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C,
+ 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7,
+ 0x0173, 0x0142, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x2019
+};
+
+static const int latin8_table[256] =
+{
+ common,
+ 0x20ac, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ common2,
+ 0x00A0, 0x1E02, 0x1E03, 0x00A3, 0x010A, 0x010B, 0x1E0A, 0x00A7,
+ 0x1E80, 0x00A9, 0x1E82, 0x1E0B, 0x1EF2, 0x00AD, 0x00AE, 0x0178,
+ 0x1E1E, 0x1E1F, 0x0120, 0x0121, 0x1E40, 0x1E41, 0x00B6, 0x1E56,
+ 0x1E81, 0x1E57, 0x1E83, 0x1E60, 0x1EF3, 0x1E84, 0x1E85, 0x1E61,
+ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
+ 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+ 0x0174, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x1E6A,
+ 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x0176, 0x00DF,
+ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+ 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ 0x0175, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x1E6B,
+ 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x0177, 0x00FF
+};
+
+static const int latin9_table[256] =
+{
+ common,
+ -1, 0x0174, 0x0175, -1, -1, 0x0176, 0x0177, -1, -1, -1, -1, -1,
+ 0x2026, 0x2122, 0x2030, 0x2022, 0x2018, 0x2019, 0x2039, 0x203a,
+ 0x201c, 0x201d, 0x201e, 0x2013, 0x2014, 0x2212, -1, -1,
+ 0x2020, 0x2021, 0xfb01, 0xfb02,
+ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AC, 0x00A5, 0x0160, 0x00A7,
+ 0x0161, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x017D, 0x00B5, 0x00B6, 0x00B7,
+ 0x017E, 0x00B9, 0x00BA, 0x00BB, 0x0152, 0x0153, 0x0178, 0x00BF,
+ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
+ 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+ 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
+ 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
+ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+ 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
+ 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
+};
+
+static const int latin10_table[256] =
+{
+ common,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0x2026, 0x2122, 0x2030, 0x2022, 0x2018, 0x2019, 0x2039, 0x203a,
+ 0x201c, -1, -1, 0x2013, 0x2014, 0x2212, -1, -1,
+ 0x2020, 0x2021, 0xfb01, 0xfb02,
+ 0x00A0, 0x0104, 0x0105, 0x0141, 0x20AC, 0x201E, 0x0160, 0x00a7,
+ 0x0161, 0x00A9, 0x0218, 0x00AB, 0x0179, 0x00AD, 0x017A, 0x017B,
+ 0x00B0, 0x00B1, 0x010C, 0x0142, 0x017D, 0x201D, 0x00B6, 0x00B7,
+ 0x017E, 0x010D, 0x0219, 0x00BB, 0x0152, 0x0153, 0x0178, 0x017C,
+ 0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0106, 0x00C6, 0x00C7,
+ 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+ 0x0110, 0x0143, 0x00D2, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x015A,
+ 0x0170, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0118, 0x021A, 0x00DF,
+ 0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x0107, 0x00E6, 0x00E7,
+ 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ 0x0111, 0x0144, 0x00F2, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x015B,
+ 0x0171, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0119, 0x021B, 0x00FF
+};
+
+static const int welsh_table[256] =
+{
+ common,
+ 0x20ac, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ common2,
+ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+ 0x1E80, 0x00A9, 0x1E82, 0x00AB, 0x1EF2, 0x00AD, 0x00AE, 0x0178,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+ 0x1E81, 0x00B9, 0x1E83, 0x00BB, 0x1EF3, 0x1E84, 0x1E85, 0x00BF,
+ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
+ 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+ 0x0174, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
+ 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x0176, 0x00DF,
+ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+ 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ 0x0175, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
+ 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x0177, 0x00FF
+};
+
+static const int greek_table[256] =
+{
+ common,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0x00A0, 0x2018, 0x2019, 0x00A3, 0x20AC, 0x20AF, 0x00A6, 0x00A7,
+ 0x00A8, 0x00A9, 0x037A, 0x00AB, 0x00AC, 0x00AD, 0x037E, 0x2015,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x0385, 0x0386, 0x0387,
+ 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F,
+ 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
+ 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,
+ 0x03A0, 0x03A1, -1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7,
+ 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF,
+ 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,
+ 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
+ 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7,
+ 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, -1
+};
+
+static const int cyrillic_table[256] =
+{
+ common,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0x00A0, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407,
+ 0x0408, 0x0409, 0x040A, 0x040B, 0x040C, 0x00AD, 0x040E, 0x040F,
+ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
+ 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
+ 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
+ 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
+ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
+ 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
+ 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
+ 0x2116, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457,
+ 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x00A7, 0x045E, 0x045F
+};
+
+static const int hebrew_table[256] =
+{
+ common,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0x00A0, -1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+ 0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x203E,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+ 0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 0x2017,
+ 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7,
+ 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
+ 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7,
+ 0x05E8, 0x05E9, 0x05EA, -1, -1, 0x200E, 0x200F, -1
+};
+
+/**
+ * Retrieve UCS table (above), given alphabet number
+ *
+ * \param alphabet The RISC OS alphabet number
+ * \return pointer to table, or NULL if not found
+ */
+const int *ucstable_from_alphabet(int alphabet)
+{
+ const int *ucstable = NULL;
+
+ switch (alphabet) {
+ case territory_ALPHABET_LATIN1:
+ ucstable = latin1_table;
+ break;
+ case territory_ALPHABET_LATIN2:
+ ucstable = latin2_table;
+ break;
+ case territory_ALPHABET_LATIN3:
+ ucstable = latin3_table;
+ break;
+ case territory_ALPHABET_LATIN4:
+ ucstable = latin4_table;
+ break;
+ case territory_ALPHABET_LATIN5:
+ ucstable = latin5_table;
+ break;
+ case territory_ALPHABET_LATIN6:
+ ucstable = latin6_table;
+ break;
+ case 114: /* Latin7 */
+ ucstable = latin7_table;
+ break;
+ case 115: /* Latin8 */
+ ucstable = latin8_table;
+ break;
+ case 116: /* Latin10 */
+ ucstable = latin10_table;
+ break;
+ case territory_ALPHABET_LATIN9:
+ ucstable = latin9_table;
+ break;
+ case territory_ALPHABET_WELSH:
+ ucstable = welsh_table;
+ break;
+ case territory_ALPHABET_GREEK:
+ ucstable = greek_table;
+ break;
+ case territory_ALPHABET_CYRILLIC:
+ ucstable = cyrillic_table;
+ break;
+ case territory_ALPHABET_HEBREW:
+ ucstable = hebrew_table;
+ break;
+ default:
+ ucstable = NULL;
+ break;
+ }
+
+ return ucstable;
+}
+
+
+static const char *localencodings[] = {
+ "ISO-8859-1//TRANSLIT", /* BFont - 100 - just use Latin1, instead */
+ "ISO-8859-1//TRANSLIT",
+ "ISO-8859-2//TRANSLIT",
+ "ISO-8859-3//TRANSLIT",
+ "ISO-8859-4//TRANSLIT",
+ "ISO-8859-5//TRANSLIT",
+ "ISO-8859-6//TRANSLIT",
+ "ISO-8859-7//TRANSLIT",
+ "ISO-8859-8//TRANSLIT",
+ "ISO-8859-9//TRANSLIT",
+ "ISO-IR-182//TRANSLIT",
+ "UTF-8",
+ "ISO-8859-15//TRANSLIT",
+ "ISO-8859-10//TRANSLIT",
+ "ISO-8859-13//TRANSLIT",
+ "ISO-8859-14//TRANSLIT",
+ "ISO-8859-16//TRANSLIT",
+#define CONT_ENC_END 116 /* RISC OS alphabet numbers lie in a
+ * contiguous range [100,CONT_ENC_END]
+ * _except_ for Cyrillic2, which doesn't.
+ */
+ "CP866//TRANSLIT" /* Cyrillic2 - 120 */
+};
+
+static const struct special {
+ char local; /**< Local 8bit representation */
+ char len; /**< Length (in bytes) of UTF-8 character */
+ const char *utf; /**< UTF-8 representation */
+} special_chars[] = {
+ { 0x80, 3, "\xE2\x82\xAC" }, /* EURO SIGN */
+ { 0x81, 2, "\xC5\xB4" }, /* LATIN CAPITAL LETTER W WITH CIRCUMFLEX */
+ { 0x82, 2, "\xC5\xB5" }, /* LATIN SMALL LETTER W WITH CIRCUMFLEX */
+ { 0x84, 3, "\xE2\x9C\x98" }, /* HEAVY BALLOT X */
+ { 0x85, 2, "\xC5\xB6" }, /* LATIN CAPITAL LETTER Y WITH CIRCUMFLEX */
+ { 0x86, 2, "\xC5\xB7" }, /* LATIN SMALL LETTER Y WITH CIRCUMFLEX */
+ { 0x88, 3, "\xE2\x87\x90" }, /* LEFTWARDS DOUBLE ARROW */
+ { 0x89, 3, "\xE2\x87\x92" }, /* RIGHTWARDS DOUBLE ARROW */
+ { 0x8a, 3, "\xE2\x87\x93" }, /* DOWNWARDS DOUBLE ARROW */
+ { 0x8b, 3, "\xE2\x87\x91" }, /* UPWARDS DOUBLE ARROW */
+ { 0x8c, 3, "\xE2\x80\xA6" }, /* HORIZONTAL ELLIPSIS */
+ { 0x8d, 3, "\xE2\x84\xA2" }, /* TRADE MARK SIGN */
+ { 0x8e, 3, "\xE2\x80\xB0" }, /* PER MILLE SIGN */
+ { 0x8f, 3, "\xE2\x80\xA2" }, /* BULLET */
+ { 0x90, 3, "\xE2\x80\x98" }, /* LEFT SINGLE QUOTATION MARK */
+ { 0x91, 3, "\xE2\x80\x99" }, /* RIGHT SINGLE QUOTATION MARK */
+ { 0x92, 3, "\xE2\x80\xB9" }, /* SINGLE LEFT-POINTING ANGLE QUOTATION MARK */
+ { 0x93, 3, "\xE2\x80\xBA" }, /* SINGLE RIGHT-POINTING ANGLE QUOTATION MARK */
+ { 0x94, 3, "\xE2\x80\x9C" }, /* LEFT DOUBLE QUOTATION MARK */
+ { 0x95, 3, "\xE2\x80\x9D" }, /* RIGHT DOUBLE QUOTATION MARK */
+ { 0x96, 3, "\xE2\x80\x9E" }, /* DOUBLE LOW-9 QUOTATION MARK */
+ { 0x97, 3, "\xE2\x80\x93" }, /* EN DASH */
+ { 0x98, 3, "\xE2\x80\x94" }, /* EM DASH */
+ { 0x99, 3, "\xE2\x88\x92" }, /* MINUS SIGN */
+ { 0x9a, 2, "\xC5\x92" }, /* LATIN CAPITAL LIGATURE OE */
+ { 0x9b, 2, "\xC5\x93" }, /* LATIN SMALL LIGATURE OE */
+ { 0x9c, 3, "\xE2\x80\xA0" }, /* DAGGER */
+ { 0x9d, 3, "\xE2\x80\xA1" }, /* DOUBLE DAGGER */
+ { 0x9e, 3, "\xEF\xAC\x81" }, /* LATIN SMALL LIGATURE FI */
+ { 0x9f, 3, "\xEF\xAC\x82" } /* LATIN SMALL LIGATURE FL */
+};
+
+
+/**
+ * Convert a UTF-8 encoded string into the system local encoding
+ *
+ * \param string The string to convert
+ * \param len The length (in bytes) of the string, or 0
+ * \param result Pointer to location in which to store result
+ * \return An nserror code
+ */
+nserror utf8_to_local_encoding(const char *string, size_t len, char **result)
+{
+ os_error *error;
+ int alphabet, i;
+ size_t off, prev_off;
+ char *temp, *cur_pos;
+ const char *enc;
+ nserror err;
+
+ assert(string);
+ assert(result);
+
+ /* get length, if necessary */
+ if (len == 0)
+ len = strlen(string);
+
+ /* read system alphabet */
+ error = xosbyte1(osbyte_ALPHABET_NUMBER, 127, 0, &alphabet);
+ if (error)
+ alphabet = territory_ALPHABET_LATIN1;
+
+ /* UTF-8 -> simply copy string */
+ if (alphabet == 111 /* UTF-8 */) {
+ *result = strndup(string, len);
+ return NSERROR_OK;
+ }
+
+ /* get encoding name */
+ enc = (alphabet <= CONT_ENC_END ? localencodings[alphabet - 100]
+ : (alphabet == 120 ?
+ localencodings[CONT_ENC_END - 100 + 1]
+ : localencodings[0]));
+
+ /* create output buffer */
+ *(result) = malloc(len + 1);
+ if (!(*result))
+ return NSERROR_NOMEM;
+ *(*result) = '\0';
+
+ prev_off = 0;
+ cur_pos = (*result);
+
+ /* Iterate over string, converting input between unconvertable
+ * characters and inserting appropriate output for characters
+ * that iconv can't handle. */
+ for (off = 0; off < len; off = utf8_next(string, len, off)) {
+ if (string[off] != 0xE2 &&
+ string[off] != 0xC5 && string[off] != 0xEF)
+ continue;
+
+ for (i = 0; i != NOF_ELEMENTS(special_chars); i++) {
+ if (strncmp(string + off, special_chars[i].utf,
+ special_chars[i].len) != 0)
+ continue;
+
+ /* 0 length has a special meaning to utf8_to_enc */
+ if (off - prev_off > 0) {
+ err = utf8_to_enc(string + prev_off, enc,
+ off - prev_off, &temp);
+ if (err != NSERROR_OK) {
+ assert(err != NSERROR_BAD_ENCODING);
+ free(*result);
+ return NSERROR_NOMEM;
+ }
+
+ strcat(cur_pos, temp);
+
+ cur_pos += strlen(temp);
+
+ free(temp);
+ }
+
+ *cur_pos = special_chars[i].local;
+ *(++cur_pos) = '\0';
+ prev_off = off + special_chars[i].len;
+ }
+ }
+
+ /* handle last chunk
+ * NB. 0 length has a special meaning to utf8_to_enc */
+
+ if (prev_off < len) {
+ err = utf8_to_enc(string + prev_off, enc, len - prev_off,
+ &temp);
+ if (err != NSERROR_OK) {
+ assert(err != NSERROR_BAD_ENCODING);
+ free(*result);
+ return NSERROR_NOMEM;
+ }
+
+ strcat(cur_pos, temp);
+
+ free(temp);
+ }
+
+ return NSERROR_OK;
+}
+
+/**
+ * Convert a string encoded in the system local encoding to UTF-8
+ *
+ * \param string The string to convert
+ * \param len The length (in bytes) of the string, or 0
+ * \param result Pointer to location in which to store result
+ * \return An nserror code
+ */
+nserror utf8_from_local_encoding(const char *string, size_t len, char **result)
+{
+ os_error *error;
+ int alphabet, i, num_specials = 0, result_alloc;
+#define SPECIAL_CHUNK_SIZE 255
+ size_t off, prev_off, cur_off;
+ char *temp;
+ const char *enc;
+ nserror err;
+
+ assert(string && result);
+
+ /* get length, if necessary */
+ if (len == 0)
+ len = strlen(string);
+
+ /* read system alphabet */
+ error = xosbyte1(osbyte_ALPHABET_NUMBER, 127, 0, &alphabet);
+ if (error)
+ alphabet = territory_ALPHABET_LATIN1;
+
+ /* UTF-8 -> simply copy string */
+ if (alphabet == 111 /* UTF-8 */) {
+ temp = strndup(string, len);
+ if (!temp)
+ return NSERROR_NOMEM;
+
+ *result = temp;
+ return NSERROR_OK;
+ }
+
+ /* get encoding name */
+ enc = (alphabet <= CONT_ENC_END ? localencodings[alphabet - 100]
+ : (alphabet == 120 ?
+ localencodings[CONT_ENC_END - 100 + 1]
+ : localencodings[0]));
+
+ /* create output buffer (oversized) */
+ result_alloc = (len * 4) + (3 * SPECIAL_CHUNK_SIZE) + 1;
+
+ *(result) = malloc(result_alloc);
+ if (!(*result))
+ return NSERROR_NOMEM;
+ *(*result) = '\0';
+
+ prev_off = 0;
+ cur_off = 0;
+
+ /* Iterate over string, converting input between unconvertable
+ * characters and inserting appropriate output for characters
+ * that iconv can't handle. */
+ for (off = 0; off < len; off++) {
+ if (string[off] < 0x80 || string[off] > 0x9f)
+ continue;
+
+ for (i = 0; i != NOF_ELEMENTS(special_chars); i++) {
+ if (string[off] != special_chars[i].local)
+ continue;
+
+ /* 0 length has a special meaning to utf8_from_enc */
+ if (off - prev_off > 0) {
+ err = utf8_from_enc(string + prev_off, enc,
+ off - prev_off, &temp, NULL);
+ if (err != NSERROR_OK) {
+ assert(err != NSERROR_BAD_ENCODING);
+ LOG("utf8_from_enc failed");
+ free(*result);
+ return NSERROR_NOMEM;
+ }
+
+ strcat((*result) + cur_off, temp);
+
+ cur_off += strlen(temp);
+
+ free(temp);
+ }
+
+ strcat((*result) + cur_off, special_chars[i].utf);
+
+ cur_off += special_chars[i].len;
+
+ prev_off = off + 1;
+
+ num_specials++;
+ if (num_specials % SPECIAL_CHUNK_SIZE ==
+ SPECIAL_CHUNK_SIZE - 1) {
+ char *temp = realloc((*result),
+ result_alloc +
+ (3 * SPECIAL_CHUNK_SIZE));
+ if (!temp) {
+ free(*result);
+ return NSERROR_NOMEM;
+ }
+
+ *result = temp;
+ result_alloc += (3 * SPECIAL_CHUNK_SIZE);
+ }
+ }
+ }
+
+ /* handle last chunk
+ * NB. 0 length has a special meaning to utf8_from_enc */
+ if (prev_off < len) {
+ err = utf8_from_enc(string + prev_off, enc, len - prev_off,
+ &temp, NULL);
+ if (err != NSERROR_OK) {
+ assert(err != NSERROR_BAD_ENCODING);
+ LOG("utf8_from_enc failed");
+ free(*result);
+ return NSERROR_NOMEM;
+ }
+
+ strcat((*result) + cur_off, temp);
+
+ cur_off += strlen(temp);
+
+ free(temp);
+ }
+
+ /* and copy into more reasonably-sized buffer */
+ temp = realloc((*result), cur_off + 1);
+ if (!temp) {
+ LOG("realloc failed");
+ free(*result);
+ return NSERROR_NOMEM;
+ }
+ *result = temp;
+
+ return NSERROR_OK;
+}
+
+static struct gui_utf8_table utf8_table = {
+ .utf8_to_local = utf8_to_local_encoding,
+ .local_to_utf8 = utf8_from_local_encoding,
+};
+
+struct gui_utf8_table *riscos_utf8_table = &utf8_table;
diff --git a/frontends/riscos/ucstables.h b/frontends/riscos/ucstables.h
new file mode 100644
index 000000000..e5d838249
--- /dev/null
+++ b/frontends/riscos/ucstables.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2005 John M Bell <jmb202@ecs.soton.ac.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * UCS conversion tables (interface)
+ * This is only used if nothing claims Service_International,8
+ */
+
+struct gui_utf8_table *riscos_utf8_table;
+
+nserror utf8_to_local_encoding(const char *string, size_t len, char **result);
+nserror utf8_from_local_encoding(const char *string, size_t len, char **result);
+
+const int *ucstable_from_alphabet(int alphabet);
diff --git a/frontends/riscos/uri.c b/frontends/riscos/uri.c
new file mode 100644
index 000000000..9c384c9c0
--- /dev/null
+++ b/frontends/riscos/uri.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2003 Rob Jackson <jacko@xms.ms>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "utils/config.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "oslib/uri.h"
+#include "oslib/wimp.h"
+
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/nsurl.h"
+#include "content/fetch.h"
+#include "desktop/browser.h"
+
+#include "riscos/gui.h"
+#include "riscos/uri.h"
+#include "riscos/url_protocol.h"
+
+void ro_uri_message_received(wimp_message *msg)
+{
+ uri_full_message_process *uri_message = (uri_full_message_process *)msg;
+ uri_h uri_handle;
+ char* uri_requested;
+ int uri_length;
+ nsurl *url;
+ nserror error;
+
+ uri_handle = uri_message->handle;
+
+ if (nsurl_create(uri_message->uri, &url) != NSERROR_OK) {
+ return;
+ }
+
+ if (!fetch_can_fetch(url)) {
+ nsurl_unref(url);
+ return;
+ }
+
+ nsurl_unref(url);
+
+ uri_message->your_ref = uri_message->my_ref;
+ uri_message->action = message_URI_PROCESS_ACK;
+
+ xwimp_send_message(wimp_USER_MESSAGE, (wimp_message*)uri_message,
+ uri_message->sender);
+
+ xuri_request_uri(0, 0, 0, uri_handle, &uri_length);
+ uri_requested = calloc((unsigned int)uri_length, sizeof(char));
+ if (uri_requested == NULL)
+ return;
+
+ xuri_request_uri(0, uri_requested, uri_length, uri_handle, NULL);
+
+ error = nsurl_create(uri_requested, &url);
+ free(uri_requested);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ ro_warn_user(messages_get_errorcode(error), 0);
+ }
+}
+
+bool ro_uri_launch(const char *uri)
+{
+ uri_h uri_handle;
+ wimp_t handle_task;
+ uri_dispatch_flags returned;
+ os_error *e;
+
+ e = xuri_dispatch(uri_DISPATCH_INFORM_CALLER, uri, task_handle,
+ &returned, &handle_task, &uri_handle);
+
+ if (e || returned & 1) {
+ return false;
+ }
+
+ return true;
+}
+
+void ro_uri_bounce(wimp_message *msg)
+{
+ uri_full_message_process *message = (uri_full_message_process *)msg;
+ int size;
+ char *uri_buf;
+ os_error *e;
+
+ if ((message->flags & 1) == 0)
+ return;
+
+ /* Get required buffer size */
+ e = xuri_request_uri(0, NULL, 0, message->handle, &size);
+ if (e) {
+ LOG("xuri_request_uri: %d: %s", e->errnum, e->errmess);
+ return;
+ }
+
+ uri_buf = malloc(size);
+ if (uri_buf == NULL)
+ return;
+
+ /* Get URI */
+ e = xuri_request_uri(0, uri_buf, size, message->handle, 0);
+ if (e) {
+ LOG("xuri_request_uri: %d: %s", e->errnum, e->errmess);
+ free(uri_buf);
+ return;
+ }
+
+ ro_url_load(uri_buf);
+
+ free(uri_buf);
+
+ return;
+}
diff --git a/frontends/riscos/uri.h b/frontends/riscos/uri.h
new file mode 100644
index 000000000..d538ea914
--- /dev/null
+++ b/frontends/riscos/uri.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2003 Rob Jackson <jacko@xms.ms>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_RISCOS_URI_H_
+#define _NETSURF_RISCOS_URI_H_
+
+#include "utils/config.h"
+
+#include "oslib/wimp.h"
+
+void ro_uri_message_received(wimp_message *message);
+bool ro_uri_launch(const char *uri);
+void ro_uri_bounce(wimp_message *message);
+
+#endif
diff --git a/frontends/riscos/url_complete.c b/frontends/riscos/url_complete.c
new file mode 100644
index 000000000..3cf7f9228
--- /dev/null
+++ b/frontends/riscos/url_complete.c
@@ -0,0 +1,740 @@
+/*
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * GUI URL auto-completion (implementation).
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <oslib/wimp.h>
+
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "content/urldb.h"
+#include "desktop/browser.h"
+
+#include "riscos/global_history.h"
+#include "riscos/gui.h"
+#include "riscos/mouse.h"
+#include "riscos/toolbar.h"
+#include "riscos/url_complete.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/wimputils.h"
+#include "riscos/filetype.h"
+
+#define MAXIMUM_VISIBLE_LINES 7
+
+static nsurl **url_complete_matches = NULL;
+static int url_complete_matches_allocated = 0;
+static int url_complete_matches_available = 0;
+static char *url_complete_matched_string = NULL;
+static int url_complete_matches_selection = -1;
+static int url_complete_keypress_selection = -1;
+static wimp_w url_complete_parent = 0;
+static bool url_complete_matches_reset = false;
+static char *url_complete_original_url = NULL;
+static bool url_complete_memory_exhausted = false;
+
+static nsurl *url_complete_redraw[MAXIMUM_VISIBLE_LINES];
+static char url_complete_icon_null[] = "";
+static char url_complete_icon_sprite[12];
+static wimp_icon url_complete_icon;
+static wimp_icon url_complete_sprite;
+static int mouse_x;
+static int mouse_y;
+
+static bool url_complete_callback(nsurl *url,
+ const struct url_data *data);
+static void ro_gui_url_complete_mouse_at(wimp_pointer *pointer, void *data);
+
+
+/* This is an exported interface documented in url_complete.h */
+
+void ro_gui_url_complete_start(struct toolbar *toolbar)
+{
+ const char *url;
+ wimp_w parent;
+
+ assert(toolbar != NULL);
+ parent = ro_toolbar_get_parent_window(toolbar);
+
+ if (!ro_toolbar_get_display_url(toolbar) ||
+ (parent == url_complete_parent))
+ return;
+
+ ro_gui_url_complete_close();
+ url = ro_toolbar_get_url(toolbar);
+ if (url != NULL) {
+ url_complete_matched_string = strdup(url);
+ if (url_complete_matched_string)
+ url_complete_parent = parent;
+ }
+}
+
+
+/* This is an exported interface documented in url_complete.h */
+
+bool ro_gui_url_complete_keypress(struct toolbar *toolbar, uint32_t key)
+{
+ wimp_w parent;
+ wimp_window_state state;
+ char *match_url;
+ const char *url;
+ int old_selection;
+ int height;
+ os_error *error;
+ bool currently_open;
+
+ assert(toolbar != NULL);
+ parent = ro_toolbar_get_parent_window(toolbar);
+
+ /* we must have a toolbar/url bar */
+ if (!ro_toolbar_get_display_url(toolbar) ||
+ (!nsoption_bool(url_suggestion))) {
+ ro_gui_url_complete_close();
+ return false;
+ }
+
+ /* if we are currently active elsewhere, remove the previous window */
+ currently_open = ((parent == url_complete_parent) &&
+ (url_complete_matches_available > 0));
+ if (parent != url_complete_parent)
+ ro_gui_url_complete_close();
+
+ /* forcibly open on down keys */
+ if ((!currently_open) && (url_complete_matched_string)) {
+ switch (key) {
+ case IS_WIMP_KEY | wimp_KEY_DOWN:
+ case IS_WIMP_KEY | wimp_KEY_PAGE_DOWN:
+ case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_DOWN:
+ free(url_complete_matched_string);
+ url_complete_matched_string = NULL;
+ }
+ }
+
+
+ /* get the text to match */
+ url_complete_parent = parent;
+ url = ro_toolbar_get_url(toolbar);
+ match_url = (url != NULL) ? strdup(url) : NULL;
+ if (match_url == NULL) {
+ ro_gui_url_complete_close();
+ return false;
+ }
+
+ /* if the text to match has changed then update it */
+ if ((!url_complete_matched_string) ||
+ (strcmp(match_url, url_complete_matched_string))) {
+
+ /* memorize the current matches */
+ int i;
+ int lines = MAXIMUM_VISIBLE_LINES;
+ if (lines > url_complete_matches_available)
+ lines = url_complete_matches_available;
+ if (url_complete_matches) {
+ for (i = 0; i < MAXIMUM_VISIBLE_LINES; i++) {
+ if (i < lines) {
+ url_complete_redraw[i] =
+ url_complete_matches[i];
+ } else {
+ url_complete_redraw[i] = NULL;
+ }
+ }
+ }
+
+ /* our selection gets wiped */
+ error = xwimp_force_redraw(dialog_url_complete,
+ 0,
+ -(url_complete_matches_selection + 1) * 44,
+ 65536, -url_complete_matches_selection * 44);
+ if (error) {
+ LOG("xwimp_force_redraw: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ /* clear our state */
+ free(url_complete_original_url);
+ free(url_complete_matched_string);
+ url_complete_matched_string = match_url;
+ url_complete_original_url = NULL;
+ url_complete_matches_available = 0;
+ url_complete_matches_selection = -1;
+ url_complete_keypress_selection = -1;
+
+ /* get some initial memory */
+ if (!url_complete_matches) {
+ url_complete_matches = malloc(64 * sizeof(char *));
+ if (!url_complete_matches) {
+ ro_gui_url_complete_close();
+ return false;
+ }
+ url_complete_matches_allocated = 64;
+ }
+
+ /* find matches */
+ url_complete_memory_exhausted = false;
+ if (strlen(match_url) == 0)
+ urldb_iterate_entries(url_complete_callback);
+ else
+ urldb_iterate_partial(match_url, url_complete_callback);
+ if ((url_complete_memory_exhausted) ||
+ (url_complete_matches_available == 0)) {
+ ro_gui_url_complete_close();
+ return false;
+ }
+
+ /* update the window */
+ state.w = parent;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+ url_complete_matches_reset = true;
+ ro_gui_url_complete_resize(toolbar, PTR_WIMP_OPEN(&state));
+ url_complete_matches_reset = false;
+
+ /* redraw the relevant bits of the window */
+ lines = MAXIMUM_VISIBLE_LINES;
+ if (lines > url_complete_matches_available)
+ lines = url_complete_matches_available;
+ for (i = 0; i < lines; i++) {
+ if (url_complete_redraw[i] !=
+ url_complete_matches[i]) {
+ error = xwimp_force_redraw(dialog_url_complete,
+ 0, -(i + 1) * 44, 65536, -i * 44);
+ if (error) {
+ LOG("xwimp_force_redraw: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError",
+ error->errmess);
+ }
+ }
+ }
+ } else {
+ free(match_url);
+ }
+
+ /* handle keypresses */
+ if (!currently_open)
+ return false;
+
+ old_selection = url_complete_matches_selection;
+
+ switch (key) {
+ case IS_WIMP_KEY | wimp_KEY_UP:
+ url_complete_matches_selection--;
+ break;
+ case IS_WIMP_KEY | wimp_KEY_DOWN:
+ url_complete_matches_selection++;
+ break;
+ case IS_WIMP_KEY | wimp_KEY_PAGE_UP:
+ url_complete_matches_selection -=
+ MAXIMUM_VISIBLE_LINES;
+ break;
+ case IS_WIMP_KEY | wimp_KEY_PAGE_DOWN:
+ url_complete_matches_selection +=
+ MAXIMUM_VISIBLE_LINES;
+ break;
+ case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_UP:
+ url_complete_matches_selection = 0;
+ break;
+ case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_DOWN:
+ url_complete_matches_selection = 65536;
+ break;
+ }
+
+ if (url_complete_matches_selection >
+ url_complete_matches_available - 1)
+ url_complete_matches_selection =
+ url_complete_matches_available - 1;
+ else if (url_complete_matches_selection < -1)
+ url_complete_matches_selection = -1;
+
+ if (old_selection == url_complete_matches_selection)
+ return false;
+
+ error = xwimp_force_redraw(dialog_url_complete,
+ 0, -(old_selection + 1) * 44,
+ 65536, -old_selection * 44);
+ if (error) {
+ LOG("xwimp_force_redraw: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ error = xwimp_force_redraw(dialog_url_complete,
+ 0, -(url_complete_matches_selection + 1) * 44,
+ 65536, -url_complete_matches_selection * 44);
+ if (error) {
+ LOG("xwimp_force_redraw: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ if (old_selection == -1) {
+ free(url_complete_original_url);
+ url_complete_original_url = malloc(strlen(url) + 1);
+ if (!url_complete_original_url)
+ return false;
+ strcpy(url_complete_original_url, url);
+ }
+
+ if (url_complete_matches_selection == -1) {
+ ro_toolbar_set_url(toolbar,
+ url_complete_original_url, true, false);
+ } else {
+ ro_toolbar_set_url(toolbar,
+ nsurl_access(url_complete_matches[
+ url_complete_matches_selection]),
+ true, false);
+ free(url_complete_matched_string);
+ url_complete_matched_string = strdup(nsurl_access(
+ url_complete_matches[
+ url_complete_matches_selection]));
+ }
+ url_complete_keypress_selection = url_complete_matches_selection;
+
+ /* auto-scroll */
+ state.w = dialog_url_complete;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return true;
+ }
+
+ if (state.yscroll < -(url_complete_matches_selection * 44))
+ state.yscroll = -(url_complete_matches_selection * 44);
+ height = state.visible.y1 - state.visible.y0;
+ if (state.yscroll - height >
+ -((url_complete_matches_selection + 1) * 44))
+ state.yscroll =
+ -((url_complete_matches_selection + 1) * 44) + height;
+
+ error = xwimp_open_window(PTR_WIMP_OPEN(&state));
+ if (error) {
+ LOG("xwimp_open_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return true;
+ }
+
+ return true;
+}
+
+
+/**
+ * Callback function for urldb_iterate_partial
+ *
+ * \param url URL which matches
+ * \param data Data associated with URL
+ * \return true to continue iteration, false otherwise
+ */
+
+bool url_complete_callback(nsurl *url, const struct url_data *data)
+{
+ nsurl **array_extend;
+
+ /* Ignore unvisited URLs */
+ if (data->visits == 0)
+ return true;
+
+ url_complete_matches_available++;
+
+ if (url_complete_matches_available >
+ url_complete_matches_allocated) {
+
+ array_extend = (nsurl **)realloc(url_complete_matches,
+ (url_complete_matches_allocated + 64) *
+ sizeof(nsurl *));
+ if (!array_extend) {
+ url_complete_memory_exhausted = true;
+ return false;
+ }
+ url_complete_matches = array_extend;
+ url_complete_matches_allocated += 64;
+ }
+
+ url_complete_matches[url_complete_matches_available - 1] = url;
+
+ return true;
+}
+
+
+/* This is an exported interface documented in url_complete.h */
+
+void ro_gui_url_complete_resize(struct toolbar *toolbar, wimp_open *open)
+{
+ os_box extent = { 0, 0, 0, 0 };
+ os_box url_extent;
+ wimp_window_state toolbar_state;
+ wimp_window_state state;
+ os_error *error;
+ int lines;
+ int scroll_v = 0;
+
+ /* only react to our window */
+ if (open->w != url_complete_parent)
+ return;
+
+ /* if there is no toolbar, or there is no URL bar shown,
+ * or there are no URL matches, close it */
+ if (!ro_toolbar_get_display_url(toolbar) ||
+ (!url_complete_matches) ||
+ (url_complete_matches_available == 0)) {
+ ro_gui_url_complete_close();
+ return;
+ }
+
+ /* get our current auto-complete window state for the scroll values */
+ state.w = dialog_url_complete;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ if (url_complete_matches_reset)
+ state.yscroll = 0;
+
+ /* move the window to the correct position */
+ toolbar_state.w = ro_toolbar_get_window(toolbar);
+ error = xwimp_get_window_state(&toolbar_state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ if (!ro_toolbar_get_url_field_extent(toolbar, &url_extent)) {
+ LOG("Failed to read URL field extent.");
+ return;
+ }
+
+ lines = url_complete_matches_available;
+ extent.y0 = -(lines * 44);
+ extent.x1 = 65536;
+ error = xwimp_set_extent(dialog_url_complete, &extent);
+ if (error) {
+ LOG("xwimp_set_extent: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ state.next = open->next;
+ state.flags &= ~wimp_WINDOW_VSCROLL;
+ state.flags &= ~(4095 << 16); /* clear bits 16-27 */
+ if (lines > MAXIMUM_VISIBLE_LINES) {
+ lines = MAXIMUM_VISIBLE_LINES;
+ scroll_v = ro_get_vscroll_width(NULL) - 2;
+ state.flags |= wimp_WINDOW_VSCROLL;
+ }
+ state.visible.x0 = open->visible.x0 + 2 + url_extent.x0;
+ state.visible.x1 = open->visible.x0 - 2 + url_extent.x1 - scroll_v;
+ state.visible.y1 = open->visible.y1 - url_extent.y1 + 2;
+ state.visible.y0 = state.visible.y1 - (lines * 44);
+ if (state.visible.x1 + scroll_v > toolbar_state.visible.x1)
+ state.visible.x1 = toolbar_state.visible.x1 - scroll_v;
+ if (state.visible.x1 - state.visible.x0 < 0) {
+ error = xwimp_close_window(dialog_url_complete);
+ if (error) {
+ LOG("xwimp_close_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ } else {
+ error = xwimp_open_window_nested_with_flags(&state,
+ (wimp_w)-1, 0);
+ if (error) {
+ LOG("xwimp_open_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ open->next = dialog_url_complete;
+ }
+}
+
+
+/* This is an exported interface documented in url_complete.h */
+
+bool ro_gui_url_complete_close(void)
+{
+ os_error *error;
+ bool currently_open;
+
+ /* There used to be a check here to see if the icon clicked was the
+ * URL text field in the toolbar. Since this only applied to clicks
+ * originating from the toolbar module following the restructuring,
+ * and this check was better done within the toolbar, it has been
+ * removed from this function and the associated parameters removed.
+ */
+
+ currently_open = ((url_complete_parent != NULL) &&
+ (url_complete_matches_available > 0));
+
+ free(url_complete_matches);
+ free(url_complete_matched_string);
+ free(url_complete_original_url);
+ url_complete_matches = NULL;
+ url_complete_matched_string = NULL;
+ url_complete_original_url = NULL;
+ url_complete_matches_allocated = 0;
+ url_complete_matches_available = 0;
+ url_complete_keypress_selection = -1;
+ url_complete_matches_selection = -1;
+ url_complete_parent = 0;
+
+ error = xwimp_close_window(dialog_url_complete);
+ if (error) {
+ LOG("xwimp_close_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ return currently_open;
+}
+
+
+/* This is an exported interface documented in url_complete.h */
+
+void ro_gui_url_complete_redraw(wimp_draw *redraw)
+{
+ osbool more;
+ os_error *error;
+ int line;
+ const struct url_data *data;
+ int type;
+
+ /* initialise our icon */
+ url_complete_icon.flags = wimp_ICON_INDIRECTED | wimp_ICON_VCENTRED |
+ wimp_ICON_TEXT | wimp_ICON_FILLED |
+ (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) |
+ (wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT);
+ url_complete_icon.extent.x0 = 50;
+ url_complete_icon.extent.x1 = 16384;
+ url_complete_icon.data.indirected_text.validation =
+ url_complete_icon_null;
+ url_complete_sprite.flags = wimp_ICON_TEXT | wimp_ICON_SPRITE |
+ wimp_ICON_INDIRECTED | wimp_ICON_FILLED |
+ wimp_ICON_HCENTRED | wimp_ICON_VCENTRED;
+ url_complete_sprite.extent.x0 = 0;
+ url_complete_sprite.extent.x1 = 50;
+ url_complete_sprite.data.indirected_text.text =
+ url_complete_icon_null;
+ url_complete_sprite.data.indirected_text.validation =
+ url_complete_icon_sprite;
+ url_complete_sprite.data.indirected_text.size = 1;
+
+ /* no matches? no redraw */
+ if (!url_complete_matches) {
+ LOG("Attempt to redraw with no matches made");
+ /* Fill is never used, so make it something obvious */
+ ro_gui_user_redraw(redraw, false, os_COLOUR_BLACK);
+ return;
+ }
+
+ /* redraw */
+ more = wimp_redraw_window(redraw);
+ while (more) {
+ int first_line, last_line;
+ int origin_y = redraw->box.y1 - redraw->yscroll;
+ int clip_y0 = redraw->clip.y0 - origin_y;
+ int clip_y1 = redraw->clip.y1 - origin_y;
+
+ first_line = (-clip_y1) / 44;
+ last_line = (-clip_y0 + 43) / 44;
+
+ for (line = first_line; line < last_line; line++) {
+ if (line == url_complete_matches_selection)
+ url_complete_icon.flags |=
+ wimp_ICON_SELECTED;
+ else
+ url_complete_icon.flags &=
+ ~wimp_ICON_SELECTED;
+ url_complete_icon.extent.y1 = -line * 44;
+ url_complete_icon.extent.y0 = -(line + 1) * 44;
+ url_complete_icon.data.indirected_text.text =
+ (char *)nsurl_access(
+ url_complete_matches[line]);
+ url_complete_icon.data.indirected_text.size =
+ nsurl_length(
+ url_complete_matches[line]);
+
+ error = xwimp_plot_icon(&url_complete_icon);
+ if (error) {
+ LOG("xwimp_plot_icon: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ data = urldb_get_url_data(url_complete_matches[line]);
+ if (data)
+ type = ro_content_filetype_from_type(
+ data->type);
+ else
+ type = 0;
+
+ sprintf(url_complete_icon_sprite, "Ssmall_%.3x",
+ type);
+
+ if (!ro_gui_wimp_sprite_exists(
+ url_complete_icon_sprite + 1))
+ sprintf(url_complete_icon_sprite,
+ "Ssmall_xxx");
+ url_complete_sprite.extent.y1 = -line * 44;
+ url_complete_sprite.extent.y0 = -(line + 1) * 44;
+ error = xwimp_plot_icon(&url_complete_sprite);
+ if (error) {
+ LOG("xwimp_plot_icon: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ }
+ more = wimp_get_rectangle(redraw);
+ }
+}
+
+
+/* This is an exported interface documented in url_complete.h */
+
+void ro_gui_url_complete_entering(wimp_entering *entering)
+{
+ ro_mouse_track_start(NULL, ro_gui_url_complete_mouse_at, NULL);
+}
+
+
+/**
+ * Handle mouse movement over the URL completion window.
+ *
+ * \param *pointer The pointer state
+ * \param *data NULL data pointer expected by mouse tracker
+ */
+
+void ro_gui_url_complete_mouse_at(wimp_pointer *pointer, void *data)
+{
+ wimp_mouse_state current;
+
+ current = pointer->buttons;
+ pointer->buttons = 0;
+ ro_gui_url_complete_click(pointer);
+ pointer->buttons = current;
+}
+
+
+/* This is an exported interface documented in url_complete.h */
+
+bool ro_gui_url_complete_click(wimp_pointer *pointer)
+{
+ wimp_window_state state;
+ os_error *error;
+ int selection;
+ struct gui_window *g;
+
+ if ((mouse_x == pointer->pos.x) && (mouse_y == pointer->pos.y) &&
+ (!pointer->buttons))
+ return false;
+
+ mouse_x = pointer->pos.x;
+ mouse_y = pointer->pos.y;
+
+ state.w = dialog_url_complete;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ selection = (state.visible.y1 - pointer->pos.y - state.yscroll) / 44;
+ if (selection != url_complete_matches_selection) {
+ int old_selection;
+
+ if (url_complete_matches_selection == -1) {
+ const char *url;
+
+ g = ro_gui_window_lookup(url_complete_parent);
+ if (!g)
+ return false;
+ url = ro_toolbar_get_url(g->toolbar);
+ free(url_complete_original_url);
+ url_complete_original_url = strdup(url);
+ if (!url_complete_original_url)
+ return false;
+ }
+ old_selection = url_complete_matches_selection;
+ url_complete_matches_selection = selection;
+ error = xwimp_force_redraw(dialog_url_complete,
+ 0, -(old_selection + 1) * 44,
+ 65536, -old_selection * 44);
+ if (error) {
+ LOG("xwimp_force_redraw: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ error = xwimp_force_redraw(dialog_url_complete,
+ 0, -(url_complete_matches_selection + 1) * 44,
+ 65536, -url_complete_matches_selection * 44);
+ if (error) {
+ LOG("xwimp_force_redraw: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ }
+ if (!pointer->buttons)
+ return true;
+
+ /* find owning window */
+ g = ro_gui_window_lookup(url_complete_parent);
+ if (!g)
+ return false;
+
+ /* Select sets the text and launches */
+ if (pointer->buttons == wimp_CLICK_SELECT) {
+ ro_toolbar_set_url(g->toolbar,
+ nsurl_access(url_complete_matches[
+ url_complete_matches_selection]),
+ true, false);
+
+ /** \todo The interaction of components here is hideous */
+ /* Do NOT make any attempt to use any of the global url
+ * completion variables after this call to browser_window_navigate.
+ * They will be invalidated by (at least):
+ * + ro_gui_window_set_url
+ * + destruction of (i)frames within the current page
+ * Any attempt to use them will probably result in a crash.
+ */
+
+ browser_window_navigate(g->bw,
+ url_complete_matches[url_complete_matches_selection],
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+
+ ro_gui_url_complete_close();
+
+ /* Adjust just sets the text */
+ } else if (pointer->buttons == wimp_CLICK_ADJUST) {
+ ro_toolbar_set_url(g->toolbar,
+ nsurl_access(url_complete_matches[
+ url_complete_matches_selection]),
+ true, false);
+ ro_gui_url_complete_keypress(g->toolbar, 0);
+ }
+ return true;
+}
+
diff --git a/frontends/riscos/url_complete.h b/frontends/riscos/url_complete.h
new file mode 100644
index 000000000..6a4660e4a
--- /dev/null
+++ b/frontends/riscos/url_complete.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Central repository for URL data (interface).
+ */
+
+#ifndef _NETSURF_RISCOS_URLCOMPLETE_H_
+#define _NETSURF_RISCOS_URLCOMPLETE_H_
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include "oslib/wimp.h"
+
+struct gui_window;
+
+/**
+ * Should be called when the caret is placed into a URL completion icon.
+ *
+ * \param *toolbar The toolbar to initialise URL completion for.
+ */
+
+void ro_gui_url_complete_start(struct toolbar *toolbar);
+
+
+/**
+ * Handles a keypress for URL completion
+ *
+ * \param *toolbar The toolbar to be updated.
+ * \param key the key pressed (as UTF32 code or
+ * wimp key + bit31 set)
+ * \return true to indicate keypress handled; else false.
+ */
+
+bool ro_gui_url_complete_keypress(struct toolbar *toolbar, uint32_t key);
+
+
+/**
+ * Move and resize the url completion window to match the toolbar.
+ *
+ * \param *toolbar The toolbar to update
+ * \param *open the wimp_open request (updated on exit)
+ */
+
+void ro_gui_url_complete_resize(struct toolbar *toolbar, wimp_open *open);
+
+
+/**
+ * Try to close the current url completion window
+ *
+ * \return whether the window was closed
+ */
+
+bool ro_gui_url_complete_close(void);
+
+
+/**
+ * Redraws a section of the URL completion window
+ *
+ * \param redraw the area to redraw
+ */
+
+void ro_gui_url_complete_redraw(wimp_draw *redraw);
+
+
+/**
+ * Handle the pointer entering the URL completion window.
+ *
+ * \param *entering The pointer entering data block.
+ */
+
+void ro_gui_url_complete_entering(wimp_entering *entering);
+
+
+/**
+ * Handle mouse clicks in the URL completion window.
+ *
+ * \param pointer the pointer state
+ * \return whether the click was handled
+ */
+
+bool ro_gui_url_complete_click(wimp_pointer *pointer);
+
+#endif
+
diff --git a/frontends/riscos/url_protocol.c b/frontends/riscos/url_protocol.c
new file mode 100644
index 000000000..2b9ef3556
--- /dev/null
+++ b/frontends/riscos/url_protocol.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
+ * Copyright 2003 Rob Jackson <jacko@xms.ms>
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * ANT URL launching protocol (implementation).
+ *
+ * See http://www.vigay.com/inet/inet_url.html
+ */
+
+#include "utils/config.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <oslib/inetsuite.h>
+#include <oslib/wimp.h>
+
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/nsurl.h"
+#include "utils/config.h"
+#include "content/fetch.h"
+#include "desktop/browser.h"
+
+#include "riscos/gui.h"
+#include "riscos/uri.h"
+#include "riscos/url_protocol.h"
+
+/**
+ * Handle a Message_InetSuiteOpenURL.
+ */
+
+void ro_url_message_received(wimp_message *message)
+{
+ char *url;
+ int i;
+ inetsuite_message_open_url *url_message =
+ (inetsuite_message_open_url*) &message->data;
+ os_error *error;
+ nsurl *nsurl;
+ nserror errorns;
+
+ /* If the url_message->indirect.tag is non-zero,
+ * then the message data is contained within the message block.
+ */
+ if (url_message->indirect.tag != 0) {
+ url = strndup(url_message->url, 236);
+ if (!url) {
+ ro_warn_user("NoMemory", 0);
+ return;
+ }
+ /* terminate at first control character */
+ for (i = 0; !iscntrl(url[i]); i++)
+ ;
+ url[i] = 0;
+
+ } else {
+ if (!url_message->indirect.url.offset) {
+ LOG("no URL in message");
+ return;
+ }
+ if (28 < message->size &&
+ url_message->indirect.body_file.offset) {
+ LOG("POST for URL message not implemented");
+ return;
+ }
+ if (url_message->indirect.url.offset < 28 ||
+ 236 <= url_message->indirect.url.offset) {
+ LOG("external pointers in URL message unimplemented");
+ /* these messages have never been seen in the wild,
+ * and there is the problem of invalid addresses which
+ * would cause an abort */
+ return;
+ }
+
+ url = strndup((char *) url_message +
+ url_message->indirect.url.offset,
+ 236 - url_message->indirect.url.offset);
+ if (!url) {
+ ro_warn_user("NoMemory", 0);
+ return;
+ }
+ for (i = 0; !iscntrl(url[i]); i++)
+ ;
+ url[i] = 0;
+ }
+
+ if (nsurl_create(url, &nsurl) != NSERROR_OK) {
+ free(url);
+ return;
+ }
+
+ if (!fetch_can_fetch(nsurl)) {
+ nsurl_unref(nsurl);
+ free(url);
+ return;
+ }
+
+ free(url);
+
+ /* send ack */
+ message->your_ref = message->my_ref;
+ error = xwimp_send_message(wimp_USER_MESSAGE_ACKNOWLEDGE, message,
+ message->sender);
+ if (error) {
+ LOG("xwimp_send_message: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ /* create new browser window */
+ errorns = browser_window_create(BW_CREATE_HISTORY,
+ nsurl,
+ NULL,
+ NULL,
+ NULL);
+
+
+ nsurl_unref(nsurl);
+ if (errorns != NSERROR_OK) {
+ ro_warn_user(messages_get_errorcode(errorns), 0);
+ }
+}
+
+
+/**
+ * Broadcast an ANT URL message.
+ */
+
+void ro_url_broadcast(const char *url)
+{
+ inetsuite_full_message_open_url_direct message;
+ os_error *error;
+ int len = strlen(url) + 1;
+
+ /* If URL is too long, then forget ANT and try URI, instead */
+ if (236 < len) {
+ ro_uri_launch(url);
+ return;
+ }
+
+ message.size = ((20+len+3) & ~3);
+ message.your_ref = 0;
+ message.action = message_INET_SUITE_OPEN_URL;
+ strncpy(message.url, url, 235);
+ message.url[235] = 0;
+ error = xwimp_send_message(wimp_USER_MESSAGE_RECORDED,
+ (wimp_message *) &message, 0);
+ if (error) {
+ LOG("xwimp_send_message: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+}
+
+
+/**
+ * Launch a program to handle an URL, using the ANT protocol
+ * Alias$URLOpen_ system.
+ */
+
+void ro_url_load(const char *url)
+{
+ char *command;
+ char *colon;
+ os_error *error;
+
+ colon = strchr(url, ':');
+ if (!colon) {
+ LOG("invalid url '%s'", url);
+ return;
+ }
+
+ command = malloc(40 + (colon - url) + strlen(url));
+ if (!command) {
+ ro_warn_user("NoMemory", 0);
+ return;
+ }
+
+ sprintf(command, "Alias$URLOpen_%.*s", (int) (colon - url), url);
+ if (!getenv(command)) {
+ free(command);
+ return;
+ }
+
+ sprintf(command, "URLOpen_%.*s %s", (int) (colon - url), url, url);
+
+ error = xwimp_start_task(command, 0);
+ if (error) {
+ LOG("xwimp_start_task: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ free(command);
+}
+
+
+/**
+ * Handle a bounced Message_InetSuiteOpenURL.
+ */
+
+void ro_url_bounce(wimp_message *message)
+{
+ inetsuite_message_open_url *url_message =
+ (inetsuite_message_open_url*) &message->data;
+
+ /* ant broadcast bounced -> try uri broadcast / load */
+ ro_uri_launch(url_message->url);
+}
+
diff --git a/frontends/riscos/url_protocol.h b/frontends/riscos/url_protocol.h
new file mode 100644
index 000000000..c066981f7
--- /dev/null
+++ b/frontends/riscos/url_protocol.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * ANT URL launching protocol (interface).
+ */
+
+#ifndef _NETSURF_RISCOS_URL_H_
+#define _NETSURF_RISCOS_URL_H_
+
+#include "utils/config.h"
+
+#include "oslib/wimp.h"
+
+void ro_url_message_received(wimp_message *message);
+void ro_url_broadcast(const char *url);
+void ro_url_load(const char *url);
+void ro_url_bounce(wimp_message *message);
+
+#endif
diff --git a/frontends/riscos/url_suggest.c b/frontends/riscos/url_suggest.c
new file mode 100644
index 000000000..3f6b6b54d
--- /dev/null
+++ b/frontends/riscos/url_suggest.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * URL Suggestion Menu (implementation).
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "oslib/wimp.h"
+#include "content/content_type.h"
+#include "content/urldb.h"
+
+#include "riscos/menus.h"
+#include "riscos/url_suggest.h"
+#include "utils/messages.h"
+
+struct url_suggest_item {
+ const char *url; /*< The URL being stored. */
+ unsigned int weight; /*< A weight assigned to the URL. */
+ struct url_suggest_item *next; /*< The next URL in the list. */
+};
+
+static bool ro_gui_url_suggest_callback(nsurl *url,
+ const struct url_data *data);
+
+static int suggest_entries;
+static time_t suggest_time;
+static struct url_suggest_item *suggest_list;
+
+static wimp_MENU(URL_SUGGEST_MAX_URLS) url_suggest_menu_block;
+wimp_menu *ro_gui_url_suggest_menu = (wimp_menu *) &url_suggest_menu_block;
+
+
+/**
+ * Initialise the URL suggestion menu. This MUST be called before anything
+ * tries to use the URL menu.
+ *
+ * \return true if initialisation was OK; else false.
+ */
+
+bool ro_gui_url_suggest_init(void)
+{
+ ro_gui_url_suggest_menu->title_data.indirected_text.text =
+ (char *) messages_get("URLSuggest");
+ ro_gui_menu_init_structure((wimp_menu *) ro_gui_url_suggest_menu,
+ URL_SUGGEST_MAX_URLS);
+
+ suggest_entries = 0;
+
+ return true;
+}
+
+
+/**
+ * Check if there is a URL suggestion menu available for use.
+ *
+ * \todo Ideally this should be able to decide if there's a menu
+ * available without actually having to build it all.
+ *
+ * \return true if the menu has entries; else false.
+ */
+
+bool ro_gui_url_suggest_get_menu_available(void)
+{
+ return ro_gui_url_suggest_prepare_menu();
+}
+
+
+/**
+ * Builds the URL suggestion menu. This is called by ro_gui_menu_create() when
+ * it is asked to display the url_suggest_menu.
+ *
+ * /return true if the menu has entries; else false.
+ */
+
+bool ro_gui_url_suggest_prepare_menu(void)
+{
+ struct url_suggest_item *list, *next;
+
+ /* Fetch the URLs we want to include from URLdb. */
+
+ suggest_entries = 0;
+ suggest_list = NULL;
+ suggest_time = time(NULL);
+
+ urldb_iterate_entries(ro_gui_url_suggest_callback);
+
+ /* If any menu entries were found, put them into the menu. The list
+ * is in reverse order, last to first, so the menu is filled backwards.
+ * Entries from the list are freed as we go.
+ */
+
+ assert(suggest_entries <= URL_SUGGEST_MAX_URLS);
+
+ if (suggest_entries > 0) {
+ int i = suggest_entries;
+
+ list = suggest_list;
+ suggest_list = NULL;
+
+ while (list != NULL && i > 0) {
+ i--;
+
+ ro_gui_url_suggest_menu->entries[i].menu_flags = 0;
+ ro_gui_url_suggest_menu->
+ entries[i].data.indirected_text.text =
+ (char *) list->url;
+ ro_gui_url_suggest_menu->
+ entries[i].data.indirected_text.size =
+ strlen(list->url) + 1;
+
+ next = list->next;
+ free(list);
+ list = next;
+ }
+
+ assert(i == 0);
+
+ ro_gui_url_suggest_menu->entries[0].menu_flags |=
+ wimp_MENU_TITLE_INDIRECTED;
+ ro_gui_url_suggest_menu->
+ entries[suggest_entries - 1].menu_flags |=
+ wimp_MENU_LAST;
+
+ return true;
+ }
+
+ return false;
+}
+
+
+/**
+ * Callback function for urldb_iterate_entries
+ *
+ * \param url URL which matches
+ * \param data Data associated with URL
+ * \return true to continue iteration, false otherwise
+ */
+
+bool ro_gui_url_suggest_callback(nsurl *url, const struct url_data *data)
+{
+ int count;
+ unsigned int weight;
+ struct url_suggest_item **list, *new;
+
+ /* Ignore unvisited URLs, and those that don't apply to HTML or Text. */
+
+ if (data->visits == 0 || (data->type != CONTENT_HTML &&
+ data->type != CONTENT_TEXTPLAIN))
+ return true;
+
+ /* Calculate a weight for the URL. */
+
+ weight = (suggest_time - data->last_visit) / data->visits;
+
+ /* Hunt through those URLs already found to see if we want to add
+ * this one. Smaller weights carry higher priority.
+ *
+ * The list is sorted into reverse order, so that lowest weight
+ * items are nearest the head. Therefore, items are dropped from
+ * the head, making things simpler.
+ */
+
+ list = &suggest_list;
+ count = 0;
+
+ while (*list != NULL && weight < (*list)->weight) {
+ list = &((*list)->next);
+ count++;
+ }
+
+ if (count > 0 || suggest_entries < URL_SUGGEST_MAX_URLS) {
+ new = (struct url_suggest_item *)
+ malloc(sizeof(struct url_suggest_item));
+
+ if (new != NULL) {
+ suggest_entries++;
+ /* TODO: keeping pointers to URLdb data is bad.
+ * should be nsurl_ref(url) or
+ * take a copy of the string. */
+ new->url = nsurl_access(url);
+ new->weight = weight;
+ new->next = *list;
+
+ *list = new;
+ }
+ }
+
+ /* If adding the URL gave us too many menu items, drop the lowest
+ * priority ones until the list is the right length again.
+ */
+
+ while (suggest_list != NULL && suggest_entries > URL_SUGGEST_MAX_URLS) {
+ struct url_suggest_item *old = suggest_list;
+ suggest_list = suggest_list->next;
+
+ free(old);
+ suggest_entries--;
+ }
+
+ return true;
+}
+
+
+/**
+ * Process a selection from the URL Suggest menu.
+ *
+ * \param *selection The menu selection.
+ * \return Pointer to the URL that was selected, or NULL for none.
+ */
+
+const char *ro_gui_url_suggest_get_selection(wimp_selection *selection)
+{
+ const char *url = NULL;
+
+ if (selection->items[0] >= 0)
+ url = ro_gui_url_suggest_menu->entries[selection->items[0]].
+ data.indirected_text.text;
+
+ return url;
+}
diff --git a/frontends/riscos/url_suggest.h b/frontends/riscos/url_suggest.h
new file mode 100644
index 000000000..738cb9bf7
--- /dev/null
+++ b/frontends/riscos/url_suggest.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * URL Suggestion Menu (interface).
+ */
+
+#ifndef _NETSURF_RISCOS_URL_SUGGEST_H_
+#define _NETSURF_RISCOS_URL_SUGGEST_H_
+
+#include "oslib/wimp.h"
+
+#define URL_SUGGEST_MAX_URLS 16
+
+extern wimp_menu *ro_gui_url_suggest_menu;
+
+bool ro_gui_url_suggest_init(void);
+bool ro_gui_url_suggest_get_menu_available(void);
+bool ro_gui_url_suggest_prepare_menu(void);
+const char *ro_gui_url_suggest_get_selection(wimp_selection *selection);
+
+#endif
+
diff --git a/frontends/riscos/wimp.c b/frontends/riscos/wimp.c
new file mode 100644
index 000000000..2579c672e
--- /dev/null
+++ b/frontends/riscos/wimp.c
@@ -0,0 +1,1134 @@
+/*
+ * Copyright 2004, 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2008 John Tytgat <joty@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * General RISC OS WIMP/OS library functions (implementation).
+ */
+
+#include <assert.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "oslib/colourtrans.h"
+#include "oslib/os.h"
+#include "oslib/osfile.h"
+#include "oslib/wimp.h"
+#include "oslib/wimpextend.h"
+#include "oslib/wimpspriteop.h"
+
+#include "utils/log.h"
+#include "utils/utf8.h"
+
+#include "riscos/gui.h"
+#include "riscos/oslib_pre7.h"
+#include "riscos/wimp.h"
+#include "riscos/ucstables.h"
+
+
+static void ro_gui_wimp_cache_furniture_sizes(wimp_w w);
+static size_t ro_gui_strlen(const char *str);
+static int ro_gui_strncmp(const char *s1, const char *s2, size_t len);
+
+static wimpextend_furniture_sizes furniture_sizes;
+static wimp_w furniture_window = NULL;
+
+/**
+ * Gets the horizontal scrollbar height
+ *
+ * \param w the window to read (or NULL to read a cached value)
+ */
+int ro_get_hscroll_height(wimp_w w)
+{
+ ro_gui_wimp_cache_furniture_sizes(w);
+ return furniture_sizes.border_widths.y0;
+}
+
+
+/**
+ * Gets the vertical scrollbar width
+ *
+ * \param w the window to read (or NULL to read a cached value)
+ */
+int ro_get_vscroll_width(wimp_w w)
+{
+ ro_gui_wimp_cache_furniture_sizes(w);
+ return furniture_sizes.border_widths.x1;
+}
+
+
+/**
+ * Gets the title bar height
+ *
+ * \param w the window to read (or NULL to read a cached value)
+ */
+int ro_get_title_height(wimp_w w)
+{
+ ro_gui_wimp_cache_furniture_sizes(w);
+ return furniture_sizes.border_widths.y1;
+}
+
+/**
+ * Caches window furniture information
+ *
+ * \param w the window to cache information from
+ * \return true on success, false on error (default values cached)
+ */
+void ro_gui_wimp_cache_furniture_sizes(wimp_w w)
+{
+ os_error *error;
+
+ if (furniture_window == w)
+ return;
+ furniture_window = w;
+ furniture_sizes.w = w;
+ furniture_sizes.border_widths.y0 = 40;
+ furniture_sizes.border_widths.x1 = 40;
+ error = xwimpextend_get_furniture_sizes(&furniture_sizes);
+ if (error) {
+ LOG("xwimpextend_get_furniture_sizes: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+}
+
+
+/**
+ * Reads a modes EIG factors.
+ *
+ * \param[in] mode mode to read EIG factors for, or -1 for current
+ * \param[out] xeig The x eig value
+ * \param[out] yeig The y eig value
+ * \return true on success else false.
+ */
+bool ro_gui_wimp_read_eig_factors(os_mode mode, int *xeig, int *yeig)
+{
+ os_error *error;
+
+ error = xos_read_mode_variable(mode, os_MODEVAR_XEIG_FACTOR, xeig, 0);
+ if (error) {
+ LOG("xos_read_mode_variable: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ return false;
+ }
+ error = xos_read_mode_variable(mode, os_MODEVAR_YEIG_FACTOR, yeig, 0);
+ if (error) {
+ LOG("xos_read_mode_variable: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ return false;
+ }
+ return true;
+}
+
+
+/**
+ * Converts the supplied os_coord from OS units to pixels.
+ *
+ * \param os_units values to convert
+ * \param mode mode to use EIG factors for, or -1 for current
+ */
+void ro_convert_os_units_to_pixels(os_coord *os_units, os_mode mode)
+{
+ int xeig = 1, yeig = 1;
+
+ ro_gui_wimp_read_eig_factors(mode, &xeig, &yeig);
+ os_units->x = ((os_units->x + (1 << xeig) - 1) >> xeig);
+ os_units->y = ((os_units->y + (1 << yeig) - 1) >> yeig);
+}
+
+
+/**
+ * Converts the supplied os_coord from pixels to OS units.
+ *
+ * \param pixels values to convert
+ * \param mode mode to use EIG factors for, or -1 for current
+ */
+void ro_convert_pixels_to_os_units(os_coord *pixels, os_mode mode)
+{
+ int xeig = 1, yeig = 1;
+
+ ro_gui_wimp_read_eig_factors(mode, &xeig, &yeig);
+ pixels->x = (pixels->x << xeig);
+ pixels->y = (pixels->y << yeig);
+}
+
+
+/**
+ * Redraws an icon
+ *
+ * \param w window handle
+ * \param i icon handle
+ */
+
+#define ro_gui_redraw_icon(w, i) xwimp_set_icon_state(w, i, 0, 0)
+
+
+/**
+ * Forces an icon to be redrawn entirely (ie not just updated).
+ *
+ * \param w window handle
+ * \param i icon handle
+ */
+void ro_gui_force_redraw_icon(wimp_w w, wimp_i i)
+{
+ wimp_icon_state ic;
+ os_error *error;
+
+ /* Get the icon data
+ */
+ ic.w = w;
+ ic.i = i;
+ error = xwimp_get_icon_state(&ic);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ error = xwimp_force_redraw(w, ic.icon.extent.x0, ic.icon.extent.y0,
+ ic.icon.extent.x1, ic.icon.extent.y1);
+ if (error) {
+ LOG("xwimp_force_redraw: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+}
+
+
+/**
+ * Read the contents of a text or sprite icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \return NUL terminated string in icon
+ *
+ * If the icon contains direct text then the returned data will
+ * be invalidated by the next call to this function. Therefore,
+ * all client calls to this function must either copy the string or
+ * ensure that this function is not called again until they are
+ * finished with the string data returned.
+ *
+ * \todo this doesn't do local encoding -> UTF-8 to match what is done in
+ * ro_gui_set_icon_string.
+ */
+const char *ro_gui_get_icon_string(wimp_w w, wimp_i i)
+{
+ static wimp_icon_state ic;
+ os_error *error;
+ char *itext;
+
+ ic.w = w;
+ ic.i = i;
+ error = xwimp_get_icon_state(&ic);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return NULL;
+ }
+ itext = (ic.icon.flags & wimp_ICON_INDIRECTED)
+ ? ic.icon.data.indirected_text.text : ic.icon.data.text;
+ /* Guarantee NUL termination. */
+ itext[ro_gui_strlen(itext)] = '\0';
+
+ return itext;
+}
+
+
+/**
+ * Set the contents of a text or sprite icon to a string.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param text NUL terminated string (copied)
+ * \param is_utf8 When true, the given string is UTF-8 encoded and will be
+ * converted to local encoding currently used by the Wimp. When false, the
+ * given string is assumed to be in local encoding in use by the Wimp.
+ */
+void ro_gui_set_icon_string(wimp_w w, wimp_i i, const char *text, bool is_utf8)
+{
+ wimp_caret caret;
+ wimp_icon_state ic;
+ os_error *error;
+ size_t old_len, new_len;
+ char *local_text = NULL;
+ const char *text_for_icon;
+ char *dst_text;
+ size_t dst_max_len;
+ unsigned int button_type;
+
+ if (is_utf8) {
+ nserror err;
+ /* convert text to local encoding */
+ err = utf8_to_local_encoding(text, 0, &local_text);
+ if (err != NSERROR_OK) {
+ /* A bad encoding should never happen, so assert this */
+ assert(err != NSERROR_BAD_ENCODING);
+ LOG("utf8_to_enc failed");
+ /* Paranoia */
+ local_text = NULL;
+ }
+ text_for_icon = local_text ? local_text : text;
+ }
+ else
+ text_for_icon = text;
+ new_len = strlen(text_for_icon);
+
+ /* get the icon data */
+ ic.w = w;
+ ic.i = i;
+ error = xwimp_get_icon_state(&ic);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ goto exit;
+ }
+
+ if (ic.icon.flags & wimp_ICON_INDIRECTED) {
+ dst_text = ic.icon.data.indirected_text.text;
+ dst_max_len = ic.icon.data.indirected_text.size;
+ }
+ else {
+ dst_text = ic.icon.data.text;
+ dst_max_len = sizeof(ic.icon.data.text);
+ }
+ old_len = ro_gui_strlen(dst_text);
+ assert(old_len < dst_max_len);
+
+ /* check that the existing text is not the same as the updated text
+ * to stop flicker */
+ if (dst_max_len) {
+ if (!ro_gui_strncmp(dst_text, text_for_icon, dst_max_len))
+ goto exit;
+
+ /* copy the text across */
+ strncpy(dst_text, text_for_icon, dst_max_len - 1);
+ dst_text[dst_max_len - 1] = '\0';
+
+ /* handle the caret being in the icon */
+ button_type = (ic.icon.flags & wimp_ICON_BUTTON_TYPE)
+ >> wimp_ICON_BUTTON_TYPE_SHIFT;
+ if ((button_type == wimp_BUTTON_WRITABLE) ||
+ (button_type == wimp_BUTTON_WRITE_CLICK_DRAG)) {
+ error = xwimp_get_caret_position(&caret);
+ if (error) {
+ LOG("xwimp_get_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ goto exit;
+ }
+ if ((caret.w == w) && (caret.i == i)) {
+ if ((size_t)caret.index > new_len
+ || (size_t)caret.index == old_len)
+ caret.index = new_len;
+ error = xwimp_set_caret_position(w, i, caret.pos.x,
+ caret.pos.y, -1, caret.index);
+ if (error) {
+ LOG("xwimp_set_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ }
+ }
+ ro_gui_redraw_icon(w, i);
+ }
+
+exit:
+ free(local_text);
+}
+
+
+/**
+ * Set the contents of an icon to a number.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param value value
+ */
+void ro_gui_set_icon_integer(wimp_w w, wimp_i i, int value)
+{
+ char buffer[20]; // Big enough for 64-bit int
+
+ setlocale(LC_NUMERIC, "");
+
+ sprintf(buffer, "%d", value);
+
+ setlocale(LC_NUMERIC, "C");
+
+ ro_gui_set_icon_string(w, i, buffer, true);
+}
+
+
+/**
+ * Set the contents of an icon to a number.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param value value to use in icon.
+ * \param decimal_places The number of decimal places to use.
+ */
+void ro_gui_set_icon_decimal(wimp_w w, wimp_i i, int value, int decimal_places)
+{
+ char buffer[20]; // Big enough for 64-bit int
+
+ setlocale(LC_NUMERIC, "");
+
+ switch (decimal_places) {
+ case 0:
+ sprintf(buffer, "%d", value);
+ break;
+ case 1:
+ sprintf(buffer, "%.1f", (float)value / 10);
+ break;
+ case 2:
+ sprintf(buffer, "%.2f", (float)value / 100);
+ break;
+ default:
+ assert(!"Unsupported decimal format");
+ break;
+ }
+
+ setlocale(LC_NUMERIC, "C");
+
+ ro_gui_set_icon_string(w, i, buffer, true);
+}
+
+
+/**
+ * Get the contents of an icon as a number.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param decimal_places number of places to show.
+ * \return value used.
+ */
+int ro_gui_get_icon_decimal(wimp_w w, wimp_i i, int decimal_places)
+{
+ double value;
+ int multiple = 1;
+
+ for (; decimal_places > 0; decimal_places--)
+ multiple *= 10;
+
+ setlocale(LC_NUMERIC, "");
+
+ value = atof(ro_gui_get_icon_string(w, i)) * multiple;
+
+ setlocale(LC_NUMERIC, "C");
+
+ return (int)value;
+}
+
+
+/**
+ * Set the selected state of an icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param state selected state
+ */
+void ro_gui_set_icon_selected_state(wimp_w w, wimp_i i, bool state)
+{
+ os_error *error;
+ if (ro_gui_get_icon_selected_state(w, i) == state) return;
+ error = xwimp_set_icon_state(w, i,
+ (state ? wimp_ICON_SELECTED : 0), wimp_ICON_SELECTED);
+ if (error) {
+ LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+}
+
+/**
+ * Gets the selected state of an icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ */
+bool ro_gui_get_icon_selected_state(wimp_w w, wimp_i i)
+{
+ os_error *error;
+ wimp_icon_state ic;
+ ic.w = w;
+ ic.i = i;
+ error = xwimp_get_icon_state(&ic);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+ return ((ic.icon.flags & wimp_ICON_SELECTED) != 0);
+}
+
+
+/**
+ * Set the shaded state of an icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param state shaded state
+ */
+void ro_gui_set_icon_shaded_state(wimp_w w, wimp_i i, bool state)
+{
+ wimp_caret caret;
+ os_error *error;
+
+ /* update the state */
+ if (ro_gui_get_icon_shaded_state(w, i) == state)
+ return;
+ error = xwimp_set_icon_state(w, i,
+ (state ? wimp_ICON_SHADED : 0), wimp_ICON_SHADED);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ if (!state)
+ return;
+
+ /* ensure the caret is not in a shaded icon */
+ error = xwimp_get_caret_position(&caret);
+ if (error) {
+ LOG("xwimp_get_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ if ((caret.w != w) || (caret.i != i))
+ return;
+ /* move the caret to the first avaiable writable */
+ if (ro_gui_set_caret_first(w))
+ return;
+ /* lose the caret */
+ error = xwimp_set_caret_position((wimp_w)-1, (wimp_i)-1, -1, -1, -1, -1);
+ if (error) {
+ LOG("xwimp_set_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+}
+
+
+/**
+ * Gets the shaded state of an icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ */
+bool ro_gui_get_icon_shaded_state(wimp_w w, wimp_i i)
+{
+ wimp_icon_state ic;
+ ic.w = w;
+ ic.i = i;
+ xwimp_get_icon_state(&ic);
+ return (ic.icon.flags & wimp_ICON_SHADED) != 0;
+}
+
+
+/**
+ * Set the deleted state of an icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param state shaded state
+ */
+void ro_gui_set_icon_deleted_state(wimp_w w, wimp_i i, bool state)
+{
+ wimp_caret caret;
+ os_error *error;
+
+ /* update the state */
+ if (ro_gui_get_icon_deleted_state(w, i) == state)
+ return;
+ error = xwimp_set_icon_state(w, i,
+ (state ? wimp_ICON_DELETED : 0), wimp_ICON_DELETED);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ if (!state)
+ return;
+
+ /* ensure the caret is not in a shaded icon */
+ error = xwimp_get_caret_position(&caret);
+ if (error) {
+ LOG("xwimp_get_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ if ((caret.w != w) || (caret.i != i))
+ return;
+ /* move the caret to the first avaiable writable */
+ if (ro_gui_set_caret_first(w))
+ return;
+ /* lose the caret */
+ error = xwimp_set_caret_position((wimp_w)-1, (wimp_i)-1, -1, -1, -1, -1);
+ if (error) {
+ LOG("xwimp_set_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+}
+
+
+/**
+ * Gets the deleted state of an icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ */
+bool ro_gui_get_icon_deleted_state(wimp_w w, wimp_i i)
+{
+ wimp_icon_state ic;
+ ic.w = w;
+ ic.i = i;
+ xwimp_get_icon_state(&ic);
+ return (ic.icon.flags & wimp_ICON_DELETED) != 0;
+}
+
+
+/**
+ * Set the button type of an icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param type button type
+ */
+void ro_gui_set_icon_button_type(wimp_w w, wimp_i i, int type)
+{
+ os_error *error;
+ error = xwimp_set_icon_state(w, i, wimp_ICON_BUTTON_TYPE,
+ (type << wimp_ICON_BUTTON_TYPE_SHIFT));
+ if (error) {
+ LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+}
+
+
+/**
+ * Set an icon's sprite
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param area sprite area containing sprite
+ * \param name name of sprite in area (in local encoding)
+ */
+void ro_gui_set_icon_sprite(wimp_w w, wimp_i i, osspriteop_area *area,
+ const char *name)
+{
+ wimp_icon_state ic;
+ os_error *error;
+
+ /* get the icon data */
+ ic.w = w;
+ ic.i = i;
+ error = xwimp_get_icon_state(&ic);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* copy the name across */
+ if (ic.icon.data.indirected_text.size) {
+ strncpy(ic.icon.data.indirected_text.text, name,
+ (unsigned int)ic.icon.data.indirected_text.size - 1);
+ ic.icon.data.indirected_text.text[
+ ic.icon.data.indirected_text.size - 1] = '\0';
+ }
+
+ ic.icon.data.indirected_sprite.area = area;
+
+ ro_gui_redraw_icon(w, i);
+}
+
+
+/**
+ * Set a window title
+ *
+ * \param w window handle
+ * \param text new title (copied)
+ */
+void ro_gui_set_window_title(wimp_w w, const char *text)
+{
+ wimp_window_info_base window;
+ os_error *error;
+ char *title_local_enc;
+ nserror err;
+
+ /* Get the window details
+ */
+ window.w = w;
+ error = xwimp_get_window_info_header_only((wimp_window_info *)&window);
+ if (error) {
+ LOG("xwimp_get_window_info: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* convert text to local encoding */
+ err = utf8_to_local_encoding(text, 0, &title_local_enc);
+ if (err != NSERROR_OK) {
+ /* A bad encoding should never happen,
+ * so assert this */
+ assert(err != NSERROR_BAD_ENCODING);
+ LOG("utf8_to_enc failed");
+ return;
+ }
+
+ /* Set the title string
+ */
+ strncpy(window.title_data.indirected_text.text, title_local_enc,
+ (unsigned int)window.title_data.indirected_text.size
+ - 1);
+ window.title_data.indirected_text.text[
+ window.title_data.indirected_text.size - 1] = '\0';
+
+ /* Redraw accordingly
+ */
+ error = xwimp_force_redraw_title(w);
+ if (error) {
+ LOG("xwimp_force_redraw_title: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ free(title_local_enc);
+}
+
+
+/**
+ * Places the caret in the first available icon
+ *
+ * \param w the window to place the caret in
+ * \return true if the caret was placed, false otherwise
+ */
+bool ro_gui_set_caret_first(wimp_w w)
+{
+ int icon, b;
+ wimp_window_state win_state;
+ wimp_window_info_base window;
+ wimp_icon_state state;
+ os_error *error;
+
+ /* check the window is open */
+ win_state.w = w;
+ error = xwimp_get_window_state(&win_state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+ if (!(win_state.flags & wimp_WINDOW_OPEN))
+ return false;
+
+ /* get the window details for the icon count */
+ window.w = w;
+ error = xwimp_get_window_info_header_only((wimp_window_info *)&window);
+ if (error) {
+ LOG("xwimp_get_window_info: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ /* work through all the icons */
+ state.w = w;
+ for (icon = 0; icon < window.icon_count; icon++) {
+ state.i = icon;
+ error = xwimp_get_icon_state(&state);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ /* ignore if it's shaded or not writable */
+ if (state.icon.flags & wimp_ICON_SHADED)
+ continue;
+ b = (state.icon.flags >> wimp_ICON_BUTTON_TYPE_SHIFT) & 0xf;
+ if ((b != wimp_BUTTON_WRITE_CLICK_DRAG) &&
+ (b != wimp_BUTTON_WRITABLE))
+ continue;
+
+ /* move the caret */
+ error = xwimp_set_caret_position(w, icon, 0, 0, -1,
+ strlen(state.icon.data.indirected_text.text));
+ if (error) {
+ LOG("xwimp_set_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Load a sprite file into memory.
+ *
+ * \param pathname file to load
+ * \return sprite area, or 0 on memory exhaustion or error and error reported
+ */
+
+osspriteop_area *ro_gui_load_sprite_file(const char *pathname)
+{
+ int len;
+ fileswitch_object_type obj_type;
+ osspriteop_area *area;
+ os_error *error;
+
+ error = xosfile_read_stamped_no_path(pathname,
+ &obj_type, 0, 0, &len, 0, 0);
+ if (error) {
+ LOG("xosfile_read_stamped_no_path: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ return 0;
+ }
+ if (obj_type != fileswitch_IS_FILE) {
+ ro_warn_user("FileError", pathname);
+ return 0;
+ }
+
+ area = malloc(len + 4);
+ if (!area) {
+ ro_warn_user("NoMemory", 0);
+ return 0;
+ }
+
+ area->size = len + 4;
+ area->sprite_count = 0;
+ area->first = 16;
+ area->used = 16;
+
+ error = xosspriteop_load_sprite_file(osspriteop_USER_AREA,
+ area, pathname);
+ if (error) {
+ LOG("xosspriteop_load_sprite_file: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ free(area);
+ return 0;
+ }
+
+ return area;
+}
+
+
+/**
+ * Check if a sprite is present in the Wimp sprite pool.
+ *
+ * \param sprite name of sprite
+ * \return true if the sprite is present
+ */
+
+bool ro_gui_wimp_sprite_exists(const char *sprite)
+{
+ static char last_sprite_found[16];
+ os_error *error;
+
+ /* make repeated calls fast */
+ if (!strncmp(sprite, last_sprite_found, sizeof(last_sprite_found)))
+ return true;
+
+ /* fallback if not known to exist */
+ error = xwimpspriteop_select_sprite(sprite, 0);
+ if (error) {
+ if (error->errnum != error_SPRITE_OP_DOESNT_EXIST) {
+ LOG("xwimpspriteop_select_sprite: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ }
+ return false;
+ }
+ snprintf(last_sprite_found, sizeof(last_sprite_found), sprite);
+ return true;
+}
+
+
+/**
+ * Locate a sprite in the Wimp sprite pool, returning a pointer to it.
+ *
+ * \param name sprite name
+ * \param sprite receives pointer to sprite if found
+ * \return error ptr iff not found
+ */
+
+os_error *ro_gui_wimp_get_sprite(const char *name, osspriteop_header **sprite)
+{
+ osspriteop_area *rom_base, *ram_base;
+ os_error *error;
+
+ error = xwimp_base_of_sprites(&rom_base, &ram_base);
+ if (error) return error;
+
+ error = xosspriteop_select_sprite(osspriteop_USER_AREA,
+ ram_base, (osspriteop_id)name, sprite);
+
+ if (error && error->errnum == error_SPRITE_OP_DOESNT_EXIST)
+ error = xosspriteop_select_sprite(osspriteop_USER_AREA,
+ rom_base, (osspriteop_id)name, sprite);
+
+ return error;
+}
+
+
+/**
+ * Get the dimensions of a sprite
+ *
+ * \param *area The sprite area to use.
+ * \param *sprite Pointer to the sprite name.
+ * \param *width Return the sprite width.
+ * \param *height Return the sprite height.
+ * \return true if successful; else false.
+ */
+
+bool ro_gui_wimp_get_sprite_dimensions(osspriteop_area *area, char *sprite,
+ int *width, int *height)
+{
+ os_error *error = NULL;
+ os_mode mode;
+ os_coord dimensions;
+
+ dimensions.x = 0;
+ dimensions.y = 0;
+
+ if (area != (osspriteop_area *) -1)
+ error = xosspriteop_read_sprite_info(osspriteop_USER_AREA,
+ area, (osspriteop_id) sprite,
+ &dimensions.x, &dimensions.y, 0, &mode);
+
+ if (error != NULL || area == (osspriteop_area *) -1)
+ error = xwimpspriteop_read_sprite_info(sprite,
+ &dimensions.x, &dimensions.y, 0, &mode);
+
+ if (error == NULL) {
+ ro_convert_pixels_to_os_units(&dimensions, mode);
+ if (width != NULL)
+ *width = dimensions.x;
+ if (height != NULL)
+ *height = dimensions.y;
+ } else if (error->errnum != error_SPRITE_OP_DOESNT_EXIST) {
+ LOG("xosspriteop_read_sprite_info: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ return false;
+ }
+
+ return true;
+}
+
+
+/**
+ * Performs simple user redraw for a window.
+ *
+ * \param redraw wimp draw
+ * \param user_fill whether to fill the redraw area
+ * \param user_colour the colour to use when filling
+ */
+
+void ro_gui_user_redraw(wimp_draw *redraw, bool user_fill,
+ os_colour user_colour)
+{
+ os_error *error;
+ osbool more;
+
+ error = xwimp_redraw_window(redraw, &more);
+ if (error) {
+ LOG("xwimp_redraw_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ while (more) {
+ if (user_fill) {
+ error = xcolourtrans_set_gcol(user_colour,
+ colourtrans_SET_BG_GCOL,
+ os_ACTION_OVERWRITE, 0, 0);
+ if (error) {
+ LOG("xcolourtrans_set_gcol: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ }
+ os_clg();
+ }
+ error = xwimp_get_rectangle(redraw, &more);
+ if (error) {
+ LOG("xwimp_get_rectangle: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ }
+}
+
+
+/**
+ * Sets whether a piece of window furniture is present for a window.
+ *
+ * \param w the window to modify
+ * \param bic_mask the furniture flags to clear
+ * \param xor_mask the furniture flags to toggle
+ */
+void ro_gui_wimp_update_window_furniture(wimp_w w, wimp_window_flags bic_mask,
+ wimp_window_flags xor_mask)
+{
+ wimp_window_state state;
+ wimp_w parent;
+ bits linkage;
+ os_error *error;
+ bool open;
+
+ state.w = w;
+ error = xwimp_get_window_state_and_nesting(&state, &parent, &linkage);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ open = state.flags & wimp_WINDOW_OPEN;
+ state.flags &= ~(63 << 16); /* clear bits 16-21 */
+ state.flags &= ~bic_mask;
+ state.flags ^= xor_mask;
+ if (!open)
+ state.next = wimp_HIDDEN;
+ error = xwimp_open_window_nested_with_flags(&state, parent, linkage);
+ if (error) {
+ LOG("xwimp_open_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ if (!open) {
+ error = xwimp_close_window(w);
+ if (error) {
+ LOG("xwimp_close_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ }
+}
+
+
+/**
+ * Checks whether a piece of window furniture is present for a window.
+ *
+ * \param w the window to modify
+ * \param mask the furniture flags to check
+ */
+bool ro_gui_wimp_check_window_furniture(wimp_w w, wimp_window_flags mask)
+{
+ wimp_window_state state;
+ os_error *error;
+
+ state.w = w;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+ return state.flags & mask;
+}
+
+/**
+ * RO GUI-specific strlen, for control character terminated strings
+ *
+ * \param str The string to measure the length of
+ * \return The length of the string
+ */
+size_t ro_gui_strlen(const char *str)
+{
+ const char *str_begin;
+
+ if (str == NULL)
+ return 0;
+
+ for (str_begin = str; *str++ >= ' '; /* */)
+ /* */;
+
+ return str - str_begin - 1;
+}
+
+/**
+ * RO GUI-specific strncmp, for control character terminated strings
+ *
+ * \param s1 The first string for comparison
+ * \param s2 The second string for comparison
+ * \param len Maximum number of bytes to be checked
+ * \return 0 for equal strings up to len bytes; pos for s1 being bigger than
+ * s2; neg for s1 being smaller than s2.
+ */
+int ro_gui_strncmp(const char *s1, const char *s2, size_t len)
+{
+ while (len--) {
+ char c1 = *s1++;
+ char c2 = *s2++;
+ if (c1 < ' ' || c2 < ' ')
+ return (c1 < ' ' ? 0 : c1) - (c2 < ' ' ? 0 : c2);
+ int diff = c1 - c2;
+ if (diff)
+ return diff;
+ }
+ return 0;
+}
+
+
+/**
+ * Generic window scroll event handler.
+ *
+ * \param *scroll Pointer to Scroll Event block.
+ */
+
+void ro_gui_scroll(wimp_scroll *scroll)
+{
+ os_error *error;
+ int x = scroll->visible.x1 - scroll->visible.x0 - 32;
+ int y = scroll->visible.y1 - scroll->visible.y0 - 32;
+
+ switch (scroll->xmin) {
+ case wimp_SCROLL_PAGE_LEFT:
+ scroll->xscroll -= x;
+ break;
+ case wimp_SCROLL_COLUMN_LEFT:
+ scroll->xscroll -= 100;
+ break;
+ case wimp_SCROLL_COLUMN_RIGHT:
+ scroll->xscroll += 100;
+ break;
+ case wimp_SCROLL_PAGE_RIGHT:
+ scroll->xscroll += x;
+ break;
+ default:
+ scroll->xscroll += (x * (scroll->xmin>>2)) >> 2;
+ break;
+ }
+
+ switch (scroll->ymin) {
+ case wimp_SCROLL_PAGE_UP:
+ scroll->yscroll += y;
+ break;
+ case wimp_SCROLL_LINE_UP:
+ scroll->yscroll += 100;
+ break;
+ case wimp_SCROLL_LINE_DOWN:
+ scroll->yscroll -= 100;
+ break;
+ case wimp_SCROLL_PAGE_DOWN:
+ scroll->yscroll -= y;
+ break;
+ default:
+ scroll->yscroll += (y * (scroll->ymin>>2)) >> 2;
+ break;
+ }
+
+ error = xwimp_open_window((wimp_open *) scroll);
+ if (error) {
+ LOG("xwimp_open_window: 0x%x: %s", error->errnum, error->errmess);
+ }
+}
+
diff --git a/frontends/riscos/wimp.h b/frontends/riscos/wimp.h
new file mode 100644
index 000000000..fdcf67b95
--- /dev/null
+++ b/frontends/riscos/wimp.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * General RISC OS WIMP/OS library functions (interface).
+ */
+
+
+#ifndef _NETSURF_RISCOS_WIMP_H_
+#define _NETSURF_RISCOS_WIMP_H_
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "oslib/os.h"
+#include "oslib/wimp.h"
+#include "rufl.h"
+
+
+int ro_get_hscroll_height(wimp_w w);
+int ro_get_vscroll_width(wimp_w w);
+int ro_get_title_height(wimp_w w);
+bool ro_gui_wimp_read_eig_factors(os_mode mode, int *xeig, int *yeig);
+void ro_convert_os_units_to_pixels(os_coord *os_units, os_mode mode);
+void ro_convert_pixels_to_os_units(os_coord *pixels, os_mode mode);
+
+#define ro_gui_redraw_icon(w, i) xwimp_set_icon_state(w, i, 0, 0)
+void ro_gui_force_redraw_icon(wimp_w w, wimp_i i);
+const char *ro_gui_get_icon_string(wimp_w w, wimp_i i);
+void ro_gui_set_icon_string(wimp_w w, wimp_i i, const char *text, bool is_utf8);
+
+void ro_gui_set_icon_integer(wimp_w w, wimp_i i, int value);
+void ro_gui_set_icon_decimal(wimp_w w, wimp_i i, int value, int decimal_places);
+int ro_gui_get_icon_decimal(wimp_w w, wimp_i i, int decimal_places);
+
+void ro_gui_set_icon_selected_state(wimp_w w, wimp_i i, bool state);
+bool ro_gui_get_icon_selected_state(wimp_w w, wimp_i i);
+void ro_gui_set_icon_shaded_state(wimp_w w, wimp_i i, bool state);
+bool ro_gui_get_icon_shaded_state(wimp_w w, wimp_i i);
+void ro_gui_set_icon_deleted_state(wimp_w w, wimp_i i, bool state);
+bool ro_gui_get_icon_deleted_state(wimp_w w, wimp_i i);
+void ro_gui_set_icon_button_type(wimp_w w, wimp_i i, int type);
+void ro_gui_set_icon_sprite(wimp_w w, wimp_i i, osspriteop_area *area,
+ const char *name);
+void ro_gui_set_window_title(wimp_w w, const char *title);
+bool ro_gui_set_caret_first(wimp_w w);
+void ro_gui_open_window_centre(wimp_w parent, wimp_w child);
+
+osspriteop_area *ro_gui_load_sprite_file(const char *pathname);
+bool ro_gui_wimp_sprite_exists(const char *sprite);
+os_error *ro_gui_wimp_get_sprite(const char *name, osspriteop_header **sprite);
+bool ro_gui_wimp_get_sprite_dimensions(osspriteop_area *area, char *sprite,
+ int *width, int *height);
+
+wimp_w ro_gui_set_window_background_colour(wimp_w window, wimp_colour background);
+void ro_gui_set_icon_colours(wimp_w window, wimp_i icon,
+ wimp_colour foreground, wimp_colour background);
+void ro_gui_user_redraw(wimp_draw *redraw, bool user_fill, os_colour user_colour);
+void ro_gui_wimp_update_window_furniture(wimp_w w, wimp_window_flags bic_mask,
+ wimp_window_flags xor_mask);
+bool ro_gui_wimp_check_window_furniture(wimp_w w, wimp_window_flags mask);
+
+void ro_gui_scroll(wimp_scroll *scroll);
+
+#endif
+
diff --git a/frontends/riscos/wimp_event.c b/frontends/riscos/wimp_event.c
new file mode 100644
index 000000000..015e87baf
--- /dev/null
+++ b/frontends/riscos/wimp_event.c
@@ -0,0 +1,1814 @@
+/*
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2010, 2011 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Automated RISC OS WIMP event handling (implementation).
+ */
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "oslib/os.h"
+#include "oslib/osbyte.h"
+#include "oslib/serviceinternational.h"
+#include "oslib/wimp.h"
+
+#include "utils/log.h"
+
+#include "riscos/gui.h"
+#include "riscos/dialog.h"
+#include "riscos/menus.h"
+#include "riscos/ucstables.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/wimputils.h"
+
+#define WIN_HASH_SIZE 32
+#define WIN_HASH(w) (((unsigned)(w) >> 5) % WIN_HASH_SIZE)
+
+typedef enum {
+ EVENT_NUMERIC_FIELD,
+ EVENT_TEXT_FIELD,
+ EVENT_UP_ARROW,
+ EVENT_DOWN_ARROW,
+ EVENT_MENU_GRIGHT,
+ EVENT_CHECKBOX,
+ EVENT_RADIO,
+ EVENT_BUTTON,
+ EVENT_CANCEL,
+ EVENT_OK
+} event_type;
+
+struct event_data_numeric_field {
+ int stepping;
+ int min;
+ int max;
+ int decimal_places;
+};
+
+struct event_data_menu_gright {
+ wimp_i field;
+ wimp_menu *menu;
+};
+
+struct icon_event {
+ event_type type;
+ wimp_i i;
+ union {
+ struct event_data_numeric_field numeric_field;
+ struct event_data_menu_gright menu_gright;
+ wimp_i linked_icon;
+ int radio_group;
+ void (*callback)(wimp_pointer *pointer);
+ } data;
+ union {
+ char *textual;
+ bool boolean;
+ } previous_value;
+ bool previous_shaded;
+ struct icon_event *next;
+};
+
+struct event_window {
+ wimp_w w;
+ bool (*ok_click)(wimp_w w);
+ bool (*mouse_click)(wimp_pointer *pointer);
+ bool (*keypress)(wimp_key *key);
+ void (*open_window)(wimp_open *open);
+ void (*close_window)(wimp_w w);
+ void (*redraw_window)(wimp_draw *redraw);
+ void (*scroll_window)(wimp_scroll *scroll);
+ void (*entering_window)(wimp_entering *entering);
+ bool (*menu_prepare)(wimp_w w, wimp_i i, wimp_menu *m,
+ wimp_pointer *p);
+ bool (*menu_selection)(wimp_w w, wimp_i i, wimp_menu *m,
+ wimp_selection *s, menu_action a);
+ void (*menu_warning)(wimp_w w, wimp_i i, wimp_menu *m,
+ wimp_selection *s, menu_action a);
+ void (*menu_close)(wimp_w w, wimp_i i, wimp_menu *m);
+ wimp_menu *window_menu;
+ bool window_menu_auto;
+ bool window_menu_iconbar;
+ const char *help_prefix;
+ const char *(*get_help_suffix)(wimp_w w, wimp_i i, os_coord *pos,
+ wimp_mouse_state buttons);
+ void *user_data;
+ struct icon_event *first;
+ struct event_window *next;
+ int max_radio_group;
+};
+
+static void ro_gui_wimp_event_ok_click(struct event_window *window,
+ wimp_mouse_state state);
+static struct event_window *ro_gui_wimp_event_get_window(wimp_w w);
+static struct event_window *ro_gui_wimp_event_find_window(wimp_w w);
+static struct icon_event *ro_gui_wimp_event_get_event(wimp_w w, wimp_i i,
+ event_type type);
+static void ro_gui_wimp_event_prepare_gright_menu(wimp_w w, struct icon_event *event);
+static struct event_window *ro_gui_wimp_event_remove_window(wimp_w w);
+
+static struct event_window *ro_gui_wimp_event_windows[WIN_HASH_SIZE];
+
+static wimp_w ro_gui_wimp_event_submenu;
+
+/**
+ * Memorises the current state of any registered components in a window.
+ *
+ * \param w the window to memorise
+ * \return true on success, false on memory exhaustion or for an unknown window
+ */
+bool ro_gui_wimp_event_memorise(wimp_w w)
+{
+ struct event_window *window;
+ struct icon_event *event;
+ bool error = false;
+
+ window = ro_gui_wimp_event_find_window(w);
+ if (!window)
+ return false;
+
+ for (event = window->first; event; event = event->next) {
+ switch (event->type) {
+ case EVENT_NUMERIC_FIELD:
+ case EVENT_TEXT_FIELD:
+ if (event->previous_value.textual)
+ free(event->previous_value.textual);
+ event->previous_value.textual = strdup(
+ ro_gui_get_icon_string(window->w, event->i));
+ if (!event->previous_value.textual) {
+ error = true;
+ LOG("Unable to store state for icon %i", event->i);
+ }
+ break;
+ case EVENT_CHECKBOX:
+ case EVENT_RADIO:
+ event->previous_value.boolean =
+ ro_gui_get_icon_selected_state(window->w, event->i);
+ break;
+ default:
+ break;
+ }
+ if (event->type != EVENT_MENU_GRIGHT)
+ event->previous_shaded = ro_gui_get_icon_shaded_state(window->w,
+ event->i);
+ }
+ return !error;
+}
+
+
+/**
+ * Restore the state of any registered components in a window to their memorised state.
+ *
+ * \param w the window to restore
+ * \return true on success, false for an unknown window
+ */
+bool ro_gui_wimp_event_restore(wimp_w w)
+{
+ struct event_window *window;
+ struct icon_event *event;
+
+ window = ro_gui_wimp_event_find_window(w);
+ if (!window)
+ return false;
+
+ for (event = window->first; event; event = event->next) {
+ switch (event->type) {
+ case EVENT_NUMERIC_FIELD:
+ case EVENT_TEXT_FIELD:
+ if (event->previous_value.textual)
+ ro_gui_set_icon_string(window->w, event->i,
+ event->previous_value.textual, true);
+ break;
+ case EVENT_CHECKBOX:
+ case EVENT_RADIO:
+ ro_gui_set_icon_selected_state(window->w, event->i,
+ event->previous_value.boolean);
+ break;
+ default:
+ break;
+ }
+ if (event->type != EVENT_MENU_GRIGHT)
+ ro_gui_set_icon_shaded_state(window->w, event->i,
+ event->previous_shaded);
+ }
+ return true;
+}
+
+
+/**
+ * Ensures all values are within pre-determined boundaries.
+ *
+ * \param w the window to memorise
+ * \return true on success, false for an unknown window
+ */
+bool ro_gui_wimp_event_validate(wimp_w w)
+{
+ struct event_window *window;
+ struct icon_event *event;
+ int value;
+
+ window = ro_gui_wimp_event_find_window(w);
+ if (!window)
+ return false;
+
+ for (event = window->first; event; event = event->next) {
+ switch (event->type) {
+ case EVENT_NUMERIC_FIELD:
+ value = ro_gui_get_icon_decimal(window->w, event->i,
+ event->data.numeric_field.decimal_places);
+ if (value < event->data.numeric_field.min)
+ value = event->data.numeric_field.min;
+ else if (value > event->data.numeric_field.max)
+ value = event->data.numeric_field.max;
+ ro_gui_set_icon_decimal(window->w, event->i, value,
+ event->data.numeric_field.decimal_places);
+ break;
+ default:
+ break;
+ }
+ }
+ return true;
+}
+
+/**
+ * Transfer event data from one window to another. This can be used as an
+ * alternative to ro_gui_wimp_event_finalise() and re-registering, if
+ * events need to continue across a change of window handle.
+ *
+ * All aspects of the registered events MUST remain the same in the new
+ * window!
+ *
+ * \param from The current window, which is to be deleted.
+ * \param to The window to which the events should transfer.
+ * \return true on success; false for an unknown window.
+ */
+
+bool ro_gui_wimp_event_transfer(wimp_w from, wimp_w to)
+{
+ struct event_window *window;
+ int h;
+
+ LOG("Transferring all events from window 0x%x to window 0x%x", (unsigned int)from, (unsigned int)to);
+
+ window = ro_gui_wimp_event_remove_window(from);
+ if (window == NULL || window->w != from)
+ return false;
+
+ h = WIN_HASH(to);
+ window->w = to;
+ window->next = ro_gui_wimp_event_windows[h];
+ ro_gui_wimp_event_windows[h] = window;
+
+ ro_gui_menu_window_changed(from, to);
+
+ return true;
+}
+
+/**
+ * Free any resources associated with a window.
+ *
+ * \param w the window to free resources for
+ */
+void ro_gui_wimp_event_finalise(wimp_w w)
+{
+ struct event_window *window;
+ struct icon_event *event;
+
+ LOG("Removing all events for window 0x%x", (unsigned int)w);
+ window = ro_gui_wimp_event_remove_window(w);
+ if (!window)
+ return;
+
+ while (window->first) {
+ event = window->first;
+ window->first = event->next;
+ switch (event->type) {
+ case EVENT_NUMERIC_FIELD:
+ case EVENT_TEXT_FIELD:
+ if (event->previous_value.textual)
+ free(event->previous_value.textual);
+ event->previous_value.textual = NULL;
+ break;
+ default:
+ break;
+ }
+ free(event);
+ }
+ free(window);
+ return;
+}
+
+
+/**
+ * Free any resources associated with a specific icon in a window.
+ *
+ * \param w The window containing the icon.
+ * \param i The icon to free resources for.
+ */
+
+void ro_gui_wimp_event_deregister(wimp_w w, wimp_i i)
+{
+ struct event_window *window;
+ struct icon_event *event, *parent, *child;
+
+ LOG("Removing all events for window 0x%x, icon %d", (unsigned int)w, (int)i);
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return;
+
+ /* Remove any events that apply to the given icon. */
+
+ event = window->first;
+ parent = NULL;
+
+ while (event != NULL) {
+ child = event->next;
+
+ if (event->i == i) {
+ LOG("Removing event 0x%x", (unsigned int)event);
+
+ if (parent == NULL)
+ window->first = child;
+ else
+ parent->next = child;
+
+ switch (event->type) {
+ case EVENT_NUMERIC_FIELD:
+ case EVENT_TEXT_FIELD:
+ if (event->previous_value.textual)
+ free(event->previous_value.textual);
+ event->previous_value.textual = NULL;
+ break;
+ default:
+ break;
+ }
+
+ free(event);
+ } else {
+ parent = event;
+ }
+
+ event = child;
+ }
+}
+
+
+/**
+ * Set the associated help prefix for a given window.
+ *
+ * \param w the window to get the prefix for
+ * \param help_prefix the prefix to associate with the window (used directly)
+ * \return true on success, or NULL for memory exhaustion
+ */
+bool ro_gui_wimp_event_set_help_prefix(wimp_w w, const char *help_prefix)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->help_prefix = help_prefix;
+ return true;
+}
+
+
+/**
+ * Get the associated help prefix.
+ *
+ * \param w the window to get the prefix for
+ * \return the associated prefix, or NULL
+ */
+const char *ro_gui_wimp_event_get_help_prefix(wimp_w w)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_find_window(w);
+ if (window)
+ return window->help_prefix;
+ return NULL;
+}
+
+
+/**
+ * Register a handler to decode help suffixes for a given window.
+ *
+ */
+
+bool ro_gui_wimp_event_register_help_suffix(wimp_w w,
+ const char *(*get_help_suffix)(wimp_w w, wimp_i i,
+ os_coord *pos, wimp_mouse_state buttons))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->get_help_suffix = get_help_suffix;
+ return true;
+}
+
+
+/**
+ * Get the associated help suffix.
+ *
+ * \param w The window to get the suffix for
+ * \param i The icon
+ * \param pos The os coordinates
+ * \param buttons The button state.
+ * \return The associated prefix, or NULL
+ */
+
+const char *ro_gui_wimp_event_get_help_suffix(wimp_w w, wimp_i i,
+ os_coord *pos, wimp_mouse_state buttons)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_find_window(w);
+ if (window == NULL || window->get_help_suffix == NULL)
+ return NULL;
+
+ return window->get_help_suffix(w, i, pos, buttons);
+}
+
+
+/**
+ * Sets the user data associated with a window.
+ *
+ * \param w the window to associate the data with
+ * \param user the data to associate
+ */
+bool ro_gui_wimp_event_set_user_data(wimp_w w, void *user)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->user_data = user;
+ return true;
+
+}
+
+
+/**
+ * Gets the user data associated with a window.
+ *
+ * \param w the window to retrieve the data for
+ * \return the associated data, or NULL
+ */
+void *ro_gui_wimp_event_get_user_data(wimp_w w)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_find_window(w);
+ if (window)
+ return window->user_data;
+ return NULL;
+}
+
+
+/**
+ * Handles a menu selection event.
+ *
+ * (At present, this is tied to being called from menus.c and relies on that
+ * module decoding the menu into an action code. If menus.c loses its
+ * menu handling in the future, such decoding might need to move here.)
+ *
+ * The order of execution is:
+ *
+ * 1. Try to match the menu to a pop-up menu. If successful, handle it as
+ * this.
+ * 2. Try to match the menu to a window menu. If successful, pass control to
+ * the menu's registered _select handler.
+ * 3. Return event as unhandled.
+ *
+ * \param w the window to owning the menu
+ * \param i the icon owning the menu
+ * \param menu the menu that has been selected
+ * \param selection the selection information
+ * \param action the menu action info from menus.c
+ * \return true if the menu is OK for an Adjust re-open;
+ * else false.
+ */
+bool ro_gui_wimp_event_menu_selection(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action)
+{
+ struct event_window *window;
+ struct icon_event *event;
+ wimp_menu_entry *menu_entry;
+ wimp_key key;
+ os_error *error;
+ wimp_caret caret;
+ wimp_icon_state ic;
+ unsigned int button_type;
+ bool prepared;
+
+ window = ro_gui_wimp_event_find_window(w);
+ if (window == NULL)
+ return false;
+
+ /* Start by looking for an icon event that matches. If there isn't one,
+ * then return details for an unconnected menu. It's up to the
+ * event recipient to sort out if this is a window menu or not, based
+ * on the menu handle passed back.
+ */
+
+ for (event = window->first; event; event = event->next)
+ if ((event->type == EVENT_MENU_GRIGHT) && (event->i == i))
+ break;
+ if (!event) {
+ if (window->menu_selection)
+ window->menu_selection(window->w, wimp_ICON_WINDOW,
+ menu, selection, action);
+
+ /* Prepare the menu pending a possible Adjust click. */
+ if (window->menu_prepare)
+ if (!window->menu_prepare(window->w, wimp_ICON_WINDOW,
+ menu, NULL))
+ return false;
+
+ return true;
+ }
+
+ menu_entry = &menu->entries[selection->items[0]];
+ for (i = 1; selection->items[i] != -1; i++)
+ menu_entry = &menu_entry->sub_menu->
+ entries[selection->items[i]];
+
+ /* if the entry is already ticked then we do nothing */
+ if (menu_entry->menu_flags & wimp_MENU_TICKED)
+ return true;
+
+ ro_gui_set_icon_string(window->w, event->data.menu_gright.field,
+ menu_entry->data.indirected_text.text, false);
+ if (window->menu_selection)
+ window->menu_selection(window->w, event->i, menu,
+ selection, action);
+ prepared = true;
+ if (window->menu_prepare)
+ prepared = window->menu_prepare(window->w, event->i,
+ menu, NULL);
+ if (prepared)
+ ro_gui_wimp_event_prepare_gright_menu(window->w, event);
+
+ /* set the caret for writable icons and send a CTRL+U keypress to
+ * stimulate activity if needed */
+ ic.w = window->w;
+ ic.i = event->data.menu_gright.field;
+ error = xwimp_get_icon_state(&ic);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+ button_type = (ic.icon.flags & wimp_ICON_BUTTON_TYPE) >> wimp_ICON_BUTTON_TYPE_SHIFT;
+ if ((button_type != wimp_BUTTON_WRITABLE) &&
+ (button_type != wimp_BUTTON_WRITE_CLICK_DRAG))
+ return prepared;
+ error = xwimp_get_caret_position(&caret);
+ if (error) {
+ LOG("xwimp_get_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+ if ((caret.w != window->w) || (caret.i != event->data.menu_gright.field)) {
+ error = xwimp_set_caret_position(window->w, event->data.menu_gright.field,
+ -1, -1, -1, strlen(menu_entry->data.indirected_text.text));
+ if (error) {
+ LOG("xwimp_set_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ }
+ if (window->keypress) {
+ key.w = window->w;
+ key.c = 21; // ctrl+u
+ window->keypress(&key);
+ }
+ return prepared;
+}
+
+
+/**
+ * Handles a mouse click event in a registered window.
+ *
+ * The order of execution is:
+ *
+ * 1. If a menu click, and the window has an automatic window menu, this is
+ * processed immediately.
+ * 2. Any registered mouse_click routine (see ro_gui_wimp_register_mouse_click())
+ * 3. If the current icon is not registered with a type then it is assumed that no
+ * action is necessary, and the click is deemed to have been handled.
+ * 4. If the registered mouse_click routine returned false, or there was no registered
+ * routine then the automated action for the registered icon type is performed
+ *
+ * \param pointer the current pointer state
+ * \return true if the event was handled, false otherwise
+ */
+bool ro_gui_wimp_event_mouse_click(wimp_pointer *pointer)
+{
+ struct event_window *window;
+ struct icon_event *event;
+ wimp_w w;
+ struct icon_event *search;
+ int current, step, stepping, min, max, decimal_places;
+ wimp_window_state open;
+ wimp_caret caret;
+ bool prepared;
+
+ w = pointer->w;
+ window = ro_gui_wimp_event_find_window(w);
+ if (!window)
+ return false;
+
+ /* Menu clicks take priority if there is an auto menu defined. */
+ if ((pointer->buttons == wimp_CLICK_MENU) &&
+ (window->window_menu != NULL) &&
+ (window->window_menu_auto)) {
+ ro_gui_wimp_event_process_window_menu_click(pointer);
+ return true;
+ }
+
+ /* registered routines take next priority */
+ if ((window->mouse_click) && (window->mouse_click(pointer)))
+ return true;
+
+ for (event = window->first; event; event = event->next)
+ if (event->i == pointer->i)
+ break;
+ if (!event)
+ return true;
+
+ switch (event->type) {
+ case EVENT_NUMERIC_FIELD:
+ case EVENT_TEXT_FIELD:
+ break;
+ case EVENT_UP_ARROW:
+ case EVENT_DOWN_ARROW:
+ for (search = window->first; search; search = search->next)
+ if (search->i == event->data.linked_icon) break;
+ if (!search) {
+ LOG("Incorrect reference.");
+ return false;
+ }
+ stepping = search->data.numeric_field.stepping;
+ min = search->data.numeric_field.min;
+ max = search->data.numeric_field.max;
+ decimal_places = search->data.numeric_field.decimal_places;
+
+ if (pointer->buttons & wimp_CLICK_ADJUST)
+ step = -stepping;
+ else if (pointer->buttons & wimp_CLICK_SELECT)
+ step = stepping;
+ else
+ return true;
+ if (event->type == EVENT_DOWN_ARROW)
+ step = -step;
+
+ current = ro_gui_get_icon_decimal(pointer->w, event->data.linked_icon,
+ decimal_places);
+ current += step;
+ if (current < min)
+ current = min;
+ if (current > max)
+ current = max;
+ ro_gui_set_icon_decimal(pointer->w, event->data.linked_icon, current,
+ decimal_places);
+ break;
+ case EVENT_MENU_GRIGHT:
+ /* if there's already a menu open then we assume that we are part of it.
+ * to follow the standard RISC OS behaviour we add a 'send to the back'
+ * button, then close the menu (which closes us) and then finally
+ * re-open ourselves. ugh! */
+ if (current_menu != NULL) {
+ os_error *error;
+ open.w = pointer->w;
+ error = xwimp_get_window_state(&open);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+ error = xwimp_get_caret_position(&caret);
+ if (error) {
+ LOG("xwimp_get_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+ ro_gui_dialog_add_persistent(current_menu_window,
+ pointer->w);
+ ro_gui_menu_destroy();
+ error = xwimp_open_window(PTR_WIMP_OPEN(&open));
+ if (error) {
+ LOG("xwimp_open_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+ if (caret.w == pointer->w) {
+ error = xwimp_set_caret_position(caret.w,
+ caret.i,
+ caret.pos.x, caret.pos.y,
+ -1, caret.index);
+ if (error) {
+ LOG("xwimp_set_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ }
+ }
+ /* display the menu */
+
+ prepared = true;
+ if (window->menu_prepare != NULL)
+ prepared = window->menu_prepare(pointer->w, pointer->i,
+ event->data.menu_gright.menu, pointer);
+ if (prepared) {
+ ro_gui_wimp_event_prepare_gright_menu(pointer->w, event);
+ ro_gui_popup_menu(event->data.menu_gright.menu, pointer->w, pointer->i);
+ }
+ break;
+ case EVENT_CHECKBOX:
+ break;
+ case EVENT_RADIO:
+ for (search = window->first; search; search = search->next)
+ if ((search->type == EVENT_RADIO) &&
+ (search->data.radio_group ==
+ event->data.radio_group))
+ ro_gui_set_icon_selected_state(pointer->w,
+ search->i, (search == event));
+ break;
+ case EVENT_BUTTON:
+ if (event->data.callback)
+ event->data.callback(pointer);
+ break;
+ case EVENT_CANCEL:
+ if (pointer->buttons & wimp_CLICK_SELECT) {
+ ro_gui_dialog_close(pointer->w);
+ ro_gui_wimp_event_close_window(pointer->w);
+ ro_gui_menu_destroy();
+ } else {
+ ro_gui_wimp_event_restore(pointer->w);
+ }
+ break;
+ case EVENT_OK:
+ ro_gui_wimp_event_ok_click(window, pointer->buttons);
+ break;
+ }
+ return true;
+}
+
+
+/**
+ * Prepare a menu ready for use
+ *
+ * /param w the window owning the menu
+ * /param event the icon event owning the menu
+ */
+void ro_gui_wimp_event_prepare_gright_menu(wimp_w w, struct icon_event *event)
+{
+ int i;
+ const char *text;
+ unsigned int button_type;
+ wimp_icon_state ic;
+ wimp_menu *menu;
+ os_error *error;
+
+ /* if the linked icon is not writable then we set the ticked state
+ * of the menu item that matches the contents */
+ ic.w = w;
+ ic.i = event->data.menu_gright.field;
+ error = xwimp_get_icon_state(&ic);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ button_type = (ic.icon.flags & wimp_ICON_BUTTON_TYPE)
+ >> wimp_ICON_BUTTON_TYPE_SHIFT;
+ if ((button_type == wimp_BUTTON_WRITABLE) ||
+ (button_type == wimp_BUTTON_WRITE_CLICK_DRAG))
+ return;
+ text = ro_gui_get_icon_string(w, event->data.menu_gright.field);
+ menu = event->data.menu_gright.menu;
+ i = 0;
+ do {
+ if (!strcmp(menu->entries[i].data.indirected_text.text, text))
+ menu->entries[i].menu_flags |= wimp_MENU_TICKED;
+ else
+ menu->entries[i].menu_flags &= ~wimp_MENU_TICKED;
+ } while (!(menu->entries[i++].menu_flags & wimp_MENU_LAST));
+}
+
+
+/**
+ * Perform the necessary actions following a click on the OK button.
+ *
+ * /param window the window to perform the action on
+ * /param state the mouse button state
+ */
+void ro_gui_wimp_event_ok_click(struct event_window *window,
+ wimp_mouse_state state)
+{
+ struct icon_event *search;
+
+ for (search = window->first; search; search = search->next)
+ if (search->type == EVENT_OK) {
+ if (ro_gui_get_icon_shaded_state(window->w, search->i))
+ return;
+ break;
+ }
+ ro_gui_wimp_event_validate(window->w);
+
+ if (window->ok_click)
+ if (!window->ok_click(window->w))
+ return;
+
+ if (state & wimp_CLICK_SELECT) {
+ ro_gui_dialog_close(window->w);
+ ro_gui_wimp_event_close_window(window->w);
+ ro_gui_menu_destroy();
+ } else {
+ ro_gui_wimp_event_memorise(window->w);
+ }
+}
+
+
+/**
+ * Handle any registered keypresses, and the standard RISC OS ones
+ *
+ * \param key the key state
+ * \return true if keypress handled, false otherwise
+ */
+bool ro_gui_wimp_event_keypress(wimp_key *key)
+{
+ static const int *ucstable = NULL;
+ static int alphabet = 0;
+ static uint32_t wc = 0; /* buffer for UTF8 alphabet */
+ static int shift = 0;
+ struct event_window *window;
+ struct icon_event *event;
+ wimp_pointer pointer;
+ wimp_key k;
+ uint32_t c = (uint32_t) key->c;
+ int t_alphabet;
+ os_error *error;
+
+ window = ro_gui_wimp_event_find_window(key->w);
+ if (!window)
+ return false;
+
+ /* copy key state so we can corrupt it safely */
+ memcpy(&k, key, sizeof(wimp_key));
+
+ /* In order to make sensible use of the 0x80->0xFF ranges specified
+ * in the RISC OS 8bit alphabets, we must do the following:
+ *
+ * + Read the currently selected alphabet
+ * + Acquire a pointer to the UCS conversion table for this alphabet:
+ * + Try using ServiceInternational 8 to get the table
+ * + If that fails, use our internal table (see ucstables.c)
+ * + If the alphabet is not UTF8 and the conversion table exists:
+ * + Lookup UCS code in the conversion table
+ * + If code is -1 (i.e. undefined):
+ * + Use codepoint 0xFFFD instead
+ * + If the alphabet is UTF8, we must buffer input, thus:
+ * + If the keycode is < 0x80:
+ * + Handle it directly
+ * + If the keycode is a UTF8 sequence start:
+ * + Initialise the buffer appropriately
+ * + Otherwise:
+ * + OR in relevant bits from keycode to buffer
+ * + If we've received an entire UTF8 character:
+ * + Handle UCS code
+ * + Otherwise:
+ * + Simply handle the keycode directly, as there's no easy way
+ * of performing the mapping from keycode -> UCS4 codepoint.
+ */
+ error = xosbyte1(osbyte_ALPHABET_NUMBER, 127, 0, &t_alphabet);
+ if (error) {
+ LOG("failed reading alphabet: 0x%x: %s", error->errnum, error->errmess);
+ /* prevent any corruption of ucstable */
+ t_alphabet = alphabet;
+ }
+
+ if (t_alphabet != alphabet) {
+ void *ostable;
+ osbool unclaimed;
+ /* Alphabet has changed, so read UCS table location */
+ alphabet = t_alphabet;
+
+ error = xserviceinternational_get_ucs_conversion_table(
+ alphabet, &unclaimed, &ostable);
+ if (error != NULL) {
+ LOG("failed reading UCS conversion table: 0x%x: %s", error->errnum, error->errmess);
+ /* Try using our own table instead */
+ ucstable = ucstable_from_alphabet(alphabet);
+ } else if (unclaimed) {
+ /* Service wasn't claimed so use our own ucstable */
+ ucstable = ucstable_from_alphabet(alphabet);
+ } else {
+ /* Use the table provided by the OS */
+ ucstable = ostable;
+ }
+ }
+
+ if (c < 256) {
+ if (alphabet != 111 /* UTF8 */ && ucstable != NULL) {
+ /* defined in this alphabet? */
+ if (ucstable[c] == -1)
+ return true;
+
+ /* read UCS4 value out of table */
+ k.c = ucstable[c];
+ }
+ else if (alphabet == 111 /* UTF8 */) {
+ if ((c & 0x80) == 0x00 || (c & 0xC0) == 0xC0) {
+ /* UTF8 start sequence */
+ if ((c & 0xE0) == 0xC0) {
+ wc = ((c & 0x1F) << 6);
+ shift = 1;
+ return true;
+ }
+ else if ((c & 0xF0) == 0xE0) {
+ wc = ((c & 0x0F) << 12);
+ shift = 2;
+ return true;
+ }
+ else if ((c & 0xF8) == 0xF0) {
+ wc = ((c & 0x07) << 18);
+ shift = 3;
+ return true;
+ }
+ /* These next two have been removed
+ * from RFC3629, but there's no
+ * guarantee that RISC OS won't
+ * generate a UCS4 value outside the
+ * UTF16 plane, so we handle them
+ * anyway. */
+ else if ((c & 0xFC) == 0xF8) {
+ wc = ((c & 0x03) << 24);
+ shift = 4;
+ }
+ else if ((c & 0xFE) == 0xFC) {
+ wc = ((c & 0x01) << 30);
+ shift = 5;
+ }
+ else if (c >= 0x80) {
+ /* If this ever happens,
+ * RISC OS' UTF8 keyboard
+ * drivers are broken */
+ LOG("unexpected UTF8 start"" byte %x (ignoring)", c);
+ return true;
+ }
+ /* Anything else is ASCII, so just
+ * handle it directly. */
+ }
+ else {
+ if ((c & 0xC0) != 0x80) {
+ /* If this ever happens,
+ * RISC OS' UTF8 keyboard
+ * drivers are broken */
+ LOG("unexpected keycode: ""%x (ignoring)", c);
+ return true;
+ }
+
+ /* Continuation of UTF8 character */
+ wc |= ((c & 0x3F) << (6 * --shift));
+ if (shift > 0)
+ /* partial character */
+ return true;
+ else
+ /* got entire character, so
+ * fetch from buffer and
+ * handle it */
+ k.c = wc;
+ }
+ }
+ } else {
+ k.c |= IS_WIMP_KEY;
+ }
+
+ /* registered routines take priority */
+ if (window->keypress)
+ if (window->keypress(&k))
+ return true;
+
+ switch (key->c) {
+ /* Escape performs the CANCEL action (simulated click) */
+ case wimp_KEY_ESCAPE:
+ for (event = window->first; event; event = event->next) {
+ switch (event->type) {
+ case EVENT_CANCEL:
+ pointer.w = key->w;
+ pointer.i = event->i;
+ pointer.buttons = wimp_CLICK_SELECT;
+ ro_gui_wimp_event_mouse_click(&pointer);
+ return true;
+ default:
+ break;
+ }
+ }
+ return false;
+ /* CTRL+F2 closes a window with a close icon */
+ case wimp_KEY_CONTROL + wimp_KEY_F2:
+ if (!ro_gui_wimp_check_window_furniture(key->w,
+ wimp_WINDOW_CLOSE_ICON))
+ return false;
+ ro_gui_dialog_close(key->w);
+ ro_gui_wimp_event_close_window(key->w);
+ ro_gui_menu_destroy();
+ return true;
+ /* Return performs the OK action */
+ case wimp_KEY_RETURN:
+ if (!window->ok_click)
+ return false;
+ /* todo: check we aren't greyed out */
+ ro_gui_wimp_event_ok_click(window, wimp_CLICK_SELECT);
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Handle any open window requests
+ *
+ * \param open the window open request
+ */
+bool ro_gui_wimp_event_open_window(wimp_open *open)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_find_window(open->w);
+ if ((window) && (window->open_window)) {
+ window->open_window(open);
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Service any close window handlers
+ *
+ * \param w the window being closed
+ */
+bool ro_gui_wimp_event_close_window(wimp_w w)
+{
+ struct event_window *window;
+
+ LOG("Close event received for window 0x%x", (unsigned int)w);
+ if (w == ro_gui_wimp_event_submenu)
+ ro_gui_wimp_event_submenu = 0;
+ window = ro_gui_wimp_event_find_window(w);
+ if ((window) && (window->close_window)) {
+ window->close_window(w);
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Handle any redraw window requests
+ *
+ * \param redraw the window redraw request
+ */
+bool ro_gui_wimp_event_redraw_window(wimp_draw *redraw)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_find_window(redraw->w);
+ if ((window) && (window->redraw_window)) {
+ window->redraw_window(redraw);
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Handle any scroll window requests
+ *
+ * \param scroll the window scroll request
+ */
+bool ro_gui_wimp_event_scroll_window(wimp_scroll *scroll)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_find_window(scroll->w);
+ if ((window) && (window->scroll_window)) {
+ window->scroll_window(scroll);
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Handle any pointer entering window requests
+ *
+ * \param entering the pointer entering window request
+ */
+bool ro_gui_wimp_event_pointer_entering_window(wimp_entering *entering)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_find_window(entering->w);
+ if ((window) && (window->entering_window)) {
+ window->entering_window(entering);
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Process a Menu click in a window, by checking for a registered window
+ * menu and opening it if one is found.
+ *
+ * \param pointer The pointer block from the mouse click event.
+ * \return true if the click was actioned; else false.
+ */
+
+bool ro_gui_wimp_event_process_window_menu_click(wimp_pointer *pointer)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_find_window(pointer->w);
+ if ((window) && (window->window_menu)
+ && (pointer->buttons == wimp_CLICK_MENU)) {
+ int xpos, ypos;
+
+ if (window->menu_prepare)
+ if (!window->menu_prepare(window->w, wimp_ICON_WINDOW,
+ window->window_menu, pointer))
+ return false;
+
+ if (window->window_menu_iconbar) {
+ int entry = 0;
+ int line_height = window->window_menu->height +
+ window->window_menu->gap;
+ int gap_height = 24; /* The fixed dotted line height */
+
+ xpos = pointer->pos.x;
+ ypos = 96;
+ do {
+ ypos += line_height;
+ if ((window->window_menu->
+ entries[entry].menu_flags &
+ wimp_MENU_SEPARATE) != 0)
+ ypos += gap_height;
+ } while ((window->window_menu->
+ entries[entry++].menu_flags &
+ wimp_MENU_LAST) == 0);
+ } else {
+ xpos = pointer->pos.x;
+ ypos = pointer->pos.y;
+ }
+
+ ro_gui_menu_create(window->window_menu, xpos, ypos, window->w);
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Trigger a window's Prepare Menu event.
+ *
+ * \param w The window to use.
+ * \param i The icon to use.
+ * \param *menu The menu handle to use.
+ * \return true if the affected menu was prepared OK; else
+ * false.
+ */
+
+bool ro_gui_wimp_event_prepare_menu(wimp_w w, wimp_i i, wimp_menu *menu)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_find_window(w);
+ if (window == NULL)
+ return false;
+
+ if (window->menu_prepare)
+ return window->menu_prepare(w, i, menu, NULL);
+
+ /* The menu is always OK if there's no event handler. */
+
+ return true;
+}
+
+/**
+ * Register a window menu to be (semi-)automatically handled.
+ *
+ * \param w The window to attach the menu to.
+ * \param m The menu to be attached.
+ * \param menu_auto true if the menu should be opened autimatically on
+ * Menu clicks with no task intervention; false to pass
+ * clicks to the window's Mouse Event handler and leave
+ * that to pass the menu click back to us for handling
+ * and menu opening.
+ * \param position_ibar true if the menu should open in an iconbar
+ * position; false to open at the pointer.
+ * \return true if the menu was registed ok; else false.
+ */
+
+bool ro_gui_wimp_event_register_menu(wimp_w w, wimp_menu *m,
+ bool menu_auto, bool position_ibar)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->window_menu = m;
+ window->window_menu_auto = menu_auto;
+ window->window_menu_iconbar = position_ibar;
+ return true;
+}
+
+/**
+ * Register a numeric field to be automatically handled
+ */
+bool ro_gui_wimp_event_register_numeric_field(wimp_w w, wimp_i i,
+ wimp_i up, wimp_i down,
+ int min, int max, int stepping, int decimal_places)
+{
+ struct icon_event *event;
+
+ event = ro_gui_wimp_event_get_event(w, i, EVENT_NUMERIC_FIELD);
+ if (!event)
+ return false;
+ event->data.numeric_field.min = min;
+ event->data.numeric_field.max = max;
+ event->data.numeric_field.stepping = stepping;
+ event->data.numeric_field.decimal_places = decimal_places;
+
+ event = ro_gui_wimp_event_get_event(w, up, EVENT_UP_ARROW);
+ if (!event)
+ return false;
+ event->data.linked_icon = i;
+
+ event = ro_gui_wimp_event_get_event(w, down, EVENT_DOWN_ARROW);
+ if (!event)
+ return false;
+ event->data.linked_icon = i;
+
+ return true;
+}
+
+
+/**
+ * Register a text field to be automatically handled
+ */
+bool ro_gui_wimp_event_register_text_field(wimp_w w, wimp_i i) {
+ struct icon_event *event;
+
+ event = ro_gui_wimp_event_get_event(w, i, EVENT_TEXT_FIELD);
+ if (!event)
+ return false;
+ return true;
+}
+
+
+/**
+ * Register an icon menu to be automatically handled
+ */
+bool ro_gui_wimp_event_register_menu_gright(wimp_w w, wimp_i i,
+ wimp_i gright, wimp_menu *menu)
+{
+ struct icon_event *event;
+
+ event = ro_gui_wimp_event_get_event(w, gright, EVENT_MENU_GRIGHT);
+ if (!event)
+ return false;
+ event->data.menu_gright.field = i;
+ event->data.menu_gright.menu = menu;
+
+ return ro_gui_wimp_event_register_text_field(w, i);
+}
+
+
+/**
+ * Register a checkbox to be automatically handled
+ */
+bool ro_gui_wimp_event_register_checkbox(wimp_w w, wimp_i i)
+{
+ struct icon_event *event;
+
+ event = ro_gui_wimp_event_get_event(w, i, EVENT_CHECKBOX);
+ if (!event)
+ return false;
+ return true;
+}
+
+
+/**
+ * Register a group of radio icons to be automatically handled
+ */
+bool ro_gui_wimp_event_register_radio(wimp_w w, wimp_i *i)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->max_radio_group++;
+
+ while (*i != -1) {
+ struct icon_event *event = ro_gui_wimp_event_get_event(w, *i,
+ EVENT_RADIO);
+ if (!event)
+ return false;
+ event->data.radio_group = window->max_radio_group;
+ i++;
+ }
+ return true;
+}
+
+
+/**
+ * Register a function to be called when a particular button is pressed.
+ */
+bool ro_gui_wimp_event_register_button(wimp_w w, wimp_i i,
+ void (*callback)(wimp_pointer *pointer))
+{
+ struct icon_event *event;
+
+ event = ro_gui_wimp_event_get_event(w, i, EVENT_BUTTON);
+ if (!event)
+ return false;
+ event->data.callback = callback;
+ return true;
+}
+
+
+/**
+ * Register a function to be called for the Cancel action on a window.
+ */
+bool ro_gui_wimp_event_register_cancel(wimp_w w, wimp_i i)
+{
+ struct icon_event *event;
+
+ event = ro_gui_wimp_event_get_event(w, i, EVENT_CANCEL);
+ if (!event)
+ return false;
+ return true;
+}
+
+
+/**
+ * Register a function to be called for the OK action on a window.
+ */
+bool ro_gui_wimp_event_register_ok(wimp_w w, wimp_i i,
+ bool (*callback)(wimp_w w))
+{
+ struct event_window *window;
+ struct icon_event *event;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->ok_click = callback;
+
+ event = ro_gui_wimp_event_get_event(w, i, EVENT_OK);
+ if (!event)
+ return false;
+ return true;
+}
+
+
+/**
+ * Register a function to be called for all mouse-clicks to icons
+ * in a window that don't have registered actions.
+ */
+bool ro_gui_wimp_event_register_mouse_click(wimp_w w,
+ bool (*callback)(wimp_pointer *pointer))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->mouse_click = callback;
+ return true;
+}
+
+
+/**
+ * Register a function to be called for all keypresses within a
+ * particular window.
+ *
+ * Important: the character code passed to the callback in key->c
+ * is UTF-32 (i.e. in the range [0, &10ffff]). WIMP keys (e.g. F1)
+ * will have bit 31 set.
+ *
+ */
+bool ro_gui_wimp_event_register_keypress(wimp_w w,
+ bool (*callback)(wimp_key *key))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->keypress = callback;
+ return true;
+}
+
+
+/**
+ * Register a function to be called for all window opening requests.
+ */
+bool ro_gui_wimp_event_register_open_window(wimp_w w,
+ void (*callback)(wimp_open *open))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->open_window = callback;
+ return true;
+}
+
+/**
+ * Register a function to be called after the window has been closed.
+ */
+bool ro_gui_wimp_event_register_close_window(wimp_w w,
+ void (*callback)(wimp_w w))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->close_window = callback;
+ return true;
+}
+
+/**
+ * Register a function to be called for all window redraw operations.
+ */
+bool ro_gui_wimp_event_register_redraw_window(wimp_w w,
+ void (*callback)(wimp_draw *redraw))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->redraw_window = callback;
+ return true;
+}
+
+/**
+ * Register a function to be called for all window scroll requests.
+ */
+
+bool ro_gui_wimp_event_register_scroll_window(wimp_w w,
+ void (*callback)(wimp_scroll *scroll))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->scroll_window = callback;
+ return true;
+}
+
+/**
+ * Register a function to be called for all pointer entering window requests.
+ */
+
+bool ro_gui_wimp_event_register_pointer_entering_window(wimp_w w,
+ void (*callback)(wimp_entering *entering))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->entering_window = callback;
+ return true;
+}
+
+/**
+ * Register a function to be called before a menu is (re-)opened.
+ *
+ * \param *w The window for which events should be returned.
+ * \param *callback A function to be called beofre the menu is
+ * (re-)opened.
+ * \return true if the menu was registed ok; else false.
+ */
+bool ro_gui_wimp_event_register_menu_prepare(wimp_w w,
+ bool (*callback)(wimp_w w, wimp_i i, wimp_menu *m,
+ wimp_pointer *p))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->menu_prepare = callback;
+ return true;
+}
+
+
+/**
+ * Register a function to be called following a menu selection.
+ *
+ * \param *w The window for which events should be returned.
+ * \param *callback A function to be called when a selection is
+ * made.
+ * \return true if the menu was registed ok; else false.
+ */
+bool ro_gui_wimp_event_register_menu_selection(wimp_w w,
+ bool (*callback)(wimp_w w, wimp_i i, wimp_menu *m,
+ wimp_selection *s, menu_action a))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->menu_selection = callback;
+ return true;
+}
+
+
+/**
+ * Register a function to be called when a sub-menu warning is received.
+ *
+ * \param *w The window for which events should be returned.
+ * \param *callback A function to be called whenever a submenu
+ * warning is received for the menu.
+ * \return true if the menu was registed ok; else false.
+ */
+bool ro_gui_wimp_event_register_menu_warning(wimp_w w,
+ void (*callback)(wimp_w w, wimp_i i, wimp_menu *m,
+ wimp_selection *s, menu_action a))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->menu_warning = callback;
+ return true;
+}
+
+
+/**
+ * Register a function to be called before a menu is finally closed.
+ *
+ * \param *w The window for which events should be returned.
+ * \param *callback A function to be called when the menu is closed.
+ * \return true if the menu was registed ok; else false.
+ */
+bool ro_gui_wimp_event_register_menu_close(wimp_w w,
+ void (*callback)(wimp_w w, wimp_i i, wimp_menu *m))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->menu_close = callback;
+ return true;
+}
+
+
+/**
+ * Finds the event data associated with a given window handle, or creates a
+ * new one.
+ *
+ * \param w the window to find data for
+ */
+struct event_window *ro_gui_wimp_event_get_window(wimp_w w)
+{
+ struct event_window *window;
+ int h;
+
+ assert((int)w != 0);
+ window = ro_gui_wimp_event_find_window(w);
+ if (window)
+ return window;
+
+ LOG("Creating structure for window 0x%x", (unsigned int)w);
+ window = calloc(1, sizeof(struct event_window));
+ if (!window)
+ return NULL;
+
+ h = WIN_HASH(w);
+ window->w = w;
+ window->next = ro_gui_wimp_event_windows[h];
+ ro_gui_wimp_event_windows[h] = window;
+ return window;
+}
+
+
+/**
+ * Removes the event data associated with a given handle from the hash tables,
+ * but does not delete it.
+ *
+ * \param w the window to be removed
+ * \return pointer to the event data or NULL if not found
+ */
+
+struct event_window *ro_gui_wimp_event_remove_window(wimp_w w)
+{
+ struct event_window **prev;
+ int h = WIN_HASH(w);
+
+ /* search hash chain for the window */
+ prev = &ro_gui_wimp_event_windows[h];
+ while (*prev) {
+ struct event_window *window = *prev;
+
+ if (window->w == w) {
+ /* remove from chain */
+ *prev = window->next;
+ return window;
+ }
+ prev = &window->next;
+ }
+
+ /* not found */
+ return NULL;
+}
+
+/**
+ * Find the event data associated with a given window handle
+ *
+ * \param w the window to find data for
+ */
+struct event_window *ro_gui_wimp_event_find_window(wimp_w w)
+{
+ struct event_window *window;
+ int h = WIN_HASH(w);
+
+ /* search hash chain for window */
+ for (window = ro_gui_wimp_event_windows[h]; window; window = window->next) {
+ if (window->w == w)
+ return window;
+ }
+ return NULL;
+}
+
+struct icon_event *ro_gui_wimp_event_get_event(wimp_w w, wimp_i i,
+ event_type type)
+{
+ struct event_window *window;
+ struct icon_event *event;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return NULL;
+
+ for (event = window->first; event; event = event->next) {
+ if (event->i == i) {
+ event->type = type;
+ return event;
+ }
+ }
+
+ event = calloc(1, sizeof(struct icon_event));
+ if (!event)
+ return NULL;
+ event->i = i;
+ event->type = type;
+ event->next = window->first;
+ window->first = event;
+
+ return event;
+}
+
+/* Handle sumbenu warnings. This is called from ro_gui_menu_warning(), and
+ * returns to that function to have the submenu opened correctly.
+ *
+ * \param w the window to owning the menu
+ * \param i the icon owning the menu
+ * \param menu the menu that has been selected
+ * \param selection the selection information
+ * \param action the menu action info from menus.c
+ * \return true if the event was handled, false otherwise
+ */
+
+bool ro_gui_wimp_event_submenu_warning(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action)
+{
+ struct event_window *window;
+ struct icon_event *event;
+
+ ro_gui_wimp_event_register_submenu(0);
+
+ /* Process the event for any window menus. Find the window data, then
+ * try and match to an icon event. If we can, then there isn't anything
+ * to do.
+ */
+
+ window = ro_gui_wimp_event_find_window(w);
+ if (!window)
+ return false;
+
+ for (event = window->first; event; event = event->next)
+ if ((event->type == EVENT_MENU_GRIGHT) && (event->i == i))
+ break;
+ if (event) {
+ if (window->menu_close != NULL &&
+ event->type == EVENT_MENU_GRIGHT &&
+ event->data.menu_gright.menu == menu) {
+ window->menu_close(w, i, menu);
+ return true;
+ }
+
+ return false;
+ }
+
+ /* If the warning is for a window menu, then pass the event on to it. */
+
+ if ((window->window_menu) && (window->window_menu == menu)) {
+ if (window->menu_warning) {
+ window->menu_warning(w, wimp_ICON_WINDOW, menu,
+ selection, action);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Handle menus being closed. This is called from the menus modules, in
+ * every scenario when one of our own menus is open.
+ *
+ * \param w the window to owning the menu
+ * \param i the icon owning the menu
+ * \param menu the menu that has been selected
+ */
+
+void ro_gui_wimp_event_menus_closed(wimp_w w, wimp_i i, wimp_menu *menu)
+{
+ struct event_window *window;
+ struct icon_event *event;
+
+ ro_gui_wimp_event_register_submenu(0);
+
+ /* Process the event for any window menus. Find the window data, then
+ * try and match to an icon event. If we can, then GRight menus are
+ * sent the event; otherwise, we do nothing.
+ */
+
+ window = ro_gui_wimp_event_find_window(w);
+ if (!window)
+ return;
+
+ for (event = window->first; event; event = event->next)
+ if ((event->type == EVENT_MENU_GRIGHT) && (event->i == i))
+ break;
+ if (event) {
+ if (window->menu_close != NULL &&
+ event->type == EVENT_MENU_GRIGHT &&
+ event->data.menu_gright.menu == menu)
+ window->menu_close(w, i, menu);
+ return;
+ }
+
+ /* If the close is for a window menu, then pass the event on to it. */
+
+ if ((window->window_menu) && (window->window_menu == menu) &&
+ (window->menu_close))
+ window->menu_close(w, wimp_ICON_WINDOW, menu);
+}
+
+/**
+ * Register a submenu as being opened
+ */
+void ro_gui_wimp_event_register_submenu(wimp_w w)
+{
+ if (ro_gui_wimp_event_submenu)
+ ro_gui_wimp_event_close_window(ro_gui_wimp_event_submenu);
+ ro_gui_wimp_event_submenu = w;
+}
+
diff --git a/frontends/riscos/wimp_event.h b/frontends/riscos/wimp_event.h
new file mode 100644
index 000000000..0a54ab04d
--- /dev/null
+++ b/frontends/riscos/wimp_event.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2010, 2011 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Automated RISC OS WIMP event handling (interface).
+ */
+
+
+#ifndef _NETSURF_RISCOS_WIMP_EVENT_H_
+#define _NETSURF_RISCOS_WIMP_EVENT_H_
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "oslib/os.h"
+#include "oslib/wimp.h"
+#include "riscos/menus.h"
+
+#define IS_WIMP_KEY (1u<<31)
+
+bool ro_gui_wimp_event_memorise(wimp_w w);
+bool ro_gui_wimp_event_restore(wimp_w w);
+bool ro_gui_wimp_event_validate(wimp_w w);
+bool ro_gui_wimp_event_transfer(wimp_w from, wimp_w to);
+void ro_gui_wimp_event_finalise(wimp_w w);
+void ro_gui_wimp_event_deregister(wimp_w w, wimp_i i);
+
+bool ro_gui_wimp_event_set_help_prefix(wimp_w w, const char *help_prefix);
+const char *ro_gui_wimp_event_get_help_prefix(wimp_w w);
+bool ro_gui_wimp_event_register_help_suffix(wimp_w w,
+ const char *(*get_help_suffix)(wimp_w w, wimp_i i,
+ os_coord *pos, wimp_mouse_state buttons));
+const char *ro_gui_wimp_event_get_help_suffix(wimp_w w, wimp_i i,
+ os_coord *pos, wimp_mouse_state buttons);
+bool ro_gui_wimp_event_set_user_data(wimp_w w, void *user);
+void *ro_gui_wimp_event_get_user_data(wimp_w w);
+
+bool ro_gui_wimp_event_menu_selection(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action);
+bool ro_gui_wimp_event_mouse_click(wimp_pointer *pointer);
+bool ro_gui_wimp_event_keypress(wimp_key *key);
+bool ro_gui_wimp_event_open_window(wimp_open *open);
+bool ro_gui_wimp_event_close_window(wimp_w w);
+bool ro_gui_wimp_event_redraw_window(wimp_draw *redraw);
+bool ro_gui_wimp_event_scroll_window(wimp_scroll *scroll);
+bool ro_gui_wimp_event_pointer_entering_window(wimp_entering *entering);
+
+bool ro_gui_wimp_event_process_window_menu_click(wimp_pointer *pointer);
+bool ro_gui_wimp_event_prepare_menu(wimp_w w, wimp_i i, wimp_menu *menu);
+
+bool ro_gui_wimp_event_register_menu(wimp_w w, wimp_menu *m,
+ bool menu_auto, bool position_ibar);
+bool ro_gui_wimp_event_register_numeric_field(wimp_w w, wimp_i i, wimp_i up,
+ wimp_i down, int min, int max, int stepping,
+ int decimal_places);
+bool ro_gui_wimp_event_register_text_field(wimp_w w, wimp_i i);
+bool ro_gui_wimp_event_register_menu_gright(wimp_w w, wimp_i i,
+ wimp_i gright, wimp_menu *menu);
+bool ro_gui_wimp_event_register_checkbox(wimp_w w, wimp_i i);
+bool ro_gui_wimp_event_register_radio(wimp_w w, wimp_i *i);
+bool ro_gui_wimp_event_register_button(wimp_w w, wimp_i i,
+ void (*callback)(wimp_pointer *pointer));
+bool ro_gui_wimp_event_register_cancel(wimp_w w, wimp_i i);
+bool ro_gui_wimp_event_register_ok(wimp_w w, wimp_i i,
+ bool (*callback)(wimp_w w));
+
+bool ro_gui_wimp_event_register_mouse_click(wimp_w w,
+ bool (*callback)(wimp_pointer *pointer));
+bool ro_gui_wimp_event_register_keypress(wimp_w w,
+ bool (*callback)(wimp_key *key));
+bool ro_gui_wimp_event_register_open_window(wimp_w w,
+ void (*callback)(wimp_open *open));
+bool ro_gui_wimp_event_register_close_window(wimp_w w,
+ void (*callback)(wimp_w w));
+bool ro_gui_wimp_event_register_redraw_window(wimp_w w,
+ void (*callback)(wimp_draw *redraw));
+bool ro_gui_wimp_event_register_scroll_window(wimp_w w,
+ void (*callback)(wimp_scroll *scroll));
+bool ro_gui_wimp_event_register_pointer_entering_window(wimp_w w,
+ void (*callback)(wimp_entering *entering));
+bool ro_gui_wimp_event_register_menu_prepare(wimp_w w,
+ bool (*callback)(wimp_w w, wimp_i i, wimp_menu *m,
+ wimp_pointer *p));
+bool ro_gui_wimp_event_register_menu_selection(wimp_w w,
+ bool (*callback)(wimp_w w, wimp_i i, wimp_menu *m,
+ wimp_selection *s, menu_action a));
+bool ro_gui_wimp_event_register_menu_warning(wimp_w w,
+ void (*callback)(wimp_w w, wimp_i i, wimp_menu *m,
+ wimp_selection *s, menu_action a));
+bool ro_gui_wimp_event_register_menu_close(wimp_w w,
+ void (*callback)(wimp_w w, wimp_i i, wimp_menu *m));
+
+bool ro_gui_wimp_event_submenu_warning(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action);
+void ro_gui_wimp_event_menus_closed(wimp_w w, wimp_i i, wimp_menu *menu);
+void ro_gui_wimp_event_register_submenu(wimp_w w);
+
+#endif
diff --git a/frontends/riscos/wimputils.h b/frontends/riscos/wimputils.h
new file mode 100644
index 000000000..5225a720e
--- /dev/null
+++ b/frontends/riscos/wimputils.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2009 John-Mark Bell <jmb@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * A collection of grubby utilities for working with OSLib's wimp API.
+ */
+
+#ifndef riscos_wimputils_h_
+#define riscos_wimputils_h_
+
+#include <oslib/wimp.h>
+
+/* Magical union to permit aliasing of wimp_window_state and wimp_open
+ * Do not use this directly. Use the macros, instead. */
+typedef union window_open_state {
+ wimp_window_state state;
+ wimp_open open;
+} window_open_state;
+
+/* Convert a pointer to a wimp_window_state into a pointer to a wimp_open */
+#define PTR_WIMP_OPEN(pstate) ((wimp_open *) (window_open_state *) (pstate))
+
+/* Similarly for wimp_message_list */
+typedef struct ns_wimp_message_list {
+ /* Nasty hack to ensure that we have at least one field in the struct */
+ int first;
+ int rest[];
+} ns_wimp_message_list;
+
+typedef union message_list {
+ wimp_message_list wimp;
+ ns_wimp_message_list ns;
+} message_list;
+
+#define PTR_WIMP_MESSAGE_LIST(l) ((wimp_message_list *) (message_list *) (l))
+
+/* Also for VDU variable lists */
+typedef struct ns_os_vdu_var_list {
+ os_vdu_var first;
+ os_vdu_var rest[];
+} ns_os_vdu_var_list;
+
+typedef union vdu_var_list {
+ os_vdu_var_list os;
+ ns_os_vdu_var_list ns;
+} vdu_var_list;
+
+#define PTR_OS_VDU_VAR_LIST(l) ((os_vdu_var_list *) (vdu_var_list *) (l))
+
+#endif
diff --git a/frontends/riscos/window.c b/frontends/riscos/window.c
new file mode 100644
index 000000000..ab1501cd6
--- /dev/null
+++ b/frontends/riscos/window.c
@@ -0,0 +1,5025 @@
+/*
+ * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
+ * Copyright 2004 Andrew Timmins <atimmins@blueyonder.co.uk>
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net>
+ * Copyright 2010-2014 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * Implementation of RISC OS browser window handling.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <oslib/colourtrans.h>
+#include <oslib/osbyte.h>
+#include <oslib/osfile.h>
+#include <oslib/osspriteop.h>
+#include <oslib/wimp.h>
+#include <oslib/wimpspriteop.h>
+#include <nsutils/time.h>
+
+#include "utils/config.h"
+#include "utils/nsoption.h"
+#include "utils/log.h"
+#include "utils/talloc.h"
+#include "utils/file.h"
+#include "utils/utf8.h"
+#include "utils/utils.h"
+#include "utils/messages.h"
+#include "utils/string.h"
+#include "content/content.h"
+#include "content/hlcache.h"
+#include "content/urldb.h"
+#include "desktop/browser_history.h"
+#include "desktop/browser.h"
+#include "desktop/cookie_manager.h"
+#include "desktop/scrollbar.h"
+#include "desktop/frames.h"
+#include "desktop/mouse.h"
+#include "desktop/plotters.h"
+#include "desktop/textinput.h"
+#include "desktop/tree.h"
+#include "desktop/gui_window.h"
+#include "image/bitmap.h"
+#include "render/form.h"
+
+#include "riscos/bitmap.h"
+#include "riscos/buffer.h"
+#include "riscos/cookies.h"
+#include "riscos/dialog.h"
+#include "riscos/global_history.h"
+#include "riscos/gui.h"
+#include "riscos/gui/status_bar.h"
+#include "riscos/help.h"
+#include "riscos/hotlist.h"
+#include "riscos/menus.h"
+#include "riscos/mouse.h"
+#include "riscos/oslib_pre7.h"
+#include "riscos/save.h"
+#include "riscos/content-handlers/sprite.h"
+#include "riscos/textselection.h"
+#include "riscos/toolbar.h"
+#include "riscos/url_complete.h"
+#include "riscos/url_suggest.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/wimputils.h"
+#include "riscos/window.h"
+#include "riscos/ucstables.h"
+#include "riscos/filetype.h"
+
+void gui_window_redraw_window(struct gui_window *g);
+
+static void gui_window_set_extent(struct gui_window *g, int width, int height);
+
+static void ro_gui_window_redraw(wimp_draw *redraw);
+static void ro_gui_window_scroll(wimp_scroll *scroll);
+static void ro_gui_window_pointer_entering(wimp_entering *entering);
+static void ro_gui_window_track_end(wimp_leaving *leaving, void *data);
+static void ro_gui_window_open(wimp_open *open);
+static void ro_gui_window_close(wimp_w w);
+static bool ro_gui_window_click(wimp_pointer *mouse);
+static bool ro_gui_window_keypress(wimp_key *key);
+static bool ro_gui_window_toolbar_keypress(void *data, wimp_key *key);
+static bool ro_gui_window_handle_local_keypress(struct gui_window *g,
+ wimp_key *key, bool is_toolbar);
+static bool ro_gui_window_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_pointer *pointer);
+static void ro_gui_window_menu_warning(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action);
+static bool ro_gui_window_menu_select(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action);
+static void ro_gui_window_menu_close(wimp_w w, wimp_i i, wimp_menu *menu);
+
+static void ro_gui_window_scroll_end(wimp_dragged *drag, void *data);
+
+static void ro_gui_window_scroll_action(struct gui_window *g,
+ int scroll_x, int scroll_y);
+
+static void ro_gui_window_toolbar_click(void *data,
+ toolbar_action_type action_type, union toolbar_action action);
+
+static bool ro_gui_window_content_export_types(hlcache_handle *h,
+ bool *export_draw, bool *export_sprite);
+static void ro_gui_window_prepare_pageinfo(struct gui_window *g);
+static void ro_gui_window_prepare_objectinfo(hlcache_handle *object,
+ nsurl *target_url);
+
+static void ro_gui_window_launch_url(struct gui_window *g, const char *url);
+static void ro_gui_window_action_home(struct gui_window *g);
+static void ro_gui_window_action_new_window(struct gui_window *g);
+static void ro_gui_window_action_local_history(struct gui_window *g);
+static void ro_gui_window_action_save(struct gui_window *g,
+ gui_save_type save_type);
+static void ro_gui_window_action_search(struct gui_window *g);
+static void ro_gui_window_action_zoom(struct gui_window *g);
+static void ro_gui_window_action_add_bookmark(struct gui_window *g);
+static void ro_gui_window_action_remove_bookmark(struct gui_window *g);
+static void ro_gui_window_action_print(struct gui_window *g);
+static void ro_gui_window_action_page_info(struct gui_window *g);
+
+static void ro_gui_window_remove_update_boxes(struct gui_window *g);
+static void ro_gui_window_update_toolbar_buttons(struct gui_window *g);
+static void ro_gui_window_update_toolbar(void *data);
+static void ro_gui_window_save_toolbar_buttons(void *data, char *config);
+static void ro_gui_window_update_theme(void *data, bool ok);
+
+static bool ro_gui_window_import_text(struct gui_window *g,
+ const char *filename);
+static void ro_gui_window_clone_options(
+ struct gui_window *new_gui,
+ struct gui_window *old_gui);
+
+static bool ro_gui_window_prepare_form_select_menu(struct gui_window *bw,
+ struct form_control *control);
+static void ro_gui_window_process_form_select_menu(struct gui_window *g,
+ wimp_selection *selection);
+
+#ifndef wimp_KEY_END
+#define wimp_KEY_END wimp_KEY_COPY
+#endif
+
+#ifndef wimp_WINDOW_GIVE_SHADED_ICON_INFO
+ /* RISC OS 5+. Requires OSLib trunk. */
+#define wimp_WINDOW_GIVE_SHADED_ICON_INFO ((wimp_extra_window_flags) 0x10u)
+#endif
+
+#define SCROLL_VISIBLE_PADDING 32
+
+/** Remembers which iconised sprite numbers are in use */
+static bool iconise_used[64];
+static int iconise_next = 0;
+
+/** Whether a pressed mouse button has become a drag */
+static bool mouse_drag_select;
+static bool mouse_drag_adjust;
+
+/** List of all browser windows. */
+static struct gui_window *window_list = 0;
+/** GUI window which is being redrawn. Valid only during redraw. */
+struct gui_window *ro_gui_current_redraw_gui;
+/** Form control which gui_form_select_menu is for. */
+static struct form_control *gui_form_select_control;
+/** The browser window menu handle. */
+static wimp_menu *ro_gui_browser_window_menu = NULL;
+/** Menu of options for form select controls. */
+static wimp_menu *gui_form_select_menu = NULL;
+/** Main content object under menu, or 0 if none. */
+static hlcache_handle *current_menu_main = 0;
+/** Object under menu, or 0 if no object. */
+static hlcache_handle *current_menu_object = 0;
+/** URL of link under menu, or 0 if no link. */
+static nsurl *current_menu_url = 0;
+
+static float scale_snap_to[] = {0.10, 0.125, 0.25, 0.333, 0.5, 0.75,
+ 1.0,
+ 1.5, 2.0, 3.0, 4.0, 6.0, 8.0, 12.0, 16.0};
+#define SCALE_SNAP_TO_SIZE (sizeof scale_snap_to) / (sizeof(float))
+
+/** An entry in ro_gui_pointer_table. */
+struct ro_gui_pointer_entry {
+ bool wimp_area; /** The pointer is in the Wimp's sprite area. */
+ char sprite_name[16];
+ int xactive;
+ int yactive;
+};
+
+/** Map from gui_pointer_shape to pointer sprite data. Must be ordered as
+ * enum gui_pointer_shape. */
+struct ro_gui_pointer_entry ro_gui_pointer_table[] = {
+ { true, "ptr_default", 0, 0 },
+ { false, "ptr_point", 6, 0 },
+ { false, "ptr_caret", 4, 9 },
+ { false, "ptr_menu", 6, 4 },
+ { false, "ptr_ud", 6, 7 },
+ { false, "ptr_ud", 6, 7 },
+ { false, "ptr_lr", 7, 6 },
+ { false, "ptr_lr", 7, 6 },
+ { false, "ptr_ld", 7, 7 },
+ { false, "ptr_ld", 7, 7 },
+ { false, "ptr_rd", 7, 7 },
+ { false, "ptr_rd", 6, 7 },
+ { false, "ptr_cross", 7, 7 },
+ { false, "ptr_move", 8, 0 },
+ { false, "ptr_wait", 7, 10 },
+ { false, "ptr_help", 0, 0 },
+ { false, "ptr_nodrop", 0, 0 },
+ { false, "ptr_nt_allwd", 10, 10 },
+ { false, "ptr_progress", 0, 0 },
+};
+
+struct update_box {
+ int x0;
+ int y0;
+ int x1;
+ int y1;
+ bool use_buffer;
+ struct gui_window *g;
+ struct update_box *next;
+};
+
+struct update_box *pending_updates;
+#define MARGIN 4
+
+static const struct toolbar_callbacks ro_gui_window_toolbar_callbacks = {
+ ro_gui_window_update_theme,
+ ro_gui_window_update_toolbar,
+ (void (*)(void *)) ro_gui_window_update_toolbar_buttons,
+ ro_gui_window_toolbar_click,
+ ro_gui_window_toolbar_keypress,
+ ro_gui_window_save_toolbar_buttons
+};
+
+
+/**
+ * Initialise the browser window module and its menus.
+ */
+
+void ro_gui_window_initialise(void)
+{
+ /* Build the browser window menu. */
+
+ static const struct ns_menu browser_definition = {
+ "NetSurf", {
+ { "Page", BROWSER_PAGE, 0 },
+ { "Page.PageInfo",BROWSER_PAGE_INFO, &dialog_pageinfo },
+ { "Page.Save", BROWSER_SAVE, &dialog_saveas },
+ { "Page.SaveComp", BROWSER_SAVE_COMPLETE, &dialog_saveas },
+ { "Page.Export", NO_ACTION, 0 },
+#ifdef WITH_DRAW_EXPORT
+ { "Page.Export.Draw", BROWSER_EXPORT_DRAW, &dialog_saveas },
+#endif
+#ifdef WITH_PDF_EXPORT
+ { "Page.Export.PDF", BROWSER_EXPORT_PDF, &dialog_saveas },
+#endif
+ { "Page.Export.Text", BROWSER_EXPORT_TEXT, &dialog_saveas },
+ { "Page.SaveURL", NO_ACTION, 0 },
+ { "Page.SaveURL.URI", BROWSER_SAVE_URL_URI, &dialog_saveas },
+ { "Page.SaveURL.URL", BROWSER_SAVE_URL_URL, &dialog_saveas },
+ { "Page.SaveURL.LinkText", BROWSER_SAVE_URL_TEXT, &dialog_saveas },
+ { "_Page.Print", BROWSER_PRINT, &dialog_print },
+ { "Page.NewWindow", BROWSER_NEW_WINDOW, 0 },
+ { "Page.FindText", BROWSER_FIND_TEXT, &dialog_search },
+ { "Page.ViewSrc", BROWSER_VIEW_SOURCE, 0 },
+ { "Object", BROWSER_OBJECT, 0 },
+ { "Object.Object", BROWSER_OBJECT_OBJECT, 0 },
+ { "Object.Object.ObjInfo", BROWSER_OBJECT_INFO, &dialog_objinfo },
+ { "Object.Object.ObjSave", BROWSER_OBJECT_SAVE, &dialog_saveas },
+ { "Object.Object.Export", BROWSER_OBJECT_EXPORT, 0 },
+ { "Object.Object.Export.Sprite", BROWSER_OBJECT_EXPORT_SPRITE, &dialog_saveas },
+#ifdef WITH_DRAW_EXPORT
+ { "Object.Object.Export.ObjDraw", BROWSER_OBJECT_EXPORT_DRAW, &dialog_saveas },
+#endif
+ { "Object.Object.SaveURL", NO_ACTION, 0 },
+ { "Object.Object.SaveURL.URI", BROWSER_OBJECT_SAVE_URL_URI, &dialog_saveas },
+ { "Object.Object.SaveURL.URL", BROWSER_OBJECT_SAVE_URL_URL, &dialog_saveas },
+ { "Object.Object.SaveURL.LinkText", BROWSER_OBJECT_SAVE_URL_TEXT, &dialog_saveas },
+ { "Object.Object.ObjPrint", BROWSER_OBJECT_PRINT, 0 },
+ { "Object.Object.ObjReload", BROWSER_OBJECT_RELOAD, 0 },
+ { "Object.Link", BROWSER_OBJECT_LINK, 0 },
+ { "Object.Link.LinkSave", BROWSER_LINK_SAVE, 0 },
+ { "Object.Link.LinkSave.URI", BROWSER_LINK_SAVE_URI, &dialog_saveas },
+ { "Object.Link.LinkSave.URL", BROWSER_LINK_SAVE_URL, &dialog_saveas },
+ { "Object.Link.LinkSave.LinkText", BROWSER_LINK_SAVE_TEXT, &dialog_saveas },
+ { "_Object.Link.LinkDload", BROWSER_LINK_DOWNLOAD, 0 },
+ { "Object.Link.LinkNew", BROWSER_LINK_NEW_WINDOW, 0 },
+ { "Selection", BROWSER_SELECTION, 0 },
+ { "_Selection.SelSave", BROWSER_SELECTION_SAVE, &dialog_saveas },
+ { "Selection.Copy", BROWSER_SELECTION_COPY, 0 },
+ { "Selection.Cut", BROWSER_SELECTION_CUT, 0 },
+ { "_Selection.Paste", BROWSER_SELECTION_PASTE, 0 },
+ { "Selection.Clear", BROWSER_SELECTION_CLEAR, 0 },
+ { "Selection.SelectAll", BROWSER_SELECTION_ALL, 0 },
+ { "Navigate", NO_ACTION, 0 },
+ { "Navigate.Home", BROWSER_NAVIGATE_HOME, 0 },
+ { "Navigate.Back", BROWSER_NAVIGATE_BACK, 0 },
+ { "Navigate.Forward", BROWSER_NAVIGATE_FORWARD, 0 },
+ { "_Navigate.UpLevel", BROWSER_NAVIGATE_UP, 0 },
+ { "Navigate.Reload", BROWSER_NAVIGATE_RELOAD_ALL, 0 },
+ { "Navigate.Stop", BROWSER_NAVIGATE_STOP, 0 },
+ { "View", NO_ACTION, 0 },
+ { "View.ScaleView", BROWSER_SCALE_VIEW, &dialog_zoom },
+ { "View.Images", NO_ACTION, 0 },
+ { "View.Images.ForeImg", BROWSER_IMAGES_FOREGROUND, 0 },
+ { "View.Images.BackImg", BROWSER_IMAGES_BACKGROUND, 0 },
+ { "View.Toolbars", NO_ACTION, 0 },
+ { "View.Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 },
+ { "View.Toolbars.ToolAddress", TOOLBAR_ADDRESS_BAR, 0 },
+ { "_View.Toolbars.ToolThrob", TOOLBAR_THROBBER, 0 },
+ { "View.Toolbars.EditToolbar", TOOLBAR_EDIT, 0 },
+ { "_View.Render", NO_ACTION, 0 },
+ { "View.Render.RenderAnims", BROWSER_BUFFER_ANIMS, 0 },
+ { "View.Render.RenderAll", BROWSER_BUFFER_ALL, 0 },
+ { "_View.OptDefault", BROWSER_SAVE_VIEW, 0 },
+ { "View.Window", NO_ACTION, 0 },
+ { "View.Window.WindowSave", BROWSER_WINDOW_DEFAULT, 0 },
+ { "View.Window.WindowStagr", BROWSER_WINDOW_STAGGER, 0 },
+ { "_View.Window.WindowSize", BROWSER_WINDOW_COPY, 0 },
+ { "View.Window.WindowReset", BROWSER_WINDOW_RESET, 0 },
+ { "Utilities", NO_ACTION, 0 },
+ { "Utilities.Hotlist", HOTLIST_SHOW, 0 },
+ { "Utilities.Hotlist.HotlistAdd", HOTLIST_ADD_URL, 0 },
+ { "Utilities.Hotlist.HotlistShow", HOTLIST_SHOW, 0 },
+ { "Utilities.History", HISTORY_SHOW_GLOBAL, 0 },
+ { "Utilities.History.HistLocal", HISTORY_SHOW_LOCAL, 0 },
+ { "Utilities.History.HistGlobal", HISTORY_SHOW_GLOBAL, 0 },
+ { "Utilities.Cookies", COOKIES_SHOW, 0 },
+ { "Utilities.Cookies.ShowCookies", COOKIES_SHOW, 0 },
+ { "Utilities.Cookies.DeleteCookies", COOKIES_DELETE, 0 },
+ { "Help", HELP_OPEN_CONTENTS, 0 },
+ { "Help.HelpContent", HELP_OPEN_CONTENTS, 0 },
+ { "Help.HelpGuide", HELP_OPEN_GUIDE, 0 },
+ { "_Help.HelpInfo", HELP_OPEN_INFORMATION, 0 },
+ { "Help.HelpCredits", HELP_OPEN_CREDITS, 0 },
+ { "_Help.HelpLicence", HELP_OPEN_LICENCE, 0 },
+ { "Help.HelpInter", HELP_LAUNCH_INTERACTIVE, 0 },
+ {NULL, 0, 0}
+ }
+ };
+ ro_gui_browser_window_menu =
+ ro_gui_menu_define_menu(&browser_definition);
+
+}
+
+
+/*
+ * Interface With Core
+ */
+
+/**
+ * Place the caret in a browser window.
+ *
+ * \param g window with caret
+ * \param x coordinates of caret
+ * \param y coordinates of caret
+ * \param height height of caret
+ * \param clip clip rectangle, or NULL if none
+ */
+
+static void gui_window_place_caret(struct gui_window *g, int x, int y, int height,
+ const struct rect *clip)
+{
+ os_error *error;
+
+ error = xwimp_set_caret_position(g->window, -1,
+ x * 2, -(y + height) * 2, height * 2, -1);
+ if (error) {
+ LOG("xwimp_set_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+}
+
+/**
+ * Create and open a new browser window.
+ *
+ * \param bw bw to create gui_window for
+ * \param existing an existing gui_window, may be NULL
+ * \param flags flags for gui window creation
+ * \return gui window, or NULL on error
+ */
+
+static struct gui_window *gui_window_create(struct browser_window *bw,
+ struct gui_window *existing,
+ gui_window_create_flags flags)
+{
+ int screen_width, screen_height;
+ static int window_count = 2;
+ wimp_window window;
+ wimp_window_state state;
+ os_error *error;
+ bool open_centred = true;
+ struct gui_window *g;
+
+ g = malloc(sizeof *g);
+ if (!g) {
+ ro_warn_user("NoMemory", 0);
+ return 0;
+ }
+ g->bw = bw;
+ g->toolbar = 0;
+ g->status_bar = 0;
+ g->old_width = 0;
+ g->old_height = 0;
+ g->update_extent = true;
+ g->active = false;
+ strcpy(g->title, "NetSurf");
+ g->iconise_icon = -1;
+ g->scale = browser_window_get_scale(bw);
+
+ /* Set the window position */
+ if (existing != NULL &&
+ flags & GW_CREATE_CLONE &&
+ nsoption_bool(window_size_clone)) {
+ state.w = existing->window;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ window.visible.x0 = state.visible.x0;
+ window.visible.x1 = state.visible.x1;
+ window.visible.y0 = state.visible.y0 - 48;
+ window.visible.y1 = state.visible.y1 - 48;
+ open_centred = false;
+ } else {
+ int win_width, win_height;
+ ro_gui_screen_size(&screen_width, &screen_height);
+
+ /* Check if we have a preferred position */
+ if ((nsoption_int(window_screen_width) != 0) &&
+ (nsoption_int(window_screen_height) != 0)) {
+ win_width = (nsoption_int(window_width) *
+ screen_width) /
+ nsoption_int(window_screen_width);
+ win_height = (nsoption_int(window_height) *
+ screen_height) /
+ nsoption_int(window_screen_height);
+ window.visible.x0 = (nsoption_int(window_x) *
+ screen_width) /
+ nsoption_int(window_screen_width);
+ window.visible.y0 = (nsoption_int(window_y) *
+ screen_height) /
+ nsoption_int(window_screen_height);
+ if (nsoption_bool(window_stagger)) {
+ window.visible.y0 += 96 -
+ (48 * (window_count % 5));
+ }
+ open_centred = false;
+ if (win_width < 100)
+ win_width = 100;
+ if (win_height < 100)
+ win_height = 100;
+ } else {
+
+ /* Base how we define the window height/width
+ on the compile time options set */
+ win_width = screen_width * 3 / 4;
+ if (1600 < win_width)
+ win_width = 1600;
+ win_height = win_width * 3 / 4;
+
+ window.visible.x0 = (screen_width - win_width) / 2;
+ window.visible.y0 = ((screen_height - win_height) / 2) +
+ 96 - (48 * (window_count % 5));
+ }
+ window.visible.x1 = window.visible.x0 + win_width;
+ window.visible.y1 = window.visible.y0 + win_height;
+ }
+
+ /* General flags for a non-movable, non-resizable, no-title bar window */
+ window.xscroll = 0;
+ window.yscroll = 0;
+ window.next = wimp_TOP;
+ window.flags = wimp_WINDOW_MOVEABLE |
+ wimp_WINDOW_NEW_FORMAT |
+ wimp_WINDOW_VSCROLL |
+ wimp_WINDOW_HSCROLL |
+ wimp_WINDOW_IGNORE_XEXTENT |
+ wimp_WINDOW_IGNORE_YEXTENT |
+ wimp_WINDOW_SCROLL_REPEAT;
+ window.title_fg = wimp_COLOUR_BLACK;
+ window.title_bg = wimp_COLOUR_LIGHT_GREY;
+ window.work_fg = wimp_COLOUR_LIGHT_GREY;
+ window.work_bg = wimp_COLOUR_TRANSPARENT;
+ window.scroll_outer = wimp_COLOUR_DARK_GREY;
+ window.scroll_inner = wimp_COLOUR_MID_LIGHT_GREY;
+ window.highlight_bg = wimp_COLOUR_CREAM;
+ window.extra_flags = wimp_WINDOW_USE_EXTENDED_SCROLL_REQUEST |
+ wimp_WINDOW_GIVE_SHADED_ICON_INFO;
+ window.extent.x0 = 0;
+ window.extent.y0 = -(window.visible.y1 - window.visible.y0);
+ window.extent.x1 = window.visible.x1 - window.visible.x0;
+ window.extent.y1 = 0;
+ window.title_flags = wimp_ICON_TEXT |
+ wimp_ICON_INDIRECTED |
+ wimp_ICON_HCENTRED;
+ window.work_flags = wimp_BUTTON_DOUBLE_CLICK_DRAG <<
+ wimp_ICON_BUTTON_TYPE_SHIFT;
+ window.sprite_area = wimpspriteop_AREA;
+ window.xmin = 1;
+ window.ymin = 1;
+ window.title_data.indirected_text.text = g->title;
+ window.title_data.indirected_text.validation = (char *) -1;
+ window.title_data.indirected_text.size = 255;
+ window.icon_count = 0;
+
+ /* Add in flags */
+ window.flags |= wimp_WINDOW_SIZE_ICON |
+ wimp_WINDOW_BACK_ICON |
+ wimp_WINDOW_CLOSE_ICON |
+ wimp_WINDOW_TITLE_ICON |
+ wimp_WINDOW_TOGGLE_ICON;
+
+ if (open_centred) {
+ int scroll_width = ro_get_vscroll_width(NULL);
+ window.visible.x0 -= scroll_width;
+ }
+
+ error = xwimp_create_window(&window, &g->window);
+ if (error) {
+ LOG("xwimp_create_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ free(g);
+ return 0;
+ }
+
+ /* Link into window list */
+ g->prev = 0;
+ g->next = window_list;
+ if (window_list)
+ window_list->prev = g;
+ window_list = g;
+ window_count++;
+
+ /* Add in a toolbar and status bar */
+ g->status_bar = ro_gui_status_bar_create(g->window,
+ nsoption_int(toolbar_status_size));
+ g->toolbar = ro_toolbar_create(NULL, g->window,
+ THEME_STYLE_BROWSER_TOOLBAR, TOOLBAR_FLAGS_NONE,
+ &ro_gui_window_toolbar_callbacks, g,
+ "HelpToolbar");
+ if (g->toolbar != NULL) {
+ ro_toolbar_add_buttons(g->toolbar,
+ brower_toolbar_buttons,
+ nsoption_charp(toolbar_browser));
+ ro_toolbar_add_url(g->toolbar);
+ ro_toolbar_add_throbber(g->toolbar);
+ ro_toolbar_rebuild(g->toolbar);
+ }
+
+ /* Register event handlers. Do this quickly, as some of the things
+ * that follow will indirectly look up our user data: this MUST
+ * be set first!
+ */
+ ro_gui_wimp_event_set_user_data(g->window, g);
+ ro_gui_wimp_event_register_open_window(g->window, ro_gui_window_open);
+ ro_gui_wimp_event_register_close_window(g->window, ro_gui_window_close);
+ ro_gui_wimp_event_register_redraw_window(g->window, ro_gui_window_redraw);
+ ro_gui_wimp_event_register_scroll_window(g->window, ro_gui_window_scroll);
+ ro_gui_wimp_event_register_pointer_entering_window(g->window, ro_gui_window_pointer_entering);
+ ro_gui_wimp_event_register_keypress(g->window, ro_gui_window_keypress);
+ ro_gui_wimp_event_register_mouse_click(g->window, ro_gui_window_click);
+ ro_gui_wimp_event_register_menu(g->window, ro_gui_browser_window_menu,
+ true, false);
+ ro_gui_wimp_event_register_menu_prepare(g->window,
+ ro_gui_window_menu_prepare);
+ ro_gui_wimp_event_register_menu_selection(g->window,
+ ro_gui_window_menu_select);
+ ro_gui_wimp_event_register_menu_warning(g->window,
+ ro_gui_window_menu_warning);
+ ro_gui_wimp_event_register_menu_close(g->window,
+ ro_gui_window_menu_close);
+
+ /* Set the window options */
+ ro_gui_window_clone_options(g, existing);
+ ro_gui_window_update_toolbar_buttons(g);
+
+ /* Open the window at the top of the stack */
+ state.w = g->window;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return g;
+ }
+
+ state.next = wimp_TOP;
+
+ ro_gui_window_open(PTR_WIMP_OPEN(&state));
+
+ /* Claim the caret */
+ if (ro_toolbar_take_caret(g->toolbar))
+ ro_gui_url_complete_start(g->toolbar);
+ else
+ gui_window_place_caret(g, -100, -100, 0, NULL);
+
+ return g;
+}
+
+
+/**
+ * Close a browser window and free any related resources.
+ *
+ * \param g gui_window to destroy
+ */
+
+static void gui_window_destroy(struct gui_window *g)
+{
+ os_error *error;
+ wimp_w w;
+
+ assert(g);
+
+ /* stop any tracking */
+ ro_mouse_kill(g);
+
+ /* remove from list */
+ if (g->prev)
+ g->prev->next = g->next;
+ else
+ window_list = g->next;
+ if (g->next)
+ g->next->prev = g->prev;
+
+ /* destroy toolbar */
+ if (g->toolbar)
+ ro_toolbar_destroy(g->toolbar);
+ if (g->status_bar)
+ ro_gui_status_bar_destroy(g->status_bar);
+
+ w = g->window;
+ ro_gui_url_complete_close();
+ ro_gui_dialog_close_persistent(w);
+ if (current_menu_window == w)
+ ro_gui_menu_destroy();
+ ro_gui_window_remove_update_boxes(g);
+
+ /* delete window */
+ error = xwimp_delete_window(w);
+ if (error) {
+ LOG("xwimp_delete_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ ro_gui_wimp_event_finalise(w);
+
+ free(g);
+}
+
+
+/**
+ * Set the title of a browser window.
+ *
+ * \param g gui_window to update
+ * \param title new window title, copied
+ */
+
+static void gui_window_set_title(struct gui_window *g, const char *title)
+{
+ assert(g);
+ assert(title);
+
+ if (g->scale != 1.0) {
+ int scale_disp = g->scale * 100;
+
+ if (ABS((float)scale_disp - g->scale * 100) >= 0.05)
+ snprintf(g->title, sizeof g->title, "%s (%.1f%%)",
+ title, g->scale * 100);
+ else
+ snprintf(g->title, sizeof g->title, "%s (%i%%)",
+ title, scale_disp);
+ } else {
+ strncpy(g->title, title, sizeof g->title);
+ }
+
+ ro_gui_set_window_title(g->window, g->title);
+}
+
+
+/**
+ * Force a redraw of the entire contents of a browser window.
+ *
+ * \param g gui_window to redraw
+ */
+void gui_window_redraw_window(struct gui_window *g)
+{
+ wimp_window_info info;
+ os_error *error;
+
+ assert(g);
+ info.w = g->window;
+ error = xwimp_get_window_info_header_only(&info);
+ if (error) {
+ LOG("xwimp_get_window_info_header_only: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ error = xwimp_force_redraw(g->window, info.extent.x0, info.extent.y0,
+ info.extent.x1, info.extent.y1);
+ if (error) {
+ LOG("xwimp_force_redraw: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+}
+
+
+/**
+ * Redraw an area of a window.
+ *
+ * \param g The window to update
+ * \param rect The area of the window to update.
+ */
+
+static void gui_window_update_box(struct gui_window *g, const struct rect *rect)
+{
+ bool use_buffer;
+ int x0, y0, x1, y1;
+ struct update_box *cur;
+
+ x0 = floorf(rect->x0 * 2 * g->scale);
+ y0 = -ceilf(rect->y1 * 2 * g->scale);
+ x1 = ceilf(rect->x1 * 2 * g->scale) + 1;
+ y1 = -floorf(rect->y0 * 2 * g->scale) + 1;
+ use_buffer =
+ (g->option.buffer_everything || g->option.buffer_animations);
+
+ /* try to optimise buffered redraws */
+ if (use_buffer) {
+ for (cur = pending_updates; cur != NULL; cur = cur->next) {
+ if ((cur->g != g) || (!cur->use_buffer))
+ continue;
+ if ((((cur->x0 - x1) < MARGIN) || ((cur->x1 - x0) < MARGIN)) &&
+ (((cur->y0 - y1) < MARGIN) || ((cur->y1 - y0) < MARGIN))) {
+ cur->x0 = min(cur->x0, x0);
+ cur->y0 = min(cur->y0, y0);
+ cur->x1 = max(cur->x1, x1);
+ cur->y1 = max(cur->y1, y1);
+ return;
+ }
+
+ }
+ }
+ cur = malloc(sizeof(struct update_box));
+ if (!cur) {
+ LOG("No memory for malloc.");
+ ro_warn_user("NoMemory", 0);
+ return;
+ }
+ cur->x0 = x0;
+ cur->y0 = y0;
+ cur->x1 = x1;
+ cur->y1 = y1;
+ cur->next = pending_updates;
+ pending_updates = cur;
+ cur->g = g;
+ cur->use_buffer = use_buffer;
+}
+
+
+/**
+ * Get the scroll position of a browser window.
+ *
+ * \param g gui_window
+ * \param sx receives x ordinate of point at top-left of window
+ * \param sy receives y ordinate of point at top-left of window
+ * \return true iff successful
+ */
+
+static bool gui_window_get_scroll(struct gui_window *g, int *sx, int *sy)
+{
+ wimp_window_state state;
+ os_error *error;
+ int toolbar_height = 0;
+
+ assert(g);
+
+ state.w = g->window;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ if (g->toolbar)
+ toolbar_height = ro_toolbar_full_height(g->toolbar);
+ *sx = state.xscroll / (2 * g->scale);
+ *sy = -(state.yscroll - toolbar_height) / (2 * g->scale);
+ return true;
+}
+
+
+/**
+ * Set the scroll position of a browser window.
+ *
+ * \param g gui_window to scroll
+ * \param sx point to place at top-left of window
+ * \param sy point to place at top-left of window
+ */
+
+static void gui_window_set_scroll(struct gui_window *g, int sx, int sy)
+{
+ wimp_window_state state;
+ os_error *error;
+
+ assert(g);
+
+ state.w = g->window;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ state.xscroll = sx * 2 * g->scale;
+ state.yscroll = -sy * 2 * g->scale;
+ if (g->toolbar)
+ state.yscroll += ro_toolbar_full_height(g->toolbar);
+ ro_gui_window_open(PTR_WIMP_OPEN(&state));
+}
+
+
+/**
+ * Scrolls the specified area of a browser window into view.
+ *
+ * \param g gui_window to scroll
+ * \param x0 left point to ensure visible
+ * \param y0 bottom point to ensure visible
+ * \param x1 right point to ensure visible
+ * \param y1 top point to ensure visible
+ */
+static void gui_window_scroll_visible(struct gui_window *g, int x0, int y0, int x1, int y1)
+{
+ wimp_window_state state;
+ os_error *error;
+ int cx0, cy0, width, height;
+ int padding_available;
+ int toolbar_height = 0;
+ int correction;
+
+ assert(g);
+
+ state.w = g->window;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ if (g->toolbar)
+ toolbar_height = ro_toolbar_full_height(g->toolbar);
+
+ x0 = x0 * 2 * g->scale;
+ y0 = y0 * 2 * g->scale;
+ x1 = x1 * 2 * g->scale;
+ y1 = y1 * 2 * g->scale;
+
+ cx0 = state.xscroll;
+ cy0 = -state.yscroll + toolbar_height;
+ width = state.visible.x1 - state.visible.x0;
+ height = state.visible.y1 - state.visible.y0 - toolbar_height;
+
+ /* make sure we're visible */
+ correction = (x1 - cx0 - width);
+ if (correction > 0)
+ cx0 += correction;
+ correction = (y1 - cy0 - height);
+ if (correction > 0)
+ cy0 += correction;
+ if (x0 < cx0)
+ cx0 = x0;
+ if (y0 < cy0)
+ cy0 = y0;
+
+ /* try to give a SCROLL_VISIBLE_PADDING border of space around us */
+ padding_available = (width - x1 + x0) / 2;
+ if (padding_available > 0) {
+ if (padding_available > SCROLL_VISIBLE_PADDING)
+ padding_available = SCROLL_VISIBLE_PADDING;
+ correction = (cx0 + width - x1);
+ if (correction < padding_available)
+ cx0 += padding_available;
+ correction = (x0 - cx0);
+ if (correction < padding_available)
+ cx0 -= padding_available;
+ }
+ padding_available = (height - y1 + y0) / 2;
+ if (padding_available > 0) {
+ if (padding_available > SCROLL_VISIBLE_PADDING)
+ padding_available = SCROLL_VISIBLE_PADDING;
+ correction = (cy0 + height - y1);
+ if (correction < padding_available)
+ cy0 += padding_available;
+ correction = (y0 - cy0);
+ if (correction < padding_available)
+ cy0 -= padding_available;
+ }
+
+ state.xscroll = cx0;
+ state.yscroll = -cy0 + toolbar_height;
+ ro_gui_window_open(PTR_WIMP_OPEN(&state));
+}
+
+
+/**
+ * Find the current dimensions of a browser window's content area.
+ *
+ * \param g gui_window to measure
+ * \param width receives width of window
+ * \param height receives height of window
+ * \param scaled whether to return scaled values
+ */
+
+static void gui_window_get_dimensions(struct gui_window *g, int *width, int *height, bool scaled)
+{
+ /* use the cached window sizes */
+ *width = g->old_width / 2;
+ *height = g->old_height / 2;
+ if (scaled) {
+ *width /= g->scale;
+ *height /= g->scale;
+ }
+}
+
+
+/**
+ * Update the extent of the inside of a browser window to that of the
+ * current content.
+ *
+ * \param g gui_window to update the extent of
+ */
+
+static void gui_window_update_extent(struct gui_window *g)
+{
+ os_error *error;
+ wimp_window_info info;
+
+ assert(g);
+
+ info.w = g->window;
+ error = xwimp_get_window_info_header_only(&info);
+ if (error) {
+ LOG("xwimp_get_window_info_header_only: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* scroll on toolbar height change */
+ if (g->toolbar) {
+ int scroll = ro_toolbar_height(g->toolbar) - info.extent.y1;
+ info.yscroll += scroll;
+ }
+
+ /* Handle change of extents */
+ g->update_extent = true;
+ ro_gui_window_open(PTR_WIMP_OPEN(&info));
+}
+
+
+/**
+ * Set the status bar of a browser window.
+ *
+ * \param g gui_window to update
+ * \param text new status text
+ */
+
+static void riscos_window_set_status(struct gui_window *g, const char *text)
+{
+ if (g->status_bar)
+ ro_gui_status_bar_set_text(g->status_bar, text);
+}
+
+
+/**
+ * Change mouse pointer shape
+ */
+
+void gui_window_set_pointer(struct gui_window *g, gui_pointer_shape shape)
+{
+ static gui_pointer_shape curr_pointer = GUI_POINTER_DEFAULT;
+ struct ro_gui_pointer_entry *entry;
+ os_error *error;
+
+ if (shape == curr_pointer)
+ return;
+
+ assert(shape < sizeof ro_gui_pointer_table /
+ sizeof ro_gui_pointer_table[0]);
+
+ entry = &ro_gui_pointer_table[shape];
+
+ if (entry->wimp_area) {
+ /* pointer in the Wimp's sprite area */
+ error = xwimpspriteop_set_pointer_shape(entry->sprite_name,
+ 1, entry->xactive, entry->yactive, 0, 0);
+ if (error) {
+ LOG("xwimpspriteop_set_pointer_shape: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ } else {
+ /* pointer in our own sprite area */
+ error = xosspriteop_set_pointer_shape(osspriteop_USER_AREA,
+ gui_sprites,
+ (osspriteop_id) entry->sprite_name,
+ 1, entry->xactive, entry->yactive, 0, 0);
+ if (error) {
+ LOG("xosspriteop_set_pointer_shape: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ }
+
+ curr_pointer = shape;
+}
+
+
+/* exported function documented in riscos/window.h */
+nserror ro_gui_window_set_url(struct gui_window *g, nsurl *url)
+{
+ size_t idn_url_l;
+ char *idn_url_s = NULL;
+
+ if (g->toolbar) {
+ if (nsoption_bool(display_decoded_idn) == true) {
+ if (nsurl_get_utf8(url, &idn_url_s, &idn_url_l) != NSERROR_OK)
+ idn_url_s = NULL;
+ }
+
+ ro_toolbar_set_url(g->toolbar, idn_url_s ? idn_url_s : nsurl_access(url), true, false);
+
+ if (idn_url_s)
+ free(idn_url_s);
+
+ ro_gui_url_complete_start(g->toolbar);
+ }
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Update the interface to reflect start of page loading.
+ *
+ * \param g window with start of load
+ */
+
+static void gui_window_start_throbber(struct gui_window *g)
+{
+ ro_gui_window_update_toolbar_buttons(g);
+ ro_gui_menu_refresh(ro_gui_browser_window_menu);
+ if (g->toolbar != NULL)
+ ro_toolbar_start_throbbing(g->toolbar);
+ g->active = true;
+}
+
+
+
+/**
+ * Update the interface to reflect page loading stopped.
+ *
+ * \param g window with start of load
+ */
+
+static void gui_window_stop_throbber(struct gui_window *g)
+{
+ ro_gui_window_update_toolbar_buttons(g);
+ ro_gui_menu_refresh(ro_gui_browser_window_menu);
+ if (g->toolbar != NULL)
+ ro_toolbar_stop_throbbing(g->toolbar);
+ g->active = false;
+}
+
+/**
+ * set favicon
+ */
+
+static void gui_window_set_icon(struct gui_window *g, hlcache_handle *icon)
+{
+ if (g == NULL || g->toolbar == NULL)
+ return;
+
+ ro_toolbar_set_site_favicon(g->toolbar, icon);
+}
+
+
+
+/**
+ * Remove the caret, if present.
+ *
+ * \param g window with caret
+ */
+
+static void gui_window_remove_caret(struct gui_window *g)
+{
+ wimp_caret caret;
+ os_error *error;
+
+ error = xwimp_get_caret_position(&caret);
+ if (error) {
+ LOG("xwimp_get_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ if (caret.w != g->window)
+ /* we don't have the caret: do nothing */
+ return;
+
+ /* hide caret, but keep input focus */
+ gui_window_place_caret(g, -100, -100, 0, NULL);
+}
+
+
+/**
+ * Called when the gui_window has new content.
+ *
+ * \param g the gui_window that has new content
+ */
+
+static void gui_window_new_content(struct gui_window *g)
+{
+ ro_gui_menu_refresh(ro_gui_browser_window_menu);
+ ro_gui_window_update_toolbar_buttons(g);
+ ro_gui_dialog_close_persistent(g->window);
+ ro_toolbar_set_content_favicon(g->toolbar, g);
+}
+
+
+/**
+ * Starts drag scrolling of a browser window
+ *
+ * \param g the window to scroll
+ */
+
+static bool gui_window_scroll_start(struct gui_window *g)
+{
+ wimp_window_info_base info;
+ wimp_pointer pointer;
+ os_error *error;
+ wimp_drag drag;
+ int height;
+ int width;
+
+ error = xwimp_get_pointer_info(&pointer);
+ if (error) {
+ LOG("xwimp_get_pointer_info 0x%x : %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ info.w = g->window;
+ error = xwimp_get_window_info_header_only((wimp_window_info*)&info);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x : %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ width = info.extent.x1 - info.extent.x0;
+ height = info.extent.y1 - info.extent.y0;
+
+ drag.type = wimp_DRAG_USER_POINT;
+ drag.bbox.x1 = pointer.pos.x + info.xscroll;
+ drag.bbox.y0 = pointer.pos.y + info.yscroll;
+ drag.bbox.x0 = drag.bbox.x1 - (width - (info.visible.x1 - info.visible.x0));
+ drag.bbox.y1 = drag.bbox.y0 + (height - (info.visible.y1 - info.visible.y0));
+
+ if (g->toolbar) {
+ int tbar_height = ro_toolbar_full_height(g->toolbar);
+ drag.bbox.y0 -= tbar_height;
+ drag.bbox.y1 -= tbar_height;
+ }
+
+ error = xwimp_drag_box(&drag);
+ if (error) {
+ LOG("xwimp_drag_box: 0x%x : %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ ro_mouse_drag_start(ro_gui_window_scroll_end, ro_gui_window_mouse_at,
+ NULL, g);
+ return true;
+}
+
+
+/**
+ * Platform-dependent part of starting drag operation.
+ *
+ * \param g gui window containing the drag
+ * \param type type of drag the core is performing
+ * \param rect rectangle to constrain pointer to (relative to drag start coord)
+ * \return true iff succesful
+ */
+
+static bool gui_window_drag_start(struct gui_window *g, gui_drag_type type,
+ const struct rect *rect)
+{
+ wimp_pointer pointer;
+ wimp_drag drag;
+
+ if (rect != NULL) {
+ /* We have a box to constrain the pointer to, for the drag
+ * duration */
+ os_error *error = xwimp_get_pointer_info(&pointer);
+ if (error) {
+ LOG("xwimp_get_pointer_info 0x%x : %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ drag.type = wimp_DRAG_USER_POINT;
+ drag.bbox.x0 = pointer.pos.x +
+ (int)(rect->x0 * 2 * g->scale);
+ drag.bbox.y0 = pointer.pos.y +
+ (int)(rect->y0 * 2 * g->scale);
+ drag.bbox.x1 = pointer.pos.x +
+ (int)(rect->x1 * 2 * g->scale);
+ drag.bbox.y1 = pointer.pos.y +
+ (int)(rect->y1 * 2 * g->scale);
+
+ error = xwimp_drag_box(&drag);
+ if (error) {
+ LOG("xwimp_drag_box: 0x%x : %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+ }
+
+ switch (type) {
+ case GDRAGGING_SCROLLBAR:
+ /* Dragging a core scrollbar */
+ ro_mouse_drag_start(ro_gui_window_scroll_end, ro_gui_window_mouse_at,
+ NULL, g);
+ break;
+
+ default:
+ /* Not handled here yet */
+ break;
+ }
+
+ return true;
+}
+
+
+/**
+ * Save the specified content as a link.
+ *
+ * \param g The window containing the content
+ * \param url The url of the link
+ * \param title The title of the link
+ */
+static nserror
+gui_window_save_link(struct gui_window *g, nsurl *url, const char *title)
+{
+ ro_gui_save_prepare(GUI_SAVE_LINK_URL, NULL, NULL, url, title);
+ ro_gui_dialog_open_persistent(g->window, dialog_saveas, true);
+ return NSERROR_OK;
+}
+
+
+/**
+ * Updates a windows extent.
+ *
+ * \param g the gui_window to update
+ * \param width the minimum width, or -1 to use window width
+ * \param height the minimum height, or -1 to use window height
+ */
+
+void gui_window_set_extent(struct gui_window *g, int width, int height)
+{
+ int screen_width;
+ int toolbar_height = 0;
+ wimp_window_state state;
+ os_error *error;
+
+ if (g->toolbar)
+ toolbar_height = ro_toolbar_full_height(g->toolbar);
+
+ /* get the current state */
+ if ((height == -1) || (width == -1)) {
+ state.w = g->window;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ if (width == -1)
+ width = state.visible.x1 - state.visible.x0;
+ if (height == -1) {
+ height = state.visible.y1 - state.visible.y0;
+ height -= toolbar_height;
+ }
+ }
+
+ /* the top-level framed window is a total pain. to get it to maximise
+ * to the top of the screen we need to fake it having a suitably large
+ * extent */
+ if (browser_window_is_frameset(g->bw)) {
+ ro_gui_screen_size(&screen_width, &height);
+ if (g->toolbar)
+ height -= ro_toolbar_full_height(g->toolbar);
+ height -= ro_get_hscroll_height(g->window);
+ height -= ro_get_title_height(g->window);
+ }
+ if (browser_window_has_content(g->bw)) {
+ int w, h;
+ browser_window_get_extents(g->bw, true, &w, &h);
+ width = max(width, w * 2);
+ height = max(height, h * 2);
+ }
+ os_box extent = { 0, -height, width, toolbar_height };
+ error = xwimp_set_extent(g->window, &extent);
+ if (error) {
+ LOG("xwimp_set_extent: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+}
+
+
+/**
+ * Display a menu of options for a form select control.
+ *
+ * \param g gui window containing form control
+ * \param control form control of type GADGET_SELECT
+ */
+
+static void gui_window_create_form_select_menu(struct gui_window *g,
+ struct form_control *control)
+{
+ os_error *error;
+ wimp_pointer pointer;
+
+ /* The first time the menu is opened, control bypasses the normal
+ * Menu Prepare event and so we prepare here. On any re-opens,
+ * ro_gui_window_prepare_form_select_menu() is called from the
+ * normal wimp event.
+ */
+
+ if (!ro_gui_window_prepare_form_select_menu(g, control))
+ return;
+
+ error = xwimp_get_pointer_info(&pointer);
+ if (error) {
+ LOG("xwimp_get_pointer_info: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ ro_gui_menu_destroy();
+ return;
+ }
+
+ gui_form_select_control = control;
+ ro_gui_menu_create(gui_form_select_menu,
+ pointer.pos.x, pointer.pos.y, g->window);
+}
+
+
+/*
+ * RISC OS Wimp Event Handlers
+ */
+
+
+/**
+ * Handle a Redraw_Window_Request for a browser window.
+ */
+
+void ro_gui_window_redraw(wimp_draw *redraw)
+{
+ osbool more;
+ struct gui_window *g = (struct gui_window *)ro_gui_wimp_event_get_user_data(redraw->w);
+ os_error *error;
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &ro_plotters
+ };
+
+ /* We can't render locked contents. If the browser window is not
+ * ready for redraw, do nothing. Else, in the case of buffered
+ * rendering we'll show random data. */
+ if (!browser_window_redraw_ready(g->bw))
+ return;
+
+ ro_gui_current_redraw_gui = g;
+
+ error = xwimp_redraw_window(redraw, &more);
+ if (error) {
+ LOG("xwimp_redraw_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ while (more) {
+ struct rect clip;
+
+ /* OS's redraw request coordinates are in screen coordinates,
+ * with an origin at the bottom left of the screen.
+ * Find the coordinate of the top left of the document in terms
+ * of OS screen coordinates.
+ * NOTE: OS units are 2 per px. */
+ ro_plot_origin_x = redraw->box.x0 - redraw->xscroll;
+ ro_plot_origin_y = redraw->box.y1 - redraw->yscroll;
+
+ /* Convert OS redraw rectangle request coordinates into NetSurf
+ * coordinates. NetSurf coordinates have origin at top left of
+ * document and units are in px. */
+ clip.x0 = (redraw->clip.x0 - ro_plot_origin_x) / 2; /* left */
+ clip.y0 = (ro_plot_origin_y - redraw->clip.y1) / 2; /* top */
+ clip.x1 = (redraw->clip.x1 - ro_plot_origin_x) / 2; /* right */
+ clip.y1 = (ro_plot_origin_y - redraw->clip.y0) / 2; /* bottom */
+
+ if (ro_gui_current_redraw_gui->option.buffer_everything)
+ ro_gui_buffer_open(redraw);
+
+ browser_window_redraw(g->bw, 0, 0, &clip, &ctx);
+
+ if (ro_gui_current_redraw_gui->option.buffer_everything)
+ ro_gui_buffer_close();
+
+ /* Check to see if there are more rectangles to draw and
+ * get next one */
+ error = xwimp_get_rectangle(redraw, &more);
+ /* RISC OS 3.7 returns an error here if enough buffer was
+ claimed to cause a new dynamic area to be created. It
+ doesn't actually stop anything working, so we mask it out
+ for now until a better fix is found. This appears to be a
+ bug in RISC OS. */
+ if (error && !(ro_gui_current_redraw_gui->
+ option.buffer_everything &&
+ error->errnum == error_WIMP_GET_RECT)) {
+ LOG("xwimp_get_rectangle: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ ro_gui_current_redraw_gui = NULL;
+ return;
+ }
+ }
+ ro_gui_current_redraw_gui = NULL;
+}
+
+
+/**
+ * Set a gui_window's scale
+ */
+void ro_gui_window_set_scale(struct gui_window *g, float scale)
+{
+ g->scale = scale;
+ browser_window_set_scale(g->bw, scale, true);
+}
+
+
+/**
+ * Open a window using the given wimp_open, handling toolbars and resizing.
+ */
+
+void ro_gui_window_open(wimp_open *open)
+{
+ struct gui_window *g = (struct gui_window *)ro_gui_wimp_event_get_user_data(open->w);
+ int width = open->visible.x1 - open->visible.x0;
+ int height = open->visible.y1 - open->visible.y0;
+ browser_scrolling h_scroll;
+ browser_scrolling v_scroll;
+ int toolbar_height = 0;
+ float new_scale = 0;
+ wimp_window_state state;
+ os_error *error;
+ wimp_w parent;
+ bits linkage;
+ bool have_content;
+
+ if (open->next == wimp_TOP && g->iconise_icon >= 0) {
+ /* window is no longer iconised, release its sprite number */
+ iconise_used[g->iconise_icon] = false;
+ g->iconise_icon = -1;
+ }
+
+ have_content = browser_window_has_content(g->bw);
+
+ /* get the current flags/nesting state */
+ state.w = g->window;
+ error = xwimp_get_window_state_and_nesting(&state, &parent, &linkage);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* account for toolbar height, if present */
+ if (g->toolbar)
+ toolbar_height = ro_toolbar_full_height(g->toolbar);
+ height -= toolbar_height;
+
+ /* work with the state from now on so we can modify flags */
+ state.visible = open->visible;
+ state.xscroll = open->xscroll;
+ state.yscroll = open->yscroll;
+ state.next = open->next;
+
+ browser_window_get_scrollbar_type(g->bw, &h_scroll, &v_scroll);
+
+ /* handle 'auto' scroll bars' and non-fitting scrollbar removal */
+ if ((h_scroll != BW_SCROLLING_NO) && (v_scroll != BW_SCROLLING_NO)) {
+ int size;
+
+ /* windows lose scrollbars when containing a frameset */
+ bool no_hscroll = false;
+ bool no_vscroll = browser_window_is_frameset(g->bw);
+
+ /* hscroll */
+ size = ro_get_hscroll_height(NULL);
+ size -= 2; /* 1px border on both sides */
+ if (!no_hscroll) {
+ if (!(state.flags & wimp_WINDOW_HSCROLL)) {
+ height -= size;
+ state.visible.y0 += size;
+ if (have_content) {
+ browser_window_schedule_reformat(g->bw);
+ }
+ }
+ state.flags |= wimp_WINDOW_HSCROLL;
+ } else {
+ if (state.flags & wimp_WINDOW_HSCROLL) {
+ height += size;
+ state.visible.y0 -= size;
+ if (have_content) {
+ browser_window_schedule_reformat(g->bw);
+ }
+ }
+ state.flags &= ~wimp_WINDOW_HSCROLL;
+ }
+
+ /* vscroll */
+ size = ro_get_vscroll_width(NULL);
+ size -= 2; /* 1px border on both sides */
+ if (!no_vscroll) {
+ if (!(state.flags & wimp_WINDOW_VSCROLL)) {
+ width -= size;
+ state.visible.x1 -= size;
+ if (have_content) {
+ browser_window_schedule_reformat(g->bw);
+ }
+ }
+ state.flags |= wimp_WINDOW_VSCROLL;
+ } else {
+ if (state.flags & wimp_WINDOW_VSCROLL) {
+ width += size;
+ state.visible.x1 += size;
+ if (have_content) {
+ browser_window_schedule_reformat(g->bw);
+ }
+ }
+ state.flags &= ~wimp_WINDOW_VSCROLL;
+ }
+ }
+
+ /* reformat or change extent if necessary */
+ if (have_content &&
+ (g->old_width != width || g->old_height != height)) {
+ /* Ctrl-resize of a top-level window scales the content size */
+ if ((g->old_width > 0) && (g->old_width != width) &&
+ (ro_gui_ctrl_pressed()))
+ new_scale = (g->scale * width) / g->old_width;
+ browser_window_schedule_reformat(g->bw);
+ }
+ if (g->update_extent || g->old_width != width ||
+ g->old_height != height) {
+ g->old_width = width;
+ g->old_height = height;
+ g->update_extent = false;
+ gui_window_set_extent(g, width, height);
+ }
+
+ /* first resize stops any flickering by making the URL window on top */
+ ro_gui_url_complete_resize(g->toolbar, PTR_WIMP_OPEN(&state));
+
+ /* Windows containing framesets can only be scrolled via the core, which
+ * is implementing frame scrollbars itself. The x and y offsets are
+ * therefore fixed.
+ */
+
+ if (browser_window_is_frameset(g->bw)) {
+ state.xscroll = 0;
+ state.yscroll = toolbar_height;
+ }
+
+ error = xwimp_open_window_nested_with_flags(&state, parent, linkage);
+ if (error) {
+ LOG("xwimp_open_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* update the toolbar */
+ if (g->status_bar)
+ ro_gui_status_bar_resize(g->status_bar);
+ if (g->toolbar) {
+ ro_toolbar_process(g->toolbar, -1, false);
+ /* second resize updates to the new URL bar width */
+ ro_gui_url_complete_resize(g->toolbar, open);
+ }
+
+ /* set the new scale from a ctrl-resize. this must be done at the end as
+ * it may cause a frameset recalculation based on the new window size.
+ */
+ if (new_scale > 0) {
+ ro_gui_window_set_scale(g, new_scale);
+ }
+}
+
+
+/**
+ * Handle wimp closing event
+ */
+
+void ro_gui_window_close(wimp_w w)
+{
+ struct gui_window *g = (struct gui_window *)ro_gui_wimp_event_get_user_data(w);
+ wimp_pointer pointer;
+ os_error *error;
+ char *temp_name;
+ char *filename = NULL;
+ struct nsurl *url;
+ bool destroy;
+
+ error = xwimp_get_pointer_info(&pointer);
+ if (error) {
+ LOG("xwimp_get_pointer_info: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ if (pointer.buttons & wimp_CLICK_ADJUST) {
+ destroy = !ro_gui_shift_pressed();
+
+ url = browser_window_get_url(g->bw);
+ if (url != NULL) {
+ netsurf_nsurl_to_path(url, &filename);
+ }
+ if (filename != NULL) {
+ temp_name = malloc(strlen(filename) + 32);
+ if (temp_name) {
+ char *r;
+ sprintf(temp_name, "Filer_OpenDir %s",
+ filename);
+ r = temp_name + strlen(temp_name);
+ while (r > temp_name) {
+ if (*r == '.') {
+ *r = '\0';
+ break;
+ }
+ r--;
+ }
+ error = xos_cli(temp_name);
+ if (error) {
+ LOG("xos_cli: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ return;
+ }
+ free(temp_name);
+ }
+ free(filename);
+ } else {
+ /* this is pointless if we are about to close the
+ * window */
+ if (!destroy && url != NULL)
+ browser_window_navigate_up(g->bw, false);
+ }
+ }
+ else
+ destroy = true;
+
+ if (destroy)
+ browser_window_destroy(g->bw);
+}
+
+
+/**
+ * Handle Mouse_Click events in a browser window. This should never see
+ * Menu clicks, as these will be routed to the menu handlers.
+ *
+ * \param *pointer details of mouse click
+ * \return true if click handled, false otherwise
+ */
+
+bool ro_gui_window_click(wimp_pointer *pointer)
+{
+ struct gui_window *g;
+ os_coord pos;
+
+ /* We should never see Menu clicks. */
+
+ if (pointer->buttons == wimp_CLICK_MENU)
+ return false;
+
+ g = (struct gui_window *) ro_gui_wimp_event_get_user_data(pointer->w);
+
+ /* try to close url-completion */
+ ro_gui_url_complete_close();
+
+ /* set input focus */
+ if (pointer->buttons & (wimp_SINGLE_SELECT | wimp_SINGLE_ADJUST))
+ gui_window_place_caret(g, -100, -100, 0, NULL);
+
+ if (ro_gui_window_to_window_pos(g, pointer->pos.x, pointer->pos.y, &pos))
+ browser_window_mouse_click(g->bw,
+ ro_gui_mouse_click_state(pointer->buttons,
+ wimp_BUTTON_DOUBLE_CLICK_DRAG),
+ pos.x, pos.y);
+
+ return true;
+}
+
+
+/**
+ * Process Key_Pressed events in a browser window.
+ *
+ * \param *key The wimp keypress block for the event.
+ * \return true if the event was handled, else false.
+ */
+
+bool ro_gui_window_keypress(wimp_key *key)
+{
+ struct gui_window *g;
+ uint32_t c = (uint32_t) key->c;
+
+ g = (struct gui_window *) ro_gui_wimp_event_get_user_data(key->w);
+ if (g == NULL)
+ return false;
+
+ /* First send the key to the browser window, eg. form fields. */
+
+ if ((unsigned)c < 0x20 || (0x7f <= c && c <= 0x9f) ||
+ (c & IS_WIMP_KEY)) {
+ /* Munge control keys into unused control chars */
+ /* We can't map onto 1->26 (reserved for ctrl+<qwerty>
+ That leaves 27->31 and 128->159 */
+ switch (c & ~IS_WIMP_KEY) {
+ case wimp_KEY_TAB: c = 9; break;
+ case wimp_KEY_SHIFT | wimp_KEY_TAB: c = 11; break;
+
+ /* cursor movement keys */
+ case wimp_KEY_HOME:
+ case wimp_KEY_CONTROL | wimp_KEY_LEFT:
+ c = NS_KEY_LINE_START;
+ break;
+ case wimp_KEY_END:
+ if (os_version >= RISCOS5)
+ c = NS_KEY_LINE_END;
+ else
+ c = NS_KEY_DELETE_RIGHT;
+ break;
+ case wimp_KEY_CONTROL | wimp_KEY_RIGHT: c = NS_KEY_LINE_END; break;
+ case wimp_KEY_CONTROL | wimp_KEY_UP: c = NS_KEY_TEXT_START; break;
+ case wimp_KEY_CONTROL | wimp_KEY_DOWN: c = NS_KEY_TEXT_END; break;
+ case wimp_KEY_SHIFT | wimp_KEY_LEFT: c = NS_KEY_WORD_LEFT ; break;
+ case wimp_KEY_SHIFT | wimp_KEY_RIGHT: c = NS_KEY_WORD_RIGHT; break;
+ case wimp_KEY_SHIFT | wimp_KEY_UP: c = NS_KEY_PAGE_UP; break;
+ case wimp_KEY_SHIFT | wimp_KEY_DOWN: c = NS_KEY_PAGE_DOWN; break;
+ case wimp_KEY_LEFT: c = NS_KEY_LEFT; break;
+ case wimp_KEY_RIGHT: c = NS_KEY_RIGHT; break;
+ case wimp_KEY_UP: c = NS_KEY_UP; break;
+ case wimp_KEY_DOWN: c = NS_KEY_DOWN; break;
+
+ /* editing */
+ case wimp_KEY_CONTROL | wimp_KEY_END:
+ c = NS_KEY_DELETE_LINE_END;
+ break;
+ case wimp_KEY_DELETE:
+ if (ro_gui_ctrl_pressed())
+ c = NS_KEY_DELETE_LINE_START;
+ else if (os_version < RISCOS5)
+ c = NS_KEY_DELETE_LEFT;
+ break;
+
+ case wimp_KEY_F8:
+ c = NS_KEY_UNDO;
+ break;
+ case wimp_KEY_F9:
+ c = NS_KEY_REDO;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (!(c & IS_WIMP_KEY)) {
+ if (browser_window_key_press(g->bw, c))
+ return true;
+ }
+
+ return ro_gui_window_handle_local_keypress(g, key, false);
+}
+
+
+/**
+ * Callback handler for keypresses within browser window toolbars.
+ *
+ * \param *data Client data, pointing to the GUI Window.
+ * \param *key The keypress data.
+ * \return true if the keypress was handled; else false.
+ */
+
+bool ro_gui_window_toolbar_keypress(void *data, wimp_key *key)
+{
+ struct gui_window *g = (struct gui_window *) data;
+
+ if (g != NULL)
+ return ro_gui_window_handle_local_keypress(g, key, true);
+
+ return false;
+}
+
+
+/**
+ * Handle keypresses within the RISC OS GUI: this is to be called after the
+ * core has been given a chance to act, or on keypresses in the toolbar where
+ * the core doesn't get involved.
+ *
+ * \param *g The gui window to which the keypress applies.
+ * \param *key The keypress data.
+ * \param is_toolbar true if the keypress is from a toolbar;
+ * else false.
+ * \return true if the keypress was claimed; else false.
+ */
+
+bool ro_gui_window_handle_local_keypress(struct gui_window *g, wimp_key *key,
+ bool is_toolbar)
+{
+ struct browser_window_features cont;
+ os_error *ro_error;
+ wimp_pointer pointer;
+ os_coord pos;
+ float scale;
+ uint32_t c = (uint32_t) key->c;
+ wimp_scroll_direction xscroll = wimp_SCROLL_NONE;
+ wimp_scroll_direction yscroll = wimp_SCROLL_NONE;
+ nsurl *url;
+
+ if (g == NULL)
+ return false;
+
+ ro_error = xwimp_get_pointer_info(&pointer);
+ if (ro_error) {
+ LOG("xwimp_get_pointer_info: 0x%x: %s\n", ro_error->errnum, ro_error->errmess);
+ ro_warn_user("WimpError", ro_error->errmess);
+ return false;
+ }
+
+ if (!ro_gui_window_to_window_pos(g, pointer.pos.x, pointer.pos.y, &pos))
+ return false;
+
+ browser_window_get_features(g->bw, pos.x, pos.y, &cont);
+
+ switch (c) {
+ case IS_WIMP_KEY + wimp_KEY_F1: /* Help. */
+ {
+ nserror error = nsurl_create(
+ "http://www.netsurf-browser.org/documentation/",
+ &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ ro_warn_user(messages_get_errorcode(error), 0);
+ }
+ return true;
+ }
+ case IS_WIMP_KEY + wimp_KEY_CONTROL + wimp_KEY_F1:
+ ro_gui_window_action_page_info(g);
+ return true;
+
+ case IS_WIMP_KEY + wimp_KEY_F2:
+ if (g->toolbar == NULL)
+ return false;
+ ro_gui_url_complete_close();
+ ro_toolbar_set_url(g->toolbar, "www.", true, true);
+ ro_gui_url_complete_start(g->toolbar);
+ ro_gui_url_complete_keypress(g->toolbar, wimp_KEY_DOWN);
+ return true;
+
+ case IS_WIMP_KEY + wimp_KEY_CONTROL + wimp_KEY_F2:
+ /* Close window. */
+ ro_gui_url_complete_close();
+ gui_window_set_pointer(g, GUI_POINTER_DEFAULT);
+ browser_window_destroy(g->bw);
+ return true;
+
+ case 19: /* Ctrl + S */
+ case IS_WIMP_KEY + wimp_KEY_F3:
+ ro_gui_window_action_save(g, GUI_SAVE_SOURCE);
+ return true;
+
+ case IS_WIMP_KEY + wimp_KEY_CONTROL + wimp_KEY_F3:
+ ro_gui_window_action_save(g, GUI_SAVE_TEXT);
+ return true;
+
+ case IS_WIMP_KEY + wimp_KEY_SHIFT + wimp_KEY_F3:
+ ro_gui_window_action_save(g, GUI_SAVE_COMPLETE);
+ return true;
+
+ case IS_WIMP_KEY + wimp_KEY_CONTROL + wimp_KEY_SHIFT + wimp_KEY_F3:
+ ro_gui_window_action_save(g, GUI_SAVE_DRAW);
+ return true;
+
+ case 6: /* Ctrl + F */
+ case IS_WIMP_KEY + wimp_KEY_F4: /* Search */
+ ro_gui_window_action_search(g);
+ return true;
+
+ case IS_WIMP_KEY + wimp_KEY_F5: /* Reload */
+ if (g->bw != NULL)
+ browser_window_reload(g->bw, false);
+ return true;
+
+ case 18: /* Ctrl+R (Full reload) */
+ case IS_WIMP_KEY + wimp_KEY_CONTROL + wimp_KEY_F5:
+ if (g->bw != NULL)
+ browser_window_reload(g->bw, true);
+ return true;
+
+ case IS_WIMP_KEY + wimp_KEY_F6: /* Hotlist */
+ ro_gui_hotlist_open();
+ return true;
+
+ case IS_WIMP_KEY + wimp_KEY_F7: /* Show local history */
+ ro_gui_window_action_local_history(g);
+ return true;
+
+ case IS_WIMP_KEY + wimp_KEY_CONTROL + wimp_KEY_F7:
+ /* Show global history */
+ ro_gui_global_history_open();
+ return true;
+
+ case IS_WIMP_KEY + wimp_KEY_F8: /* View source */
+ ro_gui_view_source((cont.main != NULL) ? cont.main :
+ browser_window_get_content(g->bw));
+ return true;
+
+ case IS_WIMP_KEY + wimp_KEY_F9:
+ /* Dump content for debugging. */
+ ro_gui_dump_browser_window(g->bw);
+ return true;
+
+ case IS_WIMP_KEY + wimp_KEY_CONTROL + wimp_KEY_F9:
+ urldb_dump();
+ return true;
+
+ case IS_WIMP_KEY + wimp_KEY_CONTROL + wimp_KEY_SHIFT + wimp_KEY_F9:
+ talloc_report_full(0, stderr);
+ return true;
+
+ case IS_WIMP_KEY + wimp_KEY_F11: /* Zoom */
+ ro_gui_window_action_zoom(g);
+ return true;
+
+ case IS_WIMP_KEY + wimp_KEY_SHIFT + wimp_KEY_F11:
+ /* Toggle display of box outlines. */
+ browser_window_debug(g->bw, CONTENT_DEBUG_REDRAW);
+
+ gui_window_redraw_window(g);
+ return true;
+
+ case wimp_KEY_RETURN:
+ if (is_toolbar) {
+ const char *toolbar_url;
+ toolbar_url = ro_toolbar_get_url(g->toolbar);
+ if (toolbar_url != NULL)
+ ro_gui_window_launch_url(g, toolbar_url);
+ }
+ return true;
+
+ case wimp_KEY_ESCAPE:
+ if (ro_gui_url_complete_close()) {
+ ro_gui_url_complete_start(g->toolbar);
+ return true;
+ }
+
+ if (g->bw != NULL)
+ browser_window_stop(g->bw);
+ return true;
+
+ case 14: /* CTRL+N */
+ ro_gui_window_action_new_window(g);
+ return true;
+
+ case 17: /* CTRL+Q (Zoom out) */
+ case 23: /* CTRL+W (Zoom in) */
+ if (browser_window_has_content(g->bw) == false)
+ break;
+ scale = g->scale;
+ if (ro_gui_shift_pressed() && c == 17)
+ scale = g->scale - 0.1;
+ else if (ro_gui_shift_pressed() && c == 23)
+ scale = g->scale + 0.1;
+ else if (c == 17) {
+ for (int i = SCALE_SNAP_TO_SIZE - 1; i >= 0; i--)
+ if (scale_snap_to[i] < g->scale) {
+ scale = scale_snap_to[i];
+ break;
+ }
+ } else {
+ for (unsigned int i = 0; i < SCALE_SNAP_TO_SIZE; i++)
+ if (scale_snap_to[i] > g->scale) {
+ scale = scale_snap_to[i];
+ break;
+ }
+ }
+ if (scale < scale_snap_to[0])
+ scale = scale_snap_to[0];
+ if (scale > scale_snap_to[SCALE_SNAP_TO_SIZE - 1])
+ scale = scale_snap_to[SCALE_SNAP_TO_SIZE - 1];
+ if (g->scale != scale) {
+ ro_gui_window_set_scale(g, scale);
+ }
+ return true;
+
+ case IS_WIMP_KEY + wimp_KEY_PRINT:
+ ro_gui_window_action_print(g);
+ return true;
+
+ case IS_WIMP_KEY | wimp_KEY_LEFT:
+ case IS_WIMP_KEY | wimp_KEY_RIGHT:
+ case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_LEFT:
+ case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_RIGHT:
+ case IS_WIMP_KEY | wimp_KEY_UP:
+ case IS_WIMP_KEY | wimp_KEY_DOWN:
+ case IS_WIMP_KEY | wimp_KEY_PAGE_UP:
+ case IS_WIMP_KEY | wimp_KEY_PAGE_DOWN:
+ case wimp_KEY_HOME:
+ case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_UP:
+ case IS_WIMP_KEY | wimp_KEY_END:
+ case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_DOWN:
+ if (is_toolbar)
+ return false;
+ break;
+ default:
+ return false; /* This catches any keys we don't want to claim */
+ }
+
+ /* Any keys that exit from the above switch() via break should be
+ * processed as scroll actions in the browser window. */
+
+ switch (c) {
+ case IS_WIMP_KEY | wimp_KEY_LEFT:
+ xscroll = wimp_SCROLL_COLUMN_LEFT;
+ break;
+ case IS_WIMP_KEY | wimp_KEY_RIGHT:
+ xscroll = wimp_SCROLL_COLUMN_RIGHT;
+ break;
+ case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_LEFT:
+ xscroll = 0x7fffffff;
+ break;
+ case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_RIGHT:
+ xscroll = 0x80000000;
+ break;
+ case IS_WIMP_KEY | wimp_KEY_UP:
+ yscroll = wimp_SCROLL_LINE_UP;
+ break;
+ case IS_WIMP_KEY | wimp_KEY_DOWN:
+ yscroll = wimp_SCROLL_LINE_DOWN;
+ break;
+ case IS_WIMP_KEY | wimp_KEY_PAGE_UP:
+ yscroll = wimp_SCROLL_PAGE_UP;
+ break;
+ case IS_WIMP_KEY | wimp_KEY_PAGE_DOWN:
+ yscroll = wimp_SCROLL_PAGE_DOWN;
+ break;
+ case wimp_KEY_HOME:
+ case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_UP:
+ yscroll = 0x7fffffff;
+ break;
+ case IS_WIMP_KEY | wimp_KEY_END:
+ case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_DOWN:
+ yscroll = 0x80000000;
+ break;
+ }
+
+ ro_gui_window_scroll_action(g, xscroll, yscroll);
+
+ return true;
+}
+
+
+/**
+ * Prepare the browser window menu for (re-)opening
+ *
+ * \param w The window owning the menu.
+ * \param i The icon owning the menu.
+ * \param *menu The menu about to be opened.
+ * \param *pointer Pointer to the relevant wimp event block, or
+ * NULL for an Adjust click.
+ * \return true if the event was handled; else false.
+ */
+
+bool ro_gui_window_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_pointer *pointer)
+{
+ struct gui_window *g;
+ struct browser_window *bw;
+ struct toolbar *toolbar;
+ struct browser_window_features cont;
+ bool export_sprite, export_draw, have_content;
+ os_coord pos;
+ browser_editor_flags editor_flags;
+
+ g = (struct gui_window *) ro_gui_wimp_event_get_user_data(w);
+ toolbar = g->toolbar;
+ bw = g->bw;
+ have_content = browser_window_has_content(g->bw);
+ editor_flags = browser_window_get_editor_flags(bw);
+
+ /* If this is the form select menu, handle it now and then exit.
+ * Otherwise, carry on to the main browser window menu.
+ */
+
+ if (menu == gui_form_select_menu) {
+ return ro_gui_window_prepare_form_select_menu(g,
+ gui_form_select_control);
+ }
+
+ if (menu != ro_gui_browser_window_menu)
+ return false;
+
+ /* If this is a new opening for the browser window menu (ie. not for a
+ * toolbar menu), get details of the object under the pointer.
+ */
+
+ if (pointer != NULL && g->window == w) {
+ ro_gui_url_complete_close();
+
+ current_menu_main = NULL;
+ current_menu_object = NULL;
+ current_menu_url = NULL;
+
+ if (ro_gui_window_to_window_pos(g, pointer->pos.x,
+ pointer->pos.y, &pos)) {
+ browser_window_get_features(bw, pos.x, pos.y, &cont);
+
+ current_menu_main = cont.main;
+ current_menu_object = cont.object;
+ current_menu_url = cont.link;
+ }
+ }
+
+ /* Shade menu entries according to the state of the window and object
+ * under the pointer.
+ */
+
+ /* Toolbar (Sub)Menu */
+
+ ro_gui_menu_set_entry_shaded(menu, TOOLBAR_BUTTONS,
+ ro_toolbar_menu_option_shade(toolbar));
+ ro_gui_menu_set_entry_ticked(menu, TOOLBAR_BUTTONS,
+ ro_toolbar_menu_buttons_tick(toolbar));
+
+ ro_gui_menu_set_entry_shaded(menu, TOOLBAR_ADDRESS_BAR,
+ ro_toolbar_menu_edit_shade(toolbar));
+ ro_gui_menu_set_entry_ticked(menu, TOOLBAR_ADDRESS_BAR,
+ ro_toolbar_menu_url_tick(toolbar));
+
+ ro_gui_menu_set_entry_shaded(menu, TOOLBAR_THROBBER,
+ ro_toolbar_menu_edit_shade(toolbar));
+ ro_gui_menu_set_entry_ticked(menu, TOOLBAR_THROBBER,
+ ro_toolbar_menu_throbber_tick(toolbar));
+
+ ro_gui_menu_set_entry_shaded(menu, TOOLBAR_EDIT,
+ ro_toolbar_menu_edit_shade(toolbar));
+ ro_gui_menu_set_entry_ticked(menu, TOOLBAR_EDIT,
+ ro_toolbar_menu_edit_tick(toolbar));
+
+ /* Page Submenu */
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_PAGE,
+ !browser_window_can_search(bw));
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_PAGE_INFO, !have_content);
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_PRINT, !have_content);
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_NEW_WINDOW, !have_content);
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_FIND_TEXT,
+ !browser_window_can_search(bw));
+
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_VIEW_SOURCE, !have_content);
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_SAVE_URL_URI, !have_content);
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_SAVE_URL_URL, !have_content);
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_SAVE_URL_TEXT,
+ !have_content);
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_SAVE, !have_content);
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_SAVE_COMPLETE,
+ !have_content);
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_EXPORT_DRAW, !have_content);
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_EXPORT_PDF, !have_content);
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_EXPORT_TEXT, !have_content);
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_LINK_SAVE_URI,
+ !current_menu_url);
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_LINK_SAVE_URL,
+ !current_menu_url);
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_LINK_SAVE_TEXT,
+ !current_menu_url);
+
+
+
+ /* Object Submenu */
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_OBJECT,
+ current_menu_object == NULL &&
+ current_menu_url == NULL);
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_OBJECT_LINK,
+ current_menu_url == NULL);
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_OBJECT_INFO,
+ current_menu_object == NULL);
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_OBJECT_RELOAD,
+ current_menu_object == NULL);
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_OBJECT_OBJECT,
+ current_menu_object == NULL);
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_OBJECT_PRINT, true);
+ /* Not yet implemented */
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_OBJECT_SAVE,
+ current_menu_object == NULL);
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_OBJECT_SAVE_URL_URI,
+ current_menu_object == NULL);
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_OBJECT_SAVE_URL_URL,
+ current_menu_object == NULL);
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_OBJECT_SAVE_URL_TEXT,
+ current_menu_object == NULL);
+
+ if (current_menu_object != NULL)
+ ro_gui_window_content_export_types(current_menu_object,
+ &export_draw, &export_sprite);
+ else
+ ro_gui_window_content_export_types(
+ browser_window_get_content(bw),
+ &export_draw, &export_sprite);
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_OBJECT_EXPORT,
+ (!have_content && current_menu_object == NULL)
+ || !(export_sprite || export_draw));
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_OBJECT_EXPORT_SPRITE,
+ (!have_content && current_menu_object == NULL)
+ || !export_sprite);
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_OBJECT_EXPORT_DRAW,
+ (!have_content && current_menu_object == NULL)
+ || !export_draw);
+
+
+ /* Selection Submenu */
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_SELECTION,
+ !browser_window_can_select(bw));
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_SELECTION_SAVE,
+ ~editor_flags & BW_EDITOR_CAN_COPY);
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_SELECTION_COPY,
+ ~editor_flags & BW_EDITOR_CAN_COPY);
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_SELECTION_CUT,
+ ~editor_flags & BW_EDITOR_CAN_CUT);
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_SELECTION_PASTE,
+ ~editor_flags & BW_EDITOR_CAN_PASTE);
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_SELECTION_CLEAR,
+ ~editor_flags & BW_EDITOR_CAN_COPY);
+
+
+ /* Navigate Submenu */
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_NAVIGATE_BACK,
+ !browser_window_back_available(bw));
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_NAVIGATE_FORWARD,
+ !browser_window_forward_available(bw));
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_NAVIGATE_RELOAD_ALL,
+ !browser_window_reload_available(bw));
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_NAVIGATE_STOP,
+ !browser_window_stop_available(bw));
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_NAVIGATE_UP,
+ !browser_window_up_available(bw));
+
+
+
+ /* View Submenu */
+
+ ro_gui_menu_set_entry_ticked(menu, BROWSER_IMAGES_FOREGROUND,
+ g != NULL && nsoption_bool(foreground_images));
+
+ ro_gui_menu_set_entry_ticked(menu, BROWSER_IMAGES_BACKGROUND,
+ g != NULL && nsoption_bool(background_images));
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_BUFFER_ANIMS,
+ g == NULL || g->option.buffer_everything);
+ ro_gui_menu_set_entry_ticked(menu, BROWSER_BUFFER_ANIMS, g != NULL &&
+ (g->option.buffer_animations ||
+ g->option.buffer_everything));
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_BUFFER_ALL, g == NULL);
+ ro_gui_menu_set_entry_ticked(menu, BROWSER_BUFFER_ALL,
+ g != NULL && g->option.buffer_everything);
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_SCALE_VIEW, !have_content);
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_WINDOW_STAGGER,
+ nsoption_int(window_screen_width) == 0);
+ ro_gui_menu_set_entry_ticked(menu, BROWSER_WINDOW_STAGGER,
+ ((nsoption_int(window_screen_width) == 0) ||
+ nsoption_bool(window_stagger)));
+
+ ro_gui_menu_set_entry_ticked(menu, BROWSER_WINDOW_COPY,
+ nsoption_bool(window_size_clone));
+
+ ro_gui_menu_set_entry_shaded(menu, BROWSER_WINDOW_RESET,
+ nsoption_int(window_screen_width) == 0);
+
+
+ /* Utilities Submenu */
+
+ ro_gui_menu_set_entry_shaded(menu, HOTLIST_ADD_URL, !have_content);
+
+ ro_gui_menu_set_entry_shaded(menu, HISTORY_SHOW_LOCAL,
+ (bw == NULL ||
+ !(have_content || browser_window_back_available(bw) ||
+ browser_window_forward_available(bw))));
+
+
+ /* Help Submenu */
+
+ ro_gui_menu_set_entry_ticked(menu, HELP_LAUNCH_INTERACTIVE,
+ ro_gui_interactive_help_available() &&
+ nsoption_bool(interactive_help));
+
+ return true;
+}
+
+
+/**
+ * Handle submenu warnings for a browser window menu
+ *
+ * \param w The window owning the menu.
+ * \param i The icon owning the menu.
+ * \param *menu The menu to which the warning applies.
+ * \param *selection The wimp menu selection data.
+ * \param action The selected menu action.
+ */
+
+void ro_gui_window_menu_warning(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action)
+{
+ struct gui_window *g;
+ hlcache_handle *h;
+ struct toolbar *toolbar;
+ bool export;
+
+ if (menu != ro_gui_browser_window_menu)
+ return;
+
+ g = (struct gui_window *) ro_gui_wimp_event_get_user_data(w);
+ toolbar = g->toolbar;
+ h = browser_window_get_content(g->bw);
+
+ switch (action) {
+ case BROWSER_PAGE_INFO:
+ if (h != NULL)
+ ro_gui_window_prepare_pageinfo(g);
+ break;
+
+ case BROWSER_FIND_TEXT:
+ if (h != NULL && (content_get_type(h) == CONTENT_HTML ||
+ content_get_type(h) == CONTENT_TEXTPLAIN))
+ ro_gui_search_prepare(g->bw);
+ break;
+
+ case BROWSER_SCALE_VIEW:
+ if (h != NULL)
+ ro_gui_dialog_prepare_zoom(g);
+ break;
+
+ case BROWSER_PRINT:
+ if (h != NULL)
+ ro_gui_print_prepare(g);
+ break;
+
+ case BROWSER_OBJECT_INFO:
+ if (current_menu_object != NULL)
+ ro_gui_window_prepare_objectinfo(current_menu_object,
+ current_menu_url);
+ break;
+
+ case BROWSER_OBJECT_SAVE:
+ if (current_menu_object != NULL)
+ ro_gui_save_prepare(GUI_SAVE_OBJECT_ORIG,
+ current_menu_object, NULL, NULL, NULL);
+ break;
+
+ case BROWSER_SELECTION_SAVE:
+ if (browser_window_get_editor_flags(g->bw) & BW_EDITOR_CAN_COPY)
+ ro_gui_save_prepare(GUI_SAVE_TEXT_SELECTION, NULL,
+ browser_window_get_selection(g->bw),
+ NULL, NULL);
+ break;
+
+ case BROWSER_SAVE_URL_URI:
+ if (h != NULL)
+ ro_gui_save_prepare(GUI_SAVE_LINK_URI, NULL, NULL,
+ hlcache_handle_get_url(h),
+ content_get_title(h));
+ break;
+
+ case BROWSER_SAVE_URL_URL:
+ if (h != NULL)
+ ro_gui_save_prepare(GUI_SAVE_LINK_URL, NULL, NULL,
+ hlcache_handle_get_url(h),
+ content_get_title(h));
+ break;
+
+ case BROWSER_SAVE_URL_TEXT:
+ if (h != NULL)
+ ro_gui_save_prepare(GUI_SAVE_LINK_TEXT, NULL, NULL,
+ hlcache_handle_get_url(h),
+ content_get_title(h));
+ break;
+
+ case BROWSER_OBJECT_SAVE_URL_URI:
+ if (current_menu_object != NULL)
+ ro_gui_save_prepare(GUI_SAVE_LINK_URI, NULL, NULL,
+ hlcache_handle_get_url(
+ current_menu_object),
+ content_get_title(current_menu_object));
+ break;
+
+ case BROWSER_OBJECT_SAVE_URL_URL:
+ if (current_menu_object != NULL)
+ ro_gui_save_prepare(GUI_SAVE_LINK_URL, NULL, NULL,
+ hlcache_handle_get_url(
+ current_menu_object),
+ content_get_title(current_menu_object));
+ break;
+
+ case BROWSER_OBJECT_SAVE_URL_TEXT:
+ if (current_menu_object != NULL)
+ ro_gui_save_prepare(GUI_SAVE_LINK_TEXT, NULL, NULL,
+ hlcache_handle_get_url(
+ current_menu_object),
+ content_get_title(current_menu_object));
+ break;
+
+ case BROWSER_SAVE:
+ if (h != NULL)
+ ro_gui_save_prepare(GUI_SAVE_SOURCE, h, NULL, NULL, NULL);
+ break;
+
+ case BROWSER_SAVE_COMPLETE:
+ if (h != NULL)
+ ro_gui_save_prepare(GUI_SAVE_COMPLETE, h, NULL, NULL, NULL);
+ break;
+
+ case BROWSER_EXPORT_DRAW:
+ if (h != NULL)
+ ro_gui_save_prepare(GUI_SAVE_DRAW, h, NULL, NULL, NULL);
+ break;
+
+ case BROWSER_EXPORT_PDF:
+ if (h != NULL)
+ ro_gui_save_prepare(GUI_SAVE_PDF, h, NULL, NULL, NULL);
+ break;
+
+ case BROWSER_EXPORT_TEXT:
+ if (h != NULL)
+ ro_gui_save_prepare(GUI_SAVE_TEXT, h, NULL, NULL, NULL);
+ break;
+
+ case BROWSER_LINK_SAVE_URI:
+ if (current_menu_url != NULL)
+ ro_gui_save_prepare(GUI_SAVE_LINK_URI, NULL, NULL,
+ current_menu_url, NULL);
+ break;
+
+ case BROWSER_LINK_SAVE_URL:
+ if (current_menu_url != NULL)
+ ro_gui_save_prepare(GUI_SAVE_LINK_URL, NULL, NULL,
+ current_menu_url, NULL);
+ break;
+
+ case BROWSER_LINK_SAVE_TEXT:
+ if (current_menu_url != NULL)
+ ro_gui_save_prepare(GUI_SAVE_LINK_TEXT, NULL, NULL,
+ current_menu_url, NULL);
+ break;
+
+ case BROWSER_OBJECT_EXPORT_SPRITE:
+ if (current_menu_object != NULL) {
+ ro_gui_window_content_export_types(current_menu_object,
+ NULL, &export);
+
+ if (export)
+ ro_gui_save_prepare(GUI_SAVE_OBJECT_NATIVE,
+ current_menu_object,
+ NULL, NULL, NULL);
+ } else if (h != NULL) {
+ ro_gui_window_content_export_types(h, NULL, &export);
+
+ if (export)
+ ro_gui_save_prepare(GUI_SAVE_OBJECT_NATIVE,
+ h, NULL, NULL, NULL);
+ }
+ break;
+
+ case BROWSER_OBJECT_EXPORT_DRAW:
+ if (current_menu_object != NULL) {
+ ro_gui_window_content_export_types(current_menu_object,
+ &export, NULL);
+
+ if (export)
+ ro_gui_save_prepare(GUI_SAVE_OBJECT_NATIVE,
+ current_menu_object,
+ NULL, NULL, NULL);
+ } else if (h != NULL) {
+ ro_gui_window_content_export_types(h, &export, NULL);
+
+ if (export)
+ ro_gui_save_prepare(GUI_SAVE_OBJECT_NATIVE,
+ h, NULL, NULL, NULL);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+static void ro_gui_window_paste_cb(void *pw)
+{
+ struct browser_window *bw = pw;
+
+ browser_window_key_press(bw, NS_KEY_PASTE);
+}
+
+
+/**
+ * Handle selections from a browser window menu
+ *
+ * \param w The window owning the menu.
+ * \param i The icon owning the menu.
+ * \param *menu The menu from which the selection was made.
+ * \param *selection The wimp menu selection data.
+ * \param action The selected menu action.
+ * \return true if action accepted; else false.
+ */
+
+bool ro_gui_window_menu_select(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action)
+{
+ struct gui_window *g;
+ struct browser_window *bw;
+ hlcache_handle *h;
+ struct toolbar *toolbar;
+ wimp_window_state state;
+ nsurl *url;
+ nserror error = NSERROR_OK;
+
+ g = (struct gui_window *) ro_gui_wimp_event_get_user_data(w);
+ toolbar = g->toolbar;
+ bw = g->bw;
+ h = browser_window_get_content(bw);
+
+ /* If this is a form menu from the core, handle it now and then exit.
+ * Otherwise, carry on to the main browser window menu.
+ */
+
+ if (menu == gui_form_select_menu && w == g->window) {
+ ro_gui_window_process_form_select_menu(g, selection);
+
+ return true;
+ }
+
+ /* We're now safe to assume that this is either the browser or
+ * toolbar window menu.
+ */
+
+ switch (action) {
+
+ /* help actions */
+ case HELP_OPEN_CONTENTS:
+ error = nsurl_create("http://www.netsurf-browser.org/documentation/", &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ break;
+
+ case HELP_OPEN_GUIDE:
+ error = nsurl_create("http://www.netsurf-browser.org/documentation/guide", &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ break;
+
+ case HELP_OPEN_INFORMATION:
+ error = nsurl_create("http://www.netsurf-browser.org/documentation/info", &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ break;
+
+ case HELP_OPEN_CREDITS:
+ error = nsurl_create("about:credits", &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ break;
+
+ case HELP_OPEN_LICENCE:
+ error = nsurl_create("about:licence", &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ break;
+
+ case HELP_LAUNCH_INTERACTIVE:
+ if (!ro_gui_interactive_help_available()) {
+ ro_gui_interactive_help_start();
+ nsoption_set_bool(interactive_help, true);
+ } else {
+ nsoption_set_bool(interactive_help, !nsoption_bool(interactive_help));
+ }
+ break;
+
+ /* history actions */
+ case HISTORY_SHOW_LOCAL:
+ ro_gui_window_action_local_history(g);
+ break;
+ case HISTORY_SHOW_GLOBAL:
+ ro_gui_global_history_open();
+ break;
+
+ /* hotlist actions */
+ case HOTLIST_ADD_URL:
+ ro_gui_window_action_add_bookmark(g);
+ break;
+ case HOTLIST_SHOW:
+ ro_gui_hotlist_open();
+ break;
+
+ /* cookies actions */
+ case COOKIES_SHOW:
+ ro_gui_cookies_open();
+ break;
+
+ case COOKIES_DELETE:
+ cookie_manager_keypress(NS_KEY_SELECT_ALL);
+ cookie_manager_keypress(NS_KEY_DELETE_LEFT);
+ break;
+
+ /* page actions */
+ case BROWSER_PAGE_INFO:
+ ro_gui_window_action_page_info(g);
+ break;
+ case BROWSER_PRINT:
+ ro_gui_window_action_print(g);
+ break;
+ case BROWSER_NEW_WINDOW:
+ ro_gui_window_action_new_window(g);
+ break;
+ case BROWSER_VIEW_SOURCE:
+ if (current_menu_main != NULL) {
+ ro_gui_view_source(current_menu_main);
+ } else if (h != NULL) {
+ ro_gui_view_source(h);
+ }
+ break;
+
+ /* object actions */
+ case BROWSER_OBJECT_INFO:
+ if (current_menu_object != NULL) {
+ ro_gui_window_prepare_objectinfo(current_menu_object,
+ current_menu_url);
+ ro_gui_dialog_open_persistent(g->window,
+ dialog_objinfo, false);
+ }
+ break;
+ case BROWSER_OBJECT_RELOAD:
+ if (current_menu_object != NULL) {
+ content_invalidate_reuse_data(current_menu_object);
+ browser_window_reload(bw, false);
+ }
+ break;
+
+ /* link actions */
+ case BROWSER_LINK_SAVE_URI:
+ if (current_menu_url != NULL) {
+ ro_gui_save_prepare(GUI_SAVE_LINK_URI, NULL, NULL,
+ current_menu_url, NULL);
+ ro_gui_dialog_open_persistent(g->window, dialog_saveas,
+ false);
+ }
+ break;
+ case BROWSER_LINK_SAVE_URL:
+ if (current_menu_url != NULL) {
+ ro_gui_save_prepare(GUI_SAVE_LINK_URL, NULL, NULL,
+ current_menu_url, NULL);
+ ro_gui_dialog_open_persistent(g->window, dialog_saveas,
+ false);
+ }
+ break;
+ case BROWSER_LINK_SAVE_TEXT:
+ if (current_menu_url != NULL) {
+ ro_gui_save_prepare(GUI_SAVE_LINK_TEXT, NULL, NULL,
+ current_menu_url, NULL);
+ ro_gui_dialog_open_persistent(g->window, dialog_saveas,
+ false);
+ }
+ break;
+
+ case BROWSER_LINK_DOWNLOAD:
+ if (current_menu_url != NULL) {
+ error = browser_window_navigate(bw,
+ current_menu_url,
+ browser_window_get_url(bw),
+ BW_NAVIGATE_DOWNLOAD,
+ NULL,
+ NULL,
+ NULL);
+ }
+ break;
+
+ case BROWSER_LINK_NEW_WINDOW:
+ if (current_menu_url != NULL) {
+ error = browser_window_create(
+ BW_CREATE_HISTORY |
+ BW_CREATE_CLONE,
+ current_menu_url,
+ browser_window_get_url(bw),
+ bw,
+ NULL);
+ }
+ break;
+
+
+ /* save actions */
+ case BROWSER_OBJECT_SAVE:
+ if (current_menu_object != NULL) {
+ ro_gui_save_prepare(GUI_SAVE_OBJECT_ORIG,
+ current_menu_object, NULL, NULL, NULL);
+ ro_gui_dialog_open_persistent(g->window, dialog_saveas,
+ false);
+ }
+ break;
+ case BROWSER_OBJECT_EXPORT_SPRITE:
+ if (current_menu_object != NULL) {
+ ro_gui_save_prepare(GUI_SAVE_OBJECT_NATIVE,
+ current_menu_object, NULL, NULL, NULL);
+ ro_gui_dialog_open_persistent(g->window, dialog_saveas,
+ false);
+ }
+ break;
+ case BROWSER_OBJECT_EXPORT_DRAW:
+ if (current_menu_object != NULL) {
+ ro_gui_save_prepare(GUI_SAVE_OBJECT_NATIVE,
+ current_menu_object, NULL, NULL, NULL);
+ ro_gui_dialog_open_persistent(g->window, dialog_saveas,
+ false);
+ }
+ break;
+ case BROWSER_SAVE:
+ ro_gui_window_action_save(g, GUI_SAVE_SOURCE);
+ break;
+ case BROWSER_SAVE_COMPLETE:
+ ro_gui_window_action_save(g, GUI_SAVE_COMPLETE);
+ break;
+ case BROWSER_EXPORT_DRAW:
+ ro_gui_window_action_save(g, GUI_SAVE_DRAW);
+ break;
+ case BROWSER_EXPORT_PDF:
+ ro_gui_window_action_save(g, GUI_SAVE_PDF);
+ break;
+ case BROWSER_EXPORT_TEXT:
+ ro_gui_window_action_save(g, GUI_SAVE_TEXT);
+ break;
+ case BROWSER_SAVE_URL_URI:
+ ro_gui_window_action_save(g, GUI_SAVE_LINK_URI);
+ break;
+ case BROWSER_SAVE_URL_URL:
+ ro_gui_window_action_save(g, GUI_SAVE_LINK_URL);
+ break;
+ case BROWSER_SAVE_URL_TEXT:
+ ro_gui_window_action_save(g, GUI_SAVE_LINK_TEXT);
+ break;
+
+ /* selection actions */
+ case BROWSER_SELECTION_SAVE:
+ if (h != NULL) {
+ ro_gui_save_prepare(GUI_SAVE_TEXT_SELECTION, NULL,
+ browser_window_get_selection(bw),
+ NULL, NULL);
+ ro_gui_dialog_open_persistent(g->window, dialog_saveas,
+ false);
+ }
+ break;
+ case BROWSER_SELECTION_COPY:
+ browser_window_key_press(bw, NS_KEY_COPY_SELECTION);
+ break;
+ case BROWSER_SELECTION_CUT:
+ browser_window_key_press(bw, NS_KEY_CUT_SELECTION);
+ break;
+ case BROWSER_SELECTION_PASTE:
+ ro_gui_selection_prepare_paste(w, ro_gui_window_paste_cb, bw);
+ break;
+ case BROWSER_SELECTION_ALL:
+ browser_window_key_press(bw, NS_KEY_SELECT_ALL);
+ break;
+ case BROWSER_SELECTION_CLEAR:
+ browser_window_key_press(bw, NS_KEY_CLEAR_SELECTION);
+ break;
+
+ /* navigation actions */
+ case BROWSER_NAVIGATE_HOME:
+ ro_gui_window_action_home(g);
+ break;
+ case BROWSER_NAVIGATE_BACK:
+ if (bw != NULL)
+ browser_window_history_back(bw, false);
+ break;
+ case BROWSER_NAVIGATE_FORWARD:
+ if (bw != NULL)
+ browser_window_history_forward(bw, false);
+ break;
+ case BROWSER_NAVIGATE_UP:
+ if (bw != NULL && h != NULL)
+ browser_window_navigate_up(g->bw, false);
+ break;
+ case BROWSER_NAVIGATE_RELOAD_ALL:
+ if (bw != NULL)
+ browser_window_reload(bw, true);
+ break;
+ case BROWSER_NAVIGATE_STOP:
+ if (bw != NULL)
+ browser_window_stop(bw);
+ break;
+
+ /* browser window/display actions */
+ case BROWSER_SCALE_VIEW:
+ ro_gui_window_action_zoom(g);
+ break;
+ case BROWSER_FIND_TEXT:
+ ro_gui_window_action_search(g);
+ break;
+ case BROWSER_IMAGES_FOREGROUND:
+ if (g != NULL)
+ nsoption_set_bool(foreground_images,
+ !nsoption_bool(foreground_images));
+ break;
+ case BROWSER_IMAGES_BACKGROUND:
+ if (g != NULL)
+ nsoption_set_bool(background_images,
+ !nsoption_bool(background_images));
+ break;
+ case BROWSER_BUFFER_ANIMS:
+ if (g != NULL)
+ g->option.buffer_animations =
+ !g->option.buffer_animations;
+ break;
+ case BROWSER_BUFFER_ALL:
+ if (g != NULL)
+ g->option.buffer_everything =
+ !g->option.buffer_everything;
+ break;
+ case BROWSER_SAVE_VIEW:
+ if (bw != NULL) {
+ ro_gui_window_default_options(g);
+ ro_gui_save_options();
+ }
+ break;
+ case BROWSER_WINDOW_DEFAULT:
+ if (g != NULL) {
+ os_error *oserror;
+
+ ro_gui_screen_size(&nsoption_int(window_screen_width),
+ &nsoption_int(window_screen_height));
+ state.w = w;
+ oserror = xwimp_get_window_state(&state);
+ if (oserror) {
+ LOG("xwimp_get_window_state: 0x%x: %s", oserror->errnum, oserror->errmess);
+ ro_warn_user("WimpError", oserror->errmess);
+ }
+ nsoption_set_int(window_x, state.visible.x0);
+ nsoption_set_int(window_y, state.visible.y0);
+ nsoption_set_int(window_width,
+ state.visible.x1 - state.visible.x0);
+ nsoption_set_int(window_height,
+ state.visible.y1 - state.visible.y0);
+ ro_gui_save_options();
+ }
+ break;
+ case BROWSER_WINDOW_STAGGER:
+ nsoption_set_bool(window_stagger,
+ !nsoption_bool(window_stagger));
+ ro_gui_save_options();
+ break;
+ case BROWSER_WINDOW_COPY:
+ nsoption_set_bool(window_size_clone,
+ !nsoption_bool(window_size_clone));
+ ro_gui_save_options();
+ break;
+ case BROWSER_WINDOW_RESET:
+ nsoption_set_int(window_screen_width, 0);
+ nsoption_set_int(window_screen_height, 0);
+ ro_gui_save_options();
+ break;
+
+ /* toolbar actions */
+ case TOOLBAR_BUTTONS:
+ assert(toolbar);
+ ro_toolbar_set_display_buttons(toolbar,
+ !ro_toolbar_get_display_buttons(toolbar));
+ break;
+ case TOOLBAR_ADDRESS_BAR:
+ assert(toolbar);
+ ro_toolbar_set_display_url(toolbar,
+ !ro_toolbar_get_display_url(toolbar));
+ if (ro_toolbar_get_display_url(toolbar))
+ ro_toolbar_take_caret(toolbar);
+ break;
+ case TOOLBAR_THROBBER:
+ assert(toolbar);
+ ro_toolbar_set_display_throbber(toolbar,
+ !ro_toolbar_get_display_throbber(toolbar));
+ break;
+ case TOOLBAR_EDIT:
+ assert(toolbar);
+ ro_toolbar_toggle_edit(toolbar);
+ break;
+
+ default:
+ return false;
+ }
+
+ if (error != NSERROR_OK) {
+ ro_warn_user(messages_get_errorcode(error), 0);
+ }
+
+ return true;
+}
+
+
+/**
+ * Handle the closure of a browser window menu
+ *
+ * \param w The window owning the menu.
+ * \param i The icon owning the menu.
+ * \param *menu The menu that is being closed.
+ */
+
+void ro_gui_window_menu_close(wimp_w w, wimp_i i, wimp_menu *menu)
+{
+ if (menu == ro_gui_browser_window_menu) {
+ current_menu_object = NULL;
+ current_menu_url = NULL;
+ } else if (menu == gui_form_select_menu) {
+ gui_form_select_control = NULL;
+ }
+}
+
+
+/**
+ * Process Scroll_Request events in a browser window.
+ *
+ * \param *scroll The wimp scroll event data block.
+ */
+
+void ro_gui_window_scroll(wimp_scroll *scroll)
+{
+ struct gui_window *g = ro_gui_window_lookup(scroll->w);
+
+ if (g && browser_window_has_content(g->bw) && ro_gui_shift_pressed()) {
+ /* extended scroll request with shift held down; change zoom */
+ float scale, inc;
+
+ if (scroll->ymin & 3)
+ inc = 0.02; /* RO5 sends the msg 5 times;
+ * don't ask me why
+ *
+ * @todo this is liable to break if
+ * HID is configured optimally for
+ * frame scrolling. *5 appears to be
+ * an artifact of non-HID mode scrolling.
+ */
+ else
+ inc = (1 << (ABS(scroll->ymin)>>2)) / 20.0F;
+
+ if (scroll->ymin > 0) {
+ scale = g->scale + inc;
+ if (scale > scale_snap_to[SCALE_SNAP_TO_SIZE - 1])
+ scale = scale_snap_to[SCALE_SNAP_TO_SIZE - 1];
+ } else {
+ scale = g->scale - inc;
+ if (scale < scale_snap_to[0])
+ scale = scale_snap_to[0];
+ }
+ if (g->scale != scale)
+ ro_gui_window_set_scale(g, scale);
+ } else if (g != NULL) {
+ ro_gui_window_scroll_action(g, scroll->xmin, scroll->ymin);
+ }
+}
+
+/**
+ * Process Pointer Entering Window events in a browser window.
+ *
+ * \param *entering The wimp pointer entering event data block.
+ */
+
+static void ro_gui_window_pointer_entering(wimp_entering *entering)
+{
+ struct gui_window *g = ro_gui_window_lookup(entering->w);
+
+ if (g != NULL)
+ ro_mouse_track_start(ro_gui_window_track_end,
+ ro_gui_window_mouse_at, g);
+}
+
+/**
+ * Process Pointer Leaving Window events in a browser window. These arrive via
+ * the termination callback handler from ro_mouse's mouse tracking.
+ *
+ * \param *leaving The wimp pointer leaving event data block.
+ * \param *data The GUI window that the pointer is leaving.
+ */
+
+static void ro_gui_window_track_end(wimp_leaving *leaving, void *data)
+{
+ struct gui_window *g = (struct gui_window *) data;
+
+ if (g != NULL)
+ gui_window_set_pointer(g, GUI_POINTER_DEFAULT);
+}
+
+
+/**
+ * Scroll a browser window, either via the core or directly using the
+ * normal Wimp_OpenWindow interface.
+ *
+ * Scroll steps are supplied in terms of the (extended) Scroll Event direction
+ * values returned by Wimp_Poll. Special values of 0x7fffffff and 0x80000000
+ * are added to mean "Home" and "End".
+ *
+ * \param *g The GUI Window to be scrolled.
+ * \param scroll_x The X scroll step to be applied.
+ * \param scroll_y The Y scroll step to be applied.
+ */
+
+void ro_gui_window_scroll_action(struct gui_window *g,
+ wimp_scroll_direction scroll_x, wimp_scroll_direction scroll_y)
+{
+ int visible_x, visible_y;
+ int step_x = 0, step_y = 0;
+ int toolbar_y;
+ wimp_window_state state;
+ wimp_pointer pointer;
+ os_error *error;
+ os_coord pos;
+ bool handled = false;
+ struct toolbar *toolbar;
+
+ if (g == NULL)
+ return;
+
+ /* Get the current window, toolbar and pointer details. */
+
+ state.w = g->window;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ return;
+ }
+
+ toolbar = ro_toolbar_parent_window_lookup(g->window);
+ assert(g == NULL || g->toolbar == NULL || g->toolbar == toolbar);
+
+ toolbar_y = (toolbar == NULL) ? 0 : ro_toolbar_full_height(toolbar);
+
+ visible_x = state.visible.x1 - state.visible.x0 - 32;
+ visible_y = state.visible.y1 - state.visible.y0 - 32 - toolbar_y;
+
+ error = xwimp_get_pointer_info(&pointer);
+ if (error) {
+ LOG("xwimp_get_pointer_info 0x%x : %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* Turn the scroll requirement from Scroll Event codes into coordinates
+ * that the core can understand.
+ */
+
+ switch (scroll_x) {
+ case wimp_SCROLL_PAGE_LEFT:
+ step_x = SCROLL_PAGE_DOWN;
+ break;
+ case wimp_SCROLL_AUTO_LEFT:
+ case wimp_SCROLL_COLUMN_LEFT:
+ step_x = -16;
+ break;
+ case wimp_SCROLL_AUTO_RIGHT:
+ case wimp_SCROLL_COLUMN_RIGHT:
+ step_x = 16;
+ break;
+ case wimp_SCROLL_PAGE_RIGHT:
+ step_x = SCROLL_PAGE_UP;
+ break;
+ case 0x80000000:
+ step_x = SCROLL_BOTTOM;
+ break;
+ case 0x7fffffff:
+ step_x = SCROLL_TOP;
+ break;
+ default:
+ step_x = (visible_x * (scroll_x>>2)) >> 2;
+ break;
+ }
+
+ switch (scroll_y) {
+ case wimp_SCROLL_PAGE_UP:
+ step_y = SCROLL_PAGE_UP;
+ break;
+ case wimp_SCROLL_AUTO_UP:
+ case wimp_SCROLL_LINE_UP:
+ step_y = -16;
+ break;
+ case wimp_SCROLL_AUTO_DOWN:
+ case wimp_SCROLL_LINE_DOWN:
+ step_y = 16;
+ break;
+ case wimp_SCROLL_PAGE_DOWN:
+ step_y = SCROLL_PAGE_DOWN;
+ break;
+ case 0x80000000:
+ step_y = SCROLL_BOTTOM;
+ break;
+ case 0x7fffffff:
+ step_y = SCROLL_TOP;
+ break;
+ default:
+ step_y = -((visible_y * (scroll_y>>2)) >> 2);
+ break;
+ }
+
+ /* If no scrolling is required, there's no point trying to do any. */
+
+ if (step_x == 0 && step_y == 0)
+ return;
+
+ /* If the pointer is over the window being scrolled, then try to get
+ * the core to do the scrolling on the object under the pointer.
+ */
+
+ if (pointer.w == g->window &&
+ ro_gui_window_to_window_pos(g,
+ pointer.pos.x, pointer.pos.y, &pos))
+ handled = browser_window_scroll_at_point(g->bw, pos.x, pos.y,
+ step_x, step_y);
+
+ /* If the core didn't do the scrolling, handle it via the Wimp.
+ * Windows which contain frames can only be scrolled by the core,
+ * because it implements frame scroll bars.
+ */
+
+ if (!handled && (browser_window_is_frameset(g->bw) == false)) {
+ switch (step_x) {
+ case SCROLL_TOP:
+ state.xscroll -= 0x10000000;
+ break;
+ case SCROLL_BOTTOM:
+ state.xscroll += 0x10000000;
+ break;
+ case SCROLL_PAGE_UP:
+ state.xscroll += visible_x;
+ break;
+ case SCROLL_PAGE_DOWN:
+ state.xscroll -= visible_x;
+ break;
+ default:
+ state.xscroll += 2 * step_x;
+ break;
+ }
+
+ switch (step_y) {
+ case SCROLL_TOP:
+ state.yscroll += 0x10000000;
+ break;
+ case SCROLL_BOTTOM:
+ state.yscroll -= 0x10000000;
+ break;
+ case SCROLL_PAGE_UP:
+ state.yscroll += visible_y;
+ break;
+ case SCROLL_PAGE_DOWN:
+ state.yscroll -= visible_y;
+ break;
+ default:
+ state.yscroll -= 2 * step_y;
+ break;
+ }
+
+ error = xwimp_open_window((wimp_open *) &state);
+ if (error) {
+ LOG("xwimp_open_window: 0x%x: %s", error->errnum, error->errmess);
+ }
+ }
+}
+
+
+/**
+ * Handle Message_DataLoad (file dragged in) for a window.
+ *
+ * \param g window
+ * \param message Message_DataLoad block
+ * \return true if the load was processed
+ *
+ * If the file was dragged into a form file input, it is used as the value.
+ */
+
+bool ro_gui_window_dataload(struct gui_window *g, wimp_message *message)
+{
+ os_error *error;
+ os_coord pos;
+
+ /* Ignore directories etc. */
+ if (0x1000 <= message->data.data_xfer.file_type)
+ return false;
+
+ if (!ro_gui_window_to_window_pos(g, message->data.data_xfer.pos.x,
+ message->data.data_xfer.pos.y, &pos))
+ return false;
+
+ if (browser_window_drop_file_at_point(g->bw, pos.x, pos.y,
+ message->data.data_xfer.file_name) == false)
+ return false;
+
+ /* send DataLoadAck */
+ message->action = message_DATA_LOAD_ACK;
+ message->your_ref = message->my_ref;
+ error = xwimp_send_message(wimp_USER_MESSAGE, message, message->sender);
+ if (error) {
+ LOG("xwimp_send_message: 0x%x: %s\n", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ return true;
+}
+
+
+/**
+ * Handle pointer movements in a browser window.
+ *
+ * \param *pointer new mouse position
+ * \param *data browser window that the pointer is in
+ */
+
+void ro_gui_window_mouse_at(wimp_pointer *pointer, void *data)
+{
+ os_coord pos;
+ struct gui_window *g = (struct gui_window *) data;
+
+ if (ro_gui_window_to_window_pos(g, pointer->pos.x, pointer->pos.y, &pos))
+ browser_window_mouse_track(g->bw,
+ ro_gui_mouse_drag_state(pointer->buttons,
+ wimp_BUTTON_DOUBLE_CLICK_DRAG),
+ pos.x, pos.y);
+}
+
+
+/**
+ * Window is being iconised. Create a suitable thumbnail sprite
+ * (which, sadly, must be in the Wimp sprite pool), and return
+ * the sprite name and truncated title to the iconiser
+ *
+ * \param g the gui window being iconised
+ * \param wi the WindowInfo message from the iconiser
+ */
+
+void ro_gui_window_iconise(struct gui_window *g,
+ wimp_full_message_window_info *wi)
+{
+ /* sadly there is no 'legal' way to get the sprite into
+ * the Wimp sprite pool other than via a filing system */
+ const char *temp_fname = "Pipe:$._tmpfile";
+ struct browser_window *bw = g->bw;
+ osspriteop_header *overlay = NULL;
+ osspriteop_header *sprite_header;
+ struct bitmap *bitmap;
+ osspriteop_area *area;
+ int width = 34, height = 34;
+ hlcache_handle *h;
+ os_error *error;
+ int len, id;
+
+ assert(bw);
+
+ h = browser_window_get_content(bw);
+ if (!h) return;
+
+ /* if an overlay sprite is defined, locate it and gets its dimensions
+ * so that we can produce a thumbnail with the same dimensions */
+ if (!ro_gui_wimp_get_sprite("ic_netsfxx", &overlay)) {
+ error = xosspriteop_read_sprite_info(osspriteop_PTR,
+ (osspriteop_area *)0x100,
+ (osspriteop_id)overlay, &width, &height, NULL,
+ NULL);
+ if (error) {
+ LOG("xosspriteop_read_sprite_info: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ overlay = NULL;
+ }
+ else if (sprite_bpp(overlay) != 8) {
+ LOG("overlay sprite is not 8bpp");
+ overlay = NULL;
+ }
+ }
+
+ /* create the thumbnail sprite */
+ bitmap = riscos_bitmap_create(width, height, BITMAP_NEW | BITMAP_OPAQUE |
+ BITMAP_CLEAR_MEMORY);
+ if (!bitmap) {
+ LOG("Thumbnail initialisation failed.");
+ return;
+ }
+ riscos_bitmap_render(bitmap, h);
+ if (overlay) {
+ riscos_bitmap_overlay_sprite(bitmap, overlay);
+ }
+ area = riscos_bitmap_convert_8bpp(bitmap);
+ riscos_bitmap_destroy(bitmap);
+ if (!area) {
+ LOG("Thumbnail conversion failed.");
+ return;
+ }
+
+ /* choose a suitable sprite name */
+ id = 0;
+ while (iconise_used[id])
+ if ((unsigned)++id >= NOF_ELEMENTS(iconise_used)) {
+ id = iconise_next;
+ if ((unsigned)++iconise_next >=
+ NOF_ELEMENTS(iconise_used))
+ iconise_next = 0;
+ break;
+ }
+
+ sprite_header = (osspriteop_header *)(area + 1);
+ len = sprintf(sprite_header->name, "ic_netsf%.2d", id);
+
+ error = xosspriteop_save_sprite_file(osspriteop_USER_AREA,
+ area, temp_fname);
+ if (error) {
+ LOG("xosspriteop_save_sprite_file: 0x%x:%s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ free(area);
+ return;
+ }
+
+ error = xwimpspriteop_merge_sprite_file(temp_fname);
+ if (error) {
+ LOG("xwimpspriteop_merge_sprite_file: 0x%x:%s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ remove(temp_fname);
+ free(area);
+ return;
+ }
+
+ memcpy(wi->sprite_name, sprite_header->name + 3, len - 2); /* inc NUL */
+ strncpy(wi->title, g->title, sizeof(wi->title));
+ wi->title[sizeof(wi->title) - 1] = '\0';
+
+ if (wimptextop_string_width(wi->title, 0) > 182) {
+ /* work around bug in Pinboard where it will fail to display
+ * the icon if the text is very wide */
+ if (strlen(wi->title) > 10)
+ wi->title[10] = '\0'; /* pinboard does this anyway */
+ while (wimptextop_string_width(wi->title, 0) > 182)
+ wi->title[strlen(wi->title) - 1] = '\0';
+ }
+
+ wi->size = sizeof(wimp_full_message_window_info);
+ wi->your_ref = wi->my_ref;
+ error = xwimp_send_message(wimp_USER_MESSAGE, (wimp_message*)wi,
+ wi->sender);
+ if (error) {
+ LOG("xwimp_send_message: 0x%x:%s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ else {
+ g->iconise_icon = id;
+ iconise_used[id] = true;
+ }
+
+ free(area);
+}
+
+
+/**
+ * Completes scrolling of a browser window
+ *
+ * \param *drag The DragEnd event data block.
+ * \param *data gui window block pointer.
+ */
+
+static void ro_gui_window_scroll_end(wimp_dragged *drag, void *data)
+{
+ wimp_pointer pointer;
+ os_error *error;
+ os_coord pos;
+ struct gui_window *g = (struct gui_window *) data;
+
+ if (!g)
+ return;
+
+ error = xwimp_drag_box((wimp_drag*)-1);
+ if (error) {
+ LOG("xwimp_drag_box: 0x%x : %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ error = xwimp_get_pointer_info(&pointer);
+ if (error) {
+ LOG("xwimp_get_pointer_info 0x%x : %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ error = xwimpspriteop_set_pointer_shape("ptr_default", 0x31, 0, 0, 0, 0);
+ if (error) {
+ LOG("xwimpspriteop_set_pointer_shape: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ if (ro_gui_window_to_window_pos(g, drag->final.x0, drag->final.y0, &pos))
+ browser_window_mouse_track(g->bw, 0, pos.x, pos.y);
+}
+
+
+/**
+ * Process Mouse_Click events in a toolbar's button bar. This does not handle
+ * other clicks in a toolbar: these are handled by the toolbar module itself.
+ *
+ * \param *data The GUI window associated with the click.
+ * \param action_type The action type to be handled.
+ * \param action The action to process.
+ */
+
+void ro_gui_window_toolbar_click(void *data,
+ toolbar_action_type action_type, union toolbar_action action)
+{
+ struct gui_window *g = data;
+ nserror err;
+
+ if (g == NULL)
+ return;
+
+
+ if (action_type == TOOLBAR_ACTION_URL) {
+ switch (action.url) {
+ case TOOLBAR_URL_DRAG_URL:
+ {
+ gui_save_type save_type;
+
+ if (!browser_window_has_content(g->bw))
+ break;
+
+ if (ro_gui_shift_pressed())
+ save_type = GUI_SAVE_LINK_URL;
+ else
+ save_type = GUI_SAVE_LINK_TEXT;
+
+ ro_gui_drag_save_link(save_type,
+ browser_window_get_url(g->bw),
+ browser_window_get_title(g->bw), g);
+ }
+ break;
+
+ case TOOLBAR_URL_SELECT_HOTLIST:
+ ro_gui_window_action_add_bookmark(g);
+ break;
+
+ case TOOLBAR_URL_ADJUST_HOTLIST:
+ ro_gui_window_action_remove_bookmark(g);
+ break;
+
+ default:
+ break;
+ }
+
+ return;
+ }
+
+
+ /* By now, the only valid action left is a button click. If it isn't
+ * one of those, give up.
+ */
+
+ if (action_type != TOOLBAR_ACTION_BUTTON)
+ return;
+
+ switch (action.button) {
+ case TOOLBAR_BUTTON_BACK:
+ if (g->bw != NULL)
+ browser_window_history_back(g->bw, false);
+ break;
+
+ case TOOLBAR_BUTTON_BACK_NEW:
+ if (g->bw != NULL)
+ browser_window_history_back(g->bw, true);
+ break;
+
+ case TOOLBAR_BUTTON_FORWARD:
+ if (g->bw != NULL)
+ browser_window_history_forward(g->bw, false);
+ break;
+
+ case TOOLBAR_BUTTON_FORWARD_NEW:
+ if (g->bw != NULL)
+ browser_window_history_forward(g->bw, true);
+ break;
+
+ case TOOLBAR_BUTTON_STOP:
+ if (g->bw != NULL)
+ browser_window_stop(g->bw);
+ break;
+
+ case TOOLBAR_BUTTON_RELOAD:
+ if (g->bw != NULL)
+ browser_window_reload(g->bw, false);
+ break;
+
+ case TOOLBAR_BUTTON_RELOAD_ALL:
+ if (g->bw != NULL)
+ browser_window_reload(g->bw, true);
+ break;
+
+ case TOOLBAR_BUTTON_HISTORY_LOCAL:
+ ro_gui_window_action_local_history(g);
+ break;
+
+ case TOOLBAR_BUTTON_HISTORY_GLOBAL:
+ ro_gui_global_history_open();
+ break;
+
+ case TOOLBAR_BUTTON_HOME:
+ ro_gui_window_action_home(g);
+ break;
+
+ case TOOLBAR_BUTTON_SEARCH:
+ ro_gui_window_action_search(g);
+ break;
+
+ case TOOLBAR_BUTTON_SCALE:
+ ro_gui_window_action_zoom(g);
+ break;
+
+ case TOOLBAR_BUTTON_BOOKMARK_OPEN:
+ ro_gui_hotlist_open();
+ break;
+
+ case TOOLBAR_BUTTON_BOOKMARK_ADD:
+ ro_gui_window_action_add_bookmark(g);
+ break;
+
+ case TOOLBAR_BUTTON_SAVE_SOURCE:
+ ro_gui_window_action_save(g, GUI_SAVE_SOURCE);
+ break;
+
+ case TOOLBAR_BUTTON_SAVE_COMPLETE:
+ ro_gui_window_action_save(g, GUI_SAVE_COMPLETE);
+ break;
+
+ case TOOLBAR_BUTTON_PRINT:
+ ro_gui_window_action_print(g);
+ break;
+
+ case TOOLBAR_BUTTON_UP:
+ err = browser_window_navigate_up(g->bw, false);
+ if (err != NSERROR_OK) {
+ ro_warn_user(messages_get_errorcode(err), NULL);
+ }
+ break;
+
+ case TOOLBAR_BUTTON_UP_NEW:
+ err = browser_window_navigate_up(g->bw, true);
+ if (err != NSERROR_OK) {
+ ro_warn_user(messages_get_errorcode(err), NULL);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ ro_gui_window_update_toolbar_buttons(g);
+}
+
+
+/**
+ * Handle Message_DataLoad (file dragged in) for a toolbar
+ *
+ * @todo This belongs in the toolbar module, and should be moved there
+ * once the module is able to usefully handle its own events.
+ *
+ * \param g window
+ * \param message Message_DataLoad block
+ * \return true if the load was processed
+ */
+
+bool ro_gui_toolbar_dataload(struct gui_window *g, wimp_message *message)
+{
+ if (message->data.data_xfer.file_type == osfile_TYPE_TEXT &&
+ ro_gui_window_import_text(g,
+ message->data.data_xfer.file_name)) {
+ os_error *error;
+
+ /* send DataLoadAck */
+ message->action = message_DATA_LOAD_ACK;
+ message->your_ref = message->my_ref;
+ error = xwimp_send_message(wimp_USER_MESSAGE, message,
+ message->sender);
+ if (error) {
+ LOG("xwimp_send_message: 0x%x: %s\n", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ return true;
+ }
+ return false;
+}
+
+
+/*
+ * Helper code for the Wimp Event Handlers.
+ */
+
+/**
+ * Check if a particular menu handle is a browser window menu
+ *
+ * \param *menu The menu in question.
+ * \return true if this menu is a browser window menu
+ */
+
+bool ro_gui_window_check_menu(wimp_menu *menu)
+{
+ return (ro_gui_browser_window_menu == menu) ? true : false;
+}
+
+
+/**
+ * Return boolean flags to show what RISC OS types we can sensibly convert
+ * the given object into.
+ *
+ * \todo This should probably be somewhere else but in window.c, and
+ * should probably even be done in content_().
+ *
+ * \param h The object to test.
+ * \param export_draw true on exit if a drawfile would be possible.
+ * \param export_sprite true on exit if a sprite would be possible.
+ * \return true if valid data is returned; else false.
+ */
+
+bool ro_gui_window_content_export_types(hlcache_handle *h,
+ bool *export_draw, bool *export_sprite)
+{
+ bool found_type = false;
+
+ if (export_draw != NULL)
+ *export_draw = false;
+ if (export_sprite != NULL)
+ *export_sprite = false;
+
+ if (h != NULL && content_get_type(h) == CONTENT_IMAGE) {
+ switch (ro_content_native_type(h)) {
+ case osfile_TYPE_SPRITE:
+ /* bitmap types (Sprite export possible) */
+ found_type = true;
+ if (export_sprite != NULL)
+ *export_sprite = true;
+ break;
+ case osfile_TYPE_DRAW:
+ /* vector types (Draw export possible) */
+ found_type = true;
+ if (export_draw != NULL)
+ *export_draw = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return found_type;
+}
+
+
+/**
+ * Prepare the page info window for use.
+ *
+ * \param *g The GUI window block to use.
+ */
+
+void ro_gui_window_prepare_pageinfo(struct gui_window *g)
+{
+ hlcache_handle *h = browser_window_get_content(g->bw);
+ char icon_buf[20] = "file_xxx";
+ char enc_buf[40];
+ const char *icon = icon_buf;
+ const char *title, *url;
+ lwc_string *mime;
+ const char *enc = "-";
+
+ assert(h);
+
+ title = content_get_title(h);
+ if (title == NULL)
+ title = "-";
+ url = nsurl_access(hlcache_handle_get_url(h));
+ if (url == NULL)
+ url = "-";
+ mime = content_get_mime_type(h);
+
+ sprintf(icon_buf, "file_%x", ro_content_filetype(h));
+ if (!ro_gui_wimp_sprite_exists(icon_buf))
+ sprintf(icon_buf, "file_xxx");
+
+ if (content_get_type(h) == CONTENT_HTML) {
+ if (content_get_encoding(h, CONTENT_ENCODING_NORMAL)) {
+ snprintf(enc_buf, sizeof enc_buf, "%s (%s)",
+ content_get_encoding(h, CONTENT_ENCODING_NORMAL),
+ content_get_encoding(h, CONTENT_ENCODING_SOURCE));
+ enc = enc_buf;
+ } else {
+ enc = messages_get("EncodingUnk");
+ }
+ }
+
+ ro_gui_set_icon_string(dialog_pageinfo, ICON_PAGEINFO_ICON,
+ icon, true);
+ ro_gui_set_icon_string(dialog_pageinfo, ICON_PAGEINFO_TITLE,
+ title, true);
+ ro_gui_set_icon_string(dialog_pageinfo, ICON_PAGEINFO_URL,
+ url, true);
+ ro_gui_set_icon_string(dialog_pageinfo, ICON_PAGEINFO_ENC,
+ enc, true);
+ ro_gui_set_icon_string(dialog_pageinfo, ICON_PAGEINFO_TYPE,
+ lwc_string_data(mime), true);
+
+ lwc_string_unref(mime);
+}
+
+
+/**
+ * Prepare the object info window for use
+ *
+ * \param *object the object for which information is to be displayed
+ * \param *target_url corresponding url, if any
+ */
+
+void ro_gui_window_prepare_objectinfo(hlcache_handle *object, nsurl *target_url)
+{
+ char icon_buf[20] = "file_xxx";
+ const char *url;
+ lwc_string *mime;
+ const char *target = "-";
+
+ sprintf(icon_buf, "file_%.3x",ro_content_filetype(object));
+ if (!ro_gui_wimp_sprite_exists(icon_buf)) {
+ sprintf(icon_buf, "file_xxx");
+ }
+
+ url = nsurl_access(hlcache_handle_get_url(object));
+ if (url == NULL) {
+ url = "-";
+ }
+ mime = content_get_mime_type(object);
+
+ if (target_url != NULL) {
+ target = nsurl_access(target_url);
+ }
+
+ ro_gui_set_icon_string(dialog_objinfo, ICON_OBJINFO_ICON,
+ icon_buf, true);
+ ro_gui_set_icon_string(dialog_objinfo, ICON_OBJINFO_URL,
+ url, true);
+ ro_gui_set_icon_string(dialog_objinfo, ICON_OBJINFO_TARGET,
+ target, true);
+ ro_gui_set_icon_string(dialog_objinfo, ICON_OBJINFO_TYPE,
+ lwc_string_data(mime), true);
+
+ lwc_string_unref(mime);
+}
+
+
+/*
+ * User Actions in the browser window
+ */
+
+/**
+ * Launch a new url in the given window.
+ *
+ * \param g gui_window to update
+ * \param url1 url to be launched
+ */
+
+void ro_gui_window_launch_url(struct gui_window *g, const char *url1)
+{
+ nserror error;
+ nsurl *url;
+
+ if (url1 == NULL)
+ return;
+
+ ro_gui_url_complete_close();
+
+ error = nsurl_create(url1, &url);
+ if (error != NSERROR_OK) {
+ ro_warn_user(messages_get_errorcode(error), 0);
+ } else {
+ ro_gui_window_set_url(g, url);
+
+ browser_window_navigate(g->bw, url,
+ NULL, BW_NAVIGATE_HISTORY,
+ NULL, NULL, NULL);
+ nsurl_unref(url);
+ }
+}
+
+
+/**
+ * Perform a Navigate Home action on a browser window.
+ *
+ * \param *g The browser window to act on.
+ */
+
+void ro_gui_window_action_home(struct gui_window *g)
+{
+ static const char *addr = NETSURF_HOMEPAGE;
+ nsurl *url;
+ nserror error;
+
+ if (g == NULL || g->bw == NULL)
+ return;
+
+ if (nsoption_charp(homepage_url) != NULL) {
+ addr = nsoption_charp(homepage_url);
+ }
+
+ error = nsurl_create(addr, &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_navigate(g->bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ ro_warn_user(messages_get_errorcode(error), 0);
+ }
+}
+
+
+/**
+ * Open a new browser window.
+ *
+ * \param *g The browser window to act on.
+ */
+
+void ro_gui_window_action_new_window(struct gui_window *g)
+{
+ nserror error;
+
+ if (g == NULL || g->bw == NULL)
+ return;
+
+ error = browser_window_create(BW_CREATE_CLONE,
+ browser_window_get_url(g->bw),
+ NULL, g->bw, NULL);
+
+ if (error != NSERROR_OK) {
+ ro_warn_user(messages_get_errorcode(error), 0);
+ }
+}
+
+
+/**
+ * Open a local history pane for a browser window.
+ *
+ * \param *g The browser window to act on.
+ */
+
+void ro_gui_window_action_local_history(struct gui_window *g)
+{
+ if (g != NULL && g->bw != NULL)
+ ro_gui_history_open(g, true);
+}
+
+
+/**
+ * Open a save dialogue for a browser window contents.
+ *
+ * \param *g The browser window to act on.
+ * \param save_type The type of save to open.
+ */
+
+void ro_gui_window_action_save(struct gui_window *g, gui_save_type save_type)
+{
+ hlcache_handle *h;
+
+ if (g == NULL || g->bw == NULL || !browser_window_has_content(g->bw))
+ return;
+
+ h = browser_window_get_content(g->bw);
+ if (h == NULL)
+ return;
+
+ ro_gui_save_prepare(save_type, h, NULL, NULL, NULL);
+ ro_gui_dialog_open_persistent(g->window, dialog_saveas, true);
+}
+
+
+/**
+ * Open a text search dialogue for a browser window.
+ *
+ * \param *g The browser window to act on.
+ */
+
+void ro_gui_window_action_search(struct gui_window *g)
+{
+ if (g == NULL || g->bw == NULL || !browser_window_can_search(g->bw))
+ return;
+
+ ro_gui_search_prepare(g->bw);
+ ro_gui_dialog_open_persistent(g->window, dialog_search, true);
+}
+
+
+/**
+ * Open a zoom dialogue for a browser window.
+ *
+ * \param *g The browser window to act on.
+ */
+
+void ro_gui_window_action_zoom(struct gui_window *g)
+{
+ if (g == NULL)
+ return;
+
+ ro_gui_dialog_prepare_zoom(g);
+ ro_gui_dialog_open_persistent(g->window, dialog_zoom, true);
+}
+
+
+/**
+ * Add a hotlist entry for a browser window.
+ *
+ * \param *g The browser window to act on.
+ */
+
+static void ro_gui_window_action_add_bookmark(struct gui_window *g)
+{
+ nsurl *url;
+
+ if (g == NULL || g->bw == NULL || g->toolbar == NULL ||
+ browser_window_has_content(g->bw) == false)
+ return;
+
+ url = browser_window_get_url(g->bw);
+
+ ro_gui_hotlist_add_page(url);
+ ro_toolbar_update_hotlist(g->toolbar);
+}
+
+
+/**
+ * Remove a hotlist entry for a browser window.
+ *
+ * \param *g The browser window to act on.
+ */
+
+static void ro_gui_window_action_remove_bookmark(struct gui_window *g)
+{
+ nsurl *url;
+
+ if (g == NULL || g->bw == NULL || g->toolbar == NULL ||
+ browser_window_has_content(g->bw) == false)
+ return;
+
+ url = browser_window_get_url(g->bw);
+
+ ro_gui_hotlist_remove_page(url);
+}
+
+
+/**
+ * Open a print dialogue for a browser window.
+ *
+ * \param *g The browser window to act on.
+ */
+
+void ro_gui_window_action_print(struct gui_window *g)
+{
+ if (g == NULL)
+ return;
+
+ ro_gui_print_prepare(g);
+ ro_gui_dialog_open_persistent(g->window, dialog_print, true);
+}
+
+
+/**
+ * Open a page info box for a browser window.
+ *
+ * \param *g The browser window to act on.
+ */
+
+void ro_gui_window_action_page_info(struct gui_window *g)
+{
+ if (g == NULL || g->bw == NULL ||
+ browser_window_has_content(g->bw) == false)
+ return;
+
+ ro_gui_window_prepare_pageinfo(g);
+ ro_gui_dialog_open_persistent(g->window, dialog_pageinfo, false);
+}
+
+
+/*
+ * Window and Toolbar Redraw and Update
+ */
+
+/**
+ * Redraws the content for all windows.
+ */
+
+void ro_gui_window_redraw_all(void)
+{
+ struct gui_window *g;
+ for (g = window_list; g; g = g->next)
+ gui_window_redraw_window(g);
+}
+
+
+/**
+ * Remove all pending update boxes for a window
+ *
+ * \param g gui_window
+ */
+void ro_gui_window_remove_update_boxes(struct gui_window *g)
+{
+ struct update_box *cur;
+
+ for (cur = pending_updates; cur != NULL; cur = cur->next) {
+ if (cur->g == g)
+ cur->g = NULL;
+ }
+}
+
+
+/**
+ * Redraw any pending update boxes.
+ */
+void ro_gui_window_update_boxes(void)
+{
+ osbool more;
+ bool use_buffer;
+ wimp_draw update;
+ struct rect clip;
+ os_error *error;
+ struct update_box *cur;
+ struct gui_window *g;
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &ro_plotters
+ };
+
+ for (cur = pending_updates; cur != NULL; cur = cur->next) {
+ g = cur->g;
+ if (!g)
+ continue;
+
+ use_buffer = cur->use_buffer;
+
+ update.w = g->window;
+ update.box.x0 = cur->x0;
+ update.box.y0 = cur->y0;
+ update.box.x1 = cur->x1;
+ update.box.y1 = cur->y1;
+
+ error = xwimp_update_window(&update, &more);
+ if (error) {
+ LOG("xwimp_update_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ continue;
+ }
+
+ /* Set the current redraw gui_window to get options from */
+ ro_gui_current_redraw_gui = g;
+
+ ro_plot_origin_x = update.box.x0 - update.xscroll;
+ ro_plot_origin_y = update.box.y1 - update.yscroll;
+
+ while (more) {
+ clip.x0 = (update.clip.x0 - ro_plot_origin_x) / 2;
+ clip.y0 = (ro_plot_origin_y - update.clip.y1) / 2;
+ clip.x1 = (update.clip.x1 - ro_plot_origin_x) / 2;
+ clip.y1 = (ro_plot_origin_y - update.clip.y0) / 2;
+
+ if (use_buffer)
+ ro_gui_buffer_open(&update);
+
+ browser_window_redraw(g->bw, 0, 0, &clip, &ctx);
+
+ if (use_buffer)
+ ro_gui_buffer_close();
+
+ error = xwimp_get_rectangle(&update, &more);
+ /* RISC OS 3.7 returns an error here if enough buffer
+ * was claimed to cause a new dynamic area to be
+ * created. It doesn't actually stop anything working,
+ * so we mask it out for now until a better fix is
+ * found. This appears to be a bug in RISC OS. */
+ if (error && !(use_buffer &&
+ error->errnum == error_WIMP_GET_RECT)) {
+ LOG("xwimp_get_rectangle: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ ro_gui_current_redraw_gui = NULL;
+ continue;
+ }
+ }
+
+ /* Reset the current redraw gui_window to prevent
+ * thumbnails from retaining options */
+ ro_gui_current_redraw_gui = NULL;
+ }
+ while (pending_updates) {
+ cur = pending_updates;
+ pending_updates = pending_updates->next;
+ free(cur);
+ }
+}
+
+
+/**
+ * callback from core to reformat a window.
+ */
+static void riscos_window_reformat(struct gui_window *gw)
+{
+ if (gw != NULL) {
+ browser_window_reformat(gw->bw, false,
+ gw->old_width / 2,
+ gw->old_height / 2);
+ }
+}
+
+/**
+ * Destroy all browser windows.
+ */
+
+void ro_gui_window_quit(void)
+{
+ while (window_list) {
+ struct gui_window *cur = window_list;
+ window_list = window_list->next;
+
+ browser_window_destroy(cur->bw);
+ }
+}
+
+
+/**
+ * Animate the "throbbers" of all browser windows.
+ */
+
+void ro_gui_throb(void)
+{
+ struct gui_window *g;
+
+ for (g = window_list; g; g = g->next) {
+ if (!g->active)
+ continue;
+ if (g->toolbar != NULL)
+ ro_toolbar_throb(g->toolbar);
+ }
+}
+
+
+/**
+ * Update the toolbar buttons for a given browser window to reflect the
+ * current state of its contents.
+ *
+ * Note that the parameters to this function are arranged so that it can be
+ * supplied to the toolbar module as an button state update callback.
+ *
+ * \param *g The browser window to update.
+ */
+
+void ro_gui_window_update_toolbar_buttons(struct gui_window *g)
+{
+ struct browser_window *bw;
+ struct toolbar *toolbar;
+
+ if (g == NULL || g->toolbar == NULL)
+ return;
+
+ bw = g->bw;
+ toolbar = g->toolbar;
+
+ ro_toolbar_set_button_shaded_state(toolbar, TOOLBAR_BUTTON_RELOAD,
+ !browser_window_reload_available(bw));
+
+ ro_toolbar_set_button_shaded_state(toolbar, TOOLBAR_BUTTON_STOP,
+ !browser_window_stop_available(bw));
+
+ ro_toolbar_set_button_shaded_state(toolbar, TOOLBAR_BUTTON_BACK,
+ !browser_window_back_available(bw));
+
+ ro_toolbar_set_button_shaded_state(toolbar, TOOLBAR_BUTTON_FORWARD,
+ !browser_window_forward_available(bw));
+
+ ro_toolbar_set_button_shaded_state(toolbar, TOOLBAR_BUTTON_UP,
+ !browser_window_up_available(bw));
+
+ ro_toolbar_set_button_shaded_state(toolbar, TOOLBAR_BUTTON_SEARCH,
+ !browser_window_can_search(bw));
+
+ ro_toolbar_set_button_shaded_state(toolbar, TOOLBAR_BUTTON_SCALE,
+ !browser_window_has_content(bw));
+
+ ro_toolbar_set_button_shaded_state(toolbar, TOOLBAR_BUTTON_PRINT,
+ !browser_window_has_content(bw));
+
+ ro_toolbar_set_button_shaded_state(toolbar, TOOLBAR_BUTTON_SAVE_SOURCE,
+ !browser_window_has_content(bw));
+
+ ro_toolbar_update_urlsuggest(toolbar);
+}
+
+
+/**
+ * Update a window to reflect a change in toolbar size: used as a callback by
+ * the toolbar module when a toolbar height changes.
+ *
+ * \param *data void pointer the window's gui_window struct
+ */
+
+void ro_gui_window_update_toolbar(void *data)
+{
+ struct gui_window *g = (struct gui_window *) data;
+
+ if (g != NULL)
+ gui_window_update_extent(g);
+}
+
+
+/**
+ * Save a new toolbar button configuration: used as a callback by the toolbar
+ * module when a buttonbar edit has finished.
+ *
+ * \param *data void pointer to the window's gui_window struct
+ * \param *config pointer to a malloc()'d button config string.
+ */
+
+void ro_gui_window_save_toolbar_buttons(void *data, char *config)
+{
+ nsoption_set_charp(toolbar_browser, config);
+ ro_gui_save_options();
+}
+
+
+/**
+ * Update a window and its toolbar to reflect a new theme: used as a callback
+ * by the toolbar module when a theme change affects a toolbar.
+ *
+ * \param *data void pointer to the window's gui_window struct
+ * \param ok true if the bar still exists; else false.
+ */
+
+void ro_gui_window_update_theme(void *data, bool ok)
+{
+ struct gui_window *g = (struct gui_window *) data;
+
+ if (g != NULL && g->toolbar != NULL) {
+ if (ok) {
+ gui_window_update_extent(g);
+ } else {
+ g->toolbar = NULL;
+ }
+ }
+}
+
+
+/*
+ * General Window Support
+ */
+
+/**
+ * Import text file into window
+ *
+ * \param g gui window containing textarea
+ * \param filename pathname of file to be imported
+ * \return true iff successful
+ */
+
+bool ro_gui_window_import_text(struct gui_window *g, const char *filename)
+{
+ fileswitch_object_type obj_type;
+ os_error *error;
+ char *buf, *utf8_buf, *sp;
+ int size;
+ nserror ret;
+ const char *ep;
+ char *p;
+
+ error = xosfile_read_stamped(filename, &obj_type, NULL, NULL,
+ &size, NULL, NULL);
+ if (error) {
+ LOG("xosfile_read_stamped: 0x%x:%s", error->errnum, error->errmess);
+ ro_warn_user("FileError", error->errmess);
+ return true; /* was for us, but it didn't work! */
+ }
+
+ /* Allocate one byte more than needed to ensure that the buffer is
+ * always terminated, regardless of file contents.
+ */
+
+ buf = calloc(size + 1, sizeof(char));
+ if (!buf) {
+ ro_warn_user("NoMemory", NULL);
+ return true;
+ }
+
+ error = xosfile_load_stamped(filename, (byte*)buf,
+ NULL, NULL, NULL, NULL, NULL);
+
+ if (error) {
+ LOG("xosfile_load_stamped: 0x%x:%s", error->errnum, error->errmess);
+ ro_warn_user("LoadError", error->errmess);
+ free(buf);
+ return true;
+ }
+
+ ret = utf8_from_local_encoding(buf, size, &utf8_buf);
+ if (ret != NSERROR_OK) {
+ /* bad encoding shouldn't happen */
+ assert(ret != NSERROR_BAD_ENCODING);
+ LOG("utf8_from_local_encoding failed");
+ free(buf);
+ ro_warn_user("NoMemory", NULL);
+ return true;
+ }
+ size = strlen(utf8_buf);
+
+ ep = utf8_buf + size;
+ p = utf8_buf;
+
+ /* skip leading whitespace */
+ while (isspace(*p)) p++;
+
+ sp = p;
+ while (*p && *p != '\r' && *p != '\n')
+ p += utf8_next(p, ep - p, 0);
+ *p = '\0';
+
+ if (p > sp)
+ ro_gui_window_launch_url(g, sp);
+
+ free(buf);
+ free(utf8_buf);
+ return true;
+}
+
+
+/**
+ * Clones a browser window's options.
+ *
+ * \param new_gui the new gui window
+ * \param old_gui the gui window to clone from, or NULL for default
+ */
+
+void ro_gui_window_clone_options(
+ struct gui_window *new_gui,
+ struct gui_window *old_gui)
+{
+ assert(new_gui);
+
+ /* Clone the basic options
+ */
+ if (!old_gui) {
+ new_gui->option.buffer_animations = nsoption_bool(buffer_animations);
+ new_gui->option.buffer_everything = nsoption_bool(buffer_everything);
+ } else {
+ new_gui->option = old_gui->option;
+ }
+
+ /* Set up the toolbar
+ */
+ if (new_gui->toolbar) {
+ ro_toolbar_set_display_buttons(new_gui->toolbar,
+ nsoption_bool(toolbar_show_buttons));
+ ro_toolbar_set_display_url(new_gui->toolbar,
+ nsoption_bool(toolbar_show_address));
+ ro_toolbar_set_display_throbber(new_gui->toolbar,
+ nsoption_bool(toolbar_show_throbber));
+ if ((old_gui) && (old_gui->toolbar)) {
+ ro_toolbar_set_display_buttons(new_gui->toolbar,
+ ro_toolbar_get_display_buttons(
+ old_gui->toolbar));
+ ro_toolbar_set_display_url(new_gui->toolbar,
+ ro_toolbar_get_display_url(
+ old_gui->toolbar));
+ ro_toolbar_set_display_throbber(new_gui->toolbar,
+ ro_toolbar_get_display_throbber(
+ old_gui->toolbar));
+ ro_toolbar_process(new_gui->toolbar, -1, true);
+ }
+ }
+}
+
+
+/**
+ * Makes a browser window's options the default.
+ *
+ * \param gui The riscos gui window to set default options in.
+ */
+
+void ro_gui_window_default_options(struct gui_window *gui)
+{
+ if (gui == NULL)
+ return;
+
+ /* Save the basic options
+ */
+ nsoption_set_int(scale, gui->scale * 100);
+ nsoption_set_bool(buffer_animations, gui->option.buffer_animations);
+ nsoption_set_bool(buffer_everything, gui->option.buffer_everything);
+
+ /* Set up the toolbar
+ */
+ if (gui->toolbar != NULL) {
+ nsoption_set_bool(toolbar_show_buttons,
+ ro_toolbar_get_display_buttons(gui->toolbar));
+ nsoption_set_bool(toolbar_show_address,
+ ro_toolbar_get_display_url(gui->toolbar));
+ nsoption_set_bool(toolbar_show_throbber,
+ ro_toolbar_get_display_throbber(gui->toolbar));
+ }
+ if (gui->status_bar != NULL)
+ nsoption_set_int(toolbar_status_size,
+ ro_gui_status_bar_get_width(gui->status_bar));
+}
+
+
+/*
+ * Custom Menu Support
+ */
+
+/**
+ * Prepare or reprepare a form select menu, setting up the menu handle
+ * globals in the process.
+ *
+ * \param g The RISC OS gui window the menu is in.
+ * \param control The form control needing a menu.
+ * \return true if the menu is OK to be opened; else false.
+ */
+
+bool ro_gui_window_prepare_form_select_menu(struct gui_window *g,
+ struct form_control *control)
+{
+ unsigned int item, entries;
+ char *text_convert, *temp;
+ struct form_option *option;
+ bool reopen = true;
+ nserror err;
+
+ assert(control);
+
+ /* enumerate the entries */
+ entries = 0;
+ option = form_select_get_option(control, entries);
+ while (option != NULL) {
+ entries++;
+ option = form_select_get_option(control, entries);
+ }
+
+ if (entries == 0) {
+ /* no menu to display */
+ ro_gui_menu_destroy();
+ return false;
+ }
+
+ /* free riscos menu if there already is one */
+ if ((gui_form_select_menu) && (control != gui_form_select_control)) {
+ for (item = 0; ; item++) {
+ free(gui_form_select_menu->entries[item].data.
+ indirected_text.text);
+ if (gui_form_select_menu->entries[item].menu_flags &
+ wimp_MENU_LAST)
+ break;
+ }
+ free(gui_form_select_menu->title_data.indirected_text.text);
+ free(gui_form_select_menu);
+ gui_form_select_menu = 0;
+ }
+
+ /* allocate new riscos menu */
+ if (!gui_form_select_menu) {
+ reopen = false;
+ gui_form_select_menu = malloc(wimp_SIZEOF_MENU(entries));
+ if (!gui_form_select_menu) {
+ ro_warn_user("NoMemory", 0);
+ ro_gui_menu_destroy();
+ return false;
+ }
+ err = utf8_to_local_encoding(messages_get("SelectMenu"), 0,
+ &text_convert);
+ if (err != NSERROR_OK) {
+ /* badenc should never happen */
+ assert(err != NSERROR_BAD_ENCODING);
+ LOG("utf8_to_local_encoding failed");
+ ro_warn_user("NoMemory", 0);
+ ro_gui_menu_destroy();
+ return false;
+ }
+ gui_form_select_menu->title_data.indirected_text.text =
+ text_convert;
+ ro_gui_menu_init_structure(gui_form_select_menu, entries);
+ }
+
+ /* initialise menu entries from form control */
+ for (item = 0; item < entries; item++) {
+ option = form_select_get_option(control, item);
+ gui_form_select_menu->entries[item].menu_flags = 0;
+ if (option->selected)
+ gui_form_select_menu->entries[item].menu_flags =
+ wimp_MENU_TICKED;
+ if (!reopen) {
+
+ /* convert spaces to hard spaces to stop things
+ * like 'Go Home' being treated as if 'Home' is a
+ * keyboard shortcut and right aligned in the menu.
+ */
+
+ temp = cnv_space2nbsp(option->text);
+ if (!temp) {
+ LOG("cnv_space2nbsp failed");
+ ro_warn_user("NoMemory", 0);
+ ro_gui_menu_destroy();
+ return false;
+ }
+
+ err = utf8_to_local_encoding(temp,
+ 0, &text_convert);
+ if (err != NSERROR_OK) {
+ /* A bad encoding should never happen,
+ * so assert this */
+ assert(err != NSERROR_BAD_ENCODING);
+ LOG("utf8_to_enc failed");
+ ro_warn_user("NoMemory", 0);
+ ro_gui_menu_destroy();
+ return false;
+ }
+
+ free(temp);
+
+ gui_form_select_menu->entries[item].data.indirected_text.text =
+ text_convert;
+ gui_form_select_menu->entries[item].data.indirected_text.size =
+ strlen(gui_form_select_menu->entries[item].
+ data.indirected_text.text) + 1;
+ }
+ }
+
+ gui_form_select_menu->entries[0].menu_flags |=
+ wimp_MENU_TITLE_INDIRECTED;
+ gui_form_select_menu->entries[item - 1].menu_flags |= wimp_MENU_LAST;
+
+ return true;
+}
+
+/**
+ * Process selections from a form select menu, passing them back to the core.
+ *
+ * \param *g The browser window affected by the menu.
+ * \param *selection The menu selection.
+ */
+
+void ro_gui_window_process_form_select_menu(struct gui_window *g,
+ wimp_selection *selection)
+{
+ assert(g != NULL);
+
+ if (selection->items[0] >= 0)
+ form_select_process_selection(gui_form_select_control,
+ selection->items[0]);
+}
+
+
+/*
+ * Window and Toolbar Lookup
+ */
+
+/**
+ * Convert a RISC OS window handle to a gui_window.
+ *
+ * \param window RISC OS window handle.
+ * \return A pointer to a riscos gui window if found or NULL.
+ */
+
+struct gui_window *ro_gui_window_lookup(wimp_w window)
+{
+ struct gui_window *g;
+ for (g = window_list; g; g = g->next)
+ if (g->window == window)
+ return g;
+ return NULL;
+}
+
+
+/**
+ * Convert a toolbar RISC OS window handle to a gui_window.
+ *
+ * \param window RISC OS window handle of a toolbar
+ * \return pointer to a structure if found, NULL otherwise
+ */
+
+struct gui_window *ro_gui_toolbar_lookup(wimp_w window)
+{
+ struct gui_window *g = NULL;
+ struct toolbar *toolbar;
+ wimp_w parent;
+
+ toolbar = ro_toolbar_window_lookup(window);
+
+ if (toolbar != NULL) {
+ parent = ro_toolbar_get_parent_window(toolbar);
+ g = ro_gui_window_lookup(parent);
+ }
+
+ return g;
+}
+
+
+/*
+ * Core to RISC OS Conversions
+ */
+
+/**
+ * Convert x,y screen co-ordinates into window co-ordinates.
+ *
+ * \param g gui window
+ * \param x x ordinate
+ * \param y y ordinate
+ * \param pos receives position in window co-ordinatates
+ * \return true iff conversion successful
+ */
+
+bool ro_gui_window_to_window_pos(struct gui_window *g, int x, int y,
+ os_coord *pos)
+{
+ wimp_window_state state;
+ os_error *error;
+
+ assert(g);
+
+ state.w = g->window;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x:%s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+ pos->x = (x - (state.visible.x0 - state.xscroll)) / 2 / g->scale;
+ pos->y = ((state.visible.y1 - state.yscroll) - y) / 2 / g->scale;
+ return true;
+}
+
+
+/**
+ * Convert x,y window co-ordinates into screen co-ordinates.
+ *
+ * \param g gui window
+ * \param x x ordinate
+ * \param y y ordinate
+ * \param pos receives position in screen co-ordinatates
+ * \return true iff conversion successful
+ */
+
+bool ro_gui_window_to_screen_pos(struct gui_window *g, int x, int y,
+ os_coord *pos)
+{
+ wimp_window_state state;
+ os_error *error;
+
+ assert(g);
+
+ state.w = g->window;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x:%s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+ pos->x = (x * 2 * g->scale) + (state.visible.x0 - state.xscroll);
+ pos->y = (state.visible.y1 - state.yscroll) - (y * 2 * g->scale);
+ return true;
+}
+
+
+/*
+ * Miscellaneous Functions
+ *
+ * \TODO -- These items might well belong elsewhere.
+ */
+
+/**
+ * Returns the state of the mouse buttons and modifiers keys for a
+ * mouse action, suitable for passing to the OS-independent
+ * browser window/ treeview/ etc code.
+ *
+ * \param buttons Wimp button state.
+ * \param type Wimp work-area/icon type for decoding.
+ * \return NetSurf core button state.
+ */
+
+browser_mouse_state ro_gui_mouse_click_state(wimp_mouse_state buttons,
+ wimp_icon_flags type)
+{
+ browser_mouse_state state = 0; /* Blank state with nothing set */
+ static struct {
+ enum { CLICK_SINGLE, CLICK_DOUBLE, CLICK_TRIPLE } type;
+ uint64_t time;
+ } last_click;
+
+ switch (type) {
+ case wimp_BUTTON_CLICK_DRAG:
+ /* Handle single clicks. */
+
+ /* We fire core PRESS and CLICK events together for "action on
+ * press" behaviour. */
+ if (buttons & (wimp_CLICK_SELECT)) /* Select click */
+ state |= BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_CLICK_1;
+ if (buttons & (wimp_CLICK_ADJUST)) /* Adjust click */
+ state |= BROWSER_MOUSE_PRESS_2 | BROWSER_MOUSE_CLICK_2;
+ break;
+
+ case wimp_BUTTON_DOUBLE_CLICK_DRAG:
+ /* Handle single, double, and triple clicks. */
+
+ /* Single clicks: Fire PRESS and CLICK events together
+ * for "action on press" behaviour. */
+ if (buttons & (wimp_SINGLE_SELECT)) {
+ /* Select single click */
+ state |= BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_CLICK_1;
+ } else if (buttons & (wimp_SINGLE_ADJUST)) {
+ /* Adjust single click */
+ state |= BROWSER_MOUSE_PRESS_2 | BROWSER_MOUSE_CLICK_2;
+ }
+
+ /* Double clicks: Fire PRESS, CLICK, and DOUBLE_CLICK
+ * events together for "action on 2nd press" behaviour. */
+ if (buttons & (wimp_DOUBLE_SELECT)) {
+ /* Select double click */
+ state |= BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_CLICK_1 |
+ BROWSER_MOUSE_DOUBLE_CLICK;
+ } else if (buttons & (wimp_DOUBLE_ADJUST)) {
+ /* Adjust double click */
+ state |= BROWSER_MOUSE_PRESS_2 | BROWSER_MOUSE_CLICK_2 |
+ BROWSER_MOUSE_DOUBLE_CLICK;
+ }
+
+ /* Need to consider what we have and decide whether to fire
+ * triple click instead */
+ if ((state == (BROWSER_MOUSE_PRESS_1 |
+ BROWSER_MOUSE_CLICK_1)) ||
+ (state == (BROWSER_MOUSE_PRESS_2 |
+ BROWSER_MOUSE_CLICK_2))) {
+ /* WIMP told us single click, but maybe we want to call
+ * it a triple click */
+
+ if (last_click.type == CLICK_DOUBLE) {
+ uint64_t ms_now;
+ nsu_getmonotonic_ms(&ms_now);
+
+ if (ms_now < (last_click.time + 500)) {
+ /* Triple click! Fire PRESS, CLICK, and
+ * TRIPLE_CLICK events together for
+ * "action on 3nd press" behaviour. */
+ last_click.type = CLICK_TRIPLE;
+ state |= BROWSER_MOUSE_TRIPLE_CLICK;
+ } else {
+ /* Single click */
+ last_click.type = CLICK_SINGLE;
+ }
+ } else {
+ /* Single click */
+ last_click.type = CLICK_SINGLE;
+ }
+ } else if ((state == (BROWSER_MOUSE_PRESS_1 |
+ BROWSER_MOUSE_CLICK_1 |
+ BROWSER_MOUSE_DOUBLE_CLICK)) ||
+ (state == (BROWSER_MOUSE_PRESS_2 |
+ BROWSER_MOUSE_CLICK_2 |
+ BROWSER_MOUSE_DOUBLE_CLICK))) {
+ /* Wimp told us double click, but we may want to
+ * call it single click */
+
+ if (last_click.type == CLICK_TRIPLE) {
+ state &= ~BROWSER_MOUSE_DOUBLE_CLICK;
+ last_click.type = CLICK_SINGLE;
+ } else {
+ last_click.type = CLICK_DOUBLE;
+ nsu_getmonotonic_ms(&last_click.time);
+ }
+ } else {
+ last_click.type = CLICK_SINGLE;
+ }
+ break;
+ }
+
+ /* Check if a drag has started */
+ if (buttons & (wimp_DRAG_SELECT)) {
+ /* A drag was _started_ with Select; Fire DRAG_1. */
+ state |= BROWSER_MOUSE_DRAG_1;
+ mouse_drag_select = true;
+ }
+ if (buttons & (wimp_DRAG_ADJUST)) {
+ /* A drag was _started_ with Adjust; Fire DRAG_2. */
+ state |= BROWSER_MOUSE_DRAG_2;
+ mouse_drag_adjust = true;
+ }
+
+ /* Set modifier key state */
+ if (ro_gui_shift_pressed()) state |= BROWSER_MOUSE_MOD_1;
+ if (ro_gui_ctrl_pressed()) state |= BROWSER_MOUSE_MOD_2;
+ if (ro_gui_alt_pressed()) state |= BROWSER_MOUSE_MOD_3;
+
+ return state;
+}
+
+
+/**
+ * Returns the state of the mouse buttons and modifiers keys whilst
+ * dragging, for passing to the OS-independent browser window/ treeview/
+ * etc code
+ *
+ * \param buttons Wimp button state.
+ * \param type Wimp work-area/icon type for decoding.
+ * \return NetSurf core button state.
+ */
+
+browser_mouse_state ro_gui_mouse_drag_state(wimp_mouse_state buttons,
+ wimp_icon_flags type)
+{
+ browser_mouse_state state = 0; /* Blank state with nothing set */
+
+ /* If mouse buttons aren't held, turn off drags */
+ if (!(buttons & (wimp_CLICK_SELECT | wimp_CLICK_ADJUST))) {
+ mouse_drag_select = false;
+ mouse_drag_adjust = false;
+ }
+
+ /* If there's a drag happening, set DRAG_ON and record which button
+ * the drag is happening with, i.e. HOLDING_1 or HOLDING_2 */
+ if (mouse_drag_select) {
+ state |= BROWSER_MOUSE_DRAG_ON | BROWSER_MOUSE_HOLDING_1;
+ }
+ if (mouse_drag_adjust) {
+ state |= BROWSER_MOUSE_DRAG_ON | BROWSER_MOUSE_HOLDING_2;
+ }
+
+ /* Set modifier key state */
+ if (ro_gui_shift_pressed()) state |= BROWSER_MOUSE_MOD_1;
+ if (ro_gui_ctrl_pressed()) state |= BROWSER_MOUSE_MOD_2;
+ if (ro_gui_alt_pressed()) state |= BROWSER_MOUSE_MOD_3;
+
+ return state;
+}
+
+
+/**
+ * Returns true iff one or more Shift keys is held down
+ */
+
+bool ro_gui_shift_pressed(void)
+{
+ int shift = 0;
+ xosbyte1(osbyte_SCAN_KEYBOARD, 0 ^ 0x80, 0, &shift);
+ return (shift == 0xff);
+}
+
+
+/**
+ * Returns true iff one or more Ctrl keys is held down
+ */
+
+bool ro_gui_ctrl_pressed(void)
+{
+ int ctrl = 0;
+ xosbyte1(osbyte_SCAN_KEYBOARD, 1 ^ 0x80, 0, &ctrl);
+ return (ctrl == 0xff);
+}
+
+
+/**
+ * Returns true iff one or more Alt keys is held down
+ */
+
+bool ro_gui_alt_pressed(void)
+{
+ int alt = 0;
+ xosbyte1(osbyte_SCAN_KEYBOARD, 2 ^ 0x80, 0, &alt);
+ return (alt == 0xff);
+}
+
+static struct gui_window_table window_table = {
+ .create = gui_window_create,
+ .destroy = gui_window_destroy,
+ .redraw = gui_window_redraw_window,
+ .update = gui_window_update_box,
+ .get_scroll = gui_window_get_scroll,
+ .set_scroll = gui_window_set_scroll,
+ .get_dimensions = gui_window_get_dimensions,
+ .update_extent = gui_window_update_extent,
+ .reformat = riscos_window_reformat,
+
+ .set_title = gui_window_set_title,
+ .set_url = ro_gui_window_set_url,
+ .set_icon = gui_window_set_icon,
+ .set_status = riscos_window_set_status,
+ .set_pointer = gui_window_set_pointer,
+ .place_caret = gui_window_place_caret,
+ .remove_caret = gui_window_remove_caret,
+ .save_link = gui_window_save_link,
+ .drag_start = gui_window_drag_start,
+ .scroll_visible = gui_window_scroll_visible,
+ .scroll_start = gui_window_scroll_start,
+ .new_content = gui_window_new_content,
+ .start_throbber = gui_window_start_throbber,
+ .stop_throbber = gui_window_stop_throbber,
+ .create_form_select_menu = gui_window_create_form_select_menu,
+
+ /* from save */
+ .drag_save_object = gui_drag_save_object,
+ .drag_save_selection =gui_drag_save_selection,
+
+ /* from textselection */
+ .start_selection = gui_start_selection,
+};
+
+struct gui_window_table *riscos_window_table = &window_table;
diff --git a/frontends/riscos/window.h b/frontends/riscos/window.h
new file mode 100644
index 000000000..2e6f6e9aa
--- /dev/null
+++ b/frontends/riscos/window.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2010, 2011 Stephen Fryatt <stevef@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/** \file
+ * Browser window handling (interface).
+ */
+
+#include <stdbool.h>
+
+#ifndef _NETSURF_RISCOS_WINDOW_H_
+#define _NETSURF_RISCOS_WINDOW_H_
+
+struct gui_window;
+struct nsurl;
+
+extern struct gui_window_table *riscos_window_table;
+
+void ro_gui_window_initialise(void);
+
+bool ro_gui_window_check_menu(wimp_menu *menu);
+
+/**
+ * Set the contents of a window's address bar.
+ *
+ * \param g gui_window to update
+ * \param url new url for address bar
+ */
+nserror ro_gui_window_set_url(struct gui_window *g, struct nsurl *url);
+
+#endif
+
diff --git a/frontends/windows/Makefile b/frontends/windows/Makefile
new file mode 100644
index 000000000..a3a95d9da
--- /dev/null
+++ b/frontends/windows/Makefile
@@ -0,0 +1,75 @@
+#
+# Makefile for NetSurf Windows target
+#
+# This file is part of NetSurf
+
+LDFLAGS += -L${GCCSDK_INSTALL_ENV}/lib
+CFLAGS += -I${GCCSDK_INSTALL_ENV}/include/
+
+
+$(eval $(call pkg_config_find_and_add,libcares,Cares))
+$(eval $(call pkg_config_find_and_add,zlib,ZLib))
+
+
+LDFLAGS += -lssl -lcrypto -lgnurx -lgdi32 -lcomctl32 -lws2_32 -lmsimg32 -mwindows
+
+CFLAGS += -U__STRICT_ANSI__ -mwin32
+# only windows versions after XP are supported
+CFLAGS += '-DWINVER=0x0501'
+CFLAGS += '-D_WIN32_WINNT=0x0501'
+CFLAGS += '-D_WIN32_WINDOWS=0x0501'
+CFLAGS += '-D_WIN32_IE=0x0501'
+
+#installed resource path
+CFLAGS += '-DNETSURF_WINDOWS_RESPATH="$(NETSURF_WINDOWS_RESPATH)"'
+
+WSCFLAGS := -std=c99 -DCURL_STATICLIB -DCARES_STATICLIB -g
+
+CFLAGS += $(WSCFLAGS)
+LDFLAGS += $(WSCFLAGS)
+
+# ----------------------------------------------------------------------------
+# built-in resource setup
+# ----------------------------------------------------------------------------
+
+$(OBJROOT)/windows_resource.o: $(FRONTEND_RESOURCES_DIR)/resource.rc
+ $(VQ)echo " WINDRES: compiling windows resources"
+ ${Q}$(WINDRES) $< -O coff -o $@
+
+S_RESOURCES := windows_resource.o
+
+# ----------------------------------------------------------------------------
+# Source file setup
+# ----------------------------------------------------------------------------
+
+# sources purely for the windows build
+S_FRONTEND := main.c window.c gui.c drawable.c plot.c findfile.c \
+ font.c bitmap.c about.c prefs.c download.c filetype.c file.c \
+ localhistory.c schedule.c windbg.c pointers.c
+
+# This is the final source build list
+# Note this is deliberately *not* expanded here as common and image
+# are not yet available
+SOURCES = $(S_COMMON) $(S_IMAGE) $(S_BROWSER) $(S_FRONTEND) $(S_RESOURCES)
+EXETARGET := NetSurf.exe
+
+# ----------------------------------------------------------------------------
+# Install target
+# ----------------------------------------------------------------------------
+
+install-windows:
+
+# ----------------------------------------------------------------------------
+# Package target
+# ----------------------------------------------------------------------------
+
+package-windows: netsurf-installer.exe
+
+WIN_RES_OBJ := installer.nsi NetSurf.ico netsurf.png welcome.html default.css
+WIN_RES_INS_OBJ := $(addprefix $(FRONTEND_RESOURCES_DIR)/,$(WIN_RES_OBJ)) $(OBJROOT)/messages
+
+$(OBJROOT)/messages: resources/FatMessages
+ $(Q)$(SPLIT_MESSAGES) -l en -p win -f messages resources/FatMessages > $@
+
+netsurf-installer.exe: $(EXETARGET) $(WIN_RES_INS_OBJ)
+ makensis -V4 -NOCD $(FRONTEND_RESOURCES_DIR)/installer.nsi
diff --git a/frontends/windows/Makefile.defaults b/frontends/windows/Makefile.defaults
new file mode 100644
index 000000000..1d844f112
--- /dev/null
+++ b/frontends/windows/Makefile.defaults
@@ -0,0 +1,25 @@
+# ----------------------------------------------------------------------------
+# windows-specific options
+# ----------------------------------------------------------------------------
+
+# Where to search for NetSurf's resources after looking in ~/.netsurf and
+# $NETSURFRES. It must have a trailing backslash
+NETSURF_WINDOWS_RESPATH :=
+
+# Enable NetSurf's use of librosprite for displaying RISC OS Sprites
+# Valid options: YES, NO, AUTO
+NETSURF_USE_ROSPRITE := NO
+
+# Enable NetSurf's use of libsvgtiny for displaying SVGs
+# Valid options: YES, NO
+NETSURF_USE_NSSVG := NO
+
+# Force using glibc internal iconv implementation instead of external libiconv
+# Valid options: YES, NO
+NETSURF_USE_LIBICONV_PLUG := NO
+
+# no pdf support
+NETSURF_USE_HARU_PDF := NO
+
+# Optimisation levels
+CFLAGS += -O2
diff --git a/frontends/windows/about.c b/frontends/windows/about.c
new file mode 100644
index 000000000..4716a5c91
--- /dev/null
+++ b/frontends/windows/about.c
@@ -0,0 +1,151 @@
+/*
+* Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+*
+* This file is part of NetSurf, http://www.netsurf-browser.org/
+*
+* NetSurf 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; version 2 of the License.
+*
+* NetSurf 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/>.
+*/
+
+/**
+ * \file
+ * This is The win32 API about dialog implementation.
+ */
+
+#include <stdio.h>
+
+#include "utils/config.h"
+
+#include <windows.h>
+
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "utils/messages.h"
+#include "desktop/version.h"
+
+#include "windows/gui.h"
+#include "windows/window.h"
+#include "windows/about.h"
+#include "windows/resourceid.h"
+
+#include "windbg.h"
+
+/**
+ * Initialize the about dialog text fields
+ */
+static BOOL init_about_dialog(HWND hwnd)
+{
+ char ver_str[128];
+ HWND dlg_itm;
+ HFONT hFont;
+
+ dlg_itm = GetDlgItem(hwnd, IDC_ABOUT_VERSION);
+ if (dlg_itm != NULL) {
+
+ hFont=CreateFont (26, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, "Arial");
+ if (hFont != NULL) {
+ LOG("Setting font object");
+ SendMessage(dlg_itm, WM_SETFONT, (WPARAM)hFont, 0);
+ }
+
+ snprintf(ver_str, sizeof(ver_str), "%s %s",
+ messages_get("NetSurf"), netsurf_version);
+
+ SendMessage(dlg_itm, WM_SETTEXT, 0, (LPARAM)ver_str);
+ }
+
+ dlg_itm = GetDlgItem(hwnd, IDC_ABOUT_COPYRIGHT);
+ if (dlg_itm != NULL) {
+ snprintf(ver_str, sizeof(ver_str), "%s",
+ messages_get("NetSurfCopyright"));
+
+ SendMessage(dlg_itm, WM_SETTEXT, 0, (LPARAM)ver_str);
+ }
+
+ return TRUE;
+}
+
+/**
+ * destroy resources used to create about dialog
+ */
+static BOOL destroy_about_dialog(HWND hwnd)
+{
+ HWND dlg_itm;
+ HFONT hFont;
+
+ dlg_itm = GetDlgItem(hwnd, IDC_ABOUT_VERSION);
+ if (dlg_itm != NULL) {
+ hFont = (HFONT)SendMessage(dlg_itm, WM_GETFONT, 0, 0);
+ if (hFont != NULL) {
+ LOG("Destroyed font object");
+ DeleteObject(hFont);
+ }
+ }
+
+ return TRUE;
+
+}
+
+static BOOL CALLBACK
+nsws_about_event_callback(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+
+ LOG_WIN_MSG(hwnd, msg, wparam, lparam);
+
+ switch(msg) {
+ case WM_INITDIALOG:
+ return init_about_dialog(hwnd);
+
+ case WM_COMMAND:
+ switch(LOWORD(wparam)) {
+ case IDOK:
+ LOG("OK clicked");
+ EndDialog(hwnd, IDOK);
+ break;
+
+ case IDCANCEL:
+ LOG("Cancel clicked");
+ EndDialog(hwnd, IDOK);
+ break;
+
+ case IDC_BTN_CREDITS:
+ nsws_window_go(hwnd, "about:credits");
+ EndDialog(hwnd, IDOK);
+ break;
+
+ case IDC_BTN_LICENCE:
+ nsws_window_go(hwnd, "about:licence");
+ EndDialog(hwnd, IDOK);
+ break;
+
+ }
+ break;
+
+ case WM_CREATE:
+ return TRUE;
+
+ case WM_DESTROY:
+ return destroy_about_dialog(hwnd);
+
+ }
+ return FALSE;
+}
+
+void nsws_about_dialog_init(HINSTANCE hinst, HWND parent)
+{
+ int ret = DialogBox(hinst, MAKEINTRESOURCE(IDD_DLG_ABOUT), parent,
+ nsws_about_event_callback);
+ if (ret == -1) {
+ win32_warning(messages_get("NoMemory"), 0);
+ return;
+ }
+}
diff --git a/frontends/windows/about.h b/frontends/windows/about.h
new file mode 100644
index 000000000..e0315b507
--- /dev/null
+++ b/frontends/windows/about.h
@@ -0,0 +1,24 @@
+/*
+* Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+*
+* This file is part of NetSurf, http://www.netsurf-browser.org/
+*
+* NetSurf 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; version 2 of the License.
+*
+* NetSurf 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 _NETSURF_WINDOWS_ABOUT_H_
+#define _NETSURF_WINDOWS_ABOUT_H_
+
+void nsws_about_dialog_init(HINSTANCE hinst, HWND parent);
+
+#endif
diff --git a/frontends/windows/bitmap.c b/frontends/windows/bitmap.c
new file mode 100644
index 000000000..664244838
--- /dev/null
+++ b/frontends/windows/bitmap.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * win32 implementation of the bitmap operations.
+ */
+
+#include "utils/config.h"
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <string.h>
+#include <windows.h>
+
+#include "utils/log.h"
+#include "image/bitmap.h"
+#include "desktop/plotters.h"
+#include "content/content.h"
+
+#include "windows/plot.h"
+#include "windows/bitmap.h"
+
+/**
+ * Create a bitmap.
+ *
+ * \param width width of image in pixels
+ * \param height width of image in pixels
+ * \param state a flag word indicating the initial state
+ * \return an opaque struct bitmap, or NULL on memory exhaustion
+ */
+void *win32_bitmap_create(int width, int height, unsigned int state)
+{
+ struct bitmap *bitmap;
+ BITMAPV5HEADER *pbmi;
+ HBITMAP windib;
+ uint8_t *pixdata;
+
+ LOG("width %d, height %d, state %u", width, height, state);
+
+ pbmi = calloc(1, sizeof(BITMAPV5HEADER));
+ if (pbmi == NULL) {
+ return NULL;
+ }
+
+ pbmi->bV5Size = sizeof(BITMAPV5HEADER);
+ pbmi->bV5Width = width;
+ pbmi->bV5Height = -height;
+ pbmi->bV5Planes = 1;
+ pbmi->bV5BitCount = 32;
+ pbmi->bV5Compression = BI_BITFIELDS;
+
+ pbmi->bV5RedMask = 0xff; /* red mask */
+ pbmi->bV5GreenMask = 0xff00; /* green mask */
+ pbmi->bV5BlueMask = 0xff0000; /* blue mask */
+ pbmi->bV5AlphaMask = 0xff000000; /* alpha mask */
+
+ windib = CreateDIBSection(NULL, (BITMAPINFO *)pbmi, DIB_RGB_COLORS, (void **)&pixdata, NULL, 0);
+
+ if (windib == NULL) {
+ free(pbmi);
+ return NULL;
+ }
+
+ bitmap = calloc(1 , sizeof(struct bitmap));
+ if (bitmap == NULL) {
+ DeleteObject(windib);
+ free(pbmi);
+ return NULL;
+ }
+
+ bitmap->width = width;
+ bitmap->height = height;
+ bitmap->windib = windib;
+ bitmap->pbmi = pbmi;
+ bitmap->pixdata = pixdata;
+ if ((state & BITMAP_OPAQUE) != 0) {
+ bitmap->opaque = true;
+ } else {
+ bitmap->opaque = false;
+ }
+
+ LOG("bitmap %p", bitmap);
+
+ return bitmap;
+}
+
+
+/**
+ * Return a pointer to the pixel data in a bitmap.
+ *
+ * The pixel data is packed as BITMAP_FORMAT, possibly with padding at the end
+ * of rows. The width of a row in bytes is given by bitmap_get_rowstride().
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ * \return pointer to the pixel buffer
+ */
+static unsigned char *bitmap_get_buffer(void *bitmap)
+{
+ struct bitmap *bm = bitmap;
+ if (bitmap == NULL) {
+ LOG("NULL bitmap!");
+ return NULL;
+ }
+
+ return bm->pixdata;
+}
+
+
+/**
+ * Find the width of a pixel row in bytes.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ * \return width of a pixel row in the bitmap
+ */
+static size_t bitmap_get_rowstride(void *bitmap)
+{
+ struct bitmap *bm = bitmap;
+
+ if (bitmap == NULL) {
+ LOG("NULL bitmap!");
+ return 0;
+ }
+
+ return (bm->width) * 4;
+}
+
+
+/**
+ * Free a bitmap.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ */
+void win32_bitmap_destroy(void *bitmap)
+{
+ struct bitmap *bm = bitmap;
+
+ if (bitmap == NULL) {
+ LOG("NULL bitmap!");
+ return;
+ }
+
+ DeleteObject(bm->windib);
+ free(bm->pbmi);
+ free(bm);
+}
+
+
+/**
+ * Save a bitmap in the platform's native format.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ * \param path pathname for file
+ * \param flags flags controlling how the bitmap is saved.
+ * \return true on success, false on error and error reported
+ */
+static bool bitmap_save(void *bitmap, const char *path, unsigned flags)
+{
+ return true;
+}
+
+
+/**
+ * The bitmap image has changed, so flush any persistant cache.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ */
+static void bitmap_modified(void *bitmap) {
+}
+
+/**
+ * Sets whether a bitmap should be plotted opaque
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ * \param opaque whether the bitmap should be plotted opaque
+ */
+static void bitmap_set_opaque(void *bitmap, bool opaque)
+{
+ struct bitmap *bm = bitmap;
+
+ if (bitmap == NULL) {
+ LOG("NULL bitmap!");
+ return;
+ }
+
+ LOG("setting bitmap %p to %s", bm, opaque ? "opaque" : "transparent");
+ bm->opaque = opaque;
+}
+
+
+/**
+ * Tests whether a bitmap has an opaque alpha channel
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ * \return whether the bitmap is opaque
+ */
+static bool bitmap_test_opaque(void *bitmap)
+{
+ int tst;
+ struct bitmap *bm = bitmap;
+
+ if (bitmap == NULL) {
+ LOG("NULL bitmap!");
+ return false;
+ }
+
+ tst = bm->width * bm->height;
+
+ while (tst-- > 0) {
+ if (bm->pixdata[(tst << 2) + 3] != 0xff) {
+ LOG("bitmap %p has transparency", bm);
+ return false;
+ }
+ }
+ LOG("bitmap %p is opaque", bm);
+ return true;
+}
+
+
+/**
+ * Gets whether a bitmap should be plotted opaque
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ */
+static bool bitmap_get_opaque(void *bitmap)
+{
+ struct bitmap *bm = bitmap;
+
+ if (bitmap == NULL) {
+ LOG("NULL bitmap!");
+ return false;
+ }
+
+ return bm->opaque;
+}
+
+static int bitmap_get_width(void *bitmap)
+{
+ struct bitmap *bm = bitmap;
+
+ if (bitmap == NULL) {
+ LOG("NULL bitmap!");
+ return 0;
+ }
+
+ return(bm->width);
+}
+
+static int bitmap_get_height(void *bitmap)
+{
+ struct bitmap *bm = bitmap;
+
+ if (bitmap == NULL) {
+ LOG("NULL bitmap!");
+ return 0;
+ }
+
+ return(bm->height);
+}
+
+static size_t bitmap_get_bpp(void *bitmap)
+{
+ return 4;
+}
+
+struct bitmap *bitmap_scale(struct bitmap *prescale, int width, int height)
+{
+ struct bitmap *ret = malloc(sizeof(struct bitmap));
+ int i, ii, v, vv;
+ uint32_t *retpixdata, *inpixdata; /* 4 byte types for quicker
+ * transfer */
+ if (ret == NULL)
+ return NULL;
+
+ retpixdata = malloc(width * height * 4);
+ if (retpixdata == NULL) {
+ free(ret);
+ return NULL;
+ }
+
+ inpixdata = (uint32_t *)prescale->pixdata;
+ ret->pixdata = (uint8_t *)retpixdata;
+ ret->height = height;
+ ret->width = width;
+ for (i = 0; i < height; i++) {
+ v = i * width;
+ vv = (int)((i * prescale->height) / height) * prescale->width;
+ for (ii = 0; ii < width; ii++) {
+ retpixdata[v + ii] = inpixdata[vv + (int)
+ ((ii * prescale->width) / width)];
+ }
+ }
+ return ret;
+
+}
+
+
+static nserror
+bitmap_render(struct bitmap *bitmap, struct hlcache_handle *content)
+{
+ int width;
+ int height;
+ HDC hdc, bufferdc, minidc;
+ struct bitmap *fsbitmap;
+ struct redraw_context ctx = {
+ .interactive = false,
+ .background_images = true,
+ .plot = &win_plotters
+ };
+
+ width = min(content_get_width(content), 1024);
+ height = ((width * bitmap->height) + (bitmap->width / 2)) /
+ bitmap->width;
+
+ LOG("bitmap %p for content %p width %d, height %d",
+ bitmap, content, width, height);
+
+ /* create two memory device contexts to put the bitmaps in */
+ bufferdc = CreateCompatibleDC(NULL);
+ if ((bufferdc == NULL)) {
+ return NSERROR_NOMEM;
+ }
+
+ minidc = CreateCompatibleDC(NULL);
+ if ((minidc == NULL)) {
+ DeleteDC(bufferdc);
+ return NSERROR_NOMEM;
+ }
+
+ /* create a full size bitmap and plot into it */
+ fsbitmap = win32_bitmap_create(width, height, BITMAP_NEW | BITMAP_CLEAR_MEMORY | BITMAP_OPAQUE);
+
+ SelectObject(bufferdc, fsbitmap->windib);
+
+ hdc = plot_hdc;
+ plot_hdc = bufferdc;
+ /* render the content */
+ content_scaled_redraw(content, width, height, &ctx);
+ plot_hdc = hdc;
+
+ /* scale bitmap bufferbm into minibm */
+ SelectObject(minidc, bitmap->windib);
+
+ bitmap->opaque = true;
+
+ StretchBlt(minidc, 0, 0, bitmap->width, bitmap->height, bufferdc, 0, 0, width, height, SRCCOPY);
+
+ DeleteDC(bufferdc);
+ DeleteDC(minidc);
+ win32_bitmap_destroy(fsbitmap);
+
+ return NSERROR_OK;
+}
+
+static struct gui_bitmap_table bitmap_table = {
+ .create = win32_bitmap_create,
+ .destroy = win32_bitmap_destroy,
+ .set_opaque = bitmap_set_opaque,
+ .get_opaque = bitmap_get_opaque,
+ .test_opaque = bitmap_test_opaque,
+ .get_buffer = bitmap_get_buffer,
+ .get_rowstride = bitmap_get_rowstride,
+ .get_width = bitmap_get_width,
+ .get_height = bitmap_get_height,
+ .get_bpp = bitmap_get_bpp,
+ .save = bitmap_save,
+ .modified = bitmap_modified,
+ .render = bitmap_render,
+};
+
+struct gui_bitmap_table *win32_bitmap_table = &bitmap_table;
diff --git a/frontends/windows/bitmap.h b/frontends/windows/bitmap.h
new file mode 100644
index 000000000..c723159e1
--- /dev/null
+++ b/frontends/windows/bitmap.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_WINDOWS_BITMAP_H_
+#define _NETSURF_WINDOWS_BITMAP_H_
+
+struct gui_bitmap_table *win32_bitmap_table;
+
+struct bitmap {
+ HBITMAP windib;
+ BITMAPV5HEADER *pbmi;
+ int width;
+ int height;
+ uint8_t *pixdata;
+ bool opaque;
+};
+
+struct bitmap *bitmap_scale(struct bitmap *prescale, int width, int height);
+
+void *win32_bitmap_create(int width, int height, unsigned int state);
+
+void win32_bitmap_destroy(void *bitmap);
+
+#endif
diff --git a/frontends/windows/download.c b/frontends/windows/download.c
new file mode 100644
index 000000000..b281ea76a
--- /dev/null
+++ b/frontends/windows/download.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <limits.h>
+
+#include "utils/config.h"
+
+#include <shlobj.h>
+#include <windows.h>
+
+#include "utils/sys_time.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/url.h"
+#include "utils/nsurl.h"
+#include "utils/utils.h"
+#include "utils/string.h"
+#include "content/fetch.h"
+#include "desktop/gui_download.h"
+#include "desktop/download.h"
+
+#include "windows/download.h"
+#include "windows/window.h"
+#include "windows/gui.h"
+#include "windows/resourceid.h"
+#include "windows/schedule.h"
+
+struct gui_download_window {
+ HWND hwnd;
+ char *title;
+ char *filename;
+ char *domain;
+ char *time_left;
+ char *total_size;
+ char *original_total_size;
+ int size;
+ int downloaded;
+ unsigned int progress;
+ int time_remaining;
+ struct timeval start_time;
+ int speed;
+ int error;
+ struct gui_window *window;
+ FILE *file;
+ download_status status;
+};
+
+static bool downloading = false;
+static struct gui_download_window *download1;
+
+BOOL CALLBACK nsws_download_event_callback(HWND hwnd, UINT msg, WPARAM wparam,
+ LPARAM lparam);
+static void nsws_download_update_label(void *p);
+static void nsws_download_update_progress(void *p);
+static void nsws_download_clear_data(struct gui_download_window *w);
+
+static bool nsws_download_window_up(struct gui_download_window *w)
+{
+ w->hwnd = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DLG_DOWNLOAD),
+ gui_window_main_window(w->window),
+ nsws_download_event_callback);
+ if (w->hwnd == NULL) {
+ return false;
+ }
+ ShowWindow(w->hwnd, SW_SHOW);
+ return true;
+}
+
+static struct gui_download_window *
+gui_download_window_create(download_context *ctx, struct gui_window *gui)
+{
+ if (downloading) {
+ /* initial implementation */
+ win32_warning("1 download at a time please", 0);
+ return NULL;
+ }
+ downloading = true;
+ struct gui_download_window *w =
+ malloc(sizeof(struct gui_download_window));
+ if (w == NULL) {
+ win32_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ int total_size = download_context_get_total_length(ctx);
+ char *domain, *filename, *destination;
+ nsurl *url = download_context_get_url(ctx);
+ bool unknown_size = (total_size == 0);
+ const char *size = (unknown_size) ?
+ messages_get("UnknownSize") :
+ human_friendly_bytesize(total_size);
+
+ if (nsurl_nice(url, &filename, false) != NSERROR_OK) {
+ filename = strdup(messages_get("UnknownFile"));
+ }
+ if (filename == NULL) {
+ win32_warning(messages_get("NoMemory"), 0);
+ free(w);
+ return NULL;
+ }
+
+ if (nsurl_has_component(url, NSURL_HOST)) {
+ domain = strdup(lwc_string_data(nsurl_get_component(url, NSURL_HOST)));
+ } else {
+ domain = strdup(messages_get("UnknownHost"));
+ }
+ if (domain == NULL) {
+ win32_warning(messages_get("NoMemory"), 0);
+ free(filename);
+ free(w);
+ return NULL;
+ }
+ destination = malloc(PATH_MAX);
+ if (destination == NULL) {
+ win32_warning(messages_get("NoMemory"), 0);
+ free(domain);
+ free(filename);
+ free(w);
+ return NULL;
+ }
+ SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, SHGFP_TYPE_CURRENT,
+ destination);
+ if (strlen(destination) < PATH_MAX - 2)
+ strcat(destination, "/");
+ if (strlen(destination) + strlen(filename) < PATH_MAX - 1)
+ strcat(destination, filename);
+ LOG("download %s [%s] from %s to %s", filename, size, domain, destination);
+ w->title = filename;
+ w->domain = domain;
+ w->size = total_size;
+ w->total_size = strdup(size);
+ if (w->total_size == NULL) {
+ win32_warning(messages_get("NoMemory"), 0);
+ free(destination);
+ free(domain);
+ free(filename);
+ free(w);
+ return NULL;
+ }
+ w->downloaded = 0;
+ w->speed = 0;
+ gettimeofday(&(w->start_time), NULL);
+ w->time_remaining = -1;
+ w->time_left = NULL;
+ w->status = DOWNLOAD_NONE;
+ w->filename = destination;
+ w->progress = 0;
+ w->error = 0;
+ w->window = gui;
+ w->file = fopen(destination, "wb");
+ if (w->file == NULL) {
+ win32_warning(messages_get("FileOpenWriteError"), destination);
+ free(destination);
+ free(domain);
+ free(filename);
+ free(w->total_size);
+ free(w->time_left);
+ free(w);
+ return NULL;
+ }
+ download1 = w;
+
+ if (nsws_download_window_up(w) == false) {
+ win32_warning(messages_get("NoMemory"), 0);
+ free(destination);
+ free(domain);
+ free(filename);
+ free(w->total_size);
+ free(w->time_left);
+ free(w);
+ return NULL;
+ }
+ return w;
+}
+
+
+BOOL CALLBACK nsws_download_event_callback(HWND hwnd, UINT msg, WPARAM wparam,
+ LPARAM lparam)
+{
+ HWND sub;
+ switch(msg){
+ case WM_INITDIALOG:
+ sub = GetDlgItem(hwnd, IDC_DOWNLOAD_LABEL);
+ nsws_download_update_label((void *)download1);
+ nsws_download_update_progress((void *)download1);
+ return TRUE;
+ case WM_COMMAND:
+ switch(LOWORD(wparam)) {
+ case IDOK:
+ if (download1->downloaded != download1->size)
+ return TRUE;
+ case IDCANCEL:
+ nsws_download_clear_data(download1);
+ download1 = NULL;
+ downloading = false;
+ EndDialog(hwnd, IDCANCEL);
+ return FALSE;
+ }
+ }
+ return FALSE;
+}
+
+void nsws_download_update_label(void *p)
+{
+ struct gui_download_window *w = p;
+ if (w->hwnd == NULL) {
+ win32_schedule(-1, nsws_download_update_label, p);
+ return;
+ }
+ HWND sub = GetDlgItem(w->hwnd, IDC_DOWNLOAD_LABEL);
+ char *size = human_friendly_bytesize(w->downloaded);
+ int i = 0, temp = w->time_remaining;
+ if (temp == -1) {
+ w->time_left = strdup(messages_get("UnknownSize"));
+ i = strlen(w->time_left);
+ } else {
+ do {
+ temp = temp / 10;
+ i++;
+ } while (temp > 2);
+ w->time_left = malloc(i + SLEN(" s") + 1);
+ if (w->time_left != NULL) {
+ if (w->time_remaining > 3600)
+ sprintf(w->time_left, "%d h",
+ w->time_remaining / 3600);
+ else if (w->time_remaining > 60)
+ sprintf(w->time_left, "%d m",
+ w->time_remaining / 60);
+ else
+ sprintf(w->time_left, "%d s",
+ w->time_remaining);
+ }
+ }
+ char label[strlen(w->title) + strlen(size) + strlen(w->total_size) +
+ + strlen(w->domain) + strlen(w->filename) +
+ SLEN("download from to \n[\t/\t]\n estimate of time"
+ " remaining ") + i + 1];
+ sprintf(label, "download %s from %s to %s\n[%s\t/\t%s] [%d%%]\n"
+ "estimate of time remaining %s", w->title, w->domain,
+ w->filename, size, w->total_size, w->progress / 100,
+ w->time_left);
+ if (w->time_left != NULL) {
+ free(w->time_left);
+ w->time_left = NULL;
+ }
+ SendMessage(sub, WM_SETTEXT, (WPARAM)0, (LPARAM)label);
+ if (w->progress < 10000) {
+ win32_schedule(500, nsws_download_update_label, p);
+ }
+}
+
+void nsws_download_update_progress(void *p)
+{
+ struct gui_download_window *w = p;
+ if (w->hwnd == NULL) {
+ win32_schedule(-1, nsws_download_update_progress, p);
+ return;
+ }
+ HWND sub = GetDlgItem(w->hwnd, IDC_DOWNLOAD_PROGRESS);
+ SendMessage(sub, PBM_SETPOS, (WPARAM)(w->progress / 100), 0);
+ if (w->progress < 10000) {
+ win32_schedule(500, nsws_download_update_progress, p);
+ }
+}
+
+void nsws_download_clear_data(struct gui_download_window *w)
+{
+ if (w == NULL)
+ return;
+ if (w->title != NULL)
+ free(w->title);
+ if (w->filename != NULL)
+ free(w->filename);
+ if (w->domain != NULL)
+ free(w->domain);
+ if (w->time_left != NULL)
+ free(w->time_left);
+ if (w->total_size != NULL)
+ free(w->total_size);
+ if (w->file != NULL)
+ fclose(w->file);
+ win32_schedule(-1, nsws_download_update_progress, (void *)w);
+ win32_schedule(-1, nsws_download_update_label, (void *)w);
+}
+
+
+static nserror
+gui_download_window_data(struct gui_download_window *w, const char *data,
+ unsigned int size)
+{
+ if ((w == NULL) || (w->file == NULL))
+ return NSERROR_SAVE_FAILED;
+ size_t res;
+ struct timeval val;
+ res = fwrite((void *)data, 1, size, w->file);
+ if (res != size)
+ LOG("file write error %d of %d", size - res, size);
+ w->downloaded += res;
+ w->progress = (unsigned int)(((long long)(w->downloaded) * 10000)
+ / w->size);
+ gettimeofday(&val, NULL);
+ w->time_remaining = (w->progress == 0) ? -1 :
+ (int)((val.tv_sec - w->start_time.tv_sec) *
+ (10000 - w->progress) / w->progress);
+ return NSERROR_OK;
+}
+
+static void gui_download_window_error(struct gui_download_window *w,
+ const char *error_msg)
+{
+ LOG("error %s", error_msg);
+}
+
+static void gui_download_window_done(struct gui_download_window *w)
+{
+ if (w == NULL)
+ return;
+ downloading = false;
+ if (w->hwnd != NULL)
+ EndDialog(w->hwnd, IDOK);
+ nsws_download_clear_data(w);
+}
+
+static struct gui_download_table download_table = {
+ .create = gui_download_window_create,
+ .data = gui_download_window_data,
+ .error = gui_download_window_error,
+ .done = gui_download_window_done,
+};
+
+struct gui_download_table *win32_download_table = &download_table;
+
diff --git a/frontends/windows/download.h b/frontends/windows/download.h
new file mode 100644
index 000000000..2fe3b54c9
--- /dev/null
+++ b/frontends/windows/download.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_WINDOWS_DOWNLOAD_H_
+#define _NETSURF_WINDOWS_DOWNLOAD_H_
+
+extern struct gui_download_table *win32_download_table;
+
+typedef enum {
+ DOWNLOAD_NONE,
+ DOWNLOAD_WORKING,
+ DOWNLOAD_ERROR,
+ DOWNLOAD_COMPLETE,
+ DOWNLOAD_CANCELED
+} download_status;
+
+/**
+ * Initialise the win32 window class for the download window
+ */
+void nsws_download_window_init(struct gui_window *);
+
+#endif
diff --git a/frontends/windows/drawable.c b/frontends/windows/drawable.c
new file mode 100644
index 000000000..4480eeaaa
--- /dev/null
+++ b/frontends/windows/drawable.c
@@ -0,0 +1,625 @@
+/*
+ * Copyright 2011 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+
+#include "utils/config.h"
+
+#include <windows.h>
+#include <windowsx.h>
+
+#include "desktop/browser.h"
+#include "desktop/textinput.h"
+#include "desktop/plotters.h"
+#include "utils/errors.h"
+#include "utils/log.h"
+#include "utils/utils.h"
+
+#include "windows/windbg.h"
+#include "windows/plot.h"
+#include "windows/window.h"
+#include "windows/localhistory.h"
+#include "windows/drawable.h"
+
+static const char windowclassname_drawable[] = "nswsdrawablewindow";
+
+/**
+ * Handle wheel scroll messages.
+ */
+static LRESULT
+nsws_drawable_wheel(struct gui_window *gw, HWND hwnd, WPARAM wparam)
+{
+ int i, z = GET_WHEEL_DELTA_WPARAM(wparam) / WHEEL_DELTA;
+ int key = LOWORD(wparam);
+ DWORD command;
+ unsigned int newmessage = WM_VSCROLL;
+
+ if (key == MK_SHIFT) {
+ command = (z > 0) ? SB_LINERIGHT : SB_LINELEFT;
+ newmessage = WM_HSCROLL;
+ } else {
+ /* add MK_CONTROL -> zoom */
+ command = (z > 0) ? SB_LINEUP : SB_LINEDOWN;
+ }
+
+ z = (z < 0) ? -1 * z : z;
+
+ for (i = 0; i < z; i++) {
+ SendMessage(hwnd, newmessage, MAKELONG(command, 0), 0);
+ }
+
+ return 0;
+}
+
+/**
+ * Handle vertical scroll messages.
+ */
+static LRESULT
+nsws_drawable_vscroll(struct gui_window *gw, HWND hwnd, WPARAM wparam)
+{
+ int width, height;
+ SCROLLINFO si;
+ int mem;
+
+ LOG("VSCROLL %d", gw->requestscrolly);
+
+ if (gw->requestscrolly != 0)
+ return 0;
+
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_ALL;
+ GetScrollInfo(hwnd, SB_VERT, &si);
+ mem = si.nPos;
+
+ switch (LOWORD(wparam)) {
+ case SB_TOP:
+ si.nPos = si.nMin;
+ break;
+
+ case SB_BOTTOM:
+ si.nPos = si.nMax;
+ break;
+
+ case SB_LINEUP:
+ si.nPos -= 30;
+ break;
+
+ case SB_LINEDOWN:
+ si.nPos += 30;
+ break;
+
+ case SB_PAGEUP:
+ si.nPos -= gw->height;
+ break;
+
+ case SB_PAGEDOWN:
+ si.nPos += gw->height;
+ break;
+
+ case SB_THUMBTRACK:
+ si.nPos = si.nTrackPos;
+ break;
+
+ default:
+ break;
+ }
+
+ si.fMask = SIF_POS;
+ if ((gw->bw != NULL) &&
+ (browser_window_get_extents(gw->bw, true,
+ &width, &height) == NSERROR_OK)) {
+ si.nPos = min(si.nPos, height - gw->height);
+ }
+
+ si.nPos = max(si.nPos, 0);
+ SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
+ GetScrollInfo(hwnd, SB_VERT, &si);
+ if (si.nPos != mem) {
+ win32_window_set_scroll(gw, gw->scrollx, gw->scrolly +
+ gw->requestscrolly + si.nPos - mem);
+ }
+
+ return 0;
+}
+
+
+/**
+ * Handle horizontal scroll messages.
+ */
+static LRESULT
+nsws_drawable_hscroll(struct gui_window *gw, HWND hwnd, WPARAM wparam)
+{
+ int width, height;
+ SCROLLINFO si;
+ int mem;
+
+ LOG("HSCROLL %d", gw->requestscrollx);
+
+ if (gw->requestscrollx != 0)
+ return 0;
+
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_ALL;
+ GetScrollInfo(hwnd, SB_HORZ, &si);
+ mem = si.nPos;
+
+ switch (LOWORD(wparam)) {
+ case SB_LINELEFT:
+ si.nPos -= 30;
+ break;
+
+ case SB_LINERIGHT:
+ si.nPos += 30;
+ break;
+
+ case SB_PAGELEFT:
+ si.nPos -= gw->width;
+ break;
+
+ case SB_PAGERIGHT:
+ si.nPos += gw->width;
+ break;
+
+ case SB_THUMBTRACK:
+ si.nPos = si.nTrackPos;
+ break;
+
+ default:
+ break;
+ }
+
+ si.fMask = SIF_POS;
+
+ if ((gw->bw != NULL) &&
+ (browser_window_get_extents(gw->bw, true,
+ &width, &height) == NSERROR_OK)) {
+ si.nPos = min(si.nPos, width - gw->width);
+ }
+ si.nPos = max(si.nPos, 0);
+ SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
+ GetScrollInfo(hwnd, SB_HORZ, &si);
+ if (si.nPos != mem) {
+ win32_window_set_scroll(gw,
+ gw->scrollx + gw->requestscrollx + si.nPos - mem,
+ gw->scrolly);
+ }
+
+ return 0;
+}
+
+/**
+ * Handle resize events.
+ */
+static LRESULT
+nsws_drawable_resize(struct gui_window *gw)
+{
+ browser_window_schedule_reformat(gw->bw);
+ return 0;
+}
+
+/**
+ * Handle key press messages.
+ */
+static LRESULT
+nsws_drawable_key(struct gui_window *gw, HWND hwnd, WPARAM wparam)
+{
+ if (GetFocus() != hwnd)
+ return 0 ;
+
+ uint32_t i;
+ bool shift = ((GetKeyState(VK_SHIFT) & 0x8000) == 0x8000);
+ bool capslock = ((GetKeyState(VK_CAPITAL) & 1) == 1);
+
+ switch(wparam) {
+ case VK_LEFT:
+ i = NS_KEY_LEFT;
+ if (shift)
+ SendMessage(hwnd, WM_HSCROLL,
+ MAKELONG(SB_LINELEFT, 0), 0);
+ break;
+
+ case VK_RIGHT:
+ i = NS_KEY_RIGHT;
+ if (shift)
+ SendMessage(hwnd, WM_HSCROLL,
+ MAKELONG(SB_LINERIGHT, 0), 0);
+ break;
+
+ case VK_UP:
+ i = NS_KEY_UP;
+ if (shift)
+ SendMessage(hwnd, WM_VSCROLL,
+ MAKELONG(SB_LINEUP, 0), 0);
+ break;
+
+ case VK_DOWN:
+ i = NS_KEY_DOWN;
+ if (shift)
+ SendMessage(hwnd, WM_VSCROLL,
+ MAKELONG(SB_LINEDOWN, 0), 0);
+ break;
+
+ case VK_HOME:
+ i = NS_KEY_LINE_START;
+ if (shift)
+ SendMessage(hwnd, WM_HSCROLL,
+ MAKELONG(SB_PAGELEFT, 0), 0);
+ break;
+
+ case VK_END:
+ i = NS_KEY_LINE_END;
+ if (shift)
+ SendMessage(hwnd, WM_HSCROLL,
+ MAKELONG(SB_PAGERIGHT, 0), 0);
+ break;
+
+ case VK_DELETE:
+ i = NS_KEY_DELETE_RIGHT;
+ break;
+
+ case VK_NEXT:
+ i = wparam;
+ SendMessage(hwnd, WM_VSCROLL, MAKELONG(SB_PAGEDOWN, 0),
+ 0);
+ break;
+
+ case VK_PRIOR:
+ i = wparam;
+ SendMessage(hwnd, WM_VSCROLL, MAKELONG(SB_PAGEUP, 0),
+ 0);
+ break;
+
+ default:
+ i = wparam;
+ break;
+ }
+
+ if ((i >= 'A') &&
+ (i <= 'Z') &&
+ (((!capslock) && (!shift)) || ((capslock) && (shift)))) {
+ i += 'a' - 'A';
+ }
+
+ if (gw != NULL)
+ browser_window_key_press(gw->bw, i);
+
+ return 0;
+}
+
+
+/**
+ * Handle paint messages.
+ */
+static LRESULT
+nsws_drawable_paint(struct gui_window *gw, HWND hwnd)
+{
+ struct rect clip;
+ PAINTSTRUCT ps;
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &win_plotters
+ };
+
+ BeginPaint(hwnd, &ps);
+
+ if (gw != NULL) {
+ plot_hdc = ps.hdc;
+
+ clip.x0 = ps.rcPaint.left;
+ clip.y0 = ps.rcPaint.top;
+ clip.x1 = ps.rcPaint.right;
+ clip.y1 = ps.rcPaint.bottom;
+
+ browser_window_redraw(gw->bw,
+ -gw->scrollx / gw->scale,
+ -gw->scrolly / gw->scale,
+ &clip, &ctx);
+ }
+
+ EndPaint(hwnd, &ps);
+
+ return 0;
+}
+
+
+/**
+ * Handle mouse button up messages.
+ */
+static LRESULT
+nsws_drawable_mouseup(struct gui_window *gw,
+ int x,
+ int y,
+ browser_mouse_state press,
+ browser_mouse_state click)
+{
+ bool shift = ((GetKeyState(VK_SHIFT) & 0x8000) == 0x8000);
+ bool ctrl = ((GetKeyState(VK_CONTROL) & 0x8000) == 0x8000);
+ bool alt = ((GetKeyState(VK_MENU) & 0x8000) == 0x8000);
+
+ if ((gw == NULL) ||
+ (gw->mouse == NULL) ||
+ (gw->bw == NULL))
+ return 0;
+
+ LOG("state 0x%x, press 0x%x", gw->mouse->state, press);
+ if ((gw->mouse->state & press) != 0) {
+ gw->mouse->state &= ~press;
+ gw->mouse->state |= click;
+ }
+
+ if (((gw->mouse->state & BROWSER_MOUSE_MOD_1) != 0) && !shift)
+ gw->mouse->state &= ~BROWSER_MOUSE_MOD_1;
+ if (((gw->mouse->state & BROWSER_MOUSE_MOD_2) != 0) && !ctrl)
+ gw->mouse->state &= ~BROWSER_MOUSE_MOD_2;
+ if (((gw->mouse->state & BROWSER_MOUSE_MOD_3) != 0) && !alt)
+ gw->mouse->state &= ~BROWSER_MOUSE_MOD_3;
+
+ if ((gw->mouse->state & click) != 0) {
+ LOG("mouse click bw %p, state 0x%x, x %f, y %f", gw->bw, gw->mouse->state, (x + gw->scrollx) / gw->scale, (y + gw->scrolly) / gw->scale);
+
+ browser_window_mouse_click(gw->bw,
+ gw->mouse->state,
+ (x + gw->scrollx) / gw->scale,
+ (y + gw->scrolly) / gw->scale);
+ } else {
+ browser_window_mouse_track(gw->bw,
+ 0,
+ (x + gw->scrollx) / gw->scale,
+ (y + gw->scrolly) / gw->scale);
+ }
+
+ gw->mouse->state = 0;
+ return 0;
+}
+
+
+/**
+ * Handle mouse button down messages.
+ */
+static LRESULT
+nsws_drawable_mousedown(struct gui_window *gw,
+ int x, int y,
+ browser_mouse_state button)
+{
+ if ((gw == NULL) ||
+ (gw->mouse == NULL) ||
+ (gw->bw == NULL)) {
+ nsws_localhistory_close(gw);
+ return 0;
+ }
+
+ gw->mouse->state = button;
+ if ((GetKeyState(VK_SHIFT) & 0x8000) == 0x8000)
+ gw->mouse->state |= BROWSER_MOUSE_MOD_1;
+ if ((GetKeyState(VK_CONTROL) & 0x8000) == 0x8000)
+ gw->mouse->state |= BROWSER_MOUSE_MOD_2;
+ if ((GetKeyState(VK_MENU) & 0x8000) == 0x8000)
+ gw->mouse->state |= BROWSER_MOUSE_MOD_3;
+
+ gw->mouse->pressed_x = (x + gw->scrollx) / gw->scale;
+ gw->mouse->pressed_y = (y + gw->scrolly) / gw->scale;
+
+ LOG("mouse click bw %p, state %x, x %f, y %f", gw->bw, gw->mouse->state, (x + gw->scrollx) / gw->scale, (y + gw->scrolly) / gw->scale);
+
+ browser_window_mouse_click(gw->bw, gw->mouse->state,
+ (x + gw->scrollx) / gw->scale,
+ (y + gw->scrolly) / gw->scale);
+
+ return 0;
+}
+
+/**
+ * Handle mouse movement messages.
+ */
+static LRESULT
+nsws_drawable_mousemove(struct gui_window *gw, int x, int y)
+{
+ bool shift = ((GetKeyState(VK_SHIFT) & 0x8000) == 0x8000);
+ bool ctrl = ((GetKeyState(VK_CONTROL) & 0x8000) == 0x8000);
+ bool alt = ((GetKeyState(VK_MENU) & 0x8000) == 0x8000);
+
+ if ((gw == NULL) || (gw->mouse == NULL) || (gw->bw == NULL))
+ return 0;
+
+ /* scale co-ordinates */
+ x = (x + gw->scrollx) / gw->scale;
+ y = (y + gw->scrolly) / gw->scale;
+
+ /* if mouse button held down and pointer moved more than
+ * minimum distance drag is happening */
+ if (((gw->mouse->state & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2)) != 0) &&
+ (abs(x - gw->mouse->pressed_x) >= 5) &&
+ (abs(y - gw->mouse->pressed_y) >= 5)) {
+
+ LOG("Drag start state 0x%x", gw->mouse->state);
+
+ if ((gw->mouse->state & BROWSER_MOUSE_PRESS_1) != 0) {
+ browser_window_mouse_click(gw->bw, BROWSER_MOUSE_DRAG_1,
+ gw->mouse->pressed_x,
+ gw->mouse->pressed_y);
+ gw->mouse->state &= ~BROWSER_MOUSE_PRESS_1;
+ gw->mouse->state |= BROWSER_MOUSE_HOLDING_1 |
+ BROWSER_MOUSE_DRAG_ON;
+ }
+ else if ((gw->mouse->state & BROWSER_MOUSE_PRESS_2) != 0) {
+ browser_window_mouse_click(gw->bw, BROWSER_MOUSE_DRAG_2,
+ gw->mouse->pressed_x,
+ gw->mouse->pressed_y);
+ gw->mouse->state &= ~BROWSER_MOUSE_PRESS_2;
+ gw->mouse->state |= BROWSER_MOUSE_HOLDING_2 |
+ BROWSER_MOUSE_DRAG_ON;
+ }
+ }
+
+ if (((gw->mouse->state & BROWSER_MOUSE_MOD_1) != 0) && !shift)
+ gw->mouse->state &= ~BROWSER_MOUSE_MOD_1;
+ if (((gw->mouse->state & BROWSER_MOUSE_MOD_2) != 0) && !ctrl)
+ gw->mouse->state &= ~BROWSER_MOUSE_MOD_2;
+ if (((gw->mouse->state & BROWSER_MOUSE_MOD_3) != 0) && !alt)
+ gw->mouse->state &= ~BROWSER_MOUSE_MOD_3;
+
+
+ browser_window_mouse_track(gw->bw, gw->mouse->state, x, y);
+
+ return 0;
+}
+
+/**
+ * Called when activity occours within the drawable window.
+ */
+static LRESULT CALLBACK
+nsws_window_drawable_event_callback(HWND hwnd,
+ UINT msg,
+ WPARAM wparam,
+ LPARAM lparam)
+{
+ struct gui_window *gw;
+
+ LOG_WIN_MSG(hwnd, msg, wparam, lparam);
+
+ gw = nsws_get_gui_window(hwnd);
+ if (gw == NULL) {
+ LOG("Unable to find gui window structure for hwnd %p", hwnd);
+ return DefWindowProc(hwnd, msg, wparam, lparam);
+ }
+
+ switch(msg) {
+
+ case WM_MOUSEMOVE:
+ return nsws_drawable_mousemove(gw,
+ GET_X_LPARAM(lparam),
+ GET_Y_LPARAM(lparam));
+
+ case WM_LBUTTONDOWN:
+ nsws_drawable_mousedown(gw,
+ GET_X_LPARAM(lparam),
+ GET_Y_LPARAM(lparam),
+ BROWSER_MOUSE_PRESS_1);
+ SetFocus(hwnd);
+ nsws_localhistory_close(gw);
+ return 0;
+ break;
+
+ case WM_RBUTTONDOWN:
+ nsws_drawable_mousedown(gw,
+ GET_X_LPARAM(lparam),
+ GET_Y_LPARAM(lparam),
+ BROWSER_MOUSE_PRESS_2);
+ SetFocus(hwnd);
+ return 0;
+ break;
+
+ case WM_LBUTTONUP:
+ return nsws_drawable_mouseup(gw,
+ GET_X_LPARAM(lparam),
+ GET_Y_LPARAM(lparam),
+ BROWSER_MOUSE_PRESS_1,
+ BROWSER_MOUSE_CLICK_1);
+
+ case WM_RBUTTONUP:
+ return nsws_drawable_mouseup(gw,
+ GET_X_LPARAM(lparam),
+ GET_Y_LPARAM(lparam),
+ BROWSER_MOUSE_PRESS_2,
+ BROWSER_MOUSE_CLICK_2);
+
+ case WM_ERASEBKGND: /* ignore as drawable window is redrawn on paint */
+ return 0;
+
+ case WM_PAINT: /* redraw the exposed part of the window */
+ return nsws_drawable_paint(gw, hwnd);
+
+ case WM_KEYDOWN:
+ return nsws_drawable_key(gw, hwnd, wparam);
+
+ case WM_SIZE:
+ return nsws_drawable_resize(gw);
+
+ case WM_HSCROLL:
+ return nsws_drawable_hscroll(gw, hwnd, wparam);
+
+ case WM_VSCROLL:
+ return nsws_drawable_vscroll(gw, hwnd, wparam);
+
+ case WM_MOUSEWHEEL:
+ return nsws_drawable_wheel(gw, hwnd, wparam);
+
+ }
+ return DefWindowProc(hwnd, msg, wparam, lparam);
+}
+
+/**
+ * Create a drawable window.
+ */
+HWND
+nsws_window_create_drawable(HINSTANCE hinstance,
+ HWND hparent,
+ struct gui_window *gw)
+{
+ HWND hwnd;
+ hwnd = CreateWindow(windowclassname_drawable,
+ NULL,
+ WS_VISIBLE | WS_CHILD,
+ 0, 0, 0, 0,
+ hparent,
+ NULL,
+ hinstance,
+ NULL);
+
+ if (hwnd == NULL) {
+ win_perror("WindowCreateDrawable");
+ LOG("Window creation failed");
+ return NULL;
+ }
+
+ /* set the gui window associated with this toolbar */
+ SetProp(hwnd, TEXT("GuiWnd"), (HANDLE)gw);
+
+ return hwnd;
+}
+
+/**
+ * Create the drawable window class.
+ */
+nserror
+nsws_create_drawable_class(HINSTANCE hinstance) {
+ nserror ret = NSERROR_OK;
+ WNDCLASSEX w;
+
+ /* drawable area */
+ w.cbSize = sizeof(WNDCLASSEX);
+ w.style = 0;
+ w.lpfnWndProc = nsws_window_drawable_event_callback;
+ w.cbClsExtra = 0;
+ w.cbWndExtra = 0;
+ w.hInstance = hinstance;
+ w.hIcon = NULL;
+ w.hCursor = NULL;
+ w.hbrBackground = (HBRUSH)(COLOR_MENU + 1);
+ w.lpszMenuName = NULL;
+ w.lpszClassName = windowclassname_drawable;
+ w.hIconSm = NULL;
+
+ if (RegisterClassEx(&w) == 0) {
+ win_perror("DrawableClass");
+ ret = NSERROR_INIT_FAILED;
+ }
+
+ return ret;
+}
diff --git a/frontends/windows/drawable.h b/frontends/windows/drawable.h
new file mode 100644
index 000000000..e770f94b2
--- /dev/null
+++ b/frontends/windows/drawable.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2011 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_WINDOWS_DRAWABLE_H_
+#define _NETSURF_WINDOWS_DRAWABLE_H_
+
+nserror nsws_create_drawable_class(HINSTANCE hinstance);
+HWND nsws_window_create_drawable(HINSTANCE hinstance, HWND hparent, struct gui_window *gw);
+
+#endif /* _NETSURF_WINDOWS_DRAWABLE_H_ */
diff --git a/frontends/windows/file.c b/frontends/windows/file.c
new file mode 100644
index 000000000..e9eb9caf9
--- /dev/null
+++ b/frontends/windows/file.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright 2014, 2015 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * Windows file operation table implementation.
+ */
+
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <windows.h>
+
+#include "utils/errors.h"
+#include "utils/nsurl.h"
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "utils/corestrings.h"
+#include "utils/url.h"
+#include "utils/file.h"
+#include "utils/string.h"
+#include "desktop/browser.h"
+
+#include "windows/file.h"
+
+/**
+ * Generate a windows path from one or more component elemnts.
+ *
+ * If a string is allocated it must be freed by the caller.
+ *
+ * @param[in,out] str pointer to string pointer if this is NULL enough
+ * storage will be allocated for the complete path.
+ * @param[in,out] size The size of the space available if \a str not
+ * NULL on input and if not NULL set to the total
+ * output length on output.
+ * @param[in] nelm The number of elements.
+ * @param[in] ap The elements of the path as string pointers.
+ * @return NSERROR_OK and the complete path is written to str
+ * or error code on faliure.
+ */
+static nserror windows_mkpath(char **str, size_t *size, size_t nelm, va_list ap)
+{
+ return vsnstrjoin(str, size, '\\', nelm, ap);
+}
+
+
+/**
+ * Get the basename of a file using windows path handling.
+ *
+ * This gets the last element of a path and returns it.
+ *
+ * @param[in] path The path to extract the name from.
+ * @param[in,out] str Pointer to string pointer if this is NULL enough
+ * storage will be allocated for the path element.
+ * @param[in,out] size The size of the space available if \a
+ * str not NULL on input and set to the total
+ * output length on output.
+ * @return NSERROR_OK and the complete path is written to str
+ * or error code on faliure.
+ */
+static nserror windows_basename(const char *path, char **str, size_t *size)
+{
+ const char *leafname;
+ char *fname;
+
+ if (path == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ leafname = strrchr(path, '\\');
+ if (!leafname) {
+ leafname = path;
+ } else {
+ leafname += 1;
+ }
+
+ fname = strdup(leafname);
+ if (fname == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ *str = fname;
+ if (size != NULL) {
+ *size = strlen(fname);
+ }
+ return NSERROR_OK;
+}
+
+
+/**
+ * Create a path from a nsurl using windows file handling.
+ *
+ * @param[in] url The url to encode.
+ * @param[out] path_out A string containing the result path which should
+ * be freed by the caller.
+ * @return NSERROR_OK and the path is written to \a path or error code
+ * on faliure.
+ */
+static nserror windows_nsurl_to_path(struct nsurl *url, char **path_out)
+{
+ lwc_string *urlpath;
+ char *path;
+ bool match;
+ lwc_string *scheme;
+ nserror res;
+
+ if ((url == NULL) || (path_out == NULL)) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ scheme = nsurl_get_component(url, NSURL_SCHEME);
+
+ if (lwc_string_caseless_isequal(scheme, corestring_lwc_file,
+ &match) != lwc_error_ok)
+ {
+ return NSERROR_BAD_PARAMETER;
+ }
+ lwc_string_unref(scheme);
+ if (match == false) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ urlpath = nsurl_get_component(url, NSURL_PATH);
+ if (urlpath == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ res = url_unescape(lwc_string_data(urlpath), &path);
+ lwc_string_unref(urlpath);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ /* if there is a drive: prefix treat path as DOS filename */
+ if ((path[2] == ':') || (path[2] == '|')) {
+ char *sidx; /* slash index */
+
+ /* move the string down to remove leading / note the
+ * strlen is *not* copying too much data as we are
+ * moving the null too!
+ */
+ memmove(path, path + 1, strlen(path));
+
+ /* swap / for \ */
+ sidx = strrchr(path, '/');
+ while (sidx != NULL) {
+ *sidx = '\\';
+ sidx = strrchr(path, '/');
+ }
+ }
+ /* if the path does not have a drive letter we return the
+ * complete path.
+ */
+ /** @todo Need to check returning the unaltered path in this
+ * case is correct
+ */
+
+ *path_out = path;
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Create a nsurl from a path using windows file handling.
+ *
+ * Perform the necessary operations on a path to generate a nsurl.
+ *
+ * @param[in] path The path to convert.
+ * @param[out] url_out pointer to recive the nsurl, The returned url
+ * should be unreferenced by the caller.
+ * @return NSERROR_OK and the url is placed in \a url or error code on
+ * faliure.
+ */
+static nserror windows_path_to_nsurl(const char *path, struct nsurl **url_out)
+{
+ nserror ret;
+ int urllen;
+ char *urlstr;
+ char *sidx; /* slash index */
+
+ if ((path == NULL) || (url_out == NULL) || (*path == 0)) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ /* build url as a string for nsurl constructor */
+ urllen = strlen(path) + FILE_SCHEME_PREFIX_LEN + 5;
+ urlstr = malloc(urllen);
+ if (urlstr == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ /** @todo check if this should be url escaping the path. */
+ if (*path == '/') {
+ /* unix style path start, so try wine Z: */
+ snprintf(urlstr, urllen, "%sZ%%3A%s", FILE_SCHEME_PREFIX, path);
+ } else {
+ snprintf(urlstr, urllen, "%s%s", FILE_SCHEME_PREFIX, path);
+ }
+
+ sidx = strrchr(urlstr, '\\');
+ while (sidx != NULL) {
+ *sidx = '/';
+ sidx = strrchr(urlstr, '\\');
+ }
+
+ ret = nsurl_create(urlstr, url_out);
+ free(urlstr);
+
+ return ret;
+}
+
+
+/**
+ * Ensure that all directory elements needed to store a filename exist.
+ *
+ * @param fname The filename to ensure the path to exists.
+ * @return NSERROR_OK on success or error code on failure.
+ */
+static nserror windows_mkdir_all(const char *fname)
+{
+ char *dname;
+ char *sep;
+ struct stat sb;
+
+ dname = strdup(fname);
+
+ sep = strrchr(dname, '\\');
+ if (sep == NULL) {
+ /* no directory separator path is just filename so its ok */
+ free(dname);
+ return NSERROR_OK;
+ }
+
+ *sep = 0; /* null terminate directory path */
+
+ if (stat(dname, &sb) == 0) {
+ free(dname);
+ if (S_ISDIR(sb.st_mode)) {
+ /* path to file exists and is a directory */
+ return NSERROR_OK;
+ }
+ return NSERROR_NOT_DIRECTORY;
+ }
+ *sep = '\\'; /* restore separator */
+
+ sep = dname;
+ while (*sep == '\\') {
+ sep++;
+ }
+ while ((sep = strchr(sep, '\\')) != NULL) {
+ *sep = 0;
+ if (stat(dname, &sb) != 0) {
+ if (nsmkdir(dname, S_IRWXU) != 0) {
+ /* could not create path element */
+ free(dname);
+ return NSERROR_NOT_FOUND;
+ }
+ } else {
+ if (! S_ISDIR(sb.st_mode)) {
+ /* path element not a directory */
+ free(dname);
+ return NSERROR_NOT_DIRECTORY;
+ }
+ }
+ *sep = '\\'; /* restore separator */
+ /* skip directory separators */
+ while (*sep == '\\') {
+ sep++;
+ }
+ }
+
+ free(dname);
+ return NSERROR_OK;
+}
+
+/* windows file handling */
+static struct gui_file_table file_table = {
+ .mkpath = windows_mkpath,
+ .basename = windows_basename,
+ .nsurl_to_path = windows_nsurl_to_path,
+ .path_to_nsurl = windows_path_to_nsurl,
+ .mkdir_all = windows_mkdir_all,
+};
+
+struct gui_file_table *win32_file_table = &file_table;
diff --git a/frontends/windows/file.h b/frontends/windows/file.h
new file mode 100644
index 000000000..5262dde2c
--- /dev/null
+++ b/frontends/windows/file.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2015 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * Windows file operation table interface.
+ */
+
+#ifndef _NETSURF_WINDOWS_FILE_H_
+#define _NETSURF_WINDOWS_FILE_H_
+
+struct gui_file_table *win32_file_table;
+
+#endif
diff --git a/frontends/windows/filetype.c b/frontends/windows/filetype.c
new file mode 100644
index 000000000..b06534d6a
--- /dev/null
+++ b/frontends/windows/filetype.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2003 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <string.h>
+
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "content/fetch.h"
+#include "desktop/gui_fetch.h"
+
+#include "windows/filetype.h"
+
+/**
+ * filetype -- determine the MIME type of a local file
+ */
+static const char *fetch_filetype(const char *unix_path)
+{
+ int l;
+ LOG("unix path %s", unix_path);
+ l = strlen(unix_path);
+ if (2 < l && strcasecmp(unix_path + l - 3, "css") == 0)
+ return "text/css";
+ if (2 < l && strcasecmp(unix_path + l - 3, "jpg") == 0)
+ return "image/jpeg";
+ if (3 < l && strcasecmp(unix_path + l - 4, "jpeg") == 0)
+ return "image/jpeg";
+ if (2 < l && strcasecmp(unix_path + l - 3, "gif") == 0)
+ return "image/gif";
+ if (2 < l && strcasecmp(unix_path + l - 3, "png") == 0)
+ return "image/png";
+ if (2 < l && strcasecmp(unix_path + l - 3, "jng") == 0)
+ return "image/jng";
+ if (2 < l && strcasecmp(unix_path + l - 3, "svg") == 0)
+ return "image/svg";
+ if (2 < l && strcasecmp(unix_path + l - 3, "bmp") == 0)
+ return "image/x-ms-bmp";
+ return "text/html";
+}
+
+static struct gui_fetch_table fetch_table = {
+ .filetype = fetch_filetype,
+};
+
+struct gui_fetch_table *win32_fetch_table = &fetch_table;
diff --git a/frontends/windows/filetype.h b/frontends/windows/filetype.h
new file mode 100644
index 000000000..f71a0b2da
--- /dev/null
+++ b/frontends/windows/filetype.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_WINDOWS_FILETYPE_H_
+#define _NETSURF_WINDOWS_FILETYPE_H_
+
+struct gui_fetch_table *win32_fetch_table;
+
+#endif
diff --git a/frontends/windows/findfile.c b/frontends/windows/findfile.c
new file mode 100644
index 000000000..8c8906a80
--- /dev/null
+++ b/frontends/windows/findfile.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2008 Daniel Silverstone <dsilvers@netsurf-browser.org>
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <winsock2.h>
+#include <windows.h>
+
+#include <limits.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#include <curl/curl.h>
+
+#include "utils/log.h"
+#include "utils/url.h"
+#include "utils/utils.h"
+#include "utils/filepath.h"
+
+#include "windows/findfile.h"
+
+/** Create an array of valid paths to search for resources.
+ *
+ * The idea is that all the complex path computation to find resources
+ * is performed here, once, rather than every time a resource is
+ * searched for.
+ */
+char **
+nsws_init_resource(const char *resource_path)
+{
+ char **pathv; /* resource path string vector */
+ char **respath; /* resource paths vector */
+ const char *lang = NULL;
+ char *winpath;
+ int pathi;
+ char *slsh;
+
+ pathv = filepath_path_to_strvec(resource_path);
+ if (pathv == NULL)
+ return NULL;
+
+ winpath = malloc(MAX_PATH);
+ GetModuleFileName(NULL, winpath, MAX_PATH);
+ slsh = strrchr(winpath, '\\');
+ if (slsh != NULL)
+ *slsh=0;
+ strncat(winpath, "\\windows\\res", MAX_PATH);
+
+ pathi = 0;
+ while (pathv[pathi] != NULL)
+ pathi++;
+ pathv[pathi] = winpath;
+
+ respath = filepath_generate(pathv, &lang);
+
+ filepath_free_strvec(pathv);
+
+ return respath;
+}
+
+static char *realpath(const char *path, char *resolved_path)
+{
+ /* useless, but there we go */
+ return strncpy(resolved_path, path, PATH_MAX);
+}
+
+
+/**
+ * Locate a shared resource file by searching known places in order.
+ *
+ * Search order is: ~/.netsurf/, $NETSURFRES/ (where NETSURFRES is an
+ * environment variable), then the path specified in
+ * NETSURF_WINDOWS_RESPATH in the Makefile then .\\res\\ [windows paths]
+ *
+ * \param buf buffer to write to. must be at least PATH_MAX chars
+ * \param filename file to look for
+ * \param def default to return if file not found
+ * \return The passed in buffer
+ */
+
+char *nsws_find_resource(char *buf, const char *filename, const char *def)
+{
+ char *cdir = getenv("HOME");
+ char t[PATH_MAX];
+
+ if (cdir != NULL) {
+ LOG("Found Home %s", cdir);
+ strcpy(t, cdir);
+ strcat(t, "/.netsurf/");
+ strcat(t, filename);
+ if ((realpath(t, buf) != NULL) && (access(buf, R_OK) == 0))
+ return buf;
+ }
+
+ cdir = getenv("NETSURFRES");
+
+ if (cdir != NULL) {
+ if (realpath(cdir , buf) != NULL) {
+ strcat(buf, "/");
+ strcat(buf, filename);
+ if (access(buf, R_OK) == 0)
+ return buf;
+ }
+ }
+
+ strcpy(t, NETSURF_WINDOWS_RESPATH);
+ strcat(t, filename);
+ if ((realpath(t, buf) != NULL) && (access(buf, R_OK) == 0))
+ return buf;
+
+ getcwd(t, PATH_MAX - SLEN("\\res\\") - strlen(filename));
+ strcat(t, "\\res\\");
+ strcat(t, filename);
+ LOG("looking in %s", t);
+ if ((realpath(t, buf) != NULL) && (access(buf, R_OK) == 0))
+ return buf;
+
+ if (def[0] == '~') {
+ snprintf(t, PATH_MAX, "%s%s", getenv("HOME"), def + 1);
+ if (realpath(t, buf) == NULL) {
+ strcpy(buf, t);
+ }
+ } else {
+ if (realpath(def, buf) == NULL) {
+ strcpy(buf, def);
+ }
+ }
+
+ return buf;
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 8
+ * End:
+ */
+
diff --git a/frontends/windows/findfile.h b/frontends/windows/findfile.h
new file mode 100644
index 000000000..808adc8ef
--- /dev/null
+++ b/frontends/windows/findfile.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2008 Daniel Silverstone <dsilvers@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_WINDOWS_FINDFILE_H_
+#define _NETSURF_WINDOWS_FINDFILE_H_
+
+extern char *nsws_find_resource(char *buf, const char *filename, const char *def);
+
+char **nsws_init_resource(const char *resource_path);
+
+#endif /* _NETSURF_WINDOWS_FINDFILE_H_ */
diff --git a/frontends/windows/font.c b/frontends/windows/font.c
new file mode 100644
index 000000000..f67205a6f
--- /dev/null
+++ b/frontends/windows/font.c
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2009 - 2014 Vincent Sanders <vince@netsurf-browser.org>
+ * Copyright 2009 - 2013 Michael Drake <tlsa@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * Windows font handling implementation.
+ */
+
+#include "utils/config.h"
+#include <inttypes.h>
+#include <assert.h>
+#include <windows.h>
+
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "utils/utf8.h"
+#include "desktop/gui_layout.h"
+#include "desktop/gui_utf8.h"
+
+#include "windows/font.h"
+
+HWND font_hwnd;
+
+nserror utf8_to_font_encoding(const struct font_desc* font,
+ const char *string,
+ size_t len,
+ char **result)
+{
+ return utf8_to_enc(string, font->encoding, len, result);
+}
+
+static nserror utf8_to_local_encoding(const char *string,
+ size_t len,
+ char **result)
+{
+ return utf8_to_enc(string, "UCS-2", len, result);
+}
+
+static nserror utf8_from_local_encoding(const char *string, size_t len,
+ char **result)
+{
+ assert(string && result);
+
+ if (len == 0)
+ len = strlen(string);
+
+ *result = strndup(string, len);
+ if (!(*result))
+ return NSERROR_NOMEM;
+
+ return NSERROR_OK;
+}
+
+HFONT get_font(const plot_font_style_t *style)
+{
+ char *face = NULL;
+ DWORD family;
+ switch(style->family) {
+ case PLOT_FONT_FAMILY_SERIF:
+ face = strdup(nsoption_charp(font_serif));
+ family = FF_ROMAN | DEFAULT_PITCH;
+ break;
+ case PLOT_FONT_FAMILY_MONOSPACE:
+ face = strdup(nsoption_charp(font_mono));
+ family = FF_MODERN | DEFAULT_PITCH;
+ break;
+ case PLOT_FONT_FAMILY_CURSIVE:
+ face = strdup(nsoption_charp(font_cursive));
+ family = FF_SCRIPT | DEFAULT_PITCH;
+ break;
+ case PLOT_FONT_FAMILY_FANTASY:
+ face = strdup(nsoption_charp(font_fantasy));
+ family = FF_DECORATIVE | DEFAULT_PITCH;
+ break;
+ case PLOT_FONT_FAMILY_SANS_SERIF:
+ default:
+ face = strdup(nsoption_charp(font_sans));
+ family = FF_SWISS | DEFAULT_PITCH;
+ break;
+ }
+
+ int nHeight = -10;
+
+ HDC hdc = GetDC(font_hwnd);
+ nHeight = -MulDiv(style->size, GetDeviceCaps(hdc, LOGPIXELSY), 72 * FONT_SIZE_SCALE);
+ ReleaseDC(font_hwnd, hdc);
+
+ HFONT font = CreateFont(
+ nHeight, /* height */
+ 0, /* width */
+ 0, /* escapement*/
+ 0, /* orientation */
+ style->weight,
+ (style->flags & FONTF_ITALIC) ? TRUE : FALSE,
+ FALSE, /* underline */
+ FALSE, /* strike */
+ DEFAULT_CHARSET, /* for locale */
+ OUT_DEFAULT_PRECIS, /* general 'best match' */
+ CLIP_DEFAULT_PRECIS,
+ DEFAULT_QUALITY,
+ family,
+ face /* name of font face */
+ );
+ if (face != NULL)
+ free(face);
+
+ if (font == NULL) {
+ if (style->family == PLOT_FONT_FAMILY_MONOSPACE)
+ font = (HFONT) GetStockObject(ANSI_FIXED_FONT);
+ else
+ font = (HFONT) GetStockObject(ANSI_VAR_FONT);
+ }
+ if (font == NULL)
+ font = (HFONT) GetStockObject(SYSTEM_FONT);
+ return font;
+}
+
+/**
+ * Measure the width of a string.
+ *
+ * \param[in] style plot style for this text
+ * \param[in] string UTF-8 string to measure
+ * \param[in] length length of string, in bytes
+ * \param[out] width updated to width of string[0..length)
+ * \return true on success and width updated else false
+ */
+static nserror
+win32_font_width(const plot_font_style_t *style,
+ const char *string,
+ size_t length,
+ int *width)
+{
+ HDC hdc;
+ HFONT font;
+ HFONT fontbak;
+ SIZE s;
+ bool ret = true;
+
+ if (length == 0) {
+ *width = 0;
+ } else {
+ hdc = GetDC(NULL);
+ font = get_font(style);
+ fontbak = SelectObject(hdc, font);
+
+ /* may well need to convert utf-8 to lpctstr */
+ if (GetTextExtentPoint32A(hdc, string, length, &s) != 0) {
+ *width = s.cx;
+ } else {
+ ret = false;
+ }
+ font = SelectObject(hdc, fontbak);
+ DeleteObject(font);
+ ReleaseDC(NULL, hdc);
+ }
+ return ret;
+}
+
+
+/**
+ * Find the position in a string where an x coordinate falls.
+ *
+ * \param style css_style for this text, with style->font_size.size ==
+ * CSS_FONT_SIZE_LENGTH
+ * \param string UTF-8 string to measure
+ * \param length length of string
+ * \param x x coordinate to search for
+ * \param char_offset updated to offset in string of actual_x, [0..length]
+ * \param actual_x updated to x coordinate of character closest to x
+ * \return true on success, false on error and error reported
+ */
+static nserror
+win32_font_position(const plot_font_style_t *style,
+ const char *string,
+ size_t length,
+ int x,
+ size_t *char_offset,
+ int *actual_x)
+{
+ HDC hdc;
+ HFONT font;
+ HFONT fontbak;
+ SIZE s;
+ int offset;
+ bool ret = true;
+
+ if ((length == 0) || (x < 1)) {
+ *char_offset = 0;
+ *actual_x = 0;
+ } else {
+ hdc = GetDC(NULL);
+ font = get_font(style);
+ fontbak = SelectObject(hdc, font);
+
+ if ((GetTextExtentExPointA(hdc, string, length, x, &offset, NULL,&s) != 0) &&
+ (GetTextExtentPoint32A(hdc, string, offset, &s) != 0)) {
+ *char_offset = (size_t)offset;
+ *actual_x = s.cx;
+ } else {
+ ret = false;
+ }
+ font = SelectObject(hdc, fontbak);
+ DeleteObject(font);
+ ReleaseDC(NULL, hdc);
+ }
+
+ return ret;
+}
+
+
+/**
+ * Find where to split a string to make it fit a width.
+ *
+ * \param style css_style for this text, with style->font_size.size ==
+ * CSS_FONT_SIZE_LENGTH
+ * \param string UTF-8 string to measure
+ * \param length length of string
+ * \param x width available
+ * \param char_offset updated to offset in string of actual_x, [0..length]
+ * \param actual_x updated to x coordinate of character closest to x
+ * \return true on success, false on error and error reported
+ *
+ * On exit, [char_offset == 0 ||
+ * string[char_offset] == ' ' ||
+ * char_offset == length]
+ */
+static nserror
+win32_font_split(const plot_font_style_t *style,
+ const char *string,
+ size_t length,
+ int x,
+ size_t *char_offset,
+ int *actual_x)
+{
+ int c_off;
+ bool ret = false;
+
+ if (win32_font_position(style, string, length, x, char_offset, actual_x)) {
+ c_off = *char_offset;
+ if (*char_offset == length) {
+ ret = true;
+ } else {
+ while ((string[*char_offset] != ' ') &&
+ (*char_offset > 0)) {
+ (*char_offset)--;
+ }
+
+ if (*char_offset == 0) {
+ *char_offset = c_off;
+ while ((*char_offset < length) &&
+ (string[*char_offset] != ' ')) {
+ (*char_offset)++;
+ }
+ }
+
+ ret = win32_font_width(style, string, *char_offset, actual_x);
+ }
+ }
+
+/*
+ LOG("ret %d Split %u chars at %ipx: Split at char %i (%ipx) - %.*s",
+ ret, length, x, *char_offset, *actual_x, *char_offset, string);
+*/
+ return ret;
+}
+
+
+static struct gui_layout_table layout_table = {
+ .width = win32_font_width,
+ .position = win32_font_position,
+ .split = win32_font_split,
+};
+
+struct gui_layout_table *win32_layout_table = &layout_table;
+
+
+static struct gui_utf8_table utf8_table = {
+ .utf8_to_local = utf8_to_local_encoding,
+ .local_to_utf8 = utf8_from_local_encoding,
+};
+
+struct gui_utf8_table *win32_utf8_table = &utf8_table;
diff --git a/frontends/windows/font.h b/frontends/windows/font.h
new file mode 100644
index 000000000..f2128afc5
--- /dev/null
+++ b/frontends/windows/font.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * The interface to the win32 font and utf8 handling.
+ */
+
+#ifndef _NETSURF_WINDOWS_FONT_H_
+#define _NETSURF_WINDOWS_FONT_H_
+
+extern HWND font_hwnd;
+
+struct font_desc {
+ const char *name;
+ int width, height;
+ const char *encoding;
+};
+
+struct gui_layout_table *win32_layout_table;
+struct gui_utf8_table *win32_utf8_table;
+
+extern nserror utf8_to_font_encoding(const struct font_desc* font,
+ const char *string,
+ size_t len,
+ char **result);
+
+/**
+ * generate a win32 font handle from a generic font style
+ *
+ * \param style The font style.
+ * \return The win32 font handle
+ */
+HFONT get_font(const plot_font_style_t *style);
+
+#endif /* NETSURF_WINDOWS_FONT_H */
+
diff --git a/frontends/windows/gui.c b/frontends/windows/gui.c
new file mode 100644
index 000000000..9923ecfaa
--- /dev/null
+++ b/frontends/windows/gui.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <windows.h>
+
+#include "utils/errors.h"
+#include "utils/nsurl.h"
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "utils/corestrings.h"
+#include "utils/url.h"
+#include "utils/file.h"
+#include "utils/messages.h"
+#include "desktop/browser.h"
+#include "desktop/gui_clipboard.h"
+
+#include "windows/schedule.h"
+#include "windows/window.h"
+#include "windows/filetype.h"
+#include "windows/gui.h"
+
+static bool win32_quit = false;
+
+HINSTANCE hInstance; /** win32 application instance handle. */
+
+
+void win32_set_quit(bool q)
+{
+ win32_quit = q;
+}
+
+/* exported interface documented in gui.h */
+void win32_run(void)
+{
+ MSG Msg; /* message from system */
+ BOOL bRet; /* message fetch result */
+ int timeout; /* timeout in miliseconds */
+ UINT timer_id = 0;
+
+ LOG("Starting messgae dispatcher");
+
+ while (!win32_quit) {
+ /* run the scheduler and discover how long to wait for
+ * the next event.
+ */
+ timeout = schedule_run();
+
+ if (timeout == 0) {
+ bRet = PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE);
+ } else {
+ if (timeout > 0) {
+ /* set up a timer to ensure we get woken */
+ timer_id = SetTimer(NULL, 0, timeout, NULL);
+ }
+
+ /* wait for a message */
+ bRet = GetMessage(&Msg, NULL, 0, 0);
+
+ /* if a timer was sucessfully created remove it */
+ if (timer_id != 0) {
+ KillTimer(NULL, timer_id);
+ timer_id = 0;
+ }
+ }
+
+ if (bRet > 0) {
+ TranslateMessage(&Msg);
+ DispatchMessage(&Msg);
+ }
+ }
+}
+
+
+/* exported function documented in windows/gui.h */
+nserror win32_warning(const char *warning, const char *detail)
+{
+ size_t len = 1 + ((warning != NULL) ? strlen(messages_get(warning)) :
+ 0) + ((detail != 0) ? strlen(detail) : 0);
+ char message[len];
+ snprintf(message, len, messages_get(warning), detail);
+ MessageBox(NULL, message, "Warning", MB_ICONWARNING);
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Core asks front end for clipboard contents.
+ *
+ * \param buffer UTF-8 text, allocated by front end, ownership yeilded to core
+ * \param length Byte length of UTF-8 text in buffer
+ */
+static void gui_get_clipboard(char **buffer, size_t *length)
+{
+ /* TODO: Implement this */
+ HANDLE clipboard_handle;
+ char *content;
+
+ clipboard_handle = GetClipboardData(CF_TEXT);
+ if (clipboard_handle != NULL) {
+ content = GlobalLock(clipboard_handle);
+ LOG("pasting %s", content);
+ GlobalUnlock(clipboard_handle);
+ }
+}
+
+
+/**
+ * Core tells front end to put given text in clipboard
+ *
+ * \param buffer UTF-8 text, owned by core
+ * \param length Byte length of UTF-8 text in buffer
+ * \param styles Array of styles given to text runs, owned by core, or NULL
+ * \param n_styles Number of text run styles in array
+ */
+static void gui_set_clipboard(const char *buffer, size_t length,
+ nsclipboard_styles styles[], int n_styles)
+{
+ /* TODO: Implement this */
+ HANDLE hnew;
+ char *new, *original;
+ HANDLE h = GetClipboardData(CF_TEXT);
+ if (h == NULL)
+ original = (char *)"";
+ else
+ original = GlobalLock(h);
+
+ size_t len = strlen(original) + 1;
+ hnew = GlobalAlloc(GHND, length + len);
+ new = (char *)GlobalLock(hnew);
+ snprintf(new, length + len, "%s%s", original, buffer);
+
+ if (h != NULL) {
+ GlobalUnlock(h);
+ EmptyClipboard();
+ }
+ GlobalUnlock(hnew);
+ SetClipboardData(CF_TEXT, hnew);
+}
+
+
+
+static struct gui_clipboard_table clipboard_table = {
+ .get = gui_get_clipboard,
+ .set = gui_set_clipboard,
+};
+
+struct gui_clipboard_table *win32_clipboard_table = &clipboard_table;
+
+
diff --git a/frontends/windows/gui.h b/frontends/windows/gui.h
new file mode 100644
index 000000000..4c3f360b1
--- /dev/null
+++ b/frontends/windows/gui.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_WINDOWS_GUI_H_
+#define _NETSURF_WINDOWS_GUI_H_
+
+struct gui_window;
+struct gui_clipboard_table *win32_clipboard_table;
+
+extern HINSTANCE hInstance;
+
+extern char *options_file_location;
+
+/* bounding box */
+typedef struct bbox_s {
+ int x0;
+ int y0;
+ int x1;
+ int y1;
+} bbox_t;
+
+/**
+ * Run the win32 message loop with scheduling
+ */
+void win32_run(void);
+
+/**
+ * cause the main message loop to exit
+ */
+void win32_set_quit(bool q);
+
+/**
+ * Warn the user of an event.
+ *
+ * \param[in] message A warning looked up in the message translation table
+ * \param[in] detail Additional text to be displayed or NULL.
+ * \return NSERROR_OK on success or error code if there was a
+ * faliure displaying the message to the user.
+ */
+nserror win32_warning(const char *warning, const char *detail);
+
+#endif
diff --git a/frontends/windows/localhistory.c b/frontends/windows/localhistory.c
new file mode 100644
index 000000000..674f198a0
--- /dev/null
+++ b/frontends/windows/localhistory.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "utils/config.h"
+
+#include <windows.h>
+#include <windowsx.h>
+#include <commctrl.h>
+
+#include "desktop/browser_history.h"
+#include "desktop/plotters.h"
+#include "utils/utils.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+
+#include "windows/window.h"
+#include "windows/localhistory.h"
+#include "windows/gui.h"
+#include "windows/plot.h"
+#include "windows/resourceid.h"
+#include "windows/windbg.h"
+
+static const char windowclassname_localhistory[] = "nswslocalhistorywindow";
+
+struct nsws_localhistory {
+ HWND hwnd; /**< the window handle */
+ int width; /**< the width of the memory history */
+ int height; /**< the height of the memory history */
+ int guiwidth; /**< the width of the history window */
+ int guiheight; /**< the height of the history window */
+ int vscroll; /**< the vertical scroll location */
+ int hscroll; /**< the horizontal scroll location */
+};
+
+
+static void nsws_localhistory_scroll_check(struct nsws_localhistory *l, struct gui_window *gw)
+{
+ SCROLLINFO si;
+
+ if ((gw->bw == NULL) || (l->hwnd == NULL))
+ return;
+
+ browser_window_history_size(gw->bw, &(l->width), &(l->height));
+
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_ALL;
+ si.nMin = 0;
+ si.nMax = l->height;
+ si.nPage = l->guiheight;
+ si.nPos = 0;
+ SetScrollInfo(l->hwnd, SB_VERT, &si, TRUE);
+
+ si.nMax = l->width;
+ si.nPage = l->guiwidth;
+ SetScrollInfo(l->hwnd, SB_HORZ, &si, TRUE);
+ if (l->guiheight >= l->height)
+ l->vscroll = 0;
+ if (l->guiwidth >= l->width)
+ l->hscroll = 0;
+ SendMessage(l->hwnd, WM_PAINT, 0, 0);
+}
+
+
+
+static void nsws_localhistory_up(struct nsws_localhistory *l, struct gui_window *gw)
+{
+ HDC tmp_hdc;
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &win_plotters
+ };
+
+ LOG("gui window %p", gw);
+
+ l->vscroll = 0;
+ l->hscroll = 0;
+
+ if (gw->bw != NULL) {
+ /* set global HDC for the plotters */
+ tmp_hdc = plot_hdc;
+ plot_hdc = GetDC(l->hwnd);
+
+ browser_window_history_redraw(gw->bw, &ctx);
+
+ ReleaseDC(l->hwnd, plot_hdc);
+
+ plot_hdc = tmp_hdc;
+ }
+
+ nsws_localhistory_scroll_check(l, gw);
+}
+
+
+void nsws_localhistory_close(struct gui_window *w)
+{
+ struct nsws_localhistory *l = gui_window_localhistory(w);
+ if (l != NULL)
+ CloseWindow(l->hwnd);
+}
+
+static LRESULT CALLBACK
+nsws_localhistory_event_callback(HWND hwnd, UINT msg,
+ WPARAM wparam, LPARAM lparam)
+{
+ int x,y;
+ struct gui_window *gw;
+
+ LOG_WIN_MSG(hwnd, msg, wparam, lparam);
+
+ gw = nsws_get_gui_window(hwnd);
+ if (gw == NULL) {
+ LOG("Unable to find gui window structure for hwnd %p", hwnd);
+ return DefWindowProc(hwnd, msg, wparam, lparam);
+ }
+
+ switch(msg) {
+
+ case WM_CREATE:
+ nsws_localhistory_scroll_check(gw->localhistory, gw);
+ break;
+
+ case WM_SIZE:
+ gw->localhistory->guiheight = HIWORD(lparam);
+ gw->localhistory->guiwidth = LOWORD(lparam);
+ nsws_localhistory_scroll_check(gw->localhistory, gw);
+ break;
+
+ case WM_LBUTTONUP:
+ if (gw->bw == NULL)
+ break;
+
+ x = GET_X_LPARAM(lparam);
+ y = GET_Y_LPARAM(lparam);
+
+ if (browser_window_history_click(gw->bw,
+ gw->localhistory->hscroll + x,
+ gw->localhistory->vscroll + y,
+ false)) {
+ DestroyWindow(hwnd);
+ }
+
+ break;
+
+ case WM_MOUSEMOVE:
+ x = GET_X_LPARAM(lparam);
+ y = GET_Y_LPARAM(lparam);
+ return DefWindowProc(hwnd, msg, wparam, lparam);
+ break;
+
+
+ case WM_VSCROLL:
+ {
+ SCROLLINFO si;
+ int mem;
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_ALL;
+ GetScrollInfo(hwnd, SB_VERT, &si);
+ mem = si.nPos;
+ switch (LOWORD(wparam)) {
+ case SB_TOP:
+ si.nPos = si.nMin;
+ break;
+ case SB_BOTTOM:
+ si.nPos = si.nMax;
+ break;
+ case SB_LINEUP:
+ si.nPos -= 30;
+ break;
+ case SB_LINEDOWN:
+ si.nPos += 30;
+ break;
+ case SB_PAGEUP:
+ si.nPos -= gw->localhistory->guiheight;
+ break;
+ case SB_PAGEDOWN:
+ si.nPos += gw->localhistory->guiheight;
+ break;
+ case SB_THUMBTRACK:
+ si.nPos = si.nTrackPos;
+ break;
+ default:
+ break;
+ }
+ si.nPos = min(si.nPos, gw->localhistory->height);
+ si.nPos = min(si.nPos, 0);
+ si.fMask = SIF_POS;
+ SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
+ GetScrollInfo(hwnd, SB_VERT, &si);
+ if (si.nPos != mem) {
+ gw->localhistory->vscroll += si.nPos - mem;
+ ScrollWindowEx(hwnd, 0, -(si.nPos - mem), NULL, NULL, NULL, NULL, SW_ERASE | SW_INVALIDATE);
+ }
+ break;
+ }
+
+ case WM_HSCROLL:
+ {
+ SCROLLINFO si;
+ int mem;
+
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_ALL;
+ GetScrollInfo(hwnd, SB_HORZ, &si);
+ mem = si.nPos;
+
+ switch (LOWORD(wparam)) {
+ case SB_LINELEFT:
+ si.nPos -= 30;
+ break;
+ case SB_LINERIGHT:
+ si.nPos += 30;
+ break;
+ case SB_PAGELEFT:
+ si.nPos -= gw->localhistory->guiwidth;
+ break;
+ case SB_PAGERIGHT:
+ si.nPos += gw->localhistory->guiwidth;
+ break;
+ case SB_THUMBTRACK:
+ si.nPos = si.nTrackPos;
+ break;
+ default:
+ break;
+ }
+ si.nPos = min(si.nPos, gw->localhistory->width);
+ si.nPos = max(si.nPos, 0);
+ si.fMask = SIF_POS;
+ SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
+ GetScrollInfo(hwnd, SB_HORZ, &si);
+ if (si.nPos != mem) {
+ gw->localhistory->hscroll += si.nPos - mem;
+ ScrollWindowEx(hwnd, -(si.nPos - mem), 0, NULL, NULL, NULL, NULL, SW_ERASE | SW_INVALIDATE);
+ }
+ break;
+ }
+
+ case WM_PAINT: {
+ PAINTSTRUCT ps;
+ HDC hdc, tmp_hdc;
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &win_plotters
+ };
+
+ hdc = BeginPaint(hwnd, &ps);
+ if (gw->bw != NULL) {
+ /* set global HDC for the plotters */
+ tmp_hdc = plot_hdc;
+ plot_hdc = hdc;
+
+ browser_window_history_redraw_rectangle(gw->bw,
+ gw->localhistory->hscroll + ps.rcPaint.left,
+ gw->localhistory->vscroll + ps.rcPaint.top,
+ gw->localhistory->hscroll + (ps.rcPaint.right - ps.rcPaint.left),
+ gw->localhistory->vscroll + (ps.rcPaint.bottom - ps.rcPaint.top),
+ ps.rcPaint.left,
+ ps.rcPaint.top, &ctx);
+
+ plot_hdc = tmp_hdc;
+
+ }
+ EndPaint(hwnd, &ps);
+
+ break;
+ }
+
+ case WM_CLOSE:
+ DestroyWindow(hwnd);
+ return 1;
+
+ case WM_DESTROY:
+ free(gw->localhistory);
+ gw->localhistory = NULL;
+ break;
+
+ default:
+ return DefWindowProc(hwnd, msg, wparam, lparam);
+
+ }
+ return 0;
+}
+
+/* exported method documented in windows/localhistory.h */
+struct nsws_localhistory *nsws_window_create_localhistory(struct gui_window *gw)
+{
+ struct nsws_localhistory *localhistory;
+ INITCOMMONCONTROLSEX icc;
+ int margin = 50;
+ RECT r;
+
+ LOG("gui window %p", gw);
+
+ /* if we already have a window, just update and re-show it */
+ if (gw->localhistory != NULL) {
+ nsws_localhistory_up(gw->localhistory, gw);
+ UpdateWindow(gw->localhistory->hwnd);
+ ShowWindow(gw->localhistory->hwnd, SW_SHOWNORMAL);
+ return gw->localhistory;
+ }
+
+ localhistory = calloc(1, sizeof(struct nsws_localhistory));
+
+ if (localhistory == NULL) {
+ return NULL;
+ }
+ gw->localhistory = localhistory;
+
+ localhistory->width = 0;
+ localhistory->height = 0;
+
+ if (gw->bw != NULL) {
+ browser_window_history_size(gw->bw,
+ &(localhistory->width),
+ &(localhistory->height));
+ }
+
+ GetWindowRect(gw->main, &r);
+ SetWindowPos(gw->main, HWND_NOTOPMOST, 0, 0, 0, 0,
+ SWP_NOSIZE | SWP_NOMOVE);
+
+ localhistory->guiwidth = min(r.right - r.left - margin,
+ localhistory->width + margin);
+ localhistory->guiheight = min(r.bottom - r.top - margin,
+ localhistory->height + margin);
+
+ icc.dwSize = sizeof(icc);
+ icc.dwICC = ICC_BAR_CLASSES | ICC_WIN95_CLASSES;
+#if WINVER > 0x0501
+ icc.dwICC |= ICC_STANDARD_CLASSES;
+#endif
+ InitCommonControlsEx(&icc);
+
+
+ LOG("creating local history window for hInstance %p", hInstance);
+ localhistory->hwnd = CreateWindow(windowclassname_localhistory,
+ "NetSurf History",
+ WS_THICKFRAME | WS_HSCROLL |
+ WS_VSCROLL | WS_CLIPCHILDREN |
+ WS_CLIPSIBLINGS | WS_SYSMENU | CS_DBLCLKS,
+ r.left + margin/2,
+ r.top + margin/2,
+ localhistory->guiwidth,
+ localhistory->guiheight,
+ NULL, NULL, hInstance, NULL);
+
+ /* set the gui window associated with this browser */
+ SetProp(localhistory->hwnd, TEXT("GuiWnd"), (HANDLE)gw);
+
+ LOG("gui_window %p width %d height %d hwnd %p", gw, localhistory->guiwidth, localhistory->guiheight, localhistory->hwnd);
+
+ nsws_localhistory_up(localhistory, gw);
+ UpdateWindow(localhistory->hwnd);
+ ShowWindow(localhistory->hwnd, SW_SHOWNORMAL);
+
+ return localhistory;
+}
+
+/* exported method documented in windows/localhistory.h */
+nserror
+nsws_create_localhistory_class(HINSTANCE hinstance) {
+ nserror ret = NSERROR_OK;
+ WNDCLASSEX w;
+
+ /* localhistory window */
+ w.cbSize = sizeof(WNDCLASSEX);
+ w.style = 0;
+ w.lpfnWndProc = nsws_localhistory_event_callback;
+ w.cbClsExtra = 0;
+ w.cbWndExtra = 0;
+ w.hInstance = hinstance;
+ w.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(IDR_NETSURF_ICON));
+ w.hCursor = LoadCursor(NULL, IDC_ARROW);
+ w.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+ w.lpszMenuName = NULL;
+ w.lpszClassName = windowclassname_localhistory;
+ w.hIconSm = LoadIcon(hinstance, MAKEINTRESOURCE(IDR_NETSURF_ICON));
+
+ if (RegisterClassEx(&w) == 0) {
+ win_perror("DrawableClass");
+ ret = NSERROR_INIT_FAILED;
+ }
+
+ return ret;
+}
diff --git a/frontends/windows/localhistory.h b/frontends/windows/localhistory.h
new file mode 100644
index 000000000..b0ad07491
--- /dev/null
+++ b/frontends/windows/localhistory.h
@@ -0,0 +1,32 @@
+/*
+* Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+*
+* This file is part of NetSurf, http://www.netsurf-browser.org/
+*
+* NetSurf 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; version 2 of the License.
+*
+* NetSurf 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 _NETSURF_WINDOWS_LOCALHISTORY_H_
+#define _NETSURF_WINDOWS_LOCALHISTORY_H_
+
+struct nsws_localhistory;
+
+void nsws_localhistory_open(struct gui_window *gw);
+void nsws_localhistory_close(struct gui_window *gw);
+
+/* creates localhistory window */
+struct nsws_localhistory *nsws_window_create_localhistory(struct gui_window *gw);
+
+nserror nsws_create_localhistory_class(HINSTANCE hinstance);
+
+#endif
diff --git a/frontends/windows/main.c b/frontends/windows/main.c
new file mode 100644
index 000000000..df64e487d
--- /dev/null
+++ b/frontends/windows/main.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2011 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "utils/config.h"
+
+#include <limits.h>
+#include <stdbool.h>
+#include <windows.h>
+#include <io.h>
+
+#include "utils/utils.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/filepath.h"
+#include "utils/file.h"
+#include "utils/nsurl.h"
+#include "utils/nsoption.h"
+#include "desktop/browser.h"
+#include "desktop/gui_fetch.h"
+#include "desktop/gui_misc.h"
+#include "desktop/netsurf.h"
+
+#include "windows/findfile.h"
+#include "windows/file.h"
+#include "windows/drawable.h"
+#include "windows/download.h"
+#include "windows/localhistory.h"
+#include "windows/window.h"
+#include "windows/schedule.h"
+#include "windows/font.h"
+#include "windows/filetype.h"
+#include "windows/pointers.h"
+#include "windows/bitmap.h"
+#include "windows/gui.h"
+
+static char **respaths; /** resource search path vector. */
+
+char *options_file_location;
+
+/**
+ * Cause an abnormal program termination.
+ *
+ * \note This never returns and is intended to terminate without any cleanup.
+ *
+ * \param error The message to display to the user.
+ */
+static void die(const char *error)
+{
+ exit(1);
+}
+
+
+static nsurl *gui_get_resource_url(const char *path)
+{
+ char buf[PATH_MAX];
+ nsurl *url = NULL;
+
+ netsurf_path_to_nsurl(filepath_sfind(respaths, buf, path), &url);
+
+ return url;
+}
+
+/**
+ * Ensures output logging stream is available
+ */
+static bool nslog_ensure(FILE *fptr)
+{
+ /* mwindows compile flag normally invalidates standard io unless
+ * already redirected
+ */
+ if (_get_osfhandle(fileno(fptr)) == -1) {
+ AllocConsole();
+ freopen("CONOUT$", "w", fptr);
+ }
+ return true;
+}
+
+/**
+ * Set option defaults for windows frontend
+ *
+ * @param defaults The option table to update.
+ * @return error status.
+ */
+static nserror set_defaults(struct nsoption_s *defaults)
+{
+ /* Set defaults for absent option strings */
+
+ /* locate CA bundle and set as default, cannot rely on curl
+ * compiled in default on windows.
+ */
+ DWORD res_len;
+ DWORD buf_tchar_size = PATH_MAX + 1;
+ DWORD buf_bytes_size = sizeof(TCHAR) * buf_tchar_size;
+ char *ptr = NULL;
+
+ char *buf;
+
+ buf = malloc(buf_bytes_size);
+ if (buf== NULL) {
+ return NSERROR_NOMEM;
+ }
+ buf[0] = '\0';
+
+ res_len = SearchPathA(NULL,
+ "ca-bundle.crt",
+ NULL,
+ buf_tchar_size,
+ buf,
+ &ptr);
+ if (res_len > 0) {
+ nsoption_setnull_charp(ca_bundle, strdup(buf));
+ }
+ free(buf);
+
+ /* ensure homepage option has a default */
+ nsoption_setnull_charp(homepage_url, strdup(NETSURF_HOMEPAGE));
+
+ return NSERROR_OK;
+}
+
+
+static struct gui_misc_table win32_misc_table = {
+ .schedule = win32_schedule,
+ .warning = win32_warning,
+};
+
+
+/**
+ * Entry point from windows
+ **/
+int WINAPI
+WinMain(HINSTANCE hInstance, HINSTANCE hLastInstance, LPSTR lpcli, int ncmd)
+{
+ char **argv = NULL;
+ int argc = 0, argctemp = 0;
+ size_t len;
+ LPWSTR *argvw;
+ char *messages;
+ nserror ret;
+ const char *addr;
+ nsurl *url;
+ struct netsurf_table win32_table = {
+ .misc = &win32_misc_table,
+ .window = win32_window_table,
+ .clipboard = win32_clipboard_table,
+ .download = win32_download_table,
+ .fetch = win32_fetch_table,
+ .file = win32_file_table,
+ .utf8 = win32_utf8_table,
+ .bitmap = win32_bitmap_table,
+ .layout = win32_layout_table,
+ };
+ win32_fetch_table->get_resource_url = gui_get_resource_url;
+
+ ret = netsurf_register(&win32_table);
+ if (ret != NSERROR_OK) {
+ die("NetSurf operation table registration failed");
+ }
+
+ if (SLEN(lpcli) > 0) {
+ argvw = CommandLineToArgvW(GetCommandLineW(), &argc);
+ }
+
+ setbuf(stderr, NULL);
+
+ /* Construct a unix style argc/argv */
+ argv = malloc(sizeof(char *) * argc);
+ while (argctemp < argc) {
+ len = wcstombs(NULL, argvw[argctemp], 0) + 1;
+ if (len > 0) {
+ argv[argctemp] = malloc(len);
+ }
+
+ if (argv[argctemp] != NULL) {
+ wcstombs(argv[argctemp], argvw[argctemp], len);
+ /* alter windows-style forward slash flags to
+ * hyphen flags.
+ */
+ if (argv[argctemp][0] == '/')
+ argv[argctemp][0] = '-';
+ }
+ argctemp++;
+ }
+
+ respaths = nsws_init_resource("${APPDATA}\\NetSurf:${HOME}\\.netsurf:${NETSURFRES}:${PROGRAMFILES}\\NetSurf\\NetSurf\\:"NETSURF_WINDOWS_RESPATH);
+
+
+ options_file_location = filepath_find(respaths, "preferences");
+
+ /* initialise logging - not fatal if it fails but not much we
+ * can do about it
+ */
+ nslog_init(nslog_ensure, &argc, argv);
+
+ /* user options setup */
+ ret = nsoption_init(set_defaults, &nsoptions, &nsoptions_default);
+ if (ret != NSERROR_OK) {
+ die("Options failed to initialise");
+ }
+ nsoption_read(options_file_location, NULL);
+ nsoption_commandline(&argc, argv, NULL);
+
+ /* message init */
+ messages = filepath_find(respaths, "messages");
+ messages_add_from_file(messages);
+ free(messages);
+
+ /* common initialisation */
+ ret = netsurf_init(NULL);
+ if (ret != NSERROR_OK) {
+ free(options_file_location);
+ LOG("NetSurf failed to initialise");
+ return 1;
+ }
+
+ ret = nsws_create_main_class(hInstance);
+ ret = nsws_create_drawable_class(hInstance);
+ ret = nsws_create_localhistory_class(hInstance);
+
+ nsoption_set_bool(target_blank, false);
+
+ nsws_window_init_pointers(hInstance);
+
+ /* If there is a url specified on the command line use it */
+ if (argc > 1) {
+ addr = argv[1];
+ } else if (nsoption_charp(homepage_url) != NULL) {
+ addr = nsoption_charp(homepage_url);
+ } else {
+ addr = NETSURF_HOMEPAGE;
+ }
+
+ LOG("calling browser_window_create");
+
+ ret = nsurl_create(addr, &url);
+ if (ret == NSERROR_OK) {
+ ret = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+
+ }
+ if (ret != NSERROR_OK) {
+ win32_warning(messages_get_errorcode(ret), 0);
+ } else {
+ win32_run();
+ }
+
+ netsurf_exit();
+
+ free(options_file_location);
+
+ return 0;
+}
diff --git a/frontends/windows/plot.c b/frontends/windows/plot.c
new file mode 100644
index 000000000..86870ea4e
--- /dev/null
+++ b/frontends/windows/plot.c
@@ -0,0 +1,868 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "utils/config.h"
+#include <sys/types.h>
+#include <stdint.h>
+#include <string.h>
+#include <limits.h>
+#include <math.h>
+#include <windows.h>
+
+#include "utils/log.h"
+#include "utils/utf8.h"
+#include "utils/utils.h"
+#include "desktop/mouse.h"
+#include "desktop/gui_window.h"
+#include "desktop/plotters.h"
+
+#include "windows/bitmap.h"
+#include "windows/font.h"
+#include "windows/gui.h"
+#include "windows/plot.h"
+
+
+/* set NSWS_PLOT_DEBUG to 0 for no debugging, 1 for debugging */
+/* #define NSWS_PLOT_DEBUG */
+
+#ifdef NSWS_PLOT_DEBUG
+#define PLOT_LOG(x...) LOG(x)
+#else
+#define PLOT_LOG(x...) ((void) 0)
+#endif
+
+HDC plot_hdc;
+
+static RECT plot_clip; /* currently set clipping rectangle */
+
+static bool clip(const struct rect *clip)
+{
+ PLOT_LOG("clip %d,%d to %d,%d", clip->x0, clip->y0, clip->x1, clip->y1);
+
+ plot_clip.left = clip->x0;
+ plot_clip.top = clip->y0;
+ plot_clip.right = clip->x1 + 1; /* co-ordinates are exclusive */
+ plot_clip.bottom = clip->y1 + 1; /* co-ordinates are exclusive */
+
+ return true;
+}
+
+static bool line(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ PLOT_LOG("from %d,%d to %d,%d", x0, y0, x1, y1);
+
+ /* ensure the plot HDC is set */
+ if (plot_hdc == NULL) {
+ LOG("HDC not set on call to plotters");
+ return false;
+ }
+
+ HRGN clipregion = CreateRectRgnIndirect(&plot_clip);
+ if (clipregion == NULL) {
+ return false;
+ }
+
+ COLORREF col = (DWORD)(style->stroke_colour & 0x00FFFFFF);
+ /* windows 0x00bbggrr */
+ DWORD penstyle = PS_GEOMETRIC | ((style->stroke_type ==
+ PLOT_OP_TYPE_DOT) ? PS_DOT :
+ (style->stroke_type == PLOT_OP_TYPE_DASH) ? PS_DASH:
+ 0);
+ LOGBRUSH lb = {BS_SOLID, col, 0};
+ HPEN pen = ExtCreatePen(penstyle, style->stroke_width, &lb, 0, NULL);
+ if (pen == NULL) {
+ DeleteObject(clipregion);
+ return false;
+ }
+ HGDIOBJ bak = SelectObject(plot_hdc, (HGDIOBJ) pen);
+ if (bak == NULL) {
+ DeleteObject(pen);
+ DeleteObject(clipregion);
+ return false;
+ }
+/*
+ RECT r;
+ r.left = x0;
+ r.top = y0;
+ r.right = x1;
+ r.bottom = y1;
+*/
+ SelectClipRgn(plot_hdc, clipregion);
+
+ MoveToEx(plot_hdc, x0, y0, (LPPOINT) NULL);
+
+ LineTo(plot_hdc, x1, y1);
+
+ SelectClipRgn(plot_hdc, NULL);
+ pen = SelectObject(plot_hdc, bak);
+
+ DeleteObject(pen);
+ DeleteObject(clipregion);
+
+ return true;
+}
+
+static bool rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ PLOT_LOG("rectangle from %d,%d to %d,%d", x0, y0, x1, y1);
+
+ /* ensure the plot HDC is set */
+ if (plot_hdc == NULL) {
+ LOG("HDC not set on call to plotters");
+ return false;
+ }
+
+ HRGN clipregion = CreateRectRgnIndirect(&plot_clip);
+ if (clipregion == NULL) {
+ return false;
+ }
+
+ x1++;
+ y1++;
+
+ COLORREF pencol = (DWORD)(style->stroke_colour & 0x00FFFFFF);
+ DWORD penstyle = PS_GEOMETRIC |
+ (style->stroke_type == PLOT_OP_TYPE_DOT ? PS_DOT :
+ (style->stroke_type == PLOT_OP_TYPE_DASH ? PS_DASH :
+ (style->stroke_type == PLOT_OP_TYPE_NONE ? PS_NULL :
+ 0)));
+ LOGBRUSH lb = {BS_SOLID, pencol, 0};
+ LOGBRUSH lb1 = {BS_SOLID, style->fill_colour, 0};
+ if (style->fill_type == PLOT_OP_TYPE_NONE)
+ lb1.lbStyle = BS_HOLLOW;
+
+ HPEN pen = ExtCreatePen(penstyle, style->stroke_width, &lb, 0, NULL);
+ if (pen == NULL) {
+ return false;
+ }
+ HGDIOBJ penbak = SelectObject(plot_hdc, (HGDIOBJ) pen);
+ if (penbak == NULL) {
+ DeleteObject(pen);
+ return false;
+ }
+ HBRUSH brush = CreateBrushIndirect(&lb1);
+ if (brush == NULL) {
+ SelectObject(plot_hdc, penbak);
+ DeleteObject(pen);
+ return false;
+ }
+ HGDIOBJ brushbak = SelectObject(plot_hdc, (HGDIOBJ) brush);
+ if (brushbak == NULL) {
+ SelectObject(plot_hdc, penbak);
+ DeleteObject(pen);
+ DeleteObject(brush);
+ return false;
+ }
+
+ SelectClipRgn(plot_hdc, clipregion);
+
+ Rectangle(plot_hdc, x0, y0, x1, y1);
+
+ pen = SelectObject(plot_hdc, penbak);
+ brush = SelectObject(plot_hdc, brushbak);
+ SelectClipRgn(plot_hdc, NULL);
+ DeleteObject(pen);
+ DeleteObject(brush);
+ DeleteObject(clipregion);
+
+ return true;
+}
+
+
+static bool polygon(const int *p, unsigned int n, const plot_style_t *style)
+{
+ PLOT_LOG("polygon %d points", n);
+
+ /* ensure the plot HDC is set */
+ if (plot_hdc == NULL) {
+ LOG("HDC not set on call to plotters");
+ return false;
+ }
+
+ POINT points[n];
+ unsigned int i;
+ HRGN clipregion = CreateRectRgnIndirect(&plot_clip);
+ if (clipregion == NULL) {
+ return false;
+ }
+
+ COLORREF pencol = (DWORD)(style->fill_colour & 0x00FFFFFF);
+ COLORREF brushcol = (DWORD)(style->fill_colour & 0x00FFFFFF);
+ HPEN pen = CreatePen(PS_GEOMETRIC | PS_NULL, 1, pencol);
+ if (pen == NULL) {
+ DeleteObject(clipregion);
+ return false;
+ }
+ HPEN penbak = SelectObject(plot_hdc, pen);
+ if (penbak == NULL) {
+ DeleteObject(clipregion);
+ DeleteObject(pen);
+ return false;
+ }
+ HBRUSH brush = CreateSolidBrush(brushcol);
+ if (brush == NULL) {
+ DeleteObject(clipregion);
+ SelectObject(plot_hdc, penbak);
+ DeleteObject(pen);
+ return false;
+ }
+ HBRUSH brushbak = SelectObject(plot_hdc, brush);
+ if (brushbak == NULL) {
+ DeleteObject(clipregion);
+ SelectObject(plot_hdc, penbak);
+ DeleteObject(pen);
+ DeleteObject(brush);
+ return false;
+ }
+ SetPolyFillMode(plot_hdc, WINDING);
+ for (i = 0; i < n; i++) {
+ points[i].x = (long) p[2 * i];
+ points[i].y = (long) p[2 * i + 1];
+
+ PLOT_LOG("%ld,%ld ", points[i].x, points[i].y);
+ }
+
+ SelectClipRgn(plot_hdc, clipregion);
+
+ if (n >= 2)
+ Polygon(plot_hdc, points, n);
+
+ SelectClipRgn(plot_hdc, NULL);
+
+ pen = SelectObject(plot_hdc, penbak);
+ brush = SelectObject(plot_hdc, brushbak);
+ DeleteObject(clipregion);
+ DeleteObject(pen);
+ DeleteObject(brush);
+
+ return true;
+}
+
+
+static bool text(int x, int y, const char *text, size_t length,
+ const plot_font_style_t *style)
+{
+ PLOT_LOG("words %s at %d,%d", text, x, y);
+
+ /* ensure the plot HDC is set */
+ if (plot_hdc == NULL) {
+ LOG("HDC not set on call to plotters");
+ return false;
+ }
+
+ HRGN clipregion = CreateRectRgnIndirect(&plot_clip);
+ if (clipregion == NULL) {
+ return false;
+ }
+
+ HFONT fontbak, font = get_font(style);
+ if (font == NULL) {
+ DeleteObject(clipregion);
+ return false;
+ }
+ int wlen;
+ SIZE s;
+ LPWSTR wstring;
+ fontbak = (HFONT) SelectObject(plot_hdc, font);
+ GetTextExtentPoint(plot_hdc, text, length, &s);
+
+/*
+ RECT r;
+ r.left = x;
+ r.top = y - (3 * s.cy) / 4;
+ r.right = x + s.cx;
+ r.bottom = y + s.cy / 4;
+*/
+ SelectClipRgn(plot_hdc, clipregion);
+
+ SetTextAlign(plot_hdc, TA_BASELINE | TA_LEFT);
+ if ((style->background & 0xFF000000) != 0x01000000)
+ /* 100% alpha */
+ SetBkColor(plot_hdc, (DWORD) (style->background & 0x00FFFFFF));
+ SetBkMode(plot_hdc, TRANSPARENT);
+ SetTextColor(plot_hdc, (DWORD) (style->foreground & 0x00FFFFFF));
+
+ wlen = MultiByteToWideChar(CP_UTF8, 0, text, length, NULL, 0);
+ wstring = malloc(2 * (wlen + 1));
+ if (wstring == NULL) {
+ return false;
+ }
+ MultiByteToWideChar(CP_UTF8, 0, text, length, wstring, wlen);
+ TextOutW(plot_hdc, x, y, wstring, wlen);
+
+ SelectClipRgn(plot_hdc, NULL);
+ free(wstring);
+ font = SelectObject(plot_hdc, fontbak);
+ DeleteObject(clipregion);
+ DeleteObject(font);
+
+ return true;
+}
+
+static bool disc(int x, int y, int radius, const plot_style_t *style)
+{
+ PLOT_LOG("disc at %d,%d radius %d", x, y, radius);
+
+ /* ensure the plot HDC is set */
+ if (plot_hdc == NULL) {
+ LOG("HDC not set on call to plotters");
+ return false;
+ }
+
+ HRGN clipregion = CreateRectRgnIndirect(&plot_clip);
+ if (clipregion == NULL) {
+ return false;
+ }
+
+ COLORREF col = (DWORD)((style->fill_colour | style->stroke_colour)
+ & 0x00FFFFFF);
+ HPEN pen = CreatePen(PS_GEOMETRIC | PS_SOLID, 1, col);
+ if (pen == NULL) {
+ DeleteObject(clipregion);
+ return false;
+ }
+ HGDIOBJ penbak = SelectObject(plot_hdc, (HGDIOBJ) pen);
+ if (penbak == NULL) {
+ DeleteObject(clipregion);
+ DeleteObject(pen);
+ return false;
+ }
+ HBRUSH brush = CreateSolidBrush(col);
+ if (brush == NULL) {
+ DeleteObject(clipregion);
+ SelectObject(plot_hdc, penbak);
+ DeleteObject(pen);
+ return false;
+ }
+ HGDIOBJ brushbak = SelectObject(plot_hdc, (HGDIOBJ) brush);
+ if (brushbak == NULL) {
+ DeleteObject(clipregion);
+ SelectObject(plot_hdc, penbak);
+ DeleteObject(pen);
+ DeleteObject(brush);
+ return false;
+ }
+/*
+ RECT r;
+ r.left = x - radius;
+ r.top = y - radius;
+ r.right = x + radius;
+ r.bottom = y + radius;
+*/
+ SelectClipRgn(plot_hdc, clipregion);
+
+ if (style->fill_type == PLOT_OP_TYPE_NONE)
+ Arc(plot_hdc, x - radius, y - radius, x + radius, y + radius,
+ x - radius, y - radius,
+ x - radius, y - radius);
+ else
+ Ellipse(plot_hdc, x - radius, y - radius, x + radius, y + radius);
+
+ SelectClipRgn(plot_hdc, NULL);
+ pen = SelectObject(plot_hdc, penbak);
+ brush = SelectObject(plot_hdc, brushbak);
+ DeleteObject(clipregion);
+ DeleteObject(pen);
+ DeleteObject(brush);
+
+ return true;
+}
+
+static bool arc(int x, int y, int radius, int angle1, int angle2,
+ const plot_style_t *style)
+{
+ PLOT_LOG("arc centre %d,%d radius %d from %d to %d", x, y, radius,
+ angle1, angle2);
+
+ /* ensure the plot HDC is set */
+ if (plot_hdc == NULL) {
+ LOG("HDC not set on call to plotters");
+ return false;
+ }
+
+ HRGN clipregion = CreateRectRgnIndirect(&plot_clip);
+ if (clipregion == NULL) {
+ return false;
+ }
+
+ COLORREF col = (DWORD)(style->stroke_colour & 0x00FFFFFF);
+ HPEN pen = CreatePen(PS_GEOMETRIC | PS_SOLID, 1, col);
+ if (pen == NULL) {
+ DeleteObject(clipregion);
+ return false;
+ }
+ HGDIOBJ penbak = SelectObject(plot_hdc, (HGDIOBJ) pen);
+ if (penbak == NULL) {
+ DeleteObject(clipregion);
+ DeleteObject(pen);
+ return false;
+ }
+
+ int q1, q2;
+ double a1=1.0, a2=1.0, b1=1.0, b2=1.0;
+ q1 = (int) ((angle1 + 45) / 90) - 45;
+ q2 = (int) ((angle2 + 45) / 90) - 45;
+ while (q1 > 4)
+ q1 -= 4;
+ while (q2 > 4)
+ q2 -= 4;
+ while (q1 <= 0)
+ q1 += 4;
+ while (q2 <= 0)
+ q2 += 4;
+ angle1 = ((angle1 + 45) % 90) - 45;
+ angle2 = ((angle2 + 45) % 90) - 45;
+
+ switch(q1) {
+ case 1:
+ a1 = 1.0;
+ b1 = -tan((M_PI / 180) * angle1);
+ break;
+ case 2:
+ b1 = -1.0;
+ a1 = -tan((M_PI / 180) * angle1);
+ break;
+ case 3:
+ a1 = -1.0;
+ b1 = tan((M_PI / 180) * angle1);
+ break;
+ case 4:
+ b1 = 1.0;
+ a1 = tan((M_PI / 180) * angle1);
+ break;
+ }
+
+ switch(q2) {
+ case 1:
+ a2 = 1.0;
+ b2 = -tan((M_PI / 180) * angle2);
+ break;
+ case 2:
+ b2 = -1.0;
+ a2 = -tan((M_PI / 180) * angle2);
+ break;
+ case 3:
+ a2 = -1.0;
+ b2 = tan((M_PI / 180) * angle2);
+ break;
+ case 4:
+ b2 = 1.0;
+ a2 = tan((M_PI / 180) * angle2);
+ break;
+ }
+
+/*
+ RECT r;
+ r.left = x - radius;
+ r.top = y - radius;
+ r.right = x + radius;
+ r.bottom = y + radius;
+*/
+ SelectClipRgn(plot_hdc, clipregion);
+
+ Arc(plot_hdc, x - radius, y - radius, x + radius, y + radius,
+ x + (int)(a1 * radius), y + (int)(b1 * radius),
+ x + (int)(a2 * radius), y + (int)(b2 * radius));
+
+ SelectClipRgn(plot_hdc, NULL);
+ pen = SelectObject(plot_hdc, penbak);
+ DeleteObject(clipregion);
+ DeleteObject(pen);
+
+ return true;
+}
+
+static bool
+plot_block(COLORREF col, int x, int y, int width, int height)
+{
+ HRGN clipregion;
+ HGDIOBJ original = NULL;
+
+ /* Bail early if we can */
+ if ((x >= plot_clip.right) ||
+ ((x + width) < plot_clip.left) ||
+ (y >= plot_clip.bottom) ||
+ ((y + height) < plot_clip.top)) {
+ /* Image completely outside clip region */
+ return true;
+ }
+
+ /* ensure the plot HDC is set */
+ if (plot_hdc == NULL) {
+ LOG("HDC not set on call to plotters");
+ return false;
+ }
+
+ clipregion = CreateRectRgnIndirect(&plot_clip);
+ if (clipregion == NULL) {
+ return false;
+ }
+
+ SelectClipRgn(plot_hdc, clipregion);
+
+ /* Saving the original pen object */
+ original = SelectObject(plot_hdc,GetStockObject(DC_PEN));
+
+ SelectObject(plot_hdc, GetStockObject(DC_PEN));
+ SelectObject(plot_hdc, GetStockObject(DC_BRUSH));
+ SetDCPenColor(plot_hdc, col);
+ SetDCBrushColor(plot_hdc, col);
+ Rectangle(plot_hdc, x, y, width, height);
+
+ SelectObject(plot_hdc,original); /* Restoring the original pen object */
+
+ DeleteObject(clipregion);
+
+ return true;
+
+}
+
+/* blunt force truma way of achiving alpha blended plotting */
+static bool
+plot_alpha_bitmap(HDC hdc,
+ struct bitmap *bitmap,
+ int x, int y,
+ int width, int height)
+{
+#ifdef WINDOWS_GDI_ALPHA_WORKED
+ BLENDFUNCTION blnd = { AC_SRC_OVER, 0, 0xff, AC_SRC_ALPHA };
+ HDC bmihdc;
+ bool bltres;
+ bmihdc = CreateCompatibleDC(hdc);
+ SelectObject(bmihdc, bitmap->windib);
+ bltres = AlphaBlend(hdc,
+ x, y,
+ width, height,
+ bmihdc,
+ 0, 0,
+ bitmap->width, bitmap->height,
+ blnd);
+ DeleteDC(bmihdc);
+ return bltres;
+#else
+ HDC Memhdc;
+ BITMAPINFOHEADER bmih;
+ int v, vv, vi, h, hh, width4, transparency;
+ unsigned char alpha;
+ bool isscaled = false; /* set if the scaled bitmap requires freeing */
+ BITMAP MemBM;
+ BITMAPINFO *bmi;
+ HBITMAP MemBMh;
+
+ PLOT_LOG("%p bitmap %d,%d width %d height %d", bitmap, x, y, width, height);
+ PLOT_LOG("clipped %ld,%ld to %ld,%ld",plot_clip.left, plot_clip.top, plot_clip.right, plot_clip.bottom);
+
+ Memhdc = CreateCompatibleDC(hdc);
+ if (Memhdc == NULL) {
+ return false;
+ }
+
+ if ((bitmap->width != width) ||
+ (bitmap->height != height)) {
+ PLOT_LOG("scaling from %d,%d to %d,%d",
+ bitmap->width, bitmap->height, width, height);
+ bitmap = bitmap_scale(bitmap, width, height);
+ if (bitmap == NULL)
+ return false;
+ isscaled = true;
+ }
+
+ bmi = (BITMAPINFO *) malloc(sizeof(BITMAPINFOHEADER) +
+ (bitmap->width * bitmap->height * 4));
+ if (bmi == NULL) {
+ DeleteDC(Memhdc);
+ return false;
+ }
+
+ MemBMh = CreateCompatibleBitmap(hdc, bitmap->width, bitmap->height);
+ if (MemBMh == NULL){
+ free(bmi);
+ DeleteDC(Memhdc);
+ return false;
+ }
+
+ /* save 'background' data for alpha channel work */
+ SelectObject(Memhdc, MemBMh);
+ BitBlt(Memhdc, 0, 0, bitmap->width, bitmap->height, hdc, x, y, SRCCOPY);
+ GetObject(MemBMh, sizeof(BITMAP), &MemBM);
+
+ bmih.biSize = sizeof(bmih);
+ bmih.biWidth = bitmap->width;
+ bmih.biHeight = bitmap->height;
+ bmih.biPlanes = 1;
+ bmih.biBitCount = 32;
+ bmih.biCompression = BI_RGB;
+ bmih.biSizeImage = 4 * bitmap->height * bitmap->width;
+ bmih.biXPelsPerMeter = 3600; /* 100 dpi */
+ bmih.biYPelsPerMeter = 3600;
+ bmih.biClrUsed = 0;
+ bmih.biClrImportant = 0;
+ bmi->bmiHeader = bmih;
+
+ GetDIBits(hdc, MemBMh, 0, bitmap->height, bmi->bmiColors, bmi,
+ DIB_RGB_COLORS);
+
+ /* then load 'foreground' bits from bitmap->pixdata */
+
+ width4 = bitmap->width * 4;
+ for (v = 0, vv = 0, vi = (bitmap->height - 1) * width4;
+ v < bitmap->height;
+ v++, vv += bitmap->width, vi -= width4) {
+ for (h = 0, hh = 0; h < bitmap->width; h++, hh += 4) {
+ alpha = bitmap->pixdata[vi + hh + 3];
+/* multiplication of alpha value; subject to profiling could be optional */
+ if (alpha == 0xFF) {
+ bmi->bmiColors[vv + h].rgbBlue =
+ bitmap->pixdata[vi + hh + 2];
+ bmi->bmiColors[vv + h].rgbGreen =
+ bitmap->pixdata[vi + hh + 1];
+ bmi->bmiColors[vv + h].rgbRed =
+ bitmap->pixdata[vi + hh];
+ } else if (alpha > 0) {
+ transparency = 0x100 - alpha;
+ bmi->bmiColors[vv + h].rgbBlue =
+ (bmi->bmiColors[vv + h].rgbBlue
+ * transparency +
+ (bitmap->pixdata[vi + hh + 2]) *
+ alpha) >> 8;
+ bmi->bmiColors[vv + h].rgbGreen =
+ (bmi->bmiColors[vv + h].
+ rgbGreen
+ * transparency +
+ (bitmap->pixdata[vi + hh + 1]) *
+ alpha) >> 8;
+ bmi->bmiColors[vv + h].rgbRed =
+ (bmi->bmiColors[vv + h].rgbRed
+ * transparency +
+ bitmap->pixdata[vi + hh]
+ * alpha) >> 8;
+ }
+ }
+ }
+ SetDIBitsToDevice(hdc, x, y, bitmap->width, bitmap->height,
+ 0, 0, 0, bitmap->height,
+ (const void *) bmi->bmiColors,
+ bmi, DIB_RGB_COLORS);
+
+ if (isscaled && bitmap && bitmap->pixdata) {
+ free(bitmap->pixdata);
+ free(bitmap);
+ }
+
+ free(bmi);
+ DeleteObject(MemBMh);
+ DeleteDC(Memhdc);
+ return true;
+#endif
+}
+
+
+static bool
+plot_bitmap(struct bitmap *bitmap, int x, int y, int width, int height)
+{
+ int bltres;
+ HRGN clipregion;
+
+ /* Bail early if we can */
+ if ((x >= plot_clip.right) ||
+ ((x + width) < plot_clip.left) ||
+ (y >= plot_clip.bottom) ||
+ ((y + height) < plot_clip.top)) {
+ /* Image completely outside clip region */
+ return true;
+ }
+
+ /* ensure the plot HDC is set */
+ if (plot_hdc == NULL) {
+ LOG("HDC not set on call to plotters");
+ return false;
+ }
+
+ clipregion = CreateRectRgnIndirect(&plot_clip);
+ if (clipregion == NULL) {
+ return false;
+ }
+
+ SelectClipRgn(plot_hdc, clipregion);
+
+ if (bitmap->opaque) {
+ /* opaque bitmap */
+ if ((bitmap->width == width) &&
+ (bitmap->height == height)) {
+ /* unscaled */
+ bltres = SetDIBitsToDevice(plot_hdc,
+ x, y,
+ width, height,
+ 0, 0,
+ 0,
+ height,
+ bitmap->pixdata,
+ (BITMAPINFO *)bitmap->pbmi,
+ DIB_RGB_COLORS);
+ } else {
+ /* scaled */
+ SetStretchBltMode(plot_hdc, COLORONCOLOR);
+ bltres = StretchDIBits(plot_hdc,
+ x, y,
+ width, height,
+ 0, 0,
+ bitmap->width, bitmap->height,
+ bitmap->pixdata,
+ (BITMAPINFO *)bitmap->pbmi,
+ DIB_RGB_COLORS,
+ SRCCOPY);
+
+
+ }
+ } else {
+ /* Bitmap with alpha.*/
+ bltres = plot_alpha_bitmap(plot_hdc, bitmap, x, y, width, height);
+ }
+
+ PLOT_LOG("bltres = %d", bltres);
+
+ DeleteObject(clipregion);
+
+ return true;
+
+}
+
+static bool
+windows_plot_bitmap(int x, int y,
+ int width, int height,
+ struct bitmap *bitmap, colour bg,
+ bitmap_flags_t flags)
+{
+ int xf,yf;
+ bool repeat_x = (flags & BITMAPF_REPEAT_X);
+ bool repeat_y = (flags & BITMAPF_REPEAT_Y);
+
+ /* Bail early if we can */
+
+ PLOT_LOG("Plotting %p at %d,%d by %d,%d",bitmap, x,y,width,height);
+
+ if (bitmap == NULL) {
+ LOG("Passed null bitmap!");
+ return true;
+ }
+
+ /* check if nothing to plot */
+ if (width == 0 || height == 0)
+ return true;
+
+ /* x and y define coordinate of top left of of the initial explicitly
+ * placed tile. The width and height are the image scaling and the
+ * bounding box defines the extent of the repeat (which may go in all
+ * four directions from the initial tile).
+ */
+
+ if (!(repeat_x || repeat_y)) {
+ /* Not repeating at all, so just plot it */
+ if ((bitmap->width == 1) && (bitmap->height == 1)) {
+ if ((*(bitmap->pixdata + 3) & 0xff) == 0) {
+ return true;
+ }
+ return plot_block((*(COLORREF *)bitmap->pixdata) & 0xffffff, x, y, x + width, y + height);
+
+ } else {
+ return plot_bitmap(bitmap, x, y, width, height);
+ }
+ }
+
+ /* Optimise tiled plots of 1x1 bitmaps by replacing with a flat fill
+ * of the area. Can only be done when image is fully opaque. */
+ if ((bitmap->width == 1) && (bitmap->height == 1)) {
+ if ((*(COLORREF *)bitmap->pixdata & 0xff000000) != 0) {
+ return plot_block((*(COLORREF *)bitmap->pixdata) & 0xffffff,
+ plot_clip.left,
+ plot_clip.top,
+ plot_clip.right,
+ plot_clip.bottom);
+ }
+ }
+
+ /* Optimise tiled plots of bitmaps scaled to 1x1 by replacing with
+ * a flat fill of the area. Can only be done when image is fully
+ * opaque. */
+ if ((width == 1) && (height == 1)) {
+ if (bitmap->opaque) {
+ /** TODO: Currently using top left pixel. Maybe centre
+ * pixel or average value would be better. */
+ return plot_block((*(COLORREF *)bitmap->pixdata) & 0xffffff,
+ plot_clip.left,
+ plot_clip.top,
+ plot_clip.right,
+ plot_clip.bottom);
+ }
+ }
+
+ PLOT_LOG("Tiled plotting %d,%d by %d,%d",x,y,width,height);
+ PLOT_LOG("clipped %ld,%ld to %ld,%ld",plot_clip.left, plot_clip.top, plot_clip.right, plot_clip.bottom);
+
+ /* get left most tile position */
+ if (repeat_x)
+ for (; x > plot_clip.left; x -= width);
+
+ /* get top most tile position */
+ if (repeat_y)
+ for (; y > plot_clip.top; y -= height);
+
+ PLOT_LOG("repeat from %d,%d to %ld,%ld", x, y, plot_clip.right, plot_clip.bottom);
+
+ /* tile down and across to extents */
+ for (xf = x; xf < plot_clip.right; xf += width) {
+ for (yf = y; yf < plot_clip.bottom; yf += height) {
+
+ plot_bitmap(bitmap, xf, yf, width, height);
+ if (!repeat_y)
+ break;
+ }
+ if (!repeat_x)
+ break;
+ }
+ return true;
+}
+
+
+static bool flush(void)
+{
+ PLOT_LOG("flush unimplemented");
+ return true;
+}
+
+static bool path(const float *p, unsigned int n, colour fill, float width,
+ colour c, const float transform[6])
+{
+ PLOT_LOG("path unimplemented");
+ return true;
+}
+
+const struct plotter_table win_plotters = {
+ .rectangle = rectangle,
+ .line = line,
+ .polygon = polygon,
+ .clip = clip,
+ .text = text,
+ .disc = disc,
+ .arc = arc,
+ .bitmap = windows_plot_bitmap,
+ .flush = flush,
+ .path = path,
+ .option_knockout = true,
+};
diff --git a/frontends/windows/plot.h b/frontends/windows/plot.h
new file mode 100644
index 000000000..d69650d2a
--- /dev/null
+++ b/frontends/windows/plot.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <windows.h>
+
+extern const struct plotter_table win_plotters;
+
+extern HDC plot_hdc;
+
diff --git a/frontends/windows/pointers.c b/frontends/windows/pointers.c
new file mode 100644
index 000000000..b5b74545d
--- /dev/null
+++ b/frontends/windows/pointers.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2015 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdbool.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <windows.h>
+
+#include "utils/errors.h"
+#include "utils/nsurl.h"
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "utils/corestrings.h"
+#include "utils/url.h"
+#include "utils/file.h"
+#include "desktop/browser.h"
+#include "desktop/gui_clipboard.h"
+
+#include "windows/schedule.h"
+#include "windows/window.h"
+#include "windows/filetype.h"
+#include "windows/pointers.h"
+
+struct nsws_pointers {
+ HCURSOR hand;
+ HCURSOR ibeam;
+ HCURSOR cross;
+ HCURSOR sizeall;
+ HCURSOR sizewe;
+ HCURSOR sizens;
+ HCURSOR sizenesw;
+ HCURSOR sizenwse;
+ HCURSOR wait;
+ HCURSOR appstarting;
+ HCURSOR no;
+ HCURSOR help;
+ HCURSOR arrow;
+};
+
+/** pre loaded pointer cursors */
+static struct nsws_pointers nsws_pointer;
+
+/* exported interface documented in windows/pointers.h */
+void nsws_window_init_pointers(HINSTANCE hinstance)
+{
+ nsws_pointer.hand = LoadCursor(NULL, IDC_HAND);
+ nsws_pointer.ibeam = LoadCursor(NULL, IDC_IBEAM);
+ nsws_pointer.cross = LoadCursor(NULL, IDC_CROSS);
+ nsws_pointer.sizeall = LoadCursor(NULL, IDC_SIZEALL);
+ nsws_pointer.sizewe = LoadCursor(NULL, IDC_SIZEWE);
+ nsws_pointer.sizens = LoadCursor(NULL, IDC_SIZENS);
+ nsws_pointer.sizenesw = LoadCursor(NULL, IDC_SIZENESW);
+ nsws_pointer.sizenwse = LoadCursor(NULL, IDC_SIZENWSE);
+ nsws_pointer.wait = LoadCursor(NULL, IDC_WAIT);
+ nsws_pointer.appstarting = LoadCursor(NULL, IDC_APPSTARTING);
+ nsws_pointer.no = LoadCursor(NULL, IDC_NO);
+ nsws_pointer.help = LoadCursor(NULL, IDC_HELP);
+ nsws_pointer.arrow = LoadCursor(NULL, IDC_ARROW);
+}
+
+/* exported interface documented in windows/pointers.h */
+HCURSOR nsws_get_pointer(gui_pointer_shape shape)
+{
+ switch (shape) {
+ case GUI_POINTER_POINT: /* link */
+ case GUI_POINTER_MENU:
+ return nsws_pointer.hand;
+
+ case GUI_POINTER_CARET: /* input */
+ return nsws_pointer.ibeam;
+
+ case GUI_POINTER_CROSS:
+ return nsws_pointer.cross;
+
+ case GUI_POINTER_MOVE:
+ return nsws_pointer.sizeall;
+
+ case GUI_POINTER_RIGHT:
+ case GUI_POINTER_LEFT:
+ return nsws_pointer.sizewe;
+
+ case GUI_POINTER_UP:
+ case GUI_POINTER_DOWN:
+ return nsws_pointer.sizens;
+
+ case GUI_POINTER_RU:
+ case GUI_POINTER_LD:
+ return nsws_pointer.sizenesw;
+
+ case GUI_POINTER_RD:
+ case GUI_POINTER_LU:
+ return nsws_pointer.sizenwse;
+
+ case GUI_POINTER_WAIT:
+ return nsws_pointer.wait;
+
+ case GUI_POINTER_PROGRESS:
+ return nsws_pointer.appstarting;
+
+ case GUI_POINTER_NO_DROP:
+ case GUI_POINTER_NOT_ALLOWED:
+ return nsws_pointer.no;
+
+ case GUI_POINTER_HELP:
+ return nsws_pointer.help;
+
+ default:
+ break;
+ }
+
+ return nsws_pointer.arrow;
+}
diff --git a/frontends/windows/pointers.h b/frontends/windows/pointers.h
new file mode 100644
index 000000000..cf91de993
--- /dev/null
+++ b/frontends/windows/pointers.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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/>.
+ */
+
+/**
+ * \file
+ * Windows mouse cursor interface.
+ */
+
+#ifndef _NETSURF_WINDOWS_POINTERS_H_
+#define _NETSURF_WINDOWS_POINTERS_H_
+
+
+/**
+ * initialise the list of mouse cursors
+ */
+void nsws_window_init_pointers(HINSTANCE hinstance);
+
+/**
+ * get a win32 cursor handle for a pointer shape
+ */
+HCURSOR nsws_get_pointer(gui_pointer_shape shape);
+
+
+#endif
diff --git a/frontends/windows/prefs.c b/frontends/windows/prefs.c
new file mode 100644
index 000000000..adc0101a7
--- /dev/null
+++ b/frontends/windows/prefs.c
@@ -0,0 +1,680 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "utils/config.h"
+
+#include <windows.h>
+#include <commctrl.h>
+
+#include "utils/nsoption.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "windows/gui.h"
+#include "windows/prefs.h"
+#include "windows/resourceid.h"
+#include "windows/windbg.h"
+
+#define NSWS_PREFS_WINDOW_WIDTH 600
+#define NSWS_PREFS_WINDOW_HEIGHT 400
+
+static CHOOSEFONT *nsws_prefs_font_prepare(int fontfamily, HWND parent)
+{
+ CHOOSEFONT *cf = malloc(sizeof(CHOOSEFONT));
+ if (cf == NULL) {
+ win32_warning(messages_get("NoMemory"),0);
+ return NULL;
+ }
+ LOGFONT *lf = malloc(sizeof(LOGFONT));
+ if (lf == NULL) {
+ win32_warning(messages_get("NoMemory"),0);
+ free(cf);
+ return NULL;
+ }
+ switch(fontfamily) {
+ case FF_ROMAN:
+ snprintf(lf->lfFaceName, LF_FACESIZE, "%s",
+ nsoption_charp(font_serif));
+ break;
+ case FF_MODERN:
+ snprintf(lf->lfFaceName, LF_FACESIZE, "%s",
+ nsoption_charp(font_mono));
+ break;
+ case FF_SCRIPT:
+ snprintf(lf->lfFaceName, LF_FACESIZE, "%s",
+ nsoption_charp(font_cursive));
+ break;
+ case FF_DECORATIVE:
+ snprintf(lf->lfFaceName, LF_FACESIZE, "%s",
+ nsoption_charp(font_fantasy));
+ break;
+ case FF_SWISS:
+ default:
+ snprintf(lf->lfFaceName, LF_FACESIZE, "%s",
+ nsoption_charp(font_sans));
+ break;
+ }
+
+ cf->lStructSize = sizeof(CHOOSEFONT);
+ cf->hwndOwner = parent;
+ cf->lpLogFont = lf;
+ cf->Flags = CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT | CF_LIMITSIZE;
+ cf->nSizeMin = 16;
+ cf->nSizeMax = 24;
+
+ return cf;
+}
+
+static void change_spinner(HWND sub, double change, double minval, double maxval)
+{
+ char *temp, number[6];
+ int len;
+ double value = 0;
+
+ len = SendMessage(sub, WM_GETTEXTLENGTH, 0, 0);
+ temp = malloc(len + 1);
+
+ if (temp == NULL)
+ return;
+
+ SendMessage(sub, WM_GETTEXT, (WPARAM)(len + 1), (LPARAM) temp);
+
+ value = strtod(temp, NULL) - change;
+
+ free(temp);
+ value = max(value, minval);
+ value = min(value, maxval);
+
+ if ((change == 1.0) || (change == -1.0))
+ snprintf(number, 6, "%.0f", value);
+ else
+ snprintf(number, 6, "%.1f", value);
+
+ SendMessage(sub, WM_SETTEXT, 0, (LPARAM)number);
+}
+
+
+static BOOL CALLBACK options_appearance_dialog_handler(HWND hwnd,
+ UINT msg, WPARAM wparam, LPARAM lParam)
+{
+ int len;
+ char *temp, number[6];
+ HWND sub;
+
+ LOG_WIN_MSG(hwnd, msg, wparam, lParam);
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ sub = GetDlgItem(hwnd, IDC_PREFS_FONTDEF);
+ SendMessage(sub, CB_ADDSTRING, 0, (LPARAM)"Sans serif");
+ SendMessage(sub, CB_ADDSTRING, 0, (LPARAM)"Serif");
+ SendMessage(sub, CB_ADDSTRING, 0, (LPARAM)"Monospace");
+ SendMessage(sub, CB_ADDSTRING, 0, (LPARAM)"Cursive");
+ SendMessage(sub, CB_ADDSTRING, 0, (LPARAM)"Fantasy");
+ SendMessage(sub, CB_SETCURSEL,
+ (WPARAM) (nsoption_int(font_default) - 1), 0);
+
+ if ((nsoption_charp(font_sans) != NULL) &&
+ (nsoption_charp(font_sans)[0] != '\0')) {
+ sub = GetDlgItem(hwnd, IDC_PREFS_SANS);
+ SendMessage(sub, WM_SETTEXT, 0,
+ (LPARAM)nsoption_charp(font_sans));
+ }
+ if ((nsoption_charp(font_serif) != NULL) &&
+ (nsoption_charp(font_serif)[0] != '\0')) {
+ sub = GetDlgItem(hwnd, IDC_PREFS_SERIF);
+ SendMessage(sub, WM_SETTEXT, 0,
+ (LPARAM)nsoption_charp(font_serif));
+ }
+ if ((nsoption_charp(font_mono) != NULL) &&
+ (nsoption_charp(font_mono)[0] != '\0')) {
+ sub = GetDlgItem(hwnd, IDC_PREFS_MONO);
+ SendMessage(sub, WM_SETTEXT, 0,
+ (LPARAM)nsoption_charp(font_mono));
+ }
+ if ((nsoption_charp(font_cursive) != NULL) &&
+ (nsoption_charp(font_cursive)[0] != '\0')) {
+ sub = GetDlgItem(hwnd, IDC_PREFS_CURSIVE);
+ SendMessage(sub, WM_SETTEXT, 0,
+ (LPARAM)nsoption_charp(font_cursive));
+ }
+ if ((nsoption_charp(font_fantasy) != NULL) &&
+ (nsoption_charp(font_fantasy)[0] != '\0')) {
+ sub = GetDlgItem(hwnd, IDC_PREFS_FANTASY);
+ SendMessage(sub, WM_SETTEXT, 0,
+ (LPARAM)nsoption_charp(font_fantasy));
+ }
+ if (nsoption_int(font_min_size) != 0) {
+ sub = GetDlgItem(hwnd, IDC_PREFS_FONT_MINSIZE);
+ snprintf(number, 6, "%.1f", nsoption_int(font_min_size) / 10.0);
+ SendMessage(sub, WM_SETTEXT, 0, (LPARAM)number);
+ }
+ if (nsoption_int(font_size) != 0) {
+ sub = GetDlgItem(hwnd, IDC_PREFS_FONT_SIZE);
+ snprintf(number, 6, "%.1f", nsoption_int(font_size) / 10.0);
+ SendMessage(sub, WM_SETTEXT, 0, (LPARAM)number);
+ }
+ if (nsoption_int(max_fetchers) != 0) {
+ sub = GetDlgItem(hwnd, IDC_PREFS_FETCHERS);
+ snprintf(number, 6, "%d", nsoption_int(max_fetchers));
+ SendMessage(sub, WM_SETTEXT, 0, (LPARAM)number);
+ }
+ if (nsoption_int(max_fetchers_per_host) != 0) {
+ sub = GetDlgItem(hwnd, IDC_PREFS_FETCH_HOST);
+ snprintf(number, 6, "%d",
+ nsoption_int(max_fetchers_per_host));
+ SendMessage(sub, WM_SETTEXT, 0, (LPARAM)number);
+ }
+ if (nsoption_int(max_cached_fetch_handles) != 0) {
+ sub = GetDlgItem(hwnd, IDC_PREFS_FETCH_HANDLES);
+ snprintf(number, 6, "%d",
+ nsoption_int(max_cached_fetch_handles));
+ SendMessage(sub, WM_SETTEXT, 0, (LPARAM)number);
+ }
+
+
+ /* animation */
+ sub = GetDlgItem(hwnd, IDC_PREFS_NOANIMATION);
+ SendMessage(sub, BM_SETCHECK, (WPARAM)((nsoption_bool(animate_images))
+ ? BST_UNCHECKED : BST_CHECKED), 0);
+
+ if (nsoption_int(minimum_gif_delay) != 0) {
+ sub = GetDlgItem(hwnd, IDC_PREFS_ANIMATIONDELAY);
+ snprintf(number, 6, "%.1f", nsoption_int(minimum_gif_delay) /
+ 100.0);
+ SendMessage(sub, WM_SETTEXT, 0, (LPARAM)number);
+ }
+ break;
+
+ case WM_NOTIFY:
+ switch (((NMHDR FAR *)lParam)->code) {
+ case PSN_APPLY:
+ sub = GetDlgItem(hwnd, IDC_PREFS_FONT_SIZE);
+ len = SendMessage(sub, WM_GETTEXTLENGTH, 0, 0);
+ temp = malloc(len + 1);
+ if (temp != NULL) {
+ SendMessage(sub, WM_GETTEXT, (WPARAM)
+ (len + 1), (LPARAM) temp);
+ nsoption_int(font_size) = (int)
+ (10 * strtod(temp, NULL));
+ free(temp);
+ }
+
+ sub = GetDlgItem(hwnd, IDC_PREFS_FONT_MINSIZE);
+ len = SendMessage(sub, WM_GETTEXTLENGTH, 0, 0);
+ temp = malloc(len + 1);
+ if (temp != NULL) {
+ SendMessage(sub, WM_GETTEXT, (WPARAM)
+ (len + 1), (LPARAM) temp);
+ nsoption_set_int(font_min_size,
+ (int)(10 * strtod(temp, NULL)));
+ free(temp);
+ }
+
+ /* animation */
+ nsoption_set_bool(animate_images,
+ (IsDlgButtonChecked(hwnd, IDC_PREFS_NOANIMATION) == BST_CHECKED) ? true : false);
+
+
+ sub = GetDlgItem(hwnd, IDC_PREFS_ANIMATIONDELAY);
+ len = SendMessage(sub, WM_GETTEXTLENGTH, 0, 0);
+ temp = malloc(len + 1);
+ if (temp != NULL) {
+ SendMessage(sub, WM_GETTEXT, (WPARAM)
+ (len + 1), (LPARAM) temp);
+ nsoption_set_int(minimum_gif_delay,
+ (int)(100 * strtod(temp, NULL)));
+ free(temp);
+ }
+
+ break;
+
+ case UDN_DELTAPOS: {
+ NMUPDOWN *ud = (NMUPDOWN *)lParam;
+ switch(((NMHDR *)lParam)->idFrom) {
+ case IDC_PREFS_FONT_SIZE_SPIN:
+ change_spinner(GetDlgItem(hwnd, IDC_PREFS_FONT_SIZE), 0.1 * ud->iDelta, 1.0, 50.0);
+ return TRUE;
+
+ case IDC_PREFS_FONT_MINSIZE_SPIN:
+ change_spinner(GetDlgItem(hwnd, IDC_PREFS_FONT_MINSIZE), 0.1 * ud->iDelta, 1.0, 50.0);
+ return TRUE;
+
+ case IDC_PREFS_ANIMATIONDELAY_SPIN:
+ change_spinner(GetDlgItem(hwnd, IDC_PREFS_ANIMATIONDELAY), 0.1 * ud->iDelta, 0.1, 100.0);
+ return TRUE;
+
+ }
+ }
+ break;
+ }
+
+
+ case WM_COMMAND:
+ LOG("WM_COMMAND Identifier 0x%x",LOWORD(wparam));
+
+ switch(LOWORD(wparam)) {
+ case IDC_PREFS_PROXYTYPE:
+ sub = GetDlgItem(hwnd, IDC_PREFS_PROXYTYPE);
+ nsoption_set_int(http_proxy_auth,
+ SendMessage(sub, CB_GETCURSEL, 0, 0) - 1);
+ nsoption_set_bool(http_proxy,
+ (nsoption_int(http_proxy_auth) != -1));
+ nsoption_set_int(http_proxy_auth,
+ nsoption_int(http_proxy_auth) +
+ (nsoption_bool(http_proxy)) ? 0 : 1);
+ break;
+
+ case IDC_PREFS_SANS: {
+ CHOOSEFONT *cf = nsws_prefs_font_prepare(FF_SWISS, hwnd);
+ if (cf == NULL) {
+ break;
+ }
+
+ if (ChooseFont(cf) == TRUE) {
+ nsoption_set_charp(font_sans,
+ strdup(cf->lpLogFont->lfFaceName));
+ }
+
+ free(cf->lpLogFont);
+ free(cf);
+ if ((nsoption_charp(font_sans) != NULL) &&
+ (nsoption_charp(font_sans)[0] != '\0')) {
+ sub = GetDlgItem(hwnd, IDC_PREFS_SANS);
+ SendMessage(sub, WM_SETTEXT, 0,
+ (LPARAM)nsoption_charp(font_sans));
+ }
+ break;
+ }
+
+ case IDC_PREFS_SERIF: {
+ CHOOSEFONT *cf = nsws_prefs_font_prepare(FF_ROMAN, hwnd);
+ if (cf == NULL) {
+ break;
+ }
+
+ if (ChooseFont(cf) == TRUE) {
+ nsoption_set_charp(font_serif,
+ strdup(cf->lpLogFont->lfFaceName));
+ }
+
+ free(cf->lpLogFont);
+ free(cf);
+ if ((nsoption_charp(font_serif) != NULL) &&
+ (nsoption_charp(font_serif)[0] != '\0')) {
+ sub = GetDlgItem(hwnd, IDC_PREFS_SERIF);
+ SendMessage(sub, WM_SETTEXT, 0,
+ (LPARAM)nsoption_charp(font_serif));
+ }
+ break;
+ }
+
+ case IDC_PREFS_MONO: {
+ CHOOSEFONT *cf = nsws_prefs_font_prepare(FF_MODERN, hwnd);
+ if (cf == NULL) {
+ break;
+ }
+
+ if (ChooseFont(cf) == TRUE) {
+ nsoption_set_charp(font_mono,
+ strdup(cf->lpLogFont->lfFaceName));
+ }
+
+ free(cf->lpLogFont);
+ free(cf);
+
+ if ((nsoption_charp(font_mono) != NULL) &&
+ (nsoption_charp(font_mono)[0] != '\0')) {
+ sub = GetDlgItem(hwnd, IDC_PREFS_MONO);
+ SendMessage(sub, WM_SETTEXT, 0,
+ (LPARAM)nsoption_charp(font_mono));
+ }
+ break;
+ }
+
+ case IDC_PREFS_CURSIVE: {
+ CHOOSEFONT *cf = nsws_prefs_font_prepare(FF_SCRIPT, hwnd);
+ if (cf == NULL) {
+ break;
+ }
+
+ if (ChooseFont(cf) == TRUE) {
+ nsoption_set_charp(font_cursive,
+ strdup(cf->lpLogFont->lfFaceName));
+ }
+ free(cf->lpLogFont);
+ free(cf);
+ if ((nsoption_charp(font_cursive) != NULL) &&
+ (nsoption_charp(font_cursive)[0] != '\0')) {
+ sub = GetDlgItem(hwnd, IDC_PREFS_CURSIVE);
+ SendMessage(sub, WM_SETTEXT, 0,
+ (LPARAM)nsoption_charp(font_cursive));
+ }
+ break;
+ }
+
+ case IDC_PREFS_FANTASY: {
+ CHOOSEFONT *cf = nsws_prefs_font_prepare(FF_DECORATIVE, hwnd);
+ if (cf == NULL) {
+ break;
+ }
+
+ if (ChooseFont(cf) == TRUE) {
+ nsoption_set_charp(font_fantasy,
+ strdup(cf->lpLogFont->lfFaceName));
+ }
+ free(cf->lpLogFont);
+ free(cf);
+ if ((nsoption_charp(font_fantasy) != NULL) &&
+ (nsoption_charp(font_fantasy)[0] != '\0')) {
+ sub = GetDlgItem(hwnd, IDC_PREFS_FANTASY);
+ SendMessage(sub, WM_SETTEXT, 0,
+ (LPARAM)nsoption_charp(font_fantasy));
+ }
+ break;
+ }
+
+ case IDC_PREFS_FONTDEF:
+ sub = GetDlgItem(hwnd, IDC_PREFS_FONTDEF);
+ nsoption_set_int(font_default,
+ SendMessage(sub, CB_GETCURSEL, 0, 0) + 1);
+ break;
+
+ }
+ break;
+
+ }
+ return FALSE;
+}
+
+static BOOL CALLBACK options_connections_dialog_handler(HWND hwnd,
+ UINT msg, WPARAM wparam, LPARAM lParam)
+{
+ int len;
+ char *temp, number[6];
+ HWND sub;
+
+ LOG_WIN_MSG(hwnd, msg, wparam, lParam);
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ sub = GetDlgItem(hwnd, IDC_PREFS_PROXYTYPE);
+ SendMessage(sub, CB_ADDSTRING, 0, (LPARAM)"None");
+ SendMessage(sub, CB_ADDSTRING, 0, (LPARAM)"Simple");
+ SendMessage(sub, CB_ADDSTRING, 0, (LPARAM)"Basic Auth");
+ SendMessage(sub, CB_ADDSTRING, 0, (LPARAM)"NTLM Auth");
+ if (nsoption_bool(http_proxy)) {
+ SendMessage(sub, CB_SETCURSEL, (WPARAM)
+ (nsoption_int(http_proxy_auth) + 1), 0);
+ } else {
+ SendMessage(sub, CB_SETCURSEL, 0, 0);
+ }
+
+ sub = GetDlgItem(hwnd, IDC_PREFS_PROXYHOST);
+ if ((nsoption_charp(http_proxy_host) != NULL) &&
+ (nsoption_charp(http_proxy_host)[0] != '\0'))
+ SendMessage(sub, WM_SETTEXT, 0,
+ (LPARAM)nsoption_charp(http_proxy_host));
+
+ sub = GetDlgItem(hwnd, IDC_PREFS_PROXYPORT);
+ if (nsoption_int(http_proxy_port) != 0) {
+ snprintf(number, 6, "%d", nsoption_int(http_proxy_port));
+ SendMessage(sub, WM_SETTEXT, 0, (LPARAM)number);
+ }
+
+ sub = GetDlgItem(hwnd, IDC_PREFS_PROXYNAME);
+ if ((nsoption_charp(http_proxy_auth_user) != NULL) &&
+ (nsoption_charp(http_proxy_auth_user)[0] != '\0'))
+ SendMessage(sub, WM_SETTEXT, 0,
+ (LPARAM)nsoption_charp(http_proxy_auth_user));
+
+ sub = GetDlgItem(hwnd, IDC_PREFS_PROXYPASS);
+ if ((nsoption_charp(http_proxy_auth_pass) != NULL) &&
+ (nsoption_charp(http_proxy_auth_pass)[0] != '\0'))
+ SendMessage(sub, WM_SETTEXT, 0,
+ (LPARAM)nsoption_charp(http_proxy_auth_pass));
+
+ sub = GetDlgItem(hwnd, IDC_PREFS_FETCHERS);
+ snprintf(number, 6, "%d", nsoption_int(max_fetchers));
+ SendMessage(sub, WM_SETTEXT, 0, (LPARAM)number);
+
+ sub = GetDlgItem(hwnd, IDC_PREFS_FETCH_HOST);
+ snprintf(number, 6, "%d", nsoption_int(max_fetchers_per_host));
+ SendMessage(sub, WM_SETTEXT, 0, (LPARAM)number);
+
+ sub = GetDlgItem(hwnd, IDC_PREFS_FETCH_HANDLES);
+ snprintf(number, 6, "%d", nsoption_int(max_cached_fetch_handles));
+ SendMessage(sub, WM_SETTEXT, 0, (LPARAM)number);
+
+ break;
+
+ case WM_NOTIFY:
+ switch (((NMHDR FAR *)lParam)->code) {
+ case PSN_APPLY:
+ sub = GetDlgItem(hwnd, IDC_PREFS_PROXYHOST);
+ len = SendMessage(sub, WM_GETTEXTLENGTH, 0, 0);
+ temp = malloc(len + 1);
+ if (temp != NULL) {
+ SendMessage(sub, WM_GETTEXT, (WPARAM)(len + 1),
+ (LPARAM)temp);
+ nsoption_set_charp(http_proxy_host, strdup(temp));
+ free(temp);
+ }
+
+ sub = GetDlgItem(hwnd, IDC_PREFS_PROXYPORT);
+ len = SendMessage(sub, WM_GETTEXTLENGTH, 0, 0);
+ temp = malloc(len + 1);
+ if (temp != NULL) {
+ SendMessage(sub, WM_GETTEXT, (WPARAM)(len + 1),
+ (LPARAM)temp);
+ nsoption_set_int(http_proxy_port, atoi(temp));
+ free(temp);
+ }
+
+ sub = GetDlgItem(hwnd, IDC_PREFS_PROXYNAME);
+ len = SendMessage(sub, WM_GETTEXTLENGTH, 0, 0);
+ temp = malloc(len + 1);
+ if (temp != NULL) {
+ SendMessage(sub, WM_GETTEXT, (WPARAM)(len + 1),
+ (LPARAM)temp);
+ nsoption_set_charp(http_proxy_auth_user, strdup(temp));
+ free(temp);
+ }
+
+ sub = GetDlgItem(hwnd, IDC_PREFS_PROXYPASS);
+ len = SendMessage(sub, WM_GETTEXTLENGTH, 0, 0);
+ temp = malloc(len + 1);
+ if (temp != NULL) {
+ SendMessage(sub, WM_GETTEXT, (WPARAM)(len + 1),
+ (LPARAM)temp);
+ nsoption_set_charp(http_proxy_auth_pass, strdup(temp));
+ free(temp);
+ }
+
+ /* fetchers */
+ sub = GetDlgItem(hwnd, IDC_PREFS_FETCHERS);
+ len = SendMessage(sub, WM_GETTEXTLENGTH, 0, 0);
+ temp = malloc(len + 1);
+ if (temp != NULL) {
+ SendMessage(sub, WM_GETTEXT, (WPARAM)(len + 1),
+ (LPARAM)temp);
+ nsoption_set_int(max_fetchers, atoi(temp));
+ free(temp);
+ }
+
+ sub = GetDlgItem(hwnd, IDC_PREFS_FETCH_HOST);
+ len = SendMessage(sub, WM_GETTEXTLENGTH, 0, 0);
+ temp = malloc(len + 1);
+ if (temp != NULL) {
+ SendMessage(sub, WM_GETTEXT, (WPARAM)(len + 1),
+ (LPARAM)temp);
+ nsoption_set_int(max_fetchers_per_host, atoi(temp));
+ free(temp);
+ }
+
+ sub = GetDlgItem(hwnd, IDC_PREFS_FETCH_HANDLES);
+ len = SendMessage(sub, WM_GETTEXTLENGTH, 0, 0);
+ temp = malloc(len + 1);
+ if (temp != NULL) {
+ SendMessage(sub, WM_GETTEXT, (WPARAM)(len + 1),
+ (LPARAM)temp);
+ nsoption_set_int(max_cached_fetch_handles, atoi(temp));
+ free(temp);
+ }
+ break;
+
+ case UDN_DELTAPOS: {
+ NMUPDOWN *ud = (NMUPDOWN *)lParam;
+ switch(((NMHDR *)lParam)->idFrom) {
+ case IDC_PREFS_FETCHERS_SPIN:
+ change_spinner(GetDlgItem(hwnd, IDC_PREFS_FETCHERS), 1.0 * ud->iDelta, 1.0, 100.0);
+ return TRUE;
+
+ case IDC_PREFS_FETCH_HOST_SPIN:
+ change_spinner(GetDlgItem(hwnd, IDC_PREFS_FETCH_HOST), 1.0 * ud->iDelta, 1.0, 100.0);
+ return TRUE;
+
+ case IDC_PREFS_FETCH_HANDLES_SPIN:
+ change_spinner(GetDlgItem(hwnd, IDC_PREFS_FETCH_HANDLES), 1.0 * ud->iDelta, 1.0, 100.0);
+ return TRUE;
+
+ }
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
+
+static BOOL CALLBACK options_general_dialog_handler(HWND hwnd,
+ UINT msg, WPARAM wparam, LPARAM lParam)
+{
+ HWND sub;
+
+ LOG_WIN_MSG(hwnd, msg, wparam, lParam);
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ /* homepage url */
+ sub = GetDlgItem(hwnd, IDC_PREFS_HOMEPAGE);
+ SendMessage(sub, WM_SETTEXT, 0, (LPARAM)nsoption_charp(homepage_url));
+
+ /* Display images */
+ sub = GetDlgItem(hwnd, IDC_PREFS_IMAGES);
+ SendMessage(sub, BM_SETCHECK,
+ (WPARAM) ((nsoption_bool(suppress_images)) ?
+ BST_CHECKED : BST_UNCHECKED), 0);
+
+ /* advert blocking */
+ sub = GetDlgItem(hwnd, IDC_PREFS_ADVERTS);
+ SendMessage(sub, BM_SETCHECK,
+ (WPARAM) ((nsoption_bool(block_advertisements)) ?
+ BST_CHECKED : BST_UNCHECKED), 0);
+
+ /* Referrer sending */
+ sub = GetDlgItem(hwnd, IDC_PREFS_REFERER);
+ SendMessage(sub, BM_SETCHECK,
+ (WPARAM)((nsoption_bool(send_referer)) ?
+ BST_CHECKED : BST_UNCHECKED), 0);
+ break;
+
+ case WM_NOTIFY:
+ switch (((NMHDR FAR *)lParam)->code) {
+ case PSN_APPLY:
+ /* homepage */
+ sub = GetDlgItem(hwnd, IDC_PREFS_HOMEPAGE);
+ if (sub != NULL) {
+ int text_length;
+ char *text;
+ text_length = SendMessage(sub,
+ WM_GETTEXTLENGTH, 0, 0);
+ text = malloc(text_length + 1);
+ if (text != NULL) {
+ SendMessage(sub, WM_GETTEXT,
+ (WPARAM)text_length + 1,
+ (LPARAM)text);
+ nsoption_set_charp(homepage_url, text);
+ }
+ }
+
+ nsoption_set_bool(suppress_images,
+ (IsDlgButtonChecked(hwnd, IDC_PREFS_IMAGES) == BST_CHECKED) ? true : false);
+
+ nsoption_set_bool(block_advertisements, (IsDlgButtonChecked(hwnd,
+ IDC_PREFS_ADVERTS) == BST_CHECKED) ? true : false);
+
+ nsoption_set_bool(send_referer, (IsDlgButtonChecked(hwnd,
+ IDC_PREFS_REFERER) == BST_CHECKED) ? true : false);
+
+ break;
+
+ }
+ }
+ return FALSE;
+}
+
+void nsws_prefs_dialog_init(HINSTANCE hinst, HWND parent)
+{
+ int ret;
+ PROPSHEETPAGE psp[3];
+ PROPSHEETHEADER psh;
+
+ psp[0].dwSize = sizeof(PROPSHEETPAGE);
+ psp[0].dwFlags = 0;/*PSP_USEICONID*/
+ psp[0].hInstance = hinst;
+ psp[0].pszTemplate = MAKEINTRESOURCE(IDD_DLG_OPTIONS_GENERAL);
+ psp[0].pfnDlgProc = options_general_dialog_handler;
+ psp[0].lParam = 0;
+ psp[0].pfnCallback = NULL;
+
+ psp[1].dwSize = sizeof(PROPSHEETPAGE);
+ psp[1].dwFlags = 0;/*PSP_USEICONID*/
+ psp[1].hInstance = hinst;
+ psp[1].pszTemplate = MAKEINTRESOURCE(IDD_DLG_OPTIONS_CONNECTIONS);
+ psp[1].pfnDlgProc = options_connections_dialog_handler;
+ psp[1].lParam = 0;
+ psp[1].pfnCallback = NULL;
+
+ psp[2].dwSize = sizeof(PROPSHEETPAGE);
+ psp[2].dwFlags = 0;/*PSP_USEICONID*/
+ psp[2].hInstance = hinst;
+ psp[2].pszTemplate = MAKEINTRESOURCE(IDD_DLG_OPTIONS_APPERANCE);
+ psp[2].pfnDlgProc = options_appearance_dialog_handler;
+ psp[2].lParam = 0;
+ psp[2].pfnCallback = NULL;
+
+
+ psh.dwSize = sizeof(PROPSHEETHEADER);
+ psh.dwFlags = PSH_NOAPPLYNOW | PSH_USEICONID | PSH_PROPSHEETPAGE;
+ psh.hwndParent = parent;
+ psh.hInstance = hinst;
+ psh.pszIcon = MAKEINTRESOURCE(IDR_NETSURF_ICON);
+ psh.pszCaption = (LPSTR) "NetSurf Options";
+ psh.nPages = sizeof(psp) / sizeof(PROPSHEETPAGE);
+ psh.nStartPage = 0;
+ psh.ppsp = (LPCPROPSHEETPAGE) &psp;
+ psh.pfnCallback = NULL;
+
+ ret = PropertySheet(&psh);
+ if (ret == -1) {
+ win_perror("PropertySheet");
+ } else if (ret > 0) {
+ /* user saved changes */
+ nsoption_write(options_file_location, NULL, NULL);
+ }
+}
diff --git a/frontends/windows/prefs.h b/frontends/windows/prefs.h
new file mode 100644
index 000000000..dec004b60
--- /dev/null
+++ b/frontends/windows/prefs.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_WINDOWS_PREFS_H_
+#define _NETSURF_WINDOWS_PREFS_H_
+
+void nsws_prefs_dialog_init(HINSTANCE hinst, HWND parent);
+
+#endif
diff --git a/frontends/windows/res/NetSurf.ico b/frontends/windows/res/NetSurf.ico
new file mode 100644
index 000000000..e0c3d01e6
--- /dev/null
+++ b/frontends/windows/res/NetSurf.ico
Binary files differ
diff --git a/frontends/windows/res/adblock.css b/frontends/windows/res/adblock.css
new file mode 120000
index 000000000..ff2485622
--- /dev/null
+++ b/frontends/windows/res/adblock.css
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/AdBlock,f79 \ No newline at end of file
diff --git a/frontends/windows/res/banner.bmp b/frontends/windows/res/banner.bmp
new file mode 100644
index 000000000..49525f83d
--- /dev/null
+++ b/frontends/windows/res/banner.bmp
Binary files differ
diff --git a/frontends/windows/res/ca-bundle.crt b/frontends/windows/res/ca-bundle.crt
new file mode 120000
index 000000000..0b0e416ad
--- /dev/null
+++ b/frontends/windows/res/ca-bundle.crt
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/ca-bundle \ No newline at end of file
diff --git a/frontends/windows/res/credits.html b/frontends/windows/res/credits.html
new file mode 120000
index 000000000..1ba17392b
--- /dev/null
+++ b/frontends/windows/res/credits.html
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/en/credits.html,faf \ No newline at end of file
diff --git a/frontends/windows/res/default.css b/frontends/windows/res/default.css
new file mode 120000
index 000000000..a8579eb7c
--- /dev/null
+++ b/frontends/windows/res/default.css
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/CSS,f79 \ No newline at end of file
diff --git a/frontends/windows/res/home.bmp b/frontends/windows/res/home.bmp
new file mode 100644
index 000000000..1f595ff4c
--- /dev/null
+++ b/frontends/windows/res/home.bmp
Binary files differ
diff --git a/frontends/windows/res/icons/back.png b/frontends/windows/res/icons/back.png
new file mode 100644
index 000000000..f219fd807
--- /dev/null
+++ b/frontends/windows/res/icons/back.png
Binary files differ
diff --git a/frontends/windows/res/icons/back_g.png b/frontends/windows/res/icons/back_g.png
new file mode 100644
index 000000000..0796bbbbf
--- /dev/null
+++ b/frontends/windows/res/icons/back_g.png
Binary files differ
diff --git a/frontends/windows/res/icons/back_h.png b/frontends/windows/res/icons/back_h.png
new file mode 100644
index 000000000..35e31386a
--- /dev/null
+++ b/frontends/windows/res/icons/back_h.png
Binary files differ
diff --git a/frontends/windows/res/icons/forward.png b/frontends/windows/res/icons/forward.png
new file mode 100644
index 000000000..f20c0cdf5
--- /dev/null
+++ b/frontends/windows/res/icons/forward.png
Binary files differ
diff --git a/frontends/windows/res/icons/forward_g.png b/frontends/windows/res/icons/forward_g.png
new file mode 100644
index 000000000..d847543f3
--- /dev/null
+++ b/frontends/windows/res/icons/forward_g.png
Binary files differ
diff --git a/frontends/windows/res/icons/forward_h.png b/frontends/windows/res/icons/forward_h.png
new file mode 100644
index 000000000..90c0fe2c7
--- /dev/null
+++ b/frontends/windows/res/icons/forward_h.png
Binary files differ
diff --git a/frontends/windows/res/icons/home.png b/frontends/windows/res/icons/home.png
new file mode 100644
index 000000000..604796025
--- /dev/null
+++ b/frontends/windows/res/icons/home.png
Binary files differ
diff --git a/frontends/windows/res/icons/home_g.png b/frontends/windows/res/icons/home_g.png
new file mode 100644
index 000000000..a644b0b03
--- /dev/null
+++ b/frontends/windows/res/icons/home_g.png
Binary files differ
diff --git a/frontends/windows/res/icons/home_h.png b/frontends/windows/res/icons/home_h.png
new file mode 100644
index 000000000..2d6be5f34
--- /dev/null
+++ b/frontends/windows/res/icons/home_h.png
Binary files differ
diff --git a/frontends/windows/res/icons/reload.png b/frontends/windows/res/icons/reload.png
new file mode 100644
index 000000000..a81f650b0
--- /dev/null
+++ b/frontends/windows/res/icons/reload.png
Binary files differ
diff --git a/frontends/windows/res/icons/reload_g.png b/frontends/windows/res/icons/reload_g.png
new file mode 100644
index 000000000..5251f206c
--- /dev/null
+++ b/frontends/windows/res/icons/reload_g.png
Binary files differ
diff --git a/frontends/windows/res/icons/reload_h.png b/frontends/windows/res/icons/reload_h.png
new file mode 100644
index 000000000..76e554e49
--- /dev/null
+++ b/frontends/windows/res/icons/reload_h.png
Binary files differ
diff --git a/frontends/windows/res/icons/stop.png b/frontends/windows/res/icons/stop.png
new file mode 100644
index 000000000..df64c5747
--- /dev/null
+++ b/frontends/windows/res/icons/stop.png
Binary files differ
diff --git a/frontends/windows/res/icons/stop_g.png b/frontends/windows/res/icons/stop_g.png
new file mode 100644
index 000000000..a2efa9e3d
--- /dev/null
+++ b/frontends/windows/res/icons/stop_g.png
Binary files differ
diff --git a/frontends/windows/res/icons/stop_h.png b/frontends/windows/res/icons/stop_h.png
new file mode 100644
index 000000000..3c3377cdf
--- /dev/null
+++ b/frontends/windows/res/icons/stop_h.png
Binary files differ
diff --git a/frontends/windows/res/installer.nsi b/frontends/windows/res/installer.nsi
new file mode 100644
index 000000000..0c733db2e
--- /dev/null
+++ b/frontends/windows/res/installer.nsi
@@ -0,0 +1,141 @@
+# This installs NetSurf execuatables and resources, creates a start menu shortcut, builds an uninstaller, and
+# adds uninstall information to the registry for Add/Remove Programs
+
+# show up in a few places.
+# All the other settings can be tweaked by editing the !defines at the top of this script
+!define APPNAME "NetSurf"
+!define COMPANYNAME "NetSurf"
+!define DESCRIPTION "Web Browser"
+# These three must be integers
+!define VERSIONMAJOR 3
+!define VERSIONMINOR 6
+!define VERSIONBUILD 1
+# These will be displayed by the "Click here for support information" link in "Add/Remove Programs"
+# It is possible to use "mailto:" links in here to open the email client
+!define HELPURL "http://www.netsurf-browser.org/" # "Support Information" link
+!define UPDATEURL "http://www.netsurf-browser.org/" # "Product Updates" link
+!define ABOUTURL "http://www.netsurf-browser.org/" # "Publisher" link
+# This is the size (in kB) of all the files copied into "Program Files"
+!define INSTALLSIZE 9000
+
+RequestExecutionLevel admin ;Require admin rights on NT6+ (When UAC is turned on)
+
+InstallDir "$PROGRAMFILES\${COMPANYNAME}\${APPNAME}"
+
+# rtf or txt file - remember if it is txt, it must be in the DOS text format (\r\n)
+LicenseData "COPYING"
+# This will be in the installer/uninstaller's title bar
+Name "${COMPANYNAME} - ${APPNAME}"
+Icon "frontends\windows\res\NetSurf.ico"
+outFile "netsurf-installer.exe"
+BrandingText "${COMPANYNAME}"
+
+!include LogicLib.nsh
+
+# Just three pages - license agreement, install location, and installation
+page license
+page directory
+Page instfiles
+
+!macro VerifyUserIsAdmin
+UserInfo::GetAccountType
+pop $0
+${If} $0 != "admin" ;Require admin rights on NT4+
+ messageBox mb_iconstop "Administrator rights required!"
+ setErrorLevel 740 ;ERROR_ELEVATION_REQUIRED
+ quit
+${EndIf}
+!macroend
+
+function .onInit
+ setShellVarContext all
+ !insertmacro VerifyUserIsAdmin
+functionEnd
+
+section "install"
+ # Files for the install directory - to build the installer, these should be in the same directory as the install script (this file)
+ setOutPath $INSTDIR
+ # Files added here should be removed by the uninstaller (see section "uninstall")
+ file "NetSurf.exe"
+ file /oname=NetSurf.ico "frontends\windows\res\NetSurf.ico"
+ file /oname=default.css "frontends\windows\res\default.css"
+ file /oname=internal.css "frontends\windows\res\internal.css"
+ file /oname=adblock.css "frontends\windows\res\adblock.css"
+ file /oname=welcome.html "frontends\windows\res\welcome.html"
+ file /oname=credits.html "frontends\windows\res\credits.html"
+ file /oname=licence.html "frontends\windows\res\licence.html"
+ file /oname=netsurf.png "frontends\windows\res\netsurf.png"
+ file /oname=messages "build-Linux-windows\messages"
+ file /oname=ca-bundle.crt "frontends\windows\res\ca-bundle.crt"
+ # Add any other files for the install directory (license files, app data, etc) here
+
+ # Uninstaller - See function un.onInit and section "uninstall" for configuration
+ writeUninstaller "$INSTDIR\uninstall.exe"
+
+ # Start Menu
+ createDirectory "$SMPROGRAMS\${COMPANYNAME}"
+ createShortCut "$SMPROGRAMS\${COMPANYNAME}\${APPNAME}.lnk" "$INSTDIR\NetSurf.exe" "" "$INSTDIR\NetSurf.ico"
+
+ # Registry information for add/remove programs
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayName" "${COMPANYNAME} - ${APPNAME} - ${DESCRIPTION}"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "InstallLocation" "$\"$INSTDIR$\""
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayIcon" "$\"$INSTDIR\NetSurf.ico$\""
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "Publisher" "$\"${COMPANYNAME}$\""
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "HelpLink" "$\"${HELPURL}$\""
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "URLUpdateInfo" "$\"${UPDATEURL}$\""
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "URLInfoAbout" "$\"${ABOUTURL}$\""
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayVersion" "$\"${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONBUILD}$\""
+ WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "VersionMajor" ${VERSIONMAJOR}
+ WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "VersionMinor" ${VERSIONMINOR}
+ # There is no option for modifying or repairing the install
+ WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "NoModify" 1
+ WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "NoRepair" 1
+ # Set the INSTALLSIZE constant (!defined at the top of this script) so Add/Remove Programs can accurately report the size
+ WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "EstimatedSize" ${INSTALLSIZE}
+sectionEnd
+
+# Uninstaller
+
+function un.onInit
+ SetShellVarContext all
+
+ #Verify the uninstaller - last chance to back out
+ MessageBox MB_OKCANCEL "Permanantly remove ${APPNAME}?" IDOK next
+ Abort
+ next:
+ !insertmacro VerifyUserIsAdmin
+functionEnd
+
+section "uninstall"
+
+ # Remove Start Menu launcher
+ delete "$SMPROGRAMS\${COMPANYNAME}\${APPNAME}.lnk"
+ # Try to remove the Start Menu folder - this will only happen if it is empty
+ rmDir "$SMPROGRAMS\${COMPANYNAME}"
+
+ # Remove files
+ delete $INSTDIR\NetSurf.exe
+ delete $INSTDIR\NetSurf.ico
+ delete $INSTDIR\libcares-2.dll
+ delete $INSTDIR\libgnurx-0.dll
+ delete $INSTDIR\default.css
+ delete $INSTDIR\internal.css
+ delete $INSTDIR\adblock.css
+ delete $INSTDIR\welcome.html
+ delete $INSTDIR\credits.html
+ delete $INSTDIR\licence.html
+ delete $INSTDIR\netsurf.png
+ delete $INSTDIR\messages
+ delete $INSTDIR\ca-bundle.crt
+
+ # Always delete uninstaller as the last action
+ delete $INSTDIR\uninstall.exe
+
+ # Try to remove the install directory - this will only happen if it is empty
+ rmDir $INSTDIR
+
+ # Remove uninstaller information from the registry
+ DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}"
+sectionEnd
diff --git a/frontends/windows/res/internal.css b/frontends/windows/res/internal.css
new file mode 120000
index 000000000..17f9f1504
--- /dev/null
+++ b/frontends/windows/res/internal.css
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/internal.css,f79 \ No newline at end of file
diff --git a/frontends/windows/res/licence.html b/frontends/windows/res/licence.html
new file mode 120000
index 000000000..147dd6db2
--- /dev/null
+++ b/frontends/windows/res/licence.html
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/en/licence.html,faf \ No newline at end of file
diff --git a/frontends/windows/res/netsurf.gif b/frontends/windows/res/netsurf.gif
new file mode 100644
index 000000000..f4ee3aa15
--- /dev/null
+++ b/frontends/windows/res/netsurf.gif
Binary files differ
diff --git a/frontends/windows/res/netsurf.png b/frontends/windows/res/netsurf.png
new file mode 120000
index 000000000..905512c25
--- /dev/null
+++ b/frontends/windows/res/netsurf.png
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/netsurf.png,b60 \ No newline at end of file
diff --git a/frontends/windows/res/quirks.css b/frontends/windows/res/quirks.css
new file mode 120000
index 000000000..88aabe48c
--- /dev/null
+++ b/frontends/windows/res/quirks.css
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/Quirks,f79 \ No newline at end of file
diff --git a/frontends/windows/res/resource.rc b/frontends/windows/res/resource.rc
new file mode 100644
index 000000000..b83205f6a
--- /dev/null
+++ b/frontends/windows/res/resource.rc
@@ -0,0 +1,265 @@
+#include <windows.h>
+#include <commctrl.h>
+#include <richedit.h>
+
+#include "../resourceid.h"
+
+
+IDR_NETSURF_ICON ICON DISCARDABLE "NetSurf.ico"
+IDR_TOOLBAR_BITMAP BITMAP DISCARDABLE "toolbar.bmp"
+IDR_TOOLBAR_BITMAP_GREY BITMAP DISCARDABLE "toolbarg.bmp"
+IDR_TOOLBAR_BITMAP_HOT BITMAP DISCARDABLE "toolbarh.bmp"
+IDR_NETSURF_BANNER BITMAP DISCARDABLE "banner.bmp"
+IDR_HOME_BITMAP BITMAP DISCARDABLE "home.bmp"
+
+IDR_THROBBER_FRAME0_BITMAP BITMAP DISCARDABLE "throbber/throbber0.bmp"
+IDR_THROBBER_FRAME1_BITMAP BITMAP DISCARDABLE "throbber/throbber1.bmp"
+IDR_THROBBER_FRAME2_BITMAP BITMAP DISCARDABLE "throbber/throbber2.bmp"
+IDR_THROBBER_FRAME3_BITMAP BITMAP DISCARDABLE "throbber/throbber3.bmp"
+IDR_THROBBER_FRAME4_BITMAP BITMAP DISCARDABLE "throbber/throbber4.bmp"
+IDR_THROBBER_FRAME5_BITMAP BITMAP DISCARDABLE "throbber/throbber5.bmp"
+IDR_THROBBER_FRAME6_BITMAP BITMAP DISCARDABLE "throbber/throbber6.bmp"
+IDR_THROBBER_FRAME7_BITMAP BITMAP DISCARDABLE "throbber/throbber7.bmp"
+
+IDR_MENU_MAIN MENU
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "&New Window",IDM_FILE_OPEN_WINDOW
+ MENUITEM "Open &Location",IDM_FILE_OPEN_LOCATION
+ MENUITEM "&Close",IDM_FILE_CLOSE_WINDOW
+ MENUITEM SEPARATOR
+ MENUITEM "&Save Page",IDM_FILE_SAVE_PAGE,GRAYED,HELP
+ POPUP "Save Page &As"
+ BEGIN
+ MENUITEM "Text",IDM_FILE_SAVEAS_TEXT,GRAYED
+ MENUITEM "PDF",IDM_FILE_SAVEAS_PDF,GRAYED
+ MENUITEM "Postscript",IDM_FILE_SAVEAS_POSTSCRIPT,GRAYED
+ END
+ MENUITEM SEPARATOR
+ MENUITEM "Print Pre&view",IDM_FILE_PRINT_PREVIEW
+ MENUITEM "&Print",IDM_FILE_PRINT
+ MENUITEM SEPARATOR
+ MENUITEM "E&xit",IDM_FILE_QUIT
+ END
+ POPUP "&Edit"
+ BEGIN
+ MENUITEM "Cu&t",IDM_EDIT_CUT
+ MENUITEM "&Copy",IDM_EDIT_COPY
+ MENUITEM "&Paste",IDM_EDIT_PASTE
+ MENUITEM "&Delete",IDM_EDIT_DELETE
+ MENUITEM SEPARATOR
+ MENUITEM "Select &All",IDM_EDIT_SELECT_ALL
+ MENUITEM SEPARATOR
+ MENUITEM "&Find",IDM_EDIT_SEARCH
+ END
+ POPUP "&View"
+ BEGIN
+ MENUITEM "Stop",IDM_NAV_STOP
+ MENUITEM "Reload",IDM_NAV_RELOAD
+ MENUITEM SEPARATOR
+ POPUP "&Zoom"
+ BEGIN
+ MENUITEM "Zoom &In",IDM_VIEW_ZOOMPLUS
+ MENUITEM "Zoom &Out",IDM_VIEW_ZOOMMINUS
+ MENUITEM "&Reset",IDM_VIEW_ZOOMNORMAL
+ END
+ MENUITEM SEPARATOR
+ MENUITEM "Page S&ource",IDM_VIEW_SOURCE
+ MENUITEM "&Full Screen",IDM_VIEW_FULLSCREEN
+ END
+ POPUP "&History"
+ BEGIN
+ MENUITEM "Back",IDM_NAV_BACK
+ MENUITEM "Forward",IDM_NAV_FORWARD
+ MENUITEM "Home",IDM_NAV_HOME
+ MENUITEM SEPARATOR
+ MENUITEM "Local History",IDM_NAV_LOCALHISTORY
+ MENUITEM "Global History",IDM_NAV_GLOBALHISTORY,GRAYED
+ END
+ POPUP "&Tools"
+ BEGIN
+ MENUITEM "&Downloads",IDM_VIEW_DOWNLOADS
+ MENUITEM "Save size and location",IDM_VIEW_SAVE_WIN_METRICS
+ POPUP "Debugging"
+ BEGIN
+ MENUITEM "Debug rendering",IDM_VIEW_TOGGLE_DEBUG_RENDERING
+ MENUITEM "Save Box Tree",IDM_VIEW_DEBUGGING_SAVE_BOXTREE,GRAYED
+ MENUITEM "Save DOM Tree",IDM_VIEW_DEBUGGING_SAVE_DOMTREE,GRAYED
+ END
+ MENUITEM SEPARATOR
+ MENUITEM "Options...",IDM_EDIT_PREFERENCES
+ END
+ POPUP "&Help"
+ BEGIN
+ MENUITEM "&Contents",IDM_HELP_CONTENTS
+ MENUITEM "G&uide",IDM_HELP_GUIDE
+ MENUITEM "&Info",IDM_HELP_INFO
+ MENUITEM SEPARATOR
+ MENUITEM "&About NetSurf",IDM_HELP_ABOUT
+ END
+END
+
+IDR_MENU_CONTEXT MENU
+BEGIN
+ POPUP "Context"
+ BEGIN
+ MENUITEM "&Back",IDM_NAV_BACK
+ MENUITEM "F&orward",IDM_NAV_FORWARD
+ MENUITEM "&Home",IDM_NAV_HOME
+ MENUITEM "&Stop",IDM_NAV_STOP
+ MENUITEM "&Reload",IDM_NAV_RELOAD
+ MENUITEM SEPARATOR
+ MENUITEM "C&ut",IDM_EDIT_CUT,GRAYED
+ MENUITEM "&Copy",IDM_EDIT_COPY,GRAYED
+ MENUITEM "&Paste",IDM_EDIT_PASTE,GRAYED
+ MENUITEM "&Delete",IDM_EDIT_DELETE,GRAYED
+ END
+END
+
+IDD_DLG_ABOUT DIALOGEX 10,10,206,133
+CAPTION "About NetSurf"
+FONT 8,"MS Sans Serif",0,0,0
+STYLE WS_VISIBLE|WS_CAPTION|WS_SYSMENU
+EXSTYLE WS_EX_DLGMODALFRAME
+BEGIN
+ CONTROL IDR_NETSURF_BANNER,IDC_IMG1,"Static",SS_BITMAP|0x40L /*SS_REALSIZECONTROL*/,0,0,205,53
+ CONTROL "NetSurf",IDC_ABOUT_VERSION,"Static",SS_LEFT,10,60,185,16
+ CONTROL "NetSurf is a small fast browser",IDC_ABOUT_TEXT,"Static",SS_LEFT,10,83,185,8
+ CONTROL "Copyright 2003-2011 The NetSurf Developers",IDC_ABOUT_COPYRIGHT,"Static",SS_LEFT,10,96,185,8
+ CONTROL "&OK",IDOK,"Button",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,143,111,54,15
+ CONTROL "&Credits",IDC_BTN_CREDITS,"Button",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,10,111,54,15
+ CONTROL "&Licence",IDC_BTN_LICENCE,"Button",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,70,111,54,15
+END
+
+IDD_DLG_DOWNLOAD DIALOGEX 0,0,201,84
+CAPTION "Download"
+FONT 8,"MS Sans Serif",0,0,0
+STYLE WS_POPUP|WS_VISIBLE|WS_CAPTION|WS_SYSMENU|DS_MODALFRAME|DS_SETFONT
+EXSTYLE WS_EX_DLGMODALFRAME
+BEGIN
+ CONTROL "downloading [file] [size] from [domain] to [destination]",IDC_DOWNLOAD_LABEL,"Static",WS_CHILDWINDOW|WS_VISIBLE|WS_GROUP|SS_CENTER,6,6,189,35
+ CONTROL "progress",IDC_DOWNLOAD_PROGRESS,PROGRESS_CLASS,WS_VISIBLE,6,50,189,10
+ CONTROL "&OK",IDOK,"Button",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,141,66,54,15
+ CONTROL "&Cancel",IDCANCEL,"Button",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,81,66,54,15
+END
+
+
+IDD_DLG_OPTIONS_GENERAL DIALOGEX 0,0,220,200
+CAPTION "General"
+FONT 8,"MS Shell Dlg"
+STYLE DS_CONTROL|DS_SHELLFONT
+BEGIN
+ /* home page entry */
+ CONTROL "Home Page",IDC_STATIC,"Static",SS_LEFT,7,7,40,8
+ CONTROL "",IDC_STATIC,"Static",SS_ETCHEDFRAME,47,11,166,1
+ CONTROL IDR_HOME_BITMAP,IDC_STATIC,"Static",SS_BITMAP,15,19,24,24
+ CONTROL "",IDC_PREFS_HOMEPAGE,"Edit",WS_TABSTOP|ES_OEMCONVERT|ES_AUTOHSCROLL,43,19,170,14,WS_EX_CLIENTEDGE
+
+
+ /* Content control */
+ CONTROL "Content Control",IDC_STATIC,"Static",SS_LEFT,7,40,60,8
+ CONTROL "",IDC_STATIC,"Static",SS_ETCHEDFRAME,67,44,146,1
+
+ CONTROL "Hide Images",IDC_PREFS_IMAGES,"Button",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|BS_AUTOCHECKBOX,43,52,67,10
+ CONTROL "Hide Advertisements",IDC_PREFS_ADVERTS,"Button",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|BS_AUTOCHECKBOX,43,66,81,10
+ CONTROL "Send referer",IDC_PREFS_REFERER,"Button",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|BS_AUTOCHECKBOX,43,80,55,10
+
+END
+
+IDD_DLG_OPTIONS_CONNECTIONS DIALOGEX 0,0,220,200
+CAPTION "Connections"
+FONT 8,"MS Shell Dlg"
+STYLE DS_CONTROL|DS_SHELLFONT
+BEGIN
+ /* proxy divider */
+ CONTROL "Proxy",IDC_STATIC,"Static",SS_LEFT,7,7,30,8
+ CONTROL "",IDC_STATIC,"Static",SS_ETCHEDFRAME,37,11,176,1
+
+ /* proxy controls */
+ CONTROL "Type:",IDC_STATIC,"Static",WS_CHILDWINDOW|WS_VISIBLE|WS_GROUP|SS_LEFT,43,22,40,8
+ CONTROL "",IDC_PREFS_PROXYTYPE,"ComboBox",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|CBS_DROPDOWNLIST,80,19,60,60
+
+ CONTROL "Server:",IDC_STATIC,"Static",WS_CHILDWINDOW|WS_VISIBLE|WS_GROUP|SS_LEFT,43,39,28,8
+ CONTROL "",IDC_PREFS_PROXYHOST,"Edit",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|ES_OEMCONVERT|ES_AUTOHSCROLL,80,36,80,14,WS_EX_CLIENTEDGE
+
+ CONTROL "Port:",IDC_STATIC,"Static",WS_CHILDWINDOW|WS_VISIBLE|WS_GROUP|SS_LEFT,164,39,16,8
+ CONTROL "",IDC_PREFS_PROXYPORT,"Edit",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|ES_OEMCONVERT|ES_AUTOHSCROLL,182,36,24,14,WS_EX_CLIENTEDGE
+
+ CONTROL "Username:",IDC_STATIC,"Static",WS_CHILDWINDOW|WS_VISIBLE|WS_GROUP|SS_LEFT,43,60,35,8
+ CONTROL "",IDC_PREFS_PROXYNAME,"Edit",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|ES_OEMCONVERT|ES_AUTOHSCROLL,80,57,45,14,WS_EX_CLIENTEDGE
+
+ CONTROL "Password:",IDC_STATIC,"Static",WS_CHILDWINDOW|WS_VISIBLE|WS_GROUP|SS_LEFT,132,60,34,8
+ CONTROL "",IDC_PREFS_PROXYPASS,"Edit",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|ES_OEMCONVERT|ES_AUTOHSCROLL|ES_PASSWORD,168,57,45,14,WS_EX_CLIENTEDGE
+
+
+ /* fetcher divider */
+ CONTROL "Fetchers",IDC_STATIC,"Static",SS_LEFT,7,78,40,8
+ CONTROL "",IDC_STATIC,"Static",SS_ETCHEDFRAME,42,82,171,1
+
+ /* max fetchers */
+ CONTROL "Max Fetchers:",IDC_STATIC,"Static",WS_CHILDWINDOW|WS_VISIBLE|WS_GROUP,43,96,64,8
+ CONTROL "",IDC_PREFS_FETCHERS,"Edit",ES_NUMBER|ES_RIGHT|WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|WS_BORDER,111,93,30,14
+ CONTROL "Max Fetchers",IDC_PREFS_FETCHERS_SPIN,UPDOWN_CLASS,UDS_AUTOBUDDY|UDS_ALIGNRIGHT|WS_VISIBLE,341,122,11,15
+
+ /* fetchers per host */
+ CONTROL "Fetches per host:",IDC_STATIC,"Static",WS_CHILDWINDOW|WS_VISIBLE|WS_GROUP,43,118,64,8
+ CONTROL "",IDC_PREFS_FETCH_HOST,"Edit",ES_NUMBER|ES_RIGHT|WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|WS_BORDER,111,114,30,14
+ CONTROL "Fetches per host",IDC_PREFS_FETCH_HOST_SPIN,UPDOWN_CLASS,UDS_AUTOBUDDY|UDS_ALIGNRIGHT|WS_VISIBLE,341,140,11,15
+
+ /* cached fetchers */
+ CONTROL "Cached Fetches:",IDC_STATIC,"Static",WS_CHILDWINDOW|WS_VISIBLE|WS_GROUP,43,138,64,8
+ CONTROL "",IDC_PREFS_FETCH_HANDLES,"Edit",ES_NUMBER|ES_RIGHT|WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|WS_BORDER,111,135,30,14
+ CONTROL "Cached Fetches",IDC_PREFS_FETCH_HANDLES_SPIN,UPDOWN_CLASS,UDS_AUTOBUDDY|UDS_ALIGNRIGHT|WS_VISIBLE,341,158,11,15
+
+END
+
+IDD_DLG_OPTIONS_APPERANCE DIALOGEX 10,10,220,200
+CAPTION "Apperance"
+FONT 8,"MS Shell Dlg"
+STYLE DS_CONTROL|DS_SHELLFONT
+BEGIN
+ /* proxy separator */
+ CONTROL "Fonts",IDC_STATIC,"Static",SS_LEFT,7,7,20,8
+ CONTROL "",IDC_STATIC,"Static",SS_ETCHEDFRAME,27,11,186,1
+
+ /* font controls */
+ CONTROL "Sans-serif:",IDC_STATIC,"Static",WS_CHILDWINDOW|WS_VISIBLE|WS_GROUP|SS_LEFT,43,22,44,8
+ CONTROL "Sans-serif",IDC_PREFS_SANS,"Button",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,89,19,50,14
+
+ CONTROL "Serif:",IDC_STATIC,"Static",WS_CHILDWINDOW|WS_VISIBLE|WS_GROUP|SS_LEFT,43,40,24,8
+ CONTROL "Serif",IDC_PREFS_SERIF,"Button",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,89,37,50,14
+
+ CONTROL "Monospace:",IDC_STATIC,"Static",WS_CHILDWINDOW|WS_VISIBLE|WS_GROUP|SS_LEFT,43,58,40,8
+ CONTROL "Monospace",IDC_PREFS_MONO,"Button",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,89,55,50,14
+
+ CONTROL "Cursive:",IDC_STATIC,"Static",WS_CHILDWINDOW|WS_VISIBLE|WS_GROUP|SS_LEFT,43,76,32,8
+ CONTROL "Cursive",IDC_PREFS_CURSIVE,"Button",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,89,73,50,14
+
+ CONTROL "Fantasy:",IDC_STATIC,"Static",WS_CHILDWINDOW|WS_VISIBLE|WS_GROUP|SS_LEFT,43,94,32,8
+ CONTROL "Fantasy",IDC_PREFS_FANTASY,"Button",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,89,91,50,14
+
+
+ CONTROL "Default:",IDC_STATIC,"Static",WS_CHILDWINDOW|WS_VISIBLE|WS_GROUP|SS_LEFT,43,112,38,8
+ CONTROL "",IDC_PREFS_FONTDEF,"ComboBox",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|CBS_DROPDOWNLIST,89,109,50,60
+
+ CONTROL "Size:",IDC_STATIC,"Static",WS_CHILDWINDOW|WS_VISIBLE|WS_GROUP|SS_LEFT,147,112,16,8
+ CONTROL "",IDC_PREFS_FONT_SIZE,"Edit",ES_NUMBER|WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,165,109,35,14,WS_EX_CLIENTEDGE
+ CONTROL "Font Size",IDC_PREFS_FONT_SIZE_SPIN,UPDOWN_CLASS,UDS_AUTOBUDDY|UDS_ALIGNRIGHT|WS_VISIBLE,75,137,11,15
+
+ CONTROL "Minimum Size:",IDC_STATIC,"Static",WS_CHILDWINDOW|WS_VISIBLE|WS_GROUP|SS_LEFT,43,130,52,8
+ CONTROL "",IDC_PREFS_FONT_MINSIZE,"Edit",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,97,127,35,14,WS_EX_CLIENTEDGE
+ CONTROL "Minimum Font Size",IDC_PREFS_FONT_MINSIZE_SPIN,UPDOWN_CLASS,UDS_AUTOBUDDY|UDS_ALIGNRIGHT|WS_VISIBLE,76,159,11,15
+
+ /* animation separator */
+ CONTROL "Animation",IDC_STATIC,"Static",SS_LEFT,7,148,36,8
+ CONTROL "",IDC_STATIC,"Static",SS_ETCHEDFRAME,43,152,170,1
+
+ CONTROL "Disable",IDC_PREFS_NOANIMATION,"Button",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|BS_AUTOCHECKBOX,43,163,39,10
+ CONTROL "Minimum delay:",IDC_STATIC,"Static",WS_CHILDWINDOW|WS_VISIBLE|WS_GROUP|SS_LEFT,55,180,56,8
+ CONTROL "",IDC_PREFS_ANIMATIONDELAY,"Edit",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,113,177,35,14,WS_EX_CLIENTEDGE
+ CONTROL "Min delay",IDC_PREFS_ANIMATIONDELAY_SPIN,UPDOWN_CLASS,UDS_AUTOBUDDY|UDS_ALIGNRIGHT|WS_VISIBLE,43,210,11,15
+
+
+
+END
diff --git a/frontends/windows/res/throbber.avi b/frontends/windows/res/throbber.avi
new file mode 100644
index 000000000..cced3a556
--- /dev/null
+++ b/frontends/windows/res/throbber.avi
Binary files differ
diff --git a/frontends/windows/res/throbber/throbber0.bmp b/frontends/windows/res/throbber/throbber0.bmp
new file mode 100644
index 000000000..2cef4da70
--- /dev/null
+++ b/frontends/windows/res/throbber/throbber0.bmp
Binary files differ
diff --git a/frontends/windows/res/throbber/throbber0.png b/frontends/windows/res/throbber/throbber0.png
new file mode 100755
index 000000000..ad13c5408
--- /dev/null
+++ b/frontends/windows/res/throbber/throbber0.png
Binary files differ
diff --git a/frontends/windows/res/throbber/throbber1.bmp b/frontends/windows/res/throbber/throbber1.bmp
new file mode 100644
index 000000000..7c7feb6b2
--- /dev/null
+++ b/frontends/windows/res/throbber/throbber1.bmp
Binary files differ
diff --git a/frontends/windows/res/throbber/throbber1.png b/frontends/windows/res/throbber/throbber1.png
new file mode 100755
index 000000000..9e4e575a3
--- /dev/null
+++ b/frontends/windows/res/throbber/throbber1.png
Binary files differ
diff --git a/frontends/windows/res/throbber/throbber2.bmp b/frontends/windows/res/throbber/throbber2.bmp
new file mode 100644
index 000000000..7d1d1147a
--- /dev/null
+++ b/frontends/windows/res/throbber/throbber2.bmp
Binary files differ
diff --git a/frontends/windows/res/throbber/throbber2.png b/frontends/windows/res/throbber/throbber2.png
new file mode 100755
index 000000000..f571f7093
--- /dev/null
+++ b/frontends/windows/res/throbber/throbber2.png
Binary files differ
diff --git a/frontends/windows/res/throbber/throbber3.bmp b/frontends/windows/res/throbber/throbber3.bmp
new file mode 100644
index 000000000..1f98b12b0
--- /dev/null
+++ b/frontends/windows/res/throbber/throbber3.bmp
Binary files differ
diff --git a/frontends/windows/res/throbber/throbber3.png b/frontends/windows/res/throbber/throbber3.png
new file mode 100755
index 000000000..0bc8b66b5
--- /dev/null
+++ b/frontends/windows/res/throbber/throbber3.png
Binary files differ
diff --git a/frontends/windows/res/throbber/throbber4.bmp b/frontends/windows/res/throbber/throbber4.bmp
new file mode 100644
index 000000000..a5ddc00e3
--- /dev/null
+++ b/frontends/windows/res/throbber/throbber4.bmp
Binary files differ
diff --git a/frontends/windows/res/throbber/throbber4.png b/frontends/windows/res/throbber/throbber4.png
new file mode 100755
index 000000000..37c9ce842
--- /dev/null
+++ b/frontends/windows/res/throbber/throbber4.png
Binary files differ
diff --git a/frontends/windows/res/throbber/throbber5.bmp b/frontends/windows/res/throbber/throbber5.bmp
new file mode 100644
index 000000000..261f8cd13
--- /dev/null
+++ b/frontends/windows/res/throbber/throbber5.bmp
Binary files differ
diff --git a/frontends/windows/res/throbber/throbber5.png b/frontends/windows/res/throbber/throbber5.png
new file mode 100755
index 000000000..54f83d224
--- /dev/null
+++ b/frontends/windows/res/throbber/throbber5.png
Binary files differ
diff --git a/frontends/windows/res/throbber/throbber6.bmp b/frontends/windows/res/throbber/throbber6.bmp
new file mode 100644
index 000000000..01d3f480d
--- /dev/null
+++ b/frontends/windows/res/throbber/throbber6.bmp
Binary files differ
diff --git a/frontends/windows/res/throbber/throbber6.png b/frontends/windows/res/throbber/throbber6.png
new file mode 100755
index 000000000..e36e1f872
--- /dev/null
+++ b/frontends/windows/res/throbber/throbber6.png
Binary files differ
diff --git a/frontends/windows/res/throbber/throbber7.bmp b/frontends/windows/res/throbber/throbber7.bmp
new file mode 100644
index 000000000..b5421cf0e
--- /dev/null
+++ b/frontends/windows/res/throbber/throbber7.bmp
Binary files differ
diff --git a/frontends/windows/res/throbber/throbber7.png b/frontends/windows/res/throbber/throbber7.png
new file mode 100755
index 000000000..c6f2628b5
--- /dev/null
+++ b/frontends/windows/res/throbber/throbber7.png
Binary files differ
diff --git a/frontends/windows/res/throbber/throbber8.png b/frontends/windows/res/throbber/throbber8.png
new file mode 100755
index 000000000..d29d94414
--- /dev/null
+++ b/frontends/windows/res/throbber/throbber8.png
Binary files differ
diff --git a/frontends/windows/res/toolbar.bmp b/frontends/windows/res/toolbar.bmp
new file mode 100644
index 000000000..4087878a6
--- /dev/null
+++ b/frontends/windows/res/toolbar.bmp
Binary files differ
diff --git a/frontends/windows/res/toolbarg.bmp b/frontends/windows/res/toolbarg.bmp
new file mode 100644
index 000000000..9a59e8d43
--- /dev/null
+++ b/frontends/windows/res/toolbarg.bmp
Binary files differ
diff --git a/frontends/windows/res/toolbarh.bmp b/frontends/windows/res/toolbarh.bmp
new file mode 100644
index 000000000..81e666f03
--- /dev/null
+++ b/frontends/windows/res/toolbarh.bmp
Binary files differ
diff --git a/frontends/windows/res/welcome.html b/frontends/windows/res/welcome.html
new file mode 120000
index 000000000..28362130a
--- /dev/null
+++ b/frontends/windows/res/welcome.html
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/en/welcome.html,faf \ No newline at end of file
diff --git a/frontends/windows/resourceid.h b/frontends/windows/resourceid.h
new file mode 100644
index 000000000..bdec80a30
--- /dev/null
+++ b/frontends/windows/resourceid.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_WINDOWS_RESOURCEID_H_
+#define _NETSURF_WINDOWS_RESOURCEID_H_
+
+#ifndef IDC_STATIC
+#define IDC_STATIC (-1)
+#endif
+
+#define IDR_NETSURF_ICON 100
+#define IDR_TOOLBAR_BITMAP 102
+#define IDR_TOOLBAR_BITMAP_GREY 103
+#define IDR_TOOLBAR_BITMAP_HOT 104
+#define IDR_NETSURF_BANNER 105
+#define IDR_HOME_BITMAP 114
+
+#define IDR_THROBBER_FRAME0_BITMAP 106
+#define IDR_THROBBER_FRAME1_BITMAP 107
+#define IDR_THROBBER_FRAME2_BITMAP 108
+#define IDR_THROBBER_FRAME3_BITMAP 109
+#define IDR_THROBBER_FRAME4_BITMAP 110
+#define IDR_THROBBER_FRAME5_BITMAP 111
+#define IDR_THROBBER_FRAME6_BITMAP 112
+#define IDR_THROBBER_FRAME7_BITMAP 113
+
+#define IDD_DLG_ABOUT 1000
+#define IDC_IMG1 1001
+#define IDC_ABOUT_VERSION 1002
+#define IDC_ABOUT_TEXT 1003
+#define IDC_ABOUT_COPYRIGHT 1004
+#define IDC_BTN_CREDITS 1005
+#define IDC_BTN_LICENCE 1006
+
+#define IDD_DLG_DOWNLOAD 1100
+#define IDC_DOWNLOAD_LABEL 1101
+#define IDC_DOWNLOAD_PROGRESS 1102
+
+#define IDD_DLG_MAIN 1300
+#define IDC_MAIN_TOOLBAR 1301
+#define IDC_MAIN_URLBAR 1302
+#define IDC_MAIN_THROBBER 1303
+#define IDC_MAIN_DRAWINGAREA 1304
+#define IDC_MAIN_STATUSBAR 1305
+#define IDC_MAIN_LAUNCH_URL 1306
+
+#define IDD_DLG_OPTIONS_GENERAL 1400
+#define IDC_PREFS_HOMEPAGE 1401
+#define IDC_PREFS_IMAGES 1402
+#define IDC_PREFS_ADVERTS 1403
+#define IDC_PREFS_REFERER 1404
+
+#define IDD_DLG_OPTIONS_CONNECTIONS 1500
+#define IDC_PREFS_FETCHERS 1501
+#define IDC_PREFS_FETCHERS_SPIN 1502
+#define IDC_PREFS_FETCH_HOST 1503
+#define IDC_PREFS_FETCH_HOST_SPIN 1504
+#define IDC_PREFS_FETCH_HANDLES 1505
+#define IDC_PREFS_FETCH_HANDLES_SPIN 1506
+
+#define IDD_DLG_OPTIONS_APPERANCE 1200
+#define IDC_PREFS_PROXYTYPE 1206
+#define IDC_PREFS_PROXYHOST 1207
+#define IDC_PREFS_PROXYPORT 1208
+#define IDC_PREFS_PROXYNAME 1209
+#define IDC_PREFS_PROXYPASS 1210
+#define IDC_PREFS_FONT_SIZE 1211
+#define IDC_PREFS_FONT_MINSIZE 1212
+#define IDC_PREFS_FONT_MINSIZE_SPIN 1213
+#define IDC_PREFS_SANS 1214
+#define IDC_PREFS_SERIF 1215
+#define IDC_PREFS_FONT_SIZE_SPIN 1216
+#define IDC_PREFS_MONO 1217
+#define IDC_PREFS_CURSIVE 1218
+#define IDC_PREFS_FANTASY 1219
+#define IDC_PREFS_FONTDEF 1220
+#define IDC_PREFS_NOANIMATION 1227
+#define IDC_PREFS_ANIMATIONDELAY 1228
+#define IDC_PREFS_ANIMATIONDELAY_SPIN 1229
+
+#define IDR_MENU_MAIN 10000
+#define IDM_FILE_OPEN_WINDOW 10001
+#define IDM_FILE_OPEN_LOCATION 10002
+#define IDM_FILE_CLOSE_WINDOW 10003
+#define IDM_FILE_SAVE_PAGE 10004
+#define IDM_FILE_SAVEAS_TEXT 10005
+#define IDM_FILE_SAVEAS_PDF 10006
+#define IDM_FILE_SAVEAS_POSTSCRIPT 10007
+#define IDM_FILE_PRINT_PREVIEW 10008
+#define IDM_FILE_PRINT 10009
+#define IDM_FILE_QUIT 10010
+#define IDM_EDIT_CUT 10011
+#define IDM_EDIT_COPY 10012
+#define IDM_EDIT_PASTE 10013
+#define IDM_EDIT_DELETE 10014
+#define IDM_EDIT_SELECT_ALL 10015
+#define IDM_EDIT_SEARCH 10016
+#define IDM_NAV_STOP 10017
+#define IDM_NAV_RELOAD 10018
+#define IDM_VIEW_ZOOMPLUS 10019
+#define IDM_VIEW_ZOOMMINUS 10020
+#define IDM_VIEW_ZOOMNORMAL 10021
+#define IDM_VIEW_SOURCE 10022
+#define IDM_VIEW_FULLSCREEN 10023
+#define IDM_NAV_BACK 10024
+#define IDM_NAV_FORWARD 10025
+#define IDM_NAV_HOME 10026
+#define IDM_NAV_LOCALHISTORY 10027
+#define IDM_NAV_GLOBALHISTORY 10028
+#define IDM_VIEW_DOWNLOADS 10029
+#define IDM_VIEW_SAVE_WIN_METRICS 10030
+#define IDM_VIEW_TOGGLE_DEBUG_RENDERING 10031
+#define IDM_VIEW_DEBUGGING_SAVE_BOXTREE 10032
+#define IDM_VIEW_DEBUGGING_SAVE_DOMTREE 10033
+#define IDM_EDIT_PREFERENCES 10034
+#define IDM_HELP_CONTENTS 10035
+#define IDM_HELP_GUIDE 10036
+#define IDM_HELP_INFO 10037
+#define IDM_HELP_ABOUT 10038
+
+#define IDR_MENU_CONTEXT 11000
+
+#endif
diff --git a/frontends/windows/schedule.c b/frontends/windows/schedule.c
new file mode 100644
index 000000000..5366add9c
--- /dev/null
+++ b/frontends/windows/schedule.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <stdlib.h>
+#include <time.h>
+
+#include "utils/sys_time.h"
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "utils/errors.h"
+
+#include "windows/schedule.h"
+
+#ifdef DEBUG_SCHEDULER
+#define SRLOG(x...) LOG(x)
+#else
+#define SRLOG(x...) ((void) 0)
+#endif
+
+/* linked list of scheduled callbacks */
+static struct nscallback *schedule_list = NULL;
+
+/**
+ * scheduled callback.
+ */
+struct nscallback
+{
+ struct nscallback *next;
+ struct timeval tv;
+ void (*callback)(void *p);
+ void *p;
+};
+
+
+/**
+ * Unschedule a callback.
+ *
+ * \param callback callback function
+ * \param p user parameter, passed to callback function
+ *
+ * All scheduled callbacks matching both callback and p are removed.
+ */
+
+static nserror schedule_remove(void (*callback)(void *p), void *p)
+{
+ struct nscallback *cur_nscb;
+ struct nscallback *prev_nscb;
+ struct nscallback *unlnk_nscb;
+
+ /* check there is something on the list to remove */
+ if (schedule_list == NULL) {
+ return NSERROR_OK;
+ }
+
+ SRLOG("removing %p, %p", callback, p);
+
+ cur_nscb = schedule_list;
+ prev_nscb = NULL;
+
+ while (cur_nscb != NULL) {
+ if ((cur_nscb->callback == callback) &&
+ (cur_nscb->p == p)) {
+ /* item to remove */
+
+ SRLOG("callback entry %p removing %p(%p)",
+ cur_nscb, cur_nscb->callback, cur_nscb->p);
+
+ /* remove callback */
+ unlnk_nscb = cur_nscb;
+ cur_nscb = unlnk_nscb->next;
+
+ if (prev_nscb == NULL) {
+ schedule_list = cur_nscb;
+ } else {
+ prev_nscb->next = cur_nscb;
+ }
+ free (unlnk_nscb);
+ } else {
+ /* move to next element */
+ prev_nscb = cur_nscb;
+ cur_nscb = prev_nscb->next;
+ }
+ }
+ return NSERROR_OK;
+}
+
+/* exported interface documented in windows/schedule.h */
+nserror win32_schedule(int ival, void (*callback)(void *p), void *p)
+{
+ struct nscallback *nscb;
+ struct timeval tv;
+ nserror ret;
+
+ ret = schedule_remove(callback, p);
+ if ((ival < 0) || (ret != NSERROR_OK)) {
+ return ret;
+ }
+
+ tv.tv_sec = ival / 1000; /* miliseconds to seconds */
+ tv.tv_usec = (ival % 1000) * 1000; /* remainder to microseconds */
+
+ nscb = calloc(1, sizeof(struct nscallback));
+ if (nscb == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ SRLOG("adding callback %p for %p(%p) at %d cs",
+ nscb, callback, p, ival);
+
+ gettimeofday(&nscb->tv, NULL);
+ timeradd(&nscb->tv, &tv, &nscb->tv);
+
+ nscb->callback = callback;
+ nscb->p = p;
+
+ /* add to list front */
+ nscb->next = schedule_list;
+ schedule_list = nscb;
+
+ return NSERROR_OK;
+}
+
+/* exported interface documented in schedule.h */
+int
+schedule_run(void)
+{
+ struct timeval tv;
+ struct timeval nexttime;
+ struct timeval rettime;
+ struct nscallback *cur_nscb;
+ struct nscallback *prev_nscb;
+ struct nscallback *unlnk_nscb;
+
+ if (schedule_list == NULL)
+ return -1;
+
+ /* reset enumeration to the start of the list */
+ cur_nscb = schedule_list;
+ prev_nscb = NULL;
+ nexttime = cur_nscb->tv;
+
+ gettimeofday(&tv, NULL);
+
+ while (cur_nscb != NULL) {
+ if (timercmp(&tv, &cur_nscb->tv, >)) {
+ /* scheduled time */
+
+ /* remove callback */
+ unlnk_nscb = cur_nscb;
+
+ if (prev_nscb == NULL) {
+ schedule_list = unlnk_nscb->next;
+ } else {
+ prev_nscb->next = unlnk_nscb->next;
+ }
+
+ SRLOG("callback entry %p running %p(%p)",
+ unlnk_nscb, unlnk_nscb->callback, unlnk_nscb->p);
+ /* call callback */
+ unlnk_nscb->callback(unlnk_nscb->p);
+
+ free(unlnk_nscb);
+
+ /* dispatched events can modify the list,
+ * instead of locking we simply reset list
+ * enumeration to the start.
+ */
+ if (schedule_list == NULL)
+ return -1; /* no more callbacks scheduled */
+
+ cur_nscb = schedule_list;
+ prev_nscb = NULL;
+ nexttime = cur_nscb->tv;
+ } else {
+ /* if the time to the event is sooner than the
+ * currently recorded soonest event record it
+ */
+ if (timercmp(&nexttime, &cur_nscb->tv, >)) {
+ nexttime = cur_nscb->tv;
+ }
+ /* move to next element */
+ prev_nscb = cur_nscb;
+ cur_nscb = prev_nscb->next;
+ }
+ }
+
+ /* make returned time relative to now */
+ timersub(&nexttime, &tv, &rettime);
+
+ SRLOG("returning time to next event as %ldms",
+ (rettime.tv_sec * 1000) + (rettime.tv_usec / 1000));
+
+ /* return next event time in milliseconds (24days max wait) */
+ return (rettime.tv_sec * 1000) + (rettime.tv_usec / 1000);
+}
+
+/* exported interface documented in schedule.h */
+void list_schedule(void)
+{
+ struct timeval tv;
+ struct nscallback *cur_nscb;
+
+ gettimeofday(&tv, NULL);
+
+ LOG("schedule list at %ld:%ld", tv.tv_sec, tv.tv_usec);
+
+ cur_nscb = schedule_list;
+
+ while (cur_nscb != NULL) {
+ LOG("Schedule %p at %ld:%ld", cur_nscb, cur_nscb->tv.tv_sec, cur_nscb->tv.tv_usec);
+ cur_nscb = cur_nscb->next;
+ }
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */
diff --git a/frontends/windows/schedule.h b/frontends/windows/schedule.h
new file mode 100644
index 000000000..6d47b2db6
--- /dev/null
+++ b/frontends/windows/schedule.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 WINDOWS_SCHEDULE_H
+#define WINDOWS_SCHEDULE_H
+
+/**
+ * Schedule a callback.
+ *
+ * \param ival interval before the callback should be made in ms
+ * \param callback callback function
+ * \param p user parameter, passed to callback function
+ *
+ * The callback function will be called as soon as possible after t ms have
+ * passed.
+ */
+nserror win32_schedule(int ival, void (*callback)(void *p), void *p);
+
+/**
+ * Process scheduled callbacks up to current time.
+ *
+ * This walks the list of outstanding scheduled events and dispatches
+ * them if they have met their scheduled time. Due to legacy issues
+ * there are a couple of subtleties with how this operates:
+ *
+ * - Generally there are so few entries on the list the overhead of
+ * ordering the list exceeds the cost of simply enumerating them.
+ *
+ * - The scheduled time is the time *after* which we should call the
+ * operation back, this can result in the next scheduled time
+ * being zero. This is exceedingly rare as the core schedules in
+ * 10ms (cs) quanta and we almost always get called to schedule
+ * after the event time.
+ *
+ * - The callbacks can cause the schedule list to be re-arranged added
+ * to or even completely deleted. This means we must reset the
+ * list enumeration to the beginning every time an event is
+ * dispatched.
+ *
+ * @return The number of milliseconds untill the next scheduled event
+ * or -1 for no event.
+ */
+int schedule_run(void);
+
+/**
+ * LOG all current scheduled events.
+ */
+void list_schedule(void);
+
+#endif
diff --git a/frontends/windows/windbg.c b/frontends/windows/windbg.c
new file mode 100644
index 000000000..ccbecc31e
--- /dev/null
+++ b/frontends/windows/windbg.c
@@ -0,0 +1,663 @@
+/*
+ * Copyright 2011 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <windows.h>
+#include <stdio.h>
+
+#include "windbg.h"
+
+const char *msg_num_to_name(int msg)
+{
+ static char str[256];
+
+ switch (msg) {
+ case 32768:
+ return "WM_APP";
+
+ case 6:
+ return "WM_ACTIVATE ";
+
+ case 28:
+ return "WM_ACTIVATEAPP ";
+
+ case 864:
+ return "WM_AFXFIRST ";
+
+ case 895:
+ return "WM_AFXLAST ";
+
+ case 780:
+ return "WM_ASKCBFORMATNAME ";
+
+ case 75:
+ return "WM_CANCELJOURNAL ";
+
+ case 31:
+ return "WM_CANCELMODE ";
+
+ case 533:
+ return "WM_CAPTURECHANGED ";
+
+ case 781:
+ return "WM_CHANGECBCHAIN ";
+
+ case 258:
+ return "WM_CHAR ";
+
+ case 47:
+ return "WM_CHARTOITEM ";
+
+ case 34:
+ return "WM_CHILDACTIVATE ";
+
+ case 771:
+ return "WM_CLEAR ";
+
+ case 16:
+ return "WM_CLOSE ";
+
+ case 273:
+ return "WM_COMMAND ";
+
+ case 68:
+ return "WM_COMMNOTIFY ";
+
+ case 65:
+ return "WM_COMPACTING ";
+
+ case 57:
+ return "WM_COMPAREITEM ";
+
+ case 123:
+ return "WM_CONTEXTMENU ";
+
+ case 769:
+ return "WM_COPY ";
+
+ case 74:
+ return "WM_COPYDATA ";
+
+ case 1:
+ return "WM_CREATE ";
+
+ case 309:
+ return "WM_CTLCOLORBTN ";
+
+ case 310:
+ return "WM_CTLCOLORDLG ";
+
+ case 307:
+ return "WM_CTLCOLOREDIT ";
+
+ case 308:
+ return "WM_CTLCOLORLISTBOX ";
+
+ case 306:
+ return "WM_CTLCOLORMSGBOX ";
+
+ case 311:
+ return "WM_CTLCOLORSCROLLBAR ";
+
+ case 312:
+ return "WM_CTLCOLORSTATIC ";
+
+ case 768:
+ return "WM_CUT ";
+
+ case 259:
+ return "WM_DEADCHAR ";
+
+ case 45:
+ return "WM_DELETEITEM ";
+
+ case 2:
+ return "WM_DESTROY ";
+
+ case 775:
+ return "WM_DESTROYCLIPBOARD ";
+
+ case 537:
+ return "WM_DEVICECHANGE ";
+
+ case 27:
+ return "WM_DEVMODECHANGE ";
+
+ case 126:
+ return "WM_DISPLAYCHANGE ";
+
+ case 776:
+ return "WM_DRAWCLIPBOARD ";
+
+ case 43:
+ return "WM_DRAWITEM ";
+
+ case 563:
+ return "WM_DROPFILES ";
+
+ case 10:
+ return "WM_ENABLE ";
+
+ case 22:
+ return "WM_ENDSESSION ";
+
+ case 289:
+ return "WM_ENTERIDLE ";
+
+ case 529:
+ return "WM_ENTERMENULOOP ";
+
+ case 561:
+ return "WM_ENTERSIZEMOVE ";
+
+ case 20:
+ return "WM_ERASEBKGND ";
+
+ case 530:
+ return "WM_EXITMENULOOP ";
+
+ case 562:
+ return "WM_EXITSIZEMOVE ";
+
+ case 29:
+ return "WM_FONTCHANGE ";
+
+ case 135:
+ return "WM_GETDLGCODE ";
+
+ case 49:
+ return "WM_GETFONT ";
+
+ case 51:
+ return "WM_GETHOTKEY ";
+
+ case 127:
+ return "WM_GETICON ";
+
+ case 36:
+ return "WM_GETMINMAXINFO ";
+
+ case 13:
+ return "WM_GETTEXT ";
+
+ case 14:
+ return "WM_GETTEXTLENGTH ";
+
+ case 856:
+ return "WM_HANDHELDFIRST ";
+
+ case 863:
+ return "WM_HANDHELDLAST ";
+
+ case 83:
+ return "WM_HELP ";
+
+ case 786:
+ return "WM_HOTKEY ";
+
+ case 276:
+ return "WM_HSCROLL ";
+
+ case 782:
+ return "WM_HSCROLLCLIPBOARD ";
+
+ case 39:
+ return "WM_ICONERASEBKGND ";
+
+ case 272:
+ return "WM_INITDIALOG ";
+
+ case 278:
+ return "WM_INITMENU ";
+
+ case 279:
+ return "WM_INITMENUPOPUP ";
+
+ case 0x00FF:
+ return "WM_INPUT ";
+
+ case 81:
+ return "WM_INPUTLANGCHANGE ";
+
+ case 80:
+ return "WM_INPUTLANGCHANGEREQUEST ";
+
+ case 256:
+ return "WM_KEYDOWN ";
+
+ case 257:
+ return "WM_KEYUP ";
+
+ case 8:
+ return "WM_KILLFOCUS ";
+
+ case 546:
+ return "WM_MDIACTIVATE ";
+
+ case 551:
+ return "WM_MDICASCADE ";
+
+ case 544:
+ return "WM_MDICREATE ";
+
+ case 545:
+ return "WM_MDIDESTROY ";
+
+ case 553:
+ return "WM_MDIGETACTIVE ";
+
+ case 552:
+ return "WM_MDIICONARRANGE ";
+
+ case 549:
+ return "WM_MDIMAXIMIZE ";
+
+ case 548:
+ return "WM_MDINEXT ";
+
+ case 564:
+ return "WM_MDIREFRESHMENU ";
+
+ case 547:
+ return "WM_MDIRESTORE ";
+
+ case 560:
+ return "WM_MDISETMENU ";
+
+ case 550:
+ return "WM_MDITILE ";
+
+ case 44:
+ return "WM_MEASUREITEM ";
+
+ case 0x003D:
+ return "WM_GETOBJECT ";
+
+ case 0x0127:
+ return "WM_CHANGEUISTATE ";
+
+ case 0x0128:
+ return "WM_UPDATEUISTATE ";
+
+ case 0x0129:
+ return "WM_QUERYUISTATE ";
+
+ case 0x0125:
+ return "WM_UNINITMENUPOPUP ";
+
+ case 290:
+ return "WM_MENURBUTTONUP ";
+
+ case 0x0126:
+ return "WM_MENUCOMMAND ";
+
+ case 0x0124:
+ return "WM_MENUGETOBJECT ";
+
+ case 0x0123:
+ return "WM_MENUDRAG ";
+
+ case 0x0319:
+ return "WM_APPCOMMAND ";
+
+ case 288:
+ return "WM_MENUCHAR ";
+
+ case 287:
+ return "WM_MENUSELECT ";
+
+ case 531:
+ return "WM_NEXTMENU ";
+
+ case 3:
+ return "WM_MOVE ";
+
+ case 534:
+ return "WM_MOVING ";
+
+ case 134:
+ return "WM_NCACTIVATE ";
+
+ case 131:
+ return "WM_NCCALCSIZE ";
+
+ case 129:
+ return "WM_NCCREATE ";
+
+ case 130:
+ return "WM_NCDESTROY ";
+
+ case 132:
+ return "WM_NCHITTEST ";
+
+ case 163:
+ return "WM_NCLBUTTONDBLCLK ";
+
+ case 161:
+ return "WM_NCLBUTTONDOWN ";
+
+ case 162:
+ return "WM_NCLBUTTONUP ";
+
+ case 169:
+ return "WM_NCMBUTTONDBLCLK ";
+
+ case 167:
+ return "WM_NCMBUTTONDOWN ";
+
+ case 168:
+ return "WM_NCMBUTTONUP ";
+
+ case 171:
+ return "WM_NCXBUTTONDOWN ";
+
+ case 172:
+ return "WM_NCXBUTTONUP ";
+
+ case 173:
+ return "WM_NCXBUTTONDBLCLK ";
+
+ case 0x02A0:
+ return "WM_NCMOUSEHOVER ";
+
+ case 0x02A2:
+ return "WM_NCMOUSELEAVE ";
+
+ case 160:
+ return "WM_NCMOUSEMOVE ";
+
+ case 133:
+ return "WM_NCPAINT ";
+
+ case 166:
+ return "WM_NCRBUTTONDBLCLK ";
+
+ case 164:
+ return "WM_NCRBUTTONDOWN ";
+
+ case 165:
+ return "WM_NCRBUTTONUP ";
+
+ case 40:
+ return "WM_NEXTDLGCTL ";
+
+ case 78:
+ return "WM_NOTIFY ";
+
+ case 85:
+ return "WM_NOTIFYFORMAT ";
+
+ case 0:
+ return "WM_NULL ";
+
+ case 15:
+ return "WM_PAINT ";
+
+ case 777:
+ return "WM_PAINTCLIPBOARD ";
+
+ case 38:
+ return "WM_PAINTICON ";
+
+ case 785:
+ return "WM_PALETTECHANGED ";
+
+ case 784:
+ return "WM_PALETTEISCHANGING ";
+
+ case 528:
+ return "WM_PARENTNOTIFY ";
+
+ case 770:
+ return "WM_PASTE ";
+
+ case 896:
+ return "WM_PENWINFIRST ";
+
+ case 911:
+ return "WM_PENWINLAST ";
+
+ case 72:
+ return "WM_POWER ";
+
+ case 536:
+ return "WM_POWERBROADCAST ";
+
+ case 791:
+ return "WM_PRINT ";
+
+ case 792:
+ return "WM_PRINTCLIENT ";
+
+ case 55:
+ return "WM_QUERYDRAGICON ";
+
+ case 17:
+ return "WM_QUERYENDSESSION ";
+
+ case 783:
+ return "WM_QUERYNEWPALETTE ";
+
+ case 19:
+ return "WM_QUERYOPEN ";
+
+ case 35:
+ return "WM_QUEUESYNC ";
+
+ case 18:
+ return "WM_QUIT ";
+
+ case 774:
+ return "WM_RENDERALLFORMATS ";
+
+ case 773:
+ return "WM_RENDERFORMAT ";
+
+ case 32:
+ return "WM_SETCURSOR ";
+
+ case 7:
+ return "WM_SETFOCUS ";
+
+ case 48:
+ return "WM_SETFONT ";
+
+ case 50:
+ return "WM_SETHOTKEY ";
+
+ case 128:
+ return "WM_SETICON ";
+
+ case 11:
+ return "WM_SETREDRAW ";
+
+ case 12:
+ return "WM_SETTEXT ";
+
+ case 26:
+ return "WM_SETTINGCHANGE ";
+
+ case 24:
+ return "WM_SHOWWINDOW ";
+
+ case 5:
+ return "WM_SIZE ";
+
+ case 779:
+ return "WM_SIZECLIPBOARD ";
+
+ case 532:
+ return "WM_SIZING ";
+
+ case 42:
+ return "WM_SPOOLERSTATUS ";
+
+ case 125:
+ return "WM_STYLECHANGED ";
+
+ case 124:
+ return "WM_STYLECHANGING ";
+
+ case 262:
+ return "WM_SYSCHAR ";
+
+ case 21:
+ return "WM_SYSCOLORCHANGE ";
+
+ case 274:
+ return "WM_SYSCOMMAND ";
+
+ case 263:
+ return "WM_SYSDEADCHAR ";
+
+ case 260:
+ return "WM_SYSKEYDOWN ";
+
+ case 261:
+ return "WM_SYSKEYUP ";
+
+ case 82:
+ return "WM_TCARD ";
+
+ case 794:
+ return "WM_THEMECHANGED ";
+
+ case 30:
+ return "WM_TIMECHANGE ";
+
+ case 275:
+ return "WM_TIMER ";
+
+ case 772:
+ return "WM_UNDO ";
+
+ case 1024:
+ return "WM_USER ";
+
+ case 84:
+ return "WM_USERCHANGED ";
+
+ case 46:
+ return "WM_VKEYTOITEM ";
+
+ case 277:
+ return "WM_VSCROLL ";
+
+ case 778:
+ return "WM_VSCROLLCLIPBOARD ";
+
+ case 71:
+ return "WM_WINDOWPOSCHANGED ";
+
+ case 70:
+ return "WM_WINDOWPOSCHANGING ";
+
+ case 264:
+ return "WM_KEYLAST ";
+
+ case 136:
+ return "WM_SYNCPAINT ";
+
+ case 33:
+ return "WM_MOUSEACTIVATE ";
+
+ case 512:
+ return "WM_MOUSEMOVE ";
+
+ case 513:
+ return "WM_LBUTTONDOWN ";
+
+ case 514:
+ return "WM_LBUTTONUP ";
+
+ case 515:
+ return "WM_LBUTTONDBLCLK ";
+
+ case 516:
+ return "WM_RBUTTONDOWN ";
+
+ case 517:
+ return "WM_RBUTTONUP ";
+
+ case 518:
+ return "WM_RBUTTONDBLCLK ";
+
+ case 519:
+ return "WM_MBUTTONDOWN ";
+
+ case 520:
+ return "WM_MBUTTONUP ";
+
+ case 521:
+ return "WM_MBUTTONDBLCLK ";
+
+ case 522:
+ return "WM_MOUSEWHEEL ";
+
+ case 523:
+ return "WM_XBUTTONDOWN ";
+
+ case 524:
+ return "WM_XBUTTONUP ";
+
+ case 525:
+ return "WM_XBUTTONDBLCLK ";
+
+ case 0x2A1:
+ return "WM_MOUSEHOVER ";
+
+ case 0x2A3:
+ return "WM_MOUSELEAVE ";
+
+ }
+
+ sprintf(str,"%d",msg);
+
+ return str;
+}
+
+void win_perror(const char * lpszFunction)
+{
+ /* Retrieve the system error message for the last-error code */
+
+ LPVOID lpMsgBuf;
+ LPVOID lpDisplayBuf;
+ DWORD dw = GetLastError();
+
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ dw,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpMsgBuf,
+ 0, NULL );
+
+ /* Display the error message and exit the process */
+
+ lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
+
+ snprintf((LPTSTR)lpDisplayBuf,
+ LocalSize(lpDisplayBuf) / sizeof(TCHAR),
+ TEXT("%s failed with error %ld: %s"),
+ lpszFunction, dw, (char *)lpMsgBuf);
+ MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
+
+ LocalFree(lpMsgBuf);
+ LocalFree(lpDisplayBuf);
+}
diff --git a/frontends/windows/windbg.h b/frontends/windows/windbg.h
new file mode 100644
index 000000000..b2d8640f4
--- /dev/null
+++ b/frontends/windows/windbg.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2010 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_WINDOWS_WINDBG_H_
+#define _NETSURF_WINDOWS_WINDBG_H_
+
+#include "utils/log.h"
+
+const char *msg_num_to_name(int msg);
+void win_perror(const char *lpszFunction);
+
+#define LOG_WIN_MSG(h, m, w, l) \
+ if (((m) != WM_SETCURSOR) && \
+ ((m) != WM_MOUSEMOVE) && \
+ ((m) != WM_NCHITTEST) && \
+ ((m) != WM_ENTERIDLE)) \
+ LOG("%s, hwnd %p, w 0x%x, l 0x%Ix", msg_num_to_name(m), h, w, l);
+
+#endif
diff --git a/frontends/windows/window.c b/frontends/windows/window.c
new file mode 100644
index 000000000..f5f13fe24
--- /dev/null
+++ b/frontends/windows/window.c
@@ -0,0 +1,1741 @@
+/*
+ * Copyright 2011 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 "utils/config.h"
+
+#include <stdbool.h>
+#include <windows.h>
+#include <windowsx.h>
+#include <commctrl.h>
+
+#include "utils/errors.h"
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "utils/nsoption.h"
+#include "utils/nsurl.h"
+#include "utils/messages.h"
+#include "content/content.h"
+#include "desktop/browser.h"
+#include "desktop/gui_window.h"
+#include "desktop/browser_history.h"
+#include "desktop/textinput.h"
+
+#include "windows/gui.h"
+#include "windows/pointers.h"
+#include "windows/about.h"
+#include "windows/resourceid.h"
+#include "windows/findfile.h"
+#include "windows/windbg.h"
+#include "windows/drawable.h"
+#include "windows/font.h"
+#include "windows/prefs.h"
+#include "windows/localhistory.h"
+#include "windows/window.h"
+
+/** List of all our gui windows */
+static struct gui_window *window_list = NULL;
+
+/** The main window class name */
+static const char windowclassname_main[] = "nswsmainwindow";
+
+/** width of the throbber element */
+#define NSWS_THROBBER_WIDTH 24
+
+/** Number of open windows */
+static int open_windows = 0;
+
+
+/**
+ * Obtain the DPI of the display.
+ */
+static int get_window_dpi(HWND hwnd)
+{
+ HDC hdc = GetDC(hwnd);
+ int dpi = GetDeviceCaps(hdc, LOGPIXELSY);
+
+ if (dpi <= 10) {
+ dpi = 96; /* 96DPI is the default */
+ }
+
+ ReleaseDC(hwnd, hdc);
+
+ LOG("FIX DPI %d", dpi);
+
+ return dpi;
+}
+
+
+/**
+ * set accelerators
+ */
+static void nsws_window_set_accels(struct gui_window *w)
+{
+ int i, nitems = 13;
+ ACCEL accels[nitems];
+
+ for (i = 0; i < nitems; i++) {
+ accels[i].fVirt = FCONTROL | FVIRTKEY;
+ }
+
+ accels[0].key = 0x51; /* Q */
+ accels[0].cmd = IDM_FILE_QUIT;
+ accels[1].key = 0x4E; /* N */
+ accels[1].cmd = IDM_FILE_OPEN_WINDOW;
+ accels[2].key = VK_LEFT;
+ accels[2].cmd = IDM_NAV_BACK;
+ accels[3].key = VK_RIGHT;
+ accels[3].cmd = IDM_NAV_FORWARD;
+ accels[4].key = VK_UP;
+ accels[4].cmd = IDM_NAV_HOME;
+ accels[5].key = VK_BACK;
+ accels[5].cmd = IDM_NAV_STOP;
+ accels[6].key = VK_SPACE;
+ accels[6].cmd = IDM_NAV_RELOAD;
+ accels[7].key = 0x4C; /* L */
+ accels[7].cmd = IDM_FILE_OPEN_LOCATION;
+ accels[8].key = 0x57; /* w */
+ accels[8].cmd = IDM_FILE_CLOSE_WINDOW;
+ accels[9].key = 0x41; /* A */
+ accels[9].cmd = IDM_EDIT_SELECT_ALL;
+ accels[10].key = VK_F8;
+ accels[10].cmd = IDM_VIEW_SOURCE;
+ accels[11].key = VK_RETURN;
+ accels[11].fVirt = FVIRTKEY;
+ accels[11].cmd = IDC_MAIN_LAUNCH_URL;
+ accels[12].key = VK_F11;
+ accels[12].fVirt = FVIRTKEY;
+ accels[12].cmd = IDM_VIEW_FULLSCREEN;
+
+ w->acceltable = CreateAcceleratorTable(accels, nitems);
+}
+
+
+/**
+ * creation of a new full browser window
+ */
+static HWND nsws_window_create(struct gui_window *gw)
+{
+ HWND hwnd;
+ INITCOMMONCONTROLSEX icc;
+
+ LOG("GUI window %p", gw);
+
+ icc.dwSize = sizeof(icc);
+ icc.dwICC = ICC_BAR_CLASSES | ICC_WIN95_CLASSES;
+#if WINVER > 0x0501
+ icc.dwICC |= ICC_STANDARD_CLASSES;
+#endif
+ InitCommonControlsEx(&icc);
+
+ gw->mainmenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU_MAIN));
+ gw->rclick = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU_CONTEXT));
+
+ LOG("creating window for hInstance %p", hInstance);
+ hwnd = CreateWindowEx(0,
+ windowclassname_main,
+ "NetSurf Browser",
+ WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CS_DBLCLKS,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ gw->width,
+ gw->height,
+ NULL,
+ gw->mainmenu,
+ hInstance,
+ NULL);
+
+ if (hwnd == NULL) {
+ LOG("Window create failed");
+ return NULL;
+ }
+
+ /* set the gui window associated with this browser */
+ SetProp(hwnd, TEXT("GuiWnd"), (HANDLE)gw);
+
+ browser_set_dpi(get_window_dpi(hwnd));
+
+ if ((nsoption_int(window_width) >= 100) &&
+ (nsoption_int(window_height) >= 100) &&
+ (nsoption_int(window_x) >= 0) &&
+ (nsoption_int(window_y) >= 0)) {
+ LOG("Setting Window position %d,%d %d,%d", nsoption_int(window_x), nsoption_int(window_y), nsoption_int(window_width), nsoption_int(window_height));
+ SetWindowPos(hwnd, HWND_TOP,
+ nsoption_int(window_x), nsoption_int(window_y),
+ nsoption_int(window_width), nsoption_int(window_height),
+ SWP_SHOWWINDOW);
+ }
+
+ nsws_window_set_accels(gw);
+
+ return hwnd;
+}
+
+
+/**
+ * calculate the dimensions of the url bar relative to the parent toolbar
+ */
+static void
+urlbar_dimensions(HWND hWndParent,
+ int toolbuttonsize,
+ int buttonc,
+ int *x,
+ int *y,
+ int *width,
+ int *height)
+{
+ RECT rc;
+ const int cy_edit = 23;
+
+ GetClientRect(hWndParent, &rc);
+ *x = (toolbuttonsize + 1) * (buttonc + 1) + (NSWS_THROBBER_WIDTH>>1);
+ *y = ((((rc.bottom - 1) - cy_edit) >> 1) * 2) / 3;
+ *width = (rc.right - 1) - *x - (NSWS_THROBBER_WIDTH>>1) - NSWS_THROBBER_WIDTH;
+ *height = cy_edit;
+}
+
+
+
+static LRESULT
+nsws_window_toolbar_command(struct gui_window *gw,
+ int notification_code,
+ int identifier,
+ HWND ctrl_window)
+{
+ LOG("notification_code %d identifier %d ctrl_window %p", notification_code, identifier, ctrl_window);
+
+ switch(identifier) {
+
+ case IDC_MAIN_URLBAR:
+ switch (notification_code) {
+ case EN_CHANGE:
+ LOG("EN_CHANGE");
+ break;
+
+ case EN_ERRSPACE:
+ LOG("EN_ERRSPACE");
+ break;
+
+ case EN_HSCROLL:
+ LOG("EN_HSCROLL");
+ break;
+
+ case EN_KILLFOCUS:
+ LOG("EN_KILLFOCUS");
+ break;
+
+ case EN_MAXTEXT:
+ LOG("EN_MAXTEXT");
+ break;
+
+ case EN_SETFOCUS:
+ LOG("EN_SETFOCUS");
+ break;
+
+ case EN_UPDATE:
+ LOG("EN_UPDATE");
+ break;
+
+ case EN_VSCROLL:
+ LOG("EN_VSCROLL");
+ break;
+
+ default:
+ LOG("Unknown notification_code");
+ break;
+ }
+ break;
+
+ default:
+ return 1; /* unhandled */
+
+ }
+ return 0; /* control message handled */
+}
+
+
+/**
+ * callback for toolbar events
+ */
+static LRESULT CALLBACK
+nsws_window_toolbar_callback(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ struct gui_window *gw;
+ int urlx, urly, urlwidth, urlheight;
+ WNDPROC toolproc;
+
+ LOG_WIN_MSG(hwnd, msg, wparam, lparam);
+
+ gw = nsws_get_gui_window(hwnd);
+
+ switch (msg) {
+ case WM_SIZE:
+ urlbar_dimensions(hwnd,
+ gw->toolbuttonsize,
+ gw->toolbuttonc,
+ &urlx, &urly, &urlwidth, &urlheight);
+
+ /* resize url */
+ if (gw->urlbar != NULL) {
+ MoveWindow(gw->urlbar,
+ urlx, urly,
+ urlwidth, urlheight,
+ true);
+ }
+
+ /* move throbber */
+ if (gw->throbber != NULL) {
+ MoveWindow(gw->throbber,
+ LOWORD(lparam) - NSWS_THROBBER_WIDTH - 4, 8,
+ NSWS_THROBBER_WIDTH, NSWS_THROBBER_WIDTH,
+ true);
+ }
+ break;
+
+ case WM_COMMAND:
+ if (nsws_window_toolbar_command(gw,
+ HIWORD(wparam),
+ LOWORD(wparam),
+ (HWND)lparam) == 0) {
+ return 0;
+ }
+ break;
+ }
+
+ /* remove properties if window is being destroyed */
+ if (msg == WM_NCDESTROY) {
+ RemoveProp(hwnd, TEXT("GuiWnd"));
+ toolproc = (WNDPROC)RemoveProp(hwnd, TEXT("OrigMsgProc"));
+ } else {
+ toolproc = (WNDPROC)GetProp(hwnd, TEXT("OrigMsgProc"));
+ }
+
+ if (toolproc == NULL) {
+ /* the original toolbar procedure is not available */
+ return DefWindowProc(hwnd, msg, wparam, lparam);
+ }
+
+ /* chain to the next handler */
+ return CallWindowProc(toolproc, hwnd, msg, wparam, lparam);
+}
+
+
+static HIMAGELIST
+get_imagelist(int resid, int bsize, int bcnt)
+{
+ HIMAGELIST hImageList;
+ HBITMAP hScrBM;
+
+ LOG("resource id %d, bzize %d, bcnt %d", resid, bsize, bcnt);
+
+ hImageList = ImageList_Create(bsize, bsize, ILC_COLOR24 | ILC_MASK, 0,
+ bcnt);
+ if (hImageList == NULL)
+ return NULL;
+
+ hScrBM = LoadImage(hInstance, MAKEINTRESOURCE(resid),
+ IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
+
+ if (hScrBM == NULL) {
+ win_perror("LoadImage");
+ return NULL;
+ }
+
+ if (ImageList_AddMasked(hImageList, hScrBM, 0xcccccc) == -1) {
+ /* failed to add masked bitmap */
+ ImageList_Destroy(hImageList);
+ hImageList = NULL;
+ }
+ DeleteObject(hScrBM);
+
+ return hImageList;
+}
+
+
+/**
+ * callback for url bar events
+ */
+static LRESULT CALLBACK
+nsws_window_urlbar_callback(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ struct gui_window *gw;
+ WNDPROC urlproc;
+ HFONT hFont;
+
+ LOG_WIN_MSG(hwnd, msg, wparam, lparam);
+
+ gw = nsws_get_gui_window(hwnd);
+
+ urlproc = (WNDPROC)GetProp(hwnd, TEXT("OrigMsgProc"));
+
+ /* override messages */
+ switch (msg) {
+ case WM_CHAR:
+ if (wparam == 13) {
+ SendMessage(gw->main, WM_COMMAND, IDC_MAIN_LAUNCH_URL, 0);
+ return 0;
+ }
+ break;
+
+ case WM_DESTROY:
+ hFont = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0);
+ if (hFont != NULL) {
+ LOG("Destroyed font object");
+ DeleteObject(hFont);
+ }
+
+
+ case WM_NCDESTROY:
+ /* remove properties if window is being destroyed */
+ RemoveProp(hwnd, TEXT("GuiWnd"));
+ RemoveProp(hwnd, TEXT("OrigMsgProc"));
+ break;
+ }
+
+ if (urlproc == NULL) {
+ /* the original toolbar procedure is not available */
+ return DefWindowProc(hwnd, msg, wparam, lparam);
+ }
+
+ /* chain to the next handler */
+ return CallWindowProc(urlproc, hwnd, msg, wparam, lparam);
+}
+
+
+/**
+ * create a urlbar and message handler
+ *
+ * Create an Edit control for enerting urls
+ */
+static HWND
+nsws_window_urlbar_create(struct gui_window *gw, HWND hwndparent)
+{
+ int urlx, urly, urlwidth, urlheight;
+ HWND hwnd;
+ WNDPROC urlproc;
+ HFONT hFont;
+
+ urlbar_dimensions(hwndparent,
+ gw->toolbuttonsize,
+ gw->toolbuttonc,
+ &urlx, &urly, &urlwidth, &urlheight);
+
+ /* Create the edit control */
+ hwnd = CreateWindowEx(0L,
+ TEXT("Edit"),
+ NULL,
+ WS_CHILD | WS_BORDER | WS_VISIBLE | ES_LEFT | ES_AUTOHSCROLL,
+ urlx,
+ urly,
+ urlwidth,
+ urlheight,
+ hwndparent,
+ (HMENU)IDC_MAIN_URLBAR,
+ hInstance,
+ 0);
+
+ if (hwnd == NULL) {
+ return NULL;
+ }
+
+ /* set the gui window associated with this control */
+ SetProp(hwnd, TEXT("GuiWnd"), (HANDLE)gw);
+
+ /* subclass the message handler */
+ urlproc = (WNDPROC)SetWindowLongPtr(hwnd,
+ GWLP_WNDPROC,
+ (LONG_PTR)nsws_window_urlbar_callback);
+
+ /* save the real handler */
+ SetProp(hwnd, TEXT("OrigMsgProc"), (HANDLE)urlproc);
+
+ hFont = CreateFont(urlheight - 4, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, "Arial");
+ if (hFont != NULL) {
+ LOG("Setting font object");
+ SendMessage(hwnd, WM_SETFONT, (WPARAM)hFont, 0);
+ }
+
+ LOG("Created url bar hwnd:%p, x:%d, y:%d, w:%d, h:%d", hwnd, urlx, urly, urlwidth, urlheight);
+
+ return hwnd;
+}
+
+
+/**
+ * creation of throbber
+ */
+static HWND
+nsws_window_throbber_create(struct gui_window *w)
+{
+ HWND hwnd;
+ char avi[PATH_MAX];
+
+ hwnd = CreateWindow(ANIMATE_CLASS,
+ "",
+ WS_CHILD | WS_VISIBLE | ACS_TRANSPARENT,
+ w->width - NSWS_THROBBER_WIDTH - 4,
+ 8,
+ NSWS_THROBBER_WIDTH,
+ NSWS_THROBBER_WIDTH,
+ w->main,
+ (HMENU) IDC_MAIN_THROBBER,
+ hInstance,
+ NULL);
+
+ nsws_find_resource(avi, "throbber.avi", "windows/res/throbber.avi");
+ LOG("setting throbber avi as %s", avi);
+ Animate_Open(hwnd, avi);
+ if (w->throbbing)
+ Animate_Play(hwnd, 0, -1, -1);
+ else
+ Animate_Seek(hwnd, 0);
+ ShowWindow(hwnd, SW_SHOWNORMAL);
+ return hwnd;
+}
+
+
+/* create a toolbar add controls and message handler */
+static HWND
+nsws_window_create_toolbar(struct gui_window *gw, HWND hWndParent)
+{
+ HIMAGELIST hImageList;
+ HWND hWndToolbar;
+ /* Toolbar buttons */
+ TBBUTTON tbButtons[] = {
+ {0, IDM_NAV_BACK, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
+ {1, IDM_NAV_FORWARD, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
+ {2, IDM_NAV_HOME, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
+ {3, IDM_NAV_RELOAD, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
+ {4, IDM_NAV_STOP, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
+ };
+ WNDPROC toolproc;
+
+ /* Create the toolbar window and subclass its message handler. */
+ hWndToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, "Toolbar",
+ WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT,
+ 0, 0, 0, 0,
+ hWndParent, NULL, HINST_COMMCTRL, NULL);
+ if (!hWndToolbar) {
+ return NULL;
+ }
+
+ /* set the gui window associated with this toolbar */
+ SetProp(hWndToolbar, TEXT("GuiWnd"), (HANDLE)gw);
+
+ /* subclass the message handler */
+ toolproc = (WNDPROC)SetWindowLongPtr(hWndToolbar,
+ GWLP_WNDPROC,
+ (LONG_PTR)nsws_window_toolbar_callback);
+
+ /* save the real handler */
+ SetProp(hWndToolbar, TEXT("OrigMsgProc"), (HANDLE)toolproc);
+
+ /* remember how many buttons are being created */
+ gw->toolbuttonc = sizeof(tbButtons) / sizeof(TBBUTTON);
+
+ /* Create the standard image list and assign to toolbar. */
+ hImageList = get_imagelist(IDR_TOOLBAR_BITMAP,
+ gw->toolbuttonsize,
+ gw->toolbuttonc);
+ if (hImageList != NULL) {
+ SendMessage(hWndToolbar, TB_SETIMAGELIST, 0, (LPARAM)hImageList);
+ }
+
+ /* Create the disabled image list and assign to toolbar. */
+ hImageList = get_imagelist(IDR_TOOLBAR_BITMAP_GREY,
+ gw->toolbuttonsize,
+ gw->toolbuttonc);
+ if (hImageList != NULL) {
+ SendMessage(hWndToolbar, TB_SETDISABLEDIMAGELIST, 0,
+ (LPARAM)hImageList);
+ }
+
+ /* Create the hot image list and assign to toolbar. */
+ hImageList = get_imagelist(IDR_TOOLBAR_BITMAP_HOT,
+ gw->toolbuttonsize,
+ gw->toolbuttonc);
+ if (hImageList != NULL) {
+ SendMessage(hWndToolbar, TB_SETHOTIMAGELIST, 0,
+ (LPARAM)hImageList);
+ }
+
+ /* Add buttons. */
+ SendMessage(hWndToolbar,
+ TB_BUTTONSTRUCTSIZE,
+ (WPARAM)sizeof(TBBUTTON),
+ 0);
+ SendMessage(hWndToolbar,
+ TB_ADDBUTTONS,
+ (WPARAM)gw->toolbuttonc,
+ (LPARAM)&tbButtons);
+
+ gw->urlbar = nsws_window_urlbar_create(gw, hWndToolbar);
+
+ gw->throbber = nsws_window_throbber_create(gw);
+
+ return hWndToolbar;
+}
+
+
+/**
+ * creation of status bar
+ */
+static HWND nsws_window_create_statusbar(struct gui_window *w)
+{
+ HWND hwnd = CreateWindowEx(0,
+ STATUSCLASSNAME,
+ NULL,
+ WS_CHILD | WS_VISIBLE,
+ 0, 0, 0, 0,
+ w->main,
+ (HMENU)IDC_MAIN_STATUSBAR,
+ hInstance,
+ NULL);
+ SendMessage(hwnd, SB_SETTEXT, 0, (LPARAM)"NetSurf");
+ return hwnd;
+}
+
+
+static void nsws_update_edit(struct gui_window *w)
+{
+ browser_editor_flags editor_flags = (w->bw == NULL) ?
+ BW_EDITOR_NONE : browser_window_get_editor_flags(w->bw);
+ bool paste, copy, del;
+ bool sel = (editor_flags & BW_EDITOR_CAN_COPY);
+ if (GetFocus() == w->urlbar) {
+ DWORD i, ii;
+ SendMessage(w->urlbar, EM_GETSEL, (WPARAM)&i, (LPARAM)&ii);
+ paste = true;
+ copy = (i != ii);
+ del = (i != ii);
+
+ } else if (sel){
+ paste = (editor_flags & BW_EDITOR_CAN_PASTE);
+ copy = sel;
+ del = (editor_flags & BW_EDITOR_CAN_CUT);
+ } else {
+ paste = false;
+ copy = false;
+ del = false;
+ }
+ EnableMenuItem(w->mainmenu,
+ IDM_EDIT_PASTE,
+ (paste ? MF_ENABLED : MF_GRAYED));
+
+ EnableMenuItem(w->rclick,
+ IDM_EDIT_PASTE,
+ (paste ? MF_ENABLED : MF_GRAYED));
+
+ EnableMenuItem(w->mainmenu,
+ IDM_EDIT_COPY,
+ (copy ? MF_ENABLED : MF_GRAYED));
+
+ EnableMenuItem(w->rclick,
+ IDM_EDIT_COPY,
+ (copy ? MF_ENABLED : MF_GRAYED));
+
+ if (del == true) {
+ EnableMenuItem(w->mainmenu, IDM_EDIT_CUT, MF_ENABLED);
+ EnableMenuItem(w->mainmenu, IDM_EDIT_DELETE, MF_ENABLED);
+ EnableMenuItem(w->rclick, IDM_EDIT_CUT, MF_ENABLED);
+ EnableMenuItem(w->rclick, IDM_EDIT_DELETE, MF_ENABLED);
+ } else {
+ EnableMenuItem(w->mainmenu, IDM_EDIT_CUT, MF_GRAYED);
+ EnableMenuItem(w->mainmenu, IDM_EDIT_DELETE, MF_GRAYED);
+ EnableMenuItem(w->rclick, IDM_EDIT_CUT, MF_GRAYED);
+ EnableMenuItem(w->rclick, IDM_EDIT_DELETE, MF_GRAYED);
+ }
+}
+
+
+static bool
+nsws_ctx_menu(struct gui_window *w, HWND hwnd, int x, int y)
+{
+ RECT rc; /* client area of window */
+ POINT pt = { x, y }; /* location of mouse click */
+
+ /* Get the bounding rectangle of the client area. */
+ GetClientRect(hwnd, &rc);
+
+ /* Convert the mouse position to client coordinates. */
+ ScreenToClient(hwnd, &pt);
+
+ /* If the position is in the client area, display a shortcut menu. */
+ if (PtInRect(&rc, pt)) {
+ ClientToScreen(hwnd, &pt);
+ nsws_update_edit(w);
+ TrackPopupMenu(GetSubMenu(w->rclick, 0),
+ TPM_CENTERALIGN | TPM_TOPALIGN,
+ x,
+ y,
+ 0,
+ hwnd,
+ NULL);
+
+ return true;
+ }
+
+ /* Return false if no menu is displayed. */
+ return false;
+}
+
+
+/**
+ * update state of forward/back buttons/menu items when page changes
+ */
+static void nsws_window_update_forward_back(struct gui_window *w)
+{
+ if (w->bw == NULL)
+ return;
+
+ bool forward = browser_window_history_forward_available(w->bw);
+ bool back = browser_window_history_back_available(w->bw);
+
+ if (w->mainmenu != NULL) {
+ EnableMenuItem(w->mainmenu, IDM_NAV_FORWARD,
+ (forward ? MF_ENABLED : MF_GRAYED));
+ EnableMenuItem(w->mainmenu, IDM_NAV_BACK,
+ (back ? MF_ENABLED : MF_GRAYED));
+ EnableMenuItem(w->rclick, IDM_NAV_FORWARD,
+ (forward ? MF_ENABLED : MF_GRAYED));
+ EnableMenuItem(w->rclick, IDM_NAV_BACK,
+ (back ? MF_ENABLED : MF_GRAYED));
+ }
+
+ if (w->toolbar != NULL) {
+ SendMessage(w->toolbar, TB_SETSTATE,
+ (WPARAM) IDM_NAV_FORWARD,
+ MAKELONG((forward ? TBSTATE_ENABLED :
+ TBSTATE_INDETERMINATE), 0));
+ SendMessage(w->toolbar, TB_SETSTATE,
+ (WPARAM) IDM_NAV_BACK,
+ MAKELONG((back ? TBSTATE_ENABLED :
+ TBSTATE_INDETERMINATE), 0));
+ }
+}
+
+
+static bool win32_window_get_scroll(struct gui_window *w, int *sx, int *sy)
+{
+ LOG("get scroll");
+ if (w == NULL)
+ return false;
+
+ *sx = w->scrollx;
+ *sy = w->scrolly;
+
+ return true;
+}
+
+
+static void nsws_set_scale(struct gui_window *gw, float scale)
+{
+ assert(gw != NULL);
+
+ if (gw->scale == scale)
+ return;
+
+ gw->scale = scale;
+
+ if (gw->bw == NULL)
+ return;
+
+ browser_window_set_scale(gw->bw, scale, true);
+}
+
+
+/**
+ * redraw the whole window
+ */
+static void win32_window_redraw_window(struct gui_window *gw)
+{
+ /* LOG("gw:%p", gw); */
+ if (gw != NULL) {
+ RedrawWindow(gw->drawingarea, NULL, NULL,
+ RDW_INVALIDATE | RDW_NOERASE);
+ }
+}
+
+
+/**
+ * scroll the window
+ *
+ * \param w The win32 gui window to scroll.
+ * \param sx the new 'absolute' horizontal scroll location
+ * \param sy the new 'absolute' vertical scroll location
+ */
+void win32_window_set_scroll(struct gui_window *w, int sx, int sy)
+{
+ SCROLLINFO si;
+ nserror err;
+ int height;
+ int width;
+ POINT p;
+
+ if ((w == NULL) || (w->bw == NULL))
+ return;
+
+ err = browser_window_get_extents(w->bw, true, &width, &height);
+ if (err != NSERROR_OK) {
+ return;
+ }
+
+ /*LOG("scroll sx,sy:%d,%d x,y:%d,%d w.h:%d,%d",sx,sy,w->scrollx,w->scrolly, width,height);*/
+
+ /* The resulting gui window scroll must remain withn the
+ * windows bounding box.
+ */
+ if (sx < 0) {
+ w->requestscrollx = -w->scrollx;
+ } else if (sx > (width - w->width)) {
+ w->requestscrollx = (width - w->width) - w->scrollx;
+ } else {
+ w->requestscrollx = sx - w->scrollx;
+ }
+ if (sy < 0) {
+ w->requestscrolly = -w->scrolly;
+ } else if (sy > (height - w->height)) {
+ w->requestscrolly = (height - w->height) - w->scrolly;
+ } else {
+ w->requestscrolly = sy - w->scrolly;
+ }
+
+ /*LOG("requestscroll x,y:%d,%d", w->requestscrollx, w->requestscrolly);*/
+
+ /* set the vertical scroll offset */
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_ALL;
+ si.nMin = 0;
+ si.nMax = height - 1;
+ si.nPage = w->height;
+ si.nPos = max(w->scrolly + w->requestscrolly, 0);
+ si.nPos = min(si.nPos, height - w->height);
+ SetScrollInfo(w->drawingarea, SB_VERT, &si, TRUE);
+ /*LOG("SetScrollInfo VERT min:%d max:%d page:%d pos:%d", si.nMin, si.nMax, si.nPage, si.nPos);*/
+
+ /* set the horizontal scroll offset */
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_ALL;
+ si.nMin = 0;
+ si.nMax = width -1;
+ si.nPage = w->width;
+ si.nPos = max(w->scrollx + w->requestscrollx, 0);
+ si.nPos = min(si.nPos, width - w->width);
+ SetScrollInfo(w->drawingarea, SB_HORZ, &si, TRUE);
+ /*LOG("SetScrollInfo HORZ min:%d max:%d page:%d pos:%d", si.nMin, si.nMax, si.nPage, si.nPos);*/
+
+ /* Set caret position */
+ GetCaretPos(&p);
+ HideCaret(w->drawingarea);
+ SetCaretPos(p.x - w->requestscrollx, p.y - w->requestscrolly);
+ ShowCaret(w->drawingarea);
+
+ RECT r, redraw;
+ r.top = 0;
+ r.bottom = w->height + 1;
+ r.left = 0;
+ r.right = w->width + 1;
+ ScrollWindowEx(w->drawingarea, - w->requestscrollx, - w->requestscrolly, &r, NULL, NULL, &redraw, SW_INVALIDATE);
+ /*LOG("ScrollWindowEx %d, %d", - w->requestscrollx, - w->requestscrolly);*/
+ w->scrolly += w->requestscrolly;
+ w->scrollx += w->requestscrollx;
+ w->requestscrollx = 0;
+ w->requestscrolly = 0;
+
+}
+
+/**
+ * Create a new window due to menu selection
+ *
+ * \param gw frontends graphical window.
+ * \return NSERROR_OK on success else appropriate error code.
+ */
+static nserror win32_open_new_window(struct gui_window *gw)
+{
+ const char *addr;
+ nsurl *url;
+ nserror ret;
+
+ if (nsoption_charp(homepage_url) != NULL) {
+ addr = nsoption_charp(homepage_url);
+ } else {
+ addr = NETSURF_HOMEPAGE;
+ }
+
+ ret = nsurl_create(addr, &url);
+ if (ret == NSERROR_OK) {
+ ret = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ gw->bw,
+ NULL);
+ nsurl_unref(url);
+ }
+
+ return ret;
+}
+
+static LRESULT
+nsws_window_command(HWND hwnd,
+ struct gui_window *gw,
+ int notification_code,
+ int identifier,
+ HWND ctrl_window)
+{
+ nserror ret;
+
+ LOG("notification_code %x identifier %x ctrl_window %p",
+ notification_code, identifier, ctrl_window);
+
+ switch(identifier) {
+
+ case IDM_FILE_QUIT:
+ {
+ struct gui_window *w;
+ w = window_list;
+ while (w != NULL) {
+ PostMessage(w->main, WM_CLOSE, 0, 0);
+ w = w->next;
+ }
+ break;
+ }
+
+ case IDM_FILE_OPEN_LOCATION:
+ SetFocus(gw->urlbar);
+ break;
+
+ case IDM_FILE_OPEN_WINDOW:
+ ret = win32_open_new_window(gw);
+ if (ret != NSERROR_OK) {
+ win32_warning(messages_get_errorcode(ret), 0);
+ }
+ break;
+
+ case IDM_FILE_CLOSE_WINDOW:
+ PostMessage(gw->main, WM_CLOSE, 0, 0);
+ break;
+
+ case IDM_FILE_SAVE_PAGE:
+ break;
+
+ case IDM_FILE_SAVEAS_TEXT:
+ break;
+
+ case IDM_FILE_SAVEAS_PDF:
+ break;
+
+ case IDM_FILE_SAVEAS_POSTSCRIPT:
+ break;
+
+ case IDM_FILE_PRINT_PREVIEW:
+ break;
+
+ case IDM_FILE_PRINT:
+ break;
+
+ case IDM_EDIT_CUT:
+ OpenClipboard(gw->main);
+ EmptyClipboard();
+ CloseClipboard();
+ if (GetFocus() == gw->urlbar) {
+ SendMessage(gw->urlbar, WM_CUT, 0, 0);
+ } else if (gw->bw != NULL) {
+ browser_window_key_press(gw->bw, NS_KEY_CUT_SELECTION);
+ }
+ break;
+
+ case IDM_EDIT_COPY:
+ OpenClipboard(gw->main);
+ EmptyClipboard();
+ CloseClipboard();
+ if (GetFocus() == gw->urlbar) {
+ SendMessage(gw->urlbar, WM_COPY, 0, 0);
+ } else if (gw->bw != NULL) {
+ browser_window_key_press(gw->bw, NS_KEY_COPY_SELECTION);
+ }
+ break;
+
+ case IDM_EDIT_PASTE: {
+ OpenClipboard(gw->main);
+ HANDLE h = GetClipboardData(CF_TEXT);
+ if (h != NULL) {
+ char *content = GlobalLock(h);
+ LOG("pasting %s\n", content);
+ GlobalUnlock(h);
+ }
+ CloseClipboard();
+ if (GetFocus() == gw->urlbar)
+ SendMessage(gw->urlbar, WM_PASTE, 0, 0);
+ else
+ browser_window_key_press(gw->bw, NS_KEY_PASTE);
+ break;
+ }
+
+ case IDM_EDIT_DELETE:
+ if (GetFocus() == gw->urlbar)
+ SendMessage(gw->urlbar, WM_CUT, 0, 0);
+ else
+ browser_window_key_press(gw->bw, NS_KEY_DELETE_RIGHT);
+ break;
+
+ case IDM_EDIT_SELECT_ALL:
+ if (GetFocus() == gw->urlbar)
+ SendMessage(gw->urlbar, EM_SETSEL, 0, -1);
+ else
+ browser_window_key_press(gw->bw, NS_KEY_SELECT_ALL);
+ break;
+
+ case IDM_EDIT_SEARCH:
+ break;
+
+ case IDM_EDIT_PREFERENCES:
+ nsws_prefs_dialog_init(hInstance, gw->main);
+ break;
+
+ case IDM_NAV_BACK:
+ if ((gw->bw != NULL) &&
+ (browser_window_history_back_available(gw->bw))) {
+ browser_window_history_back(gw->bw, false);
+ }
+ nsws_window_update_forward_back(gw);
+ break;
+
+ case IDM_NAV_FORWARD:
+ if ((gw->bw != NULL) &&
+ (browser_window_history_forward_available(gw->bw))) {
+ browser_window_history_forward(gw->bw, false);
+ }
+ nsws_window_update_forward_back(gw);
+ break;
+
+ case IDM_NAV_HOME:
+ {
+ nsurl *url;
+
+ if (nsurl_create(nsoption_charp(homepage_url), &url) != NSERROR_OK) {
+ win32_warning("NoMemory", 0);
+ } else {
+ browser_window_navigate(gw->bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ break;
+ }
+
+ case IDM_NAV_STOP:
+ browser_window_stop(gw->bw);
+ break;
+
+ case IDM_NAV_RELOAD:
+ browser_window_reload(gw->bw, true);
+ break;
+
+ case IDM_NAV_LOCALHISTORY:
+ gw->localhistory = nsws_window_create_localhistory(gw);
+ break;
+
+ case IDM_NAV_GLOBALHISTORY:
+ break;
+
+ case IDM_VIEW_ZOOMPLUS: {
+ int x, y;
+ win32_window_get_scroll(gw, &x, &y);
+ if (gw->bw != NULL) {
+ nsws_set_scale(gw, gw->scale * 1.1);
+ }
+ win32_window_redraw_window(gw);
+ win32_window_set_scroll(gw, x, y);
+ break;
+ }
+
+ case IDM_VIEW_ZOOMMINUS: {
+ int x, y;
+ win32_window_get_scroll(gw, &x, &y);
+ if (gw->bw != NULL) {
+ nsws_set_scale(gw, gw->scale * 0.9);
+ }
+ win32_window_redraw_window(gw);
+ win32_window_set_scroll(gw, x, y);
+ break;
+ }
+
+ case IDM_VIEW_ZOOMNORMAL: {
+ int x, y;
+ win32_window_get_scroll(gw, &x, &y);
+ if (gw->bw != NULL) {
+ nsws_set_scale(gw, 1.0);
+ }
+ win32_window_redraw_window(gw);
+ win32_window_set_scroll(gw, x, y);
+ break;
+ }
+
+ case IDM_VIEW_SOURCE:
+ break;
+
+ case IDM_VIEW_SAVE_WIN_METRICS: {
+ RECT r;
+ GetWindowRect(gw->main, &r);
+ nsoption_set_int(window_x, r.left);
+ nsoption_set_int(window_y, r.top);
+ nsoption_set_int(window_width, r.right - r.left);
+ nsoption_set_int(window_height, r.bottom - r.top);
+ nsoption_write(options_file_location, NULL, NULL);
+ break;
+ }
+
+ case IDM_VIEW_FULLSCREEN: {
+ RECT rdesk;
+ if (gw->fullscreen == NULL) {
+ HWND desktop = GetDesktopWindow();
+ gw->fullscreen = malloc(sizeof(RECT));
+ if ((desktop == NULL) ||
+ (gw->fullscreen == NULL)) {
+ win32_warning("NoMemory", 0);
+ break;
+ }
+ GetWindowRect(desktop, &rdesk);
+ GetWindowRect(gw->main, gw->fullscreen);
+ DeleteObject(desktop);
+ SetWindowLong(gw->main, GWL_STYLE, 0);
+ SetWindowPos(gw->main, HWND_TOPMOST, 0, 0,
+ rdesk.right - rdesk.left,
+ rdesk.bottom - rdesk.top,
+ SWP_SHOWWINDOW);
+ } else {
+ SetWindowLong(gw->main, GWL_STYLE,
+ WS_OVERLAPPEDWINDOW |
+ WS_HSCROLL | WS_VSCROLL |
+ WS_CLIPCHILDREN |
+ WS_CLIPSIBLINGS | CS_DBLCLKS);
+ SetWindowPos(gw->main, HWND_TOPMOST,
+ gw->fullscreen->left,
+ gw->fullscreen->top,
+ gw->fullscreen->right -
+ gw->fullscreen->left,
+ gw->fullscreen->bottom -
+ gw->fullscreen->top,
+ SWP_SHOWWINDOW | SWP_FRAMECHANGED);
+ free(gw->fullscreen);
+ gw->fullscreen = NULL;
+ }
+ break;
+ }
+
+ case IDM_VIEW_DOWNLOADS:
+ break;
+
+ case IDM_VIEW_TOGGLE_DEBUG_RENDERING:
+ if (gw->bw != NULL) {
+ browser_window_debug(gw->bw, CONTENT_DEBUG_REDRAW);
+ /* TODO: This should only redraw, not reformat.
+ * (Layout doesn't change, so reformat is a waste of time) */
+ browser_window_reformat(gw->bw, false, gw->width, gw->height);
+ }
+ break;
+
+ case IDM_VIEW_DEBUGGING_SAVE_BOXTREE:
+ break;
+
+ case IDM_VIEW_DEBUGGING_SAVE_DOMTREE:
+ break;
+
+ case IDM_HELP_CONTENTS:
+ nsws_window_go(hwnd,
+ "http://www.netsurf-browser.org/documentation/");
+ break;
+
+ case IDM_HELP_GUIDE:
+ nsws_window_go(hwnd,
+ "http://www.netsurf-browser.org/documentation/guide");
+ break;
+
+ case IDM_HELP_INFO:
+ nsws_window_go(hwnd,
+ "http://www.netsurf-browser.org/documentation/info");
+ break;
+
+ case IDM_HELP_ABOUT:
+ nsws_about_dialog_init(hInstance, gw->main);
+ break;
+
+ case IDC_MAIN_LAUNCH_URL:
+ {
+ nsurl *url;
+
+ if (GetFocus() != gw->urlbar)
+ break;
+
+ int len = SendMessage(gw->urlbar, WM_GETTEXTLENGTH, 0, 0);
+ char addr[len + 1];
+ SendMessage(gw->urlbar, WM_GETTEXT, (WPARAM)(len + 1), (LPARAM)addr);
+ LOG("launching %s\n", addr);
+
+ if (nsurl_create(addr, &url) != NSERROR_OK) {
+ win32_warning("NoMemory", 0);
+ } else {
+ browser_window_navigate(gw->bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+
+ break;
+ }
+
+
+ default:
+ return 1; /* unhandled */
+
+ }
+ return 0; /* control message handled */
+}
+
+
+static LRESULT
+nsws_window_resize(struct gui_window *gw,
+ HWND hwnd,
+ WPARAM wparam,
+ LPARAM lparam)
+{
+ int x, y;
+ RECT rstatus, rtool;
+
+ if ((gw->toolbar == NULL) ||
+ (gw->urlbar == NULL) ||
+ (gw->statusbar == NULL))
+ return 0;
+
+ SendMessage(gw->statusbar, WM_SIZE, wparam, lparam);
+ SendMessage(gw->toolbar, WM_SIZE, wparam, lparam);
+
+ GetClientRect(gw->toolbar, &rtool);
+ GetWindowRect(gw->statusbar, &rstatus);
+ win32_window_get_scroll(gw, &x, &y);
+ gw->width = LOWORD(lparam);
+ gw->height = HIWORD(lparam) - (rtool.bottom - rtool.top) - (rstatus.bottom - rstatus.top);
+
+ if (gw->drawingarea != NULL) {
+ MoveWindow(gw->drawingarea,
+ 0,
+ rtool.bottom,
+ gw->width,
+ gw->height,
+ true);
+ }
+ nsws_window_update_forward_back(gw);
+
+ win32_window_set_scroll(gw, x, y);
+
+ if (gw->toolbar != NULL) {
+ SendMessage(gw->toolbar, TB_SETSTATE,
+ (WPARAM) IDM_NAV_STOP,
+ MAKELONG(TBSTATE_INDETERMINATE, 0));
+ }
+
+ return 0;
+}
+
+
+/**
+ * callback for window events generally
+ */
+static LRESULT CALLBACK
+nsws_window_event_callback(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ struct gui_window *gw;
+ RECT rmain;
+
+ LOG_WIN_MSG(hwnd, msg, wparam, lparam);
+
+ /* deal with window creation as a special case */
+ if (msg == WM_CREATE) {
+ /* To cause all the component child windows to be
+ * re-sized correctly a WM_SIZE message of the actual
+ * created size must be sent.
+ *
+ * The message must be posted here because the actual
+ * size values of the component windows are not known
+ * until after the WM_CREATE message is dispatched.
+ */
+ GetClientRect(hwnd, &rmain);
+ PostMessage(hwnd, WM_SIZE, 0, MAKELPARAM(rmain.right, rmain.bottom));
+ return DefWindowProc(hwnd, msg, wparam, lparam);
+ }
+
+
+ gw = nsws_get_gui_window(hwnd);
+ if (gw == NULL) {
+ LOG("Unable to find gui window structure for hwnd %p", hwnd);
+ return DefWindowProc(hwnd, msg, wparam, lparam);
+ }
+
+ switch (msg) {
+
+ case WM_CONTEXTMENU:
+ if (nsws_ctx_menu(gw, hwnd, GET_X_LPARAM(lparam),
+ GET_Y_LPARAM(lparam))) {
+ return 0;
+ }
+ break;
+
+ case WM_COMMAND:
+ if (nsws_window_command(hwnd, gw, HIWORD(wparam),
+ LOWORD(wparam), (HWND)lparam) == 0) {
+ return 0;
+ }
+ break;
+
+ case WM_SIZE:
+ return nsws_window_resize(gw, hwnd, wparam, lparam);
+
+ case WM_NCDESTROY:
+ RemoveProp(hwnd, TEXT("GuiWnd"));
+ browser_window_destroy(gw->bw);
+ if (--open_windows <= 0) {
+ win32_set_quit(true);
+ }
+ break;
+
+ }
+
+ return DefWindowProc(hwnd, msg, wparam, lparam);
+}
+
+
+/**
+ * Create the main window class.
+ */
+nserror
+nsws_create_main_class(HINSTANCE hinstance) {
+ nserror ret = NSERROR_OK;
+ WNDCLASSEX w;
+
+ /* main window */
+ w.cbSize = sizeof(WNDCLASSEX);
+ w.style = 0;
+ w.lpfnWndProc = nsws_window_event_callback;
+ w.cbClsExtra = 0;
+ w.cbWndExtra = 0;
+ w.hInstance = hinstance;
+ w.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(IDR_NETSURF_ICON));
+ w.hCursor = NULL;
+ w.hbrBackground = (HBRUSH)(COLOR_MENU + 1);
+ w.lpszMenuName = NULL;
+ w.lpszClassName = windowclassname_main;
+ w.hIconSm = LoadIcon(hinstance, MAKEINTRESOURCE(IDR_NETSURF_ICON));
+
+ if (RegisterClassEx(&w) == 0) {
+ win_perror("DrawableClass");
+ ret = NSERROR_INIT_FAILED;
+ }
+
+ hInstance = hinstance;
+
+ return ret;
+}
+
+
+
+
+
+
+/**
+ * create a new gui_window to contain a browser_window.
+ *
+ * \param bw the browser_window to connect to the new gui_window
+ * \param existing An existing window.
+ * \param flags The flags controlling the construction.
+ * \return The new win32 gui window or NULL on error.
+ */
+static struct gui_window *
+win32_window_create(struct browser_window *bw,
+ struct gui_window *existing,
+ gui_window_create_flags flags)
+{
+ struct gui_window *gw;
+
+ LOG("Creating gui window for browser window %p", bw);
+
+ gw = calloc(1, sizeof(struct gui_window));
+
+ if (gw == NULL) {
+ return NULL;
+ }
+
+ /* connect gui window to browser window */
+ gw->bw = bw;
+
+ gw->width = 800;
+ gw->height = 600;
+ gw->scale = 1.0;
+ gw->toolbuttonsize = 24;
+ gw->requestscrollx = 0;
+ gw->requestscrolly = 0;
+ gw->localhistory = NULL;
+
+ gw->mouse = malloc(sizeof(struct browser_mouse));
+ if (gw->mouse == NULL) {
+ free(gw);
+ LOG("Unable to allocate mouse state");
+ return NULL;
+ }
+ gw->mouse->gui = gw;
+ gw->mouse->state = 0;
+ gw->mouse->pressed_x = 0;
+ gw->mouse->pressed_y = 0;
+
+ /* add window to list */
+ if (window_list != NULL)
+ window_list->prev = gw;
+ gw->next = window_list;
+ window_list = gw;
+
+ gw->main = nsws_window_create(gw);
+ gw->toolbar = nsws_window_create_toolbar(gw, gw->main);
+ gw->statusbar = nsws_window_create_statusbar(gw);
+ gw->drawingarea = nsws_window_create_drawable(hInstance, gw->main, gw);
+
+ LOG("new window: main:%p toolbar:%p statusbar %p drawingarea %p", gw->main, gw->toolbar, gw->statusbar, gw->drawingarea);
+
+ font_hwnd = gw->drawingarea;
+ open_windows++;
+ ShowWindow(gw->main, SW_SHOWNORMAL);
+
+ return gw;
+}
+
+
+/**
+ * window cleanup code
+ */
+static void win32_window_destroy(struct gui_window *w)
+{
+ if (w == NULL)
+ return;
+
+ if (w->prev != NULL)
+ w->prev->next = w->next;
+ else
+ window_list = w->next;
+
+ if (w->next != NULL)
+ w->next->prev = w->prev;
+
+ DestroyAcceleratorTable(w->acceltable);
+
+ free(w);
+ w = NULL;
+}
+
+
+
+
+static void
+win32_window_update_box(struct gui_window *gw, const struct rect *rect)
+{
+ /* LOG("gw:%p %f,%f %f,%f", gw, data->redraw.x, data->redraw.y, data->redraw.width, data->redraw.height); */
+
+ if (gw == NULL)
+ return;
+
+ RECT redrawrect;
+
+ redrawrect.left = (long)rect->x0 - (gw->scrollx / gw->scale);
+ redrawrect.top = (long)rect->y0 - (gw->scrolly / gw->scale);
+ redrawrect.right =(long)rect->x1;
+ redrawrect.bottom = (long)rect->y1;
+
+ RedrawWindow(gw->drawingarea, &redrawrect, NULL,
+ RDW_INVALIDATE | RDW_NOERASE);
+
+}
+
+
+
+
+
+static void win32_window_get_dimensions(struct gui_window *gw, int *width, int *height,
+ bool scaled)
+{
+ if (gw == NULL)
+ return;
+
+ LOG("get dimensions %p w=%d h=%d", gw, gw->width, gw->height);
+
+ *width = gw->width;
+ *height = gw->height;
+}
+
+static void win32_window_update_extent(struct gui_window *w)
+{
+
+}
+
+/**
+ * callback from core to reformat a window.
+ *
+ * \param gw The win32 gui window to reformat.
+ */
+static void win32_window_reformat(struct gui_window *gw)
+{
+ if (gw != NULL) {
+ browser_window_reformat(gw->bw, false, gw->width, gw->height);
+ }
+}
+
+
+/**
+ * set window title
+ *
+ * \param w the Windows gui window.
+ * \param title to set on window
+ */
+static void win32_window_set_title(struct gui_window *w, const char *title)
+{
+ if (w == NULL)
+ return;
+ LOG("%p, title %s", w, title);
+ char *fulltitle = malloc(strlen(title) +
+ SLEN(" - NetSurf") + 1);
+ if (fulltitle == NULL) {
+ win32_warning("NoMemory", 0);
+ return;
+ }
+ strcpy(fulltitle, title);
+ strcat(fulltitle, " - NetSurf");
+ SendMessage(w->main, WM_SETTEXT, 0, (LPARAM)fulltitle);
+ free(fulltitle);
+}
+
+
+static nserror win32_window_set_url(struct gui_window *w, nsurl *url)
+{
+ SendMessage(w->urlbar, WM_SETTEXT, 0, (LPARAM) nsurl_access(url));
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * set the status bar message
+ */
+static void win32_window_set_status(struct gui_window *w, const char *text)
+{
+ if (w == NULL)
+ return;
+ SendMessage(w->statusbar, WM_SETTEXT, 0, (LPARAM)text);
+}
+
+
+/**
+ * set the pointer shape
+ */
+static void win32_window_set_pointer(struct gui_window *w,
+ gui_pointer_shape shape)
+{
+ SetCursor(nsws_get_pointer(shape));
+}
+
+
+/**
+ * place caret in window
+ */
+static void win32_window_place_caret(struct gui_window *w, int x, int y,
+ int height, const struct rect *clip)
+{
+ if (w == NULL)
+ return;
+ CreateCaret(w->drawingarea, (HBITMAP)NULL, 1, height * w->scale);
+ SetCaretPos(x * w->scale - w->scrollx,
+ y * w->scale - w->scrolly);
+ ShowCaret(w->drawingarea);
+}
+
+/**
+ * clear window caret
+ */
+static void win32_window_remove_caret(struct gui_window *w)
+{
+ if (w == NULL)
+ return;
+ HideCaret(w->drawingarea);
+}
+
+
+
+
+static void win32_window_start_throbber(struct gui_window *w)
+{
+ if (w == NULL)
+ return;
+ nsws_window_update_forward_back(w);
+
+ if (w->mainmenu != NULL) {
+ EnableMenuItem(w->mainmenu, IDM_NAV_STOP, MF_ENABLED);
+ EnableMenuItem(w->mainmenu, IDM_NAV_RELOAD, MF_GRAYED);
+ }
+ if (w->rclick != NULL) {
+ EnableMenuItem(w->rclick, IDM_NAV_STOP, MF_ENABLED);
+ EnableMenuItem(w->rclick, IDM_NAV_RELOAD, MF_GRAYED);
+ }
+ if (w->toolbar != NULL) {
+ SendMessage(w->toolbar, TB_SETSTATE, (WPARAM) IDM_NAV_STOP,
+ MAKELONG(TBSTATE_ENABLED, 0));
+ SendMessage(w->toolbar, TB_SETSTATE,
+ (WPARAM) IDM_NAV_RELOAD,
+ MAKELONG(TBSTATE_INDETERMINATE, 0));
+ }
+ w->throbbing = true;
+ Animate_Play(w->throbber, 0, -1, -1);
+}
+
+
+
+static void win32_window_stop_throbber(struct gui_window *w)
+{
+ if (w == NULL)
+ return;
+
+ nsws_window_update_forward_back(w);
+ if (w->mainmenu != NULL) {
+ EnableMenuItem(w->mainmenu, IDM_NAV_STOP, MF_GRAYED);
+ EnableMenuItem(w->mainmenu, IDM_NAV_RELOAD, MF_ENABLED);
+ }
+
+ if (w->rclick != NULL) {
+ EnableMenuItem(w->rclick, IDM_NAV_STOP, MF_GRAYED);
+ EnableMenuItem(w->rclick, IDM_NAV_RELOAD, MF_ENABLED);
+ }
+
+ if (w->toolbar != NULL) {
+ SendMessage(w->toolbar, TB_SETSTATE, (WPARAM) IDM_NAV_STOP,
+ MAKELONG(TBSTATE_INDETERMINATE, 0));
+ SendMessage(w->toolbar, TB_SETSTATE,
+ (WPARAM) IDM_NAV_RELOAD,
+ MAKELONG(TBSTATE_ENABLED, 0));
+ }
+
+ w->throbbing = false;
+ Animate_Stop(w->throbber);
+ Animate_Seek(w->throbber, 0);
+}
+
+static struct gui_window_table window_table = {
+ .create = win32_window_create,
+ .destroy = win32_window_destroy,
+ .redraw = win32_window_redraw_window,
+ .update = win32_window_update_box,
+ .get_scroll = win32_window_get_scroll,
+ .set_scroll = win32_window_set_scroll,
+ .get_dimensions = win32_window_get_dimensions,
+ .update_extent = win32_window_update_extent,
+ .reformat = win32_window_reformat,
+
+ .set_title = win32_window_set_title,
+ .set_url = win32_window_set_url,
+ .set_status = win32_window_set_status,
+ .set_pointer = win32_window_set_pointer,
+ .place_caret = win32_window_place_caret,
+ .remove_caret = win32_window_remove_caret,
+ .start_throbber = win32_window_start_throbber,
+ .stop_throbber = win32_window_stop_throbber,
+};
+
+struct gui_window_table *win32_window_table = &window_table;
+
+/* exported interface documented in windows/window.h */
+struct gui_window *nsws_get_gui_window(HWND hwnd)
+{
+ struct gui_window *gw = NULL;
+ HWND phwnd = hwnd;
+
+ /* scan the window hierachy for gui window */
+ while (phwnd != NULL) {
+ gw = GetProp(phwnd, TEXT("GuiWnd"));
+ if (gw != NULL)
+ break;
+ phwnd = GetParent(phwnd);
+ }
+
+ if (gw == NULL) {
+ /* try again looking for owner windows instead */
+ phwnd = hwnd;
+ while (phwnd != NULL) {
+ gw = GetProp(phwnd, TEXT("GuiWnd"));
+ if (gw != NULL)
+ break;
+ phwnd = GetWindow(phwnd, GW_OWNER);
+ }
+ }
+
+ return gw;
+}
+
+
+/* exported interface documented in windows/window.h */
+bool nsws_window_go(HWND hwnd, const char *urltxt)
+{
+ struct gui_window *gw;
+ nsurl *url;
+
+ gw = nsws_get_gui_window(hwnd);
+ if (gw == NULL)
+ return false;
+
+ if (nsurl_create(urltxt, &url) != NSERROR_OK) {
+ win32_warning("NoMemory", 0);
+ } else {
+ browser_window_navigate(gw->bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+
+ return true;
+}
+
+
+/* exported interface documented in windows/window.h */
+HWND gui_window_main_window(struct gui_window *w)
+{
+ if (w == NULL)
+ return NULL;
+ return w->main;
+}
+
+
+/* exported interface documented in windows/window.h */
+struct nsws_localhistory *gui_window_localhistory(struct gui_window *w)
+{
+ if (w == NULL)
+ return NULL;
+ return w->localhistory;
+}
diff --git a/frontends/windows/window.h b/frontends/windows/window.h
new file mode 100644
index 000000000..ec54287f4
--- /dev/null
+++ b/frontends/windows/window.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2011 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 _NETSURF_WINDOWS_WINDOW_H_
+#define _NETSURF_WINDOWS_WINDOW_H_
+
+/** The window operation function table for win32 */
+extern struct gui_window_table *win32_window_table;
+
+#include "desktop/mouse.h"
+
+struct browser_mouse {
+ struct gui_window *gui;
+ struct box *box;
+
+ double pressed_x;
+ double pressed_y;
+ bool waiting;
+ browser_mouse_state state;
+};
+
+struct gui_window {
+ /* The front's private data connected to a browser window */
+ /* currently 1<->1 gui_window<->windows window [non-tabbed] */
+ struct browser_window *bw; /** the browser_window */
+
+ HWND main; /**< handle to the actual window */
+ HWND toolbar; /**< toolbar handle */
+ HWND urlbar; /**< url bar handle */
+ HWND throbber; /** throbber handle */
+ HWND drawingarea; /**< drawing area handle */
+ HWND statusbar; /**< status bar handle */
+ HWND vscroll; /**< vertical scrollbar handle */
+ HWND hscroll; /**< horizontal scrollbar handle */
+
+ HMENU mainmenu; /**< the main menu */
+ HMENU rclick; /**< the right-click menu */
+ struct nsws_localhistory *localhistory; /**< handle to local history window */
+ int width; /**< width of window */
+ int height; /**< height of drawing area */
+
+ int toolbuttonc; /**< number of toolbar buttons */
+ int toolbuttonsize; /**< width, height of buttons */
+ bool throbbing; /**< whether currently throbbing */
+
+ struct browser_mouse *mouse; /**< mouse state */
+
+ HACCEL acceltable; /**< accelerators */
+
+ float scale; /**< scale of content */
+
+ int scrollx; /**< current scroll location */
+ int scrolly; /**< current scroll location */
+
+ RECT *fullscreen; /**< memorize non-fullscreen area */
+ RECT redraw; /**< Area needing redraw. */
+ int requestscrollx, requestscrolly; /**< scolling requested. */
+ struct gui_window *next, *prev; /**< global linked list */
+};
+
+
+/**
+ * Obtain gui window structure from window handle.
+ *
+ * \param hwnd The window handle.
+ * \return The gui window associated with the window handle.
+ */
+struct gui_window *nsws_get_gui_window(HWND hwnd);
+
+/**
+ * Cause a browser window to navigate to a url
+ *
+ * \param hwnd The win32 handle to the browser window or one of its decendants.
+ * \param urltxt The URL to navigate to.
+ */
+bool nsws_window_go(HWND hwnd, const char *urltxt);
+
+void win32_window_set_scroll(struct gui_window *w, int sx, int sy);
+
+nserror nsws_create_main_class(HINSTANCE hinstance);
+
+/**
+ * Get the main win32 window handle from a gui window
+ */
+HWND gui_window_main_window(struct gui_window *);
+
+
+/**
+ * Get the localhistory win32 window handle from a gui window
+ */
+struct nsws_localhistory *gui_window_localhistory(struct gui_window *);
+
+
+#endif /* _NETSURF_WINDOWS_WINDOW_H_ */