diff --git a/trunk/include/fb2def.h b/trunk/include/fb2def.h index c074a5c00..aa75205a6 100644 --- a/trunk/include/fb2def.h +++ b/trunk/include/fb2def.h @@ -115,6 +115,7 @@ XS_END_TAGS XS_BEGIN_ATTRS XS_ATTR( id ) +XS_ATTR( class ) XS_ATTR( value ) XS_ATTR( name ) XS_ATTR( number ) diff --git a/trunk/include/lvstsheet.h b/trunk/include/lvstsheet.h index b5df5123f..aea570e44 100644 --- a/trunk/include/lvstsheet.h +++ b/trunk/include/lvstsheet.h @@ -98,6 +98,7 @@ enum LVCssSelectorRuleType cssrt_attrhas, // E[foo~="value"] cssrt_attrstarts, // E[foo|="value"] cssrt_id, // E#id + cssrt_class, // E.class }; class LVCssSelectorRule diff --git a/trunk/include/lvtinydom.h b/trunk/include/lvtinydom.h index 1a1e892da..d6a38c9ab 100644 --- a/trunk/include/lvtinydom.h +++ b/trunk/include/lvtinydom.h @@ -653,6 +653,24 @@ class ldomXPointerEx : public ldomXPointer void recurseElements( void (*pFun)( ldomXPointerEx & node ) ); /// calls specified function recursively for all nodes of DOM tree void recurseNodes( void (*pFun)( ldomXPointerEx & node ) ); + /// set new offset value + void setOffset( int offset ) + { + _offset = offset; + } +}; + +class ldomXRange; + +/// callback for DOM tree iteration interface +class ldomNodeCallback { +public: + /// destructor + virtual ~ldomNodeCallback() { } + /// called for each found text fragment in range + virtual void onText( ldomXRange * nodeRange ) = 0; + /// called for each found node in range + virtual bool onElement( ldomXPointerEx * ptr ) = 0; }; /// DOM range @@ -721,6 +739,8 @@ class ldomXRange { lString16 getRangeText( lChar16 blockDelimiter=0, int maxTextLen=0 ); /// sets range to nearest word bounds, returns true if success static bool getWordRange( ldomXRange & range, ldomXPointer & p ); + /// run callback for each node in range + void forEach( ldomNodeCallback * callback ); }; class ldomMarkedText diff --git a/trunk/src/lvstsheet.cpp b/trunk/src/lvstsheet.cpp index 176563a22..04c9127a1 100644 --- a/trunk/src/lvstsheet.cpp +++ b/trunk/src/lvstsheet.cpp @@ -15,6 +15,7 @@ #include "../include/lvstsheet.h" #include "../include/lvtinydom.h" +#include "../include/fb2def.h" enum css_decl_code { cssd_unknown, @@ -778,6 +779,21 @@ bool LVCssSelectorRule::check( const ldomNode * & node ) break; case cssrt_id: // E#id // todo + { + lString16 val = node->getAttributeValue(attr_id); + if (_value.length()>val.length()) + return false; + return val == _value; + } + break; + case cssrt_class: // E.class + // todo + { + lString16 val = node->getAttributeValue(attr_class); + if (_value.length()>val.length()) + return false; + return val == _value; + } break; case cssrt_universal: // * return true; @@ -848,16 +864,38 @@ bool parse_attr_value( const char * &str, char * buf ) LVCssSelectorRule * parse_attr( const char * &str, lxmlDocBase * doc ) { + char attrname[64]; + char attrvalue[64]; LVCssSelectorRuleType st = cssrt_universal; - if (*str != '[') + if (*str=='.') { + // E.class + str++; + skip_spaces( str ); + if (!parse_ident( str, attrvalue )) + return NULL; + skip_spaces( str ); + LVCssSelectorRule * rule = new LVCssSelectorRule(cssrt_class); + lString16 s( attrvalue ); + rule->setAttr(attr_class, s); + return rule; + } else if ( *str=='#' ) { + // E#id + str++; + skip_spaces( str ); + if (!parse_ident( str, attrvalue )) + return NULL; + skip_spaces( str ); + LVCssSelectorRule * rule = new LVCssSelectorRule(cssrt_id); + lString16 s( attrvalue ); + rule->setAttr(attr_id, s); + return rule; + } else if (*str != '[') return NULL; str++; skip_spaces( str ); - char attrname[64]; if (!parse_ident( str, attrname )) return NULL; skip_spaces( str ); - char attrvalue[64]; attrvalue[0] = 0; if (*str==']') { @@ -932,7 +970,7 @@ bool LVCssSelector::parse( const char * &str, lxmlDocBase * doc ) return true; // one or more attribute rules bool attr_rule = false; - while ( *str == '[' ) + while ( *str == '[' || *str=='.' || *str=='#' ) { LVCssSelectorRule * rule = parse_attr( str, doc ); if (!rule) diff --git a/trunk/src/lvtinydom.cpp b/trunk/src/lvtinydom.cpp index 7069b91a4..2c50ee36a 100644 --- a/trunk/src/lvtinydom.cpp +++ b/trunk/src/lvtinydom.cpp @@ -2212,18 +2212,114 @@ bool ldomXRange::getWordRange( ldomXRange & range, ldomXPointer & p ) return true; } -/// returns text between two XPointer positions -lString16 ldomXRange::getRangeText( lChar16 blockDelimiter, int maxTextLen ) +/// run callback for each node in range +void ldomXRange::forEach( ldomNodeCallback * callback ) { - ldomXPointerEx pos( _start ); - ldomXPointerEx endpos( _end ); - lString16 res; - if ( pos.getNode() == endpos.getNode() ) { - if ( pos.getOffset() >= endpos.getOffset() ) - return res; - if ( pos.getNode()->getNodeType()==LXML_TEXT_NODE ) { - return pos.getNode()->getText().substr( pos.getOffset(), endpos.getOffset()-pos.getOffset() ); + if ( isNull() ) + return; + ldomXRange pos( _start, _end, 0 ); + bool allowGoRecurse = true; + while ( pos._start.compare( _end ) < 0 ) { + // do something + ldomNode * node = pos._start.getNode(); + if ( node->getNodeType()==LXML_ELEMENT_NODE ) { + allowGoRecurse = callback->onElement( &pos.getStart() ); + } else if ( node->getNodeType()==LXML_TEXT_NODE ) { + lString16 txt = node->getText(); + pos._end = pos._start; + pos._start.setOffset( 0 ); + pos._end.setOffset( txt.length() ); + if ( _start.getNode() == node ) { + pos._start.setOffset( _start.getOffset() ); + } + if ( _end.getNode() == node && pos._end.getOffset() > _end.getOffset()) { + pos._end.setOffset( _end.getOffset() ); + } + callback->onText( &pos ); + allowGoRecurse = false; + } + // move to next item + if ( !allowGoRecurse || !pos._start.child(0) ) { + while ( !pos._start.nextSibling() ) { + if ( !pos._start.parent() ) + break; + } } } - return res; +} + +class ldomTextCollector : public ldomNodeCallback +{ +private: + bool lastText; + bool newBlock; + int delimiter; + int maxLen; + lString16 text; +public: + ldomTextCollector( lChar16 blockDelimiter, int maxTextLen ) + : lastText(false), newBlock(true), delimiter( blockDelimiter), maxLen( maxTextLen ) + { + } + /// destructor + virtual ~ldomTextCollector() { } + /// called for each found text fragment in range + virtual void onText( ldomXRange * nodeRange ) + { + if ( newBlock && !text.empty()) { + text << delimiter; + } + lString16 txt = nodeRange->getStart().getNode()->getText(); + int start = nodeRange->getStart().getOffset(); + int end = nodeRange->getEnd().getOffset(); + if ( start < end ) { + text << txt.substr( start, end-start ); + } + lastText = true; + newBlock = false; + } + /// called for each found node in range + virtual bool onElement( ldomXPointerEx * ptr ) + { + ldomElement * elem = (ldomElement *)ptr->getNode(); + if ( elem->getRendMethod()==erm_invisible ) + return false; + switch ( elem->getStyle()->display ) { + /* + case css_d_inherit: + case css_d_block: + case css_d_list_item: + case css_d_compact: + case css_d_marker: + case css_d_table: + case css_d_inline_table: + case css_d_table_row_group: + case css_d_table_header_group: + case css_d_table_footer_group: + case css_d_table_row: + case css_d_table_column_group: + case css_d_table_column: + case css_d_table_cell: + case css_d_table_caption: + */ + default: + newBlock = true; + case css_d_none: + return false; + case css_d_inline: + case css_d_run_in: + newBlock = false; + return true; + } + } + /// get collected text + lString16 getText() { return text; } +}; + +/// returns text between two XPointer positions +lString16 ldomXRange::getRangeText( lChar16 blockDelimiter, int maxTextLen ) +{ + ldomTextCollector callback( blockDelimiter, maxTextLen ); + forEach( &callback ); + return callback.getText(); }