diff options
author | Don Scorgie <dscorgie@src.gnome.org> | 2007-07-30 21:46:35 +0000 |
---|---|---|
committer | Don Scorgie <dscorgie@src.gnome.org> | 2007-07-30 21:46:35 +0000 |
commit | d9eb206fa6e6e42fad9cf1a07947e5ec502e4b69 (patch) | |
tree | 37f37194801a20aad7cd6ae7468cae128884ad55 | |
parent | 89a4f049d8729dc6cac08ad14e5e4b1ba9c6d9d2 (diff) | |
download | yelp-d9eb206fa6e6e42fad9cf1a07947e5ec502e4b69.tar.gz |
Merge of yelp rework branch:
- Depreciate yelp-*-pager in favour of
yelp-document and such.
Gives us on-demand loading of pages
- Switch to using rarian instead of scrollkeeper
- Update to using shiny newness of gnome-doc-utils
- Make printing docbook pretty
- Clean up lots of errors and general cleanup
of other source files
- Make our resolver much, much better
- and remove reliance on libgnome there
svn path=/trunk/; revision=2848
58 files changed, 10431 insertions, 2746 deletions
@@ -1,3 +1,904 @@ + +== REWORK CHANGES == + +2007-07-30 Don Scorgie <dscorgie@svn.gnome.org> + + Merge of yelp rework branch: + - Depreciate yelp-*-pager in favour of + yelp-document and such. + Gives us on-demand loading of pages + - Switch to using rarian instead of scrollkeeper + - Update to using shiny newness of gnome-doc-utils + - Make printing docbook pretty + - Clean up lots of errors and general cleanup + of other source files + - Make our resolver much, much better + - and remove reliance on libgnome there + + +2007-07-30 Don Scorgie <dscorgie@svn.gnome.org> + + * src/yelp-settings.c: + * configure.in: + * src/yelp-settings.h: + * src/Makefile.am: + * stylesheets/yelp-common.xsl: + Make themes sort-of work. Good enough for + a release, anyway. + +2007-07-29 Don Scorgie <dscorgie@svn.gnome.org> + + * src/yelp-window.c: + Make loading of files directly work again + +2007-07-24 Don Scorgie <dscorgie@svn.gnome.org> + + * src/yelp-info.c: + * src/yelp-man.c: + * src/yelp-docbook.c: + Add in some pretty parameters + + * src/yelp-db-print.c: + Fix parameters to make printed docs *slightly* + prettier + + * src/yelp-search-parser.c: + Convert search across to use Rarian + and clean up lots of crud from the file + +2007-07-23 Don Scorgie <dscorgie@svn.gnome.org> + + * configure.ac: + * src/yelp-base.c: + * src/yelp-search-parser.c: + * src/yelp-search.c: + * src/yelp-window.c: + * src/yelp-window.h: + * src/yelp-utils.c: + * src/yelp-utils.h: + * src/Makefile.am: + * src/yelp-main.c: + Massive cleanup + Remove old code from yelp-utils + Fix shutdown requests from sessions (hopefully) + Remove as many warnings as possible (the rest are + mostly mozilla errors) + Make sure we get and set GLIB_GENMARSHAL as, apparently, + we have never done this which seems to result in badness + making the marshal.list + + * src/Makefile.am: + * configure.ac: + * src/yelp-toc.c: + * src/yelp-search-parser.c: + * src/yelp-window.c: + * src/yelp-info-parser.c: + Lots of fun updates... + Clean up yelp-window.c a LOT + Remove old, obsolete code + Make search, man and info mandatory + Bump g-d-u min version to include new db2xhtml + code + Remove extra printf I'd forgotten + +2007-07-10 Don Scorgie <dscorgie@svn.gnome.org> + + * src/yelp-window.c: + Handle external URI's + Beginnings of error support (not complete) + +2007-07-08 Don Scorgie <dscorgie@svn.gnome.org> + + * src/yelp-window.c: + * src/yelp-search.c: + * src/yelp-search.h: + * src/yelp-search-parser.c: + * src/yelp-search-parser.h: + * src/yelp-utils.c: + * src/Makefile.am: + First pass at searching. + Search still relies on scrollkeeper. Other + than that, it seems to all work (with basic search at least) + + * src/yelp-db-print.c: + * src/yelp-db-print.h: + * src/Makefile.am: + Make printing (pages and documents) work again. + Although, the calling xslt needs somewhat cleaned + up, it doesn't look particularly appealing atm. + + * configure.in: + Temporarily work around breakage in gnome-doc-utils + + * src/yelp-window.c: + Add back (empty) print menu items to stop errors + Add the "Loading..." and watch cursor when we are loading + Correctly set the sensitivity of relevant menu items for DocBooks + Make "About this document" work again + + * src/yelp-docbook.c: + Since the new g-d-u currently has a list of topics on the + right, don't display the sections pane + + * src/yelp-window.c: + Make history work again + Make loading HTML docs work + + * src/yelp-utils.h: + * src/yelp-utils.c: + Make a distinction between html and xhtml + to keep gecko happy + +2007-07-07 Don Scorgie <dscorgie@svn.gnome.org> + + * src/yelp-utils.c: + * src/yelp-toc.c: + Fix info page titles in TOC + Fix file:// uri's when with too many /'s + + * src/Makefile.am: + * src/yelp-info-parser.c: + * src/yelp-info.c: + * src/yelp-info.h: + * src/yelp-window.c: + Convert info pages across to new pager system + Hook up info pages into yelp, new style + Fix various things in the parser to cope with + having less available info for resolving frags + + * src/yelp-toc.c: + Fix the most annoying error message in the world EVER + Unexpected async xlib errors generally mean threading issues + except, apparently, when you're trying to parse using libxml + in which case, they could mean anything. In this case, it + meant the params passed in was not NULL terminating. + Why this resulted in an unexpected xlib async reply is + left as an exercise to the reader. I spent 7 days trying to + figure it out. + +2007-07-01 Don Scorgie <dscorgie@svn.gnome.org> + + * src/yelp-toc.c: + Now Rarian is fast, don't split out man and info pages. + Add in params so the TOC looks nice and shiny + Fix a couple of silly warnings + + * src/yelp-base.c: + Move initialisation of bookmarks and settings to before TOC + +2007-06-20 Don Scorgie <dscorgie@svn.gnome.org> + + * configure.in: + * src/test-resolver.c: + * src/yelp-toc.c: + * src/yelp-window.c: + * src/yelp-utils.c: + * src/yelp-utils.h: + Spoon is now Rarian. Bump min version to 0.5.0 + +2007-06-14 Don Scorgie <dscorgie@svn.gnome.org> + + * src/yelp-utils.c: + Minor fix to recognise man:<filepath> + + * src/Makefile.am: + * src/yelp-window.c: + Add support for docbook (yay!) and + man pages + Rename silly function names to something slightly + more sensible + Update my email address in help dialog (minor) + + * src/test-document.c: + * src/yelp-document.c: + * src/yelp-document.h: + * src/yelp-docbook.c: + Add support for getting the sections GtkTreeModel + out + + * src/yelp-page.c: + * src/yelp-page.h: + Add functions to get the length (remaining) + of page + + +2007-06-13 Don Scorgie <dscorgie@svn.gnome.org> + + * src/yelp-window.c: + Make bookmarks work in the New World Order + + * src/yelp-base.c: + start loading yelp-toc at start of + base to take advantage of threading + and make the toc load even quicker + (except man pages of course) + + * src/yelp-toc.c: + Make icon work (when the rest of the + customisations are plumbed in) + + * src/yelp-utils.c: + Fix result for TOC pages + + * src/yelp-document.c: + Fix crash when canceling page request + + * src/yelp-window.c: + Implement canceling of requests + Implement location dialog + +2007-06-12 Don Scorgie <dscorgie@svn.gnome.org> + + * src/yelp-window.c: + * src/yelp-base.c: + * src/yelp-window.h: + * src/yelp-utils.c: + * src/yelp-utils.h: + * src/Makefile.am: + * stylesheets/toc2html.xsl: + First pass at making yelp work again + Very, very broken, but TOC shows up (and is navigatable) + Thought, nothing else works and is + liable to crash if used + rename YELP_TYPE_* to YELP_SPOON_TYPE_* + to stop conflict when compiling + Oh, and introduce hundreds of compiler warnings + + * src/yelp-toc.c: + * src/yelp-toc.h: + Make TOC doc singleton + +2007-06-12 Don Scorgie <dscorgie@svn.gnome.org> + + * src/yelp-utils.c: + Add ghelp resolve support + Many minor fixes to pass default test suite + + * src/test-resolver.c: + Add default tests + + +2007-06-11 Don Scorgie <dscorgie@svn.gnome.org> + + * src/yelp-utils.c: + Add man page resolver support. Make info fall back + to man pages when not found + +2007-06-11 Don Scorgie <dscorgie@svn.gnome.org> + + * src/test-resolver.c: + Add gnome-vfs-init() and section (for results section) + + * src/yelp-utils.c: + Add support for resolving info and full path uri's. + + * src/yelp-utils.h: + Add long description of resolver function + +2007-06-02 Don Scorgie <dscorgie@cvs.gnome.org> + + * src/test-resolver.c: + Bit more framework + + * src/yelp-utils.c: + * src/yelp-utils.h: + Beginnings of testing (sort-of recognises different types) + Add enum for different types + Add lots of printfs that should be removed at some point + +2007-05-31 Don Scorgie <dscorgie@svn.gnome.org> + + * src/yelp-utils.h: + * src/yelp-utils.c: + Beginning (empty) of new utils resolver (masked by DON_UTILS define) + + * src/test-resolver.c: + * src/Makefile.am: + Beginnings of resolver test + + +2007-04-30 Don Scorgie <dscorgie@cvs.gnome.org> + + * src/yelp-toc.c: + Add in man pages + Do an interesting use of the new stuff to + run 3 transforms - main, info and man so + we quickly get the main page, then get man and info when ready + + * src/yelp-document.h: + * src/yelp-document.c: + Add yelp_document_has_page that's used + in yelp-toc + +2007-04-26 Don Scorgie <dscorgie@cvs.gnome.org> + + * src/yelp-toc.c: + Add info page support + + * stylesheets/toc2html.xsl: + * src/test-document.c: + * src/yelp-toc.c: + Make TOC work properly with new document structure + + * src/yelp-page.c: + * src/yelp-docbook.c: + Various misc. changes to make docbook work properly + Add TODO's + +2007-04-24 Don Scorgie <dscorgie@cvs.gnome.org> + + * src/yelp-toc.c: + * src/yelp-toc.h: + * src/yelp-db-pager.c: + * src/test-document.c: + * src/Makefile.am: + Initial commit of new yelp-toc (broken) + Add toc option to test-document (broken) + + +2007-04-22 Shaun McCance <shaunm@gnome.org> + + * ChangeLog + * src/Makefile.am: + * src/test-man-parser.c: + * src/yelp-debug.h: + * src/yelp-error.c: + * src/yelp-error.h: + * src/yelp-io-channel.c: + * src/yelp-man-parser.c: + * src/yelp-man-parser.h: + * src/yelp-utils.c: + * src/yelp-utils.h: + - Merging yelp-document into yelp-spoon + +2007-04-14 Don Scorgie <dscorgie@cvs.gnome.org> + + * src/yelp-toc-pager.c: + Convert info TOC to use new spoon work + (slightly later) Convert man TOC to use new spoon work as well + +2007-03-25 Don Scorgie <dscorgie@cvs.gnome.org> + + * stylesheets/toc2html.xsl: + * src/yelp-toc-pager.c: + First pass at spoon integration + TOC correctly displayed, replacing all sk code + with spoon_for_each_in_category() + + * src/yelp-toc-pager.c: + * configure.in: + First work on Spoon integration + Search for pkg-config Spoon file + Run a spoon-for-each, printing doc names + +2007-03-12 Shaun McCance <shaunm@gnome.org> + + * configure.in: + * NEWS: + - Version 2.18.0 + +2007-02-23 Christian Persch <chpe@svn.gnome.org> + + * src/Yelper.cpp: + * src/Yelper.h: + Fix for mozilla API change on trunk. + +2006-12-01 Christian Persch <chpe@cvs.gnome.org> + + * configure.in: + * m4/gecko.m4: + * src/Makefile.am: + * src/Yelper.cpp: + Fix for mozilla API change on trunk. + +2006-11-20 Don Scorgie <dscorgie@cvs.gnome.org> + + * NEWS: + * configure.in: + - Version 2.16.2 + +2006-11-20 Don Scorgie <dscorgie@cvs.gnome.org> + + * src/yelp-utils.c: + When info:dir contains a link to a subdir, + but not an info name, work as expected + (Really fixes bug #371680) + + * src/yelp-io-channel.c: + Check the file exists and is valid before trying + to read it (fixes bug #371680) + + * src/yelp-gecko-services.cpp: + Fix printing ranges (bug #370618, Christian Persch) + + * src/yelp-info-parser.c: + When whitespace on new line of a note is < 2, + don't crash (bug #376861) + Fix crash on libc info page (bug #367410, Matthias Clasen) + +2006-11-05 Don Scorgie <dscorgie@cvs.gnome.org> + + * src/yelp-main.c: + Don't crash when saving session (bug #364790) + + * src/yelp-search-pager.c: + Replace ':' chars in search strings with ' ' + Stops from crashing (bug #364768) + Don't chew memory when search string contains + 2 consecutive spaces + +2006-10-25 Don Scorgie <dscorgie@cvs.gnome.org> + + * src/yelp-info-parser.c: + Fix double free in stupid link types only used by gdb + info page (apparently). Bug #364850 + + * src/yelp-search-pager.c: + Don't crash when searching for empty term. Bug #363949 + +2006-10-07 Elijah Newren <newren gmail com> + + * src/yelp-print.c (yelp_print_present_status_dialog): Remove + markup from translatable messages. #360450 + +2006-10-05 Don Scorgie <dscorgie@cvs.gnome.org> + + * src/yelp-utils.c: + Don't crash when manpath (prog) || MANPATH (env.) + is available + +==================== 2.16.1 ==================== +2006-10-02 Don Scorgie <dscorgie@cvs.gnome.org> + + * NEWS: + * configure.in: + - Version 2.16.1 + +2006-09-29 Don Scorgie <dscorgie@cvs.gnome.org> + + * src/yelp-settings.c: + * src/yelp-settings.h: + * src/yelp-window.c: + Make F7 toggle caret properly + Set initial state of caret in prefs window + +2006-09-17 Christian Persch <chpe@cvs.gnome.org> + + * src/Yelper.cpp: + * src/Yelper.h: + + Fix the build with gecko trunk. + +2006-09-12 Christian Persch <chpe@cvs.gnome.org> + + * m4/gecko.m4: + + Prefer xulrunner over mozilla. + +==================== 2.16.0 ==================== +2006-09-04 Brent Smith <gnome@nextreality.net> + + * NEWS: + * configure.in: + - Version 2.16.0 + +2006-09-03 Brent Smith <gnome@nextreality.net> + + * src/yelp-toc-pager.c: (toc_add_doc_info): check if the document + category exists, and return if it does not should fix 353554 + +2006-08-30 Don Scorgie <don@Madaline> + + * src/yelp-io-channel.c: + Don't double free bzip streams + +2006-08-30 Don Scorgie <dscorgie@cvs.gnome.org> + + * src/yelp-search-pager.c: + Move online url to perminant address at gnome.org + +2006-08-28 Don Scorgie <dscorgie@cvs.gnome.org> + + * src/yelp-io-channel.c: + Supress critical warning when it isn't actually + critical + + * src/yelp-info-parser.c: + * src/yelp-io-channel.c: + Don't fall over when part of the info file + goes missing (bug #353209) + +2006-08-16 Don Scorgie <dscorgie@cvs.gnome.org> + + * src/yelp-search-pager.c: + Fix crash when apropos goes nuts (bug #347467) + Patch from Priit Laes + +2006-08-10 Don Scorgie <dscorgie@cvs.gnome.org> + + * src/yelp-info-parser.c: + Fix stupid crash on CVS info file caused by + fake *Notes, trying to trick yelp into making + them hyperlinks when they shouldn't be + +==================== 2.15.91 ==================== +2006-08-07 Don Scorgie <dscorgie@cvs.gnome.org> + + * NEWS: + * configure.in: + - Version 2.15.91 + + * src/yelp-search-pager.c: + Remove translation of online URL in anticipation of a + new script to do the redirection + +2006-08-05 Don Scorgie <dscorgie@cvs.gnome.org> + + * src/yelp-pager.c: + * src/yelp-toc-pager.c: + * src/yelp-window.c: + * src/yelp-search-pager.c: + Various misc. cleanups. + - Keep track of idle functions and callbacks + - don't use depreciated gtk_idle_add function + Based on patch from Christian Persch + + * src/yelp-main.c: + Initialise dbus threading before gnome-vfs. Bug #350079 + +2006-07-25 Shaun McCance <shaunm@gnome.org> + + * data/toc.xml.in: + - Changed "Sound & Vision" to "Sound & Video" + +2006-07-25 Don Scorgie <dscorgie@cvs.gnome.org> + + * stylesheets/search2html.xsl: + Remove fixed font sizes from search results + Remove fixed colours from search results + Fixes bug #348563 and makes page respect + theme setting + + +==================== 2.15.5 ==================== + +2006-07-24 Don Scorgie <dscorgie@cvs.gnome.org> + + * NEWS: + * configure.in: + - Version 2.15.5 + +2006-07-24 Don Scorgie <dscorgie@cvs.gnome.org> + + * src/yelp-search-pager.c: + * stylesheets/search2html.xsl: + Add a new link to the search results to allow + repeating the search online at the GNOME support forums + Additionally: Allow better translation of the message + Allow translation of the site name + Additionally (2): Allow online search locations to be + translated to point to support sites + in a native language + + +2006-07-23 Brent Smith <gnome@nextreality.net> + + * configure.in: Depend on beagle 0.2.4 since that is the first version + with the beagle_util_daemon_is_running() function. + * src/yelp-search-pager.c: (search_pager_class_init): + Use beagle_util_daemon_is_running() to make sure that the daemon is + actually running, and the socket at ~/.beagle/socket is not stale. + Fixes #348362 + +2006-07-23 Don Scorgie <dscorgie@cvs.gnome.org> + + * src/yelp-search-pager.c: + Large basic search update: + - Common words are ignored (how, do etc.) + - Common suffixes and prefixes are checked + - Fix problems with searching in some docbook docs + - Better scoring mechanism + - Slightly improved matching algorithm + - Fix searching HTML docs + - Better snippet highlighting + - Clean sections of code slightly + Bugs fixed: #341797, #341800, #347819 (work around) + Partially fixed: #335962 + + + * src/yelp-settings.c: + Use a gtk_link_button instead of gnome_href + + * src/yelp-print.c: + * src/yelp-print.h: + * src/yelp-gecko-services.cpp: + * src/yelp-gecko-services.h: + * src/Yelper.cpp: + * configure.in: + Move to GtkPrinting. + Bump min gtk version to 2.10 + Remove libgnomeprint dependency + +2006-07-22 Don Scorgie <dscorgie@cvs.gnome.org> + + * configure.in: + Remove additional but not run pkg-config check + + * src/yelp-window.c: + Remove a couple of headers that shouldn't be here + Remove call to gnome_help_display_desktop and + replace with a much simpler call to create a new window + and show the relevant section of the user guide, making it + much quicker to display help->Contents + + * src/yelp-html.cpp: + * src/yelp-window.c: + Remove libgnomeui dependancy from both. + Switch to GTK_STOCK_ABOUT instead of libgnome equivalent + +2006-07-21 Don Scorgie <dscorgie@cvs.gnome.org> + + * src/yelp-window.c: + * src/yelp-search-pager.c: + Clean up some compiler warnings + (Casts and unused variables) + +2006-07-19 Don Scorgie <dscorgie@cvs.gnome.org> + + * src/yelp-toc-pager.c: + Select the correct translation of the description of sections + Fixes bug #347816 + Clean up a couple of compiler warnings + +==================== 2.15.4 ==================== + +2006-07-10 Brent Smith <gnome@nextreality.net> + + * NEWS: + * configure.in: + - Version 2.15.4 + +2006-07-10 Brent Smith <gnome@nextreality.net> + + * data/info.xml.in: + * data/man.xml.in: + * data/scrollkeeper.xml: + * data/toc.xml.in: + - Restructure the table of contents to make most documentation + accessible via a single click (except for man pages and info pages) + - Put descriptions for each section at the top in the right side + above the document listing + - Rename the top level category to 'Desktop' and include the + User guide, System Administration Guide, and Accessibility Guide + in this section + * src/yelp-pager.c: (yelp_pager_get_page_from_id): + * src/yelp-pager.h: + * src/yelp-toc-pager.c: (toc_pager_resolve_frag), + (toc_process_pending), (process_mandir_pending), + (process_info_pending), (process_read_menu), (process_cleanup): + * src/yelp-window.c: (window_do_load_pager), (pager_finish_cb): + Load each table of contents page on demand instead of + creating all pages at once. + * stylesheets/toc2html.xsl: + Maintain a list of categories on the left hand side in the + table of contents, that expands subcategories for the currently + selected category + +2006-07-07 Wouter Bolsterlee <uws+gnome@xs4all.nl> + + * src/yelp-window.c: (tree_row_expand_cb): + Expand/collapse on double-click. Really fixes bug + #346871. + +2006-07-07 Don Scorgie <dscorgie@cvs.gnome.org> + + * src/yelp-window.c: + Expand sections on double-click (bug #346871) + +======= +2007-04-12 Shaun McCance <shaunm@gnome.org> + + * src/Makefile.am: + * src/yelp-man.c: + * src/yelp-man.h: + * src/test-document.c: + - Made man pages work with YelpDocument + + * src/yelp-docbook.c: + * src/yelp-document.c: + - General clean up + +2007-04-12 Shaun McCance <shaunm@gnome.org> + + * src/yelp-document.c: + * src/yelp-page.c: + * src/yelp-page.h: + - Added preliminary support for mime types in yelp-page + +2007-04-12 Shaun McCance <shaunm@gnome.org> + + * src/yelp-man-parser.c: + * src/Makefile.am: + - Made yelp-man-parser use yelp-debug + +2007-04-11 Shaun McCance <shaunm@gnome.org> + + * src/yelp-error.c: + * src/yelp-error.h: + * src/yelp-io-channel.c: + - Added back the GError convenience functions + + * src/Makefile.am: + * src/test-man-parser.c: + * src/yelp-man-parser.c: + * src/yelp-man-parser.h: + - Getting the man stuff to work in the new world order + + * src/yelp-transform.c: + - Added a cast to shut gcc up + + * src/test-document.c: + * src/yelp-document.h: + * src/yelp-docbook.c: + * src/yelp-docbook.h: + * src/yelp-page.c: + * src/yelp-page.h: + - Moving much of the request-handling to YelpDocument + +2007-04-04 Shaun McCance <shaunm@gnome.org> + + * src/yelp-error.c: + * src/yelp-error.h: + - Added yelp_error_copy + + * src/yelp-transform.c: + - Added a few debug statements + + * src/test-transform.c: + - Added the --random-timeout option + + * src/yelp-page.c: + - Don't use g_free with memory slicese. + + * src/yelp-docbook.c: + * src/yelp-docbook.h: + * src/yelp-document.c: + * src/yelp-document.h: + - Added a more-or-less complete YelpDocument implementation for DocBook + + * src/test-document.c: + * src/Makefile.am: + - Added a test program for YelpDocument, currently only using DocBook + +2007-03-22 Shaun McCance <shaunm@gnome.org> + + * src/test-transform.c: + * src/yelp-transform.c: + * src/yelp-transform.h: + - Do not do callbacks if released + + * src/yelp-page.h: + - Added a comment + + * src/yelp-document.c: + * src/yelp-document.h: + - Filled out the abstract functions + + * src/Makefile.am: + - Some changes to the test programs we build + +2007-03-20 Shaun McCance <shaunm@gnome.org> + + * src/yelp-transform.c: + - Added some locks + + * src/Makefile.am: + * src/test-page.c: + - Adding a test application for YelpPage + + * src/yelp-page.c: + * src/yelp-page.h: + - Fairly complete YelpPage for strings + + * src/yelp-document.c: + * src/yelp-document.h: + - Big not-yet-done changes, but needed for test-page + +2007-03-20 Shaun McCance <shaunm@gnome.org> + + * src/yelp-transform.c: + - Fixed concurrency issues when freeing + + * src/test-transform.c: + - Using GOption to get a --timeout option + - Don't release a transform twice + +2007-03-20 Shaun McCance <shaunm@gnome.org> + + * src/test-transform.c: + * src/yelp-error.c: + * src/yelp-error.h: + - Making YelpError cleaner and more usable for our purposes + + * src/yelp-transform.c: + * src/yelp-transform.h: + - Copyright in 2007 too + + * src/yelp-page.c: + * src/yelp-page.h: + - The beginnings of the new read()-able YelpPage + + * src/Makefile.am: + - yelp-error.h != yelp-transform.h + +2007-03-17 Shaun McCance <shaunm@gnome.org> + + * src/test-transform.c: + * src/yelp-transform.c: + * src/yelp-transform.h: + - Changed the callback API a bit, basically ready + +2006-07-03 Brent Smith <gnome@nextreality.net> + + * src/yelp-document.c: (yelp_document_dispose), + (yelp_document_finalize), (yelp_document_class_init), + (yelp_document_init), (document_set_property), + (document_get_property), (yelp_document_get_page), + (yelp_document_cancel_get), (yelp_document_get_sections), + (yelp_document_add_page): + * src/yelp-document.h: + * src/yelp-page.c: (yelp_page_new), (yelp_page_set_id), + (yelp_page_get_id), (yelp_page_set_title), (yelp_page_get_title), + (yelp_page_set_contents), (yelp_page_get_contents), + (yelp_page_set_prev_id), (yelp_page_get_prev_id), + (yelp_page_set_next_id), (yelp_page_get_next_id), + (yelp_page_set_toc_id), (yelp_page_get_toc_id), (yelp_page_free): + * src/yelp-page.h: + - Add these to the correct directory + * yelp-document.c: + * yelp-document.h: + * yelp-page.c: + * yelp-page.h: + - Remove these (I put them in the wrong directory in the last commit) + +2006-07-03 Brent Smith <gnome@nextreality.net> + + * src/yelp-debug.h: Put an #ifdef around HAVE_CONFIG_H + * src/yelp-utils.c: + * src/yelp-utils.h: + - Get rid of the unused YelpDocPage type and related function(s) + - created branch "yelp-document", branchpoint + YELP_DOCUMENT_BRANCHPOINT for working on the new YelpDocument API; see + http://live.gnome.org/Yelp + * yelp-document.c: (yelp_document_dispose), + (yelp_document_finalize), (yelp_document_class_init), + (yelp_document_init), (document_set_property), + (document_get_property), (yelp_document_get_page), + (yelp_document_cancel_get), (yelp_document_get_sections), + (yelp_document_add_page): + * yelp-document.h: + * yelp-page.c: (yelp_page_new), (yelp_page_set_id), + (yelp_page_get_id), (yelp_page_set_title), (yelp_page_get_title), + (yelp_page_set_contents), (yelp_page_get_contents), + (yelp_page_set_prev_id), (yelp_page_get_prev_id), + (yelp_page_set_next_id), (yelp_page_get_next_id), + (yelp_page_set_toc_id), (yelp_page_get_toc_id), (yelp_page_free): + * yelp-page.h: + New files yelp-document.[ch] and yelp-page.[ch] for the new API + +2006-07-03 Shaun McCance <shaunm@gnome.org> + + * src/Makefile.am: + * src/test-transform.c: + * src/yelp-transform.c: + * src/yelp-transform.h: + * src/yelp-error.c: + * src/yelp-error.h: + - Glorious threaded XSLT transformation + +== END OF REWORK CHANGES == + + 2007-04-20 Christian Kirbach <Christian.Kirbach@googlemail.com> * yelp.desktop.in.in: diff --git a/configure.in b/configure.in index afca729e..d7a1d338 100644 --- a/configure.in +++ b/configure.in @@ -1,4 +1,4 @@ -AC_INIT([Yelp],[2.18.1],[http://bugzilla.gnome.org/enter_bug.cgi?product=yelp],[yelp]) +AC_INIT([Yelp],[2.18.0],[http://bugzilla.gnome.org/enter_bug.cgi?product=yelp],[yelp]) GNOME_COMMON_INIT @@ -66,7 +66,7 @@ AM_GLIB_DEFINE_LOCALEDIR([GNOMELOCALEDIR]) PKG_CHECK_MODULES(YELP, [ gconf-2.0 - gnome-doc-utils >= 0.3.1 + gnome-doc-utils >= 0.11.1 gtk+-unix-print-2.0 gnome-vfs-2.0 >= 1.1 gtk+-2.0 >= 2.10.0 @@ -78,6 +78,7 @@ PKG_CHECK_MODULES(YELP, libexslt >= 0.8.1 libstartup-notification-1.0 >= 0.8 dbus-glib-1 + rarian >= 0.5.0 ]) AC_SUBST([YELP_CFLAGS]) AC_SUBST([YELP_LIBS]) @@ -88,9 +89,15 @@ XSLT_PATH="`$PKG_CONFIG --variable=xsltdir gnome-doc-utils`" DB_TITLE="$XSLT_PATH""/docbook/common/db-title.xsl" AC_SUBST(DB_TITLE) -DB2HTML="`$PKG_CONFIG --variable=db2html gnome-doc-utils`" +DB2HTML="`$PKG_CONFIG --variable=db2xhtml gnome-doc-utils`" AC_SUBST(DB2HTML) +GDU_ICON_PATH="`$PKG_CONFIG --variable=icondir gnome-doc-utils`/hicolor/48x48/" +AC_SUBST(GDU_ICON_PATH) + +GLIB_GENMARSHAL="`$PKG_CONFIG --variable=glib_genmarshal glib-2.0`" +AC_SUBST(GLIB_GENMARSHAL) + # # If Pango included the shared library dependencies from X11 in # the pkg-config output, then we use that (to avoid duplicates). @@ -121,34 +128,6 @@ fi AC_SUBST(X_LIBS) -AC_ARG_ENABLE([man], - [AC_HELP_STRING([--enable-man], - [turn on man page support [default=yes]])],, - enable_man=yes) -AC_ARG_ENABLE([info], - [AC_HELP_STRING([--enable-info], - [turn on GNU info support [default=yes]])],, - enable_info=yes) -if test "x$enable_man" = "xyes"; then - AC_DEFINE(ENABLE_MAN, 1, [turn on man page support]) - AM_CONDITIONAL(ENABLE_MAN, true) -else - AM_CONDITIONAL(ENABLE_MAN, false) -fi -if test "x$enable_info" = "xyes"; then - AC_DEFINE(ENABLE_INFO, 1, [turn on GNU info support]) - AM_CONDITIONAL(ENABLE_INFO, true) -else - AM_CONDITIONAL(ENABLE_INFO, false) -fi - -if test "x$enable_man" = "xyes" -o "x$enable_info" = "xyes"; then - AC_DEFINE(ENABLE_MAN_OR_INFO, 1, [turn of man page or GNU info support]) - AM_CONDITIONAL(ENABLE_MAN_OR_INFO, true) -else - AM_CONDITIONAL(ENABLE_MAN_OR_INFO, false) -fi - dnl ****** dnl beagle @@ -157,7 +136,7 @@ dnl ****** BEAGLE_MODULES="libbeagle-0.0 >= 0.2.4" AC_ARG_WITH([search], - [AC_HELP_STRING([--with-search=basic|beagle|auto|no], + [AC_HELP_STRING([--with-search=basic|beagle|auto], [turn on search support [default=auto]])], , with_search=auto) @@ -165,7 +144,6 @@ if test "x$with_search" = "xauto"; then PKG_CHECK_MODULES(YELP_SEARCH, $BEAGLE_MODULES, have_beagle=yes, have_beagle=no) fi -AM_CONDITIONAL(ENABLE_SEARCH, test x"$with_search" != xno) AM_CONDITIONAL(ENABLE_BEAGLE, test x"$with_search" = xbeagle) if test "x$with_search" = "xbeagle"; then @@ -176,18 +154,12 @@ fi case "x$with_search" in "xbasic") search_backend="basic" - AC_DEFINE(ENABLE_SEARCH, 1, [Define if you want any search]) ;; "xbeagle") search_backend="beagle" - AC_DEFINE(ENABLE_SEARCH, 1, [Define if you want any search]) AC_DEFINE(ENABLE_BEAGLE, 1, [Define if you want the power of Beagle]) ;; - "xno") - search_backend="none" - ;; "xauto") - AC_DEFINE(ENABLE_SEARCH, 1) search_backend="auto - basic" if test x$have_beagle = xyes; then AC_DEFINE(ENABLE_BEAGLE, 1) @@ -195,7 +167,7 @@ case "x$with_search" in fi ;; *) - AC_MSG_ERROR([Unknown search type selected - Please use --with-search=auto|basic|beagle|no]) + AC_MSG_ERROR([Unknown search type selected - Please use --with-search=auto|basic|beagle]) ;; esac @@ -289,8 +261,6 @@ yelp-$VERSION: compiler: ${CC} Debug enabled: ${enable_debug} - Man Pages enabled: ${enable_man} - Info Pages enabled: ${enable_info} Search backend: ${search_backend} Mozilla version: ${MOZILLA} " diff --git a/po/ChangeLog b/po/ChangeLog index 8e7532c4..f9c50687 100644 --- a/po/ChangeLog +++ b/po/ChangeLog @@ -1,40 +1,3 @@ -2007-07-16 Ilkka Tuohela <hile@iki.fi> - - * fi.po: Updated Finnish translation. - -2007-07-16 Sunil Mohan Adapa <sunil@atc.tcs.com> - - * te.po: Added Telugu translation done by - Bharat Kumar <jonnalagaddabharat@gmail.com>. - -2007-06-28 Priit Laes <plaes@svn.gnome.org> - - * et.po: Estonian translation update by Ivar Smolin. - -2007-06-13 Yair Hershkovitz <yairhr@gmail.com> - - * he.po: Updated Hebrew translation. - -2007-05-23 Rhys Jones <rhys@sucs.org> - - * cy.po: Updated Welsh translation. - -2007-04-17 Guntupalli Karunakar <karunakar@indlinux.org> - - * hi.po: Updated Hindi translation. - -2007-04-10 Peter Bach <bach.peter@gmail.com> - - * da.po: Updated Danish translation. - -2007-04-03 Inaki Larranaga Murgoitio <dooteo@euskalgnu.org> - - * eu.po: Updated Basque translation. - -2007-03-25 Ignacio Casal Quinteiro <nacho.resa@gmail.com> - - * gl.po: Updated Galician Translation. - 2007-03-23 Luca Ferretti <elle.uca@libero.it> * it.po: Updated Italian translation. @@ -8,16 +8,16 @@ msgid "" msgstr "" "Project-Id-Version: or\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2006-08-10 00:46+0200\n" +"POT-Creation-Date: 2007-07-29 15:14+0100\n" "PO-Revision-Date: 2007-05-13 13:26+0200\n" "Last-Translator: Yannig MARCHEGAY <yannig@marchegay.org>\n" "Language-Team: Occitan (post 1500) <ubuntu-l10n-oci@lists.ubuntu.com>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n > 1);" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: ../data/info.xml.in.h:1 ../src/yelp-toc-pager.c:1849 +#: ../data/info.xml.in.h:1 ../src/yelp-toc-pager.c:1847 msgid "GNU Info Pages" msgstr "Paginas d'informacions GNU" @@ -61,7 +61,7 @@ msgstr "Periferics materials" msgid "Kernel Routines" msgstr "" -#: ../data/man.xml.in.h:10 ../src/yelp-toc-pager.c:1843 +#: ../data/man.xml.in.h:10 ../src/yelp-toc-pager.c:1841 msgid "Manual Pages" msgstr "Paginas del manual" @@ -605,7 +605,7 @@ msgstr "" "Impossible d'analizar '%s'. Ja lo fichièr existÃs pas, ja es pas una pagina " "XML corrècta." -#: ../src/yelp-io-channel.c:101 +#: ../src/yelp-io-channel.c:107 #, c-format msgid "" "The file ‘%s’ could not be read and decoded. The file may be compressed in " @@ -614,20 +614,20 @@ msgstr "" "Impossible de legir e descodar lo fichièr \"%s\". Es possible que lo fichièr " "siá compressat dins un format pas gerit." -#: ../src/yelp-main.c:91 +#: ../src/yelp-main.c:92 msgid "Use a private session" msgstr "Utilisar una session privada" -#: ../src/yelp-main.c:100 +#: ../src/yelp-main.c:101 msgid "Define which cache directory to use" msgstr "DefinÃs lo repertòri d'escondedor d'utilizar" #. Commandline parsing is done here -#: ../src/yelp-main.c:354 +#: ../src/yelp-main.c:358 msgid " GNOME Help Browser" msgstr " Navegador d'ajuda GNOME" -#: ../src/yelp-main.c:372 ../yelp.desktop.in.in.h:2 +#: ../src/yelp-main.c:376 ../yelp.desktop.in.in.h:2 msgid "Help" msgstr "Ajuda" @@ -661,23 +661,19 @@ msgstr "Aquesta estampadoira gerÃs pas l'estampatge" msgid "Printer %s does not support postscript printing." msgstr "L'estampadoira %s gerÃs pas l'estampatge PostScript." -#: ../src/yelp-print.c:337 +#: ../src/yelp-print.c:337 ../src/yelp-print.c:365 msgid "Printing" msgstr "Estampatge" -#: ../src/yelp-print.c:364 -msgid "<b>Printing</b>" -msgstr "" - -#: ../src/yelp-print.c:366 +#: ../src/yelp-print.c:367 msgid "Waiting to print" msgstr "" -#: ../src/yelp-print.c:578 +#: ../src/yelp-print.c:579 msgid "An error occurred while printing" msgstr "I a agut una error al moment d'estampar" -#: ../src/yelp-print.c:582 +#: ../src/yelp-print.c:583 #, c-format msgid "It was not possible to print your document: %s" msgstr "Impossible d'estampar vòstre document : %s" @@ -719,17 +715,17 @@ msgstr "Resultas de la recèrca per \"%s\"" msgid "Repeat the search online at %s" msgstr "" -#: ../src/yelp-search-pager.c:905 ../src/yelp-toc-pager.c:2144 +#: ../src/yelp-search-pager.c:905 ../src/yelp-toc-pager.c:2068 #: ../src/yelp-xslt-pager.c:364 msgid "No href attribute found on yelp:document" msgstr "" -#: ../src/yelp-search-pager.c:919 ../src/yelp-toc-pager.c:2157 +#: ../src/yelp-search-pager.c:919 ../src/yelp-toc-pager.c:2081 #: ../src/yelp-xslt-pager.c:380 ../src/yelp-xslt-pager.c:531 msgid "Out of memory" msgstr "Pas pro de memòria" -#: ../src/yelp-search-pager.c:960 ../src/yelp-toc-pager.c:2206 +#: ../src/yelp-search-pager.c:960 ../src/yelp-toc-pager.c:2130 msgid "Help Contents" msgstr "Somari de l'ajuda" @@ -741,7 +737,7 @@ msgstr "Somari de l'ajuda" #. * "how do I", and words for functional states like "not", #. * "work", and "broken". #. -#: ../src/yelp-search-pager.c:1286 +#: ../src/yelp-search-pager.c:1288 msgid "" "a:about:an:are:as:at:be:broke:broken:by:can:can't:dialog:dialogue:do:doesn't:" "doesnt:don't:dont:explain:for:from:get:gets:got:make:makes:not:when:has:have:" @@ -757,7 +753,7 @@ msgstr "" #. * E.g. if the common prefix is re then the string would be #. * "re:" #. -#: ../src/yelp-search-pager.c:1302 +#: ../src/yelp-search-pager.c:1304 msgid "re" msgstr "" @@ -768,11 +764,11 @@ msgstr "" #. * please use the strig NULL. If there is only 1, please #. * add a colon at the end of the list #. -#: ../src/yelp-search-pager.c:1311 +#: ../src/yelp-search-pager.c:1313 msgid "ers:er:ing:es:s:'s" msgstr "" -#: ../src/yelp-search-pager.c:1506 ../src/yelp-toc-pager.c:631 +#: ../src/yelp-search-pager.c:1518 ../src/yelp-toc-pager.c:629 #, c-format msgid "Could not load the OMF file '%s'." msgstr "Impossible de cargar lo fichièr OMF '%s'." @@ -802,248 +798,248 @@ msgstr "Impossible de cargar lo fichièr OMF '%s'." msgid "yelp-watermark-blockquote-201C" msgstr "yelp-watermark-blockquote-00AB" -#: ../src/yelp-toc-pager.c:268 +#: ../src/yelp-toc-pager.c:267 msgid "YelpTocPager: Pause count is negative." msgstr "" -#: ../src/yelp-toc-pager.c:379 ../src/yelp-toc-pager.c:1957 +#: ../src/yelp-toc-pager.c:378 #, c-format msgid "" "The table of contents could not be processed. The file ‘%s’ is either " "missing or is not a valid XSLT stylesheet." msgstr "" -#: ../src/yelp-toc-pager.c:1110 +#: ../src/yelp-toc-pager.c:1109 #, c-format msgid "Read man page for %s" msgstr "Legir la pagina man per %s" -#: ../src/yelp-toc-pager.c:1703 +#: ../src/yelp-toc-pager.c:1701 #, c-format msgid "Read info page for %s" msgstr "Legir la pagina info per %s" -#: ../src/yelp-toc-pager.c:1816 +#: ../src/yelp-toc-pager.c:1814 #, c-format msgid "" "The table of contents could not be loaded. The file ‘%s’ is either missing " "or is not well-formed XML." msgstr "" -#: ../src/yelp-toc-pager.c:1838 +#: ../src/yelp-toc-pager.c:1836 msgid "Command Line Help" msgstr "Ajuda per la linha de comanda" -#: ../src/yelp-window.c:318 +#: ../src/yelp-window.c:317 msgid "_File" msgstr "_Fichièr" -#: ../src/yelp-window.c:319 +#: ../src/yelp-window.c:318 msgid "_Edit" msgstr "_Edicion" -#: ../src/yelp-window.c:320 +#: ../src/yelp-window.c:319 msgid "_Go" msgstr "_Anar" -#: ../src/yelp-window.c:321 +#: ../src/yelp-window.c:320 msgid "_Bookmarks" msgstr "_Favorits" -#: ../src/yelp-window.c:322 +#: ../src/yelp-window.c:321 msgid "_Help" msgstr "_Ajuda" -#: ../src/yelp-window.c:325 +#: ../src/yelp-window.c:324 msgid "_New Window" msgstr "Fenèstra _nòva" -#: ../src/yelp-window.c:330 +#: ../src/yelp-window.c:329 msgid "Print This Document" msgstr "Estampar aqueste document" -#: ../src/yelp-window.c:335 +#: ../src/yelp-window.c:334 msgid "Print This Page" msgstr "Estampar aquesta pagina" -#: ../src/yelp-window.c:340 +#: ../src/yelp-window.c:339 msgid "About This Document" msgstr "A prepaus del document" -#: ../src/yelp-window.c:345 +#: ../src/yelp-window.c:344 msgid "Open _Location" msgstr "Dobrir un _emplaçament" -#: ../src/yelp-window.c:350 +#: ../src/yelp-window.c:349 msgid "_Close Window" msgstr "_Tampar la fenèstra" -#: ../src/yelp-window.c:356 +#: ../src/yelp-window.c:355 msgid "_Copy" msgstr "_Copiar" -#: ../src/yelp-window.c:362 +#: ../src/yelp-window.c:361 msgid "_Select All" msgstr "Tot _seleccionar" -#: ../src/yelp-window.c:367 +#: ../src/yelp-window.c:366 msgid "_Find..." msgstr "_Recèrcar" -#: ../src/yelp-window.c:372 +#: ../src/yelp-window.c:371 msgid "Find Pre_vious" msgstr "Recèrcar lo _precedent" -#: ../src/yelp-window.c:374 +#: ../src/yelp-window.c:373 msgid "Find previous occurrence of the word or phrase" msgstr "" -#: ../src/yelp-window.c:377 +#: ../src/yelp-window.c:376 msgid "Find Ne_xt" msgstr "Recèrcar lo seguent" -#: ../src/yelp-window.c:379 +#: ../src/yelp-window.c:378 msgid "Find next occurrence of the word or phrase" msgstr "" -#: ../src/yelp-window.c:382 +#: ../src/yelp-window.c:381 msgid "_Preferences" msgstr "_Preferéncias" -#: ../src/yelp-window.c:387 +#: ../src/yelp-window.c:386 msgid "_Reload" msgstr "_Tornar cargar" -#: ../src/yelp-window.c:399 +#: ../src/yelp-window.c:398 msgid "_Back" msgstr "_Tornar" -#: ../src/yelp-window.c:401 +#: ../src/yelp-window.c:400 msgid "Show previous page in history" msgstr "Visualizar la pagina precedenta de l'istoric" -#: ../src/yelp-window.c:404 +#: ../src/yelp-window.c:403 msgid "_Forward" msgstr "_Seguent" -#: ../src/yelp-window.c:406 +#: ../src/yelp-window.c:405 msgid "Show next page in history" msgstr "Visualizar la pagina seguenta de l'istoric" -#: ../src/yelp-window.c:409 +#: ../src/yelp-window.c:408 msgid "_Help Topics" msgstr "Somari de l'_ajuda" -#: ../src/yelp-window.c:411 +#: ../src/yelp-window.c:410 msgid "Go to the listing of help topics" msgstr "" -#: ../src/yelp-window.c:414 +#: ../src/yelp-window.c:413 msgid "_Previous Section" msgstr "Seccion _precedenta" -#: ../src/yelp-window.c:419 +#: ../src/yelp-window.c:418 msgid "_Next Section" msgstr "Seccion _seguenta" -#: ../src/yelp-window.c:424 ../src/yelp-window.c:456 +#: ../src/yelp-window.c:423 ../src/yelp-window.c:455 msgid "_Contents" msgstr "_Somari" -#: ../src/yelp-window.c:430 +#: ../src/yelp-window.c:429 msgid "_Add Bookmark" msgstr "_Apondre un favorit" -#: ../src/yelp-window.c:435 +#: ../src/yelp-window.c:434 msgid "_Edit Bookmarks..." msgstr "_Editar los favorits..." -#: ../src/yelp-window.c:441 +#: ../src/yelp-window.c:440 msgid "_Open Link" msgstr "_Dubrir lo ligam" -#: ../src/yelp-window.c:446 +#: ../src/yelp-window.c:445 msgid "Open Link in _New Window" msgstr "Dobrir lo ligam dins una fenèstra _nòva" -#: ../src/yelp-window.c:451 +#: ../src/yelp-window.c:450 msgid "_Copy Link Address" msgstr "_Copiar l'adreça del ligam" -#: ../src/yelp-window.c:458 +#: ../src/yelp-window.c:457 msgid "Help On this application" msgstr "Ajuda sus l'aplicacion" -#: ../src/yelp-window.c:461 +#: ../src/yelp-window.c:460 msgid "_About" msgstr "_A prepaus" -#: ../src/yelp-window.c:466 +#: ../src/yelp-window.c:465 msgid "Copy _Email Address" msgstr "Copiar l'adreça _electronica" -#: ../src/yelp-window.c:515 +#: ../src/yelp-window.c:514 msgid "Help Browser" msgstr "Navegador d'ajuda" -#: ../src/yelp-window.c:926 ../src/yelp-window.c:1082 +#: ../src/yelp-window.c:925 ../src/yelp-window.c:1081 msgid "The Uniform Resource Identifier for the file is invalid." msgstr "L'URI del fichièr es pas valid." -#: ../src/yelp-window.c:938 ../src/yelp-window.c:1077 +#: ../src/yelp-window.c:937 ../src/yelp-window.c:1076 #, c-format msgid "" "The Uniform Resource Identifier ‘%s’ is invalid or does not point to an " "actual file." msgstr "" -#: ../src/yelp-window.c:1026 ../src/yelp-window.c:1506 +#: ../src/yelp-window.c:1025 ../src/yelp-window.c:1505 msgid "Man pages are not supported in this version." msgstr "Las paginas de manual son pas geridas dins aquesta version." -#: ../src/yelp-window.c:1036 ../src/yelp-window.c:1497 +#: ../src/yelp-window.c:1035 ../src/yelp-window.c:1496 msgid "GNU info pages are not supported in this version" msgstr "Las paginas GNU info son pas geridas dins aquesta version" -#: ../src/yelp-window.c:1052 ../src/yelp-window.c:1519 +#: ../src/yelp-window.c:1051 ../src/yelp-window.c:1518 msgid "Search is not supported in this version." msgstr "La foncion de recèrca es pas gerida dins aquesta version." -#: ../src/yelp-window.c:1057 +#: ../src/yelp-window.c:1056 msgid "" "SGML documents are no longer supported. Please ask the author of the " "document to convert to XML." msgstr "" -#: ../src/yelp-window.c:1223 +#: ../src/yelp-window.c:1222 msgid "_Search:" msgstr "_Recèrcar :" -#: ../src/yelp-window.c:1224 +#: ../src/yelp-window.c:1223 msgid "Search for other documentation" msgstr "Recercar d'autras documentacions" -#: ../src/yelp-window.c:1386 +#: ../src/yelp-window.c:1385 msgid "Fin_d:" msgstr "_Recèrcar :" -#: ../src/yelp-window.c:1406 +#: ../src/yelp-window.c:1405 msgid "Find _Next" msgstr "Recèrcar lo seguent" -#: ../src/yelp-window.c:1418 +#: ../src/yelp-window.c:1417 msgid "Find _Previous" msgstr "Recèrcar lo _precedent" -#: ../src/yelp-window.c:1532 +#: ../src/yelp-window.c:1531 #, c-format msgid "" "A transformation context could not be created for the file ‘%s’. The format " "may not be supported." msgstr "" -#: ../src/yelp-window.c:1562 ../src/yelp-window.c:1981 -#: ../src/yelp-window.c:2064 +#: ../src/yelp-window.c:1561 ../src/yelp-window.c:1980 +#: ../src/yelp-window.c:2063 #, c-format msgid "" "The section ‘%s’ does not exist in this document. If you were directed to " @@ -1051,29 +1047,30 @@ msgid "" "maintainers of that application." msgstr "" -#: ../src/yelp-window.c:1677 ../src/yelp-window.c:2468 +#: ../src/yelp-window.c:1676 ../src/yelp-window.c:2467 #, c-format msgid "" "The file ‘%s’ could not be read. This file might be missing, or you might " "not have permissions to read it." msgstr "" -#: ../src/yelp-window.c:1728 +#: ../src/yelp-window.c:1727 msgid "Loading..." msgstr "Cargament..." #. Note to translators: put here your name (and address) so it #. * will show up in the "about" box -#: ../src/yelp-window.c:2911 +#: ../src/yelp-window.c:2901 msgid "translator-credits" msgstr "" -"La còla occitana de revirada d'Ubuntu, 2007 - ubuntu-l10n-oci@lists.ubuntu.com" +"La còla occitana de revirada d'Ubuntu, 2007 - ubuntu-l10n-oci@lists.ubuntu." +"com" -#: ../src/yelp-window.c:2914 +#: ../src/yelp-window.c:2904 msgid "Yelp" msgstr "Yelp" -#: ../src/yelp-window.c:2916 +#: ../src/yelp-window.c:2906 msgid "A documentation browser and viewer for the Gnome Desktop." msgstr "Un navegador e legidor de documentacion pel burèu GNOME." @@ -1096,4 +1093,3 @@ msgstr "" #: ../yelp.desktop.in.in.h:1 msgid "Get help with GNOME" msgstr "" - @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: yelp\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2007-07-16 16:26+0530\n" +"POT-Creation-Date: 2007-07-16 12:14+0100\n" "PO-Revision-Date: 2007-07-16 16:28+0530\n" "Last-Translator: Bharat Kumar <jonnalagaddabharat@gmail.com>\n" "Language-Team: Swecha Telugu Localisation Team <localisation@swecha.org>\n" diff --git a/src/Makefile.am b/src/Makefile.am index f8c48782..741ce799 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,41 +4,31 @@ yelp_SOURCES = \ Yelper.cpp Yelper.h \ yelp-base.c yelp-base.h \ yelp-bookmarks.c yelp-bookmarks.h \ - yelp-db-pager.c yelp-db-pager.h \ - yelp-db-print-pager.c yelp-db-print-pager.h \ yelp-debug.c yelp-debug.h \ yelp-error.c yelp-error.h \ yelp-gecko-utils.cpp yelp-gecko-utils.h \ yelp-html.cpp yelp-html.h \ yelp-io-channel.c yelp-io-channel.h \ - yelp-pager.c yelp-pager.h \ yelp-settings.c yelp-settings.h \ - yelp-toc-pager.c yelp-toc-pager.h \ yelp-utils.c yelp-utils.h \ yelp-window.c yelp-window.h \ - yelp-xslt-pager.c yelp-xslt-pager.h \ yelp-marshal.c yelp-marshal.h \ yelp-main.c \ yelp-print.c yelp-print.h \ - yelp-gecko-services.h yelp-gecko-services.cpp - -if ENABLE_MAN -yelp_SOURCES += \ - yelp-man-parser.c yelp-man-parser.h \ - yelp-man-pager.c yelp-man-pager.h -endif - -if ENABLE_INFO -yelp_SOURCES += \ - yelp-info-pager.c yelp-info-pager.h \ - yelp-info-parser.c yelp-info-parser.h -endif - -if ENABLE_SEARCH -yelp_SOURCES += \ - gtkentryaction.c gtkentryaction.h \ - yelp-search-pager.c yelp-search-pager.h -endif + yelp-page.c yelp-page.h \ + yelp-transform.c yelp-transform.h \ + yelp-gecko-services.h yelp-gecko-services.cpp \ + yelp-document.h yelp-document.c \ + yelp-toc.h yelp-toc.c \ + yelp-docbook.h yelp-docbook.c \ + yelp-db-print.c yelp-db-print.h \ + yelp-man-parser.c yelp-man-parser.h \ + yelp-man.c yelp-man.h \ + yelp-info.c yelp-info.h \ + yelp-info-parser.c yelp-info-parser.h \ + gtkentryaction.c gtkentryaction.h \ + yelp-search.c yelp-search.h \ + yelp-search-parser.c yelp-search-parser.h YELP_DEFINES = \ -DG_LOG_DOMAIN=\"Yelp\" \ @@ -50,7 +40,8 @@ YELP_DEFINES = \ -DSERVERDIR=\"$(libexecdir)\" \ -DBINDIR=\""$(bindir)"\" \ -DSHAREDIR=\""$(pkgdatadir)"\" \ - -DMOZILLA_HOME=\""$(MOZILLA_HOME)\"" + -DMOZILLA_HOME=\""$(MOZILLA_HOME)\"" \ + -DGDU_ICON_PATH=\"$(GDU_ICON_PATH)\" mozilla_include_subdirs = \ . \ @@ -102,68 +93,67 @@ yelp_LDADD = \ yelp_LDFLAGS = -R$(MOZILLA_HOME) $(AM_LDFLAGS) -check_PROGRAMS = test-man-parser test-pager test-uri +check_PROGRAMS = \ + test-document \ + test-man-parser \ + test-page \ + test-transform \ + test-resolver + +test_document_SOURCES = \ + yelp-debug.c yelp-debug.h \ + yelp-docbook.c yelp-docbook.h \ + yelp-document.c yelp-document.h \ + yelp-error.c yelp-error.h \ + yelp-io-channel.c yelp-io-channel.h \ + yelp-man.c yelp-man.h \ + yelp-man-parser.c yelp-man-parser.h \ + yelp-page.c yelp-page.h \ + yelp-toc.c yelp-toc.h \ + yelp-transform.c yelp-transform.h \ + test-document.c +test_document_CFLAGS = $(YELP_CFLAGS) $(AM_CFLAGS) $(YELP_DEFINES) +test_document_LDADD = $(YELP_LIBS) $(Z_LIBS) $(BZ_LIBS) +test_document_LDFLAGS = $(AM_LDFLAGS) test_man_parser_SOURCES = \ + yelp-debug.c yelp-debug.h \ yelp-error.c yelp-error.h \ yelp-io-channel.c yelp-io-channel.h \ yelp-man-parser.c yelp-man-parser.h \ - yelp-utils.c yelp-utils.h \ test-man-parser.c - -test_man_parser_CPPFLAGS = \ - $(YELP_DEFINES) \ - $(AM_CPPFLAGS) - -test_man_parser_CFLAGS = \ - $(YELP_CFLAGS) \ - $(AM_CFLAGS) - +test_man_parser_CPPFLAGS = $(YELP_DEFINES) $(AM_CPPFLAGS) +test_man_parser_CFLAGS = $(YELP_CFLAGS) $(AM_CFLAGS) test_man_parser_LDADD = $(YELP_LIBS) $(Z_LIBS) $(BZ_LIBS) - test_man_parser_LDFLAGS = $(AM_LDFLAGS) -test_pager_SOURCES = \ - yelp-db-pager.c yelp-db-pager.h \ - yelp-db-print-pager.c yelp-db-print-pager.h \ - yelp-error.c yelp-error.h \ - yelp-io-channel.c yelp-io-channel.h \ - yelp-man-pager.c yelp-man-pager.h \ - yelp-man-parser.c yelp-man-parser.h \ - yelp-pager.c yelp-pager.h \ - yelp-toc-pager.c yelp-toc-pager.h \ - yelp-utils.c yelp-utils.h \ - yelp-xslt-pager.c yelp-xslt-pager.h \ - yelp-marshal.c yelp-marshal.h \ - test-pager.c - -test_pager_CPPFLAGS = \ - $(YELP_DEFINES) \ - $(AM_CPPFLAGS) - -test_pager_CFLAGS = \ - $(YELP_CFLAGS) \ - $(AM_CFLAGS) - -test_pager_LDADD = $(YELP_LIBS) $(POPT_LIBS) $(Z_LIBS) $(BZ_LIBS) - -test_pager_LDFLAGS = $(AM_LDFLAGS) - -test_uri_SOURCES = \ - yelp-utils.c yelp-utils.h \ - test-uri.c - -test_uri_CPPFLAGS = \ - $(YELP_DEFINES) \ - $(AM_CPPFLAGS) - -test_uri_CFLAGS = \ - $(YELP_CFLAGS) \ - $(AM_CFLAGS) - -test_uri_LDADD = $(YELP_LIBS) - -test_uri_LDFLAGS = $(AM_LDFLAGS) +test_page_SOURCES = \ + yelp-debug.c yelp-debug.h \ + yelp-document.c yelp-document.h \ + yelp-error.c yelp-error.h \ + yelp-page.c yelp-page.h \ + test-page.c +test_page_CFLAGS = $(YELP_CFLAGS) $(AM_CFLAGS) $(YELP_DEFINES) +test_page_LDADD = $(YELP_LIBS) +test_page_LDFLAGS = $(AM_LDFLAGS) + +test_transform_SOURCES = \ + yelp-debug.c yelp-debug.h \ + yelp-error.c yelp-error.h \ + yelp-transform.c yelp-transform.h \ + test-transform.c +test_transform_CFLAGS = $(YELP_CFLAGS) $(AM_CFLAGS) $(YELP_DEFINES) +test_transform_LDADD = $(YELP_LIBS) +test_transform_LDFLAGS = $(AM_LDFLAGS) + +test_resolver_SOURCES = \ + yelp-debug.c yelp-debug.h \ + yelp-error.c yelp-error.h \ + yelp-utils.c yelp-utils.h \ + test-resolver.c +test_resolver_CFLAGS = $(YELP_CFLAGS) $(AM_CFLAGS) $(YELP_DEFINES) +test_resolver_LDADD = $(YELP_LIBS) +test_resolver_LDFLAGS = $(AM_LDFLAGS) @INTLTOOL_SERVER_RULE@ diff --git a/src/test-document.c b/src/test-document.c new file mode 100644 index 00000000..a7adb427 --- /dev/null +++ b/src/test-document.c @@ -0,0 +1,149 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2006 Shaun McCance + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Cambridge, MA 02139, USA. + * + * Author: Shaun McCance <shaunm@gnome.org> + */ + +#include <glib.h> +#include <gtk/gtk.h> +#include <libxml/parser.h> +#include <libxml/xinclude.h> + +#include "yelp-error.h" +#include "yelp-docbook.h" +#include "yelp-man.h" +#include "yelp-toc.h" + +static gchar *mode = NULL; +static gchar **files = NULL; +static const GOptionEntry options[] = { + { "mode", 'm', + 0, G_OPTION_ARG_STRING, + &mode, + "One of man, docbook or toc", "MODE" }, + { G_OPTION_REMAINING, + 0, 0, G_OPTION_ARG_FILENAME_ARRAY, + &files, NULL, NULL }, + { NULL } +}; + +static void document_func (YelpDocument *document, + YelpDocumentSignal signal, + gint req_id, + gpointer *func_data, + gpointer user_data); + +GMainLoop *loop; + +static void +document_func (YelpDocument *document, + YelpDocumentSignal signal, + gint req_id, + gpointer *func_data, + gpointer user_data) +{ + gchar contents[60]; + gchar *contents_; + gsize read; + YelpPage *page; + YelpError *error; + switch (signal) { + case YELP_DOCUMENT_SIGNAL_PAGE: + page = (YelpPage *) func_data; + printf ("PAGE: %s (%i)\n", page->id, req_id); + printf (" PREV: %s\n", page->prev_id); + printf (" NEXT: %s\n", page->next_id); + printf (" UP: %s\n", page->up_id); + printf (" ROOT: %s\n", page->root_id); + printf (" SECTIONS: %s\n", yelp_document_get_sections (document) ? "yep": "nah"); + yelp_page_read (page, contents, 60, &read, NULL); + /* contents isn't \0-terminated */ + contents_ = g_strndup (contents, read); + printf (" DATA: %s\n", contents_); + g_free (contents_); + yelp_page_free (page); + break; + case YELP_DOCUMENT_SIGNAL_TITLE: + printf ("TITLE: %s (%i)\n", (gchar *) func_data, req_id); + g_free (func_data); + break; + case YELP_DOCUMENT_SIGNAL_ERROR: + error = (YelpError *) func_data; + printf ("ERROR: %s\n", yelp_error_get_title (error)); + printf (" %s\n", yelp_error_get_message (error)); + yelp_error_free (error); + break; + } +} + +gint +main (gint argc, gchar **argv) +{ + GOptionContext *context; + YelpDocument *document; + gint i; + + g_thread_init (NULL); + gdk_threads_init (); + gdk_threads_leave (); + + gtk_init (&argc, &argv); + + context = g_option_context_new ("FILE PAGE_IDS..."); + g_option_context_add_main_entries (context, options, NULL); + g_option_context_parse (context, &argc, &argv, NULL); + + if (!mode) + mode = g_strdup ("docbook"); + + if ((files == NULL || files[0] == NULL) && !g_str_equal (mode, "toc")) { + g_printerr ("Usage: test-docbook FILE PAGE_IDS...\n"); + return 1; + } + + if (g_str_equal (mode, "man")) + document = yelp_man_new (files[0]); + else if (g_str_equal (mode, "toc")) + document = yelp_toc_new (); + else if (g_str_equal (mode, "docbook")) + document = yelp_docbook_new (files[0]); + else { + g_error ("Unknown test. Please try again later\n"); + return 3; + } + + if ((files == NULL || files[1] == NULL) && (!g_str_equal (mode, "toc"))) + yelp_document_get_page (document, "x-yelp-index", (YelpDocumentFunc) document_func, NULL); + else if (!g_str_equal (mode, "toc")) { + for (i = 1; files[i]; i++) + yelp_document_get_page (document, files[i], (YelpDocumentFunc) document_func, NULL); + } else { + if (files) { + for (i = 0; files[i]; i++) + yelp_document_get_page (document, files[i], (YelpDocumentFunc) document_func, NULL); + } else { + yelp_document_get_page (document, "index", (YelpDocumentFunc) document_func, NULL); + } + } + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + + gdk_threads_leave (); + + return 0; +} diff --git a/src/test-man-parser.c b/src/test-man-parser.c index 3b115430..38b38497 100644 --- a/src/test-man-parser.c +++ b/src/test-man-parser.c @@ -19,39 +19,50 @@ * Author: Shaun McCance <shaunm@gnome.org> */ -#include <libgnomevfs/gnome-vfs.h> +#include <glib.h> +#include <libxml/tree.h> + #include "yelp-man-parser.h" -#include "yelp-utils.h" + +static gchar **files = NULL; +static const GOptionEntry options[] = { + { G_OPTION_REMAINING, + 0, 0, G_OPTION_ARG_FILENAME_ARRAY, + &files, NULL, NULL }, + { NULL } +}; gint main (gint argc, gchar **argv) { + GOptionContext *context; YelpManParser *parser; - YelpDocInfo *doc_info; - xmlDocPtr doc; + xmlDocPtr doc; + gchar *encoding; + gint i; + + context = g_option_context_new ("FILES..."); + g_option_context_add_main_entries (context, options, NULL); + g_option_context_parse (context, &argc, &argv, NULL); - if (argc < 2) { - g_error ("Usage: test-man-parser file\n"); + if (files == NULL || files[0] == NULL) { + g_printerr ("Usage: test-man-parser [OPTION...] FILES...\n"); return 1; } - gnome_vfs_init (); - parser = yelp_man_parser_new (); - doc_info = yelp_doc_info_get (argv[1], FALSE); - if (!doc_info) { - printf ("Failed to load URI: %s\n", argv[1]); - return -1; - } - doc = yelp_man_parser_parse_doc (parser, doc_info); - yelp_man_parser_free (parser); + encoding = (gchar *) g_getenv("MAN_ENCODING"); + if (encoding == NULL) + encoding = "ISO-8859-1"; - xmlDocDump (stdout, doc); - xmlFreeDoc (doc); + for (i = 0; files[i]; i++) { + doc = yelp_man_parser_parse_file (parser, files[i], encoding); + xmlDocDump (stdout, doc); + xmlFreeDoc (doc); + } - gnome_vfs_shutdown (); + yelp_man_parser_free (parser); return 0; } - diff --git a/src/test-page.c b/src/test-page.c new file mode 100644 index 00000000..c7d2c701 --- /dev/null +++ b/src/test-page.c @@ -0,0 +1,83 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2007 Shaun McCance <shaunm@gnome.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Cambridge, MA 02139, USA. + * + * Author: Shaun McCance <shaunm@gnome.org> + */ + +#include <glib.h> + +#include "yelp-page.h" +#include "yelp-error.h" + +#define READ_SIZE 1024 + +static gchar *file = NULL; +static const GOptionEntry options[] = { + { G_OPTION_REMAINING, + 0, 0, G_OPTION_ARG_FILENAME, + &file, NULL, NULL }, + { NULL } +}; + +gint +main (gint argc, gchar **argv) +{ + GOptionContext *context; + YelpPage *page; + YelpError *error; + gchar buffer[READ_SIZE]; + gsize read; + gint num_reads; + + context = g_option_context_new ("[FILE]"); + g_option_context_add_main_entries (context, options, NULL); + g_option_context_parse (context, &argc, &argv, NULL); + + if (file == NULL) { + gint i; + gchar *str, *new; + str = g_strdup ("Fe fi fo fum. I smell the blood of an Englishman.\n"); + for (i = 0; i < 5; i++) { + new = g_strconcat (str, str, str, str, str, str, str, NULL); + g_free (str); + str = new; + } + page = yelp_page_new_string (NULL, NULL, str); + } else { + g_error ("File pages not yet supported.\n"); + return 1; + } + + num_reads = 0; + while (yelp_page_read (page, buffer, READ_SIZE, &read, &error) + == G_IO_STATUS_NORMAL) { + gchar *str; + num_reads++; + /* buffer isn't NULL-terminated */ + str = g_strndup (buffer, READ_SIZE); + printf ("%s", str); + g_free (str); + } + + printf ("\n%i total reads\n", num_reads); + printf ("%i bytes in last read\n", read); + + yelp_page_free (page); + + return 0; +} diff --git a/src/test-resolver.c b/src/test-resolver.c new file mode 100644 index 00000000..b39d11a5 --- /dev/null +++ b/src/test-resolver.c @@ -0,0 +1,271 @@ +/* A test util to try out the new + * yelp-utils resolver for URIs + * Basically, tries to resolve each + * given URI and compares to the expected results + * (only checks the files exist and the type is correct) + */ + +#include <stdio.h> +#include <glib.h> +#include <libgnomevfs/gnome-vfs.h> +#include "yelp-utils.h" + + +int DefaultTests (void); +int TestFile (char *uri); + + +int +TestFile (char *uri) +{ + return (g_file_test (uri, G_FILE_TEST_EXISTS)); +} + +int +DefaultTests (void) +{ + gchar *result = NULL; + gchar *section = NULL; + YelpRrnType restype = YELP_TYPE_ERROR; + + /* First, normal docs - these will only work with rrn XDG_DATA_DIRS set correctly */ + /* Normal doc, no section */ + restype = yelp_uri_resolve ("ghelp:user-guide", &result, §ion); + if (restype != YELP_TYPE_DOC || !TestFile (result) || + section != NULL) { + return 101; + } + g_free (result); result=NULL; + + /* Section type 1*/ + restype = yelp_uri_resolve ("ghelp:user-guide#madeupsection", &result, §ion); + if (restype != YELP_TYPE_DOC || !TestFile (result) || + !g_str_equal (section, "madeupsection")) { + return 102; + } + g_free (result); result=NULL; + g_free (section); section = NULL; + + /* Section type 2 */ + restype = yelp_uri_resolve ("ghelp:user-guide?madeupsection", &result, §ion); + if (restype != YELP_TYPE_DOC || !TestFile (result) || + !g_str_equal (section, "madeupsection")) { + return 103; + } + g_free (result); result=NULL; + g_free (section); section = NULL; + + /* man pages - only work with correct man pages installed */ + /* Simple man page */ + restype = yelp_uri_resolve ("man:yelp", &result, §ion); + if (restype != YELP_TYPE_MAN || !TestFile (result) || + !g_str_equal (section, "1")) { + return 104; + } + g_free (result); result=NULL; + g_free (section); section = NULL; + + /* man page from specific section 1*/ + restype = yelp_uri_resolve ("man:yelp(1)", &result, §ion); + if (restype != YELP_TYPE_MAN || !TestFile (result) || + !g_str_equal (section, "1")) { + return 105; + } + g_free (result); result=NULL; + g_free (section); section = NULL; + + /* man page from specific section 2*/ + restype = yelp_uri_resolve ("man:yelp.1", &result, §ion); + if (restype != YELP_TYPE_MAN || !TestFile (result) || + !g_str_equal (section, "1")) { + return 106; + } + g_free (result); result=NULL; + g_free (section); section = NULL; + + /* man page from specific section 3*/ + restype = yelp_uri_resolve ("man:yelp#1", &result, §ion); + if (restype != YELP_TYPE_MAN || !TestFile (result) || + !g_str_equal (section, "1")) { + return 107; + } + g_free (result); result=NULL; + g_free (section); section = NULL; + + /* Info pages */ + /* Simple info page */ + restype = yelp_uri_resolve ("info:cvs", &result, §ion); + if (restype != YELP_TYPE_INFO || !TestFile (result) || + section != NULL) { + return 108; + } + g_free (result); result=NULL; + g_free (section); section = NULL; + + /* info page with section */ + restype = yelp_uri_resolve ("info:cvs#toolbar", &result, §ion); + if (restype != YELP_TYPE_INFO || !TestFile (result) || + !g_str_equal (section, "toolbar")) { + return 109; + } + g_free (result); result=NULL; + g_free (section); section = NULL; + + /* info page with section 2*/ + restype = yelp_uri_resolve ("info:cvs?toolbar", &result, §ion); + if (restype != YELP_TYPE_INFO || !TestFile (result) || + !g_str_equal (section, "toolbar")) { + return 110; + } + g_free (result); result=NULL; + g_free (section); section = NULL; + + + /* info page with section included */ + restype = yelp_uri_resolve ("info:autopoint", &result, §ion); + if (restype != YELP_TYPE_INFO || !TestFile (result) || + !g_str_equal (section, "autopoint Invocation")) { + return 111; + } + g_free (result); result=NULL; + g_free (section); section = NULL; + + /* Other types: html - no html installed by default. Should be the same + * as ghelp + */ + /* External */ + restype = yelp_uri_resolve ("http://www.gnome.org", &result, §ion); + if (restype != YELP_TYPE_EXTERNAL) { + return 112; + } + g_free (result); result=NULL; + g_free (section); section = NULL; + + /* External, but local */ + restype = yelp_uri_resolve ("/usr/bin/yelp", &result, §ion); + if (restype != YELP_TYPE_EXTERNAL) { + return 113; + } + g_free (result); result=NULL; + g_free (section); section = NULL; + + /* External, local using file: uri */ + restype = yelp_uri_resolve ("file:///usr/bin/yelp", &result, §ion); + if (restype != YELP_TYPE_EXTERNAL) { + return 114; + } + g_free (result); result=NULL; + g_free (section); section = NULL; + + /* Local, file, readable */ + restype = yelp_uri_resolve ("file:///usr/share/gnome/help/user-guide/C/user-guide.xml", &result, §ion); + if (restype != YELP_TYPE_DOC || !TestFile (result) || + section != NULL) { + return 115; + } + g_free (result); result=NULL; + g_free (section); section = NULL; + + /* Local, readable, html */ + restype = yelp_uri_resolve ("/usr/share/doc/shared-mime-info/shared-mime-info-spec.html/index.html", &result, §ion); + if (restype != YELP_TYPE_HTML || !TestFile (result) || + section != NULL) { + return 116; + } + g_free (result); result=NULL; + g_free (section); section = NULL; + + /* Local, readable, html, with section */ + restype = yelp_uri_resolve ("/usr/share/doc/shared-mime-info/shared-mime-info-spec.html/index.html#foobar", &result, §ion); + if (restype != YELP_TYPE_HTML || !TestFile (result) || + !g_str_equal (section, "foobar")) { + return 117; + } + g_free (result); result=NULL; + g_free (section); section = NULL; + + /* error */ + restype = yelp_uri_resolve ("file:///usr/fake_file1", &result, §ion); + if (restype != YELP_TYPE_ERROR || result != NULL || section != NULL) { + return 118; + } + g_free (result); result=NULL; + g_free (section); section = NULL; + + + + + return 0; +} + +int +main (int argc, char *argv[]) +{ + int i=1; + + /* Used within yelp-utils */ + gnome_vfs_init (); + + if (argc % 2 != 1) { + printf ("Usage: %s [<test-uri> <type> <test-uri> <type> ... ]\n", argv[0]); + printf ("type can be one of:\n"); + printf ("man, info, doc, html, external, fail\n"); + return 1; + } + + if (argc == 1) { + int ret; + ret = DefaultTests (); + return ret; + } + while (i < argc) { + char *uri = argv[i]; + char *type = argv[i+1]; + char *result = NULL; + char *section = NULL; + YelpRrnType restype = YELP_TYPE_ERROR; + + + printf ("uri: %s type: %s\n", argv[i], argv[i+1]); + + if (g_str_equal (type, "doc")) { + restype = yelp_uri_resolve (argv[i], &result, §ion); + if (restype != YELP_TYPE_DOC || !TestFile (result)) { + printf ("Failed doc test %s. Aborting.\n", uri); + return 2; + } + } else if (g_str_equal (type, "info")) { + restype = yelp_uri_resolve (argv[i], &result, §ion); + if (restype != YELP_TYPE_INFO || !TestFile (result)) { + printf ("Failed doc test %s. Aborting.\n", uri); + return 3; + } + } else if (g_str_equal (type, "man")) { + restype = yelp_uri_resolve (argv[i], &result, §ion); + if (restype != YELP_TYPE_MAN || !TestFile (result)) { + printf ("Failed doc test %s. Aborting.\n", uri); + return 4; + } + } else if (g_str_equal (type, "external")) { + restype = yelp_uri_resolve (argv[i], &result, §ion); + if (restype != YELP_TYPE_EXTERNAL) { + printf ("Failed doc test %s. Aborting.\n", uri, restype, YELP_TYPE_EXTERNAL); + return 5; + } + } else if (g_str_equal (type, "error")) { + restype = yelp_uri_resolve (argv[i], &result, §ion); + if (restype != YELP_TYPE_ERROR || result != NULL) { + printf ("Failed doc test %s. Aborting.\n", uri); + return 6; + } + } else { + printf ("Unknown test: %s. Ignoring.\n", type); + } + i+=2; + g_free (result); + result = NULL; + } + return 0; +} + + diff --git a/src/test-transform.c b/src/test-transform.c new file mode 100644 index 00000000..ae659be0 --- /dev/null +++ b/src/test-transform.c @@ -0,0 +1,163 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2006 Shaun McCance + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Cambridge, MA 02139, USA. + * + * Author: Shaun McCance <shaunm@gnome.org> + */ + +#include <glib.h> +#include <libxml/parser.h> +#include <libxml/xinclude.h> + +#include "yelp-error.h" +#include "yelp-transform.h" + +static gint timeout = -1; +static gboolean random_timeout = FALSE; +static gchar **files = NULL; +static const GOptionEntry options[] = { + { "random-timeout", 'r', + 0, G_OPTION_ARG_NONE, + &random_timeout, + "Time out after a random amount of time", NULL }, + { "timeout", 't', + 0, G_OPTION_ARG_INT, + &timeout, + "Time out after N milliseconds", "N" }, + { G_OPTION_REMAINING, + 0, 0, G_OPTION_ARG_FILENAME_ARRAY, + &files, NULL, NULL }, + { NULL } +}; + +GMainLoop *loop; + +static void +transform_release (YelpTransform *transform) +{ + printf ("\nRELEASE\n"); + yelp_transform_release (transform); + /* Quit after pending things are done. This helps test + * whether YelpTransform takes its release seriously. + */ + g_idle_add ((GSourceFunc) g_main_loop_quit, loop); +} + +static void +transform_func (YelpTransform *transform, + YelpTransformSignal signal, + gpointer *func_data, + gpointer user_data) +{ + gchar *chunk_id; + gchar *chunk_data; + gchar *chunk_short; + YelpError *error; + + switch (signal) { + case YELP_TRANSFORM_CHUNK: + chunk_id = (gchar *) func_data; + printf ("\nCHUNK: %s\n", chunk_id); + + chunk_data = yelp_transform_eat_chunk (transform, chunk_id); + chunk_short = g_strndup (chunk_data, 300); + printf ("%s\n", chunk_short); + + g_free (chunk_short); + g_free (chunk_data); + g_free (chunk_id); + break; + case YELP_TRANSFORM_ERROR: + error = (YelpError *) func_data; + printf ("\nERROR: %s\n", yelp_error_get_title (error)); + yelp_error_free (error); + break; + case YELP_TRANSFORM_FINAL: + printf ("\nFINAL\n"); + transform_release (transform); + break; + } +} + +gint +main (gint argc, gchar **argv) +{ + GOptionContext *context; + xmlParserCtxtPtr parser; + xmlDocPtr doc; + YelpTransform *transform; + gchar **params; + gchar *stylesheet; + gchar *file; + + g_thread_init (NULL); + + context = g_option_context_new ("[STYLESHEET] FILE"); + g_option_context_add_main_entries (context, options, NULL); + g_option_context_parse (context, &argc, &argv, NULL); + + if (files == NULL || files[0] == NULL) { + g_printerr ("Usage: test-transform [OPTION...] [STYLESHEET] FILE\n"); + return 1; + } + + if (files[1] == NULL) { + stylesheet = DATADIR"/yelp/xslt/db2html.xsl"; + file = files[0]; + } else { + stylesheet = files[0]; + file = files[1]; + } + + params = g_new0 (gchar *, 7); + params[0] = "db.chunk.extension"; + params[1] = "\"\""; + params[2] = "db.chunk.info_basename"; + params[3] = "\"x-yelp-titlepage\""; + params[4] = "db.chunk.max_depth"; + params[5] = "2"; + params[6] = NULL; + + transform = yelp_transform_new (stylesheet, + (YelpTransformFunc) transform_func, + NULL); + parser = xmlNewParserCtxt (); + doc = xmlCtxtReadFile (parser, + file, + NULL, + XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA | + XML_PARSE_NOENT | XML_PARSE_NONET ); + xmlFreeParserCtxt (parser); + xmlXIncludeProcessFlags (doc, + XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA | + XML_PARSE_NOENT | XML_PARSE_NONET ); + yelp_transform_start (transform, doc, params); + + if (random_timeout) { + GRand *rand = g_rand_new (); + timeout = g_rand_int_range (rand, 80, 280); + g_rand_free (rand); + } + + if (timeout >= 0) + g_timeout_add (timeout, (GSourceFunc) transform_release, transform); + + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + + return 0; +} diff --git a/src/yelp-base.c b/src/yelp-base.c index 6761e969..98c23e6d 100644 --- a/src/yelp-base.c +++ b/src/yelp-base.c @@ -32,8 +32,7 @@ #include "yelp-window.h" #include "yelp-settings.h" -#include "yelp-pager.h" -#include "yelp-toc-pager.h" +#include "yelp-toc.h" #include "yelp-base.h" #include "yelp-bookmarks.h" #include "server-bindings.h" @@ -98,9 +97,12 @@ yelp_base_init (YelpBase *base) priv->toc_tree = g_node_new (NULL); priv->index = NULL; priv->windows = NULL; - yelp_bookmarks_init (); yelp_settings_init (); + /* Init here to start processing before + * we even start the window */ + yelp_toc_new(); + } static void @@ -129,8 +131,7 @@ server_get_url_list (YelpBase *server, gchar **urls, GError **error) { gint len, i; GSList *node; - YelpDocInfo *doc_info; - gchar *uri; + const gchar *uri; YelpBasePriv *priv; priv = server->priv; @@ -139,23 +140,18 @@ server_get_url_list (YelpBase *server, gchar **urls, GError **error) node = priv->windows; - doc_info = yelp_window_get_doc_info (YELP_WINDOW (node->data)); - uri = yelp_doc_info_get_uri (doc_info, NULL, - YELP_URI_TYPE_ANY); + uri = yelp_window_get_uri ((YelpWindow *) node->data); *urls = g_strdup (uri); - g_free (uri); node = node->next; for (i = 0; node; node = node->next, i++) { gchar *list; - doc_info = yelp_window_get_doc_info (YELP_WINDOW (node->data)); - uri = yelp_doc_info_get_uri (doc_info, NULL, - YELP_URI_TYPE_ANY); - list = g_strconcat (*urls, ";", uri, NULL); + uri = yelp_window_get_uri ((YelpWindow *) node->data); + + list = g_strconcat (uri, ";", *urls, NULL); g_free (*urls); *urls = g_strdup (list); g_free (list); - g_free (uri); } return TRUE; } @@ -200,7 +196,7 @@ yelp_base_new (gboolean priv) if (!priv) yelp_base_register_dbus (base); base->priv->private_session = priv; - yelp_toc_pager_init (); + //yelp_toc_pager_init (); return base; } diff --git a/src/yelp-db-pager.c b/src/yelp-db-pager.c index 928c8d20..800ff5f0 100644 --- a/src/yelp-db-pager.c +++ b/src/yelp-db-pager.c @@ -235,7 +235,7 @@ db_pager_parse (YelpPager *pager) XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA | XML_PARSE_NOENT | XML_PARSE_NONET ); if (doc == NULL) { - g_set_error (&error, YELP_ERROR, YELP_ERROR_NO_DOC, + g_set_error (&error, YELP_GERROR, YELP_ERROR_NO_DOC, _("The file ‘%s’ could not be parsed. Either the file " "does not exist, or it is not well-formed XML."), filename); @@ -295,8 +295,8 @@ db_pager_params (YelpPager *pager) debug_print (DB_FUNCTION, "entering\n"); - g_return_val_if_fail (pager != NULL, NULL); - g_return_val_if_fail (YELP_IS_DB_PAGER (pager), NULL); + g_return_val_if_fail (pager != NULL, FALSE); + g_return_val_if_fail (YELP_IS_DB_PAGER (pager), FALSE); priv = YELP_DB_PAGER (pager)->priv; if (yelp_pager_get_state (pager) >= YELP_PAGER_STATE_ERROR) @@ -496,12 +496,10 @@ node_get_title (DBWalker *walker, gchar *type) { gchar *title = NULL; xmlChar *node_name = (xmlChar *) walker->cur->name; + xmlNodePtr child = NULL; xmlNodePtr title_node = NULL; if (xmlStrcmp (node_name, BAD_CAST "refentry")) { - - xmlNodePtr child = NULL; - /*refentry is special cased below */ title_node = node_find_child (walker->cur, type); if (!title_node) { diff --git a/src/yelp-db-print-pager.c b/src/yelp-db-print-pager.c index 734d7deb..8e184004 100644 --- a/src/yelp-db-print-pager.c +++ b/src/yelp-db-print-pager.c @@ -228,8 +228,8 @@ db_print_pager_params (YelpPager *pager) doc_info = yelp_pager_get_doc_info (pager); - g_return_val_if_fail (pager != NULL, NULL); - g_return_val_if_fail (YELP_IS_DB_PRINT_PAGER (pager), NULL); + g_return_val_if_fail (pager != NULL, FALSE); + g_return_val_if_fail (YELP_IS_DB_PRINT_PAGER (pager), FALSE); priv = YELP_DB_PRINT_PAGER (pager)->priv; if (yelp_pager_get_state (pager) >= YELP_PAGER_STATE_ERROR) diff --git a/src/yelp-db-print.c b/src/yelp-db-print.c new file mode 100644 index 00000000..61d7d1c6 --- /dev/null +++ b/src/yelp-db-print.c @@ -0,0 +1,767 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2003-2007 Shaun McCance <shaunm@gnome.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Shaun McCance <shaunm@gnome.org> + * Author: Don Scorgie <Don@Scorgie.org> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <libxml/parser.h> +#include <libxml/parserInternals.h> +#include <libxml/xinclude.h> + +#include "yelp-error.h" +#include "yelp-db-print.h" +#include "yelp-settings.h" +#include "yelp-transform.h" +#include "yelp-debug.h" + +#define STYLESHEET DATADIR"/yelp/xslt/db2html.xsl" + +#define YELP_DBPRINT_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_DBPRINT, YelpDbprintPriv)) + +typedef enum { + DBPRINT_STATE_BLANK, /* Brand new, run transform as needed */ + DBPRINT_STATE_PARSING, /* Parsing/transforming document, please wait */ + DBPRINT_STATE_PARSED, /* All done, if we ain't got it, it ain't here */ + DBPRINT_STATE_STOP /* Stop everything now, object to be disposed */ +} DbprintState; + +struct _YelpDbprintPriv { + gchar *filename; + DbprintState state; + + GMutex *mutex; + GThread *thread; + + gboolean process_running; + gboolean transform_running; + + YelpTransform *transform; + + xmlDocPtr xmldoc; + xmlNodePtr xmlcur; + gint max_depth; + gint cur_depth; + gchar *cur_page_id; + gchar *cur_prev_id; +}; + + +static void dbprint_class_init (YelpDbprintClass *klass); +static void dbprint_init (YelpDbprint *dbprint); +static void dbprint_try_dispose (GObject *object); +static void dbprint_dispose (GObject *object); + +/* YelpDocument */ +static void dbprint_request (YelpDocument *document, + gint req_id, + gboolean handled, + gchar *page_id, + YelpDocumentFunc func, + gpointer user_data); + +/* YelpTransform */ +static void transform_func (YelpTransform *transform, + YelpTransformSignal signal, + gpointer func_data, + YelpDbprint *dbprint); +static void transform_page_func (YelpTransform *transform, + gchar *page_id, + YelpDbprint *dbprint); +static void transform_final_func (YelpTransform *transform, + YelpDbprint *dbprint); + +/* Threaded */ +static void dbprint_process (YelpDbprint *dbprint); + +#if 0 +/* Walker */ +static void dbprint_walk (YelpDbprint *dbprint); +static gboolean dbprint_walk_chunkQ (YelpDbprint *dbprint); +static gboolean dbprint_walk_divisionQ (YelpDbprint *dbprint); +static gchar * dbprint_walk_get_title (YelpDbprint *dbprint); +#endif + +static YelpDocumentClass *parent_class; + +GType +yelp_dbprint_get_type (void) +{ + static GType type = 0; + if (!type) { + static const GTypeInfo info = { + sizeof (YelpDbprintClass), + NULL, NULL, + (GClassInitFunc) dbprint_class_init, + NULL, NULL, + sizeof (YelpDbprint), + 0, + (GInstanceInitFunc) dbprint_init, + }; + type = g_type_register_static (YELP_TYPE_DOCUMENT, + "YelpDbprint", + &info, 0); + } + return type; +} + +static void +dbprint_class_init (YelpDbprintClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + YelpDocumentClass *document_class = YELP_DOCUMENT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->dispose = dbprint_try_dispose; + + document_class->request = dbprint_request; + document_class->cancel = NULL; + + g_type_class_add_private (klass, sizeof (YelpDbprintPriv)); +} + +static void +dbprint_init (YelpDbprint *dbprint) +{ + YelpDbprintPriv *priv; + priv = dbprint->priv = YELP_DBPRINT_GET_PRIVATE (dbprint); + + + priv->state = DBPRINT_STATE_BLANK; + + priv->mutex = g_mutex_new (); +} + +static void +dbprint_try_dispose (GObject *object) +{ + YelpDbprintPriv *priv; + + g_assert (object != NULL && YELP_IS_DBPRINT (object)); + + priv = YELP_DBPRINT (object)->priv; + + g_mutex_lock (priv->mutex); + if (priv->process_running || priv->transform_running) { + priv->state = DBPRINT_STATE_STOP; + g_idle_add ((GSourceFunc) dbprint_try_dispose, object); + g_mutex_unlock (priv->mutex); + } else { + g_mutex_unlock (priv->mutex); + dbprint_dispose (object); + } +} + +static void +dbprint_dispose (GObject *object) +{ + YelpDbprint *dbprint = YELP_DBPRINT (object); + + g_free (dbprint->priv->filename); + + if (dbprint->priv->xmldoc) + xmlFreeDoc (dbprint->priv->xmldoc); + + g_free (dbprint->priv->cur_page_id); + g_free (dbprint->priv->cur_prev_id); + + g_mutex_free (dbprint->priv->mutex); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +/******************************************************************************/ + +YelpDocument * +yelp_dbprint_new (gchar *filename) +{ + YelpDbprint *dbprint; + + g_return_val_if_fail (filename != NULL, NULL); + + debug_print (DB_FUNCTION, "entering\n"); + debug_print (DB_ARG, " filename = \"%s\"\n", filename); + + dbprint = (YelpDbprint *) g_object_new (YELP_TYPE_DBPRINT, NULL); + dbprint->priv->filename = g_strdup (filename); + + yelp_document_add_page_id (YELP_DOCUMENT (dbprint), "x-yelp-titlepage", "x-yelp-titlepage"); + + return (YelpDocument *) dbprint; +} + + +/******************************************************************************/ +/** YelpDocument **************************************************************/ + +static void +dbprint_request (YelpDocument *document, + gint req_id, + gboolean handled, + gchar *page_id, + YelpDocumentFunc func, + gpointer user_data) +{ + YelpDbprint *dbprint; + YelpDbprintPriv *priv; + YelpError *error; + + debug_print (DB_FUNCTION, "entering\n"); + debug_print (DB_ARG, " req_id = %i\n", req_id); + debug_print (DB_ARG, " page_id = \"%s\"\n", page_id); + g_assert (document != NULL && YELP_IS_DBPRINT (document)); + + if (handled) { + return; + } + + dbprint = YELP_DBPRINT (document); + priv = dbprint->priv; + + g_mutex_lock (priv->mutex); + + switch (priv->state) { + case DBPRINT_STATE_BLANK: + priv->state = DBPRINT_STATE_PARSING; + priv->process_running = TRUE; + priv->thread = g_thread_create ((GThreadFunc) dbprint_process, + dbprint, FALSE, NULL); + break; + case DBPRINT_STATE_PARSING: + break; + case DBPRINT_STATE_PARSED: + case DBPRINT_STATE_STOP: + error = yelp_error_new (_("Page not found"), + _("The page %s was not found in the document %s."), + page_id, priv->filename); + yelp_document_error_request (document, req_id, error); + break; + } + + g_mutex_unlock (priv->mutex); +} + +/******************************************************************************/ +/** YelpTransform *************************************************************/ + +static void +transform_func (YelpTransform *transform, + YelpTransformSignal signal, + gpointer func_data, + YelpDbprint *dbprint) +{ + YelpDbprintPriv *priv; + debug_print (DB_FUNCTION, "entering\n"); + + g_assert (dbprint != NULL && YELP_IS_DBPRINT (dbprint)); + + priv = dbprint->priv; + + g_assert (transform == priv->transform); + + if (priv->state == DBPRINT_STATE_STOP) { + switch (signal) { + case YELP_TRANSFORM_CHUNK: + g_free (func_data); + break; + case YELP_TRANSFORM_ERROR: + yelp_error_free ((YelpError *) func_data); + break; + case YELP_TRANSFORM_FINAL: + break; + } + yelp_transform_release (transform); + priv->transform = NULL; + priv->transform_running = FALSE; + return; + } + + switch (signal) { + case YELP_TRANSFORM_CHUNK: + transform_page_func (transform, (gchar *) func_data, dbprint); + break; + case YELP_TRANSFORM_ERROR: + yelp_document_error_pending (YELP_DOCUMENT (dbprint), (YelpError *) func_data); + yelp_transform_release (transform); + priv->transform = NULL; + priv->transform_running = FALSE; + break; + case YELP_TRANSFORM_FINAL: + transform_final_func (transform, dbprint); + break; + } +} + +static void +transform_page_func (YelpTransform *transform, + gchar *page_id, + YelpDbprint *dbprint) +{ + YelpDbprintPriv *priv; + gchar *content; + + debug_print (DB_FUNCTION, "entering\n"); + + priv = dbprint->priv; + g_mutex_lock (priv->mutex); + + content = yelp_transform_eat_chunk (transform, page_id); + yelp_document_add_page (YELP_DOCUMENT (dbprint), page_id, content); + + g_free (page_id); + + g_mutex_unlock (priv->mutex); +} + +static void +transform_final_func (YelpTransform *transform, YelpDbprint *dbprint) +{ + YelpError *error; + YelpDbprintPriv *priv = dbprint->priv; + + debug_print (DB_FUNCTION, "entering\n"); + + g_mutex_lock (priv->mutex); + + error = yelp_error_new (_("Page not found"), + _("The requested page was not found in the document %s."), + priv->filename); + yelp_document_error_pending (YELP_DOCUMENT (dbprint), error); + + yelp_transform_release (transform); + priv->transform = NULL; + priv->transform_running = FALSE; + + if (priv->xmldoc) + xmlFreeDoc (priv->xmldoc); + priv->xmldoc = NULL; + + g_mutex_unlock (priv->mutex); +} + + +/******************************************************************************/ +/** Threaded ******************************************************************/ + +static void +dbprint_process (YelpDbprint *dbprint) +{ + YelpDbprintPriv *priv; + xmlDocPtr xmldoc = NULL; + xmlChar *id = NULL; + YelpError *error = NULL; + xmlParserCtxtPtr parserCtxt = NULL; + YelpDocument *document; + gchar **params; + gint params_i = 0; + gint params_max = 20; + + + debug_print (DB_FUNCTION, "entering\n"); + + g_assert (dbprint != NULL && YELP_IS_DBPRINT (dbprint)); + g_object_ref (dbprint); + priv = dbprint->priv; + document = YELP_DOCUMENT (dbprint); + + if (!g_file_test (priv->filename, G_FILE_TEST_IS_REGULAR)) { + error = yelp_error_new (_("File not found"), + _("The file ‘%s’ does not exist."), + priv->filename); + yelp_document_error_pending (document, error); + goto done; + } + + parserCtxt = xmlNewParserCtxt (); + xmldoc = xmlCtxtReadFile (parserCtxt, + (const char *) priv->filename, NULL, + XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA | + XML_PARSE_NOENT | XML_PARSE_NONET ); + + if (xmldoc == NULL) { + error = yelp_error_new (_("Could not parse file"), + _("The file ‘%s’ could not be parsed because it is" + " not a well-formed XML document."), + priv->filename); + yelp_document_error_pending (document, error); + goto done; + } + + if (xmlXIncludeProcessFlags (xmldoc, + XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA | + XML_PARSE_NOENT | XML_PARSE_NONET ) + < 0) { + error = yelp_error_new (_("Could not parse file"), + _("The file ‘%s’ could not be parsed because" + " one or more of its included files is not" + " a well-formed XML document."), + priv->filename); + yelp_document_error_pending (document, error); + goto done; + } + + g_mutex_lock (priv->mutex); + if (!xmlStrcmp (xmlDocGetRootElement (xmldoc)->name, BAD_CAST "book")) + priv->max_depth = 2; + else + priv->max_depth = 1; + + priv->xmldoc = xmldoc; + priv->xmlcur = xmlDocGetRootElement (xmldoc); + + id = xmlGetProp (priv->xmlcur, BAD_CAST "id"); + if (id) { + yelp_document_set_root_id (document, (gchar *) id); + yelp_document_add_page_id (document, "x-yelp-index", (gchar *) id); + yelp_document_add_prev_id (document, (gchar *) id, "x-yelp-titlepage"); + yelp_document_add_next_id (document, "x-yelp-titlepage", (gchar *) id); + } + else { + yelp_document_set_root_id (document, "x-yelp-index"); + yelp_document_add_page_id (document, "x-yelp-index", "x-yelp-index"); + yelp_document_add_prev_id (document, "x-yelp-index", "x-yelp-titlepage"); + yelp_document_add_next_id (document, "x-yelp-titlepage", "x-yelp-index"); + /* add the id attribute to the root element with value "index" + * so when we try to load the document later, it doesn't fail */ + xmlNewProp (priv->xmlcur, BAD_CAST "id", BAD_CAST "x-yelp-index"); + } + g_mutex_unlock (priv->mutex); + + g_mutex_lock (priv->mutex); + if (priv->state == DBPRINT_STATE_STOP) { + g_mutex_unlock (priv->mutex); + goto done; + } + g_mutex_unlock (priv->mutex); + + g_mutex_lock (priv->mutex); + if (priv->state == DBPRINT_STATE_STOP) { + g_mutex_unlock (priv->mutex); + goto done; + } + priv->transform = yelp_transform_new (STYLESHEET, + (YelpTransformFunc) transform_func, + dbprint); + priv->transform_running = TRUE; + /* FIXME: we probably need to set our own params */ + + params = g_new0 (gchar *, params_max); + + yelp_settings_params (¶ms, ¶ms_i, ¶ms_max); + + if ((params_i + 10) >= params_max - 1) { + params_max += 20; + params = g_renew (gchar *, params, params_max); + } + params[params_i++] = "db.chunk.extension"; + params[params_i++] = g_strdup ("\"\""); + params[params_i++] = "db.chunk.info_basename"; + params[params_i++] = g_strdup ("\"index\""); + params[params_i++] = "db.chunk.max_depth"; + params[params_i++] = g_strdup ("0"); + params[params_i++] = "db2html.navbar.top"; + params[params_i++] = g_strdup ("0"); + params[params_i++] = "db2html.navbar.bottom"; + params[params_i++] = g_strdup ("0"); + params[params_i++] = "db2html.sidenav"; + params[params_i++] = g_strdup ("0"); + + params[params_i] = NULL; + + yelp_transform_start (priv->transform, + priv->xmldoc, + params); + g_mutex_unlock (priv->mutex); + + done: + if (id) + xmlFree (id); + if (parserCtxt) + xmlFreeParserCtxt (parserCtxt); + + priv->process_running = FALSE; + g_object_unref (dbprint); +} + +#if 0 +/******************************************************************************/ +/** Walker ********************************************************************/ + +static void +dbprint_walk (YelpDbprint *dbprint) +{ + static gint autoid = 0; + gchar autoidstr[20]; + xmlChar *id = NULL; + xmlChar *title = NULL; + gchar *old_page_id = NULL; + xmlNodePtr cur, old_cur; + GtkTreeIter iter; + GtkTreeIter *old_iter = NULL; + YelpDbprintPriv *priv = dbprint->priv; + YelpDocument *document = YELP_DOCUMENT (dbprint); + + debug_print (DB_FUNCTION, "entering\n"); + debug_print (DB_DEBUG, " priv->xmlcur->name: %s\n", priv->xmlcur->name); + + /* check for the yelp:chunk-depth or db.chunk.max_depth processing + * instruction and set the max chunk depth accordingly. + */ + if (priv->cur_depth == 0) + for (cur = priv->xmlcur; cur; cur = cur->prev) + if (cur->type == XML_PI_NODE) + if (!xmlStrcmp (cur->name, (const xmlChar *) "yelp:chunk-depth") || + !xmlStrcmp (cur->name, (const xmlChar *) "db.chunk.max_depth")) { + gint max = atoi ((gchar *) cur->content); + if (max) + priv->max_depth = max; + break; + } + + id = xmlGetProp (priv->xmlcur, BAD_CAST "id"); + + if (dbprint_walk_chunkQ (dbprint)) { + title = BAD_CAST dbprint_walk_get_title (dbprint); + + /* if id attribute is not present, autogenerate a + * unique value, and insert it into the in-memory tree */ + if (!id) { + g_snprintf (autoidstr, 20, "_auto-gen-id-%d", ++autoid); + xmlNewProp (priv->xmlcur, BAD_CAST "id", BAD_CAST autoidstr); + id = xmlGetProp (priv->xmlcur, BAD_CAST "id"); + } + + debug_print (DB_DEBUG, " id: \"%s\"\n", id); + debug_print (DB_DEBUG, " title: \"%s\"\n", title); + + yelp_document_add_title (document, (gchar *) id, (gchar *) title); + + + if (priv->cur_prev_id) { + yelp_document_add_prev_id (document, (gchar *) id, priv->cur_prev_id); + yelp_document_add_next_id (document, priv->cur_prev_id, (gchar *) id); + g_free (priv->cur_prev_id); + } + priv->cur_prev_id = g_strdup ((gchar *) id); + + if (priv->cur_page_id) + yelp_document_add_up_id (document, (gchar *) id, priv->cur_page_id); + old_page_id = priv->cur_page_id; + priv->cur_page_id = g_strdup ((gchar *) id); + + } + + old_cur = priv->xmlcur; + priv->cur_depth++; + + if (id) + yelp_document_add_page_id (document, (gchar *) id, priv->cur_page_id); + + for (cur = priv->xmlcur->children; cur; cur = cur->next) { + if (cur->type == XML_ELEMENT_NODE) { + priv->xmlcur = cur; + dbprint_walk (dbprint); + } + } + priv->cur_depth--; + priv->xmlcur = old_cur; + + if (dbprint_walk_chunkQ (dbprint)) { + priv->sections_iter = old_iter; + g_free (priv->cur_page_id); + priv->cur_page_id = old_page_id; + } + + if (priv->cur_depth == 0) { + g_free (priv->cur_prev_id); + priv->cur_prev_id = NULL; + + g_free (priv->cur_page_id); + priv->cur_page_id = NULL; + } + + if (id != NULL) + xmlFree (id); + if (title != NULL) + xmlFree (title); +} + +static gboolean +dbprint_walk_chunkQ (YelpDbprint *dbprint) +{ + if (dbprint->priv->cur_depth <= dbprint->priv->max_depth + && dbprint_walk_divisionQ (dbprint)) + return TRUE; + else + return FALSE; +} + +static gboolean +dbprint_walk_divisionQ (YelpDbprint *dbprint) +{ + xmlNodePtr node = dbprint->priv->xmlcur; + return (!xmlStrcmp (node->name, (const xmlChar *) "appendix") || + !xmlStrcmp (node->name, (const xmlChar *) "article") || + !xmlStrcmp (node->name, (const xmlChar *) "book") || + !xmlStrcmp (node->name, (const xmlChar *) "bibliography") || + !xmlStrcmp (node->name, (const xmlChar *) "chapter") || + !xmlStrcmp (node->name, (const xmlChar *) "colophon") || + !xmlStrcmp (node->name, (const xmlChar *) "glossary") || + !xmlStrcmp (node->name, (const xmlChar *) "index") || + !xmlStrcmp (node->name, (const xmlChar *) "part") || + !xmlStrcmp (node->name, (const xmlChar *) "preface") || + !xmlStrcmp (node->name, (const xmlChar *) "reference") || + !xmlStrcmp (node->name, (const xmlChar *) "refentry") || + !xmlStrcmp (node->name, (const xmlChar *) "refsect1") || + !xmlStrcmp (node->name, (const xmlChar *) "refsect2") || + !xmlStrcmp (node->name, (const xmlChar *) "refsect3") || + !xmlStrcmp (node->name, (const xmlChar *) "refsection") || + !xmlStrcmp (node->name, (const xmlChar *) "sect1") || + !xmlStrcmp (node->name, (const xmlChar *) "sect2") || + !xmlStrcmp (node->name, (const xmlChar *) "sect3") || + !xmlStrcmp (node->name, (const xmlChar *) "sect4") || + !xmlStrcmp (node->name, (const xmlChar *) "sect5") || + !xmlStrcmp (node->name, (const xmlChar *) "section") || + !xmlStrcmp (node->name, (const xmlChar *) "set") || + !xmlStrcmp (node->name, (const xmlChar *) "setindex") || + !xmlStrcmp (node->name, (const xmlChar *) "simplesect") ); +} + +static gchar * +dbprint_walk_get_title (YelpDbprint *dbprint) +{ + gchar *infoname = NULL; + xmlNodePtr child = NULL; + xmlNodePtr title = NULL; + xmlNodePtr title_tmp = NULL; + YelpDbprintPriv *priv = dbprint->priv; + + if (!xmlStrcmp (priv->xmlcur->name, BAD_CAST "refentry")) { + /* The title for a refentry element can come from the following: + * refmeta/refentrytitle + * refentryinfo/title[abbrev] + * refnamediv/refname + * We take the first one we find. + */ + for (child = priv->xmlcur->children; child; child = child->next) { + if (!xmlStrcmp (child->name, BAD_CAST "refmeta")) { + for (title = child->children; title; title = title->next) { + if (!xmlStrcmp (title->name, BAD_CAST "refentrytitle")) + break; + } + if (title) + goto done; + } + else if (!xmlStrcmp (child->name, BAD_CAST "refentryinfo")) { + for (title = child->children; title; title = title->next) { + if (!xmlStrcmp (title->name, BAD_CAST "titleabbrev")) + break; + else if (!xmlStrcmp (title->name, BAD_CAST "title")) + title_tmp = title; + } + if (title) + goto done; + else if (title_tmp) { + title = title_tmp; + goto done; + } + } + else if (!xmlStrcmp (child->name, BAD_CAST "refnamediv")) { + for (title = child->children; title; title = title->next) { + if (!xmlStrcmp (title->name, BAD_CAST "refname")) + break; + else if (!xmlStrcmp (title->name, BAD_CAST "refpurpose")) { + title = NULL; + break; + } + } + if (title) + goto done; + } + else if (!xmlStrncmp (child->name, BAD_CAST "refsect", 7)) + break; + } + } + else { + /* The title for other elements appears in the following: + * title[abbrev] + * *info/title[abbrev] + * blockinfo/title[abbrev] + * objectinfo/title[abbrev] + * We take them in that order. + */ + xmlNodePtr infos[3] = {NULL, NULL, NULL}; + int i; + + infoname = g_strdup_printf ("%sinfo", priv->xmlcur->name); + + for (child = priv->xmlcur->children; child; child = child->next) { + if (!xmlStrcmp (child->name, BAD_CAST "titleabbrev")) { + title = child; + goto done; + } + else if (!xmlStrcmp (child->name, BAD_CAST "title")) + title_tmp = child; + else if (!xmlStrcmp (child->name, BAD_CAST infoname)) + infos[0] = child; + else if (!xmlStrcmp (child->name, BAD_CAST "blockinfo")) + infos[1] = child; + else if (!xmlStrcmp (child->name, BAD_CAST "objectinfo")) + infos[2] = child; + } + + if (title_tmp) { + title = title_tmp; + goto done; + } + + for (i = 0; i < 3; i++) { + child = infos[i]; + if (child) { + for (title = child->children; title; title = title->next) { + if (!xmlStrcmp (title->name, BAD_CAST "titleabbrev")) + goto done; + else if (!xmlStrcmp (title->name, BAD_CAST "title")) + title_tmp = title; + } + if (title_tmp) { + title = title_tmp; + goto done; + } + } + } + } + + done: + g_free (infoname); + + if (title) + return (gchar *) xmlNodeGetContent (title); + else + return g_strdup (_("Unknown")); +} +#endif diff --git a/src/yelp-db-print.h b/src/yelp-db-print.h new file mode 100644 index 00000000..c0ded190 --- /dev/null +++ b/src/yelp-db-print.h @@ -0,0 +1,54 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2003-2007 Shaun McCance <shaunm@gnome.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Shaun McCance <shaunm@gnome.org> + * Author: Don Scorgie <Don@Scorgie.org> + */ + +#ifndef __YELP_DB_PRINT_H__ +#define __YELP_DB_PRINT_H__ + +#include <glib-object.h> + +#include "yelp-document.h" + +#define YELP_TYPE_DBPRINT (yelp_dbprint_get_type ()) +#define YELP_DBPRINT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), YELP_TYPE_DBPRINT, YelpDbprint)) +#define YELP_DBPRINT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), YELP_TYPE_DBPRINT, YelpDbprintClass)) +#define YELP_IS_DBPRINT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), YELP_TYPE_DBPRINT)) +#define YELP_IS_DBPRINT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), YELP_TYPE_DBPRINT)) +#define YELP_DBPRINT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), YELP_TYPE_DBPRINT, YelpDbprintClass)) + +typedef struct _YelpDbprint YelpDbprint; +typedef struct _YelpDbprintClass YelpDbprintClass; +typedef struct _YelpDbprintPriv YelpDbprintPriv; + +struct _YelpDbprint { + YelpDocument parent; + YelpDbprintPriv *priv; +}; + +struct _YelpDbprintClass { + YelpDocumentClass parent_class; +}; + +GType yelp_dbprint_get_type (void); +YelpDocument * yelp_dbprint_new (gchar *uri); + +#endif /* __YELP_DB_PRINT_H__ */ diff --git a/src/yelp-debug.c b/src/yelp-debug.c index ae47c52b..34aa56df 100644 --- a/src/yelp-debug.c +++ b/src/yelp-debug.c @@ -27,7 +27,7 @@ #include "yelp-debug.h" -static const GDebugKey debug_keys[] = { +GDebugKey debug_keys[] = { { "function-calls", DB_FUNCTION }, { "function-args", DB_ARG }, { "enable-profiling", DB_PROFILE }, diff --git a/src/yelp-debug.h b/src/yelp-debug.h index 657859f9..846ad750 100644 --- a/src/yelp-debug.h +++ b/src/yelp-debug.h @@ -23,7 +23,10 @@ G_BEGIN_DECLS +#ifdef HAVE_CONFIG_H #include <config.h> +#endif + #include <glib.h> typedef enum { diff --git a/src/yelp-docbook.c b/src/yelp-docbook.c new file mode 100644 index 00000000..1f34b174 --- /dev/null +++ b/src/yelp-docbook.c @@ -0,0 +1,785 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2003-2007 Shaun McCance <shaunm@gnome.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Shaun McCance <shaunm@gnome.org> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <libxml/parser.h> +#include <libxml/parserInternals.h> +#include <libxml/xinclude.h> + +#include "yelp-error.h" +#include "yelp-docbook.h" +#include "yelp-settings.h" +#include "yelp-transform.h" +#include "yelp-debug.h" + +#define STYLESHEET DATADIR"/yelp/xslt/db2html.xsl" + +#define YELP_DOCBOOK_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_DOCBOOK, YelpDocbookPriv)) + +typedef enum { + DOCBOOK_STATE_BLANK, /* Brand new, run transform as needed */ + DOCBOOK_STATE_PARSING, /* Parsing/transforming document, please wait */ + DOCBOOK_STATE_PARSED, /* All done, if we ain't got it, it ain't here */ + DOCBOOK_STATE_STOP /* Stop everything now, object to be disposed */ +} DocbookState; + +struct _YelpDocbookPriv { + gchar *filename; + DocbookState state; + + GMutex *mutex; + GThread *thread; + + gboolean process_running; + gboolean transform_running; + + YelpTransform *transform; + + GtkTreeModel *sections; + GtkTreeIter *sections_iter; /* On the stack, do not free */ + + xmlDocPtr xmldoc; + xmlNodePtr xmlcur; + gint max_depth; + gint cur_depth; + gchar *cur_page_id; + gchar *cur_prev_id; +}; + + +static void docbook_class_init (YelpDocbookClass *klass); +static void docbook_init (YelpDocbook *docbook); +static void docbook_try_dispose (GObject *object); +static void docbook_dispose (GObject *object); + +/* YelpDocument */ +static void docbook_request (YelpDocument *document, + gint req_id, + gboolean handled, + gchar *page_id, + YelpDocumentFunc func, + gpointer user_data); +static gpointer docbook_get_sections (YelpDocument *document); + +/* YelpTransform */ +static void transform_func (YelpTransform *transform, + YelpTransformSignal signal, + gpointer func_data, + YelpDocbook *docbook); +static void transform_page_func (YelpTransform *transform, + gchar *page_id, + YelpDocbook *docbook); +static void transform_final_func (YelpTransform *transform, + YelpDocbook *docbook); + +/* Threaded */ +static void docbook_process (YelpDocbook *docbook); + +/* Walker */ +static void docbook_walk (YelpDocbook *docbook); +static gboolean docbook_walk_chunkQ (YelpDocbook *docbook); +static gboolean docbook_walk_divisionQ (YelpDocbook *docbook); +static gchar * docbook_walk_get_title (YelpDocbook *docbook); + + +static YelpDocumentClass *parent_class; + +GType +yelp_docbook_get_type (void) +{ + static GType type = 0; + if (!type) { + static const GTypeInfo info = { + sizeof (YelpDocbookClass), + NULL, NULL, + (GClassInitFunc) docbook_class_init, + NULL, NULL, + sizeof (YelpDocbook), + 0, + (GInstanceInitFunc) docbook_init, + }; + type = g_type_register_static (YELP_TYPE_DOCUMENT, + "YelpDocbook", + &info, 0); + } + return type; +} + +static void +docbook_class_init (YelpDocbookClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + YelpDocumentClass *document_class = YELP_DOCUMENT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->dispose = docbook_try_dispose; + + document_class->request = docbook_request; + document_class->cancel = NULL; + /*document_class->get_sections = docbook_get_sections;*/ + + g_type_class_add_private (klass, sizeof (YelpDocbookPriv)); +} + +static void +docbook_init (YelpDocbook *docbook) +{ + YelpDocbookPriv *priv; + priv = docbook->priv = YELP_DOCBOOK_GET_PRIVATE (docbook); + + priv->sections = NULL; + + priv->state = DOCBOOK_STATE_BLANK; + + priv->mutex = g_mutex_new (); +} + +static void +docbook_try_dispose (GObject *object) +{ + YelpDocbookPriv *priv; + + g_assert (object != NULL && YELP_IS_DOCBOOK (object)); + + priv = YELP_DOCBOOK (object)->priv; + + g_mutex_lock (priv->mutex); + if (priv->process_running || priv->transform_running) { + priv->state = DOCBOOK_STATE_STOP; + g_idle_add ((GSourceFunc) docbook_try_dispose, object); + g_mutex_unlock (priv->mutex); + } else { + g_mutex_unlock (priv->mutex); + docbook_dispose (object); + } +} + +static void +docbook_dispose (GObject *object) +{ + YelpDocbook *docbook = YELP_DOCBOOK (object); + + g_free (docbook->priv->filename); + + g_object_unref (docbook->priv->sections); + + if (docbook->priv->xmldoc) + xmlFreeDoc (docbook->priv->xmldoc); + + g_free (docbook->priv->cur_page_id); + g_free (docbook->priv->cur_prev_id); + + g_mutex_free (docbook->priv->mutex); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +/******************************************************************************/ + +YelpDocument * +yelp_docbook_new (gchar *filename) +{ + YelpDocbook *docbook; + + g_return_val_if_fail (filename != NULL, NULL); + + debug_print (DB_FUNCTION, "entering\n"); + debug_print (DB_ARG, " filename = \"%s\"\n", filename); + + docbook = (YelpDocbook *) g_object_new (YELP_TYPE_DOCBOOK, NULL); + docbook->priv->filename = g_strdup (filename); + + yelp_document_add_page_id (YELP_DOCUMENT (docbook), "x-yelp-titlepage", "x-yelp-titlepage"); + + docbook->priv->sections = + GTK_TREE_MODEL (gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_STRING)); + + return (YelpDocument *) docbook; +} + + +/******************************************************************************/ +/** YelpDocument **************************************************************/ + +static gpointer +docbook_get_sections (YelpDocument *document) +{ + YelpDocbook *db = (YelpDocbook *) document; + + return (gpointer) (db->priv->sections); +} + +static void +docbook_request (YelpDocument *document, + gint req_id, + gboolean handled, + gchar *page_id, + YelpDocumentFunc func, + gpointer user_data) +{ + YelpDocbook *docbook; + YelpDocbookPriv *priv; + YelpError *error; + + debug_print (DB_FUNCTION, "entering\n"); + debug_print (DB_ARG, " req_id = %i\n", req_id); + debug_print (DB_ARG, " page_id = \"%s\"\n", page_id); + g_assert (document != NULL && YELP_IS_DOCBOOK (document)); + + if (handled) { + return; + } + + docbook = YELP_DOCBOOK (document); + priv = docbook->priv; + + g_mutex_lock (priv->mutex); + + switch (priv->state) { + case DOCBOOK_STATE_BLANK: + priv->state = DOCBOOK_STATE_PARSING; + priv->process_running = TRUE; + priv->thread = g_thread_create ((GThreadFunc) docbook_process, + docbook, FALSE, NULL); + break; + case DOCBOOK_STATE_PARSING: + break; + case DOCBOOK_STATE_PARSED: + case DOCBOOK_STATE_STOP: + error = yelp_error_new (_("Page not found"), + _("The page %s was not found in the document %s."), + page_id, priv->filename); + yelp_document_error_request (document, req_id, error); + break; + } + + g_mutex_unlock (priv->mutex); +} + +/******************************************************************************/ +/** YelpTransform *************************************************************/ + +static void +transform_func (YelpTransform *transform, + YelpTransformSignal signal, + gpointer func_data, + YelpDocbook *docbook) +{ + YelpDocbookPriv *priv; + debug_print (DB_FUNCTION, "entering\n"); + + g_assert (docbook != NULL && YELP_IS_DOCBOOK (docbook)); + + priv = docbook->priv; + + g_assert (transform == priv->transform); + + if (priv->state == DOCBOOK_STATE_STOP) { + switch (signal) { + case YELP_TRANSFORM_CHUNK: + g_free (func_data); + break; + case YELP_TRANSFORM_ERROR: + yelp_error_free ((YelpError *) func_data); + break; + case YELP_TRANSFORM_FINAL: + break; + } + yelp_transform_release (transform); + priv->transform = NULL; + priv->transform_running = FALSE; + return; + } + + switch (signal) { + case YELP_TRANSFORM_CHUNK: + transform_page_func (transform, (gchar *) func_data, docbook); + break; + case YELP_TRANSFORM_ERROR: + yelp_document_error_pending (YELP_DOCUMENT (docbook), (YelpError *) func_data); + yelp_transform_release (transform); + priv->transform = NULL; + priv->transform_running = FALSE; + break; + case YELP_TRANSFORM_FINAL: + transform_final_func (transform, docbook); + break; + } +} + +static void +transform_page_func (YelpTransform *transform, + gchar *page_id, + YelpDocbook *docbook) +{ + YelpDocbookPriv *priv; + gchar *content; + + debug_print (DB_FUNCTION, "entering\n"); + + priv = docbook->priv; + g_mutex_lock (priv->mutex); + + content = yelp_transform_eat_chunk (transform, page_id); + yelp_document_add_page (YELP_DOCUMENT (docbook), page_id, content); + + g_free (page_id); + + g_mutex_unlock (priv->mutex); +} + +static void +transform_final_func (YelpTransform *transform, YelpDocbook *docbook) +{ + YelpError *error; + YelpDocbookPriv *priv = docbook->priv; + + debug_print (DB_FUNCTION, "entering\n"); + + g_mutex_lock (priv->mutex); + + error = yelp_error_new (_("Page not found"), + _("The requested page was not found in the document %s."), + priv->filename); + yelp_document_error_pending (YELP_DOCUMENT (docbook), error); + + yelp_transform_release (transform); + priv->transform = NULL; + priv->transform_running = FALSE; + + if (priv->xmldoc) + xmlFreeDoc (priv->xmldoc); + priv->xmldoc = NULL; + + g_mutex_unlock (priv->mutex); +} + + +/******************************************************************************/ +/** Threaded ******************************************************************/ + +static void +docbook_process (YelpDocbook *docbook) +{ + YelpDocbookPriv *priv; + xmlDocPtr xmldoc = NULL; + xmlChar *id = NULL; + YelpError *error = NULL; + xmlParserCtxtPtr parserCtxt = NULL; + YelpDocument *document; + + gint params_i = 0; + gint params_max = 10; + gchar **params = NULL; + + debug_print (DB_FUNCTION, "entering\n"); + + g_assert (docbook != NULL && YELP_IS_DOCBOOK (docbook)); + g_object_ref (docbook); + priv = docbook->priv; + document = YELP_DOCUMENT (docbook); + + if (!g_file_test (priv->filename, G_FILE_TEST_IS_REGULAR)) { + error = yelp_error_new (_("File not found"), + _("The file ‘%s’ does not exist."), + priv->filename); + yelp_document_error_pending (document, error); + goto done; + } + + parserCtxt = xmlNewParserCtxt (); + xmldoc = xmlCtxtReadFile (parserCtxt, + (const char *) priv->filename, NULL, + XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA | + XML_PARSE_NOENT | XML_PARSE_NONET ); + + if (xmldoc == NULL) { + error = yelp_error_new (_("Could not parse file"), + _("The file ‘%s’ could not be parsed because it is" + " not a well-formed XML document."), + priv->filename); + yelp_document_error_pending (document, error); + goto done; + } + + if (xmlXIncludeProcessFlags (xmldoc, + XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA | + XML_PARSE_NOENT | XML_PARSE_NONET ) + < 0) { + error = yelp_error_new (_("Could not parse file"), + _("The file ‘%s’ could not be parsed because" + " one or more of its included files is not" + " a well-formed XML document."), + priv->filename); + yelp_document_error_pending (document, error); + goto done; + } + + g_mutex_lock (priv->mutex); + if (!xmlStrcmp (xmlDocGetRootElement (xmldoc)->name, BAD_CAST "book")) + priv->max_depth = 2; + else + priv->max_depth = 1; + + priv->xmldoc = xmldoc; + priv->xmlcur = xmlDocGetRootElement (xmldoc); + + id = xmlGetProp (priv->xmlcur, BAD_CAST "id"); + if (id) { + yelp_document_set_root_id (document, (gchar *) id); + yelp_document_add_page_id (document, "x-yelp-index", (gchar *) id); + yelp_document_add_prev_id (document, (gchar *) id, "x-yelp-titlepage"); + yelp_document_add_next_id (document, "x-yelp-titlepage", (gchar *) id); + } + else { + yelp_document_set_root_id (document, "x-yelp-index"); + yelp_document_add_page_id (document, "x-yelp-index", "x-yelp-index"); + yelp_document_add_prev_id (document, "x-yelp-index", "x-yelp-titlepage"); + yelp_document_add_next_id (document, "x-yelp-titlepage", "x-yelp-index"); + /* add the id attribute to the root element with value "index" + * so when we try to load the document later, it doesn't fail */ + xmlNewProp (priv->xmlcur, BAD_CAST "id", BAD_CAST "x-yelp-index"); + } + g_mutex_unlock (priv->mutex); + + g_mutex_lock (priv->mutex); + if (priv->state == DOCBOOK_STATE_STOP) { + g_mutex_unlock (priv->mutex); + goto done; + } + g_mutex_unlock (priv->mutex); + + docbook_walk (docbook); + + g_mutex_lock (priv->mutex); + if (priv->state == DOCBOOK_STATE_STOP) { + g_mutex_unlock (priv->mutex); + goto done; + } + priv->transform = yelp_transform_new (STYLESHEET, + (YelpTransformFunc) transform_func, + docbook); + priv->transform_running = TRUE; + + params = g_new0 (gchar *, params_max); + yelp_settings_params (¶ms, ¶ms_i, ¶ms_max); + + + if ((params_i + 10) >= params_max - 1) { + params_max += 20; + params = g_renew (gchar *, params, params_max); + } + + params[params_i] = NULL; + + yelp_transform_start (priv->transform, + priv->xmldoc, + params); + g_mutex_unlock (priv->mutex); + + done: + if (id) + xmlFree (id); + if (parserCtxt) + xmlFreeParserCtxt (parserCtxt); + + priv->process_running = FALSE; + g_object_unref (docbook); +} + + +/******************************************************************************/ +/** Walker ********************************************************************/ + +static void +docbook_walk (YelpDocbook *docbook) +{ + static gint autoid = 0; + gchar autoidstr[20]; + xmlChar *id = NULL; + xmlChar *title = NULL; + gchar *old_page_id = NULL; + xmlNodePtr cur, old_cur; + GtkTreeIter iter; + GtkTreeIter *old_iter = NULL; + YelpDocbookPriv *priv = docbook->priv; + YelpDocument *document = YELP_DOCUMENT (docbook); + + debug_print (DB_FUNCTION, "entering\n"); + debug_print (DB_DEBUG, " priv->xmlcur->name: %s\n", priv->xmlcur->name); + + /* check for the yelp:chunk-depth or db.chunk.max_depth processing + * instruction and set the max chunk depth accordingly. + */ + if (priv->cur_depth == 0) + for (cur = priv->xmlcur; cur; cur = cur->prev) + if (cur->type == XML_PI_NODE) + if (!xmlStrcmp (cur->name, (const xmlChar *) "yelp:chunk-depth") || + !xmlStrcmp (cur->name, (const xmlChar *) "db.chunk.max_depth")) { + gint max = atoi ((gchar *) cur->content); + if (max) + priv->max_depth = max; + break; + } + + id = xmlGetProp (priv->xmlcur, BAD_CAST "id"); + + if (docbook_walk_chunkQ (docbook)) { + title = BAD_CAST docbook_walk_get_title (docbook); + + /* if id attribute is not present, autogenerate a + * unique value, and insert it into the in-memory tree */ + if (!id) { + g_snprintf (autoidstr, 20, "_auto-gen-id-%d", ++autoid); + xmlNewProp (priv->xmlcur, BAD_CAST "id", BAD_CAST autoidstr); + id = xmlGetProp (priv->xmlcur, BAD_CAST "id"); + } + + debug_print (DB_DEBUG, " id: \"%s\"\n", id); + debug_print (DB_DEBUG, " title: \"%s\"\n", title); + + yelp_document_add_title (document, (gchar *) id, (gchar *) title); + + gdk_threads_enter (); + gtk_tree_store_append (GTK_TREE_STORE (priv->sections), + &iter, + priv->sections_iter); + gtk_tree_store_set (GTK_TREE_STORE (priv->sections), + &iter, + YELP_DOCUMENT_COLUMN_ID, id, + YELP_DOCUMENT_COLUMN_TITLE, title, + -1); + gdk_threads_leave (); + + if (priv->cur_prev_id) { + yelp_document_add_prev_id (document, (gchar *) id, priv->cur_prev_id); + yelp_document_add_next_id (document, priv->cur_prev_id, (gchar *) id); + g_free (priv->cur_prev_id); + } + priv->cur_prev_id = g_strdup ((gchar *) id); + + if (priv->cur_page_id) + yelp_document_add_up_id (document, (gchar *) id, priv->cur_page_id); + old_page_id = priv->cur_page_id; + priv->cur_page_id = g_strdup ((gchar *) id); + + old_iter = priv->sections_iter; + if (priv->xmlcur->parent->type != XML_DOCUMENT_NODE) + priv->sections_iter = &iter; + } + + old_cur = priv->xmlcur; + priv->cur_depth++; + + if (id) + yelp_document_add_page_id (document, (gchar *) id, priv->cur_page_id); + + for (cur = priv->xmlcur->children; cur; cur = cur->next) { + if (cur->type == XML_ELEMENT_NODE) { + priv->xmlcur = cur; + docbook_walk (docbook); + } + } + priv->cur_depth--; + priv->xmlcur = old_cur; + + if (docbook_walk_chunkQ (docbook)) { + priv->sections_iter = old_iter; + g_free (priv->cur_page_id); + priv->cur_page_id = old_page_id; + } + + if (priv->cur_depth == 0) { + g_free (priv->cur_prev_id); + priv->cur_prev_id = NULL; + + g_free (priv->cur_page_id); + priv->cur_page_id = NULL; + } + + if (id != NULL) + xmlFree (id); + if (title != NULL) + xmlFree (title); +} + +static gboolean +docbook_walk_chunkQ (YelpDocbook *docbook) +{ + if (docbook->priv->cur_depth <= docbook->priv->max_depth + && docbook_walk_divisionQ (docbook)) + return TRUE; + else + return FALSE; +} + +static gboolean +docbook_walk_divisionQ (YelpDocbook *docbook) +{ + xmlNodePtr node = docbook->priv->xmlcur; + return (!xmlStrcmp (node->name, (const xmlChar *) "appendix") || + !xmlStrcmp (node->name, (const xmlChar *) "article") || + !xmlStrcmp (node->name, (const xmlChar *) "book") || + !xmlStrcmp (node->name, (const xmlChar *) "bibliography") || + !xmlStrcmp (node->name, (const xmlChar *) "chapter") || + !xmlStrcmp (node->name, (const xmlChar *) "colophon") || + !xmlStrcmp (node->name, (const xmlChar *) "glossary") || + !xmlStrcmp (node->name, (const xmlChar *) "index") || + !xmlStrcmp (node->name, (const xmlChar *) "part") || + !xmlStrcmp (node->name, (const xmlChar *) "preface") || + !xmlStrcmp (node->name, (const xmlChar *) "reference") || + !xmlStrcmp (node->name, (const xmlChar *) "refentry") || + !xmlStrcmp (node->name, (const xmlChar *) "refsect1") || + !xmlStrcmp (node->name, (const xmlChar *) "refsect2") || + !xmlStrcmp (node->name, (const xmlChar *) "refsect3") || + !xmlStrcmp (node->name, (const xmlChar *) "refsection") || + !xmlStrcmp (node->name, (const xmlChar *) "sect1") || + !xmlStrcmp (node->name, (const xmlChar *) "sect2") || + !xmlStrcmp (node->name, (const xmlChar *) "sect3") || + !xmlStrcmp (node->name, (const xmlChar *) "sect4") || + !xmlStrcmp (node->name, (const xmlChar *) "sect5") || + !xmlStrcmp (node->name, (const xmlChar *) "section") || + !xmlStrcmp (node->name, (const xmlChar *) "set") || + !xmlStrcmp (node->name, (const xmlChar *) "setindex") || + !xmlStrcmp (node->name, (const xmlChar *) "simplesect") ); +} + +static gchar * +docbook_walk_get_title (YelpDocbook *docbook) +{ + gchar *infoname = NULL; + xmlNodePtr child = NULL; + xmlNodePtr title = NULL; + xmlNodePtr title_tmp = NULL; + YelpDocbookPriv *priv = docbook->priv; + + if (!xmlStrcmp (priv->xmlcur->name, BAD_CAST "refentry")) { + /* The title for a refentry element can come from the following: + * refmeta/refentrytitle + * refentryinfo/title[abbrev] + * refnamediv/refname + * We take the first one we find. + */ + for (child = priv->xmlcur->children; child; child = child->next) { + if (!xmlStrcmp (child->name, BAD_CAST "refmeta")) { + for (title = child->children; title; title = title->next) { + if (!xmlStrcmp (title->name, BAD_CAST "refentrytitle")) + break; + } + if (title) + goto done; + } + else if (!xmlStrcmp (child->name, BAD_CAST "refentryinfo")) { + for (title = child->children; title; title = title->next) { + if (!xmlStrcmp (title->name, BAD_CAST "titleabbrev")) + break; + else if (!xmlStrcmp (title->name, BAD_CAST "title")) + title_tmp = title; + } + if (title) + goto done; + else if (title_tmp) { + title = title_tmp; + goto done; + } + } + else if (!xmlStrcmp (child->name, BAD_CAST "refnamediv")) { + for (title = child->children; title; title = title->next) { + if (!xmlStrcmp (title->name, BAD_CAST "refname")) + break; + else if (!xmlStrcmp (title->name, BAD_CAST "refpurpose")) { + title = NULL; + break; + } + } + if (title) + goto done; + } + else if (!xmlStrncmp (child->name, BAD_CAST "refsect", 7)) + break; + } + } + else { + /* The title for other elements appears in the following: + * title[abbrev] + * *info/title[abbrev] + * blockinfo/title[abbrev] + * objectinfo/title[abbrev] + * We take them in that order. + */ + xmlNodePtr infos[3] = {NULL, NULL, NULL}; + int i; + + infoname = g_strdup_printf ("%sinfo", priv->xmlcur->name); + + for (child = priv->xmlcur->children; child; child = child->next) { + if (!xmlStrcmp (child->name, BAD_CAST "titleabbrev")) { + title = child; + goto done; + } + else if (!xmlStrcmp (child->name, BAD_CAST "title")) + title_tmp = child; + else if (!xmlStrcmp (child->name, BAD_CAST infoname)) + infos[0] = child; + else if (!xmlStrcmp (child->name, BAD_CAST "blockinfo")) + infos[1] = child; + else if (!xmlStrcmp (child->name, BAD_CAST "objectinfo")) + infos[2] = child; + } + + if (title_tmp) { + title = title_tmp; + goto done; + } + + for (i = 0; i < 3; i++) { + child = infos[i]; + if (child) { + for (title = child->children; title; title = title->next) { + if (!xmlStrcmp (title->name, BAD_CAST "titleabbrev")) + goto done; + else if (!xmlStrcmp (title->name, BAD_CAST "title")) + title_tmp = title; + } + if (title_tmp) { + title = title_tmp; + goto done; + } + } + } + } + + done: + g_free (infoname); + + if (title) + return (gchar *) xmlNodeGetContent (title); + else + return g_strdup (_("Unknown")); +} diff --git a/src/yelp-docbook.h b/src/yelp-docbook.h new file mode 100644 index 00000000..9690a718 --- /dev/null +++ b/src/yelp-docbook.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2003-2007 Shaun McCance <shaunm@gnome.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Shaun McCance <shaunm@gnome.org> + */ + +#ifndef __YELP_DOCBOOK_H__ +#define __YELP_DOCBOOK_H__ + +#include <glib-object.h> + +#include "yelp-document.h" + +#define YELP_TYPE_DOCBOOK (yelp_docbook_get_type ()) +#define YELP_DOCBOOK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), YELP_TYPE_DOCBOOK, YelpDocbook)) +#define YELP_DOCBOOK_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), YELP_TYPE_DOCBOOK, YelpDocbookClass)) +#define YELP_IS_DOCBOOK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), YELP_TYPE_DOCBOOK)) +#define YELP_IS_DOCBOOK_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), YELP_TYPE_DOCBOOK)) +#define YELP_DOCBOOK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), YELP_TYPE_DOCBOOK, YelpDocbookClass)) + +typedef struct _YelpDocbook YelpDocbook; +typedef struct _YelpDocbookClass YelpDocbookClass; +typedef struct _YelpDocbookPriv YelpDocbookPriv; + +struct _YelpDocbook { + YelpDocument parent; + YelpDocbookPriv *priv; +}; + +struct _YelpDocbookClass { + YelpDocumentClass parent_class; +}; + +GType yelp_docbook_get_type (void); +YelpDocument * yelp_docbook_new (gchar *uri); + +#endif /* __YELP_DOCBOOK_H__ */ diff --git a/src/yelp-document.c b/src/yelp-document.c new file mode 100644 index 00000000..0679d568 --- /dev/null +++ b/src/yelp-document.c @@ -0,0 +1,807 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2003-2007 Shaun McCance <shaunm@gnome.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Shaun McCance <shaunm@gnome.org> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib.h> +#include <glib/gi18n.h> + +#include "yelp-document.h" +#include "yelp-debug.h" + +#define YELP_DOCUMENT_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_DOCUMENT, YelpDocumentPriv)) + +typedef struct _Request Request; +struct _Request { + YelpDocument *document; + YelpDocumentFunc func; + gpointer user_data; + + YelpError *error; + + gint req_id; + gchar *page_id; + + gint idle_funcs; + gboolean cancel; +}; + +struct _YelpDocumentPriv { + GMutex *mutex; + + gchar *root_id; + + GHashTable *reqs_by_req_id; /* Indexed by the request ID */ + GHashTable *reqs_by_page_id; /* Indexed by page ID, contains GSList */ + GSList *reqs_pending; /* List of requests that need a page */ + + GtkTreeModel *sections; /* Sections of the document, for display */ + /* Real page IDs map to themselves, so this list doubles + * as a list of all valid page IDs. + */ + GHashTable *page_ids; /* Mapping of fragment IDs to real page IDs */ + GHashTable *titles; /* Mapping of page IDs to titles */ + GHashTable *contents; /* Mapping of page IDs to string content */ + GHashTable *pages; /* Mapping of page IDs to open YelpPages */ + + GHashTable *prev_ids; /* Mapping of page IDs to "previous page" IDs */ + GHashTable *next_ids; /* Mapping of page IDs to "next page" IDs */ + GHashTable *up_ids; /* Mapping of page IDs to "up page" IDs */ +}; + +static void document_class_init (YelpDocumentClass *klass); +static void document_init (YelpDocument *document); +static void document_dispose (GObject *object); +static gpointer document_get_sections (YelpDocument *document); + +static gboolean request_idle_title (Request *request); +static gboolean request_idle_page (Request *request); +static gboolean request_idle_error (Request *request); +static void request_try_free (Request *request); +static void request_free (Request *request); + +static void hash_slist_insert (GHashTable *hash, + const gchar *key, + gpointer value); +static void hash_slist_remove (GHashTable *hash, + const gchar *key, + gpointer value); + +GStaticMutex str_mutex = G_STATIC_MUTEX_INIT; +GHashTable *str_refs = NULL; +static gchar * str_ref (gchar *str); +static void str_unref (gchar *str); + +static GObjectClass *parent_class; + +GType +yelp_document_get_type (void) +{ + static GType type = 0; + if (!type) { + static const GTypeInfo info = { + sizeof (YelpDocumentClass), + NULL, NULL, + (GClassInitFunc) document_class_init, + NULL, NULL, + sizeof (YelpDocument), + 0, + (GInstanceInitFunc) document_init, + }; + type = g_type_register_static (G_TYPE_OBJECT, + "YelpDocument", + &info, 0); + } + return type; +} + +static void +document_class_init (YelpDocumentClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->dispose = document_dispose; + + klass->get_sections = document_get_sections; + + g_type_class_add_private (klass, sizeof (YelpDocumentPriv)); +} + +static void +document_init (YelpDocument *document) +{ + YelpDocumentPriv *priv; + + document->priv = priv = YELP_DOCUMENT_GET_PRIVATE (document); + + priv->mutex = g_mutex_new (); + + priv->reqs_by_req_id = + g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, + (GDestroyNotify) request_try_free); + priv->reqs_by_page_id = + g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, + (GDestroyNotify) g_slist_free); + priv->reqs_pending = NULL; + + priv->sections = NULL; + priv->page_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + priv->titles = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + priv->contents = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, + (GDestroyNotify) str_unref); + priv->pages = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, + (GDestroyNotify) g_slist_free); + priv->prev_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + priv->next_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + priv->up_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); +} + +static gpointer +document_get_sections (YelpDocument *document) +{ + return NULL; +} + + +static void +document_dispose (GObject *object) +{ + YelpDocument *document = YELP_DOCUMENT (object); + + g_free (document->priv->root_id); + + g_slist_free (document->priv->reqs_pending); + g_hash_table_destroy (document->priv->reqs_by_page_id); + g_hash_table_destroy (document->priv->reqs_by_req_id); + + g_hash_table_destroy (document->priv->page_ids); + g_hash_table_destroy (document->priv->titles); + + g_hash_table_destroy (document->priv->contents); + g_hash_table_destroy (document->priv->pages); + + g_hash_table_destroy (document->priv->prev_ids); + g_hash_table_destroy (document->priv->next_ids); + g_hash_table_destroy (document->priv->up_ids); + + g_mutex_free (document->priv->mutex); + + parent_class->dispose (object); +} + +/******************************************************************************/ + +gint +yelp_document_get_page (YelpDocument *document, + gchar *page_id, + YelpDocumentFunc func, + gpointer user_data) +{ + YelpDocumentPriv *priv; + gchar *real_id; + gboolean handled = FALSE; + Request *request; + gint req_id; + static gint request_id = 0; + + g_assert (document != NULL && YELP_IS_DOCUMENT (document)); + + debug_print (DB_FUNCTION, "entering\n"); + debug_print (DB_ARG, " page_id = \"%s\"\n", page_id); + + priv = document->priv; + + request = g_slice_new0 (Request); + request->document = document; + request->func = func; + request->user_data = user_data; + request->req_id = req_id = ++request_id; + + real_id = g_hash_table_lookup (priv->page_ids, page_id); + if (real_id) + request->page_id = g_strdup (real_id); + else + request->page_id = g_strdup (page_id); + + g_mutex_lock (priv->mutex); + + g_hash_table_insert (priv->reqs_by_req_id, + GINT_TO_POINTER (req_id), + request); + hash_slist_insert (priv->reqs_by_page_id, + request->page_id, + request); + priv->reqs_pending = g_slist_prepend (priv->reqs_pending, request); + + if (g_hash_table_lookup (priv->titles, request->page_id)) { + request->idle_funcs++; + g_idle_add ((GSourceFunc) request_idle_title, request); + } + + if (g_hash_table_lookup (priv->contents, request->page_id)) { + request->idle_funcs++; + g_idle_add ((GSourceFunc) request_idle_page, request); + handled = TRUE; + } + + g_mutex_unlock (priv->mutex); + + YELP_DOCUMENT_GET_CLASS (document)->request (document, + req_id, + handled, + request->page_id, + func, + user_data); + + return req_id; +} + +void +yelp_document_cancel_page (YelpDocument *document, gint req_id) +{ + YelpDocumentPriv *priv; + Request *request; + + g_assert (document != NULL && YELP_IS_DOCUMENT (document)); + + debug_print (DB_FUNCTION, "entering\n"); + debug_print (DB_ARG, " req_id = %i\n", req_id); + + priv = document->priv; + + g_mutex_lock (priv->mutex); + request = g_hash_table_lookup (priv->reqs_by_req_id, + GINT_TO_POINTER (req_id)); + if (request) { + priv->reqs_pending = g_slist_remove (priv->reqs_pending, + (gconstpointer) request); + hash_slist_remove (priv->reqs_by_page_id, + request->page_id, + request); + } else { + g_warning ("YelpDocument: Attempted to remove request %i," + " but no such request exists.", + req_id); + } + g_mutex_unlock (priv->mutex); + + if (YELP_DOCUMENT_GET_CLASS (document)->cancel) + YELP_DOCUMENT_GET_CLASS (document)->cancel (document, req_id); +} + +/******************************************************************************/ + +void +yelp_document_release_page (YelpDocument *document, YelpPage *page) +{ + YelpDocumentPriv *priv; + + g_return_if_fail (YELP_IS_DOCUMENT (document)); + + debug_print (DB_FUNCTION, "entering\n"); + + priv = document->priv; + + g_mutex_lock (priv->mutex); + + hash_slist_remove (priv->pages, page->id, page); + if (page->content) + str_unref (page->content); + + g_mutex_unlock (priv->mutex); +} + +/******************************************************************************/ + +void +yelp_document_set_root_id (YelpDocument *document, gchar *root_id) +{ + g_assert (document != NULL && YELP_IS_DOCUMENT (document)); + + if (document->priv->root_id) + g_free (document->priv->root_id); + + document->priv->root_id = g_strdup (root_id); +} + +void +yelp_document_add_page_id (YelpDocument *document, gchar *id, gchar *page_id) +{ + GSList *reqs, *cur; + Request *request; + gchar *title, *contents; + YelpDocumentPriv *priv; + + g_assert (document != NULL && YELP_IS_DOCUMENT (document)); + + priv = document->priv; + + g_mutex_lock (priv->mutex); + if (g_hash_table_lookup (priv->page_ids, id)) { + g_warning ("YelpDocument: Attempted to add an ID mapping from" + " %s to %s, but %s is already mapped.", + id, page_id, id); + g_mutex_unlock (priv->mutex); + return; + } + + g_hash_table_insert (priv->page_ids, g_strdup (id), g_strdup (page_id)); + + if (!g_str_equal (id, page_id)) { + title = g_hash_table_lookup (priv->titles, page_id); + contents = g_hash_table_lookup (priv->contents, page_id); + reqs = g_hash_table_lookup (priv->reqs_by_page_id, id); + for (cur = reqs; cur != NULL; cur = cur->next) { + if (cur->data) { + request = (Request *) cur->data; + g_free (request->page_id); + request->page_id = g_strdup (page_id); + hash_slist_insert (priv->reqs_by_page_id, page_id, request); + if (title) { + request->idle_funcs++; + g_idle_add ((GSourceFunc) request_idle_title, request); + } + if (contents) { + request->idle_funcs++; + g_idle_add ((GSourceFunc) request_idle_page, request); + } + } + } + if (reqs) + g_hash_table_remove (priv->reqs_by_page_id, id); + } + + g_mutex_unlock (priv->mutex); +} + +void +yelp_document_add_prev_id (YelpDocument *document, gchar *page_id, gchar *prev_id) +{ + g_assert (document != NULL && YELP_IS_DOCUMENT (document)); + + g_mutex_lock (document->priv->mutex); + g_hash_table_replace (document->priv->prev_ids, g_strdup (page_id), g_strdup (prev_id)); + g_mutex_unlock (document->priv->mutex); +} + +void +yelp_document_add_next_id (YelpDocument *document, gchar *page_id, gchar *next_id) +{ + g_assert (document != NULL && YELP_IS_DOCUMENT (document)); + + g_mutex_lock (document->priv->mutex); + g_hash_table_replace (document->priv->next_ids, g_strdup (page_id), g_strdup (next_id)); + g_mutex_unlock (document->priv->mutex); +} + +void +yelp_document_add_up_id (YelpDocument *document, gchar *page_id, gchar *up_id) +{ + g_assert (document != NULL && YELP_IS_DOCUMENT (document)); + + g_mutex_lock (document->priv->mutex); + g_hash_table_replace (document->priv->up_ids, g_strdup (page_id), g_strdup (up_id)); + g_mutex_unlock (document->priv->mutex); +} + +void +yelp_document_add_title (YelpDocument *document, gchar *page_id, gchar *title) +{ + GSList *reqs, *cur; + Request *request; + YelpDocumentPriv *priv; + + g_assert (document != NULL && YELP_IS_DOCUMENT (document)); + + debug_print (DB_FUNCTION, "entering\n"); + priv = document->priv; + + g_mutex_lock (priv->mutex); + + g_hash_table_replace (priv->titles, g_strdup (page_id), g_strdup (title)); + + reqs = g_hash_table_lookup (priv->reqs_by_page_id, page_id); + for (cur = reqs; cur != NULL; cur = cur->next) { + if (cur->data) { + request = (Request *) cur->data; + request->idle_funcs++; + g_idle_add ((GSourceFunc) request_idle_title, request); + } + } + + g_mutex_unlock (priv->mutex); +} + +void +yelp_document_add_page (YelpDocument *document, gchar *page_id, const gchar *contents) +{ + GSList *reqs, *cur; + Request *request; + YelpDocumentPriv *priv; + + g_assert (document != NULL && YELP_IS_DOCUMENT (document)); + + debug_print (DB_FUNCTION, "entering\n"); + debug_print (DB_ARG, " page_id = \"%s\"\n", page_id); + priv = document->priv; + + g_mutex_lock (priv->mutex); + + g_hash_table_replace (priv->contents, + g_strdup (page_id), + str_ref ((gchar *) contents)); + + reqs = g_hash_table_lookup (priv->reqs_by_page_id, page_id); + for (cur = reqs; cur != NULL; cur = cur->next) { + if (cur->data) { + request = (Request *) cur->data; + request->idle_funcs++; + g_idle_add ((GSourceFunc) request_idle_page, request); + } + } + + g_mutex_unlock (priv->mutex); +} + +gboolean +yelp_document_has_page (YelpDocument *document, gchar *page_id) +{ + gchar *content; + g_assert (document != NULL && YELP_IS_DOCUMENT (document)); + content = g_hash_table_lookup (document->priv->contents, page_id); + return !(content == NULL); +} + +GtkTreeModel * +yelp_document_get_sections (YelpDocument *document) +{ + return (YELP_DOCUMENT_GET_CLASS (document)->get_sections(document)); +} + +void +yelp_document_error_request (YelpDocument *document, gint req_id, YelpError *error) +{ + Request *request; + YelpDocumentPriv *priv; + + g_assert (document != NULL && YELP_IS_DOCUMENT (document)); + + debug_print (DB_FUNCTION, "entering\n"); + priv = document->priv; + + g_mutex_lock (priv->mutex); + + request = g_hash_table_lookup (priv->reqs_by_req_id, + GINT_TO_POINTER (req_id)); + if (request) { + request->error = error; + request->idle_funcs++; + g_idle_add ((GSourceFunc) request_idle_error, request); + } else { + yelp_error_free (error); + } + + g_mutex_unlock (priv->mutex); +} + +void +yelp_document_error_pending (YelpDocument *document, YelpError *error) +{ + GSList *cur; + Request *request; + YelpDocumentPriv *priv; + + g_assert (document != NULL && YELP_IS_DOCUMENT (document)); + + debug_print (DB_FUNCTION, "entering\n"); + priv = document->priv; + + g_mutex_lock (priv->mutex); + + if (priv->reqs_pending) { + for (cur = priv->reqs_pending; cur; cur = cur->next) { + request = cur->data; + if (cur->next) + request->error = yelp_error_copy (error); + else + request->error = error; + request->idle_funcs++; + g_idle_add ((GSourceFunc) request_idle_error, request); + } + + g_slist_free (priv->reqs_pending); + priv->reqs_pending = NULL; + } else { + yelp_error_free (error); + } + + g_mutex_unlock (priv->mutex); +} + +/******************************************************************************/ + +static gboolean +request_idle_title (Request *request) +{ + YelpDocument *document; + YelpDocumentPriv *priv; + YelpDocumentFunc func = NULL; + gchar *title; + gint req_id = 0; + gpointer user_data = user_data; + + g_assert (request != NULL && YELP_IS_DOCUMENT (request->document)); + + if (request->cancel) { + request->idle_funcs--; + return FALSE; + } + + debug_print (DB_FUNCTION, "entering\n"); + + document = g_object_ref (request->document); + priv = document->priv; + + g_mutex_lock (priv->mutex); + + title = g_hash_table_lookup (priv->titles, request->page_id); + if (title) { + func = request->func; + req_id = request->req_id; + title = g_strdup (title); + user_data = request->user_data; + } + + request->idle_funcs--; + g_mutex_unlock (priv->mutex); + + if (func) + func (document, + YELP_DOCUMENT_SIGNAL_TITLE, + req_id, + title, + user_data); + + g_object_unref (document); + return FALSE; +} + +static gboolean +request_idle_page (Request *request) +{ + YelpDocument *document; + YelpDocumentPriv *priv; + YelpDocumentFunc func = NULL; + YelpPage *page = NULL; + gchar *contents, *tmp; + gint req_id = 0; + gpointer user_data = user_data; + + g_assert (request != NULL && YELP_IS_DOCUMENT (request->document)); + + if (request->cancel) { + request->idle_funcs--; + return FALSE; + } + + debug_print (DB_FUNCTION, "entering\n"); + + document = g_object_ref (request->document); + priv = document->priv; + + g_mutex_lock (priv->mutex); + + contents = g_hash_table_lookup (priv->contents, request->page_id); + if (contents) { + func = request->func; + req_id = request->req_id; + user_data = request->user_data; + + /* FIXME: there will come a day when we can't just assume XHTML */ + page = yelp_page_new_string (YELP_DOCUMENT (request->document), + request->page_id, + str_ref (contents), + YELP_PAGE_MIME_XHTML); + tmp = g_hash_table_lookup (priv->prev_ids, request->page_id); + if (tmp) + page->prev_id = g_strdup (tmp); + tmp = g_hash_table_lookup (priv->next_ids, request->page_id); + if (tmp) + page->next_id = g_strdup (tmp); + tmp = g_hash_table_lookup (priv->up_ids, request->page_id); + if (tmp) + page->up_id = g_strdup (tmp); + if (priv->root_id) + page->root_id = g_strdup (priv->root_id); + priv->reqs_pending = g_slist_remove (priv->reqs_pending, request); + } + + request->idle_funcs--; + g_mutex_unlock (priv->mutex); + + if (func) { + func (document, + YELP_DOCUMENT_SIGNAL_PAGE, + req_id, + page, + user_data); + } + + g_object_unref (document); + + return FALSE; +} + +static gboolean +request_idle_error (Request *request) +{ + YelpDocument *document; + YelpDocumentPriv *priv; + YelpDocumentFunc func = NULL; + YelpError *error = NULL; + gint req_id = 0; + gpointer user_data = user_data; + + g_assert (request != NULL && YELP_IS_DOCUMENT (request->document)); + + if (request->cancel) { + request->idle_funcs--; + return FALSE; + } + + debug_print (DB_FUNCTION, "entering\n"); + + document = g_object_ref (request->document); + priv = document->priv; + + g_mutex_lock (priv->mutex); + + if (request->error) { + func = request->func; + req_id = request->req_id; + user_data = request->user_data; + error = request->error; + request->error = NULL; + + priv->reqs_pending = g_slist_remove (priv->reqs_pending, request); + } + + request->idle_funcs--; + g_mutex_unlock (priv->mutex); + + if (func) + func (document, + YELP_DOCUMENT_SIGNAL_ERROR, + req_id, + error, + user_data); + + g_object_unref (document); + return FALSE; +} + +static void +request_try_free (Request *request) { + debug_print (DB_FUNCTION, "entering\n"); + request->cancel = TRUE; + + if (request->idle_funcs == 0) + request_free (request); + else + g_idle_add ((GSourceFunc) request_try_free, request); +} + +static void +request_free (Request *request) +{ + debug_print (DB_FUNCTION, "entering\n"); + g_free (request->page_id); + g_free (request); +} + +/******************************************************************************/ + +static void +hash_slist_insert (GHashTable *hash, + const gchar *key, + gpointer value) +{ + GSList *list; + list = g_hash_table_lookup (hash, key); + if (list) { + list->next = g_slist_prepend (list->next, value); + } else { + list = g_slist_prepend (NULL, value); + list = g_slist_prepend (list, NULL); + g_hash_table_insert (hash, g_strdup (key), list); + } +} + +static void +hash_slist_remove (GHashTable *hash, + const gchar *key, + gpointer value) +{ + GSList *list; + list = g_hash_table_lookup (hash, key); + if (list) { + list = g_slist_remove (list, value); + if (list->next == NULL) + g_hash_table_remove (hash, key); + } +} + +static gchar * +str_ref (gchar *str) +{ + gpointer p; + guint i; + + g_static_mutex_lock (&str_mutex); + + if (str_refs == NULL) + str_refs = g_hash_table_new (g_direct_hash, g_direct_equal); + + p = g_hash_table_lookup (str_refs, str); + + i = GPOINTER_TO_UINT (p); + i++; + p = GUINT_TO_POINTER (i); + + g_hash_table_insert (str_refs, str, p); + + g_static_mutex_unlock (&str_mutex); + + return str; +} + +static void +str_unref (gchar *str) +{ + gpointer p; + guint i; + + g_static_mutex_lock (&str_mutex); + + p = g_hash_table_lookup (str_refs, str); + + i = GPOINTER_TO_UINT (p); + i--; + p = GUINT_TO_POINTER (i); + + if (i > 0) + g_hash_table_insert (str_refs, str, p); + else { + g_hash_table_remove (str_refs, str); + g_free (str); + } + + g_static_mutex_unlock (&str_mutex); +} diff --git a/src/yelp-document.h b/src/yelp-document.h new file mode 100644 index 00000000..e8a1ffca --- /dev/null +++ b/src/yelp-document.h @@ -0,0 +1,132 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2003-2007 Shaun McCance <shaunm@gnome.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Shaun McCance <shaunm@gnome.org> + */ + +#ifndef __YELP_DOCUMENT_H__ +#define __YELP_DOCUMENT_H__ + +#include <glib-object.h> +#include <gtk/gtk.h> + +#define YELP_TYPE_DOCUMENT (yelp_document_get_type ()) +#define YELP_DOCUMENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), YELP_TYPE_DOCUMENT, YelpDocument)) +#define YELP_DOCUMENT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), YELP_TYPE_DOCUMENT, YelpDocumentClass)) +#define YELP_IS_DOCUMENT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), YELP_TYPE_DOCUMENT)) +#define YELP_IS_DOCUMENT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), YELP_TYPE_DOCUMENT)) +#define YELP_DOCUMENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), YELP_TYPE_DOCUMENT, YelpDocumentClass)) + +typedef struct _YelpDocument YelpDocument; +typedef struct _YelpDocumentClass YelpDocumentClass; +typedef struct _YelpDocumentPriv YelpDocumentPriv; + +/* This needs to be after the typedefs. */ +#include "yelp-page.h" + +typedef enum { + YELP_DOCUMENT_SIGNAL_PAGE, + YELP_DOCUMENT_SIGNAL_TITLE, + YELP_DOCUMENT_SIGNAL_ERROR +} YelpDocumentSignal; + +enum { + YELP_DOCUMENT_COLUMN_ID = 0, + YELP_DOCUMENT_COLUMN_TITLE, + YELP_DOCUMENT_NUM_COLUMNS +}; + +typedef void (*YelpDocumentFunc) (YelpDocument *document, + YelpDocumentSignal signal, + gint req_id, + gpointer func_data, + gpointer user_data); + +struct _YelpDocument { + GObject parent; + YelpDocumentPriv *priv; +}; + +struct _YelpDocumentClass { + GObjectClass parent_class; + + /* Virtual Functions */ + void (*request) (YelpDocument *document, + gint req_id, + gboolean handled, + gchar *page_id, + YelpDocumentFunc func, + gpointer user_data); + void (*cancel) (YelpDocument *document, + gint req_id); + + gint (*get_page) (YelpDocument *document, + gchar *page_id, + gpointer user_data); + void (*release_page) (YelpDocument *document, + YelpPage *page); + gpointer (*get_sections) (YelpDocument *document); +}; + + +GType yelp_document_get_type (void); + +gint yelp_document_get_page (YelpDocument *document, + gchar *page_id, + YelpDocumentFunc func, + gpointer user_data); +void yelp_document_cancel_page (YelpDocument *document, + gint req_id); + +/* Only called by yelp_page_free */ +void yelp_document_release_page (YelpDocument *document, + YelpPage *page); + +/* Only called by subclasses */ +void yelp_document_set_root_id (YelpDocument *document, + gchar *root_id); +void yelp_document_add_page_id (YelpDocument *document, + gchar *id, + gchar *page_id); +void yelp_document_add_prev_id (YelpDocument *document, + gchar *page_id, + gchar *prev_id); +void yelp_document_add_next_id (YelpDocument *document, + gchar *page_id, + gchar *next_id); +void yelp_document_add_up_id (YelpDocument *document, + gchar *page_id, + gchar *up_id); +void yelp_document_add_title (YelpDocument *document, + gchar *page_id, + gchar *title); +void yelp_document_add_page (YelpDocument *document, + gchar *page_id, + const gchar *contents); +gboolean yelp_document_has_page (YelpDocument *document, + gchar *page_id); +void yelp_document_error_request (YelpDocument *document, + gint req_id, + YelpError *error); +void yelp_document_error_pending (YelpDocument *document, + YelpError *error); +GtkTreeModel *yelp_document_get_sections (YelpDocument *document); + + +#endif /* __YELP_DOCUMENT_H__ */ diff --git a/src/yelp-error.c b/src/yelp-error.c index b32bd5e5..fcb21425 100644 --- a/src/yelp-error.c +++ b/src/yelp-error.c @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * Copyright (C) 2002 Mikael Hallendal <micke@imendio.com> + * Copyright (C) 2007 Shaun McCance <shaunm@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -20,12 +21,101 @@ #include <config.h> +#include <glib.h> #include <glib/gi18n.h> #include "yelp-error.h" +struct _YelpError { + gchar *title; + gchar *message; +}; + +static YelpError * +yelp_error_new_valist (gchar *title, gchar *format, va_list args) +{ + YelpError *error; + + error = g_slice_new (YelpError); + error->title = g_strdup (title); + error->message = g_strdup_vprintf (format, args); + + return error; +} + +YelpError * +yelp_error_new (gchar *title, gchar *format, ...) +{ + YelpError *error; + va_list args; + + va_start (args, format); + error = yelp_error_new_valist (title, format, args); + va_end (args); + + return error; +} + +void +yelp_error_set (YelpError **error, gchar *title, gchar *format, ...) +{ + YelpError *new; + va_list args; + + va_start (args, format); + new = yelp_error_new_valist (title, format, args); + va_end (args); + + if (*error == NULL) + *error = new; + else + g_warning + ("YelpError set over the top of a previous YelpError or uninitialized\n" + "memory. This indicates a bug in someone's code. You must ensure an\n" + "error is NULL before it's set. The overwriting error message was:\n" + "%s", + new->message); +} + +YelpError * +yelp_error_copy (YelpError *error) +{ + YelpError *new; + + new = g_slice_new (YelpError); + new->title = g_strdup (error->title); + new->message = g_strdup (error->message); + + return error; +} + +const gchar * +yelp_error_get_title (YelpError *error) +{ + g_return_val_if_fail (error != NULL, NULL); + return error->title; +} + +const gchar * +yelp_error_get_message (YelpError *error) +{ + g_return_val_if_fail (error != NULL, NULL); + return error->message; +} + +void +yelp_error_free (YelpError *error) +{ + g_return_if_fail (error != NULL); + g_free (error->title); + g_free (error->message); + g_slice_free (YelpError, error); +} + +/******************************************************************************/ + GQuark -yelp_error_quark (void) +yelp_gerror_quark (void) { static GQuark q = 0; @@ -36,34 +126,24 @@ yelp_error_quark (void) } const gchar * -yelp_error_get_primary (GError *error) +yelp_gerror_get_title (GError *error) { - if (!error || error->domain != YELP_ERROR) - return _("An unknown error occured"); + if (!error || error->domain != YELP_GERROR) + return _("Unknown Error"); switch (error->code) { - case YELP_ERROR_NO_DOC: - return _("Could not load document"); - case YELP_ERROR_NO_PAGE: - return _("Could not load section"); - case YELP_ERROR_NO_TOC: - return _("Could not read the table of contents"); - case YELP_ERROR_FORMAT: - return _("Unsupported Format"); - case YELP_ERROR_IO: - return _("Could not read document"); - case YELP_ERROR_PROC: - return _("Could not process document"); - default: - return _("An unknown error occured"); + case YELP_GERROR_IO: + return _("Could Not Read File"); } + + return _("Unknown Error"); } const gchar * -yelp_error_get_secondary (GError *error) +yelp_gerror_get_message (GError *error) { if (!error || !error->message) - return _("No information is available about the error."); + return _("No information is available about this error."); else return error->message; } diff --git a/src/yelp-error.h b/src/yelp-error.h index 02a1dc46..91a5bda2 100644 --- a/src/yelp-error.h +++ b/src/yelp-error.h @@ -1,6 +1,7 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * Copyright (C) 2002 Mikael Hallendal <micke@imendio.com> + * Copyright (C) 2007 Shaun McCance <shaunm@gnome.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,20 +24,31 @@ #include <glib.h> -#define YELP_ERROR yelp_error_quark () +typedef struct _YelpError YelpError; -typedef enum { - YELP_ERROR_NO_DOC, /* Selected document not found */ - YELP_ERROR_NO_PAGE, /* Selected page not found */ - YELP_ERROR_NO_TOC, /* Could not read the TOC */ - YELP_ERROR_FORMAT, /* Format is not supported */ - YELP_ERROR_IO, /* Error in IO */ - YELP_ERROR_PROC /* Error processing the document */ -} YelpError; +YelpError * yelp_error_new (gchar *title, + gchar *format, + ...); +void yelp_error_set (YelpError **error, + gchar *title, + gchar *format, + ...); +YelpError * yelp_error_copy (YelpError *error); -GQuark yelp_error_quark (void) G_GNUC_CONST; +const gchar * yelp_error_get_title (YelpError *error); +const gchar * yelp_error_get_message (YelpError *error); -const gchar * yelp_error_get_primary (GError *error); -const gchar * yelp_error_get_secondary (GError *error); +void yelp_error_free (YelpError *error); + + +#define YELP_GERROR yelp_gerror_quark () + +enum { + YELP_GERROR_IO +}; + +GQuark yelp_gerror_quark (void) G_GNUC_CONST; +const gchar * yelp_gerror_get_title (GError *error); +const gchar * yelp_gerror_get_message (GError *error); #endif /* __YELP_ERROR_H__ */ diff --git a/src/yelp-info-pager.c b/src/yelp-info-pager.c index 98843615..2c674c22 100644 --- a/src/yelp-info-pager.c +++ b/src/yelp-info-pager.c @@ -171,7 +171,7 @@ info_pager_parse (YelpPager *pager) GError *error = NULL; YelpInfoPagerPriv *priv; - g_return_val_if_fail (YELP_IS_INFO_PAGER (pager), NULL); + g_return_val_if_fail (YELP_IS_INFO_PAGER (pager), FALSE); priv = YELP_INFO_PAGER (pager)->priv; doc_info = yelp_pager_get_doc_info (pager); diff --git a/src/yelp-info-parser.c b/src/yelp-info-parser.c index c6eb89a0..1dafea0d 100644 --- a/src/yelp-info-parser.c +++ b/src/yelp-info-parser.c @@ -93,8 +93,8 @@ page_type (char *page) return PAGE_OTHER; } -static char * -open_info_file (char *file) +static char +*open_info_file (char *file) { GIOChannel *channel = NULL; int i; @@ -157,8 +157,8 @@ find_info_part (gchar *part_name, gchar *base) } -static char * -process_indirect_map (char *page, gchar * file) +static char +*process_indirect_map (char *page, gchar * file) { char **lines; char **ptr; @@ -253,8 +253,8 @@ static GHashTable return table; } -static char * -get_value_after (char *source, char *required) +static char +*get_value_after (char *source, char *required) { char *ret, *ret_cp; char *source_cp; @@ -486,11 +486,14 @@ process_page (GtkTreeStore *tree, GHashTable *nodes2offsets, } d (if (iter) debug_print (DB_DEBUG, "Have a valid iter, storing for %s\n", node)); + g_hash_table_insert (nodes2iters, g_strdup (node), iter); debug_print (DB_DEBUG, "size: %i\n", g_hash_table_size (nodes2iters)); - tmp = g_strdup_printf ("%i", - node2page (nodes2offsets, offsets2pages, node)); + /*tmp = g_strdup_printf ("%i", + node2page (nodes2offsets, offsets2pages, node));*/ + tmp = g_strdup (node); + tmp = g_strdelimit (tmp, " ", '_'); gtk_tree_store_set (tree, iter, COLUMN_PAGE_NO, tmp, COLUMN_PAGE_NAME, node, @@ -785,7 +788,9 @@ resolve_frag_id (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, -1); if (g_str_equal (page_name, *xref)) { g_free (*xref); - *xref = g_strdup (page_no); + *xref = g_strdup (page_name); + *xref = g_strdelimit (*xref, " ", '_'); + g_free (page_name); g_free (page_no); return TRUE; diff --git a/src/yelp-info.c b/src/yelp-info.c new file mode 100644 index 00000000..2783ebc6 --- /dev/null +++ b/src/yelp-info.c @@ -0,0 +1,433 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2007 Don Scorgie <dscorgie@svn.gnome.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Don Scorgie <dscorgie@svn.gnome.org> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <libxml/tree.h> + +#include "yelp-error.h" +#include "yelp-info.h" +#include "yelp-info-parser.h" +#include "yelp-transform.h" +#include "yelp-debug.h" + +#define STYLESHEET DATADIR"/yelp/xslt/info2html.xsl" + +#define YELP_INFO_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_INFO, YelpInfoPriv)) + +typedef enum { + INFO_STATE_BLANK, /* Brand new, run transform as needed */ + INFO_STATE_PARSING, /* Parsing/transforming document, please wait */ + INFO_STATE_PARSED, /* All done, if we ain't got it, it ain't here */ + INFO_STATE_STOP /* Stop everything now, object to be disposed */ +} InfoState; + +struct _YelpInfoPriv { + gchar *filename; + InfoState state; + + GMutex *mutex; + GThread *thread; + + xmlDocPtr xmldoc; + GtkTreeModel *sections; + + gboolean process_running; + gboolean transform_running; + + YelpTransform *transform; +}; + + +static void info_class_init (YelpInfoClass *klass); +static void info_init (YelpInfo *info); +static void info_try_dispose (GObject *object); +static void info_dispose (GObject *object); + +/* YelpDocument */ +static void info_request (YelpDocument *document, + gint req_id, + gboolean handled, + gchar *page_id, + YelpDocumentFunc func, + gpointer user_data); + +/* YelpTransform */ +static void transform_func (YelpTransform *transform, + YelpTransformSignal signal, + gpointer func_data, + YelpInfo *info); +static void transform_page_func (YelpTransform *transform, + gchar *page_id, + YelpInfo *info); +static void transform_final_func (YelpTransform *transform, + YelpInfo *info); +static gpointer info_get_sections (YelpDocument *document); + +/* Threaded */ +static void info_process (YelpInfo *info); + +static YelpDocumentClass *parent_class; + +GType +yelp_info_get_type (void) +{ + static GType type = 0; + if (!type) { + static const GTypeInfo info = { + sizeof (YelpInfoClass), + NULL, NULL, + (GClassInitFunc) info_class_init, + NULL, NULL, + sizeof (YelpInfo), + 0, + (GInstanceInitFunc) info_init, + }; + type = g_type_register_static (YELP_TYPE_DOCUMENT, + "YelpInfo", + &info, 0); + } + return type; +} + +static void +info_class_init (YelpInfoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + YelpDocumentClass *document_class = YELP_DOCUMENT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->dispose = info_try_dispose; + + document_class->request = info_request; + document_class->cancel = NULL; + document_class->get_sections = info_get_sections; + + g_type_class_add_private (klass, sizeof (YelpInfoPriv)); +} + +static void +info_init (YelpInfo *info) +{ + YelpInfoPriv *priv; + + priv = info->priv = YELP_INFO_GET_PRIVATE (info); + + priv->state = INFO_STATE_BLANK; + + priv->xmldoc = NULL; + + priv->mutex = g_mutex_new (); +} + +static void +info_try_dispose (GObject *object) +{ + YelpInfoPriv *priv; + + g_assert (object != NULL && YELP_IS_INFO (object)); + priv = YELP_INFO (object)->priv; + + g_mutex_lock (priv->mutex); + if (priv->process_running || priv->transform_running) { + priv->state = INFO_STATE_STOP; + g_idle_add ((GSourceFunc) info_try_dispose, object); + g_mutex_unlock (priv->mutex); + } else { + g_mutex_unlock (priv->mutex); + info_dispose (object); + } +} + +static void +info_dispose (GObject *object) +{ + YelpInfo *info = YELP_INFO (object); + + g_free (info->priv->filename); + + if (info->priv->xmldoc) + xmlFreeDoc (info->priv->xmldoc); + + g_mutex_free (info->priv->mutex); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +/******************************************************************************/ + +YelpDocument * +yelp_info_new (gchar *filename) +{ + YelpInfo *info; + + g_return_val_if_fail (filename != NULL, NULL); + + info = (YelpInfo *) g_object_new (YELP_TYPE_INFO, NULL); + info->priv->filename = g_strdup (filename); + + debug_print (DB_FUNCTION, "entering\n"); + debug_print (DB_ARG, " filename = \"%s\"\n", filename); + + yelp_document_add_page_id (YELP_DOCUMENT (info), "x-yelp-index", "index"); + + return (YelpDocument *) info; +} + + +/******************************************************************************/ +/** YelpDocument **************************************************************/ + +static void +info_request (YelpDocument *document, + gint req_id, + gboolean handled, + gchar *page_id, + YelpDocumentFunc func, + gpointer user_data) +{ + YelpInfo *info; + YelpInfoPriv *priv; + YelpError *error; + + debug_print (DB_FUNCTION, "entering\n"); + debug_print (DB_ARG, " req_id = %i\n", req_id); + debug_print (DB_ARG, " page_id = \"%s\"\n", page_id); + + g_assert (document != NULL && YELP_IS_INFO (document)); + + if (handled) + return; + + info = YELP_INFO (document); + priv = info->priv; + + g_mutex_lock (priv->mutex); + + switch (priv->state) { + case INFO_STATE_BLANK: + priv->state = INFO_STATE_PARSING; + priv->process_running = TRUE; + priv->thread = g_thread_create ((GThreadFunc) info_process, info, FALSE, NULL); + break; + case INFO_STATE_PARSING: + break; + case INFO_STATE_PARSED: + case INFO_STATE_STOP: + error = yelp_error_new (_("Page not found"), + _("The page %s was not found in the document %s."), + page_id, priv->filename); + yelp_document_error_request (document, req_id, error); + break; + } + + g_mutex_unlock (priv->mutex); +} + + +/******************************************************************************/ +/** YelpTransform *************************************************************/ + +static void +transform_func (YelpTransform *transform, + YelpTransformSignal signal, + gpointer func_data, + YelpInfo *info) +{ + YelpInfoPriv *priv; + + debug_print (DB_FUNCTION, "entering\n"); + + g_assert (info != NULL && YELP_IS_INFO (info)); + + priv = info->priv; + + g_assert (transform == priv->transform); + + if (priv->state == INFO_STATE_STOP) { + switch (signal) { + case YELP_TRANSFORM_CHUNK: + g_free (func_data); + break; + case YELP_TRANSFORM_ERROR: + yelp_error_free ((YelpError *) func_data); + break; + case YELP_TRANSFORM_FINAL: + break; + } + yelp_transform_release (transform); + priv->transform = NULL; + priv->transform_running = FALSE; + return; + } + + switch (signal) { + case YELP_TRANSFORM_CHUNK: + transform_page_func (transform, (gchar *) func_data, info); + break; + case YELP_TRANSFORM_ERROR: + yelp_document_error_pending (YELP_DOCUMENT (info), (YelpError *) func_data); + yelp_transform_release (transform); + priv->transform = NULL; + priv->transform_running = FALSE; + break; + case YELP_TRANSFORM_FINAL: + transform_final_func (transform, info); + break; + } +} + +static void +transform_page_func (YelpTransform *transform, + gchar *page_id, + YelpInfo *info) +{ + YelpInfoPriv *priv; + gchar *content; + + debug_print (DB_FUNCTION, "entering\n"); + + priv = info->priv; + g_mutex_lock (priv->mutex); + + content = yelp_transform_eat_chunk (transform, page_id); + + yelp_document_add_page (YELP_DOCUMENT (info), page_id, content); + + g_free (page_id); + + g_mutex_unlock (priv->mutex); +} + +static void +transform_final_func (YelpTransform *transform, YelpInfo *info) +{ + YelpError *error; + YelpInfoPriv *priv = info->priv; + + debug_print (DB_FUNCTION, "entering\n"); + + g_mutex_lock (priv->mutex); + + error = yelp_error_new (_("Page not found"), + _("The requested page was not found in the document %s."), + priv->filename); + yelp_document_error_pending (YELP_DOCUMENT (info), error); + + yelp_transform_release (transform); + priv->transform = NULL; + priv->transform_running = FALSE; + + if (priv->xmldoc) + xmlFreeDoc (priv->xmldoc); + priv->xmldoc = NULL; + + g_mutex_unlock (priv->mutex); +} + + +/******************************************************************************/ +/** Threaded ******************************************************************/ + +static void +info_process (YelpInfo *info) +{ + YelpInfoPriv *priv; + YelpError *error = NULL; + YelpDocument *document; + GtkTreeModel *model; + + gint params_i = 0; + gint params_max = 10; + gchar **params = NULL; + + + debug_print (DB_FUNCTION, "entering\n"); + + g_assert (info != NULL && YELP_IS_INFO (info)); + g_object_ref (info); + priv = info->priv; + document = YELP_DOCUMENT (info); + + if (!g_file_test (priv->filename, G_FILE_TEST_IS_REGULAR)) { + error = yelp_error_new (_("File not found"), + _("The file ‘%s’ does not exist."), + priv->filename); + yelp_document_error_pending (document, error); + goto done; + } + + priv->sections = (GtkTreeModel *) yelp_info_parser_parse_file (priv->filename); + if (!model) { + /* TODO: Handle errors - exit out somehow */ + } + + priv->xmldoc = yelp_info_parser_parse_tree ((GtkTreeStore *) priv->sections); + + if (priv->xmldoc == NULL) { + error = yelp_error_new (_("Could not parse file"), + _("The file ‘%s’ could not be parsed because it is" + " not a well-formed info page."), + priv->filename); + yelp_document_error_pending (document, error); + } + + g_mutex_lock (priv->mutex); + if (priv->state == INFO_STATE_STOP) { + g_mutex_unlock (priv->mutex); + goto done; + } + + priv->transform = yelp_transform_new (STYLESHEET, + (YelpTransformFunc) transform_func, + info); + priv->transform_running = TRUE; + + params = g_new0 (gchar *, params_max); + yelp_settings_params (¶ms, ¶ms_i, ¶ms_max); + + params[params_i] = NULL; + + + yelp_transform_start (priv->transform, + priv->xmldoc, + params); + g_mutex_unlock (priv->mutex); + + done: + priv->process_running = FALSE; + g_object_unref (info); +} + +static gpointer +info_get_sections (YelpDocument *document) +{ + YelpInfo *info = (YelpInfo *) document; + + return (gpointer) (info->priv->sections); +} diff --git a/src/yelp-info.h b/src/yelp-info.h new file mode 100644 index 00000000..d592dd72 --- /dev/null +++ b/src/yelp-info.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2007 Don Scorgie <dscorgie@svn.gnome.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Don Scorgie <dscorgie@svn.gnome.org> + */ + +#ifndef __YELP_INFO_H__ +#define __YELP_INFO_H__ + +#include <glib-object.h> + +#include "yelp-document.h" + +#define YELP_TYPE_INFO (yelp_info_get_type ()) +#define YELP_INFO(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), YELP_TYPE_INFO, YelpInfo)) +#define YELP_INFO_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), YELP_TYPE_INFO, YelpInfoClass)) +#define YELP_IS_INFO(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), YELP_TYPE_INFO)) +#define YELP_IS_INFO_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), YELP_TYPE_INFO)) +#define YELP_INFO_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), YELP_TYPE_INFO, YelpInfoClass)) + +typedef struct _YelpInfo YelpInfo; +typedef struct _YelpInfoClass YelpInfoClass; +typedef struct _YelpInfoPriv YelpInfoPriv; + +struct _YelpInfo { + YelpDocument parent; + YelpInfoPriv *priv; +}; + +struct _YelpInfoClass { + YelpDocumentClass parent_class; +}; + +GType yelp_info_get_type (void); +YelpDocument * yelp_info_new (gchar *uri); + +#endif /* __YELP_INFO_H__ */ diff --git a/src/yelp-io-channel.c b/src/yelp-io-channel.c index 85f26d60..16c57509 100644 --- a/src/yelp-io-channel.c +++ b/src/yelp-io-channel.c @@ -103,7 +103,7 @@ yelp_io_channel_new_file (gchar *file, if (!channel) { if (error) { - g_set_error (error, YELP_ERROR, YELP_ERROR_IO, + g_set_error (error, YELP_GERROR, YELP_GERROR_IO, _("The file ‘%s’ could not be read and decoded. " "The file may be compressed in an unsupported " "format."), diff --git a/src/yelp-main.c b/src/yelp-main.c index d4a1dd11..66be4baa 100644 --- a/src/yelp-main.c +++ b/src/yelp-main.c @@ -268,7 +268,7 @@ slowly_and_stupidly_obtain_timestamp (Display *xdisplay) 0, CopyFromParent, CopyFromParent, - (Visual *)CopyFromParent, + CopyFromParent, CWOverrideRedirect | CWEventMask, &attrs); @@ -294,7 +294,7 @@ slowly_and_stupidly_obtain_timestamp (Display *xdisplay) return event.xproperty.time; } -static DBusGProxy * +DBusGProxy * main_dbus_get_proxy (void) { if (!connection) @@ -306,7 +306,7 @@ main_dbus_get_proxy (void) "org.gnome.YelpService"); } -static gboolean +gboolean main_is_running (void) { DBusGProxy *proxy = NULL; diff --git a/src/yelp-man-pager.c b/src/yelp-man-pager.c index 150bd7ae..a7d7ba71 100644 --- a/src/yelp-man-pager.c +++ b/src/yelp-man-pager.c @@ -228,7 +228,7 @@ man_pager_parse (YelpPager *pager) GError *error = NULL; gint i; - g_return_val_if_fail (YELP_IS_MAN_PAGER (pager), NULL); + g_return_val_if_fail (YELP_IS_MAN_PAGER (pager), FALSE); doc_info = yelp_pager_get_doc_info (pager); filename = yelp_doc_info_get_filename (doc_info); diff --git a/src/yelp-man-parser.c b/src/yelp-man-parser.c index 0b7e97cd..edba95c5 100644 --- a/src/yelp-man-parser.c +++ b/src/yelp-man-parser.c @@ -26,15 +26,12 @@ #include <glib.h> #include <glib/gi18n.h> -#include <glib/gprintf.h> #include <libxml/tree.h> #include <string.h> +#include "yelp-debug.h" #include "yelp-io-channel.h" #include "yelp-man-parser.h" -#include "yelp-utils.h" - -#define d(x) #define PARSER_CUR (g_utf8_get_char (parser->cur) != '\0' \ && (parser->cur - parser->buffer < parser->length)) @@ -172,34 +169,6 @@ yelp_man_parser_parse_file (YelpManParser *parser, return parser->doc; } -xmlDocPtr -yelp_man_parser_parse_doc (YelpManParser *parser, - YelpDocInfo *doc_info) -{ - gchar *file; - gchar *encoding = NULL; - xmlDocPtr doc = NULL; - - g_return_val_if_fail (parser != NULL, NULL); - g_return_val_if_fail (doc_info != NULL, NULL); - g_return_val_if_fail (yelp_doc_info_get_type (doc_info) != YELP_DOC_TYPE_MAN, NULL); - - file = yelp_doc_info_get_filename (doc_info); - - if (!file) - return NULL; - - encoding = (gchar *)g_getenv("MAN_ENCODING"); - if (encoding == NULL) - encoding = "ISO-8859-1"; - - doc = yelp_man_parser_parse_file (parser, file, encoding); - - g_free (file); - - return doc; -} - void yelp_man_parser_free (YelpManParser *parser) { @@ -605,7 +574,7 @@ macro_url_handler (YelpManParser *parser, gchar *macro, GSList *args) tmpNode = parser_stack_pop_node (parser, "UR"); if (tmpNode == NULL) - d (g_warning ("Found unexpected tag: '%s'\n", macro)); + debug_print (DB_WARN, "Found unexpected tag: '%s'\n", macro); else parser->ins = tmpNode->parent; } else @@ -702,7 +671,7 @@ macro_mandoc_list_handler (YelpManParser *parser, gchar *macro, GSList *args) tmpNode = parser_stack_pop_node (parser, "Bl"); if (tmpNode == NULL) - d (g_warning ("Found unexpected tag: '%s'\n", macro)); + debug_print (DB_WARN, "Found unexpected tag: '%s'\n", macro); else parser->ins = tmpNode->parent; } @@ -721,7 +690,7 @@ macro_verbatim_handler (YelpManParser *parser, gchar *macro, GSList *args) tmpNode = parser_stack_pop_node (parser, "Verbatim"); if (tmpNode == NULL) - d (g_warning ("Found unexpected tag: '%s'\n", macro)); + debug_print (DB_WARN, "Found unexpected tag: '%s'\n", macro); else parser->ins = tmpNode->parent; } @@ -1266,7 +1235,7 @@ get_argument: } else if (g_str_equal (str, "TE")) { /* We should only see this from within parser_parse_table */ - d (g_warning ("Found unexpected tag: '%s'\n", str)); + debug_print (DB_WARN, "Found unexpected tag: '%s'\n", str); g_free (str); } /* "ie" and "if" are conditional macros in groff @@ -1451,7 +1420,7 @@ parser_append_given_text_handle_escapes (YelpManParser *parser, gchar *text, gbo if (g_str_equal (str, "fI") || g_str_equal (str, "fB")) parser->ins = parser_append_node (parser, str); else if (!g_str_equal (str, "fR") && !g_str_equal (str, "fP")) - d (g_warning ("No rule matching the tag '%s'\n", str)); + debug_print (DB_WARN, "No rule matching the tag '%s'\n", str); g_free (str); anc = ptr; @@ -1798,7 +1767,7 @@ parser_parse_table (YelpManParser *parser) if (*(parser->buffer + 1) == 'T' && *(parser->buffer + 2) == 'E') { if (parser_stack_pop_node (parser, "TABLE") == NULL) - d (g_warning ("Found unexpected tag: 'TE'\n")); + debug_print (DB_WARN, "Found unexpected tag: 'TE'\n"); else { parser->ins = table_start; diff --git a/src/yelp-man-parser.h b/src/yelp-man-parser.h index 26553e0a..26976d2a 100644 --- a/src/yelp-man-parser.h +++ b/src/yelp-man-parser.h @@ -26,16 +26,12 @@ #include <glib.h> #include <libxml/tree.h> -#include "yelp-utils.h" - typedef struct _YelpManParser YelpManParser; YelpManParser * yelp_man_parser_new (void); xmlDocPtr yelp_man_parser_parse_file (YelpManParser *parser, gchar *file, const gchar *encoding); -xmlDocPtr yelp_man_parser_parse_doc (YelpManParser *parser, - YelpDocInfo *doc); void yelp_man_parser_free (YelpManParser *parser); #endif /* __YELP_MAN_PARSER_H__ */ diff --git a/src/yelp-man.c b/src/yelp-man.c new file mode 100644 index 00000000..5d40f895 --- /dev/null +++ b/src/yelp-man.c @@ -0,0 +1,490 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2007 Shaun McCance <shaunm@gnome.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Shaun McCance <shaunm@gnome.org> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <libxml/tree.h> + +#include "yelp-error.h" +#include "yelp-man.h" +#include "yelp-man-parser.h" +#include "yelp-transform.h" +#include "yelp-debug.h" + +#define STYLESHEET DATADIR"/yelp/xslt/man2html.xsl" + +#define YELP_MAN_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_MAN, YelpManPriv)) + +typedef enum { + MAN_STATE_BLANK, /* Brand new, run transform as needed */ + MAN_STATE_PARSING, /* Parsing/transforming document, please wait */ + MAN_STATE_PARSED, /* All done, if we ain't got it, it ain't here */ + MAN_STATE_STOP /* Stop everything now, object to be disposed */ +} ManState; + +struct _YelpManPriv { + gchar *filename; + ManState state; + + GMutex *mutex; + GThread *thread; + + xmlDocPtr xmldoc; + + gboolean process_running; + gboolean transform_running; + + YelpTransform *transform; +}; + +typedef struct _YelpLangEncodings YelpLangEncodings; +struct _YelpLangEncodings { + gchar *language; + gchar *encoding; +}; +/* http://www.w3.org/International/O-charset-lang.html */ +static const YelpLangEncodings langmap[] = { + { "C", "ISO-8859-1" }, + { "af", "ISO-8859-1" }, + { "ar", "ISO-8859-6" }, + { "bg", "ISO-8859-5" }, + { "be", "ISO-8859-5" }, + { "ca", "ISO-8859-1" }, + { "cs", "ISO-8859-2" }, + { "da", "ISO-8859-1" }, + { "de", "ISO-8859-1" }, + { "el", "ISO-8859-7" }, + { "en", "ISO-8859-1" }, + { "eo", "ISO-8859-3" }, + { "es", "ISO-8859-1" }, + { "et", "ISO-8859-15" }, + { "eu", "ISO-8859-1" }, + { "fi", "ISO-8859-1" }, + { "fo", "ISO-8859-1" }, + { "fr", "ISO-8859-1" }, + { "ga", "ISO-8859-1" }, + { "gd", "ISO-8859-1" }, + { "gl", "ISO-8859-1" }, + { "hu", "ISO-8859-2" }, + { "id", "ISO-8859-1" }, /* is this right */ + { "mt", "ISO-8859-3" }, + { "is", "ISO-8859-1" }, + { "it", "ISO-8859-1" }, + { "iw", "ISO-8859-8" }, + { "ja", "EUC-JP" }, + { "ko", "EUC-KR" }, + { "lt", "ISO-8859-13" }, + { "lv", "ISO-8859-13" }, + { "mk", "ISO-8859-5" }, + { "mt", "ISO-8859-3" }, + { "no", "ISO-8859-1" }, + { "pl", "ISO-8859-2" }, + { "pt_BR", "ISO-8859-1" }, + { "ro", "ISO-8859-2" }, + { "ru", "KOI8-R" }, + { "sl", "ISO-8859-2" }, + { "sr", "ISO-8859-2" }, /* Latin, not cyrillic */ + { "sk", "ISO-8859-2" }, + { "sv", "ISO-8859-1" }, + { "tr", "ISO-8859-9" }, + { "uk", "ISO-8859-5" }, + { "zh_CN", "BIG5" }, + { "zh_TW", "BIG5" }, + { NULL, NULL }, +}; + +static void man_class_init (YelpManClass *klass); +static void man_init (YelpMan *man); +static void man_try_dispose (GObject *object); +static void man_dispose (GObject *object); + +/* YelpDocument */ +static void man_request (YelpDocument *document, + gint req_id, + gboolean handled, + gchar *page_id, + YelpDocumentFunc func, + gpointer user_data); + +/* YelpTransform */ +static void transform_func (YelpTransform *transform, + YelpTransformSignal signal, + gpointer func_data, + YelpMan *man); +static void transform_page_func (YelpTransform *transform, + gchar *page_id, + YelpMan *man); +static void transform_final_func (YelpTransform *transform, + YelpMan *man); + +/* Threaded */ +static void man_process (YelpMan *man); + +static YelpDocumentClass *parent_class; + +GType +yelp_man_get_type (void) +{ + static GType type = 0; + if (!type) { + static const GTypeInfo info = { + sizeof (YelpManClass), + NULL, NULL, + (GClassInitFunc) man_class_init, + NULL, NULL, + sizeof (YelpMan), + 0, + (GInstanceInitFunc) man_init, + }; + type = g_type_register_static (YELP_TYPE_DOCUMENT, + "YelpMan", + &info, 0); + } + return type; +} + +static void +man_class_init (YelpManClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + YelpDocumentClass *document_class = YELP_DOCUMENT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->dispose = man_try_dispose; + + document_class->request = man_request; + document_class->cancel = NULL; + + g_type_class_add_private (klass, sizeof (YelpManPriv)); +} + +static void +man_init (YelpMan *man) +{ + YelpManPriv *priv; + + priv = man->priv = YELP_MAN_GET_PRIVATE (man); + + priv->state = MAN_STATE_BLANK; + + priv->mutex = g_mutex_new (); +} + +static void +man_try_dispose (GObject *object) +{ + YelpManPriv *priv; + + g_assert (object != NULL && YELP_IS_MAN (object)); + priv = YELP_MAN (object)->priv; + + g_mutex_lock (priv->mutex); + if (priv->process_running || priv->transform_running) { + priv->state = MAN_STATE_STOP; + g_idle_add ((GSourceFunc) man_try_dispose, object); + g_mutex_unlock (priv->mutex); + } else { + g_mutex_unlock (priv->mutex); + man_dispose (object); + } +} + +static void +man_dispose (GObject *object) +{ + YelpMan *man = YELP_MAN (object); + + g_free (man->priv->filename); + + if (man->priv->xmldoc) + xmlFreeDoc (man->priv->xmldoc); + + g_mutex_free (man->priv->mutex); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +/******************************************************************************/ + +YelpDocument * +yelp_man_new (gchar *filename) +{ + YelpMan *man; + + g_return_val_if_fail (filename != NULL, NULL); + + man = (YelpMan *) g_object_new (YELP_TYPE_MAN, NULL); + man->priv->filename = g_strdup (filename); + + debug_print (DB_FUNCTION, "entering\n"); + debug_print (DB_ARG, " filename = \"%s\"\n", filename); + + yelp_document_add_page_id (YELP_DOCUMENT (man), "x-yelp-index", "index"); + + return (YelpDocument *) man; +} + + +/******************************************************************************/ +/** YelpDocument **************************************************************/ + +static void +man_request (YelpDocument *document, + gint req_id, + gboolean handled, + gchar *page_id, + YelpDocumentFunc func, + gpointer user_data) +{ + YelpMan *man; + YelpManPriv *priv; + YelpError *error; + + debug_print (DB_FUNCTION, "entering\n"); + debug_print (DB_ARG, " req_id = %i\n", req_id); + debug_print (DB_ARG, " page_id = \"%s\"\n", page_id); + + g_assert (document != NULL && YELP_IS_MAN (document)); + + if (handled) + return; + + man = YELP_MAN (document); + priv = man->priv; + + g_mutex_lock (priv->mutex); + + switch (priv->state) { + case MAN_STATE_BLANK: + priv->state = MAN_STATE_PARSING; + priv->process_running = TRUE; + priv->thread = g_thread_create ((GThreadFunc) man_process, man, FALSE, NULL); + break; + case MAN_STATE_PARSING: + break; + case MAN_STATE_PARSED: + case MAN_STATE_STOP: + error = yelp_error_new (_("Page not found"), + _("The page %s was not found in the document %s."), + page_id, priv->filename); + yelp_document_error_request (document, req_id, error); + break; + } + + g_mutex_unlock (priv->mutex); +} + + +/******************************************************************************/ +/** YelpTransform *************************************************************/ + +static void +transform_func (YelpTransform *transform, + YelpTransformSignal signal, + gpointer func_data, + YelpMan *man) +{ + YelpManPriv *priv; + + debug_print (DB_FUNCTION, "entering\n"); + + g_assert (man != NULL && YELP_IS_MAN (man)); + + priv = man->priv; + + g_assert (transform == priv->transform); + + if (priv->state == MAN_STATE_STOP) { + switch (signal) { + case YELP_TRANSFORM_CHUNK: + g_free (func_data); + break; + case YELP_TRANSFORM_ERROR: + yelp_error_free ((YelpError *) func_data); + break; + case YELP_TRANSFORM_FINAL: + break; + } + yelp_transform_release (transform); + priv->transform = NULL; + priv->transform_running = FALSE; + return; + } + + switch (signal) { + case YELP_TRANSFORM_CHUNK: + transform_page_func (transform, (gchar *) func_data, man); + break; + case YELP_TRANSFORM_ERROR: + yelp_document_error_pending (YELP_DOCUMENT (man), (YelpError *) func_data); + yelp_transform_release (transform); + priv->transform = NULL; + priv->transform_running = FALSE; + break; + case YELP_TRANSFORM_FINAL: + transform_final_func (transform, man); + break; + } +} + +static void +transform_page_func (YelpTransform *transform, + gchar *page_id, + YelpMan *man) +{ + YelpManPriv *priv; + gchar *content; + + debug_print (DB_FUNCTION, "entering\n"); + + priv = man->priv; + g_mutex_lock (priv->mutex); + + content = yelp_transform_eat_chunk (transform, page_id); + + yelp_document_add_page (YELP_DOCUMENT (man), page_id, content); + + g_free (page_id); + + g_mutex_unlock (priv->mutex); +} + +static void +transform_final_func (YelpTransform *transform, YelpMan *man) +{ + YelpError *error; + YelpManPriv *priv = man->priv; + + debug_print (DB_FUNCTION, "entering\n"); + + g_mutex_lock (priv->mutex); + + error = yelp_error_new (_("Page not found"), + _("The requested page was not found in the document %s."), + priv->filename); + yelp_document_error_pending (YELP_DOCUMENT (man), error); + + yelp_transform_release (transform); + priv->transform = NULL; + priv->transform_running = FALSE; + + if (priv->xmldoc) + xmlFreeDoc (priv->xmldoc); + priv->xmldoc = NULL; + + g_mutex_unlock (priv->mutex); +} + + +/******************************************************************************/ +/** Threaded ******************************************************************/ + +static void +man_process (YelpMan *man) +{ + YelpManPriv *priv; + const gchar *language; + const gchar *encoding; + YelpManParser *parser; + YelpError *error = NULL; + YelpDocument *document; + gint i; + + gint params_i = 0; + gint params_max = 10; + gchar **params = NULL; + + debug_print (DB_FUNCTION, "entering\n"); + + g_assert (man != NULL && YELP_IS_MAN (man)); + g_object_ref (man); + priv = man->priv; + document = YELP_DOCUMENT (man); + + if (!g_file_test (priv->filename, G_FILE_TEST_IS_REGULAR)) { + error = yelp_error_new (_("File not found"), + _("The file ‘%s’ does not exist."), + priv->filename); + yelp_document_error_pending (document, error); + goto done; + } + + /* FIXME: get the language */ + language = "C"; + + /* default encoding if the language doesn't match below */ + encoding = g_getenv("MAN_ENCODING"); + if (encoding == NULL) + encoding = "ISO-8859-1"; + + if (language != NULL) { + for (i = 0; langmap[i].language != NULL; i++) { + if (g_str_equal (language, langmap[i].language)) { + encoding = langmap[i].encoding; + break; + } + } + } + + parser = yelp_man_parser_new (); + priv->xmldoc = yelp_man_parser_parse_file (parser, priv->filename, encoding); + yelp_man_parser_free (parser); + + if (priv->xmldoc == NULL) { + error = yelp_error_new (_("Could not parse file"), + _("The file ‘%s’ could not be parsed because it is" + " not a well-formed man page."), + priv->filename); + yelp_document_error_pending (document, error); + } + + g_mutex_lock (priv->mutex); + if (priv->state == MAN_STATE_STOP) { + g_mutex_unlock (priv->mutex); + goto done; + } + + priv->transform = yelp_transform_new (STYLESHEET, + (YelpTransformFunc) transform_func, + man); + priv->transform_running = TRUE; + + params = g_new0 (gchar *, params_max); + yelp_settings_params (¶ms, ¶ms_i, ¶ms_max); + + params[params_i] = NULL; + + yelp_transform_start (priv->transform, + priv->xmldoc, + params); + g_mutex_unlock (priv->mutex); + + done: + priv->process_running = FALSE; + g_object_unref (man); +} diff --git a/src/yelp-man.h b/src/yelp-man.h new file mode 100644 index 00000000..844c44ab --- /dev/null +++ b/src/yelp-man.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2007 Shaun McCance <shaunm@gnome.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Shaun McCance <shaunm@gnome.org> + */ + +#ifndef __YELP_MAN_H__ +#define __YELP_MAN_H__ + +#include <glib-object.h> + +#include "yelp-document.h" + +#define YELP_TYPE_MAN (yelp_man_get_type ()) +#define YELP_MAN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), YELP_TYPE_MAN, YelpMan)) +#define YELP_MAN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), YELP_TYPE_MAN, YelpManClass)) +#define YELP_IS_MAN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), YELP_TYPE_MAN)) +#define YELP_IS_MAN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), YELP_TYPE_MAN)) +#define YELP_MAN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), YELP_TYPE_MAN, YelpManClass)) + +typedef struct _YelpMan YelpMan; +typedef struct _YelpManClass YelpManClass; +typedef struct _YelpManPriv YelpManPriv; + +struct _YelpMan { + YelpDocument parent; + YelpManPriv *priv; +}; + +struct _YelpManClass { + YelpDocumentClass parent_class; +}; + +GType yelp_man_get_type (void); +YelpDocument * yelp_man_new (gchar *uri); + +#endif /* __YELP_MAN_H__ */ diff --git a/src/yelp-page.c b/src/yelp-page.c new file mode 100644 index 00000000..c0ad894e --- /dev/null +++ b/src/yelp-page.c @@ -0,0 +1,164 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2006 Brent Smith <gnome@nextreality.net> + * Copyright (C) 2007 Shaun McCance <shaunm@gnome.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Brent Smith <gnome@nextreality.net> + * Shaun McCance <shaunm@gnome.org> + */ + +#include <glib.h> +#include <string.h> + +#include "yelp-page.h" + +static GIOStatus page_read_string (YelpPage *page, + gchar *buffer, + gsize count, + gsize *bytes_read, + YelpError **error); +static GIOStatus page_read_file (YelpPage *page, + gchar *buffer, + gsize count, + gsize *bytes_read, + YelpError **error); + +YelpPage * +yelp_page_new_string (YelpDocument *document, + gchar *id, + const gchar *content, + YelpPageMime mime) +{ + YelpPage *page; + + page = g_slice_new0 (YelpPage); + + page->mime = mime; + + if (document) + page->document = g_object_ref (document); + page->source = YELP_PAGE_SOURCE_STRING; + page->id = g_strdup (id); + + page->content = (gchar *) content; + page->content_len = strlen (content); + + return page; +} + +gsize +yelp_page_get_length (YelpPage *page) +{ + g_return_val_if_fail (page != NULL, 0); + + return page->content_len; +} + +GIOStatus +yelp_page_read (YelpPage *page, + gchar *buffer, + gsize count, + gsize *bytes_read, + YelpError **error) +{ + /* FIXME: set error */ + g_return_val_if_fail (page != NULL, G_IO_STATUS_ERROR); + + if (page->source == YELP_PAGE_SOURCE_STRING) + return page_read_string (page, buffer, count, bytes_read, error); + else + return page_read_file (page, buffer, count, bytes_read, error); +} + +static GIOStatus +page_read_string (YelpPage *page, + gchar *buffer, + gsize count, + gsize *bytes_read, + YelpError **error) +{ + gint real_count = 0; + g_return_val_if_fail (page != NULL, G_IO_STATUS_ERROR); + + if (count < 0) { + real_count = (page->content_len - page->content_offset) + 1; + } else { + real_count = count; + } + + + if (page->content_offset == page->content_len) { + /* FIXME: set the error */ + return G_IO_STATUS_EOF; + } + else if (page->content_offset > page->content_len) { + /* FIXME: set the error */ + return G_IO_STATUS_ERROR; + } + else if (page->content_offset + real_count <= page->content_len) { + strncpy (buffer, page->content + page->content_offset, count); + page->content_offset += count; + *bytes_read = count; + return G_IO_STATUS_NORMAL; + } + else { + strcpy (buffer, page->content + page->content_offset); + *bytes_read = strlen (buffer); + page->content_offset += *bytes_read; + return G_IO_STATUS_NORMAL; + } +} + +static GIOStatus +page_read_file (YelpPage *page, + gchar *buffer, + gsize count, + gsize *bytes_read, + YelpError **error) +{ + g_return_val_if_fail (page != NULL, G_IO_STATUS_ERROR); + /* FIXME: just use yelp-io-channel? */ + return G_IO_STATUS_ERROR; +} + +void +yelp_page_free (YelpPage *page) +{ + g_return_if_fail (page != NULL); + + if (page->document) { + yelp_document_release_page (page->document, page); + g_object_unref (page->document); + } + + if (page->title) + g_free (page->title); + + if (page->id) + g_free (page->id); + if (page->prev_id) + g_free (page->prev_id); + if (page->next_id) + g_free (page->next_id); + if (page->up_id) + g_free (page->up_id); + if (page->root_id) + g_free (page->root_id); + + g_slice_free (YelpPage, page); +} diff --git a/src/yelp-page.h b/src/yelp-page.h new file mode 100644 index 00000000..30a45bf7 --- /dev/null +++ b/src/yelp-page.h @@ -0,0 +1,88 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2006 Brent Smith <gnome@nextreality.net> + * Copyright (C) 2007 Shaun McCance <shaunm@gnome.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Brent Smith <gnome@nextreality.net> + * Shaun McCance <shaunm@gnome.org> + */ + +#ifndef __YELP_PAGE_H__ +#define __YELP_PAGE_H__ + +#include <glib.h> + +#include "yelp-error.h" + +G_BEGIN_DECLS + +typedef enum { + YELP_PAGE_MIME_HTML, + YELP_PAGE_MIME_XHTML +} YelpPageMime; + +typedef enum { + YELP_PAGE_SOURCE_STRING, + YELP_PAGE_SOURCE_FILE +} YelpPageSource; + +typedef struct _YelpPage YelpPage; + +/* This needs to be right here to compile. */ +#include "yelp-document.h" + +struct _YelpPage { + YelpDocument *document; + YelpPageSource source; + YelpPageMime mime; + + gchar *title; + + /* Do not free content. The string is owned by the YelpDocument, + * and it does some internal reference counting to make sure it's + * only reed when it's no longer referenced. These strings are + * just too big to strdup all over the place. + */ + gchar *content; + gsize content_len; + gsize content_offset; + + gchar *id; + gchar *prev_id; + gchar *next_id; + gchar *up_id; + gchar *root_id; +}; + +YelpPage * yelp_page_new_string (YelpDocument *document, + gchar *id, + const gchar *content, + YelpPageMime mime); + +GIOStatus yelp_page_read (YelpPage *page, + gchar *buffer, + gsize count, + gsize *bytes_read, + YelpError **error); +gsize yelp_page_get_length (YelpPage *page); + +void yelp_page_free (YelpPage *page); + +G_END_DECLS + +#endif diff --git a/src/yelp-print.c b/src/yelp-print.c index 42989b8f..7ce42c4a 100644 --- a/src/yelp-print.c +++ b/src/yelp-print.c @@ -159,7 +159,7 @@ print_present_config_dialog (YelpPrintInfo *info) } static gboolean -print_jobs_run (void) +print_jobs_run () { YelpPrintInfo * info = current_jobs->data; info->started = TRUE; @@ -172,7 +172,7 @@ print_jobs_run (void) } static GtkPrintSettings * -yelp_print_load_config_from_file (void) +yelp_print_load_config_from_file () { GtkPrintSettings *settings; @@ -222,8 +222,8 @@ yelp_print_info_free (YelpPrintInfo *info) } -static YelpPrintInfo * -yelp_print_get_print_info (void) +YelpPrintInfo * +yelp_print_get_print_info () { YelpPrintInfo *info; diff --git a/src/yelp-search-pager.c b/src/yelp-search-pager.c index 77209f09..63aa8edc 100644 --- a/src/yelp-search-pager.c +++ b/src/yelp-search-pager.c @@ -1022,7 +1022,7 @@ sk_characters (void *empty, const xmlChar *ch, } } -static void s_startElement(void *data, +void s_startElement(void *data, const xmlChar * name, const xmlChar ** attrs) { @@ -1093,7 +1093,7 @@ static void s_startElement(void *data, return; } -static void s_endElement(void * data, +void s_endElement(void * data, const xmlChar * name) { SearchContainer *c = (SearchContainer *) data; @@ -1116,7 +1116,7 @@ static void s_endElement(void * data, return; } -static void s_characters(void * data, +void s_characters(void * data, const xmlChar * ch, int len) { @@ -1133,7 +1133,6 @@ static void s_characters(void * data, if (c->html && c->search_status != SEARCH_DOC) c->search_status = SEARCH_DOC; if (c->search_status != NOT_SEARCHING) { - gchar *location; gchar *tmp = g_utf8_casefold ((gchar *) ch, len); gint i = 0; gchar *s_term = c->search_term[i]; @@ -1144,7 +1143,7 @@ static void s_characters(void * data, continue; } - location = strstr (tmp, s_term); + gchar *location = strstr (tmp, s_term); if (location) { gchar before = *(location-1); gchar after = *(location+strlen(s_term)); @@ -1197,7 +1196,7 @@ static void s_characters(void * data, return; } -static void s_declEntity (void *data, const xmlChar *name, int type, +void s_declEntity (void *data, const xmlChar *name, int type, const xmlChar *pID, const xmlChar *sID, xmlChar *content) { @@ -1211,8 +1210,7 @@ static void s_declEntity (void *data, const xmlChar *name, int type, return; } -static xmlEntityPtr -s_getEntity (void *data, const xmlChar *name) +xmlEntityPtr s_getEntity (void *data, const xmlChar *name) { SearchContainer *c = (SearchContainer *) data; xmlEntityPtr t = xmlGetPredefinedEntity(name); @@ -1446,7 +1444,7 @@ slow_search_setup (YelpSearchPager *pager) gint terms_number = 0; gint required_no = 0; - static xmlSAXHandler sk_sax_handler = { NULL, }; + static xmlSAXHandler sk_sax_handler = { 0, }; xmlParserCtxtPtr parser; if (langs && langs[0]) lang = (gchar *) langs[0]; @@ -1736,7 +1734,7 @@ slow_search_process (YelpSearchPager *pager) } } -static gchar * +gchar * search_clean_snippet (gchar *snippet, gchar **terms) { /* This is probably what you want to change */ @@ -1830,7 +1828,7 @@ search_clean_snippet (gchar *snippet, gchar **terms) return result; } -static void +void search_parse_result (YelpSearchPager *pager, SearchContainer *c) { xmlNode *child; @@ -1864,7 +1862,7 @@ search_parse_result (YelpSearchPager *pager, SearchContainer *c) xmlFreeDoc (snippet_doc); } -static void +void process_man_result (YelpSearchPager *pager, gchar *result, gchar **terms) { gchar ** split = g_strsplit (result, "\n", -1); @@ -1989,7 +1987,7 @@ process_info_result (YelpSearchPager *pager, gchar *result, gchar **terms) } -static void +void search_process_man (YelpSearchPager *pager, gchar **terms) { gchar *command; @@ -2017,7 +2015,7 @@ search_process_man (YelpSearchPager *pager, gchar **terms) return; } -static void +void search_process_info (YelpSearchPager *pager, gchar **terms) { gchar *command; diff --git a/src/yelp-search-parser.c b/src/yelp-search-parser.c new file mode 100644 index 00000000..646f5d41 --- /dev/null +++ b/src/yelp-search-parser.c @@ -0,0 +1,1459 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2007 Don Scorgie <Don@Scorgie.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Don Scorgie <Don@Scorgie.org> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <glib.h> +#include <glib/gi18n.h> +#include <libgnomevfs/gnome-vfs.h> +#include <libxml/parser.h> +#include <libxml/parserInternals.h> +#include <libxml/xmlreader.h> +#include <libxml/xpath.h> +#include <libxml/xpathInternals.h> +#include <libxml/HTMLtree.h> +#include <libxml/tree.h> +#include <libxslt/xslt.h> +#include <libxslt/templates.h> +#include <libxslt/transform.h> +#include <libxslt/extensions.h> +#include <libxslt/xsltInternals.h> +#include <libxslt/xsltutils.h> +#include <rarian.h> + +#ifdef ENABLE_BEAGLE +#include <beagle/beagle.h> +#endif /* ENABLE_BEAGLE */ + +#include "yelp-error.h" +#include "yelp-settings.h" +#include "yelp-search-parser.h" +#include "yelp-utils.h" +#include "yelp-debug.h" + +#define DESKTOP_ENTRY_GROUP "Desktop Entry" +#define KDE_DESKTOP_ENTRY_GROUP "KDE Desktop Entry" + +#define YELP_NAMESPACE "http://www.gnome.org/yelp/ns" + +typedef gboolean (*ProcessFunction) (YelpSearchParser *parser); + +typedef struct _SearchContainer SearchContainer; + +#define ONLINE_URL "http://api.gnome.org/yelp/forums?query=%s" + +#define ONLINE_NAME N_("the GNOME Support Forums") + +enum { + NOT_SEARCHING = 0, + SEARCH_1, + SEARCH_CHILD, + SEARCH_DOC = 99 +}; + +struct _SearchContainer { + gchar * current_subsection; + gchar * result_subsection; + gchar * doc_title; + gchar * base_path; + gchar * base_filename; + gchar * snippet; + GSList * components; + GHashTable *entities; + gchar ** search_term; + gint required_words; + gint * dup_of; + gboolean * found_terms; + gboolean * stop_word; + gfloat * score_per_word; + gchar * top_element; + gint search_status; + gchar * elem_type; + GSList * elem_stack; + gfloat score; + gfloat snippet_score; + gboolean html; + gchar * sect_name; + gboolean grab_text; + gchar * default_snippet; +}; + +typedef struct { + YelpSearchParser *parser; + gint required_no; + gint terms_number; + gboolean *stop_list; + gint *dup_list; + gchar **terms_list; + SearchContainer *container; +} SearchDocData; + + +struct _YelpSearchParser { + gchar *search_terms; + xmlDocPtr search_doc; + xmlNodePtr root; + + GPtrArray *hits; + int snippet_request_count; + GSList * pending_searches; + + guint search_process_id; + guint slow_search_setup_process_id; + guint slow_search_process_id; + + gboolean finished; + +}; + + +static gboolean search_parser_process_idle (YelpSearchParser *parser); + +static void s_startElement (void *data, + const xmlChar *name, + const xmlChar **attrs); +static void s_endElement (void *data, + const xmlChar *name); +static void s_characters (void *data, + const xmlChar *ch, + int len); +static void s_declEntity (void *data, + const xmlChar *name, + int type, + const xmlChar *pID, + const xmlChar *sID, + xmlChar *content); +static xmlEntityPtr s_getEntity (void *data, + const xmlChar *name); +static gboolean slow_search_setup (YelpSearchParser *parser); +static gboolean slow_search_process (RrnReg *reg, + SearchDocData *data); +static void search_parse_result (YelpSearchParser *parser, + SearchContainer *c); +static gchar * search_clean_snippet (gchar *snippet, + gchar **terms); +static void search_process_man (YelpSearchParser *parser, + gchar **terms); +static void search_process_info (YelpSearchParser *parser, + gchar **terms); +static void process_man_result (YelpSearchParser *parser, + gchar *result, + gchar **terms); +void process_info_result (YelpSearchParser *parser, + gchar *result, + gchar **terms); +gchar * string_append (gchar *current, + gchar *new, + gchar *suffix); +static void search_free_container (SearchContainer *c); + + +#ifdef ENABLE_BEAGLE +static BeagleClient *beagle_client; +#endif /* ENABLE_BEAGLE */ + +YelpSearchParser * +yelp_search_parser_new (void) +{ + YelpSearchParser *parser = g_new0 (YelpSearchParser, 1); + + return parser; +} + +void +yelp_search_parser_free (YelpSearchParser *parser) +{ + g_free (parser); +} + +static gboolean +check_hex (char check) +{ + if (check >= '0' && check <= '9') + return TRUE; + if (check >= 'a' && check <= 'f') + return TRUE; + if (check >= 'A' && check <= 'F') + return TRUE; + return FALSE; +} + +static int +conv_hex (char conv) +{ + if (conv >= '0' && conv <= '9') + return conv - '0'; + if (conv >= 'a' && conv <= 'f') + return conv - 'a' + 10; + if (conv >= 'A' && conv <= 'F') + return conv - 'A' + 10; + return 0; +} + +static char * +decode_uri (const char *uri) +{ + char *decoded = g_strdup (uri); + char *iterator; + + for (iterator = decoded; *iterator; iterator ++) { + if (*iterator == '%' && check_hex (iterator[1]) && check_hex(iterator[2])) { + *iterator = conv_hex (iterator[1]) * 16 + conv_hex (iterator[2]); + memmove (iterator + 1, iterator + 3, strlen (iterator + 3)); + } + } + + return decoded; +} + +xmlDocPtr +yelp_search_parser_process (YelpSearchParser *parser, gchar *search_terms) +{ + debug_print (DB_FUNCTION, "entering\n"); + + parser->search_terms = decode_uri (search_terms); + + parser->search_process_id = + g_idle_add_full (G_PRIORITY_LOW, + (GSourceFunc) search_parser_process_idle, + parser, NULL); + + while (!parser->finished) + g_thread_yield (); + return parser->search_doc; +} + +/******************************************************************************/ +static void +check_finished (YelpSearchParser *parser) +{ + gchar *tmp; + gchar **split; + xmlNodePtr online = NULL; + gchar *check; + gchar *title; + gchar *text = NULL; + xmlXPathContextPtr results_xpath_ctx = NULL; + xmlXPathObjectPtr results_xpath = NULL; + gint number_of_results = 0; + + results_xpath_ctx = xmlXPathNewContext(parser->search_doc); + results_xpath = xmlXPathEvalExpression(BAD_CAST "/search/result", results_xpath_ctx); + if (results_xpath && results_xpath->nodesetval && results_xpath->nodesetval->nodeNr) { + number_of_results = results_xpath->nodesetval->nodeNr; + } else { + number_of_results = 0; + } + xmlXPathFreeObject(results_xpath); + xmlXPathFreeContext(results_xpath_ctx); + + if (number_of_results == 0) { + title = g_strdup_printf( _("No results for \"%s\""), parser->search_terms); + text = g_strdup(_("Try using different words to describe the problem " + "you're having or the topic you want help with.")); + } else { + title = g_strdup_printf( _("Search results for \"%s\""), parser->search_terms); + } + xmlNewTextChild (parser->root, NULL, BAD_CAST "title", BAD_CAST title); + + if (text) { + xmlNewTextChild (parser->root, NULL, BAD_CAST "text", BAD_CAST text); + g_free(text); + } + + /* TRANSLATORS: Please don't do anything funny with the + * format arguement. It isn't really going through a printf + * The %s is used to indicate where the name of the site (linked) + * should be. This is done in the XSLT + */ + tmp = g_strdup (_("Repeat the search online at %s")); + split = g_strsplit (tmp, "%s", 2); + check = g_strdup_printf (ONLINE_URL, parser->search_terms); + + online = xmlNewTextChild (parser->root, NULL, BAD_CAST "online", BAD_CAST split[0]); + g_free (tmp); + xmlNewProp (online, BAD_CAST "name", + BAD_CAST ONLINE_NAME); + xmlNewProp (online, BAD_CAST "href", + BAD_CAST check); + g_free (check); + xmlNewTextChild (parser->root, NULL, BAD_CAST "online1", BAD_CAST split[1]); + + parser->finished = TRUE; + +} + +#ifdef ENABLE_BEAGLE +typedef struct +{ + YelpSearchParser *parser; + xmlNode *node; +} SnippetLocation; + +static void snippet_closed (BeagleSnippetRequest *request, + SnippetLocation *snippet_location); +static void snippet_response (BeagleSnippetRequest *request, + BeagleSnippetResponse *response, + SnippetLocation *snippet_location); +static void snippet_error (BeagleSnippetRequest *request, + GError *error, + SnippetLocation *snippet_location); + +static void +snippet_closed (BeagleSnippetRequest *request, SnippetLocation *snippet_location) +{ + YelpSearchParser *parser = snippet_location->parser; + + debug_print (DB_FUNCTION, "entering\n"); + + parser->snippet_request_count --; + check_finished (parser); + + g_signal_handlers_disconnect_by_func (request, + G_CALLBACK (snippet_response), + snippet_location); + g_signal_handlers_disconnect_by_func (request, + G_CALLBACK (snippet_error), + snippet_location); + g_signal_handlers_disconnect_by_func (request, + G_CALLBACK (snippet_closed), + snippet_location); + + g_free (snippet_location); + g_object_unref (request); +} + +static void +snippet_response (BeagleSnippetRequest *request, BeagleSnippetResponse *response, SnippetLocation *snippet_location) +{ + xmlDoc *snippet_doc; + xmlNode *node; + char *xmldoc; + + const char *xml = beagle_snippet_response_get_snippet (response); + + if (xml == NULL) { + debug_print (DB_DEBUG, "snippet_response empty\n"); + return; + } + debug_print (DB_DEBUG, "snippet_response: %s\n", xml); + + xmldoc = g_strdup_printf ("<snippet>%s</snippet>", xml); + snippet_doc = xmlParseDoc (BAD_CAST xmldoc); + g_free (xmldoc); + if (!snippet_doc) + return; + node = xmlDocGetRootElement (snippet_doc); + xmlUnlinkNode (node); + xmlAddChild (snippet_location->node, node); + xmlFreeDoc (snippet_doc); +} + +static void +snippet_error (BeagleSnippetRequest *request, GError *error, SnippetLocation *snippet_location) +{ + debug_print (DB_FUNCTION, "entering\n"); +} + + +static void +hits_added_cb (BeagleQuery *query, BeagleHitsAddedResponse *response, YelpSearchParser *parser) +{ + GSList *hits, *l; + + debug_print (DB_FUNCTION, "hits_added\n"); + + hits = beagle_hits_added_response_get_hits (response); + + for (l = hits; l; l = l->next) { + BeagleHit *hit = l->data; + beagle_hit_ref (hit); + debug_print (DB_DEBUG, "%f\n", beagle_hit_get_score (hit)); + g_ptr_array_add (parser->hits, hit); + } +} + +static gboolean +check_lang (const char *lang) { + int i; + for (i = 0; langs[i]; i++) { + if (!strncmp (lang, langs[i], 2)) { + debug_print (DB_DEBUG, "%s preferred\n", lang); + return TRUE; + } + } + debug_print (DB_DEBUG, "%s not preferred\n", lang); + return FALSE; +} + +static gint +compare_hits (gconstpointer a, + gconstpointer b) +{ + BeagleHit **hita = (BeagleHit **) a; + BeagleHit **hitb = (BeagleHit **) b; + const char *langa, *langb; + gboolean a_preferred = TRUE, b_preferred = TRUE; + + if (beagle_hit_get_one_property (*hita, "fixme:language", &langa)) + a_preferred = check_lang(langa); + if (beagle_hit_get_one_property (*hitb, "fixme:language", &langb)) + b_preferred = check_lang(langb); + + if (a_preferred != b_preferred) { + if (a_preferred) + return -1; + if (b_preferred) + return 1; + } + + double scorea = beagle_hit_get_score (*hita); + double scoreb = beagle_hit_get_score (*hitb); + + /* The values here are inverted so that it's a descending sort. */ + if (scorea < scoreb) + return 1; + if (scoreb < scorea) + return -1; + return 0; +} + +static void +finished_cb (BeagleQuery *query, + BeagleFinishedResponse *response, + YelpSearchParser *parser) +{ + int i; + + debug_print (DB_FUNCTION, "entering\n"); + + g_ptr_array_sort (parser->hits, compare_hits); + + for (i = 0; i < 10 && i < parser->hits->len; i++) { + BeagleHit *hit = g_ptr_array_index (parser->hits, i); + xmlNode *child; + /* static float score_fake = 0; */ + char *score; + const char *property; + BeagleSnippetRequest *request; + SnippetLocation *snippet_location; + + child = xmlNewTextChild (parser->root, NULL, BAD_CAST "result", NULL); + xmlSetProp (child, BAD_CAST "uri", BAD_CAST beagle_hit_get_uri (hit)); + xmlSetProp (child, BAD_CAST "parent_uri", + BAD_CAST beagle_hit_get_parent_uri (hit)); + if (beagle_hit_get_one_property (hit, "dc:title", &property)) + xmlSetProp (child, BAD_CAST "title", BAD_CAST property); + if (beagle_hit_get_one_property (hit, "fixme:base_title", &property)) + xmlSetProp (child, BAD_CAST "base_title", BAD_CAST property); + + score = g_strdup_printf ("%f", beagle_hit_get_score (hit)); + debug_print (DB_DEBUG, "%f\n", beagle_hit_get_score (hit)); + /*xmlSetProp (child, BAD_CAST "score", BAD_CAST score);*/ + g_free (score); + + parser->snippet_request_count ++; + + snippet_location = g_new (SnippetLocation, 1); + + snippet_location->parser = parser; + snippet_location->node = child; + + request = beagle_snippet_request_new (); + beagle_snippet_request_set_hit (request, hit); + beagle_snippet_request_set_query (request, query); + + g_signal_connect (request, "response", + G_CALLBACK (snippet_response), snippet_location); + g_signal_connect (request, "error", + G_CALLBACK (snippet_error), snippet_location); + g_signal_connect (request, "closed", + G_CALLBACK (snippet_closed), snippet_location); + + debug_print (DB_DEBUG, "Requesting snippet\n"); + beagle_client_send_request_async (beagle_client, BEAGLE_REQUEST (request), + NULL); + } + + g_signal_handlers_disconnect_by_func (query, + G_CALLBACK (hits_added_cb), + parser); + g_signal_handlers_disconnect_by_func (query, + G_CALLBACK (finished_cb), + parser); + g_object_unref (query); + + g_ptr_array_foreach (parser->hits, (GFunc) beagle_hit_unref, NULL); + g_ptr_array_free (parser->hits, TRUE); + parser->hits = NULL; + + check_finished (parser); +} +#endif /* ENABLE_BEAGLE */ + +static gboolean +search_parser_process_idle (YelpSearchParser *parser) +{ +#ifdef ENABLE_BEAGLE + BeagleQuery *query; + GError *error = NULL; +#endif /* ENABLE_BEAGLE */ + + parser->search_doc = xmlNewDoc (BAD_CAST "1.0"); + parser->root = xmlNewNode (NULL, BAD_CAST "search"); + xmlSetProp (parser->root, BAD_CAST "title", BAD_CAST parser->search_terms); + xmlDocSetRootElement (parser->search_doc, parser->root); + +#ifdef ENABLE_BEAGLE + if (beagle_client != NULL) { + query = beagle_query_new (); + + beagle_query_set_max_hits (query, 10000); + beagle_query_add_text (query, parser->search_terms); + beagle_query_add_source (query, "documentation"); + + parser->hits = g_ptr_array_new (); + + g_signal_connect (query, "hits-added", + G_CALLBACK (hits_added_cb), + parser); + + g_signal_connect (query, "finished", + G_CALLBACK (finished_cb), + parser); + + beagle_client_send_request_async (beagle_client, BEAGLE_REQUEST (query), &error); + + if (error) { + debug_print (DB_DEBUG, "error: %s\n", error->message); + } + + g_clear_error (&error); + } else { + g_warning ("beagled not running, using basic search support."); + } +#endif /* ENABLE_BEAGLE */ + +#ifdef ENABLE_BEAGLE + if (beagle_client == NULL) { +#endif + g_return_val_if_fail (parser->slow_search_setup_process_id == 0, FALSE); + + parser->slow_search_setup_process_id = + g_idle_add ((GSourceFunc) slow_search_setup, + parser); +#ifdef ENABLE_BEAGLE + } +#endif + + /* returning false removes this idle function from the main loop; + * we also set our search process id to zero */ + parser->search_process_id = 0; + return FALSE; +} + +void s_startElement(void *data, + const xmlChar * name, + const xmlChar ** attrs) +{ + SearchContainer *c = (SearchContainer *) data; + + if (g_str_equal (name, "xi:include") || g_str_equal (name, "include")) { + gint i=0; + while (attrs[i]) { + if (g_str_equal (attrs[i], "href")) { + + c->components = g_slist_append (c->components, + g_strconcat (c->base_path, + "/", + attrs[i+1], + NULL)); + break; + } + i+=2; + } + } + + if (attrs) { + gint i=0; + while (attrs[i]) { + if (g_str_equal (attrs[i], "id")) { + g_free (c->current_subsection); + c->current_subsection = g_strdup ((gchar *) attrs[i+1]); + } + i+=2; + } + } + /* Do we need to grab the title of the document? + * used in snippets when displaying results from an indexterm etc. + */ + if (c->search_status != NOT_SEARCHING && g_str_equal (name, "title")) { + c->grab_text = TRUE; + } + + /* Are we allowed to search this element? */ + if (c->search_status == NOT_SEARCHING) { + if (c->html && g_str_equal (name, "html")) { + c->search_status = SEARCH_DOC; + return; + } + + if (g_str_equal (name, "title")) { + c->search_status = SEARCH_1; + } + else if (g_str_equal (name, "indexterm")) + c->search_status = SEARCH_1; + else if (g_str_equal (name, "sect1") || + g_str_equal (name, "section") || + g_str_equal (name, "chapter") || + g_str_equal (name, "body")) + c->search_status = SEARCH_DOC; + } else if (c->search_status == SEARCH_1) { + c->search_status = SEARCH_CHILD; + } + + if (c->elem_type) { + c->elem_stack = g_slist_prepend (c->elem_stack, + g_strdup (c->elem_type)); + g_free (c->elem_type); + } + + c->elem_type = g_strdup ((gchar *) name); + + return; +} + +void s_endElement(void * data, + const xmlChar * name) +{ + SearchContainer *c = (SearchContainer *) data; + + if (c->search_status == SEARCH_CHILD) { + c->search_status = SEARCH_1; + } else if (c->search_status == SEARCH_1) { + c->search_status = NOT_SEARCHING; + } + + g_free (c->elem_type); + c->elem_type = NULL; + + if (c->elem_stack) { + GSList *top = c->elem_stack; + c->elem_type = g_strdup ((gchar *) top->data); + c->elem_stack = g_slist_delete_link (c->elem_stack, top); + } + c->grab_text = FALSE; + return; +} + +void s_characters(void * data, + const xmlChar * ch, + int len) +{ + SearchContainer *c = (SearchContainer *) data; + if (c->grab_text) { + g_free (c->sect_name); + c->sect_name = g_strndup ((gchar *) ch, len); + } + + /* Sometimes html docs don't trigger the "startElement" method + * I don't know why. Instead, we just search the entire + * html file, hoping to find something. + */ + if (c->html && c->search_status != SEARCH_DOC) + c->search_status = SEARCH_DOC; + if (c->search_status != NOT_SEARCHING) { + gchar *tmp = g_utf8_casefold ((gchar *) ch, len); + gint i = 0; + gchar *s_term = c->search_term[i]; + while (s_term && c->score_per_word[i] < 1.0) { + if (c->stop_word[i] || c->score_per_word[c->dup_of[i]] == 1.0) { + i++; + s_term = c->search_term[i]; + continue; + } + + gchar *location = strstr (tmp, s_term); + if (location) { + gchar before = *(location-1); + gchar after = *(location+strlen(s_term)); + gfloat local_score = 0.0; + gboolean use_text = TRUE; + if (location == tmp) + before = ' '; + if (strlen(location) == strlen(s_term)) + after = ' '; + + if ((g_ascii_ispunct (before) || g_ascii_isspace (before)) + && (g_ascii_ispunct (after) || g_ascii_isspace (after))) { + if (!c->elem_type) { + /* Stupid HTML. Treat like its a normal tag */ + local_score = 0.1; + } else if (g_str_equal(c->elem_type, "primary")) { + local_score = 1.0; + use_text = FALSE; + } else if (g_str_equal (c->elem_type, "secondary")) { + local_score = 0.9; + use_text = FALSE; + } else if (g_str_equal (c->elem_type, "title") || + g_str_equal (c->elem_type, "titleabbrev")) { + local_score = 0.8; + } else { + local_score = 0.1; + } + c->score += local_score; + c->found_terms[c->dup_of[i]] = TRUE; + if (local_score > c->snippet_score) { + g_free (c->snippet); + if (use_text) { + c->snippet = g_strndup (g_utf8_casefold ((gchar *) ch, + len), + len); + } else { + c->snippet = g_strdup (c->sect_name); + } + c->result_subsection = g_strdup (c->current_subsection); + c->snippet_score = local_score; + c->score_per_word[c->dup_of[i]] = local_score; + } + } + } + i++; + s_term = c->search_term[i]; + } + g_free (tmp); + } + return; +} + +void s_declEntity (void *data, const xmlChar *name, int type, + const xmlChar *pID, const xmlChar *sID, + xmlChar *content) +{ + SearchContainer *c = (SearchContainer *) data; + if (type == 2) { + g_hash_table_insert (c->entities, + g_strdup ((gchar *) name), + g_strdup ((gchar *) sID)); + + } + return; +} + +xmlEntityPtr s_getEntity (void *data, const xmlChar *name) +{ + SearchContainer *c = (SearchContainer *) data; + xmlEntityPtr t = xmlGetPredefinedEntity(name); + + if (!t) { + gchar * lookup = g_hash_table_lookup (c->entities, name); + if (lookup) { + c->components = g_slist_append (c->components, + g_strconcat (c->base_path, + "/", + lookup, NULL)); + } + } + + return t; + +} + + + + + +static xmlSAXHandler handlers = { + NULL, NULL, NULL, NULL, NULL, + s_getEntity, + s_declEntity, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + s_startElement, s_endElement, NULL, s_characters, + NULL, NULL, NULL, NULL, NULL, NULL +}; + + +/* Parse the omfs and build the list of files to be searched */ + +/* A common bit of code used below. Chucked in a function for easy */ +gchar * +string_append (gchar *current, gchar *new, gchar *suffix) +{ + gchar *ret; + + if (suffix) { + ret = g_strconcat (current, ":", new, suffix, NULL); + } else { + ret = g_strconcat (current, ":", new, NULL); + } + g_free (current); + return ret; +} + +static gint +build_lists (gchar *search_terms, gchar ***terms, gint **dups, + gboolean ** stops, gint *req) +{ + gchar *ignore_words, *common_prefixes, *common_suffixes; + gchar **prefixes, **suffixes, **ignore; + gchar **list_copy; + gchar **iter, **iter1 = NULL; + gchar *term_str = NULL; + gchar *dup_str = NULL; + gint n_terms = 0, i=-1; + gint orig_term = 0; + gint non_stop = 0; + + + /* Translators: Do not translate this list exactly. These are + * colon-separated words that aren't useful for choosing search + * results; they will be different for each language. Include + * pronouns, articles, very common verbs and prepositions, + * words from question structures like "tell me about" and + * "how do I", and words for functional states like "not", + * "work", and "broken". + */ + ignore_words = g_strdup (_("a:about:an:are:as:at:be:broke:broken:by" + ":can:can't:dialog:dialogue:do:doesn't" + ":doesnt:don't:dont:explain:for:from:get" + ":gets:got:make:makes:not:when:has" + ":have:help:how:i:in:is:it:item:me:my:of" + ":on:or:tell:that:the:thing:this:to:what" + ":where:who:will:with:won't:wont:why:work" + ":working:works")); + /* Translators: This is a list of common prefixes for words. + * Do not translate this directly. Instead, use a colon + * seperated list of word-starts. In English, an example + * is re-. If there is none, please use the term NULL + * If there is only one, please put a colon after. + * E.g. if the common prefix is re then the string would be + * "re:" + */ + common_prefixes = g_strdup (_("re")); + + /* Translators: This is a list of (guess what?) common suffixes + * to words. Things that may be put at ends of words to slightly + * alter their meaning (like -ing and -s in English). This is a + * colon seperated list (I like colons). If there are none, + * please use the strig NULL. If there is only 1, please + * add a colon at the end of the list + */ + common_suffixes = g_strdup (_("ers:er:ing:es:s:'s")); + + ignore = g_strsplit (ignore_words, ":", -1); + if (strchr (common_prefixes, ':')) { + prefixes = g_strsplit (common_prefixes, ":", -1); + } else { + prefixes = NULL; + } + if (strchr (common_suffixes, ':')) { + suffixes = g_strsplit (common_suffixes, ":", -1); + } else { + suffixes = NULL; + } + search_terms = g_strdelimit (search_terms, ":", ' '); + list_copy = g_strsplit (g_utf8_casefold (g_strstrip ( + search_terms), -1), + " ", -1); + + for (iter = list_copy; *iter != NULL; iter++) { + gboolean ignoring = FALSE; + if (strlen (*iter) == 0) { + continue; + } + if (g_str_has_suffix (*iter, "?")) { + gchar *tmp; + tmp = g_strndup (*iter, strlen (*iter) - 1); + g_free (*iter); + *iter = g_strdup (tmp); + g_free (tmp); + } + if (!term_str) { + term_str = g_strdup (*iter); + } else { + term_str = string_append (term_str, *iter, NULL); + } + + for (iter1 = ignore; *iter1; iter1++) { + if (g_str_equal (*iter, *iter1)) { + ignoring = TRUE; + break; + } + } + if (ignoring) { + if (!dup_str) { + dup_str = g_strdup ("I"); + } else { + dup_str = string_append (dup_str, "I", NULL); + } + continue; + } + non_stop++; + + if (!dup_str) { + dup_str = g_strdup ("O"); + } else { + dup_str = string_append (dup_str, "O", NULL); + } + (*req)++; + if (prefixes) { + for (iter1 = prefixes; *iter1; iter1++) { + if (g_str_has_prefix (*iter, *iter1)) { + term_str = string_append (term_str, + (*iter+strlen(*iter1)), NULL); + } else { + term_str = string_append (term_str, *iter, *iter1); + } + dup_str = string_append (dup_str, "D", NULL); + } + } + if (suffixes) { + for (iter1 = suffixes; *iter1; iter1++) { + if (g_str_has_suffix (*iter, *iter1)) { + gchar *tmp; + tmp = g_strndup (*iter, (strlen(*iter)-strlen(*iter1))); + term_str = string_append (term_str, tmp, NULL); + g_free (tmp); + } else { + term_str = string_append (term_str, *iter, *iter1); + } + dup_str = string_append (dup_str, "D", NULL); + } + } + } + g_strfreev (list_copy); + *terms = g_strsplit (term_str, ":", -1); + n_terms = g_strv_length (*terms); + (*dups) = g_new0 (gint, n_terms); + (*stops) = g_new0 (gboolean, n_terms); + list_copy = g_strsplit (dup_str, ":", -1); + + for (iter = *terms; *iter; iter++) { + i++; + if (g_str_equal (list_copy[i], "O")) { + orig_term = i; + } + (*dups)[i] = orig_term; + + for (iter1 = ignore; *iter1; iter1++) { + if (non_stop > 0 && g_str_equal (*iter, *iter1)) { + (*stops)[i] = TRUE; + (*dups)[i] = -2; + break; + } + } + } + + /* Clean up all those pesky strings */ + g_free (ignore_words); + g_free (common_prefixes); + g_free (common_suffixes); + g_free (term_str); + g_free (dup_str); + g_strfreev (prefixes); + g_strfreev (suffixes); + g_strfreev (ignore); + g_strfreev (list_copy); + + return n_terms; +} + +static gboolean +slow_search_setup (YelpSearchParser *parser) +{ + gchar **terms_list = NULL; + gint *dup_list = NULL; + gboolean *stop_list = NULL; + gint terms_number = 0; + gint required_no = 0; + SearchDocData *data; + + + terms_number = build_lists (parser->search_terms,&terms_list, + &dup_list, &stop_list, + &required_no); + data = g_new0 (SearchDocData, 1); + data->container = g_new0 (SearchContainer, 1); + data->parser = parser; + data->required_no = required_no; + data->terms_number = terms_number; + data->stop_list = stop_list; + data->dup_list = dup_list; + data->terms_list = terms_list; + data->terms_number = terms_number; + + rrn_for_each ((RrnForeachFunc) slow_search_process, data); + + search_process_man (parser, terms_list); + search_process_info (parser, terms_list); + + check_finished (parser); + + return FALSE; +} + + +static gboolean +slow_search_process (RrnReg *reg, SearchDocData *data) +{ + gint i, j=0; + SearchContainer *container = data->container; + gchar *ptr, *path; + gchar *fname; + + /* Set up the container with the new data */ + if (g_str_has_prefix (reg->uri, "file:")) { + fname = &(reg->uri[5]); + } else { + fname = reg->uri; + } + + while (fname[0] == '/' && fname[1] == '/') { + fname++; + } + + container->base_filename = g_strdup (fname); + fname = g_strdup (container->base_filename); + + container->entities = g_hash_table_new (g_str_hash, g_str_equal); + container->doc_title = g_strdup ((gchar *) reg->name); + container->score=0; + container->html = FALSE; + container->default_snippet = g_strdup ((gchar *) reg->comment); + container->current_subsection = NULL; + container->elem_type = NULL; + + ptr = g_strrstr (container->base_filename, "/"); + + path = g_strndup (container->base_filename, + ptr - container->base_filename); + + /* BEGIN HTML special block */ + if (g_str_equal (reg->type, "text/html") || + g_str_has_suffix (fname, "html")) { + GDir *dir; + gchar *filename; + container->html = TRUE; + ptr++; + + dir = g_dir_open (path, 0, NULL); + + while ((filename = (gchar *) g_dir_read_name (dir))) { + if ((g_str_has_suffix (filename, ".html") || + g_str_has_suffix (filename, ".htm")) && + !g_str_equal (filename, ptr)) { + container->components = + g_slist_append (container->components, + g_strconcat (path, "/", filename, + NULL)); + + } + } + /* END HTML special blcok */ + } + + container->base_path = g_strdup (path); + + container->required_words = data->required_no; + container->grab_text = FALSE; + container->sect_name = NULL; + + container->search_term = g_strdupv (data->terms_list); + container->stop_word = g_new0 (gboolean, data->terms_number); + container->dup_of = g_new0 (gint, data->terms_number); + container->found_terms = g_new0 (gboolean, data->terms_number); + container->score_per_word = g_new0 (gfloat, data->terms_number); + container->found_terms = g_new0 (gboolean, data->terms_number); + container->result_subsection = NULL; + container->search_status = NOT_SEARCHING; + container->snippet_score = 0; + container->snippet = NULL; + + for (i=0; i< data->terms_number; i++) { + container->stop_word[i] = data->stop_list[i]; + container->dup_of[i] = data->dup_list[i]; + } + + + xmlSAXUserParseFile (&handlers, container, fname); + for (i=0; i< g_strv_length (container->search_term); ++i) { + if (container->found_terms[i]) { + j++; + } + } + if (j >= container->required_words) { + search_parse_result (data->parser, container); + } else while (container->components) { + GSList *next = container->components; + container->components = g_slist_remove_link (container->components, next); + container->search_status = NOT_SEARCHING; + xmlSAXUserParseFile (&handlers, container, (gchar *) next->data); + j = 0; + for (i=0; i< g_strv_length (container->search_term); ++i) { + if (container->found_terms[i]) + j++; + } + if (j >= container->required_words) { + search_parse_result (data->parser, container); + break; + } + } + + search_free_container (container); + g_free (path); + return TRUE; + +} + +static void +search_free_container (SearchContainer *c) +{ + g_strfreev (c->search_term); + g_free (c->dup_of); + g_free (c->found_terms); + g_free (c->stop_word); + g_free (c->score_per_word); + g_free (c->top_element); + g_free (c->elem_type); + g_free (c->sect_name); + g_free (c->default_snippet); + g_free (c->current_subsection); + g_free (c->result_subsection); + g_free (c->doc_title); + g_free (c->base_path); + g_free (c->base_filename); + g_free (c->snippet); + g_hash_table_destroy (c->entities); +} + +gchar * +search_clean_snippet (gchar *snippet, gchar **terms) +{ + /* This is probably what you want to change */ + gint len_before_term = 47; + gint len_after_term = 47; + gchar **iteration; + gboolean am_cutting = FALSE; + gchar *result = NULL; + gboolean found_terms = FALSE; + + + if (!snippet) + return NULL; + + if (strlen(snippet) > (len_before_term+len_after_term)) { + am_cutting = TRUE; + } + result = g_strdup (snippet); + + for (iteration = terms; *iteration; iteration++) { + gchar *before, *after, *tmp; + gchar *str; + gchar before_c, after_c; + gint count = 0; + + while ((str = strstr (result, (*iteration)))) { + gboolean breaking = FALSE; + gint i; + for (i=0; i< count; i++) { + str++; + str = strstr (str, (*iteration)); + if (!str) { + breaking = TRUE; + break; + } + } + count++; + if (breaking) + break; + + before_c = *(str-1); + after_c = *(str+strlen(*iteration)); + + if (g_ascii_isalpha (before_c) || g_ascii_isalpha (after_c)) { + continue; + } + + tmp = g_strndup (result, (str-result)); + /* If we have to chop the snippet down to size, here is the + * place to do it. Only the first time through though + */ + if (am_cutting && !found_terms && strlen (tmp) > len_before_term) { + gchar *tmp1; + gchar *tmp2; + gint cut_by; + + tmp1 = tmp; + cut_by = strlen(tmp) - len_before_term; + + tmp1 += cut_by; + tmp2 = g_strdup (tmp1); + g_free (tmp); + tmp = g_strconcat ("...",tmp2, NULL); + g_free (tmp2); + } + + before = g_strconcat (tmp, "<em>", NULL); + g_free (tmp); + + str += strlen (*iteration); + + if (am_cutting && !found_terms && strlen (str) > len_after_term) { + gchar *tmp1; + + tmp1 = g_strndup (str, len_after_term); + tmp = g_strconcat (tmp1, "...", NULL); + g_free (tmp1); + } else { + tmp = g_strdup (str); + } + + after = g_strconcat ((*iteration), "</em>", tmp, NULL); + + + + g_free (result); + result = g_strconcat (before, after, NULL); + found_terms = TRUE; + } + } + return result; +} + +void +search_parse_result (YelpSearchParser *parser, SearchContainer *c) +{ + xmlNode *child; + gchar *new_uri; + xmlDoc *snippet_doc; + xmlNode *node; + char *xmldoc; + + new_uri = g_strconcat (c->base_filename, "#", c->result_subsection, + NULL); + child = xmlNewTextChild (parser->root, NULL, + BAD_CAST "result", NULL); + xmlSetProp (child, BAD_CAST "uri", BAD_CAST new_uri); + xmlSetProp (child, BAD_CAST "title", BAD_CAST g_strstrip (c->doc_title)); + xmlSetProp (child, BAD_CAST "score", + BAD_CAST g_strdup_printf ("%f", c->score)); + /* Fix up the snippet to show the break_term in bold */ + if (!c->snippet) + c->snippet = g_strdup (c->default_snippet); + xmldoc = g_strdup_printf ("<snippet>%s</snippet>", + search_clean_snippet (c->snippet, c->search_term)); + snippet_doc = xmlParseDoc (BAD_CAST xmldoc); + g_free (xmldoc); + + if (!snippet_doc) + return; + + node = xmlDocGetRootElement (snippet_doc); + xmlUnlinkNode (node); + xmlAddChild (child, node); + xmlFreeDoc (snippet_doc); +} + +void +process_man_result (YelpSearchParser *parser, gchar *result, gchar **terms) +{ + gchar ** split = g_strsplit (result, "\n", -1); + gint i; + + for (i=0;split[i];i++) { + gchar ** line = g_strsplit (split[i], "(", 2); + gchar *filename = NULL; + gchar *desc = NULL; + xmlNode *child; + gchar *tmp = NULL; + gchar *after = NULL; + /*gchar *before = NULL;*/ + gchar *title = NULL; + /*gint i;*/ + + if (line == NULL || line[0] == NULL || line[1] == NULL) + continue; + + title = g_strdup (g_strstrip (line[0])); + after = strstr (line[1], ")"); + + tmp = g_strndup (line[1], after-line[1]); + + filename = g_strconcat ("man:", title, "(", tmp,")", NULL); + + after++; + g_free (tmp); + + tmp = g_strdup (g_strchug (after)); + after = tmp; after++; + desc = g_strdup (g_strchug (after)); + + child = xmlNewTextChild (parser->root, NULL, + BAD_CAST "result", NULL); + xmlSetProp (child, BAD_CAST "uri", BAD_CAST filename); + xmlSetProp (child, BAD_CAST "title", + BAD_CAST g_strconcat (title, + " manual page", NULL)); + + xmlNewChild (child, NULL, BAD_CAST "snippet", + BAD_CAST desc); + xmlNewChild (child, NULL, BAD_CAST "score", + BAD_CAST "0.1"); + g_free (tmp); + g_strfreev (line); + } + +} + +void +process_info_result (YelpSearchParser *parser, gchar *result, gchar **terms) +{ + gchar ** split = NULL; + gint i; + + split = g_strsplit (result, "\n", -1); + if (split == NULL) + return; + + for (i=0;split[i];i++) { + gchar ** line = NULL; + gchar *filename = NULL; + gchar *desc = NULL; + gchar *title = NULL; + xmlNode *child; + gchar *tmp; + gchar *tmp1; + gchar *file_name; + + line = g_strsplit (split[i], "--", 3); + if (g_strv_length (line) != 2) { + g_strfreev (line); + continue; + } + + /* First is the filename + * We gotta do some fiddling to get the actual filename + * we can use + */ + tmp = g_strdup (g_strchomp (line[0])); + tmp++; + tmp1 = strstr (tmp, "\""); + if (!tmp1) { + g_strfreev (line); + g_free (tmp); + continue; + } + file_name = g_strndup (tmp, tmp1-tmp); + tmp++; + tmp1 = strstr (tmp, ")"); + if (tmp1) + title = g_strndup (tmp, tmp1-tmp); + else { + title = g_strdup (++file_name); + --file_name; + } + tmp--; + tmp--; + filename = g_strconcat ("info:", file_name, NULL); + g_free (tmp); + g_free (file_name); + + /* Then the description */ + desc = g_strdup (g_strchug (line[1])); + + /* Now we add the result to the page */ + child = xmlNewTextChild (parser->root, NULL, + BAD_CAST "result", NULL); + xmlSetProp (child, BAD_CAST "uri", BAD_CAST filename); + xmlSetProp (child, BAD_CAST "title", + BAD_CAST g_strconcat (title, + " info page", NULL)); + + xmlNewChild (child, NULL, BAD_CAST "snippet", + BAD_CAST desc); + xmlNewChild (child, NULL, BAD_CAST "score", + BAD_CAST "0.05"); + g_strfreev (line); + g_free (title); + } + +} + +void +search_process_man (YelpSearchParser *parser, gchar **terms) +{ + gchar *command; + gchar *stdout_str = NULL; + gint exit_code; + gchar *tmp = NULL; + gchar *search = NULL; + + tmp = g_strescape (parser->search_terms, NULL); + tmp = g_strdelimit (tmp, "\'", '\''); + search = g_strconcat ("\"",tmp,"\"", NULL); + + command = g_strconcat("apropos ", search, NULL); + + if (g_spawn_command_line_sync (command, &stdout_str, NULL, + &exit_code, NULL) && exit_code == 0) { + process_man_result (parser, stdout_str, terms); + + } + g_free (tmp); + g_free (search); + g_free (stdout_str); + g_free (command); + + return; +} + +void +search_process_info (YelpSearchParser *parser, gchar **terms) +{ + gchar *command; + gchar *stdout_str = NULL; + gchar *stderr_str = NULL; + gchar *tmp; + gint exit_code; + + gchar *search = NULL; + + tmp = g_strescape (parser->search_terms, NULL); + tmp = g_strdelimit (tmp, "\'", '\''); + search = g_strconcat ("\"",tmp,"\"", NULL); + command = g_strconcat("info --apropos ", search, NULL); + + if (g_spawn_command_line_sync (command, &stdout_str, &stderr_str, + &exit_code, NULL) && + stdout_str != NULL) { + process_info_result (parser, stdout_str, terms); + } + g_free (tmp); + g_free (stdout_str); + g_free (stderr_str); + g_free (command); + + return; +} diff --git a/src/yelp-search-parser.h b/src/yelp-search-parser.h new file mode 100644 index 00000000..d0d25ac7 --- /dev/null +++ b/src/yelp-search-parser.h @@ -0,0 +1,36 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2007 Don Scorgie <Don@Scorgie.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Don Scorgie <Don@Scorgie.org> + */ + +#ifndef __YELP_SEARCH_PARSER_H__ +#define __YELP_SEARCH_PARSER_H__ + +#include <glib.h> +#include <libxml/tree.h> + +typedef struct _YelpSearchParser YelpSearchParser; + +YelpSearchParser * yelp_search_parser_new (void); +xmlDocPtr yelp_search_parser_process (YelpSearchParser *parser, + gchar *terms); +void yelp_search_parser_free (YelpSearchParser *parser); + +#endif /* __YELP_SEARCH_PARSER_H__ */ diff --git a/src/yelp-search.c b/src/yelp-search.c new file mode 100644 index 00000000..ef2ca9ff --- /dev/null +++ b/src/yelp-search.c @@ -0,0 +1,391 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2007 Don Scorgie <Don@Scorgie.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Don Scorgie <Don@Scorgie.org> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <libxml/tree.h> + +#include "yelp-error.h" +#include "yelp-search.h" +#include "yelp-search-parser.h" +#include "yelp-transform.h" +#include "yelp-debug.h" + +#define STYLESHEET DATADIR"/yelp/xslt/search2html.xsl" + +#define YELP_SEARCH_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_SEARCH, YelpSearchPriv)) + +typedef enum { + SEARCH_STATE_BLANK, /* Brand new, run transform as needed */ + SEARCH_STATE_PARSING, /* Parsing/transforming document, please wait */ + SEARCH_STATE_PARSED, /* All done, if we ain't got it, it ain't here */ + SEARCH_STATE_STOP /* Stop everything now, object to be disposed */ +} SearchState; + +struct _YelpSearchPriv { + gchar *search_terms; + SearchState state; + + GMutex *mutex; + GThread *thread; + + xmlDocPtr xmldoc; + + gboolean process_running; + gboolean transform_running; + + YelpTransform *transform; +}; + + +static void search_class_init (YelpSearchClass *klass); +static void search_init (YelpSearch *search); +static void search_try_dispose (GObject *object); +static void search_dispose (GObject *object); + +/* YelpDocument */ +static void search_request (YelpDocument *document, + gint req_id, + gboolean handled, + gchar *page_id, + YelpDocumentFunc func, + gpointer user_data); + +/* YelpTransform */ +static void transform_func (YelpTransform *transform, + YelpTransformSignal signal, + gpointer func_data, + YelpSearch *search); +static void transform_page_func (YelpTransform *transform, + gchar *page_id, + YelpSearch *search); +static void transform_final_func (YelpTransform *transform, + YelpSearch *search); + +/* Threaded */ +static void search_process (YelpSearch *search); + +static YelpDocumentClass *parent_class; + +GType +yelp_search_get_type (void) +{ + static GType type = 0; + if (!type) { + static const GTypeInfo info = { + sizeof (YelpSearchClass), + NULL, NULL, + (GClassInitFunc) search_class_init, + NULL, NULL, + sizeof (YelpSearch), + 0, + (GInstanceInitFunc) search_init, + }; + type = g_type_register_static (YELP_TYPE_DOCUMENT, + "YelpSearch", + &info, 0); + } + return type; +} + +static void +search_class_init (YelpSearchClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + YelpDocumentClass *document_class = YELP_DOCUMENT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->dispose = search_try_dispose; + + document_class->request = search_request; + document_class->cancel = NULL; + + g_type_class_add_private (klass, sizeof (YelpSearchPriv)); +} + +static void +search_init (YelpSearch *search) +{ + YelpSearchPriv *priv; + + priv = search->priv = YELP_SEARCH_GET_PRIVATE (search); + + priv->state = SEARCH_STATE_BLANK; + + priv->mutex = g_mutex_new (); +} + +static void +search_try_dispose (GObject *object) +{ + YelpSearchPriv *priv; + + g_assert (object != NULL && YELP_IS_SEARCH (object)); + priv = YELP_SEARCH (object)->priv; + + g_mutex_lock (priv->mutex); + if (priv->process_running || priv->transform_running) { + priv->state = SEARCH_STATE_STOP; + g_idle_add ((GSourceFunc) search_try_dispose, object); + g_mutex_unlock (priv->mutex); + } else { + g_mutex_unlock (priv->mutex); + search_dispose (object); + } +} + +static void +search_dispose (GObject *object) +{ + YelpSearch *search = YELP_SEARCH (object); + + g_free (search->priv->search_terms); + + if (search->priv->xmldoc) + xmlFreeDoc (search->priv->xmldoc); + + g_mutex_free (search->priv->mutex); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +/******************************************************************************/ + +YelpDocument * +yelp_search_new (gchar *filename) +{ + YelpSearch *search; + + g_return_val_if_fail (filename != NULL, NULL); + + search = (YelpSearch *) g_object_new (YELP_TYPE_SEARCH, NULL); + search->priv->search_terms = g_strdup (filename); + + debug_print (DB_FUNCTION, "entering\n"); + debug_print (DB_ARG, " filename = \"%s\"\n", filename); + + yelp_document_add_page_id (YELP_DOCUMENT (search), "x-yelp-index", "index"); + + return (YelpDocument *) search; +} + + +/******************************************************************************/ +/** YelpDocument **************************************************************/ + +static void +search_request (YelpDocument *document, + gint req_id, + gboolean handled, + gchar *page_id, + YelpDocumentFunc func, + gpointer user_data) +{ + YelpSearch *search; + YelpSearchPriv *priv; + YelpError *error; + + debug_print (DB_FUNCTION, "entering\n"); + debug_print (DB_ARG, " req_id = %i\n", req_id); + debug_print (DB_ARG, " page_id = \"%s\"\n", page_id); + + g_assert (document != NULL && YELP_IS_SEARCH (document)); + + if (handled) + return; + + search = YELP_SEARCH (document); + priv = search->priv; + + g_mutex_lock (priv->mutex); + + switch (priv->state) { + case SEARCH_STATE_BLANK: + priv->state = SEARCH_STATE_PARSING; + priv->process_running = TRUE; + priv->thread = g_thread_create ((GThreadFunc) search_process, search, FALSE, NULL); + break; + case SEARCH_STATE_PARSING: + break; + case SEARCH_STATE_PARSED: + case SEARCH_STATE_STOP: + /* Much bigger problems */ + error = yelp_error_new (_("Page not found"), + _("Could not process search")); + yelp_document_error_request (document, req_id, error); + break; + } + + g_mutex_unlock (priv->mutex); +} + + +/******************************************************************************/ +/** YelpTransform *************************************************************/ + +static void +transform_func (YelpTransform *transform, + YelpTransformSignal signal, + gpointer func_data, + YelpSearch *search) +{ + YelpSearchPriv *priv; + + debug_print (DB_FUNCTION, "entering\n"); + + g_assert (search != NULL && YELP_IS_SEARCH (search)); + + priv = search->priv; + + g_assert (transform == priv->transform); + + if (priv->state == SEARCH_STATE_STOP) { + switch (signal) { + case YELP_TRANSFORM_CHUNK: + g_free (func_data); + break; + case YELP_TRANSFORM_ERROR: + yelp_error_free ((YelpError *) func_data); + break; + case YELP_TRANSFORM_FINAL: + break; + } + yelp_transform_release (transform); + priv->transform = NULL; + priv->transform_running = FALSE; + return; + } + + switch (signal) { + case YELP_TRANSFORM_CHUNK: + transform_page_func (transform, (gchar *) func_data, search); + break; + case YELP_TRANSFORM_ERROR: + yelp_document_error_pending (YELP_DOCUMENT (search), (YelpError *) func_data); + yelp_transform_release (transform); + priv->transform = NULL; + priv->transform_running = FALSE; + break; + case YELP_TRANSFORM_FINAL: + transform_final_func (transform, search); + break; + } +} + +static void +transform_page_func (YelpTransform *transform, + gchar *page_id, + YelpSearch *search) +{ + YelpSearchPriv *priv; + gchar *content; + + debug_print (DB_FUNCTION, "entering\n"); + + priv = search->priv; + g_mutex_lock (priv->mutex); + + content = yelp_transform_eat_chunk (transform, page_id); + + yelp_document_add_page (YELP_DOCUMENT (search), page_id, content); + + g_free (page_id); + + g_mutex_unlock (priv->mutex); +} + +static void +transform_final_func (YelpTransform *transform, YelpSearch *search) +{ + YelpSearchPriv *priv = search->priv; + + debug_print (DB_FUNCTION, "entering\n"); + + g_mutex_lock (priv->mutex); + + yelp_transform_release (transform); + priv->transform = NULL; + priv->transform_running = FALSE; + + if (priv->xmldoc) + xmlFreeDoc (priv->xmldoc); + priv->xmldoc = NULL; + + g_mutex_unlock (priv->mutex); +} + + +/******************************************************************************/ +/** Threaded ******************************************************************/ + +static void +search_process (YelpSearch *search) +{ + YelpSearchPriv *priv; + YelpSearchParser *parser; + YelpError *error = NULL; + YelpDocument *document; + + debug_print (DB_FUNCTION, "entering\n"); + + g_assert (search != NULL && YELP_IS_SEARCH (search)); + g_object_ref (search); + priv = search->priv; + document = YELP_DOCUMENT (search); + + parser = yelp_search_parser_new (); + priv->xmldoc = yelp_search_parser_process (parser, priv->search_terms); + yelp_search_parser_free (parser); + + if (priv->xmldoc == NULL) { + error = yelp_error_new (_("Could not parse file"), + _("Bigger problems still")); + yelp_document_error_pending (document, error); + } + + g_mutex_lock (priv->mutex); + if (priv->state == SEARCH_STATE_STOP) { + g_mutex_unlock (priv->mutex); + goto done; + } + + priv->transform = yelp_transform_new (STYLESHEET, + (YelpTransformFunc) transform_func, + search); + priv->transform_running = TRUE; + + /* FIXME: we probably need to set our own params */ + yelp_transform_start (priv->transform, + priv->xmldoc, + NULL); + g_mutex_unlock (priv->mutex); + + done: + priv->process_running = FALSE; + g_object_unref (search); +} diff --git a/src/yelp-search.h b/src/yelp-search.h new file mode 100644 index 00000000..a6cec17e --- /dev/null +++ b/src/yelp-search.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2007 Don Scorgie <Don@Scorgie.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Don Scorgie <Don@Scorgie.org> + */ + +#ifndef __YELP_SEARCH_H__ +#define __YELP_SEARCH_H__ + +#include <glib-object.h> + +#include "yelp-document.h" + +#define YELP_TYPE_SEARCH (yelp_search_get_type ()) +#define YELP_SEARCH(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), YELP_TYPE_SEARCH, YelpSearch)) +#define YELP_SEARCH_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), YELP_TYPE_SEARCH, YelpSearchClass)) +#define YELP_IS_SEARCH(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), YELP_TYPE_SEARCH)) +#define YELP_IS_SEARCH_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), YELP_TYPE_SEARCH)) +#define YELP_SEARCH_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), YELP_TYPE_SEARCH, YelpSearchClass)) + +typedef struct _YelpSearch YelpSearch; +typedef struct _YelpSearchClass YelpSearchClass; +typedef struct _YelpSearchPriv YelpSearchPriv; + +struct _YelpSearch { + YelpDocument parent; + YelpSearchPriv *priv; +}; + +struct _YelpSearchClass { + YelpDocumentClass parent_class; +}; + +GType yelp_search_get_type (void); +YelpDocument * yelp_search_new (gchar *uri); + +#endif /* __YELP_SEARCH_H__ */ diff --git a/src/yelp-settings.c b/src/yelp-settings.c index 3fc2b45d..a5d1aa94 100644 --- a/src/yelp-settings.c +++ b/src/yelp-settings.c @@ -46,35 +46,24 @@ #define KEY_YELP_FIXED_FONT KEY_YELP_DIR "/fixed_font" static const gchar * const color_params[YELP_NUM_COLORS] = { - "yelp.color.fg", - "yelp.color.bg", - "yelp.color.anchor", - "yelp.color.rule", - "yelp.color.gray.fg", - "yelp.color.gray.bg", - "yelp.color.gray.bg.dark1", - "yelp.color.gray.bg.dark2", - "yelp.color.gray.bg.dark3", - "yelp.color.selected.fg", - "yelp.color.selected.bg", - "yelp.color.selected.bg.dark1", - "yelp.color.selected.bg.dark2", - "yelp.color.selected.bg.dark3", - "yelp.color.admon.fg", - "yelp.color.admon.bg", - "yelp.color.admon.bg.dark1", - "yelp.color.admon.bg.dark2", - "yelp.color.admon.bg.dark3" + "yelp.color.text", + "yelp.color.background", + "yelp.color.text_light", + "yelp.color.link", + "yelp.color.link_visited", + "yelp.color.gray_background", + "yelp.color.gray_border", + "theme.color.blue_background", + "theme.color.blue_border", + "theme.color.red_background", + "theme.color.red_border", + "theme.color.yellow_background", + "theme.color.yelllow_border" }; static const gchar * const icon_params[YELP_NUM_ICONS] = { - "yelp.icon.blockquote", - "yelp.icon.caution", - "yelp.icon.important", - "yelp.icon.note", - "yelp.icon.programlisting", - "yelp.icon.tip", - "yelp.icon.warning" + "theme.icon.admon.path", + "theme.icon.admon.size" }; static void settings_update (YelpSettingsType type); @@ -106,7 +95,6 @@ static GHookList *hook_lists[YELP_SETTINGS_NUM_TYPES]; static GtkSettings *gtk_settings; static GtkIconTheme *icon_theme; static gchar colors[YELP_NUM_COLORS][10]; -static gchar *icon_names[YELP_NUM_ICONS] = {NULL,}; static GtkWidget *prefs_dialog = NULL; static GtkWidget *system_fonts_widget = NULL; @@ -124,55 +112,6 @@ yelp_settings_init (void) { gint i; - for (i = 0; i < YELP_NUM_ICONS; i++) { - switch (i) { - case YELP_ICON_BLOCKQUOTE: - /* TRANSLATORS: - This is an image of the opening quote character used to watermark - blockquote elements. Different languages use different opening - quote characters, so the icon name is translatable. The name of - the icon should be "yelp-watermark-blockquote-XXXX", where XXXX - is the Unicode code point of the opening quote character. For - example, some languages use the double angle quotation mark, so - those would use "yelp-watermark-blockquote-00AB". However, the - image is not automagically created. Do not translate this to a - value if there isn't a corresponding icon in yelp/data/icons. - If you need an image created, contact the maintainers. - - Phew, now some notes on which character to use. Languages that - use guillemets (angle quotations) should use either 00AB or 00BB, - depending on whether the opening quotation is the left guillemet - or the right guillemet. Languages that use inverted comma style - quotations should use 201C, 201D, or 201E. Note that single - quotation marks don't make very nice watermarks. So if you use - single quotes as your primary (outer) quotation marks, you should - just use the corresponding double quote watermark. - */ - icon_names[i] = _("yelp-watermark-blockquote-201C"); - break; - case YELP_ICON_CAUTION: - icon_names[i] = "yelp-icon-caution"; - break; - case YELP_ICON_IMPORTANT: - icon_names[i] = "yelp-icon-important"; - break; - case YELP_ICON_NOTE: - icon_names[i] = "yelp-icon-note"; - break; - case YELP_ICON_PROGRAMLISTING: - icon_names[i] = "yelp-watermark-programlisting"; - break; - case YELP_ICON_TIP: - icon_names[i] = "yelp-icon-tip"; - break; - case YELP_ICON_WARNING: - icon_names[i] = "yelp-icon-warning"; - break; - default: - g_assert_not_reached (); - } - } - gconf_client = gconf_client_get_default (); gconf_client_add_dir (gconf_client, KEY_GNOME_DIR, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL); @@ -350,9 +289,9 @@ yelp_settings_get_icon (YelpIconType icon) g_return_val_if_fail (icon < YELP_NUM_ICONS, NULL); - info = gtk_icon_theme_lookup_icon (icon_theme, + /*info = gtk_icon_theme_lookup_icon (icon_theme, icon_names[icon], - 36, 0); + 36, 0);*/ return info; } @@ -665,68 +604,71 @@ settings_update (YelpSettingsType type) color->blue >> 8); if (color != &blue) gdk_color_free (color); + + color = NULL; + gtk_widget_style_get (widget, "visited-link-color", &color, NULL); + if (color) { + g_snprintf (colors[YELP_COLOR_ANCHOR], 8, + "#%02X%02X%02X", + color->red >> 8, + color->green >> 8, + color->blue >> 8); + gdk_color_free (color); + } + gtk_object_sink (GTK_OBJECT (widget)); - /* YELP_COLOR_RULE */ - g_snprintf (colors[YELP_COLOR_RULE], 8, + /* YELP_COLOR_FG_LIGHT */ + g_snprintf (colors[YELP_COLOR_FG_LIGHT], 8, "#%02X%02X%02X", - ((style->base[GTK_STATE_NORMAL].red >> 8) + - (style->bg[GTK_STATE_NORMAL].red >> 8) ) / 2, - ((style->base[GTK_STATE_NORMAL].green >> 8) + - (style->bg[GTK_STATE_NORMAL].green >> 8) ) / 2, - ((style->base[GTK_STATE_NORMAL].blue >> 8) + - (style->bg[GTK_STATE_NORMAL].blue >> 8) ) / 2); - - /* YELP_COLOR_GRAY_BG */ - for (i = 0; i < 4; i++) { - rval = ((4 - i) * (style->bg[GTK_STATE_NORMAL].red >> 8) + - i * max_text) / 4; - gval = ((4 - i) * (style->bg[GTK_STATE_NORMAL].green >> 8) + - i * max_text) / 4; - bval = ((4 - i) * (style->bg[GTK_STATE_NORMAL].blue >> 8) + - i * max_text) / 4; + style->text[GTK_STATE_PRELIGHT].red >> 8, + style->text[GTK_STATE_PRELIGHT].green >> 8, + style->text[GTK_STATE_PRELIGHT].blue >> 8); + + /* YELP_COLOR_GRAY_BG and border */ + for (i = 0; i < 2; i++) { + rval = ((2 - i) * (style->bg[GTK_STATE_NORMAL].red >> 8) + + i * max_text) / 2; + gval = ((2 - i) * (style->bg[GTK_STATE_NORMAL].green >> 8) + + i * max_text) / 2; + bval = ((2 - i) * (style->bg[GTK_STATE_NORMAL].blue >> 8) + + i * max_text) / 2; g_snprintf (colors[YELP_COLOR_GRAY_BG + i], 8, "#%02X%02X%02X", rval, gval, bval); } - /* YELP_COLOR_GRAY_FG */ - g_snprintf (colors[YELP_COLOR_GRAY_FG], 8, "%s", - colors[YELP_COLOR_GRAY_BG_DARK3]); - - /* YELP_COLOR_SELECTED_FG */ - g_snprintf (colors[YELP_COLOR_SELECTED_FG], 8, - "#%02X%02X%02X", - style->text[GTK_STATE_SELECTED].red >> 8, - style->text[GTK_STATE_SELECTED].green >> 8, - style->text[GTK_STATE_SELECTED].blue >> 8); - - /* YELP_COLOR_SELECTED_BG */ - for (i = 0; i < 4; i++) { - rval = ((4 - i) * (style->bg[GTK_STATE_SELECTED].red >> 8) + - i * max_text) / 4; - gval = ((4 - i) * (style->bg[GTK_STATE_SELECTED].green >> 8) + - i * max_text) / 4; - bval = ((4 - i) * (style->bg[GTK_STATE_SELECTED].blue >> 8) + - i * max_text) / 4; - g_snprintf (colors[YELP_COLOR_SELECTED_BG + i], 8, + /* YELP_COLOR_BLUE_BG and border */ + for (i = 0; i < 2; i++) { + rval = (i * max_base) / 2; + gval = (i * max_base) / 2; + bval = ((2 - i) * (style->bg[GTK_STATE_NORMAL].blue >> 8) + + i * max_base) / 2; + g_snprintf (colors[YELP_COLOR_BLUE_BG + (2 - i)], 8, "#%02X%02X%02X", rval, gval, bval); } - /* YELP_COLOR_ADMON_FG */ - g_snprintf (colors[YELP_COLOR_ADMON_FG], 8, "%s", - colors[YELP_COLOR_GRAY_BG_DARK3]); - - /* YELP_COLOR_ADMON_BG */ - for (i = 0; i < 4; i++) { - gint mult = max_base + ((i * (max_base - max_text)) / 3); - rval = ((255 * mult) / 255); - gval = ((245 * mult) / 255); - bval = ((207 * mult) / 255); + /* YELP_COLOR_RED_BG and border */ + for (i = 0; i < 2; i++) { + rval = ((2 - i) * (style->bg[GTK_STATE_NORMAL].red >> 8) + + i * max_base) / 2; + gval = (i * max_base) / 2; + bval = (i * max_base) / 2; + g_snprintf (colors[YELP_COLOR_RED_BG + (2 - i)], 8, + "#%02X%02X%02X", rval, gval, bval); + } - g_snprintf (colors[YELP_COLOR_ADMON_BG + i], 8, + /* YELP_COLOR_YELLOW_BG and border */ + for (i = 0; i < 2; i++) { + rval = ((2 - i) * (style->bg[GTK_STATE_NORMAL].red >> 8) + + i * max_base) / 2; + gval = ((2 - i) * (style->bg[GTK_STATE_NORMAL].green >> 8) + + i * max_base) / 2; + bval = (i * max_base) / 2; + g_snprintf (colors[YELP_COLOR_YELLOW_BG + (2 - i)], 8, "#%02X%02X%02X", rval, gval, bval); } + g_object_unref (G_OBJECT (style)); } @@ -740,34 +682,27 @@ yelp_settings_params (gchar ***params, gint *params_i, gint *params_max) { - GtkIconInfo *icon_info; - gchar *icon_file; - gint colors_i, icons_i; + /*GtkIconInfo *icon_info; + gchar *icon_file;*/ + gint colors_i /*, icons_i*/; if ((*params_i + 2 * (YELP_NUM_COLORS + YELP_NUM_ICONS)) >= *params_max) { *params_max += 2 * (YELP_NUM_COLORS + YELP_NUM_ICONS); *params = g_renew (gchar *, *params, *params_max); } - for (colors_i = 0; colors_i < YELP_NUM_COLORS; colors_i++) { + for (colors_i = 0; colors_i < YELP_NUM_COLORS - 1; colors_i++) { (*params)[(*params_i)++] = (gchar *) color_params[colors_i]; (*params)[(*params_i)++] = g_strdup_printf ("\"%s\"", yelp_settings_get_color (colors_i)); } - for (icons_i = 0; icons_i < YELP_NUM_ICONS; icons_i++) { - (*params)[(*params_i)++] = (gchar *) icon_params[icons_i]; - - icon_info = yelp_settings_get_icon (icons_i); - if (icon_info) { - icon_file = (gchar *) gtk_icon_info_get_filename (icon_info); - if (icon_file) - (*params)[(*params_i)++] = g_strdup_printf ("\"%s\"", icon_file); - else - (*params)[(*params_i)++] = g_strdup ("\"\""); - gtk_icon_info_free (icon_info); - } else { - (*params)[(*params_i)++] = g_strdup ("\"\""); - } - } + /* Icon Path */ + (*params)[(*params_i)++] = (gchar *) icon_params[0]; + (*params)[(*params_i)++] = (gchar *) g_strdup_printf ("\"%s\"", GDU_ICON_PATH); + + /* Icon Size */ + (*params)[(*params_i)++] = (gchar *) icon_params[1]; + (*params)[(*params_i)++] = (gchar *) g_strdup_printf ("\"%d\"", 48); + } diff --git a/src/yelp-settings.h b/src/yelp-settings.h index 8d6f1a30..6c3cf97d 100644 --- a/src/yelp-settings.h +++ b/src/yelp-settings.h @@ -53,34 +53,24 @@ typedef enum { typedef enum { YELP_COLOR_FG = 0, YELP_COLOR_BG, + YELP_COLOR_FG_LIGHT, YELP_COLOR_ANCHOR, - YELP_COLOR_RULE, - YELP_COLOR_GRAY_FG, + YELP_COLOR_ANCHOR_VISITED, YELP_COLOR_GRAY_BG, - YELP_COLOR_GRAY_BG_DARK1, - YELP_COLOR_GRAY_BG_DARK2, - YELP_COLOR_GRAY_BG_DARK3, - YELP_COLOR_SELECTED_FG, - YELP_COLOR_SELECTED_BG, - YELP_COLOR_SELECTED_BG_DARK1, - YELP_COLOR_SELECTED_BG_DARK2, - YELP_COLOR_SELECTED_BG_DARK3, - YELP_COLOR_ADMON_FG, - YELP_COLOR_ADMON_BG, - YELP_COLOR_ADMON_BG_DARK1, - YELP_COLOR_ADMON_BG_DARK2, - YELP_COLOR_ADMON_BG_DARK3, + YELP_COLOR_GRAY_BORDER, + YELP_COLOR_BLUE_BG, + YELP_COLOR_BLUE_BORDER, + YELP_COLOR_RED_BG, + YELP_COLOR_RED_BORDER, + YELP_COLOR_YELLOW_BG, + YELP_COLOR_YELLOW_BORDER, + YELP_FORCE, YELP_NUM_COLORS } YelpColorType; typedef enum { - YELP_ICON_BLOCKQUOTE = 0, - YELP_ICON_CAUTION, - YELP_ICON_IMPORTANT, - YELP_ICON_NOTE, - YELP_ICON_PROGRAMLISTING, - YELP_ICON_TIP, - YELP_ICON_WARNING, + YELP_ICON_ADMON_PATH, + YELP_ICON_ADMON_SIZE, YELP_NUM_ICONS } YelpIconType; diff --git a/src/yelp-toc-pager.c b/src/yelp-toc-pager.c index 0f1a1852..624a6ebf 100644 --- a/src/yelp-toc-pager.c +++ b/src/yelp-toc-pager.c @@ -45,6 +45,10 @@ #include <libxslt/extensions.h> #include <libxslt/xsltInternals.h> #include <libxslt/xsltutils.h> +#include <spoon.h> +#include <spoon-reg-utils.h> +#include <spoon-info.h> +#include <spoon-man.h> #include "yelp-debug.h" #include "yelp-error.h" @@ -122,6 +126,7 @@ static gboolean toc_process_pending (YelpTocPager *pager); static gboolean process_read_menu (YelpTocPager *pager); +static gboolean process_xslt (YelpTocPager *pager); static gboolean process_read_scrollkeeper (YelpTocPager *pager, gchar *content_list); static gboolean process_omf_pending (YelpTocPager *pager); @@ -460,13 +465,14 @@ toc_process_pending (YelpTocPager *pager) YelpTocPagerPriv *priv = pager->priv; static ProcessFunction process_funcs[] = { process_read_menu, - process_omf_pending, + /*process_omf_pending,*/ #ifdef ENABLE_MAN process_mandir_pending, #endif #ifdef ENABLE_INFO process_info_pending, #endif + /* process_xslt, */ process_cleanup, NULL }; @@ -565,7 +571,7 @@ sk_characters (void *pager, static gboolean process_read_scrollkeeper (YelpTocPager *pager, gchar *content_list) { - static xmlSAXHandler sk_sax_handler = { NULL, }; + static xmlSAXHandler sk_sax_handler = { 0, }; if (!sk_sax_handler.startElement) { sk_sax_handler.startElement = sk_startElement; @@ -811,13 +817,12 @@ process_omf_pending (YelpTocPager *pager) * it doesn't exist, then we create a list of omf files to process * with the process_read_scrollkeeper() function */ if (first_call) { - const gchar * const * langs = g_get_language_names (); - first_call = FALSE; sk_file = g_build_filename (yelp_dot_dir(), "sk-content-list.last", NULL); /* get current language */ + const gchar * const * langs = g_get_language_names (); if (langs && langs[0]) lang = (gchar *) langs[0]; else @@ -1326,9 +1331,65 @@ create_toc_from_index (YelpTocPager *pager, gchar *index_file) return 1; } +static int +spoon_add_man_document (SpoonManEntry *entry, void *user_data) +{ + xmlNodePtr node = (xmlNodePtr) user_data; + xmlNodePtr new; + gchar tmp[255]; + new = xmlNewChild (node, NULL, BAD_CAST "doc", NULL); + g_sprintf (&tmp, "man:%s", entry->path); + + xmlNewNsProp (new, NULL, BAD_CAST "href", BAD_CAST tmp); + xmlNewTextChild (new, NULL, BAD_CAST "title", BAD_CAST entry->name); + if (entry->comment) + xmlNewTextChild (new, NULL, BAD_CAST "description", BAD_CAST entry->comment); + return TRUE; +} + static gboolean process_mandir_pending (YelpTocPager *pager) { + xmlNodePtr node = NULL; + xmlNodePtr cat_node = NULL; + xmlNodePtr mynode = NULL; + char **categories = NULL; + char **cat_iter = NULL; + int sectno = 0; + YelpTocPagerPriv * priv = pager->priv; + int i, j; + xmlXPathContextPtr xpath; + xmlXPathObjectPtr obj; + + priv->man_doc = xmlCtxtReadFile (priv->parser, DATADIR "/yelp/man.xml", NULL, + XML_PARSE_NOBLANKS | XML_PARSE_NOCDATA | + XML_PARSE_NOENT | XML_PARSE_NOERROR | + XML_PARSE_NONET ); + + xpath = xmlXPathNewContext (priv->man_doc); + obj = xmlXPathEvalExpression (BAD_CAST "//toc", xpath); + + for (i = 0; i < obj->nodesetval->nodeNr; i++) { + xmlNodePtr node = obj->nodesetval->nodeTab[i]; + xmlChar *sect = xmlGetProp (node, BAD_CAST "sect"); + + if (sect) { + gchar **sects = g_strsplit ((gchar *)sect, " ", 0); + + cat_node = xmlNewChild (node, NULL, BAD_CAST "toc", + NULL); + for (j = 0; sects[j] != NULL; j++) + spoon_man_for_each_in_category (sects[j], spoon_add_man_document, node); + g_strfreev (sects); + } + xmlFree (sect); + xml_trim_titles (node, BAD_CAST "title"); + xml_trim_titles (node, BAD_CAST "description"); + } + xmlXPathFreeObject (obj); + xmlXPathFreeContext (xpath); + +#if 0 static gchar *index_file = NULL; gchar *filename = NULL; gchar *dirname = NULL; @@ -1341,6 +1402,7 @@ process_mandir_pending (YelpTocPager *pager) xmlXPathContextPtr xpath; xmlXPathObjectPtr obj; + /* NOTE: this document is free()'d at the end of the process_xslt function */ priv->man_doc = xmlCtxtReadFile (priv->parser, DATADIR "/yelp/man.xml", NULL, XML_PARSE_NOBLANKS | XML_PARSE_NOCDATA | XML_PARSE_NOENT | XML_PARSE_NOERROR | @@ -1606,13 +1668,103 @@ process_mandir_pending (YelpTocPager *pager) } return TRUE; +#endif + + mynode = xmlCopyNode (xmlDocGetRootElement (priv->man_doc), 1); + xmlAddChild (xmlDocGetRootElement (priv->toc_doc), mynode); + + xmlFreeDoc (priv->man_doc); + + return FALSE; } #endif // ENABLE_MAN #ifdef ENABLE_INFO + +static int +spoon_info_add_document (SpoonInfoEntry *entry, void *user_data) +{ + xmlNodePtr node = (xmlNodePtr) user_data; + xmlNodePtr new; + gchar tmp[255]; + new = xmlNewChild (node, NULL, BAD_CAST "doc", NULL); + if (entry->section) + g_sprintf(&tmp, "info:%s#%s", entry->name, entry->section); + else + g_sprintf(&tmp, "info:%s", entry->name); + xmlNewNsProp (new, NULL, BAD_CAST "href", BAD_CAST tmp); + xmlNewTextChild (new, NULL, BAD_CAST "title", BAD_CAST entry->name); + xmlNewTextChild (new, NULL, BAD_CAST "description", BAD_CAST entry->comment); + return TRUE; + +} + + static gboolean process_info_pending (YelpTocPager *pager) { + xmlNodePtr node = NULL; + xmlNodePtr cat_node = NULL; + xmlNodePtr mynode = NULL; + char **categories = NULL; + char **cat_iter = NULL; + int sectno = 0; + YelpTocPagerPriv * priv = pager->priv; + int i; + xmlXPathContextPtr xpath; + xmlXPathObjectPtr obj; + + priv->info_doc = xmlCtxtReadFile (priv->parser, + DATADIR "/yelp/info.xml", NULL, + XML_PARSE_NOBLANKS | + XML_PARSE_NOCDATA | + XML_PARSE_NOENT | + XML_PARSE_NOERROR | + XML_PARSE_NONET ); + + xpath = xmlXPathNewContext (priv->info_doc); + obj = xmlXPathEvalExpression (BAD_CAST "//toc", xpath); + node = obj->nodesetval->nodeTab[0]; + for (i=0; i < obj->nodesetval->nodeNr; i++) { + xmlNodePtr tmpnode = obj->nodesetval->nodeTab[i]; + xml_trim_titles (tmpnode, BAD_CAST "title"); + xml_trim_titles (tmpnode, BAD_CAST "description"); + } + xmlXPathFreeObject (obj); + xmlXPathFreeContext (xpath); + + categories = spoon_info_get_categories (); + cat_iter = categories; + + while (cat_iter && *cat_iter) { + char *tmp; + + cat_node = xmlNewChild (node, NULL, BAD_CAST "toc", + NULL); + tmp = g_strdup_printf ("%d", sectno); + xmlNewNsProp (cat_node, NULL, BAD_CAST "sect", + BAD_CAST tmp); + g_free (tmp); + tmp = g_strdup_printf ("infosect%d", sectno); + xmlNewNsProp (cat_node, NULL, BAD_CAST "id", + BAD_CAST tmp); + g_free (tmp); + sectno++; + xmlNewTextChild (cat_node, NULL, BAD_CAST "title", + BAD_CAST *cat_iter); + + spoon_info_for_each_in_category (*cat_iter, spoon_info_add_document, + cat_node); + cat_iter++; + } + + mynode = xmlCopyNode (xmlDocGetRootElement (priv->info_doc), 1); + xmlAddChild (xmlDocGetRootElement (priv->toc_doc), mynode); + + xmlFreeDoc (priv->info_doc); + + +#if 0 gchar ** info_paths = yelp_get_info_paths (); int i = 0; gchar *filename = NULL; @@ -1625,7 +1777,7 @@ process_info_pending (YelpTocPager *pager) YelpTocPagerPriv *priv = YELP_TOC_PAGER (pager)->priv; xmlNodePtr node = NULL; - + printf ("Info parsing\n"); for (i=0; info_paths[i]; i++) { filename = g_strconcat (info_paths[i], "/dir", NULL); @@ -1790,10 +1942,28 @@ process_info_pending (YelpTocPager *pager) xmlFreeDoc (priv->info_doc); +#endif return FALSE; } #endif /* ENABLE_INFO */ +static int +spoon_add_document (void *reg, void * user_data) +{ + xmlNodePtr node = (xmlNodePtr) user_data; + SpoonReg *r = (SpoonReg *) reg; + xmlNodePtr new; + gchar tmp[10]; + + new = xmlNewChild (node, NULL, BAD_CAST "doc", NULL); + xmlNewNsProp (new, NULL, BAD_CAST "href", BAD_CAST r->uri); + xmlNewTextChild (new, NULL, BAD_CAST "title", BAD_CAST r->name); + xmlNewTextChild (new, NULL, BAD_CAST "description", BAD_CAST r->comment); + g_sprintf (&tmp, "%d", r->weight); + xmlNewNsProp (new, NULL, BAD_CAST "weight", BAD_CAST tmp); + return FALSE; +} + static gboolean process_read_menu (YelpTocPager *pager) { @@ -1906,9 +2076,12 @@ process_read_menu (YelpTocPager *pager) BAD_CAST "subject")) { xmlChar *cat = xmlTextReaderGetAttribute (reader, BAD_CAST "category"); - g_hash_table_insert (priv->category_hash, + /*g_hash_table_insert (priv->category_hash, g_strdup ((gchar *) cat), - node); + node);*/ + spoon_for_each_in_category (spoon_add_document, + (char *) cat, + (void *) node); xmlFree (cat); } else if (!xmlStrcmp (xmlTextReaderConstLocalName (reader), @@ -1931,6 +2104,92 @@ process_read_menu (YelpTocPager *pager) } static gboolean +process_xslt (YelpTocPager *pager) +{ + GError *error = NULL; + xmlDocPtr outdoc = NULL; + YelpTocPagerPriv *priv = pager->priv; + gchar **params = NULL; + gint params_i = 0; + gint params_max = 10; + GtkIconInfo *info; + GtkIconTheme *theme = (GtkIconTheme *) yelp_settings_get_icon_theme (); + + if (!priv->toc_doc) + return FALSE; + + /* only create and parse the stylesheet on the first call to this function */ + if (!priv->stylesheet) { + priv->stylesheet = xsltParseStylesheetFile (BAD_CAST TOC_STYLESHEET); + } + + if (!priv->stylesheet) { + g_set_error (&error, YELP_ERROR, YELP_ERROR_PROC, + _("The table of contents could not be processed. The " + "file ‘%s’ is either missing or is not a valid XSLT " + "stylesheet."), + TOC_STYLESHEET); + yelp_pager_error (YELP_PAGER (pager), error); + goto done; + } + + priv->transformContext = xsltNewTransformContext (priv->stylesheet, + priv->toc_doc); + priv->transformContext->_private = pager; + xsltRegisterExtElement (priv->transformContext, + BAD_CAST "document", + BAD_CAST YELP_NAMESPACE, + (xsltTransformFunction) xslt_yelp_document); + + params = g_new0 (gchar *, params_max); + yelp_settings_params (¶ms, ¶ms_i, ¶ms_max); + + if ((params_i + 10) >= params_max - 1) { + params_max += 10; + params = g_renew (gchar *, params, params_max); + } + + info = gtk_icon_theme_lookup_icon (theme, "yelp-icon-big", 192, 0); + if (info) { + params[params_i++] = "help_icon"; + params[params_i++] = g_strdup_printf ("\"%s\"", + gtk_icon_info_get_filename (info)); + params[params_i++] = "help_icon_size"; + params[params_i++] = g_strdup_printf ("%i", + gtk_icon_info_get_base_size (info)); + gtk_icon_info_free (info); + } + + params[params_i++] = NULL; + + outdoc = xsltApplyStylesheetUser (priv->stylesheet, + priv->toc_doc, + (const gchar **)params, NULL, NULL, + priv->transformContext); + /* Don't do this */ + g_signal_emit_by_name (pager, "finish"); + + done: + if (params) { + for (params_i = 0; params[params_i] != NULL; params_i++) + if (params_i % 2 == 1) + g_free ((gchar *) params[params_i]); + } + if (outdoc) + xmlFreeDoc (outdoc); + if (priv->toc_doc) { + xmlFreeDoc (priv->toc_doc); + priv->toc_doc = NULL; + } + if (priv->transformContext) { + xsltFreeTransformContext (priv->transformContext); + priv->transformContext = NULL; + } + + return FALSE; +} + +static gboolean process_cleanup (YelpTocPager *pager) { YelpTocPagerPriv *priv = pager->priv; @@ -1974,8 +2233,7 @@ toc_add_doc_info (YelpTocPager *pager, YelpDocInfo *doc_info) xmlNodePtr node; xmlNodePtr new; gchar *text; - const gchar *category; - YelpTocPagerPriv *priv = pager->priv; + gchar *category; g_return_if_fail (pager != NULL); if (doc_info == NULL) @@ -1990,6 +2248,8 @@ toc_add_doc_info (YelpTocPager *pager, YelpDocInfo *doc_info) return; } + YelpTocPagerPriv *priv = pager->priv; + g_hash_table_insert (priv->unique_hash, (gchar *) yelp_doc_info_get_id (doc_info), doc_info); diff --git a/src/yelp-toc.c b/src/yelp-toc.c new file mode 100644 index 00000000..973aa12a --- /dev/null +++ b/src/yelp-toc.c @@ -0,0 +1,833 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2003-2007 Shaun McCance <shaunm@gnome.org> + * 2007 Don Scorgie <Don@Scorgie.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Shaun McCance <shaunm@gnome.org> + * Don Scorgie <Don@Scorgie.org> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <libxml/parser.h> +#include <libxml/parserInternals.h> +#include <libxml/xinclude.h> +#include <libxml/xmlreader.h> +#include <rarian.h> +#include <rarian-info.h> +#include <rarian-man.h> + +#include "yelp-error.h" +#include "yelp-toc.h" +#include "yelp-settings.h" +#include "yelp-transform.h" +#include "yelp-debug.h" + +#define STYLESHEET DATADIR"/yelp/xslt/toc2html.xsl" + +#define YELP_TOC_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_TOC, YelpTocPriv)) + +typedef enum { + TOC_STATE_BLANK, /* Brand new, run transform as needed */ + TOC_STATE_PARSING, /* Parsing/transforming document, please wait */ + TOC_STATE_PARSED, /* All done, if we ain't got it, it ain't here */ + TOC_STATE_STOP /* Stop everything now, object to be disposed */ +} TocState; + +struct _YelpTocPriv { + TocState state; + + GMutex *mutex; + GThread *thread; + + gboolean process_running; + gboolean transform_running; + gboolean man_processed; + gboolean info_processed; + + YelpTransform *transform; + + xmlDocPtr xmldoc; + xmlNodePtr xmlcur; + gchar *cur_page_id; + gchar *cur_prev_id; +}; + + +static YelpDocument *toc_doc = NULL; + +static void toc_class_init (YelpTocClass *klass); +static void toc_init (YelpToc *toc); +static void toc_try_dispose (GObject *object); +static void toc_dispose (GObject *object); + +/* YelpDocument */ +static void toc_request (YelpDocument *document, + gint req_id, + gboolean handled, + gchar *page_id, + YelpDocumentFunc func, + gpointer user_data); + +/* YelpTransform */ +static void transform_func (YelpTransform *transform, + YelpTransformSignal signal, + gpointer func_data, + YelpToc *toc); +static void transform_page_func (YelpTransform *transform, + gchar *page_id, + YelpToc *toc); +static void transform_final_func (YelpTransform *transform, + YelpToc *toc); + +/* Threaded */ +static void toc_process (YelpToc *toc); +static void toc_process_info (YelpToc *toc); +static void toc_process_man (YelpToc *toc); +static void xml_trim_titles (xmlNodePtr node, + xmlChar * nodetype); + +static YelpDocumentClass *parent_class; + +GType +yelp_toc_get_type (void) +{ + static GType type = 0; + if (!type) { + static const GTypeInfo info = { + sizeof (YelpTocClass), + NULL, NULL, + (GClassInitFunc) toc_class_init, + NULL, NULL, + sizeof (YelpToc), + 0, + (GInstanceInitFunc) toc_init, + }; + type = g_type_register_static (YELP_TYPE_DOCUMENT, + "YelpToc", + &info, 0); + } + return type; +} + +static void +toc_class_init (YelpTocClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + YelpDocumentClass *document_class = YELP_DOCUMENT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->dispose = toc_try_dispose; + + document_class->request = toc_request; + document_class->cancel = NULL; + + g_type_class_add_private (klass, sizeof (YelpTocPriv)); +} + +static void +toc_init (YelpToc *toc) +{ + YelpTocPriv *priv; + priv = toc->priv = YELP_TOC_GET_PRIVATE (toc); + + priv->state = TOC_STATE_BLANK; + + priv->man_processed = FALSE; + priv->info_processed = FALSE; + + priv->mutex = g_mutex_new (); +} + +static void +toc_try_dispose (GObject *object) +{ + YelpTocPriv *priv; + + g_assert (object != NULL && YELP_IS_TOC (object)); + + priv = YELP_TOC (object)->priv; + + g_mutex_lock (priv->mutex); + if (priv->process_running || priv->transform_running) { + priv->state = TOC_STATE_STOP; + g_idle_add ((GSourceFunc) toc_try_dispose, object); + g_mutex_unlock (priv->mutex); + } else { + g_mutex_unlock (priv->mutex); + toc_dispose (object); + } +} + +static void +toc_dispose (GObject *object) +{ + YelpToc *toc = YELP_TOC (object); + + if (toc->priv->xmldoc) + xmlFreeDoc (toc->priv->xmldoc); + + g_free (toc->priv->cur_page_id); + g_free (toc->priv->cur_prev_id); + + g_mutex_free (toc->priv->mutex); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +/******************************************************************************/ + +YelpDocument * +yelp_toc_new (void) +{ + debug_print (DB_FUNCTION, "entering\n"); + + if (toc_doc == NULL) { + toc_doc = (YelpDocument *) g_object_new (YELP_TYPE_TOC, NULL); + } + + return (YelpDocument *) toc_doc; +} + +YelpDocument * +yelp_toc_get (void) +{ + debug_print (DB_FUNCTION, "entering\n"); + + if (toc_doc == NULL) { + toc_doc = (YelpDocument *) g_object_new (YELP_TYPE_TOC, NULL); + } + + return (YelpDocument *) toc_doc; +} + + +/******************************************************************************/ +/** YelpDocument **************************************************************/ + +static void +toc_request (YelpDocument *document, + gint req_id, + gboolean handled, + gchar *page_id, + YelpDocumentFunc func, + gpointer user_data) +{ + YelpToc *toc; + YelpTocPriv *priv; + YelpError *error; + + debug_print (DB_FUNCTION, "entering\n"); + debug_print (DB_ARG, " req_id = %i\n", req_id); + debug_print (DB_ARG, " page_id = \"%s\"\n", page_id); + + g_assert (document != NULL && YELP_IS_TOC (document)); + + if (handled) + return; + + toc = YELP_TOC (document); + priv = toc->priv; + + g_mutex_lock (priv->mutex); + + switch (priv->state) { + case TOC_STATE_BLANK: + priv->state = TOC_STATE_PARSING; + priv->process_running = TRUE; + priv->thread = g_thread_create ((GThreadFunc) toc_process, + toc, FALSE, NULL); + break; + case TOC_STATE_PARSING: + break; + case TOC_STATE_PARSED: + case TOC_STATE_STOP: + error = yelp_error_new (_("Page not found"), + _("The page %s was not found in the TOC."), + page_id); + yelp_document_error_request (document, req_id, error); + break; + } + + g_mutex_unlock (priv->mutex); +} + +/******************************************************************************/ +/** YelpTransform *************************************************************/ + +static void +transform_func (YelpTransform *transform, + YelpTransformSignal signal, + gpointer func_data, + YelpToc *toc) +{ + YelpTocPriv *priv; + + debug_print (DB_FUNCTION, "entering\n"); + + g_assert (toc != NULL && YELP_IS_TOC (toc)); + + priv = toc->priv; + + g_assert (transform == priv->transform); + + if (priv->state == TOC_STATE_STOP) { + switch (signal) { + case YELP_TRANSFORM_CHUNK: + g_free (func_data); + break; + case YELP_TRANSFORM_ERROR: + yelp_error_free ((YelpError *) func_data); + break; + case YELP_TRANSFORM_FINAL: + break; + } + yelp_transform_release (transform); + priv->transform = NULL; + priv->transform_running = FALSE; + return; + } + + switch (signal) { + case YELP_TRANSFORM_CHUNK: + transform_page_func (transform, (gchar *) func_data, toc); + break; + case YELP_TRANSFORM_ERROR: + yelp_document_error_pending (YELP_DOCUMENT (toc), (YelpError *) func_data); + yelp_transform_release (transform); + priv->transform = NULL; + priv->transform_running = FALSE; + break; + case YELP_TRANSFORM_FINAL: + transform_final_func (transform, toc); + break; + } +} + +static void +transform_page_func (YelpTransform *transform, + gchar *page_id, + YelpToc *toc) +{ + YelpTocPriv *priv; + gchar *content; + debug_print (DB_FUNCTION, "entering\n"); + + priv = toc->priv; + g_mutex_lock (priv->mutex); + + content = yelp_transform_eat_chunk (transform, page_id); + +#if 0 /* Used for debugging */ + gchar * filename = NULL; + filename = g_strdup_printf ("out/%s.html", page_id); + g_file_set_contents (filename, content, -1, NULL); +#endif + + yelp_document_add_page (YELP_DOCUMENT (toc), page_id, content); + + g_free (page_id); + + g_mutex_unlock (priv->mutex); +} + +static void +transform_final_func (YelpTransform *transform, YelpToc *toc) +{ + YelpError *error; + YelpTocPriv *priv = toc->priv; + + debug_print (DB_FUNCTION, "entering\n"); + g_mutex_lock (priv->mutex); + + error = yelp_error_new (_("Page not found"), + _("The requested page was not found in the TOC.")); + yelp_document_error_pending (YELP_DOCUMENT (toc), error); + + yelp_transform_release (priv->transform); + priv->transform = NULL; + + priv->transform_running = FALSE; + + if (priv->xmldoc) { + xmlFreeDoc (priv->xmldoc); + } + priv->xmldoc = NULL; + + g_mutex_unlock (priv->mutex); +} + + +/******************************************************************************/ +/** Threaded ******************************************************************/ + +static int +rrn_add_document (void *reg, void * user_data) +{ + xmlNodePtr node = (xmlNodePtr) user_data; + RrnReg *r = (RrnReg *) reg; + xmlNodePtr new; + gchar *tmp; + new = xmlNewChild (node, NULL, BAD_CAST "doc", NULL); + xmlNewNsProp (new, NULL, BAD_CAST "href", BAD_CAST r->uri); + xmlNewTextChild (new, NULL, BAD_CAST "title", BAD_CAST r->name); + xmlNewTextChild (new, NULL, BAD_CAST "description", BAD_CAST r->comment); + tmp = g_strdup_printf ("%d", r->weight); + xmlNewNsProp (new, NULL, BAD_CAST "weight", BAD_CAST tmp); + g_free (tmp); + return FALSE; +} + +static void +toc_process (YelpToc *toc) +{ + YelpTocPriv *priv; + YelpError *error = NULL; + xmlParserCtxtPtr parserCtxt = NULL; + YelpDocument *document; + gint params_i = 0; + gint params_max = 10; + gchar **params = NULL; + GtkIconInfo *info = NULL ; + GtkIconTheme *theme = (GtkIconTheme *) yelp_settings_get_icon_theme (); + + GThread *info_thread; + GThread *man_thread; + xmlTextReaderPtr reader; + xmlXPathContextPtr xpath; + xmlXPathObjectPtr obj; + gint i, ret; + debug_print (DB_FUNCTION, "entering\n"); + + g_assert (toc != NULL && YELP_IS_TOC (toc)); + g_object_ref (toc); + priv = toc->priv; + document = YELP_DOCUMENT (toc); + + parserCtxt = xmlNewParserCtxt (); + priv->xmldoc = xmlCtxtReadFile (parserCtxt, + (const char *) DATADIR "/yelp/toc.xml", NULL, + XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA | + XML_PARSE_NOENT | XML_PARSE_NONET ); + + if (priv->xmldoc == NULL) { + error = yelp_error_new (_("Could not parse file"), + _("The ‘âTOC file€™ " + "could not be parsed because it is" + " not a well-formed XML document.")); + yelp_document_error_pending (document, error); + goto done; + } + + xpath = xmlXPathNewContext (priv->xmldoc); + obj = xmlXPathEvalExpression (BAD_CAST "//toc", xpath); + + for (i = 0; i < obj->nodesetval->nodeNr; i++) { + xmlNodePtr node = obj->nodesetval->nodeTab[i]; + xmlChar *icon = NULL; + + xml_trim_titles (node, BAD_CAST "title"); + xml_trim_titles (node, BAD_CAST "description"); + + icon = xmlGetProp (node, BAD_CAST "icon"); + if (icon) { + info = gtk_icon_theme_lookup_icon (theme, (gchar *) icon, 48, 0); + if (info) { + xmlNodePtr new = xmlNewChild (node, NULL, BAD_CAST "icon", + NULL); + xmlNewNsProp (new, NULL, BAD_CAST "file", + BAD_CAST gtk_icon_info_get_filename (info)); + gtk_icon_info_free (info); + } + } + xmlFree (icon); + + } + xmlXPathFreeObject (obj); + + reader = xmlReaderForFile (DATADIR "/yelp/scrollkeeper.xml", NULL, + XML_PARSE_NOBLANKS | XML_PARSE_NOCDATA | + XML_PARSE_NOENT | XML_PARSE_NOERROR | + XML_PARSE_NONET ); + ret = xmlTextReaderRead (reader); + while (ret == 1) { + if (!xmlStrcmp (xmlTextReaderConstLocalName (reader), + BAD_CAST "toc")) { + xmlChar *id = xmlTextReaderGetAttribute (reader, BAD_CAST "id"); + xmlNodePtr node; + gchar *xpath_s; + if (!id) { + ret = xmlTextReaderRead (reader); + continue; + } + + g_mutex_lock (priv->mutex); + + yelp_document_add_page_id (YELP_DOCUMENT (toc), (gchar *) id, (gchar *) id); + g_mutex_unlock (priv->mutex); + xpath_s = g_strdup_printf ("//toc[@id = '%s']", id); + obj = xmlXPathEvalExpression (BAD_CAST xpath_s, xpath); + g_free (xpath_s); + + node = obj->nodesetval->nodeTab[0]; + xmlXPathFreeObject (obj); + + ret = xmlTextReaderRead (reader); + while (ret == 1) { + if (!xmlStrcmp (xmlTextReaderConstLocalName (reader), + BAD_CAST "subject")) { + xmlChar *cat = xmlTextReaderGetAttribute (reader, + BAD_CAST "category"); + rrn_for_each_in_category (rrn_add_document, + (char *) cat, + (void *) node); + xmlFree (cat); + } + else if (!xmlStrcmp (xmlTextReaderConstLocalName (reader), + BAD_CAST "toc")) { + break; + } + ret = xmlTextReaderRead (reader); + } + + xmlFree (id); + ret = xmlTextReaderRead (reader); + } else { + ret = xmlTextReaderRead (reader); + } + } + xmlFreeTextReader (reader); + xmlXPathFreeContext (xpath); + + + man_thread = g_thread_create ((GThreadFunc) toc_process_man, toc, TRUE, NULL); + if (!man_thread) { + g_warning ("Could not create Man page thread"); + priv->man_processed = TRUE; + } + + info_thread = g_thread_create ((GThreadFunc) toc_process_info, toc, TRUE, NULL); + if (!info_thread) { + g_warning ("Could not create Info page thread"); + priv->info_processed = TRUE; + } + + params = g_new0 (gchar *, params_max); + yelp_settings_params (¶ms, ¶ms_i, ¶ms_max); + + if ((params_i + 10) >= params_max - 1) { + params_max += 10; + params = g_renew (gchar *, params, params_max); + } + + info = gtk_icon_theme_lookup_icon (theme, "yelp-icon-big", 192, 0); + if (info) { + params[params_i++] = "help_icon"; + params[params_i++] = g_strdup_printf ("\"%s\"", + gtk_icon_info_get_filename (info)); + params[params_i++] = "help_icon_size"; + params[params_i++] = g_strdup_printf ("%i", + gtk_icon_info_get_base_size (info)); + gtk_icon_info_free (info); + } + + params[params_i] = NULL; + + while (!priv->info_processed || !priv->man_processed) { + g_thread_yield (); + } + + g_mutex_lock (priv->mutex); + + priv->transform = yelp_transform_new (STYLESHEET, + (YelpTransformFunc) transform_func, + toc); + priv->transform_running = TRUE; + + yelp_transform_start (priv->transform, + priv->xmldoc, + params); + g_mutex_unlock (priv->mutex); + + done: + if (parserCtxt) + xmlFreeParserCtxt (parserCtxt); + + g_object_unref (toc); +} + + +static void +xml_trim_titles (xmlNodePtr node, xmlChar * nodetype) +{ + xmlNodePtr cur, keep = NULL; + xmlChar *keep_lang = NULL; + int j, keep_pri = INT_MAX; + + const gchar * const * langs = g_get_language_names (); + + for (cur = node->children; cur; cur = cur->next) { + if (!xmlStrcmp (cur->name, nodetype)) { + xmlChar *cur_lang = NULL; + int cur_pri = INT_MAX; + cur_lang = xmlNodeGetLang (cur); + if (cur_lang) { + for (j = 0; langs[j]; j++) { + if (g_str_equal (cur_lang, langs[j])) { + cur_pri = j; + break; + } + } + } else { + cur_pri = INT_MAX - 1; + } + if (cur_pri <= keep_pri) { + if (keep_lang) + xmlFree (keep_lang); + keep_lang = cur_lang; + keep_pri = cur_pri; + keep = cur; + } else { + if (cur_lang) + xmlFree (cur_lang); + } + } + } + cur = node->children; + while (cur) { + xmlNodePtr this = cur; + cur = cur->next; + if (!xmlStrcmp (this->name, nodetype)) { + if (this != keep) { + xmlUnlinkNode (this); + xmlFreeNode (this); + } + } + } + xmlFree (keep_lang); +} + +static int +rrn_info_add_document (RrnInfoEntry *entry, void *user_data) +{ + xmlNodePtr node = (xmlNodePtr) user_data; + xmlNodePtr new; + gchar *tmp; + + new = xmlNewChild (node, NULL, BAD_CAST "doc", NULL); + if (entry->section) + tmp = g_strdup_printf("info:%s#%s", entry->name, entry->section); + else + tmp = g_strdup_printf("info:%s", entry->name); + xmlNewNsProp (new, NULL, BAD_CAST "href", BAD_CAST tmp); + xmlNewTextChild (new, NULL, BAD_CAST "title", BAD_CAST entry->doc_name); + xmlNewTextChild (new, NULL, BAD_CAST "description", BAD_CAST entry->comment); + g_free(tmp); + return TRUE; + +} + +static void +toc_process_info (YelpToc *toc) +{ + xmlNodePtr node = NULL; + xmlNodePtr cat_node = NULL; + xmlNodePtr mynode = NULL; + char **categories = NULL; + char **cat_iter = NULL; + int sectno = 0; + YelpTocPriv * priv = toc->priv; + int i; + xmlXPathContextPtr xpath; + xmlXPathObjectPtr obj; + xmlDocPtr info_doc; + xmlParserCtxtPtr parserCtxt = NULL; + + debug_print (DB_FUNCTION, "entering\n"); + + parserCtxt = xmlNewParserCtxt (); + + info_doc = xmlCtxtReadFile (parserCtxt, + DATADIR "/yelp/info.xml", NULL, + XML_PARSE_NOBLANKS | + XML_PARSE_NOCDATA | + XML_PARSE_NOENT | + XML_PARSE_NOERROR | + XML_PARSE_NONET ); + + if (!info_doc) { + g_warning ("Could not process info TOC"); + goto done; + } + + g_mutex_lock (priv->mutex); + yelp_document_add_page_id (YELP_DOCUMENT (toc), (gchar *) "Info", (gchar *) "Info"); + g_mutex_unlock (priv->mutex); + + xpath = xmlXPathNewContext (info_doc); + obj = xmlXPathEvalExpression (BAD_CAST "//toc", xpath); + node = obj->nodesetval->nodeTab[0]; + for (i=0; i < obj->nodesetval->nodeNr; i++) { + xmlNodePtr tmpnode = obj->nodesetval->nodeTab[i]; + xml_trim_titles (tmpnode, BAD_CAST "title"); + xml_trim_titles (tmpnode, BAD_CAST "description"); + } + xmlXPathFreeObject (obj); + xmlXPathFreeContext (xpath); + + categories = rrn_info_get_categories (); + cat_iter = categories; + + while (cat_iter && *cat_iter) { + char *tmp; + + cat_node = xmlNewChild (node, NULL, BAD_CAST "toc", + NULL); + tmp = g_strdup_printf ("%d", sectno); + xmlNewNsProp (cat_node, NULL, BAD_CAST "sect", + BAD_CAST tmp); + g_free (tmp); + tmp = g_strdup_printf ("infosect%d", sectno); + g_mutex_lock (priv->mutex); + yelp_document_add_page_id (YELP_DOCUMENT (toc), (gchar *) tmp, (gchar *) tmp); + g_mutex_unlock (priv->mutex); + + xmlNewNsProp (cat_node, NULL, BAD_CAST "id", + BAD_CAST tmp); + g_free (tmp); + sectno++; + xmlNewTextChild (cat_node, NULL, BAD_CAST "title", + BAD_CAST *cat_iter); + + rrn_info_for_each_in_category (*cat_iter, (RrnInfoForeachFunc) rrn_info_add_document, + cat_node); + cat_iter++; + } + + mynode = xmlCopyNode (xmlDocGetRootElement (info_doc), 1); + + g_mutex_lock (priv->mutex); + xmlAddChild (xmlDocGetRootElement (priv->xmldoc), mynode); + g_mutex_unlock (priv->mutex); + + xmlFreeDoc (info_doc); + + + done: + if (parserCtxt) + xmlFreeParserCtxt (parserCtxt); + g_mutex_lock (priv->mutex); + priv->info_processed = TRUE; + g_mutex_unlock (priv->mutex); +} + +static int +rrn_add_man_document (RrnManEntry *entry, void *user_data) +{ + xmlNodePtr node = (xmlNodePtr) user_data; + xmlNodePtr new; + gchar *tmp; + + new = xmlNewChild (node, NULL, BAD_CAST "doc", NULL); + tmp = g_strdup_printf ("man:%s", entry->path); + + xmlNewNsProp (new, NULL, BAD_CAST "href", BAD_CAST tmp); + xmlNewTextChild (new, NULL, BAD_CAST "title", BAD_CAST entry->name); + if (entry->comment) + xmlNewTextChild (new, NULL, BAD_CAST "description", BAD_CAST entry->comment); + g_free(tmp); + + return TRUE; +} + +void +toc_process_man (YelpToc *toc) +{ + xmlNodePtr cat_node = NULL; + xmlNodePtr mynode = NULL; + YelpTocPriv * priv = toc->priv; + int i, j; + xmlXPathContextPtr xpath; + xmlXPathObjectPtr obj; + xmlDocPtr man_doc; + xmlParserCtxtPtr parserCtxt = NULL; + + debug_print (DB_FUNCTION, "entering\n"); + + parserCtxt = xmlNewParserCtxt (); + + man_doc = xmlCtxtReadFile (parserCtxt, DATADIR "/yelp/man.xml", NULL, + XML_PARSE_NOBLANKS | XML_PARSE_NOCDATA | + XML_PARSE_NOENT | XML_PARSE_NOERROR | + XML_PARSE_NONET ); + if (!man_doc) { + g_warning ("Could not process man TOC"); + goto done; + } + + xpath = xmlXPathNewContext (man_doc); + obj = xmlXPathEvalExpression (BAD_CAST "//toc", xpath); + + for (i = 0; i < obj->nodesetval->nodeNr; i++) { + xmlNodePtr node = obj->nodesetval->nodeTab[i]; + xmlChar *sect = xmlGetProp (node, BAD_CAST "sect"); + xmlChar *id = xmlGetProp (node, BAD_CAST "id"); + + if (sect) { + gchar **sects = g_strsplit ((gchar *)sect, " ", 0); + + g_mutex_lock (priv->mutex); + yelp_document_add_page_id (YELP_DOCUMENT (toc), (gchar *) id, (gchar *) id); + g_mutex_unlock (priv->mutex); + + cat_node = xmlNewChild (node, NULL, BAD_CAST "toc", + NULL); + for (j = 0; sects[j] != NULL; j++) { + rrn_man_for_each_in_category (sects[j], (RrnManForeachFunc) rrn_add_man_document, node); + } + g_strfreev (sects); + } + xmlFree (sect); + xmlFree (id); + xml_trim_titles (node, BAD_CAST "title"); + xml_trim_titles (node, BAD_CAST "description"); + } + xmlXPathFreeObject (obj); + xmlXPathFreeContext (xpath); + + mynode = xmlCopyNode (xmlDocGetRootElement (man_doc), 1); + + g_mutex_lock (priv->mutex); + xmlAddChild (xmlDocGetRootElement (priv->xmldoc), mynode); + g_mutex_unlock (priv->mutex); + + xmlFreeDoc (man_doc); + + done: + if (parserCtxt) + xmlFreeParserCtxt (parserCtxt); + g_mutex_lock (priv->mutex); + priv->man_processed = TRUE; + g_mutex_unlock (priv->mutex); +} diff --git a/src/yelp-toc.h b/src/yelp-toc.h new file mode 100644 index 00000000..83b6e919 --- /dev/null +++ b/src/yelp-toc.h @@ -0,0 +1,54 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2003-2007 Don Scorgie <Don@Scorgie.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Don Scorgie <Don@Scorgie.org> + */ + +#ifndef __YELP_TOC_H__ +#define __YELP_TOC_H__ + +#include <glib-object.h> + +#include "yelp-document.h" + +#define YELP_TYPE_TOC (yelp_toc_get_type ()) +#define YELP_TOC(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), YELP_TYPE_TOC, YelpToc)) +#define YELP_TOC_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), YELP_TYPE_TOC, YelpTocClass)) +#define YELP_IS_TOC(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), YELP_TYPE_TOC)) +#define YELP_IS_TOC_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), YELP_TYPE_TOC)) +#define YELP_TOC_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), YELP_TYPE_TOC, YelpTocClass)) + +typedef struct _YelpToc YelpToc; +typedef struct _YelpTocClass YelpTocClass; +typedef struct _YelpTocPriv YelpTocPriv; + +struct _YelpToc { + YelpDocument parent; + YelpTocPriv *priv; +}; + +struct _YelpTocClass { + YelpDocumentClass parent_class; +}; + +GType yelp_toc_get_type (void); +YelpDocument * yelp_toc_new (void); +YelpDocument * yelp_toc_get (void); + +#endif /* __YELP_TOC_H__ */ diff --git a/src/yelp-transform.c b/src/yelp-transform.c new file mode 100644 index 00000000..1e6f8f5b --- /dev/null +++ b/src/yelp-transform.c @@ -0,0 +1,416 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2003-2007 Shaun McCance <shaunm@gnome.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Shaun McCance <shaunm@gnome.org> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib.h> +#include <glib/gi18n.h> +#include <libxml/parser.h> +#include <libxml/parserInternals.h> +#include <libxml/xinclude.h> +#include <libxslt/xslt.h> +#include <libxslt/templates.h> +#include <libxslt/transform.h> +#include <libxslt/extensions.h> +#include <libxslt/xsltInternals.h> +#include <libxslt/xsltutils.h> + +#include "yelp-debug.h" +#include "yelp-error.h" +#include "yelp-transform.h" + +#define YELP_NAMESPACE "http://www.gnome.org/yelp/ns" + +static void transform_run (YelpTransform *transform); +static gboolean transform_free (YelpTransform *transform); +static void transform_set_error (YelpTransform *transform, + YelpError *error); + +static gboolean transform_chunk (YelpTransform *transform); +static gboolean transform_error (YelpTransform *transform); +static gboolean transform_final (YelpTransform *transform); + +static void xslt_yelp_document (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltStylePreCompPtr comp); +static void xslt_yelp_cache (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltStylePreCompPtr comp); + +/******************************************************************************/ + +YelpTransform +*yelp_transform_new (gchar *stylesheet, + YelpTransformFunc func, + gpointer user_data) +{ + YelpTransform *transform; + + transform = g_new0 (YelpTransform, 1); + + transform->stylesheet = xsltParseStylesheetFile (BAD_CAST stylesheet); + if (!transform->stylesheet) { + transform->error = + yelp_error_new (_("Invalid Stylesheet"), + _("The XSLT stylesheet ‘%s’ is either missing, or it is " + "not valid."), + stylesheet); + transform_error (transform); + return NULL; + } + + transform->func = func; + + transform->queue = g_async_queue_new (); + transform->chunks = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + NULL); + + transform->user_data = user_data; + + return transform; +} + +void +yelp_transform_start (YelpTransform *transform, + xmlDocPtr document, + gchar **params) +{ + transform->inputDoc = document; + + transform->context = xsltNewTransformContext (transform->stylesheet, + transform->inputDoc); + if (!transform->context) { + YelpError *error = + yelp_error_new (_("Broken Transformation"), + _("An unknown error occurred while attempting to " + "transform the document.")); + transform_set_error (transform, error); + return; + } + + transform->params = g_strdupv (params); + + transform->context->_private = transform; + xsltRegisterExtElement (transform->context, + BAD_CAST "document", + BAD_CAST YELP_NAMESPACE, + (xsltTransformFunction) xslt_yelp_document); + xsltRegisterExtElement (transform->context, + BAD_CAST "cache", + BAD_CAST YELP_NAMESPACE, + (xsltTransformFunction) xslt_yelp_cache); + + transform->mutex = g_mutex_new (); + g_mutex_lock (transform->mutex); + transform->running = TRUE; + transform->thread = g_thread_create ((GThreadFunc) transform_run, + transform, FALSE, NULL); + g_mutex_unlock (transform->mutex); +} + +gchar * +yelp_transform_eat_chunk (YelpTransform *transform, gchar *chunk_id) +{ + gchar *buf; + + g_mutex_lock (transform->mutex); + + buf = g_hash_table_lookup (transform->chunks, chunk_id); + if (buf) + g_hash_table_remove (transform->chunks, chunk_id); + + g_mutex_unlock (transform->mutex); + + /* The caller assumes ownership of this memory. */ + return buf; +} + +void +yelp_transform_release (YelpTransform *transform) +{ + g_mutex_lock (transform->mutex); + if (transform->running) { + /* We can't free it just now, because the thread is running. + * Instead, we'll tell libxslt to stop and mark the transform + * as released. + */ + transform->released = TRUE; + transform->context->state = XSLT_STATE_STOPPED; + } else { + /* We might still have pending pops from the queue, so just + * schedule transform_free. + */ + g_idle_add ((GSourceFunc) transform_free, transform); + } + g_mutex_unlock (transform->mutex); +} + +/******************************************************************************/ + +static void +transform_run (YelpTransform *transform) +{ + transform->outputDoc = xsltApplyStylesheetUser (transform->stylesheet, + transform->inputDoc, + (const char **) transform->params, + NULL, NULL, + transform->context); + + /* FIXME: do something with outputDoc? */ + transform->idle_funcs++; + g_idle_add ((GSourceFunc) transform_final, transform); + + g_mutex_lock (transform->mutex); + transform->running = FALSE; + if (transform->released) { + /* The transform was released by its owner, but it couldn't + * be freed because this thread was running. But we're in + * a thread, and the main thread might still be popping stuff + * off the asynchronous queue. Schedule this for freeing. + */ + g_idle_add ((GSourceFunc) transform_free, transform); + } + g_mutex_unlock (transform->mutex); +} + +static gboolean +transform_free (YelpTransform *transform) +{ + gchar *chunk_id; + + /* If the queue isn't empty yet, try again later. But because + * threads scare me and I don't want runaway code, stop trying + * after an insane number of attempts and just leak. + */ + if (transform->idle_funcs > 0) { + transform->free_attempts++; + if (transform->free_attempts < 1000) { + return TRUE; + } else { + g_warning ("Runaway free attempt detected. Memory is about to leak.\n"); + return FALSE; + } + } + + g_mutex_lock (transform->mutex); + if (transform->outputDoc) + xmlFreeDoc (transform->outputDoc); + if (transform->stylesheet) + xsltFreeStylesheet (transform->stylesheet); + if (transform->context) + xsltFreeTransformContext (transform->context); + + g_strfreev (transform->params); + + /* FIXME: destroy data */ + while ((chunk_id = (gchar *) g_async_queue_try_pop (transform->queue))) + g_free (chunk_id); + g_async_queue_unref (transform->queue); + g_mutex_unlock (transform->mutex); + g_mutex_free (transform->mutex); + + if (transform->error) + yelp_error_free (transform->error); + + g_free (transform); + return FALSE; +} + +static void +transform_set_error (YelpTransform *transform, + YelpError *error) +{ + g_mutex_lock (transform->mutex); + if (transform->released) { + yelp_error_free (error); + g_mutex_unlock (transform->mutex); + return; + } + if (transform->error) + yelp_error_free (transform->error); + transform->error = error; + transform->idle_funcs++; + g_idle_add ((GSourceFunc) transform_error, transform); + g_mutex_unlock (transform->mutex); +} + +static gboolean +transform_chunk (YelpTransform *transform) +{ + gchar *chunk_id; + + transform->idle_funcs--; + if (transform->released) + return FALSE; + + chunk_id = (gchar *) g_async_queue_try_pop (transform->queue); + + if (chunk_id) { + if (transform->func) + transform->func (transform, + YELP_TRANSFORM_CHUNK, + chunk_id, + transform->user_data); + else + g_free (chunk_id); + } + + return FALSE; +} + +static gboolean +transform_error (YelpTransform *transform) +{ + YelpError *error; + + transform->idle_funcs--; + if (transform->released) + return FALSE; + + g_mutex_lock (transform->mutex); + + error = transform->error; + transform->error = NULL; + + g_mutex_unlock (transform->mutex); + + if (transform->func) + transform->func (transform, + YELP_TRANSFORM_ERROR, + error, transform->user_data); + else + yelp_error_free (error); + + return FALSE; +} + +static gboolean +transform_final (YelpTransform *transform) +{ + transform->idle_funcs--; + if (transform->released) + return FALSE; + + /* FIXME: check for anything remaining on the queue */ + if (transform->func) + transform->func (transform, + YELP_TRANSFORM_FINAL, + NULL, transform->user_data); + + return FALSE; +} + +/******************************************************************************/ + +static void +xslt_yelp_document (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltStylePreCompPtr comp) +{ + YelpTransform *transform; + xmlChar *page_id = NULL; + xmlChar *page_buf; + gint buf_size; + xsltStylesheetPtr style = NULL; + const char *old_outfile; + xmlDocPtr new_doc = NULL; + xmlDocPtr old_doc; + xmlNodePtr old_insert; + + debug_print (DB_FUNCTION, "entering\n"); + + if (ctxt->state == XSLT_STATE_STOPPED) + return; + + if (!ctxt || !node || !inst || !comp) + return; + + transform = (YelpTransform *) ctxt->_private; + + page_id = xsltEvalAttrValueTemplate (ctxt, inst, + (const xmlChar *) "href", + NULL); + if (page_id == NULL) { + xsltTransformError (ctxt, NULL, inst, + _("No href attribute found on yelp:document")); + /* FIXME: put a real error here */ + goto done; + } + debug_print (DB_ARG, " page_id = \"%s\"\n", page_id); + + old_outfile = ctxt->outputFile; + old_doc = ctxt->output; + old_insert = ctxt->insert; + ctxt->outputFile = (const char *) page_id; + + style = xsltNewStylesheet (); + if (style == NULL) { + xsltTransformError (ctxt, NULL, inst, + _("Out of memory")); + goto done; + } + + style->omitXmlDeclaration = TRUE; + + new_doc = xmlNewDoc (BAD_CAST "1.0"); + new_doc->charset = XML_CHAR_ENCODING_UTF8; + new_doc->dict = ctxt->dict; + xmlDictReference (new_doc->dict); + + ctxt->output = new_doc; + ctxt->insert = (xmlNodePtr) new_doc; + + xsltApplyOneTemplate (ctxt, node, inst->children, NULL, NULL); + xsltSaveResultToString (&page_buf, &buf_size, new_doc, style); + + ctxt->outputFile = old_outfile; + ctxt->output = old_doc; + ctxt->insert = old_insert; + + g_mutex_lock (transform->mutex); + g_hash_table_insert (transform->chunks, page_id, page_buf); + g_async_queue_push (transform->queue, g_strdup ((gchar *) page_id)); + transform->idle_funcs++; + g_idle_add ((GSourceFunc) transform_chunk, transform); + g_mutex_unlock (transform->mutex); + + done: + if (new_doc) + xmlFreeDoc (new_doc); + if (style) + xsltFreeStylesheet (style); +} + +static void +xslt_yelp_cache (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltStylePreCompPtr comp) +{ +} diff --git a/src/yelp-transform.h b/src/yelp-transform.h new file mode 100644 index 00000000..e1262e39 --- /dev/null +++ b/src/yelp-transform.h @@ -0,0 +1,81 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2003-2007 Shaun McCance <shaunm@gnome.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Shaun McCance <shaunm@gnome.org> + */ + +#ifndef __YELP_TRANSFORM_H__ +#define __YELP_TRANSFORM_H__ + +#include <glib.h> +#include <libxml/tree.h> +#include <libxslt/xslt.h> +#include <libxslt/transform.h> + +#include "yelp-error.h" + +typedef struct _YelpTransform YelpTransform; + +typedef enum { + YELP_TRANSFORM_CHUNK, + YELP_TRANSFORM_ERROR, + YELP_TRANSFORM_FINAL +} YelpTransformSignal; + +typedef void (*YelpTransformFunc) (YelpTransform *transform, + YelpTransformSignal signal, + gpointer func_data, + gpointer user_data); + +struct _YelpTransform { + xmlDocPtr inputDoc; + xmlDocPtr outputDoc; + xsltStylesheetPtr stylesheet; + xsltTransformContextPtr context; + + YelpTransformFunc func; + + gchar **params; + + GThread *thread; + GMutex *mutex; + GAsyncQueue *queue; + GHashTable *chunks; + + gboolean running; + gboolean released; + gint idle_funcs; + gint free_attempts; + + gpointer user_data; + + YelpError *error; +}; + +YelpTransform *yelp_transform_new (gchar *stylesheet, + YelpTransformFunc func, + gpointer user_data); +void yelp_transform_start (YelpTransform *transform, + xmlDocPtr document, + gchar **params); +gchar * yelp_transform_eat_chunk (YelpTransform *transform, + gchar *chunk_id); +void yelp_transform_release (YelpTransform *transform); + +#endif /* __YELP_TRANSFORM_H__ */ diff --git a/src/yelp-utils.c b/src/yelp-utils.c index d1d92da9..59a0fad4 100644 --- a/src/yelp-utils.c +++ b/src/yelp-utils.c @@ -29,1036 +29,393 @@ #include <glib/gi18n.h> #include <libgnomevfs/gnome-vfs.h> #include <libgnomevfs/gnome-vfs-mime-utils.h> -#include <libgnome/gnome-program.h> -#include <libgnome/gnome-init.h> - -#include "yelp-utils.h" -#include "yelp-debug.h" #include <string.h> +#include <rarian-info.h> +#include <rarian-man.h> +#include <rarian.h> -static GHashTable *doc_info_table; - -typedef struct { - gchar *uri; - YelpURIType type; -} DocInfoURI; - -struct _YelpDocInfo { - gchar *id; - gchar *title; - gchar *description; - gchar *category; - gchar *lang; - gint lang_priority; - - DocInfoURI *uris; - gint num_uris; - gint max_uris; - - YelpDocType type; - - YelpPager *pager; - - gint ref_count; -}; - -static gchar *mandirs[] = { - "man0p", - "man1", - "man1p", - "man2", - "man3", - "man3p", - "man4", - "man5", - "man6", - "man7", - "man8", - "man9", - "mann", - NULL -}; - -static YelpDocType get_doc_type (gchar *uri); -static gchar * convert_ghelp_uri (gchar *uri); - -static gchar * convert_man_uri (gchar *uri, gboolean trust_uri); -static gchar * convert_info_uri (gchar *uri); - -static gchar *dot_dir = NULL; +#include "yelp-utils.h" +#include "yelp-debug.h" -static gchar **infopath = NULL; -static gchar *infopath_d[] = {"/usr/info", "/usr/share/info", - "/usr/local/info", "/usr/local/share/info", - NULL}; +YelpRrnType resolve_process_ghelp (char *uri, + gchar **result); +gchar * resolve_get_section (const gchar *uri); +gboolean resolve_is_man_path (const gchar *path, + const gchar *encoding); +YelpRrnType resolve_full_file (const gchar *path); +YelpRrnType resolve_man_page (const gchar *name, + gchar **result, + gchar **section); +gchar * resolve_remove_section (const gchar *uri, + const gchar *sect); +YelpRrnType yelp_uri_resolve (gchar *uri, + gchar **result, + gchar **section); + +YelpRrnType +resolve_process_ghelp (char *uri, gchar **result) +{ + RrnReg *reg = rrn_find_from_ghelp (&uri[6]); + YelpRrnType type = YELP_RRN_TYPE_ERROR; + + if (reg) { + gchar *mime = NULL; + if (g_str_has_prefix (reg->uri, "file:")) + *result = g_strdup (®->uri[5]); + else + *result = g_strdup (reg->uri); -const char * -yelp_dot_dir (void) -{ - if (dot_dir == NULL) { - dot_dir = g_build_filename (g_get_home_dir(), GNOME_DOT_GNOME, - "yelp.d", NULL); + /* mime types are horrible in omf-translated files */ + if (reg->type && *(reg->type)) + mime = g_strdup (reg->type); + else + mime = gnome_vfs_get_mime_type (*result); - if (!g_file_test (dot_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) - mkdir (dot_dir, 0750); + if (g_str_equal (mime, "text/xml") || + g_str_equal (mime, "application/docbook+xml") || + g_str_equal (mime, "application/xml")) + type = YELP_RRN_TYPE_DOC; + else if (g_str_equal (mime, "text/html")) + type = YELP_RRN_TYPE_HTML; + else if (g_str_equal (mime, "application/xhtml+xml")) + type = YELP_RRN_TYPE_XHTML; + } - return dot_dir; + return type; } -/* @uri: the uri to interpret for the new YelpDocInfo struct - * @trust_uri: if the uri is absolute and is known to exist, - * then this should be set. Only makes sense for local - * files. This is here for performance reasons, adding - * 40,000 man pages and accessing the disk for each one - * can get pretty expensive. - */ -YelpDocInfo * -yelp_doc_info_new (const gchar *uri, gboolean trust_uri) +gchar * +resolve_get_section (const gchar *uri) { - YelpDocInfo *doc; - gchar *doc_uri = NULL; - gchar *full_uri = NULL; - YelpDocType doc_type = YELP_DOC_TYPE_ERROR; - YelpURIType uri_type; - gchar *cur; - - g_return_val_if_fail (uri != NULL, NULL); + gchar *sect_delimit; + gchar *sect; - debug_print (DB_FUNCTION, "entering\n"); - debug_print (DB_ARG, " uri = \"%s\"\n", uri); - - if (trust_uri) - full_uri = gnome_vfs_make_uri_from_input (uri); - else - full_uri = - gnome_vfs_make_uri_from_input_with_dirs (uri, - GNOME_VFS_MAKE_URI_DIR_CURRENT); - - if (g_str_has_prefix (full_uri, "file:")) { - if ((cur = strchr (full_uri, '#'))) - doc_uri = g_strndup (full_uri, cur - full_uri); - else - doc_uri = g_strdup (full_uri); - doc_type = get_doc_type (doc_uri); - uri_type = YELP_URI_TYPE_FILE; - } - else if (g_str_has_prefix (full_uri, "ghelp:") || - g_str_has_prefix (full_uri, "gnome-help:")) { - doc_uri = convert_ghelp_uri (full_uri); - if (doc_uri) - doc_type = get_doc_type (doc_uri); - uri_type = YELP_URI_TYPE_GHELP; - } - else if (g_str_has_prefix (full_uri, "man:")) { - doc_uri = convert_man_uri (full_uri, trust_uri); - doc_type = YELP_DOC_TYPE_MAN; - uri_type = YELP_URI_TYPE_MAN; - } - else if (g_str_has_prefix (full_uri, "info:")) { - doc_uri = convert_info_uri (full_uri); - if (!g_str_has_prefix (doc_uri, "man:")) { - doc_type = YELP_DOC_TYPE_INFO; - uri_type = YELP_URI_TYPE_INFO; - } else { - gchar *tmp; - tmp = g_strdup (doc_uri); - g_free (doc_uri); - doc_uri = convert_man_uri (tmp, trust_uri); - g_free (tmp); - doc_type = YELP_DOC_TYPE_MAN; - uri_type = YELP_URI_TYPE_MAN; - } - } - else if (g_str_has_prefix (full_uri, "x-yelp-toc:")) { - doc_uri = g_strdup ("file://" DATADIR "/yelp/toc.xml"); - doc_type = YELP_DOC_TYPE_TOC; - uri_type = YELP_URI_TYPE_TOC; - } - else if (g_str_has_prefix (full_uri, "x-yelp-search:")) { - doc_uri = g_strconcat ("file://" DATADIR "/yelp/xslt/search2html.xsl?", full_uri + strlen ("x-yelp-search:"), NULL); - doc_type = YELP_DOC_TYPE_SEARCH; - uri_type = YELP_URI_TYPE_SEARCH; - } - else { - doc_uri = g_strdup (uri); - doc_type = YELP_DOC_TYPE_EXTERNAL; - uri_type = YELP_URI_TYPE_EXTERNAL; + sect_delimit = strrchr (uri, '#'); + if (!sect_delimit) { + sect_delimit = strrchr (uri, '?'); } - - debug_print (DB_ARG, " full_uri = \"%s\"\n", full_uri); - debug_print (DB_ARG, " doc_uri = \"%s\"\n", doc_uri); - - g_free (full_uri); - - if (doc_uri) { - doc = g_new0 (YelpDocInfo, 1); - doc->uris = g_new (DocInfoURI, 8); - doc->num_uris = 0; - doc->max_uris = 8; - - yelp_doc_info_add_uri (doc, doc_uri, YELP_URI_TYPE_FILE); - if (uri_type && uri_type != YELP_URI_TYPE_FILE) - yelp_doc_info_add_uri (doc, uri, uri_type); - g_free (doc_uri); - - doc->type = doc_type; - doc->ref_count = 1; - return doc; - } else { + if (!sect_delimit) { return NULL; } + sect = g_strdup (sect_delimit+1); + + return sect; } -YelpDocInfo * -yelp_doc_info_get (const gchar *uri, gboolean trust_uri) +gboolean +resolve_is_man_path (const gchar *path, const gchar *encoding) { - YelpDocInfo *doc; - gint i; - gchar *c, *doc_uri; - - g_return_val_if_fail (uri != NULL, NULL); - - debug_print (DB_FUNCTION, "entering\n"); - debug_print (DB_ARG, " uri = \"%s\"\n", uri); + gchar **cats; + gchar **iter; - if (!doc_info_table) - doc_info_table = - g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - (GDestroyNotify) yelp_doc_info_unref); + cats = rrn_man_get_categories (); - c = strchr (uri, '?'); - if (c == NULL) - c = strchr (uri, '#'); + iter = cats; - if (c != NULL) - doc_uri = g_strndup (uri, c - uri); - else - doc_uri = g_strdup (uri); - - doc = (YelpDocInfo *) g_hash_table_lookup (doc_info_table, doc_uri); - - if (!doc) { - doc = yelp_doc_info_new (uri, trust_uri); - if (doc && doc->type != YELP_DOC_TYPE_EXTERNAL) { - YelpDocInfo *old_doc = NULL; - - for (i = 0; i < doc->num_uris; i++) { - old_doc = g_hash_table_lookup (doc_info_table, - (doc->uris + i)->uri); - if (old_doc) - break; - } - - if (old_doc) { - for (i = 0; i < doc->num_uris; i++) { - yelp_doc_info_add_uri (old_doc, - (doc->uris + i)->uri, - (doc->uris + i)->type); - g_hash_table_insert (doc_info_table, - g_strdup ((doc->uris + i)->uri), - yelp_doc_info_ref (old_doc)); - } - yelp_doc_info_free (doc); - doc = old_doc; - } else { - for (i = 0; i < doc->num_uris; i++) { - g_hash_table_insert (doc_info_table, - g_strdup ((doc->uris + i)->uri), - yelp_doc_info_ref (doc)); - } + if (encoding && *encoding) { + while (iter) { + gchar *ending = g_strdup_printf ("%s.%s", *iter, encoding); + if (g_str_has_suffix (path, ending)) { + g_free (ending); + return TRUE; } + g_free (ending); + iter++; } - } - - g_free (doc_uri); - return doc; -} - -YelpDocInfo * -yelp_doc_info_ref (YelpDocInfo *doc) -{ - g_return_val_if_fail (doc != NULL, NULL); - (doc->ref_count)++; - return doc; -} - -void -yelp_doc_info_unref (YelpDocInfo *doc) -{ - g_return_if_fail (doc != NULL); - - if (--(doc->ref_count) < 1) - yelp_doc_info_free (doc); -} - -void -yelp_doc_info_free (YelpDocInfo *doc) -{ - gint i; - - debug_print (DB_FUNCTION, "entering\n"); - debug_print (DB_ARG, " uri = \"%s\"\n", doc->uris->uri); - - if (!doc) - return; - - if (doc->pager) - g_object_unref (doc->pager); - - g_free (doc->title); - for (i = 0; i < doc->num_uris; i++) - g_free ((doc->uris + i)->uri); - g_free (doc->uris); - g_free (doc); -} - -YelpPager * -yelp_doc_info_get_pager (YelpDocInfo *doc) -{ - g_return_val_if_fail (doc != NULL, NULL); - - return doc->pager; -} - -void -yelp_doc_info_set_pager (YelpDocInfo *doc, YelpPager *pager) -{ - g_return_if_fail (doc != NULL); - - if (doc->pager) - g_object_unref (doc->pager); - - if (pager) - g_object_ref (pager); - - doc->pager = pager; -} - -const gchar * -yelp_doc_info_get_id (YelpDocInfo *doc) -{ - return (const gchar *) doc->id; -} - -void -yelp_doc_info_set_id (YelpDocInfo *doc, gchar *id) -{ - if (doc->id) - g_free (doc->id); - - doc->id = g_strdup (id); -} - -const gchar * -yelp_doc_info_get_title (YelpDocInfo *doc) -{ - return (const gchar *) doc->title; -} - -void -yelp_doc_info_set_title (YelpDocInfo *doc, gchar *title) -{ - if (doc->title) - g_free (doc->title); - - doc->title = g_strdup (title); -} - -const gchar * -yelp_doc_info_get_description (YelpDocInfo *doc) -{ - return (const gchar *) doc->description; -} - -void -yelp_doc_info_set_description (YelpDocInfo *doc, gchar *desc) -{ - if (doc->description) - g_free (doc->description); - - doc->description = g_strdup (desc); -} - -const gchar * -yelp_doc_info_get_category (YelpDocInfo *doc) -{ - return (const gchar *) doc->category; -} - -void -yelp_doc_info_set_category (YelpDocInfo *doc, gchar *category) -{ - if (doc->category) - g_free (doc->category); - - doc->category = g_strdup (category); -} - -const gchar * -yelp_doc_info_get_language (YelpDocInfo *doc) -{ - return (const gchar *) doc->lang; -} - -void -yelp_doc_info_set_language (YelpDocInfo *doc, gchar *language) -{ - gint i; - const gchar * const * langs = g_get_language_names (); - - if (doc->lang) - g_free (doc->lang); - - doc->lang = g_strdup (language); - - doc->lang_priority = INT_MAX; - for (i = 0; langs[i] != NULL; i++) { - if (g_str_equal (language, langs[i])) { - doc->lang_priority = i; - break; + } else { + while (iter) { + if (g_str_has_suffix (path, *iter)) { + return TRUE; + } + iter++; } } + return FALSE; } -gint -yelp_doc_info_cmp_language (YelpDocInfo *doc1, YelpDocInfo *doc2) -{ - return CLAMP (doc1->lang_priority - doc2->lang_priority, -1, 1); -} - -YelpDocType -yelp_doc_info_get_type (YelpDocInfo *doc) -{ - g_return_val_if_fail (doc != NULL, YELP_DOC_TYPE_ERROR); - - return doc->type; -} - -gchar * -yelp_doc_info_get_uri (YelpDocInfo *doc, - gchar *frag_id, - YelpURIType uri_type) -{ - gchar *base = NULL; - gint i; - - g_return_val_if_fail (doc != NULL, NULL); - - for (i = 0; i < doc->num_uris; i++) - if ((doc->uris + i)->type & uri_type) { - base = (doc->uris + i)->uri; - break; - } - - if (!base) - return NULL; - - if (!frag_id || *frag_id == '\0') - return g_strdup (base); - - return g_strconcat (base, "#", frag_id, NULL); -} - -gchar * -yelp_doc_info_get_filename (YelpDocInfo *doc) { - gchar *filename = NULL; - gchar *base = NULL; - gint i; - - g_return_val_if_fail (doc != NULL, NULL); - - for (i = 0; i < doc->num_uris; i++) - if ((doc->uris + i)->type == YELP_URI_TYPE_FILE) { - base = (doc->uris + i)->uri; - break; - } - - if (g_str_has_prefix (base, "file://")) - filename = g_strdup (base + 7); - - return filename; -} - -gboolean -yelp_doc_info_equal (YelpDocInfo *doc1, YelpDocInfo *doc2) -{ - gboolean equal = TRUE; - - g_return_val_if_fail (doc1 != NULL, FALSE); - g_return_val_if_fail (doc2 != NULL, FALSE); - - /* FIXME: this sucks */ - if (!g_str_equal (doc1->uris->uri, doc2->uris->uri)) - equal = FALSE; - - return equal; -} - -void -yelp_doc_page_free (YelpDocPage *page) -{ - if (!page) - return; - - g_free (page->page_id); - g_free (page->title); - g_free (page->contents); - - g_free (page->prev_id); - g_free (page->next_id); - g_free (page->toc_id); - - g_free (page); -} - -gchar * -yelp_uri_get_fragment (const gchar *uri) -{ - gchar *cur; - gchar *frag_id = NULL; - - g_return_val_if_fail (uri != NULL, NULL); - - if (g_str_has_prefix (uri, "ghelp:")) - if ((cur = strchr (uri, '?'))) - if (*(++cur) != '\0') - frag_id = g_strdup (cur); - - if (g_str_has_prefix (uri, "x-yelp-toc:")) - if ((cur = strchr (uri, ':'))) - if (*(++cur) != '\0') - frag_id = g_strdup (cur); - if (g_str_has_prefix (uri, "info:")) - if ((cur = strchr (uri, ')'))) - if (*(++cur) != '\0') - frag_id = g_strdup (cur); - if ((cur = strchr (uri, '#'))) - if (*(++cur) != '\0') { - if (frag_id) - g_free (frag_id); - frag_id = g_strdup (cur); - } - - return frag_id; -} - -gchar * -yelp_uri_get_relative (gchar *base, gchar *ref) -{ - GnomeVFSURI *vfs_base, *vfs_uri; - gchar *uri; - - vfs_base = gnome_vfs_uri_new (base); - vfs_uri = gnome_vfs_uri_resolve_relative (vfs_base, ref); - - uri = gnome_vfs_uri_to_string (vfs_uri, GNOME_VFS_URI_HIDE_NONE); - - gnome_vfs_uri_unref (vfs_base); - gnome_vfs_uri_unref (vfs_uri); - - return uri; -} - -static YelpDocType -get_doc_type (gchar *uri) +YelpRrnType +resolve_full_file (const gchar *path) { gchar *mime_type; - YelpDocType type; - - g_return_val_if_fail (uri != NULL, YELP_DOC_TYPE_ERROR); + YelpRrnType type = YELP_RRN_TYPE_ERROR; - if (strncmp (uri, "file:", 5)) - return YELP_DOC_TYPE_EXTERNAL; - - mime_type = gnome_vfs_get_mime_type (uri); - if (mime_type == NULL) - return YELP_DOC_TYPE_ERROR; + if (!g_file_test (path, G_FILE_TEST_EXISTS)) { + return YELP_RRN_TYPE_ERROR; + } + mime_type = gnome_vfs_get_mime_type (path); + if (mime_type == NULL) { + return YELP_RRN_TYPE_ERROR; + } if (g_str_equal (mime_type, "text/xml") || g_str_equal (mime_type, "application/docbook+xml") || g_str_equal (mime_type, "application/xml")) - type = YELP_DOC_TYPE_DOCBOOK_XML; - else if (g_str_equal (mime_type, "text/sgml")) - type = YELP_DOC_TYPE_DOCBOOK_SGML; + type = YELP_RRN_TYPE_DOC; else if (g_str_equal (mime_type, "text/html")) - type = YELP_DOC_TYPE_HTML; + type = YELP_RRN_TYPE_HTML; else if (g_str_equal (mime_type, "application/xhtml+xml")) - type = YELP_DOC_TYPE_XHTML; - else - type = YELP_DOC_TYPE_EXTERNAL; - - g_free (mime_type); - return type; -} - -void -yelp_doc_info_add_uri (YelpDocInfo *doc_info, - const gchar *uri, - YelpURIType type) -{ - DocInfoURI *info_uri; - - debug_print (DB_FUNCTION, "entering\n"); - - g_assert (doc_info->num_uris <= doc_info->max_uris); - - if (doc_info->num_uris == doc_info->max_uris) { - doc_info->max_uris += 8; - doc_info->uris = g_renew (DocInfoURI, - doc_info->uris, - doc_info->max_uris); - } - - info_uri = doc_info->uris + doc_info->num_uris; - - info_uri->uri = g_strdup (uri); - info_uri->type = type; - - doc_info->num_uris++; - - debug_print (DB_ARG, " uri = \"%s\"\n", uri); - debug_print (DB_ARG, " num_uris = %i\n", doc_info->num_uris); - debug_print (DB_ARG, " max_uris = %i\n", doc_info->max_uris); -} - -/******************************************************************************/ -/** Convert fancy URIs to file URIs *******************************************/ - -static gchar * -locate_file_lang (gchar *path, gchar *file, const gchar *lang) -{ - gchar *exts[] = {".xml", ".docbook", ".sgml", ".html", "", NULL}; - gint i; - gchar *full, *uri = NULL; - - for (i = 0; exts[i] != NULL; i++) { - full = g_strconcat (path, "/", lang, "/", file, exts[i], NULL); - if (g_file_test (full, G_FILE_TEST_IS_REGULAR)) - uri = g_strconcat ("file://", full, NULL); - g_free (full); - if (uri) - return uri; - } - return NULL; -} - -static gchar * -convert_ghelp_uri (gchar *uri) -{ - GSList *locations = NULL; - GSList *node; - gchar *path, *cur; - gchar *doc_id = NULL; - gchar *doc_name = NULL; - gchar *doc_uri = NULL; - - GnomeProgram *program = gnome_program_get (); - - if ((path = strchr(uri, ':'))) - path++; - else - goto done; - - if (path && path[0] == '/') { - if ((cur = strchr (path, '?')) || (cur = strchr (path, '#'))) - *cur = '\0'; - doc_uri = g_strconcat ("file://", path, NULL); - if (cur) - *cur = '#'; - - goto done; - } - - if ((cur = strchr (path, '/'))) { - doc_id = g_strndup (path, cur - path); - path = cur + 1; - } - - if ((cur = strchr (path, '?')) || (cur = strchr (path, '#'))) - doc_name = g_strndup (path, cur - path); - else - doc_name = g_strdup (path); - - if (doc_id) - gnome_program_locate_file (program, - GNOME_FILE_DOMAIN_HELP, - doc_id, - FALSE, - &locations); - else - gnome_program_locate_file (program, - GNOME_FILE_DOMAIN_HELP, - doc_name, - FALSE, - &locations); - - if (!locations) - goto done; - - for (node = locations; node; node = node->next) { - gint i; - const gchar * const * langs; - gchar *location = (gchar *) node->data; - - langs = g_get_language_names (); - - for (i = 0; langs[i] != NULL; i++) { - - /* This has to be a valid language AND a language with - * no encoding postfix. The language will come up without - * encoding next */ - if (strchr (langs[i], '.') != NULL) - continue; - - doc_uri = locate_file_lang (location, doc_name, langs[i]); - - if (!doc_uri) - doc_uri = locate_file_lang (location, "index", langs[i]); - - if (doc_uri) - goto done; + type = YELP_RRN_TYPE_XHTML; + else if (g_str_equal (mime_type, "application/x-gzip")) { + if (g_str_has_suffix (path, ".info.gz")) { + type = YELP_RRN_TYPE_INFO; + } else if (resolve_is_man_path (path, "gz")) { + type = YELP_RRN_TYPE_MAN; } - /* Look in C locale since that exists for almost all docs */ - doc_uri = locate_file_lang (location, doc_name, "C"); - if (doc_uri) - goto done; - - /* Last chance, look for index-file with C lang */ - doc_uri = locate_file_lang (location, "index", "C"); - } - - done: - if (locations) { - g_slist_foreach (locations, (GFunc) g_free, NULL); - g_slist_free (locations); + } else if (g_str_equal (mime_type, "application/x-bzip")) { + if (g_str_has_suffix (path, ".info.bz2")) { + type = YELP_RRN_TYPE_INFO; + } else if (resolve_is_man_path (path, "bz2")) { + type = YELP_RRN_TYPE_MAN; + } + } else if (g_str_equal (mime_type, "text/plain")) { + if (g_str_has_suffix (path, ".info")) { + type = YELP_RRN_TYPE_INFO; + } else if (resolve_is_man_path (path, NULL)) { + type = YELP_RRN_TYPE_MAN; + } + } else { + type = YELP_RRN_TYPE_EXTERNAL; } - g_free (doc_id); - g_free (doc_name); - if (!doc_uri) - g_warning ("Could not resolve ghelp URI: %s", uri); + g_free (mime_type); + return type; - return doc_uri; } -static gchar * -convert_man_uri (gchar *uri, gboolean trust_uri) -{ - gchar *path, *cur; - gchar *doc_uri = NULL; - gchar *man_name = NULL; - gchar *man_num = NULL; - gchar *man_dir = NULL; - const gchar * const * langs = g_get_language_names (); - gint langs_i; - - static gchar **manpath = NULL; - gint i, j; - - GDir *dir; - gchar *dirname; - const gchar *filename; - gchar *pattern; - GPatternSpec *pspec = NULL; - - if ((path = strchr(uri, ':'))) - path++; - else - goto done; - - /* An absolute file path after man: */ - if (path[0] == '/') { - if (trust_uri) - doc_uri = g_strconcat ("file://", path, NULL); - else if (g_file_test (path, G_FILE_TEST_IS_REGULAR)) - doc_uri = g_strconcat ("file://", path, NULL); - goto done; - } - - /* Get the manpath, either from the 'manpath' command, for from the - MANPATH envar. manpath is static, so this should only run once - for each program invocation. - */ - if (!manpath) { - gchar *manp; - - if (!g_spawn_command_line_sync ("manpath", &manp, NULL, NULL, NULL)) - manp = g_strdup (g_getenv ("MANPATH")); - if (!manp) { - return NULL; - } - g_strstrip (manp); - manpath = g_strsplit (manp, ":", -1); - - g_free (manp); - } - - /* The URI is either man:frobnicate or man:frobnicate(1). If the former, - set man_name to everything after man:. If the latter, set man_name to - everything leading to (1), and man_num to the section number. - */ - if ((cur = strchr (path, '('))) { - man_name = g_strndup (path, cur - path); - path = cur + 1; - if ((cur = strchr (path, ')'))) - man_num = g_strndup (path, cur - path); - if (man_num[0]) { - man_dir = g_new (gchar, 5); - g_snprintf (man_dir, 5, "man%c", man_num[0]); +YelpRrnType +resolve_man_page (const gchar *name, gchar **result, gchar **section) +{ + /* Various ways the path could be presented: + * filename - full filename after man: + * name(section) - resolve to a particular section + * name.section - ditto. This must be tested twice. Once for full filename and + * once for section + * name#section - ditto, though this is never used, just added for completeness + * name - resolve to the first one found + */ + gchar *lbrace = NULL; + gchar *rbrace = NULL; + gchar *sect = NULL; + gchar *real_name = NULL; + gboolean repeat = FALSE; + RrnManEntry *entry = NULL; + + lbrace = strrchr (name, '('); + if (lbrace) { + rbrace = strrchr (name, ')'); + if (rbrace) { + /*sect = g_strndup (lbrace+1, rbrace - lbrace - 1);*/ + real_name = g_strndup (name, lbrace - name); + } else { + sect = NULL; + real_name = strdup (name); } } else { - man_name = g_strdup (path); - } - - /* Create the glob pattern. If we have man_num, then look for - man_name.man_num*. Otherwise, looks for man_name.* - */ - if (man_num && man_num[0]) - pattern = g_strdup_printf ("%s.%s*", man_name, man_num); - else - pattern = g_strdup_printf ("%s.*", man_name); - pspec = g_pattern_spec_new (pattern); - g_free (pattern); - - for (i = 0; manpath[i]; i++) { - for (langs_i = 0; langs[langs_i]; langs_i++) { - for (j = 0; man_dir ? (j < 1) : (mandirs[j] != NULL); j++) { - if (g_str_equal (langs[langs_i], "C")) - dirname = g_build_filename (manpath[i], - man_dir ? man_dir : mandirs[j], - NULL); - else - dirname = g_build_filename (manpath[i], - langs[langs_i], - man_dir ? man_dir : mandirs[j], - NULL); - dir = g_dir_open (dirname, 0, NULL); - if (dir) { - while ((filename = g_dir_read_name (dir))) { - if (g_pattern_match_string (pspec, filename)) { - doc_uri = g_strconcat ("file://", - dirname, "/", - filename, - NULL); - g_dir_close (dir); - g_free (dirname); - goto done; - } - } - g_dir_close (dir); - } - g_free (dirname); + lbrace = strrchr (name, '.'); + if (lbrace) { + repeat = TRUE; + /*sect = strdup (lbrace+1);*/ + real_name = g_strndup (name, lbrace - name); + } else { + lbrace = strrchr (name, '#'); + if (lbrace) { + /*sect = strdup (lbrace+1);*/ + real_name = g_strndup (name, lbrace - name); + } else { + real_name = strdup (name); + sect = NULL; } } } + if (g_file_test (real_name, G_FILE_TEST_EXISTS)) { + /* Full filename */ + *result = g_strdup (real_name); + return YELP_RRN_TYPE_MAN; + } else if (g_file_test (name, G_FILE_TEST_EXISTS)) { + /* Full filename */ + *result = g_strdup (name); + return YELP_RRN_TYPE_MAN; + } - done: - if (pspec) - g_pattern_spec_free (pspec); - g_free (man_dir); - g_free (man_num); - g_free (man_name); - - return doc_uri; -} - -gchar ** -yelp_get_info_paths (void) -{ - /* Get the infopath, either from the INFOPATH envar, - or from the default infopath_d. - */ - if (!infopath) { - gchar *infop; - - infop = g_strdup (g_getenv ("INFOPATH")); - if (infop) { - g_strstrip (infop); - infopath = g_strsplit (infop, ":", -1); - g_free (infop); - } else { - infopath = infopath_d; + entry = rrn_man_find_from_name (real_name, sect); + + if (entry) { + *result = strdup (entry->path); + /**section = strdup (entry->section);*/ + return YELP_RRN_TYPE_MAN; + } else if (repeat) { + entry = rrn_man_find_from_name ((char *) name, NULL); + if (entry) { + *result = strdup (entry->path); + /**section = strdup (entry->section);*/ + return YELP_RRN_TYPE_MAN; } } - - - return infopath; + return YELP_RRN_TYPE_ERROR; } -gchar ** -yelp_get_man_paths (void) +gchar * +resolve_remove_section (const gchar *uri, const gchar *sect) { - return mandirs; + if (sect) + return (g_strndup (uri, (strlen(uri) - strlen(sect) - 1 /*for the delimiter char */))); + else + return (g_strdup (uri)); } -static gchar * -convert_info_uri (gchar *uri) +YelpRrnType +yelp_uri_resolve (gchar *uri, gchar **result, gchar **section) { - gchar *path, *cur; - gchar *doc_uri = NULL; - gchar *info_name = NULL; - gchar *info_dot_info = NULL; - gchar **infopaths = NULL; - gboolean need_subdir = FALSE; - gchar *test_filename = NULL; - - gint i; - - GDir *dir; - const gchar *filename; - gchar *pattern; - GPatternSpec *pspec = NULL; - GPatternSpec *pspec1 = NULL; - gchar *subdir = NULL; - - if ((path = strchr(uri, ':'))) - path++; - else - goto done; - - /* An absolute path after info: */ - if (path[0] == '/') { - if (g_file_test (path, G_FILE_TEST_IS_REGULAR)) - doc_uri = g_strconcat ("file://", path, NULL); - goto done; + YelpRrnType ret = YELP_RRN_TYPE_ERROR; + gchar *intern_section = NULL; + gchar *intern_uri = NULL; + g_assert (result != NULL); + if (*result != NULL) { + g_warning ("result is not empty: %s.", *result); + return ret; } - - - /* The URI is one of the following: - info:info_name - info:info_name#node - info:(info_name) - info:(info_name)node - In the first two, spaces are replaced with underscores. In the other - two, they're preserved. That should really only matter for the node - identifier, which we're not concerned with here. All we need is to - extract info_name. - */ - if (path[0] == '(') { - path++; - cur = strchr (path, ')'); - if (!cur) - goto done; - info_name = g_strndup (path, cur - path); + g_assert (section != NULL); + if (*section != NULL) { + g_warning ("section is not empty: %s.", *section); + return ret; } - else if ((cur = strchr (path, '#'))) - info_name = g_strndup (path, cur - path); - else - info_name = g_strdup (path); - - if (strstr (info_name, "/")) { - gchar *tmp = NULL; - gchar *real_name = NULL; - tmp = strstr (info_name, "/"); - tmp++; - real_name = g_strdup (tmp); - subdir = g_strndup (info_name, (strstr (info_name, "/") - info_name)); - g_free (info_name); - info_name = g_strdup (real_name); - g_free (real_name); - need_subdir = TRUE; + if (uri == NULL) { + g_warning ("URI is NULL"); + return ret; } + intern_section = resolve_get_section(uri); + intern_uri = resolve_remove_section (uri, intern_section); + if (intern_section && g_str_equal (intern_section, "")) { + intern_section = NULL; + } - pattern = g_strdup_printf ("%s.info.*", info_name); - pspec = g_pattern_spec_new (pattern); - g_free (pattern); - pattern = g_strdup_printf ("%s.?z*", info_name); - pspec1 = g_pattern_spec_new (pattern); - g_free (pattern); - - info_dot_info = g_strconcat (info_name, ".info", NULL); - - infopaths = yelp_get_info_paths (); - - for (i = 0; infopaths[i]; i++) { - dir = g_dir_open (infopath[i], 0, NULL); - if (dir) { - while ((filename = g_dir_read_name (dir))) { - g_free (test_filename); - test_filename = g_strconcat (infopath[i], "/", filename, NULL); - if (need_subdir && g_str_equal (filename, subdir)) { - gchar *dirname = NULL; - g_dir_close (dir); - dirname = g_strconcat (infopath[i], "/", subdir, NULL); - dir = g_dir_open (dirname, 0, NULL); - g_free (dirname); - filename = g_dir_read_name (dir); - need_subdir = FALSE; - } - else if (g_str_equal (filename,info_name) && - g_file_test (test_filename, G_FILE_TEST_IS_DIR)) { - /* In dir, they've specified the subdir but not the - * info file name. Here, do some work to get the name - * ...*/ - const gchar *real_filename; - - g_dir_close (dir); - dir = g_dir_open (test_filename, 0, NULL); - - while ((real_filename = g_dir_read_name (dir))) { - if ((g_str_equal (info_dot_info, real_filename) || - g_pattern_match_string (pspec, real_filename) || - g_pattern_match_string (pspec1, real_filename) || - g_str_equal (info_name, real_filename))) { - doc_uri = g_strconcat ("file://", - test_filename, "/", - real_filename, - NULL); - g_dir_close (dir); - goto done; - } - } - - } - else if (!need_subdir && - (g_str_equal (info_dot_info, filename) || - g_pattern_match_string (pspec, filename) || - g_pattern_match_string (pspec1, filename) || - g_str_equal (info_name, filename))) { - if (subdir) { - doc_uri = g_strconcat ("file://", - infopath[i], "/", subdir, "/", - filename, - NULL); - } else { - doc_uri = g_strconcat ("file://", - infopath[i], "/", - filename, - NULL); - - } - g_dir_close (dir); - goto done; - } + if (!strncmp (uri, "ghelp:", 6) || !strncmp (uri, "gnome-help:", 11)) { + ret = resolve_process_ghelp (intern_uri, result); + if (*result) { + *section = intern_section; + } + } else if (!strncmp (uri, "man:", 4)) { + ret = resolve_man_page (&uri[4], result, section); + if (ret == YELP_RRN_TYPE_ERROR) { + *result = NULL; + *section = NULL; + } + /* Man page */ + } else if (!strncmp (uri, "info:", 5)) { + /* info page */ + + gchar *info_name = intern_uri; + gchar *info_sect = intern_section; + gboolean free_stuff = FALSE; + RrnInfoEntry *entry = NULL; + + if (g_str_equal (&uri[5], "dir")) { + *section = g_strdup ("info"); + *result = NULL; + ret = YELP_RRN_TYPE_TOC; + return ret; + } + + if (!intern_section) { + gchar *lbrace = NULL; + gchar *rbrace = NULL; + lbrace = strchr (info_name, '('); + rbrace = strchr (info_name, ')'); + if (lbrace && rbrace) { + info_name = g_strndup (lbrace+1, (rbrace-lbrace-1)); + info_sect = g_strdup (rbrace+1); + free_stuff = TRUE; + } else { + info_name += 5; } - g_dir_close (dir); + } else { + info_name += 5; } - } - /* If we got this far and doc_uri is still NULL, we resort to looking - * for a man page. Let above handle that. We just let it know that - * somethings gotta be done */ - if (doc_uri == NULL) { - gchar *tmp; - tmp = strchr (uri, ':'); - doc_uri = g_strconcat ("man",tmp,NULL); + entry = rrn_info_find_from_uri (info_name, info_sect); + if (entry) { + ret = YELP_RRN_TYPE_INFO; + if (entry->section) + *section = g_strdup (entry->section); + else + *section = g_strdup (intern_section); + *result = g_strdup (entry->base_filename); + } else { + ret = resolve_man_page (&uri[5], result, section); + if (!ret) { + ret = YELP_RRN_TYPE_ERROR; + *section = NULL; + *result = NULL; + } + } + if (free_stuff) { + g_free (info_name); + g_free (info_sect); + } + } else if (!strncmp (uri, "file:", 5)) { + int file_cut = 5; + while (uri[file_cut+1] == '/') + file_cut++; + ret = resolve_full_file (&intern_uri[file_cut]); + if (ret == YELP_RRN_TYPE_EXTERNAL) { + *section = NULL; + *result = g_strdup (uri); + } + else if (ret == YELP_RRN_TYPE_ERROR) { + *section = NULL; + *result = NULL; + } else { + *result = g_strdup (&intern_uri[file_cut]); + *section = intern_section; + } + /* full file path. Ensure file exists and determine type */ + } else if (!strncmp (uri, "x-yelp-toc:", 11)) { + ret = YELP_RRN_TYPE_TOC; + if (strlen (uri) > 11) { + *section = g_strdup (&uri[11]); + } else { + *section = g_strdup("index"); + } + *result = g_strdup ("x-yelp-toc:"); + /* TOC page */ + } else if (!strncmp (uri, "x-yelp-search:", 14)) { + /* Search pager request. *result contains the search terms */ + *result = g_strdup (uri); + *section = g_strdup ("results"); + ret = YELP_RRN_TYPE_SEARCH; + } else if (g_file_test (intern_uri, G_FILE_TEST_EXISTS)) { + /* Full path */ + ret = resolve_full_file (intern_uri); + if (ret == YELP_RRN_TYPE_EXTERNAL) { + *section = NULL; + *result = g_strdup (uri); + } + else if (ret == YELP_RRN_TYPE_ERROR) { + *section = NULL; + *result = NULL; + } else { + *result = g_strdup (intern_uri); + *section = g_strdup (intern_section); + } + } else if (*uri == '/' || g_str_has_suffix (uri, ".xml")) { + /* Quite probable it was supposed to be ours, but + * the file doesn't exist. Hence, we should bin it + */ + ret = YELP_RRN_TYPE_ERROR; + *result = NULL; + *section = NULL; + } else { + /* We really don't care what it is. It's not ours. Let + * someone else handle it + */ + ret = YELP_RRN_TYPE_EXTERNAL; + *result = g_strdup (uri); + *section = NULL; } - done: - if (pspec) - g_pattern_spec_free (pspec); - if (pspec1) - g_pattern_spec_free (pspec1); - g_free (test_filename); - g_free (subdir); - g_free (info_dot_info); - g_free (info_name); - - return doc_uri; + return ret; } diff --git a/src/yelp-utils.h b/src/yelp-utils.h index a2fede29..faf08e74 100644 --- a/src/yelp-utils.h +++ b/src/yelp-utils.h @@ -25,106 +25,32 @@ #include <glib/gi18n.h> -typedef struct _YelpDocInfo YelpDocInfo; -typedef struct _YelpDocPage YelpDocPage; - -typedef enum { - YELP_DOC_TYPE_ERROR = 0, - YELP_DOC_TYPE_DOCBOOK_XML, - YELP_DOC_TYPE_DOCBOOK_SGML, - YELP_DOC_TYPE_HTML, - YELP_DOC_TYPE_XHTML, - YELP_DOC_TYPE_MAN, - YELP_DOC_TYPE_INFO, - YELP_DOC_TYPE_TOC, - YELP_DOC_TYPE_EXTERNAL, - YELP_DOC_TYPE_SEARCH -} YelpDocType; - typedef enum { - YELP_URI_TYPE_ERROR = 0, - YELP_URI_TYPE_FILE = 1 << 0, - YELP_URI_TYPE_GHELP = 1 << 1, - YELP_URI_TYPE_MAN = 1 << 2, - YELP_URI_TYPE_INFO = 1 << 3, - YELP_URI_TYPE_TOC = 1 << 4, - YELP_URI_TYPE_EXTERNAL = 1 << 5, - YELP_URI_TYPE_SEARCH = 1 << 6, - - YELP_URI_TYPE_NO_FILE = - YELP_URI_TYPE_GHELP | - YELP_URI_TYPE_MAN | - YELP_URI_TYPE_INFO | - YELP_URI_TYPE_TOC | - YELP_URI_TYPE_EXTERNAL | - YELP_URI_TYPE_SEARCH, - YELP_URI_TYPE_ANY = - YELP_URI_TYPE_FILE | - YELP_URI_TYPE_NO_FILE -} YelpURIType; - -#include "yelp-pager.h" - -struct _YelpDocPage { - YelpDocInfo *document; - gchar *page_id; - gchar *title; - gchar *contents; - - gchar *prev_id; - gchar *next_id; - gchar *toc_id; -}; - -const char * yelp_dot_dir (void); -YelpDocInfo * yelp_doc_info_new (const gchar *uri, - gboolean trust_uri); -YelpDocInfo * yelp_doc_info_get (const gchar *uri, - gboolean trust_uri); -void yelp_doc_info_add_uri (YelpDocInfo *doc_info, - const gchar *uri, - YelpURIType type); -YelpDocInfo * yelp_doc_info_ref (YelpDocInfo *doc); -void yelp_doc_info_unref (YelpDocInfo *doc); -void yelp_doc_info_free (YelpDocInfo *doc); - -YelpPager * yelp_doc_info_get_pager (YelpDocInfo *doc); -void yelp_doc_info_set_pager (YelpDocInfo *doc, - YelpPager *pager); - -const gchar * yelp_doc_info_get_id (YelpDocInfo *doc); -void yelp_doc_info_set_id (YelpDocInfo *doc, - gchar *id); -const gchar * yelp_doc_info_get_title (YelpDocInfo *doc); -void yelp_doc_info_set_title (YelpDocInfo *doc, - gchar *title); -const gchar * yelp_doc_info_get_description (YelpDocInfo *doc); -void yelp_doc_info_set_description (YelpDocInfo *doc, - gchar *desc); -const gchar * yelp_doc_info_get_language (YelpDocInfo *doc); -void yelp_doc_info_set_language (YelpDocInfo *doc, - gchar *language); -const gchar * yelp_doc_info_get_category (YelpDocInfo *doc); -void yelp_doc_info_set_category (YelpDocInfo *doc, - gchar *category); -gint yelp_doc_info_cmp_language (YelpDocInfo *doc1, - YelpDocInfo *doc2); - -YelpDocType yelp_doc_info_get_type (YelpDocInfo *doc); -gchar * yelp_doc_info_get_uri (YelpDocInfo *doc, - gchar *frag_id, - YelpURIType uri_type); -gchar * yelp_doc_info_get_filename (YelpDocInfo *doc); -gboolean yelp_doc_info_equal (YelpDocInfo *doc1, - YelpDocInfo *doc2); - -void yelp_doc_page_free (YelpDocPage *page); - -gchar * yelp_uri_get_fragment (const gchar *uri); -gchar * yelp_uri_get_relative (gchar *base, - gchar *ref); -gchar ** yelp_get_info_paths (void); - -gchar ** yelp_get_man_paths (void); + YELP_RRN_TYPE_DOC = 0, + YELP_RRN_TYPE_MAN, + YELP_RRN_TYPE_INFO, + YELP_RRN_TYPE_HTML, + YELP_RRN_TYPE_XHTML, + YELP_RRN_TYPE_TOC, + YELP_RRN_TYPE_SEARCH, + YELP_RRN_TYPE_NOT_FOUND, + YELP_RRN_TYPE_EXTERNAL, + YELP_RRN_TYPE_ERROR +} YelpRrnType; + + +/* Generic resolver function. Takes in the uri (which can be + * anything) and returns the type (enum above) + * The result is filled with a new string that the callee + * must free, except when returning YELP_TYPE_ERROR, when it will + * be NULL. The result is the base filename for the document. + * The section will be filled when the requested uri has a section + * otherwise, it will be NULL + * Both *result and *section must be NULL when calling (otherwise + * we throw an error + */ +YelpRrnType yelp_uri_resolve (gchar *uri, + gchar **result, + gchar **section); #endif /* __YELP_UTILS_H__ */ diff --git a/src/yelp-window.c b/src/yelp-window.c index 4bf5764e..05f64ca2 100644 --- a/src/yelp-window.c +++ b/src/yelp-window.c @@ -37,27 +37,21 @@ #include <libgnome/gnome-url.h> #include "yelp-bookmarks.h" -#include "yelp-db-pager.h" -#include "yelp-db-print-pager.h" -#include "yelp-error.h" +#include "yelp-utils.h" #include "yelp-html.h" -#include "yelp-pager.h" #include "yelp-settings.h" -#include "yelp-toc-pager.h" +#include "yelp-toc.h" +#include "yelp-docbook.h" +#include "yelp-db-print.h" #include "yelp-window.h" #include "yelp-print.h" #include "yelp-debug.h" -#ifdef ENABLE_MAN -#include "yelp-man-pager.h" -#endif -#ifdef ENABLE_INFO -#include "yelp-info-pager.h" -#endif -#ifdef ENABLE_SEARCH -#include "yelp-search-pager.h" + +#include "yelp-man.h" +#include "yelp-info.h" +#include "yelp-search.h" #include "gtkentryaction.h" -#endif #define YELP_CONFIG_WIDTH "/yelp/Geometry/width" #define YELP_CONFIG_HEIGHT "/yelp/Geometry/height" @@ -67,14 +61,13 @@ #define BUFFER_SIZE 16384 typedef struct { - YelpWindow *window; - gchar *uri; -} YelpLoadData; - -typedef struct { - YelpDocInfo *doc_info; + YelpDocument *doc; + YelpRrnType type; + gchar *uri; + gchar *req_uri; gchar *frag_id; + gchar *base_uri; GtkWidget *menu_entry; YelpWindow *window; gint callback; @@ -83,49 +76,46 @@ typedef struct { gchar *frag_title; } YelpHistoryEntry; +typedef struct { + YelpPage *page; + YelpWindow *window; + +} YelpLoadData; + static void window_init (YelpWindow *window); static void window_class_init (YelpWindowClass *klass); static void window_error (YelpWindow *window, - GError *error, + gchar *title, + gchar *message, gboolean pop); static void window_populate (YelpWindow *window); static void window_populate_find (YelpWindow *window, GtkWidget *find_bar); static void window_set_sections (YelpWindow *window, GtkTreeModel *sections); -static void window_do_load (YelpWindow *window, - YelpDocInfo *doc_info, - gchar *frag_id); -static gboolean window_do_load_pager (YelpWindow *window, - YelpDocInfo *doc_info, - gchar *frag_id); +static void window_set_section_cursor (YelpWindow *window, + GtkTreeModel *model); + static gboolean window_do_load_html (YelpWindow *window, - YelpDocInfo *doc_info, - gchar *frag_id); + gchar *uri, + gchar *frag_id, + YelpRrnType type, + gboolean need_history); static void window_set_loading (YelpWindow *window); -static void window_handle_page (YelpWindow *window, - YelpPage *page); -static void window_disconnect (YelpWindow *window); +static void window_setup_window (YelpWindow *window, + YelpRrnType type, + gchar *loading_uri, + gchar *frag, + gchar *req_uri, + gchar *base_uri, + gboolean add_history); /** Window Callbacks **/ static gboolean window_configure_cb (GtkWidget *widget, GdkEventConfigure *event, gpointer data); -/** Pager Callbacks **/ -static void pager_start_cb (YelpPager *pager, - gpointer user_data); -static void pager_page_cb (YelpPager *pager, - gchar *page_id, - gpointer user_data); -static void pager_error_cb (YelpPager *pager, - gpointer user_data); -static void pager_cancel_cb (YelpPager *pager, - gpointer user_data); -static void pager_finish_cb (YelpPager *pager, - gpointer user_data); - /** Gecko Callbacks **/ static void html_uri_selected_cb (YelpHtml *html, gchar *uri, @@ -186,8 +176,6 @@ static void window_open_link_cb (GtkAction *action, YelpWindow *window); static void window_open_link_new_cb (GtkAction *action, YelpWindow *window); static void window_copy_mail_cb (GtkAction *action, YelpWindow *window); -static gboolean window_load_async (YelpLoadData *data); - /** History Functions **/ static void history_push_back (YelpWindow *window); static void history_push_forward (YelpWindow *window); @@ -200,8 +188,8 @@ static void history_back_to (GtkMenuItem *menuitem, YelpHistoryEntry *entry); static void history_forward_to (GtkMenuItem *menuitem, YelpHistoryEntry *entry); - -static void load_data_free (YelpLoadData *data); +static void history_load_entry (YelpWindow *window, + YelpHistoryEntry *entry); static void location_response_cb (GtkDialog *dialog, gint id, @@ -227,6 +215,9 @@ static void window_find_previous_cb (GtkAction *action, YelpWindow *window); static gboolean tree_model_iter_following (GtkTreeModel *model, GtkTreeIter *iter); +static gboolean window_write_html (YelpLoadData *data); +static void window_write_print_html (YelpHtml *html, + YelpPage *page); enum { NEW_WINDOW_REQUESTED, @@ -263,15 +254,20 @@ struct _YelpWindowPriv { GtkWidget *popup; gint merge_id; GtkWidget *maillink; - gchar *uri; /* Location Information */ - YelpDocInfo *current_doc; + gchar *uri; + YelpRrnType current_type; + gchar *req_uri; + gchar *base_uri; + gint current_request; + YelpDocument *current_document; gchar *current_frag; GSList *history_back; GSList *history_forward; GtkWidget *back_menu; GtkWidget *forward_menu; + GtkTreeModel *current_sidebar; /* Callbacks and Idles */ gulong start_handler; @@ -279,9 +275,9 @@ struct _YelpWindowPriv { gulong error_handler; gulong cancel_handler; gulong finish_handler; - guint idle_write; gint toc_pause; + guint html_idle_handle; GtkActionGroup *action_group; GtkUIManager *ui_manager; @@ -292,22 +288,6 @@ struct _YelpWindowPriv { gchar *toc_id; }; -typedef struct _IdleWriterContext IdleWriterContext; -struct _IdleWriterContext { - YelpWindow *window; - - enum { - IDLE_WRITER_MEMORY, - IDLE_WRITER_VFS - } type; - - const gchar *buffer; - gint cur; - gint length; -}; - -static gboolean idle_write (IdleWriterContext *context); - #define TARGET_TYPE_URI_LIST "text/uri-list" enum { TARGET_URI_LIST @@ -326,12 +306,12 @@ static const GtkActionEntry entries[] = { NULL, G_CALLBACK (window_new_window_cb) }, { "PrintDocument", NULL, - N_("Print This Document"), + N_("Print This Document ..."), NULL, NULL, G_CALLBACK (window_print_document_cb) }, { "PrintPage", NULL, - N_("Print This Page"), + N_("Print This Page ..."), NULL, NULL, G_CALLBACK (window_print_page_cb) }, @@ -519,10 +499,6 @@ window_init (YelpWindow *window) static void window_dispose (GObject *object) { - YelpWindow *window = YELP_WINDOW (object); - - window_disconnect (YELP_WINDOW (window)); - parent_class->dispose (object); } @@ -537,8 +513,6 @@ window_finalize (GObject *object) g_free (priv->find_string); - if (priv->current_doc) - yelp_doc_info_unref (priv->current_doc); g_free (priv->current_frag); /* FIXME there are many more things to free */ @@ -569,6 +543,13 @@ window_class_init (YelpWindowClass *klass) g_type_class_add_private (klass, sizeof (YelpWindowPriv)); } +const gchar * +yelp_window_get_uri (YelpWindow *window) +{ + + return ((const gchar *) window->priv->uri); +} + /** History Functions *********************************************************/ static void @@ -580,13 +561,18 @@ history_push_back (YelpWindow *window) gchar *title; g_return_if_fail (YELP_IS_WINDOW (window)); - g_return_if_fail (window->priv->current_doc != NULL); priv = window->priv; entry = g_new0 (YelpHistoryEntry, 1); - entry->doc_info = yelp_doc_info_ref (priv->current_doc); + entry->frag_id = g_strdup (priv->current_frag); + entry->uri = g_strdup (priv->uri); + entry->req_uri = priv->req_uri; + entry->doc = priv->current_document; + entry->type = priv->current_type; + entry->base_uri = g_strdup (priv->base_uri); + /* page_title, frag_title */ priv->history_back = g_slist_prepend (priv->history_back, entry); @@ -596,8 +582,8 @@ history_push_back (YelpWindow *window) g_object_set (G_OBJECT (action), "sensitive", TRUE, NULL); title = (gchar *) gtk_window_get_title (GTK_WINDOW (window)); - if (g_str_equal (title, "Loading...")) - entry->page_title = g_strdup ("Unknown Page"); + if (g_str_equal (title, _("Loading..."))) + entry->page_title = g_strdup (_("Unknown Page")); else entry->page_title = g_strdup (title); @@ -626,14 +612,19 @@ history_push_forward (YelpWindow *window) gchar *title; g_return_if_fail (YELP_IS_WINDOW (window)); - g_return_if_fail (window->priv->current_doc != NULL); priv = window->priv; entry = g_new0 (YelpHistoryEntry, 1); - entry->doc_info = yelp_doc_info_ref (priv->current_doc); + entry->frag_id = g_strdup (priv->current_frag); - /* page_title, frag_title */ + entry->uri = g_strdup (priv->uri); + entry->req_uri = priv->req_uri; + entry->doc = priv->current_document; + entry->type = priv->current_type; + entry->base_uri = g_strdup (priv->base_uri); + + /* page_title, frag_title */ priv->history_forward = g_slist_prepend (priv->history_forward, entry); @@ -643,8 +634,8 @@ history_push_forward (YelpWindow *window) title = (gchar *) gtk_window_get_title (GTK_WINDOW (window)); - if (g_str_equal (title, "Loading...")) - entry->page_title = g_strdup ("Unknown Page"); + if (g_str_equal (title, _("Loading..."))) + entry->page_title = g_strdup (_("Unknown Page")); else entry->page_title = g_strdup (title); @@ -698,17 +689,12 @@ history_step_back (YelpWindow *window) priv = window->priv; entry = history_pop_back (window); - if (priv->current_doc) { - yelp_doc_info_unref (priv->current_doc); - priv->current_doc = NULL; - } if (priv->current_frag) { g_free (priv->current_frag); priv->current_frag = NULL; } if (entry) { - priv->current_doc = yelp_doc_info_ref (entry->doc_info); priv->current_frag = g_strdup (entry->frag_id); history_entry_free (entry); } else { @@ -771,10 +757,10 @@ history_entry_free (YelpHistoryEntry *entry) { g_return_if_fail (entry != NULL); - yelp_doc_info_unref (entry->doc_info); g_free (entry->frag_id); g_free (entry->page_title); g_free (entry->frag_title); + g_free (entry->base_uri); if (entry->menu_entry) { gtk_widget_destroy (entry->menu_entry); @@ -821,16 +807,7 @@ history_back_to (GtkMenuItem *menuitem, YelpHistoryEntry *entry) latest->menu_entry = NULL; } - if (priv->current_doc) - yelp_doc_info_unref (priv->current_doc); - if (priv->current_frag) - g_free (priv->current_frag); - - priv->current_doc = yelp_doc_info_ref (entry->doc_info); - priv->current_frag = g_strdup (entry->frag_id); - - window_do_load (window, entry->doc_info, entry->frag_id); - + history_load_entry (window, entry); } static void @@ -870,118 +847,245 @@ history_forward_to (GtkMenuItem *menuitem, YelpHistoryEntry *entry) latest->menu_entry = NULL; } - if (priv->current_doc) - yelp_doc_info_unref (priv->current_doc); - if (priv->current_frag) - g_free (priv->current_frag); + history_load_entry (window, entry); + +} - priv->current_doc = yelp_doc_info_ref (entry->doc_info); - priv->current_frag = g_strdup (entry->frag_id); +/******************************************************************************/ - window_do_load (window, entry->doc_info, entry->frag_id); +GtkWidget * +yelp_window_new (GNode *doc_tree, GList *index) +{ + return GTK_WIDGET (g_object_new (YELP_TYPE_WINDOW, NULL)); } static void -load_data_free (YelpLoadData *data) +page_request_cb (YelpDocument *document, + YelpDocumentSignal signal, + gint req_id, + gpointer *func_data, + YelpWindow *window) { - g_return_if_fail (data != NULL); + YelpError *error; + YelpLoadData *data; - g_object_unref (data->window); - g_free (data->uri); + switch (signal) { + case YELP_DOCUMENT_SIGNAL_PAGE: + window_set_sections (window, yelp_document_get_sections (document)); - g_free (data); -} + data = g_new0 (YelpLoadData, 1); + data->window = window; + data->page = (YelpPage *) func_data; -/******************************************************************************/ + window->priv->html_idle_handle = g_idle_add ((GSourceFunc)window_write_html, data); -GtkWidget * -yelp_window_new (GNode *doc_tree, GList *index) -{ - return GTK_WIDGET (g_object_new (YELP_TYPE_WINDOW, NULL)); + window->priv->current_request = -1; + yelp_page_free ((YelpPage *) func_data); + gdk_window_set_cursor (GTK_WIDGET (window)->window, NULL); + break; + case YELP_DOCUMENT_SIGNAL_TITLE: + /* We don't need to actually handle title signals as gecko + * is wise enough to not annoy me by not handling it + */ + g_free (func_data); + break; + case YELP_DOCUMENT_SIGNAL_ERROR: + error = (YelpError *) func_data; + window_error (window, (gchar *) yelp_error_get_title (error), + (gchar *) yelp_error_get_message (error), FALSE); + yelp_error_free (error); + gdk_window_set_cursor (GTK_WIDGET (window)->window, NULL); + break; + default: + g_assert_not_reached(); + } } -void -yelp_window_load (YelpWindow *window, const gchar *uri) +static void +window_setup_window (YelpWindow *window, YelpRrnType type, + gchar *loading_uri, gchar *frag, gchar *req_uri, + gchar *base_uri, gboolean add_history) { + /* Before asking the YelpDocument to find + * a page, this should be called to set up various + * things (such as fixing history, setting + * menu items to sensitive etc.) + * These are all read from the YelpWindow struct + * so they must be set BEFORE calling this. + */ YelpWindowPriv *priv; - YelpDocInfo *doc_info; - gchar *frag_id; GtkAction *action; - gchar *real_uri; + g_return_if_fail (YELP_IS_WINDOW (window)); - if (g_str_has_prefix (uri, "info:") && g_str_has_suffix (uri, "dir")) { - real_uri = g_strdup ("x-yelp-toc:#Info"); - } else { - real_uri = g_strdup (uri); + priv = window->priv; + + if (priv->current_request != -1) { + yelp_document_cancel_page (priv->current_document, priv->current_request); + priv->current_request = -1; + } else if (add_history) { + gchar *tmp = window->priv->base_uri; + window->priv->base_uri = base_uri; + history_push_back(window); + window->priv->base_uri = tmp; } - debug_print (DB_FUNCTION, "entering\n"); - debug_print (DB_ARG, " uri = \"%s\"\n", uri); - if (!real_uri) { - GError *error = NULL; - g_set_error (&error, YELP_ERROR, YELP_ERROR_NO_DOC, - _("The Uniform Resource Identifier for the file is " - "invalid.")); - window_error (window, error, FALSE); - return; + if (window->priv->html_idle_handle) { + g_source_remove (window->priv->html_idle_handle); + window->priv->html_idle_handle = 0; } - priv = window->priv; + window_set_loading (window); - doc_info = yelp_doc_info_get (real_uri, FALSE); - if (!doc_info) { - GError *error = NULL; - g_set_error (&error, YELP_ERROR, YELP_ERROR_NO_DOC, - _("The Uniform Resource Identifier ‘%s’ is invalid " - "or does not point to an actual file."), - real_uri); - window_error (window, error, FALSE); - return; + priv->current_type = type; + priv->uri = loading_uri; + priv->current_frag = g_strdup (frag); + priv->req_uri = g_strdup (req_uri); + + switch (priv->current_type) { + case YELP_RRN_TYPE_DOC: + action = gtk_action_group_get_action (window->priv->action_group, + "PrintDocument"); + g_object_set (G_OBJECT (action), "sensitive", TRUE, NULL); + + action = gtk_action_group_get_action (window->priv->action_group, + "AboutDocument"); + g_object_set (G_OBJECT (action), "sensitive", TRUE, NULL); + break; + default: + action = gtk_action_group_get_action (window->priv->action_group, + "PrintDocument"); + g_object_set (G_OBJECT (action), "sensitive", FALSE, NULL); + + action = gtk_action_group_get_action (window->priv->action_group, + "AboutDocument"); + g_object_set (G_OBJECT (action), "sensitive", FALSE, NULL); + break; + } - frag_id = yelp_uri_get_fragment (real_uri); +} - if (priv->current_doc && yelp_doc_info_equal (priv->current_doc, doc_info)) { - if (priv->current_frag) { - if (frag_id && g_str_equal (priv->current_frag, frag_id)) - goto load; - } - else if (!frag_id) - goto load; - } - if (priv->current_doc) - history_push_back (window); - history_clear_forward (window); +void +yelp_window_load (YelpWindow *window, const gchar *uri) +{ + YelpWindowPriv *priv; + gchar *frag_id = NULL; + gchar *real_uri = NULL; + gchar *trace_uri = NULL; + YelpRrnType type = YELP_RRN_TYPE_ERROR; + YelpDocument *doc = NULL; + gchar *current_base = NULL; - if (priv->current_doc) - yelp_doc_info_unref (priv->current_doc); - if (priv->current_frag) - g_free (priv->current_frag); + g_return_if_fail (YELP_IS_WINDOW (window)); - action = gtk_action_group_get_action (priv->action_group, - "PrintDocument"); - g_object_set (G_OBJECT (action), "sensitive", FALSE, NULL); + priv = window->priv; + current_base = g_strdup (priv->base_uri); + + /* If someone asks for info:dir, they WILL get redirected to + * our index. Tough. + */ + if (g_str_has_prefix (uri, "info:") && g_str_has_suffix (uri, "dir")) { + trace_uri = g_strdup ("x-yelp-toc:#Info"); + } else { + trace_uri = g_strdup (uri); + } - priv->current_doc = yelp_doc_info_ref (doc_info); - priv->current_frag = g_strdup (frag_id); + /* The way this route was taken, we need to clear the + * forward history now + */ + history_clear_forward (window); - load: - window_do_load (window, doc_info, frag_id); + type = yelp_uri_resolve (trace_uri, &real_uri, &frag_id); + if (type == YELP_RRN_TYPE_ERROR) { + gchar *message = g_strdup_printf (_("The requested URI \"%s\" is invalid"), trace_uri); + window_error (window, _("Unable to load page"), message, FALSE); + g_free (message); + return; + } - if (priv->current_frag != frag_id) - g_free (frag_id); - g_free (real_uri); -} + if (priv->uri && g_str_equal (real_uri, priv->uri)) { + doc = priv->current_document; + } else { + g_free (priv->base_uri); + switch (type) { + case YELP_RRN_TYPE_TOC: + doc = yelp_toc_get (); + priv->base_uri = g_strdup ("file:///fakefile"); + break; + case YELP_RRN_TYPE_MAN: + priv->base_uri = g_strdup_printf ("file:/%s", real_uri); + doc = yelp_man_new (real_uri); + break; + case YELP_RRN_TYPE_INFO: + priv->base_uri = g_strdup_printf ("file:/%s", real_uri); + if (!frag_id) { + frag_id = g_strdup ("Top"); + } else { + g_strdelimit (frag_id, " ", '_'); + } + doc = yelp_info_new (real_uri); + break; + case YELP_RRN_TYPE_DOC: + priv->base_uri = g_strdup_printf ("file:/%s", real_uri); + doc = yelp_docbook_new (real_uri); + break; + case YELP_RRN_TYPE_SEARCH: + doc = yelp_search_new (&real_uri[14]); /* to remove x-yelp-search:*/ + break; + case YELP_RRN_TYPE_HTML: + case YELP_RRN_TYPE_XHTML: + window_do_load_html (window, real_uri, frag_id, type, TRUE); + break; + case YELP_RRN_TYPE_EXTERNAL: + { + gchar *stdout = NULL; + gchar *stderr = NULL; + gchar *cmd = NULL; + gint status = 0; + GError *error = NULL; + cmd = g_strdup_printf ("gnome-open %s", uri); + if (!g_spawn_command_line_sync (cmd, &stdout, &stderr, &status, &error)) { + g_free (error); + error = NULL; + g_free (cmd); + cmd = g_strdup_printf ("xdg-open %s", uri); + if (!g_spawn_command_line_sync (cmd, &stdout, &stderr, &status, &error)) { + window_error(window, _("Error executing \"gnome-open\""), error->message, FALSE); + return; + } + } + if (status) { + gchar *message = g_strdup_printf (_("The requested URI \"%s\" is invalid"), trace_uri); + window_error (window, _("Unable to load page"), message, FALSE); + return; + } + } -YelpDocInfo * -yelp_window_get_doc_info (YelpWindow *window) -{ - g_return_val_if_fail (YELP_IS_WINDOW (window), NULL); + default: + break; + } + } + + if (doc) { + gboolean need_hist = FALSE; + if (!frag_id) + frag_id = g_strdup ("index"); + + if (priv->current_document || (priv->current_type == YELP_RRN_TYPE_HTML || + priv->current_type == YELP_RRN_TYPE_XHTML)) + need_hist = TRUE; + window_setup_window (window, type, real_uri, frag_id, + (gchar *) uri, current_base, need_hist); - return window->priv->current_doc; + priv->current_request = yelp_document_get_page (doc, + frag_id, + (YelpDocumentFunc) page_request_cb, + (void *) window); + } + priv->current_document = doc; } GtkUIManager * @@ -992,109 +1096,10 @@ yelp_window_get_ui_manager (YelpWindow *window) return window->priv->ui_manager; } -static void -window_do_load (YelpWindow *window, - YelpDocInfo *doc_info, - gchar *frag_id) -{ - GtkAction *action; - GtkAction *about; - GError *error = NULL; - gchar *uri; - - g_return_if_fail (YELP_IS_WINDOW (window)); - g_return_if_fail (doc_info != NULL); - - debug_print (DB_FUNCTION, "entering\n"); - - action = gtk_action_group_get_action (window->priv->action_group, - "PrintDocument"); - g_object_set (G_OBJECT (action), "sensitive", FALSE, NULL); - - about = gtk_action_group_get_action (window->priv->action_group, - "AboutDocument"); - g_object_set (G_OBJECT (about), "sensitive", FALSE, NULL); - - switch (yelp_doc_info_get_type (doc_info)) { - case YELP_DOC_TYPE_MAN: -#ifdef ENABLE_MAN - window_do_load_pager (window, doc_info, frag_id); - break; -#else - g_set_error (&error, YELP_ERROR, YELP_ERROR_FORMAT, - _("Man pages are not supported in this version.")); - break; -#endif - - case YELP_DOC_TYPE_INFO: -#ifdef ENABLE_INFO - window_do_load_pager (window, doc_info, frag_id); - break; -#else - g_set_error (&error, YELP_ERROR, YELP_ERROR_FORMAT, - _("GNU info pages are not supported in this version")); - break; -#endif - - case YELP_DOC_TYPE_DOCBOOK_XML: - g_object_set (G_OBJECT (action), "sensitive", TRUE, NULL); - g_object_set (G_OBJECT (about), "sensitive", TRUE, NULL); - case YELP_DOC_TYPE_TOC: - window_do_load_pager (window, doc_info, frag_id); - break; - case YELP_DOC_TYPE_SEARCH: -#ifdef ENABLE_SEARCH - window_do_load_pager (window, doc_info, frag_id); - break; -#else - g_set_error (&error, YELP_ERROR, YELP_ERROR_FORMAT, - _("Search is not supported in this version.")); - break; -#endif - case YELP_DOC_TYPE_DOCBOOK_SGML: - g_set_error (&error, YELP_ERROR, YELP_ERROR_FORMAT, - _("SGML documents are no longer supported. Please ask " - "the author of the document to convert to XML.")); - break; - case YELP_DOC_TYPE_HTML: - case YELP_DOC_TYPE_XHTML: - window_do_load_html (window, doc_info, frag_id); - break; - case YELP_DOC_TYPE_EXTERNAL: - history_step_back (window); - uri = yelp_doc_info_get_uri (doc_info, NULL, YELP_URI_TYPE_ANY); - gnome_url_show (uri, &error); - g_free (uri); - break; - case YELP_DOC_TYPE_ERROR: - default: - uri = yelp_doc_info_get_uri (doc_info, NULL, YELP_URI_TYPE_NO_FILE); - if (!uri) - uri = yelp_doc_info_get_uri (doc_info, NULL, YELP_URI_TYPE_FILE); - if (uri) - g_set_error (&error, YELP_ERROR, YELP_ERROR_NO_DOC, - _("The Uniform Resource Identifier ‘%s’ is invalid " - "or does not point to an actual file."), - uri); - else - g_set_error (&error, YELP_ERROR, YELP_ERROR_NO_DOC, - _("The Uniform Resource Identifier for the file is " - "invalid.")); - g_free (uri); - break; - } - - if (error) { - window_error (window, error, TRUE); - } - - window_find_buttons_set_sensitive (window, TRUE, TRUE); -} - /******************************************************************************/ static void -window_error (YelpWindow *window, GError *error, gboolean pop) +window_error (YelpWindow *window, gchar *title, gchar *message, gboolean pop) { YelpWindowPriv *priv; GtkWidget *dialog; @@ -1120,17 +1125,14 @@ window_error (YelpWindow *window, GError *error, gboolean pop) GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, - "%s", yelp_error_get_primary (error)); + title); gtk_message_dialog_format_secondary_markup - (GTK_MESSAGE_DIALOG (dialog), "%s", - yelp_error_get_secondary (error)); - gtk_dialog_run (GTK_DIALOG (dialog)); + (GTK_MESSAGE_DIALOG (dialog), message); + gtk_dialog_run (GTK_DIALOG (dialog)); - g_error_free (error); gtk_widget_destroy (dialog); } -#ifdef ENABLE_SEARCH static char * encode_search_uri (const char *search_terms) { @@ -1158,15 +1160,28 @@ search_activated (GtkAction *action, !strstr (&(search_terms[4])," ")) { uri = g_strdup (search_terms); uri[3]=':'; + } else if (g_str_has_prefix (search_terms, "info ")) { + gint count = 0; + gchar *spaces; + + spaces = strchr (search_terms, ' '); + while (spaces) { + count++; + spaces = strchr (search_terms, ' '); + } + if (count == 1) { + uri = g_strdup (search_terms); + uri[4] = ':'; + } else { + uri = encode_search_uri (search_terms); + } } else { uri = encode_search_uri (search_terms); } - yelp_window_load (window, uri); g_free (uri); } -#endif static void @@ -1185,6 +1200,8 @@ window_populate (YelpWindow *window) priv = window->priv; + priv->current_request = -1; + priv->main_box = gtk_vbox_new (FALSE, 0); gtk_container_add (GTK_CONTAINER (window), priv->main_box); @@ -1217,7 +1234,6 @@ window_populate (YelpWindow *window) gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (f_proxy), priv->forward_menu); -#ifdef ENABLE_SEARCH action = gtk_entry_action_new ("Search", _("_Search:"), _("Search for other documentation"), @@ -1225,7 +1241,6 @@ window_populate (YelpWindow *window) g_signal_connect (G_OBJECT (action), "activate", G_CALLBACK (search_activated), window); gtk_action_group_add_action (priv->action_group, action); -#endif priv->ui_manager = gtk_ui_manager_new (); gtk_ui_manager_insert_action_group (priv->ui_manager, priv->action_group, 0); @@ -1240,16 +1255,14 @@ window_populate (YelpWindow *window) if (!gtk_ui_manager_add_ui_from_file (priv->ui_manager, DATADIR "/yelp/ui/yelp-ui.xml", &error)) { - window_error (window, error, FALSE); + window_error (window, _("Cannot create window"), error->message, FALSE); } -#ifdef ENABLE_SEARCH if (!gtk_ui_manager_add_ui_from_file (priv->ui_manager, DATADIR "/yelp/ui/yelp-search-ui.xml", &error)) { - window_error (window, error, FALSE); + window_error (window, _("Cannot create search component"), error->message, FALSE); } -#endif yelp_bookmarks_register (window); @@ -1299,7 +1312,7 @@ window_populate (YelpWindow *window) gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->side_sects), -1, NULL, gtk_cell_renderer_text_new (), - "text", YELP_PAGER_COLUMN_TITLE, + "text", YELP_DOCUMENT_COLUMN_TITLE, NULL); /* DISABLE FOR NOW @@ -1367,7 +1380,6 @@ window_populate (YelpWindow *window) gtk_widget_show (priv->main_box); gtk_container_set_focus_child (GTK_CONTAINER (window), GTK_WIDGET (priv->html_view)); - } static void @@ -1447,197 +1459,67 @@ window_set_sections (YelpWindow *window, g_return_if_fail (YELP_IS_WINDOW (window)); priv = window->priv; - gtk_tree_view_set_model (GTK_TREE_VIEW (priv->side_sects), sections); + gtk_tree_view_set_model (GTK_TREE_VIEW (priv->side_sects), sections); + + if (sections) { + gtk_widget_show_all (priv->side_sw); + window_set_section_cursor (window, sections); + } else + gtk_widget_hide (priv->side_sw); - if (sections) - gtk_widget_show_all (priv->side_sw); - else - gtk_widget_hide (priv->side_sw); } -static gboolean -window_do_load_pager (YelpWindow *window, - YelpDocInfo *doc_info, - gchar *frag_id) +static void +window_set_section_cursor (YelpWindow * window, GtkTreeModel *model) { - YelpWindowPriv *priv; - YelpPagerState state; - YelpPager *pager; - GError *error = NULL; - YelpPage *page = NULL; - gboolean loadnow = FALSE; - gboolean startnow = TRUE; - gboolean handled = FALSE; - - gchar *uri; - - g_return_val_if_fail (YELP_IS_WINDOW (window), FALSE); - g_return_val_if_fail (doc_info != NULL, FALSE); - - priv = window->priv; - - uri = yelp_doc_info_get_uri (doc_info, frag_id, YELP_URI_TYPE_FILE); - - window_disconnect (window); - - pager = yelp_doc_info_get_pager (doc_info); - - if (!pager) { - switch (yelp_doc_info_get_type (doc_info)) { - case YELP_DOC_TYPE_DOCBOOK_XML: - pager = yelp_db_pager_new (doc_info); - break; - case YELP_DOC_TYPE_INFO: -#ifdef ENABLE_INFO - pager = yelp_info_pager_new (doc_info); - break; -#else - g_set_error (&error, YELP_ERROR, YELP_ERROR_FORMAT, - _("GNU info pages are not supported in this version")); -#endif - - case YELP_DOC_TYPE_MAN: -#ifdef ENABLE_MAN - pager = yelp_man_pager_new (doc_info); - break; -#else - g_set_error (&error, YELP_ERROR, YELP_ERROR_FORMAT, - _("Man pages are not supported in this version.")); - break; -#endif - - case YELP_DOC_TYPE_TOC: - pager = YELP_PAGER (yelp_toc_pager_get ()); - break; - case YELP_DOC_TYPE_SEARCH: -#ifdef ENABLE_SEARCH - pager = YELP_PAGER (yelp_search_pager_get (doc_info)); - break; -#else - g_set_error (&error, YELP_ERROR, YELP_ERROR_FORMAT, - _("Search is not supported in this version.")); - break; -#endif - default: - break; - } - if (pager) - yelp_doc_info_set_pager (doc_info, pager); - } - - if (!pager) { - if (!error) - g_set_error (&error, YELP_ERROR, YELP_ERROR_PROC, - _("A transformation context could not be created for " - "the file ‘%s’. The format may not be supported."), - uri); - window_error (window, error, TRUE); - handled = FALSE; - goto done; - } - - g_object_ref (pager); - - loadnow = FALSE; - startnow = FALSE; - - state = yelp_pager_get_state (pager); - debug_print (DB_DEBUG, "pager state=%d\n", state); - switch (state) { - case YELP_PAGER_STATE_ERROR: - error = yelp_pager_get_error (pager); - if (error) - window_error (window, error, TRUE); - handled = FALSE; - goto done; - case YELP_PAGER_STATE_RUNNING: - case YELP_PAGER_STATE_FINISHED: - /* Check if the page exists */ - pager_start_cb (pager, window); - - page = (YelpPage *) yelp_pager_get_page (pager, frag_id); - if (!page && (state == YELP_PAGER_STATE_FINISHED)) { - g_set_error (&error, YELP_ERROR, YELP_ERROR_NO_PAGE, - _("The section ‘%s’ does not exist in this document. " - "If you were directed to this section from a Help " - "button in an application, please report this to " - "the maintainers of that application."), - frag_id); - window_error (window, error, TRUE); - handled = FALSE; - goto done; - } - loadnow = (page ? TRUE : FALSE); - startnow = FALSE; - break; - case YELP_PAGER_STATE_NEW: - case YELP_PAGER_STATE_INVALID: - startnow = TRUE; - /* no break */ - case YELP_PAGER_STATE_STARTED: - case YELP_PAGER_STATE_PARSING: - priv->start_handler = - g_signal_connect (pager, - "start", - G_CALLBACK (pager_start_cb), - window); - break; - default: - g_assert_not_reached (); - } - - window_set_sections (window, - yelp_pager_get_sections (pager)); - - if (loadnow) { - window_handle_page (window, page); - handled = TRUE; - goto done; - } else { - window_set_loading (window); - - priv->page_handler = - g_signal_connect (pager, - "page", - G_CALLBACK (pager_page_cb), - window); - priv->error_handler = - g_signal_connect (pager, - "error", - G_CALLBACK (pager_error_cb), - window); - priv->cancel_handler = - g_signal_connect (pager, - "error", - G_CALLBACK (pager_cancel_cb), - window); - priv->finish_handler = - g_signal_connect (pager, - "finish", - G_CALLBACK (pager_finish_cb), - window); - - if (startnow) { - handled = yelp_pager_start (pager); - if (handled) { - yelp_toc_pager_pause (yelp_toc_pager_get ()); - priv->toc_pause++; + gboolean valid; + gchar *id = NULL; + GtkTreeIter iter; + YelpWindowPriv *priv = window->priv; + GtkTreeSelection *selection = + gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->side_sects)); + g_signal_handlers_block_by_func (selection, + tree_selection_changed_cb, + window); + gtk_tree_selection_unselect_all (selection); + + valid = gtk_tree_model_get_iter_first (model, &iter); + while (valid) { + gtk_tree_model_get (model, &iter, + YELP_DOCUMENT_COLUMN_ID, &id, + -1); + if (g_str_equal (id, priv->current_frag)) { + GtkTreePath *path = NULL; + GtkTreeIter parent; + if (gtk_tree_model_iter_parent (model, &parent, &iter)) { + path = gtk_tree_model_get_path (model, &parent); + gtk_tree_view_expand_to_path (GTK_TREE_VIEW (priv->side_sects), + path); + gtk_tree_path_free(path); } + path = gtk_tree_model_get_path (model, &iter); + gtk_tree_selection_select_path (selection, path); + + gtk_tree_path_free (path); + g_free (id); + break; } - - /* FIXME: error if !handled */ + + g_free (id); + + valid = tree_model_iter_following (model, &iter); } - - done: - g_free (uri); - - return handled; + g_signal_handlers_unblock_by_func (selection, + tree_selection_changed_cb, + window); } static gboolean window_do_load_html (YelpWindow *window, - YelpDocInfo *doc_info, - gchar *frag_id) + gchar *uri, + gchar *frag_id, + YelpRrnType type, + gboolean need_history) { YelpWindowPriv *priv; GnomeVFSHandle *handle; @@ -1645,17 +1527,14 @@ window_do_load_html (YelpWindow *window, GnomeVFSFileSize n; gchar buffer[BUFFER_SIZE]; GtkAction *action; + gchar *real_uri = NULL; gboolean handled = TRUE; - gchar *uri; g_return_val_if_fail (YELP_IS_WINDOW (window), FALSE); - g_return_val_if_fail (doc_info != NULL, FALSE); priv = window->priv; - uri = yelp_doc_info_get_uri (doc_info, frag_id, YELP_URI_TYPE_FILE); - window_set_sections (window, NULL); action = gtk_action_group_get_action (priv->action_group, "GoPrevious"); @@ -1667,28 +1546,34 @@ window_do_load_html (YelpWindow *window, action = gtk_action_group_get_action (priv->action_group, "GoContents"); if (action) g_object_set (G_OBJECT (action), "sensitive", FALSE, NULL); + + window_setup_window (window, type, uri, frag_id, uri, priv->base_uri, need_history); result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ); if (result != GNOME_VFS_OK) { - GError *error = NULL; - g_set_error (&error, YELP_ERROR, YELP_ERROR_IO, - _("The file ‘%s’ could not be read. This file might " - "be missing, or you might not have permissions to " - "read it."), - uri); - window_error (window, error, TRUE); + gchar *message; + + message = g_strdup_printf (_("The file ‘%s’ could not be read. This file might " + "be missing, or you might not have permissions to " + "read it."), uri); + window_error (window, _("Could Not Read File"), message, TRUE); + g_free (message); handled = FALSE; goto done; } - yelp_html_set_base_uri (priv->html_view, uri); + if (frag_id) + real_uri = g_strdup_printf ("file:/%s#%s", uri, frag_id); + else + real_uri = g_strdup_printf ("file:/%s", uri); + yelp_html_set_base_uri (priv->html_view, real_uri); - switch (yelp_doc_info_get_type (doc_info)) { - case YELP_DOC_TYPE_HTML: + switch (type) { + case YELP_RRN_TYPE_HTML: yelp_html_open_stream (priv->html_view, "text/html"); break; - case YELP_DOC_TYPE_XHTML: + case YELP_RRN_TYPE_XHTML: yelp_html_open_stream (priv->html_view, "application/xhtml+xml"); break; default: @@ -1698,22 +1583,21 @@ window_do_load_html (YelpWindow *window, while ((result = gnome_vfs_read (handle, buffer, BUFFER_SIZE, &n)) == GNOME_VFS_OK) { gchar *tmp; - tmp = g_utf8_strup (buffer, n); - if (strstr (tmp, "<FRAMESET")) + if (strstr (tmp, "<FRAMESET")) { yelp_html_frames (priv->html_view, TRUE); + } g_free (tmp); yelp_html_write (priv->html_view, buffer, n); } - yelp_html_close (priv->html_view); done: if (handle) gnome_vfs_close (handle); - - g_free (uri); + g_free (real_uri); + gdk_window_set_cursor (GTK_WIDGET (window)->window, NULL); return handled; } @@ -1747,193 +1631,6 @@ window_set_loading (YelpWindow *window) gtk_window_set_title (GTK_WINDOW (window), (const gchar *) loading); - /* - yelp_html_set_base_uri (priv->html_view, NULL); - yelp_html_clear (priv->html_view); - yelp_html_printf - (priv->html_view, - "<html><head><meta http-equiv='Content-Type'" - " content='text/html=; charset=utf-8'>" - "<title>%s</title></head>" - "<body><center>%s</center></body>" - "</html>", - loading, loading); - yelp_html_close (priv->html_view); - */ -} - -static void -window_handle_page (YelpWindow *window, - YelpPage *page) -{ - YelpWindowPriv *priv; - YelpPager *pager; - GtkAction *action; - GtkTreeModel *model; - GtkTreeIter iter; - - gchar *id; - gchar *uri; - gboolean valid; - - IdleWriterContext *context; - - g_return_if_fail (YELP_IS_WINDOW (window)); - priv = window->priv; - window_disconnect (window); - - debug_print (DB_FUNCTION, "entering\n"); - debug_print (DB_ARG, " page->page_id = \"%s\"\n", page->page_id); - debug_print (DB_ARG, " page->title = \"%s\"\n", page->title); - debug_print (DB_ARG, " page->contents = %i bytes\n", strlen (page->contents)); - - model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->side_sects)); - pager = yelp_doc_info_get_pager (priv->current_doc); - - if (model) { - GtkTreeSelection *selection = - gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->side_sects)); - g_signal_handlers_block_by_func (selection, - tree_selection_changed_cb, - window); - gtk_tree_selection_unselect_all (selection); - - valid = gtk_tree_model_get_iter_first (model, &iter); - while (valid) { - gtk_tree_model_get (model, &iter, - YELP_PAGER_COLUMN_ID, &id, - -1); - if (yelp_pager_page_contains_frag (pager, - id, - priv->current_frag)) { - GtkTreePath *path = NULL; - GtkTreeIter parent; - if (gtk_tree_model_iter_parent (model, &parent, &iter)) { - path = gtk_tree_model_get_path (model, &parent); - gtk_tree_view_expand_to_path (GTK_TREE_VIEW (priv->side_sects), - path); - gtk_tree_path_free(path); - } - path = gtk_tree_model_get_path (model, &iter); - gtk_tree_selection_select_path (selection, path); - - gtk_tree_path_free (path); - g_free (id); - break; - } - - g_free (id); - - valid = tree_model_iter_following (model, &iter); - } - g_signal_handlers_unblock_by_func (selection, - tree_selection_changed_cb, - window); - } - - priv->prev_id = page->prev_id; - action = gtk_action_group_get_action (priv->action_group, "GoPrevious"); - if (action) - g_object_set (G_OBJECT (action), - "sensitive", - priv->prev_id ? TRUE : FALSE, - NULL); - priv->next_id = page->next_id; - action = gtk_action_group_get_action (priv->action_group, "GoNext"); - if (action) - g_object_set (G_OBJECT (action), - "sensitive", - priv->next_id ? TRUE : FALSE, - NULL); - priv->toc_id = page->toc_id; - action = gtk_action_group_get_action (priv->action_group, "GoContents"); - if (action) - g_object_set (G_OBJECT (action), - "sensitive", - priv->toc_id ? TRUE : FALSE, - NULL); - - context = g_new0 (IdleWriterContext, 1); - context->window = window; - context->type = IDLE_WRITER_MEMORY; - context->buffer = page->contents; - context->length = strlen (page->contents); - - uri = yelp_doc_info_get_uri (priv->current_doc, - priv->current_frag, - YELP_URI_TYPE_FILE); - - debug_print (DB_ARG, " uri = %s\n", uri); - - yelp_html_set_base_uri (priv->html_view, uri); - yelp_html_open_stream (priv->html_view, "application/xhtml+xml"); - - priv->idle_write = g_idle_add ((GtkFunction) idle_write, context); - - /* - if (gnome_vfs_uri_get_fragment_identifier (uri->uri)) { - yelp_html_jump_to_anchor - (priv->html_view, - (gchar *) gnome_vfs_uri_get_fragment_identifier (uri->uri)); - } - */ - - g_free (uri); -} - -static void -window_disconnect (YelpWindow *window) -{ - YelpWindowPriv *priv; - YelpPager *pager = NULL; - - g_return_if_fail (YELP_IS_WINDOW (window)); - - priv = window->priv; - - if (priv && priv->current_doc) { - pager = yelp_doc_info_get_pager (priv->current_doc); - } - - if (GTK_WIDGET (window)->window) - gdk_window_set_cursor (GTK_WIDGET (window)->window, NULL); - - if (priv && priv->toc_pause > 0) { - priv->toc_pause--; - yelp_toc_pager_unpause (yelp_toc_pager_get ()); - } - - if (priv && priv->current_doc) { - if (priv->start_handler) { - g_signal_handler_disconnect (pager, - priv->start_handler); - priv->start_handler = 0; - } - if (priv->page_handler) { - g_signal_handler_disconnect (pager, - priv->page_handler); - priv->page_handler = 0; - } - if (priv->error_handler) { - g_signal_handler_disconnect (pager, - priv->error_handler); - priv->error_handler = 0; - } - if (priv->cancel_handler) { - g_signal_handler_disconnect (pager, - priv->cancel_handler); - priv->cancel_handler = 0; - } - if (priv->finish_handler) { - g_signal_handler_disconnect (pager, - priv->finish_handler); - priv->finish_handler = 0; - } - } - if (priv && priv->idle_write) { - g_source_remove (priv->idle_write); - priv->idle_write = 0; - } } /** Window Callbacks **********************************************************/ @@ -1952,125 +1649,6 @@ window_configure_cb (GtkWidget *widget, return FALSE; } -/** Pager Callbacks ***********************************************************/ - -static void -pager_start_cb (YelpPager *pager, - gpointer user_data) -{ - YelpWindow *window = YELP_WINDOW (user_data); - GError *error = NULL; - const gchar *page_id; - - page_id = yelp_pager_resolve_frag (pager, - window->priv->current_frag); - - debug_print (DB_FUNCTION, "entering\n"); - debug_print (DB_ARG, " page_id=\"%s\"\n", page_id); - - window_set_sections (window, - yelp_pager_get_sections (pager)); - - if (!page_id && window->priv->current_frag && - strcmp (window->priv->current_frag, "")) { - - window_disconnect (window); - - g_set_error (&error, YELP_ERROR, YELP_ERROR_NO_PAGE, - _("The section ‘%s’ does not exist in this document. " - "If you were directed to this section from a Help " - "button in an application, please report this to " - "the maintainers of that application."), - window->priv->current_frag); - window_error (window, error, TRUE); - } -} - -static void -pager_page_cb (YelpPager *pager, - gchar *page_id, - gpointer user_data) -{ - YelpWindow *window = YELP_WINDOW (user_data); - YelpPage *page; - - debug_print (DB_FUNCTION, "entering\n"); - debug_print (DB_ARG, " page_id=\"%s\"\n", page_id); - - if (yelp_pager_page_contains_frag (pager, - page_id, - window->priv->current_frag)) { - page = (YelpPage *) yelp_pager_get_page (pager, page_id); - - /* now that yelp automatically inserts the id="index" attribute - * on the root element of a document, the _contains_frag function - * is no longer a good indication of whether a section exists. - * Therefore if the returned page is NULL, then the stylesheets - * were not able to create a "page" from this document through the - * exsl:document extension (see yelp_xslt_document()) - * -Brent Smith, 1/4/2006 */ - if (page) { - window_disconnect (window); - window_handle_page (window, page); - } - } -} - -static void -pager_error_cb (YelpPager *pager, - gpointer user_data) -{ - YelpWindow *window = YELP_WINDOW (user_data); - GError *error = yelp_pager_get_error (pager); - - debug_print (DB_FUNCTION, "entering\n"); - - window_disconnect (window); - window_error (window, error, TRUE); - - history_step_back (window); -} - -static void -pager_cancel_cb (YelpPager *pager, - gpointer user_data) -{ - YelpWindow *window = YELP_WINDOW (user_data); - debug_print (DB_FUNCTION, "entering\n"); - - window_disconnect (window); -} - -static void -pager_finish_cb (YelpPager *pager, - gpointer user_data) -{ - GError *error = NULL; - YelpWindow *window = YELP_WINDOW (user_data); - const gchar *page_id; - - page_id = yelp_pager_resolve_frag (pager, - window->priv->current_frag); - - debug_print (DB_FUNCTION, "entering\n"); - - if (!page_id && window->priv->current_frag && - strcmp (window->priv->current_frag, "")) { - - window_disconnect (window); - - g_set_error (&error, YELP_ERROR, YELP_ERROR_NO_PAGE, - _("The section ‘%s’ does not exist in this document. " - "If you were directed to this section from a Help " - "button in an application, please report this to " - "the maintainers of that application."), - window->priv->current_frag); - window_error (window, error, TRUE); - } - - /* FIXME: Remove the URI from the history and go back */ -} - /** Gecko Callbacks ***********************************************************/ static void @@ -2095,10 +1673,10 @@ html_frame_selected_cb (YelpHtml *html, gchar *uri, gboolean handled, { YelpWindow *window = YELP_WINDOW (user_data); gboolean handle; - YelpDocInfo *info = yelp_doc_info_get (uri, FALSE); - switch (yelp_doc_info_get_type (info)) { - case YELP_DOC_TYPE_HTML: - case YELP_DOC_TYPE_XHTML: + + switch (window->priv->current_type) { + case YELP_RRN_TYPE_XHTML: + case YELP_RRN_TYPE_HTML: handle = TRUE; break; default: @@ -2165,9 +1743,9 @@ tree_selection_changed_cb (GtkTreeSelection *selection, if (gtk_tree_selection_get_selected (selection, NULL, &iter)) { model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->side_sects)); gtk_tree_model_get (model, &iter, - YELP_PAGER_COLUMN_ID, &id, + YELP_DOCUMENT_COLUMN_ID, &id, -1); - uri = yelp_doc_info_get_uri (priv->current_doc, id, YELP_URI_TYPE_ANY); + uri = g_strdup_printf ("%s#%s", priv->base_uri, id); yelp_window_load (window, uri); g_free (uri); } @@ -2181,6 +1759,8 @@ tree_drag_data_get_cb (GtkWidget *widget, guint32 time, YelpWindow *window) { + /* TODO: This is disabled by default anyway */ +#if 0 YelpWindowPriv *priv; GtkTreeSelection *tree_selection; GtkTreeModel *model; @@ -2199,7 +1779,7 @@ tree_drag_data_get_cb (GtkWidget *widget, if (gtk_tree_selection_get_selected (tree_selection, NULL, &iter)) { model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->side_sects)); gtk_tree_model_get (model, &iter, - YELP_PAGER_COLUMN_ID, &id, + YELP_DOCUMENT_COLUMN_ID, &id, -1); uri = yelp_doc_info_get_uri (priv->current_doc, id, YELP_URI_TYPE_NO_FILE); @@ -2219,9 +1799,10 @@ tree_drag_data_get_cb (GtkWidget *widget, } g_free (uri); +#endif } -static void +void tree_row_expand_cb (GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *column, YelpWindow *window) { @@ -2252,181 +1833,102 @@ window_new_window_cb (GtkAction *action, YelpWindow *window) g_signal_emit (window, signals[NEW_WINDOW_REQUESTED], 0, NULL); } + typedef struct { - gulong page_handler; - gulong error_handler; - gulong cancel_handler; - gulong finish_handler; - YelpPager *pager; YelpWindow *window; + GtkWindow *gtk_win; + GtkVBox *vbox; + YelpHtml *html; } PrintStruct; static void -print_disconnect (PrintStruct *data) -{ - debug_print (DB_FUNCTION, "entering\n"); - if (data->page_handler) { - g_signal_handler_disconnect (data->pager, - data->page_handler); - data->page_handler = 0; - } - if (data->error_handler) { - g_signal_handler_disconnect (data->pager, - data->error_handler); - data->error_handler = 0; - } - if (data->cancel_handler) { - g_signal_handler_disconnect (data->pager, - data->cancel_handler); - data->cancel_handler = 0; - } - if (data->finish_handler) { - g_signal_handler_disconnect (data->pager, - data->finish_handler); - data->finish_handler = 0; - } - g_free (data); -} - -static void -print_pager_page_cb (YelpPager *pager, - gchar *page_id, - gpointer user_data) +window_print_signal (YelpDocument *document, + YelpDocumentSignal signal, + gint req_id, + gpointer *func_data, + PrintStruct *print) { - PrintStruct *data = user_data; - YelpPage *page; - debug_print (DB_FUNCTION, "entering\n"); - debug_print (DB_ARG, " page_id=\"%s\"\n", page_id); - - page = (YelpPage *) yelp_pager_get_page (pager, page_id); - - if (page) { - YelpHtml *html; - GtkWidget *gtk_window; - int length, offset; - char *uri; - GtkWidget *vbox = gtk_vbox_new (FALSE, FALSE); - debug_print (DB_DEBUG, page->contents); - - gtk_window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - html = yelp_html_new (); + YelpError *error; - gtk_container_add (GTK_CONTAINER (gtk_window), GTK_WIDGET (vbox)); - gtk_box_pack_end (GTK_BOX (vbox), GTK_WIDGET (html), TRUE, TRUE, 0); + switch (signal) { + case YELP_DOCUMENT_SIGNAL_PAGE: + window_write_print_html (print->html, (YelpPage *) func_data); - gtk_widget_show (gtk_window); - gtk_widget_show (GTK_WIDGET (html)); - gtk_widget_show (vbox); - gtk_widget_hide (gtk_window); - uri = yelp_doc_info_get_uri (yelp_pager_get_doc_info (pager), - page_id, - YELP_URI_TYPE_FILE); - - debug_print (DB_ARG, " uri = %s\n", uri); - - yelp_html_set_base_uri (html, uri); - g_free (uri); - - yelp_html_open_stream (html, "application/xhtml+xml"); - for (length = strlen (page->contents), offset = 0; length > 0; length -= BUFFER_SIZE, offset += BUFFER_SIZE) { - debug_print (DB_DEBUG, "data: %.*s\n", MIN (length, BUFFER_SIZE), page->contents + offset); - yelp_html_write (html, page->contents + offset, MIN (length, BUFFER_SIZE)); - } - yelp_html_close (html); - - yelp_print_run (data->window, html, gtk_window, vbox); - - print_disconnect (data); + yelp_page_free ((YelpPage *) func_data); + yelp_print_run (print->window, print->html, print->gtk_win, print->vbox); + break; + case YELP_DOCUMENT_SIGNAL_TITLE: + g_free (func_data); + break; + case YELP_DOCUMENT_SIGNAL_ERROR: + error = (YelpError *) func_data; + window_error (print->window, (gchar *) yelp_error_get_title (error), + (gchar *) yelp_error_get_message (error), FALSE); + yelp_error_free (error); + break; + default: + g_assert_not_reached(); } -} - -static void -print_pager_error_cb (YelpPager *pager, - gpointer user_data) -{ - PrintStruct *data = user_data; - /* GError *error = yelp_pager_get_error (pager);*/ - - debug_print (DB_FUNCTION, "entering\n"); - print_disconnect (data); } -static void -print_pager_cancel_cb (YelpPager *pager, - gpointer user_data) -{ - PrintStruct *data = user_data; - debug_print (DB_FUNCTION, "entering\n"); - - print_disconnect (data); -} - -static void -print_pager_finish_cb (YelpPager *pager, - gpointer user_data) -{ - PrintStruct *data = user_data; - - debug_print (DB_FUNCTION, "entering\n"); - - print_disconnect (data); -} static void window_print_document_cb (GtkAction *action, YelpWindow *window) { - PrintStruct *data; - YelpPager *pager; + YelpWindowPriv *priv; + GtkWidget *gtk_win; + YelpHtml *html; + GtkWidget *vbox = gtk_vbox_new (FALSE, FALSE); + PrintStruct *print; + YelpDocument *doc = NULL; + + priv = window->priv; - if (!window->priv->current_doc) + switch (priv->current_type) { + case YELP_RRN_TYPE_DOC: + doc = yelp_dbprint_new (priv->uri); + break; + default: + g_assert_not_reached (); return; + } - pager = yelp_db_print_pager_new (window->priv->current_doc); - if (!pager) { - return; - } + gtk_win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + html = yelp_html_new (); + + gtk_container_add (GTK_CONTAINER (gtk_win), GTK_WIDGET (vbox)); + gtk_box_pack_end (GTK_BOX (vbox), GTK_WIDGET (html), TRUE, TRUE, 0); + gtk_widget_show (gtk_win); + gtk_widget_show (vbox); + gtk_widget_show (GTK_WIDGET (html)); + gtk_widget_hide (gtk_win); - data = g_new0 (PrintStruct, 1); - data->pager = pager; - data->window = window; - - data->page_handler = - g_signal_connect (data->pager, - "page", - G_CALLBACK (print_pager_page_cb), - data); - data->error_handler = - g_signal_connect (data->pager, - "error", - G_CALLBACK (print_pager_error_cb), - data); - data->cancel_handler = - g_signal_connect (data->pager, - "error", - G_CALLBACK (print_pager_cancel_cb), - data); - data->finish_handler = - g_signal_connect (data->pager, - "finish", - G_CALLBACK (print_pager_finish_cb), - data); - - /* handled = */ yelp_pager_start (data->pager); + print = g_new0 (PrintStruct, 1); + + print->window = window; + print->gtk_win = (GtkWindow *) gtk_win; + print->vbox = (GtkVBox *) vbox; + print->html = html; + + yelp_document_get_page (doc, + "index", + (YelpDocumentFunc) window_print_signal, + (void *) print); } static void window_print_page_cb (GtkAction *action, YelpWindow *window) { + YelpWindowPriv *priv; GtkWidget *gtk_win; - YelpPager *pager; - YelpPage *page = NULL; YelpHtml *html; - int length, offset; - gchar *uri; GtkWidget *vbox = gtk_vbox_new (FALSE, FALSE); + PrintStruct *print; + + priv = window->priv; gtk_win = gtk_window_new (GTK_WINDOW_TOPLEVEL); html = yelp_html_new (); @@ -2438,48 +1940,51 @@ window_print_page_cb (GtkAction *action, YelpWindow *window) gtk_widget_show (GTK_WIDGET (html)); gtk_widget_hide (gtk_win); - pager = yelp_doc_info_get_pager (window->priv->current_doc); - - uri = yelp_doc_info_get_uri (window->priv->current_doc, NULL, YELP_URI_TYPE_FILE); + print = g_new0 (PrintStruct, 1); - yelp_html_set_base_uri (html, uri); + print->window = window; + print->gtk_win = (GtkWindow *) gtk_win; + print->vbox = (GtkVBox *) vbox; + print->html = html; + + + if (priv->current_document) { + /* Need to go through the paging system */ + yelp_document_get_page (priv->current_document, + priv->current_frag, + (YelpDocumentFunc) window_print_signal, + (void *) print); + + } else { + /* HTML file */ - if (pager) { - page = (YelpPage *) yelp_pager_get_page (pager, window->priv->current_frag); - - yelp_html_open_stream (html, "application/xhtml+xml"); - for (length = strlen (page->contents), offset = 0; length > 0; length -= BUFFER_SIZE, offset += BUFFER_SIZE) { - debug_print (DB_DEBUG, "data: %.*s\n", MIN (length, BUFFER_SIZE), page->contents + offset); - yelp_html_write (html, page->contents + offset, MIN (length, BUFFER_SIZE)); - } - yelp_html_close (html); - } else { /*html file. Dump file to window the easy way*/ GnomeVFSHandle *handle; GnomeVFSResult result; GnomeVFSFileSize n; gchar buffer[BUFFER_SIZE]; - - result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ); + + result = gnome_vfs_open (&handle, priv->uri, GNOME_VFS_OPEN_READ); if (result != GNOME_VFS_OK) { - GError *error = NULL; + /*GError *error = NULL; g_set_error (&error, YELP_ERROR, YELP_ERROR_IO, _("The file ‘%s’ could not be read. This file might " "be missing, or you might not have permissions to " "read it."), uri); - window_error (window, error, TRUE); + window_error (window, error, TRUE);*/ + /* TODO: Proper errors */ return; } /* Assuming the file exists. If it doesn't how did we get this far? * There are more sinister forces at work... */ - switch (yelp_doc_info_get_type (window->priv->current_doc)) { - case YELP_DOC_TYPE_HTML: + switch (priv->current_type) { + case YELP_RRN_TYPE_HTML: yelp_html_open_stream (html, "text/html"); break; - case YELP_DOC_TYPE_XHTML: + case YELP_RRN_TYPE_XHTML: yelp_html_open_stream (html, "application/xhtml+xml"); break; default: @@ -2492,12 +1997,10 @@ window_print_page_cb (GtkAction *action, YelpWindow *window) } yelp_html_close (html); - - + yelp_print_run (window, html, gtk_win, vbox); + } - g_free (uri); - yelp_print_run (window, html, gtk_win, vbox); } static void @@ -2510,9 +2013,8 @@ window_about_document_cb (GtkAction *action, YelpWindow *window) priv = window->priv; - uri = yelp_doc_info_get_uri (priv->current_doc, - "x-yelp-titlepage", - YELP_URI_TYPE_ANY); + uri = g_strdup_printf("%s#__yelp_info", priv->uri); + yelp_window_load (window, uri); g_free (uri); } @@ -2524,24 +2026,12 @@ window_open_location_cb (GtkAction *action, YelpWindow *window) GladeXML *glade; GtkWidget *dialog; GtkWidget *entry; - gchar *uri; + gchar *uri = NULL; g_return_if_fail (YELP_IS_WINDOW (window)); priv = window->priv; - if (priv->current_doc) { - uri = yelp_doc_info_get_uri (priv->current_doc, - priv->current_frag, - YELP_URI_TYPE_NO_FILE); - if (!uri) - uri = yelp_doc_info_get_uri (priv->current_doc, - priv->current_frag, - YELP_URI_TYPE_FILE); - } else { - uri = NULL; - } - glade = glade_xml_new (DATADIR "/yelp/ui/yelp.glade", "location_dialog", NULL); @@ -2557,6 +2047,7 @@ window_open_location_cb (GtkAction *action, YelpWindow *window) priv->location_dialog = dialog; priv->location_entry = entry; + uri = priv->req_uri; if (uri) { gtk_entry_set_text (GTK_ENTRY (entry), uri); gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1); @@ -2638,29 +2129,13 @@ window_preferences_cb (GtkAction *action, YelpWindow *window) static void window_reload_cb (GtkAction *action, YelpWindow *window) { - YelpPager *pager; - - g_return_if_fail (YELP_IS_WINDOW (window)); - - debug_print (DB_FUNCTION, "entering\n"); - - if (window->priv->current_doc) { - YelpLoadData *data; - gchar *uri; - pager = yelp_doc_info_get_pager (window->priv->current_doc); - - if (!pager) - return; - - yelp_pager_cancel (pager); - - uri = yelp_doc_info_get_uri (window->priv->current_doc, - window->priv->current_frag, - YELP_URI_TYPE_ANY); - data = g_new0 (YelpLoadData, 1); - data->window = g_object_ref (window); - data->uri = uri; - g_idle_add ((GSourceFunc) window_load_async, data); + if (window->priv->current_document) { + if (window->priv->current_request > -1) { + yelp_document_cancel_page (window->priv->current_document, window->priv->current_request); + } + //g_object_unref (window->priv->current_document); + window->priv->current_document = NULL; + yelp_window_load (window, window->priv->req_uri); } } @@ -2670,16 +2145,29 @@ window_enable_cursor_cb (GtkAction *action, YelpWindow *window) yelp_settings_toggle_caret (); } -static gboolean -window_load_async (YelpLoadData *data) +static void +history_load_entry (YelpWindow *window, YelpHistoryEntry *entry) { - yelp_window_load (data->window, data->uri); + g_return_if_fail (YELP_IS_WINDOW (window)); - load_data_free (data); + if (entry->type == YELP_RRN_TYPE_HTML || entry->type == YELP_RRN_TYPE_XHTML) { + window_do_load_html (window, entry->uri, entry->frag_id, entry->type, FALSE); + } else { + g_assert (entry->doc != NULL); + window_setup_window (window, entry->type, entry->uri, entry->frag_id, entry->req_uri, + window->priv->base_uri, FALSE); + g_free (window->priv->base_uri); + window->priv->base_uri = g_strdup (entry->base_uri); + window->priv->current_document = entry->doc; + window->priv->current_request = yelp_document_get_page (entry->doc, + entry->frag_id, + (YelpDocumentFunc) page_request_cb, + (void *) window); + } - return FALSE; } + static void window_go_back_cb (GtkAction *action, YelpWindow *window) { @@ -2695,15 +2183,7 @@ window_go_back_cb (GtkAction *action, YelpWindow *window) entry = history_pop_back (window); - if (priv->current_doc) - yelp_doc_info_unref (priv->current_doc); - if (priv->current_frag) - g_free (priv->current_frag); - - priv->current_doc = yelp_doc_info_ref (entry->doc_info); - priv->current_frag = g_strdup (entry->frag_id); - - window_do_load (window, entry->doc_info, entry->frag_id); + history_load_entry (window, entry); history_entry_free (entry); } @@ -2723,15 +2203,7 @@ window_go_forward_cb (GtkAction *action, YelpWindow *window) entry = history_pop_forward (window); - if (priv->current_doc) - yelp_doc_info_unref (priv->current_doc); - if (priv->current_frag) - g_free (priv->current_frag); - - priv->current_doc = yelp_doc_info_ref (entry->doc_info); - priv->current_frag = g_strdup (entry->frag_id); - - window_do_load (window, entry->doc_info, entry->frag_id); + history_load_entry (window, entry); history_entry_free (entry); } @@ -2747,6 +2219,8 @@ window_go_home_cb (GtkAction *action, YelpWindow *window) static void window_go_previous_cb (GtkAction *action, YelpWindow *window) { + printf ("Prev: %s\n", window->priv->prev_id); +#if 0 YelpWindowPriv *priv; gchar *base, *uri; @@ -2762,11 +2236,14 @@ window_go_previous_cb (GtkAction *action, YelpWindow *window) g_free (uri); g_free (base); +#endif } static void window_go_next_cb (GtkAction *action, YelpWindow *window) { + printf ("Next: %s\n", window->priv->next_id); +#if 0 YelpWindowPriv *priv; gchar *base, *uri; @@ -2782,11 +2259,14 @@ window_go_next_cb (GtkAction *action, YelpWindow *window) g_free (uri); g_free (base); +#endif } static void window_go_toc_cb (GtkAction *action, YelpWindow *window) { + printf ("index toc: %s\n", window->priv->toc_id); +#if 0 YelpWindowPriv *priv; gchar *base, *uri; @@ -2802,28 +2282,21 @@ window_go_toc_cb (GtkAction *action, YelpWindow *window) g_free (uri); g_free (base); +#endif } static void window_add_bookmark_cb (GtkAction *action, YelpWindow *window) { - gchar *uri; YelpWindowPriv *priv = window->priv; debug_print (DB_FUNCTION, "entering\n"); - uri = yelp_doc_info_get_uri (priv->current_doc, priv->current_frag, - YELP_URI_TYPE_NO_FILE); - if (!uri) - uri = yelp_doc_info_get_uri (priv->current_doc, priv->current_frag, - YELP_URI_TYPE_FILE); - - if (!uri) + if (!priv->req_uri) return; - yelp_bookmarks_add (uri, window); + yelp_bookmarks_add (priv->req_uri, window); - g_free (uri); } static void window_copy_link_cb (GtkAction *action, YelpWindow *window) @@ -2850,6 +2323,7 @@ window_open_link_new_cb (GtkAction *action, YelpWindow *window) g_free (window->priv->uri); } +/* TODO: This doesn't work... */ static void window_copy_mail_cb (GtkAction *action, YelpWindow *window) { @@ -2892,7 +2366,7 @@ window_about_cb (GtkAction *action, YelpWindow *window) "Mikael Hallendal <micke@imendio.com>", "Alexander Larsson <alexl@redhat.com>", "Shaun McCance <shaunm@gnome.org>", - "Don Scorgie <DonScorgie@Blueyonder.co.uk>", + "Don Scorgie <Don@Scorgie.org>", "Brent Smith <gnome@nextreality.net>", NULL }; @@ -3151,53 +2625,39 @@ tree_model_iter_following (GtkTreeModel *model, return FALSE; } -/* Writing incrementally in an idle function has a number of advantages. First, - * it keeps the interface responsive for really big pages. Second, it prevents - * some weird rendering artifacts in gtkhtml2. Third, and most important, it - * helps me beat the relayout race condition that I discuss at length in a big - * comment in yelp-html-gtkhtml2.c. - */ - static gboolean -idle_write (IdleWriterContext *context) +window_write_html (YelpLoadData *data) { - YelpWindowPriv *priv; - - g_return_val_if_fail (context != NULL, FALSE); - g_return_val_if_fail (context->window != NULL, FALSE); - - debug_print (DB_FUNCTION, "entering\n"); - - priv = context->window->priv; - - switch (context->type) { - case IDLE_WRITER_MEMORY: - debug_print (DB_DEBUG, " context->buffer = %i bytes\n", strlen (context->buffer)); - debug_print (DB_DEBUG, " context->cur = %i\n", context->cur); - debug_print (DB_DEBUG, " context->length = %i\n", context->length); - - if (context->cur + BUFFER_SIZE < context->length) { - yelp_html_write (priv->html_view, - context->buffer + context->cur, - BUFFER_SIZE); - context->cur += BUFFER_SIZE; - return TRUE; - } else { - if (context->length > context->cur) - yelp_html_write (priv->html_view, - context->buffer + context->cur, - context->length - context->cur); - yelp_html_close (priv->html_view); - g_free (context); - priv->idle_write = 0; - return FALSE; - } - break; - case IDLE_WRITER_VFS: - default: - g_assert_not_reached (); - } - priv->idle_write = 0; + gsize read; + YelpHtml *html = data->window->priv->html_view; + gchar contents[BUFFER_SIZE]; + + /* Use a silly fake URI to stop gecko doing silly things */ + yelp_html_set_base_uri (html, data->window->priv->base_uri); + yelp_html_open_stream (html, "application/xhtml+xml"); + + do { + yelp_page_read (data->page, contents, BUFFER_SIZE, &read, NULL); + yelp_html_write (html, contents, read); + } while (read == BUFFER_SIZE); + yelp_html_close (html); + data->window->priv->html_idle_handle = 0; return FALSE; } +static void +window_write_print_html (YelpHtml *html, YelpPage * page) +{ + gsize read; + gchar contents[BUFFER_SIZE]; + + /* Use a silly fake URI to stop gecko doing silly things */ + yelp_html_set_base_uri (html, "file:///foobar"); + yelp_html_open_stream (html, "application/xhtml+xml"); + + do { + yelp_page_read (page, contents, BUFFER_SIZE, &read, NULL); + yelp_html_write (html, contents, read); + } while (read == BUFFER_SIZE); + yelp_html_close (html); +} diff --git a/src/yelp-window.h b/src/yelp-window.h index 0f45fa3e..89364a68 100644 --- a/src/yelp-window.h +++ b/src/yelp-window.h @@ -25,6 +25,7 @@ #include <gtk/gtktreemodel.h> #include <gtk/gtkwindow.h> +#include <gtk/gtk.h> #include "yelp-base.h" #include "yelp-utils.h" @@ -61,7 +62,7 @@ GtkWidget * yelp_window_new (GNode *doc_tree, GList *index); void yelp_window_load (YelpWindow *window, const gchar *uri); -YelpDocInfo * yelp_window_get_doc_info (YelpWindow *window); +const gchar * yelp_window_get_uri (YelpWindow *window); GtkUIManager * yelp_window_get_ui_manager (YelpWindow *window); #endif /* __YELP_WINDOW_H__ */ diff --git a/src/yelp-xslt-pager.c b/src/yelp-xslt-pager.c index 84de6c28..0439a411 100644 --- a/src/yelp-xslt-pager.c +++ b/src/yelp-xslt-pager.c @@ -326,7 +326,7 @@ xslt_pager_finish (YelpPager *pager) /** XSLT Extension Elements ***************************************************/ -static void +void xslt_yelp_document (xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst, @@ -456,7 +456,7 @@ xslt_yelp_document (xsltTransformContextPtr ctxt, xsltFreeStylesheet (style); } -static void +void xslt_yelp_cache (xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst, diff --git a/stylesheets/toc2html.xsl b/stylesheets/toc2html.xsl index 5be5b544..26cb7727 100644 --- a/stylesheets/toc2html.xsl +++ b/stylesheets/toc2html.xsl @@ -28,8 +28,6 @@ <xsl:param name="yelp.color.admon.bg.dark2"/> <xsl:param name="yelp.color.admon.bg.dark3"/> -<xsl:param name="yelp.toc.id" select="'Man-man6'"/> - <xsl:template match="toc"> <yelp:document href="{@id}"> <html> @@ -151,6 +149,8 @@ <div class="docs"> <dl> <xsl:for-each select="doc"> + <xsl:sort order="ascending" data-type="number" + select="normalize-space(@weight)"/> <xsl:sort select="normalize-space(title)"/> <dt class="doc"> <a href="{@href}" title="{@href}"> @@ -237,7 +237,7 @@ <xsl:template mode="leftbar.mode" match="toc"> <xsl:param name="curid" select="0"/> <ul> - <xsl:for-each select="toc[.//doc[1]]"> + <xsl:for-each select="toc[.//doc[1] or @protected]"> <li class="toclist"> <xsl:choose> <xsl:when test="@id != $curid"> @@ -260,7 +260,7 @@ </xsl:template> <xsl:template match="/"> - <xsl:apply-templates select="//toc[@id = $yelp.toc.id]" /> + <xsl:apply-templates select="//toc" /> </xsl:template> </xsl:stylesheet> diff --git a/stylesheets/yelp-common.xsl b/stylesheets/yelp-common.xsl index abea4566..c4625fd9 100644 --- a/stylesheets/yelp-common.xsl +++ b/stylesheets/yelp-common.xsl @@ -54,13 +54,13 @@ h6 span[class~="title"] { border-bottom: none; } h7 span[class~="title"] { border-bottom: none; } - /* Gecko seems to get selection color wrong on some themes */ + <!--/* Gecko seems to get selection color wrong on some themes */ ::-moz-selection { background-color: </xsl:text> <xsl:value-of select="$yelp.color.selected.bg"/><xsl:text>; color: </xsl:text> <xsl:value-of select="$yelp.color.selected.fg"/><xsl:text>; - } + } --> div[class~="linktrail"] { -moz-box-sizing: border-box; diff --git a/yelp.desktop.in.in b/yelp.desktop.in.in index a203a7de..b8010b61 100644 --- a/yelp.desktop.in.in +++ b/yelp.desktop.in.in @@ -7,7 +7,7 @@ Icon=gnome-help.png StartupNotify=true Terminal=false Type=Application -Categories=GNOME;GTK;Core; +Categories=GNOME;GTK;Application;Core; X-GNOME-Bugzilla-Bugzilla=GNOME X-GNOME-Bugzilla-Product=Yelp X-GNOME-Bugzilla-Component=general |