diff options
78 files changed, 8798 insertions, 4054 deletions
@@ -41,6 +41,7 @@ ^test/dw-table$ ^test/dw-table-aligned$ ^test/dw-ui-test$ +^test/identity$ ^test/liang$ ^test/notsosimplevector$ ^test/shapes$ @@ -6,6 +6,15 @@ Here we list changes that are relatively significant and/or visible to the user. For a history of changes in full detail, see our Mercurial repository at http://hg.dillo.org/dillo +dillo_grows repository [to be merged] + ++- Absolutely and relatively positioned elements. + - Fixedly positioned elements (incomplete: referring to the canvas instead to + the viewport). + - "Z-index", stacking contexts. + Patches: Sebastian Geerken + +----------------------------------------------------------------------------- dillo-3.1 [not released yet] diff --git a/configure.ac b/configure.ac index b574f8d6..db37619d 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,8 @@ dnl Process this file with aclocal, autoconf and automake. -AC_INIT([dillo], [3.1-dev]) +# "grows2" (for second phase of GROWS) to distinguish this version +# from version 3.1-dev. Remember to remove this again after merging. +AC_INIT([dillo], [3.1-dev_grows2]) dnl Detect the canonical target build environment AC_CANONICAL_TARGET diff --git a/doc/Makefile.am b/doc/Makefile.am index 8ade3d15..7f627d09 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -13,6 +13,7 @@ EXTRA_DIST = \ dw-images-and-backgrounds.doc \ dw-dw-out-of-flow.doc \ dw-dw-out-of-flow-2.doc \ + dw-stacking-context.doc \ fltk-problems.doc \ rounding-errors.doc \ uml-legend.doc \ diff --git a/doc/dw-fixed-positions.doc b/doc/dw-fixed-positions.doc new file mode 100644 index 00000000..d62565ff --- /dev/null +++ b/doc/dw-fixed-positions.doc @@ -0,0 +1,63 @@ +/** \page dw-fixed-positions Fixed positions + +In some cases, widgets or widget content must be positioned relative +to the viewport. As in the CSS specification, these positions will be +called "fixed positions". This must not be confused with "fixedly +positioned elements" (see \ref dw-out-of-flow), which are a special +case of fixed positions. + + +Applications +============ + +As defined by CSS +----------------- + +- "position: fixed"; see \ref dw-out-of-flow. +- "background-attachment: fixed"; see \ref dw-images-and-backgrounds. + +Idea for tables +--------------- + +Often, tables have a header, which contains informations necessary to +interpret the columns in the table body. For this, HTML defines the elements +<thead> and <tbody> +<sup><a href="#note-table-footer" id="ref-table-footer">[1]</a></sup>. + +For large tables, the problem occurs that the table header gets out of +the reader's view. In paged media, where a large table covers multiple +pages, this is often solved by *repeating* the table header on each +page occupied by the table. When using a viewport, a table larger than +the vieport could be displayed like this: + +1. If the top of the table is within the viewport, show the table + header at the usual position. +2. As soon as top of the table gets above the top border of the + viewport, keep the table header at the viewport top, so that it is + still visible (this means, it moves down, relative to the + *canvas*). This way, the header is still visible, so our objective + is achieved. +3. When scrolling further down, at some point the table body gets out + of the viewport again, and so should the table header. + +(Some images would be nice.) + +These ideas should be considered when developing a design for fixed +positions. + + +Design sketch +============== + +[...] + + +---------------------------------------------------------------------- + +<sup><a href="#ref-table-footer" id="note-table-footer">[1]</a></sup> +... and also <tfoot>, which is not discussed here, for reasons +of simplicity. However, it is obvious that <tfoot> should be +dealt with in an analogue way as <thead>. + + +*/
\ No newline at end of file diff --git a/doc/dw-interrupted-drawing-1.png b/doc/dw-interrupted-drawing-1.png Binary files differnew file mode 100644 index 00000000..58289490 --- /dev/null +++ b/doc/dw-interrupted-drawing-1.png diff --git a/doc/dw-interrupted-drawing-2.png b/doc/dw-interrupted-drawing-2.png Binary files differnew file mode 100644 index 00000000..8ec54035 --- /dev/null +++ b/doc/dw-interrupted-drawing-2.png diff --git a/doc/dw-interrupted-drawing-2.svg b/doc/dw-interrupted-drawing-2.svg new file mode 100644 index 00000000..6dbc6aca --- /dev/null +++ b/doc/dw-interrupted-drawing-2.svg @@ -0,0 +1,706 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="210mm" + height="297mm" + id="svg2" + version="1.1" + inkscape:version="0.48.3.1 r9886" + sodipodi:docname="dw-interrupted-drawing-2.svg" + inkscape:export-filename="/home/sg/dev/dillo/dillo-grows-work-intdraw/doc/dw-interrupted-drawing-2.png" + inkscape:export-xdpi="69.620003" + inkscape:export-ydpi="69.620003"> + <defs + id="defs4"> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0.0" + refX="0.0" + id="Arrow2Lend" + style="overflow:visible;"> + <path + id="path4042" + style="fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;" + d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z " + transform="scale(1.1) rotate(180) translate(1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lend-2" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4042-4" + style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lend-8" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4042-0" + style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lend-24" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4042-2" + style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lend-29" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4042-1" + style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lend-5" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4042-41" + style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lend-23" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4042-19" + style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lend-7" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4042-9" + style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lend-0" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4042-97" + style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lend-0-4" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4042-97-1" + style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lend-0-7" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4042-97-5" + style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lend-0-8" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4042-97-8" + style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lend-0-3" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4042-97-2" + style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lend-1" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4042-7" + style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="marker5604" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path5606" + style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.35355339" + inkscape:cx="437.30872" + inkscape:cy="337.60168" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:window-width="1600" + inkscape:window-height="900" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="0"> + <inkscape:grid + type="xygrid" + id="grid2985" /> + </sodipodi:namedview> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Ebene 1" + inkscape:groupmode="layer" + id="layer1"> + <g + id="g3977" + transform="translate(2.992126e-6,177.16535)"> + <rect + y="24.803127" + x="17.716536" + height="70.866142" + width="141.73228" + id="rect2997" + style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> + <flowRoot + transform="translate(20.094709,-2.1798012)" + style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + id="flowRoot3786" + xml:space="preserve"><flowRegion + id="flowRegion3788"><rect + y="48.270561" + x="46.467018" + height="28.284271" + width="75.256363" + id="rect3790" /></flowRegion><flowPara + style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L" + id="flowPara3792">body</flowPara></flowRoot> </g> + <g + id="g3984" + transform="translate(2.992126e-6,177.16535)"> + <rect + y="24.803127" + x="194.8819" + height="70.866142" + width="141.73228" + id="rect3767" + style="fill:#ffe0e0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> + <text + sodipodi:linespacing="125%" + id="text3832" + y="67.096199" + x="243.2318" + style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L" + y="67.096199" + x="243.2318" + id="tspan3834" + sodipodi:role="line">#sc-1</tspan></text> + </g> + <g + id="g3989" + transform="translate(176.96508,177.13197)"> + <rect + y="24.803127" + x="372.04724" + height="70.866142" + width="141.73228" + id="rect3767-6" + style="fill:#b0ffb0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> + <text + sodipodi:linespacing="125%" + id="text3836" + y="67.356201" + x="425.39713" + style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="67.356201" + x="425.39713" + id="tspan3838" + sodipodi:role="line" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">#fl-1</tspan></text> + </g> + <g + id="g3994" + transform="translate(-177.81225,177.39197)"> + <rect + y="24.803127" + x="549.21259" + height="70.866142" + width="141.73228" + id="rect3767-2" + style="fill:#f0f0ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> + <text + sodipodi:linespacing="125%" + id="text3840" + y="67.096199" + x="595.92249" + style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="67.096199" + x="595.92249" + id="tspan3842" + sodipodi:role="line" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">#sc-2</tspan></text> + </g> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="m 88.582679,272.83462 -2e-6,531.49607" + id="path3890" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="m 265.74803,272.83462 0,531.49607" + id="path3890-9" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="m 442.91339,272.83462 0,531.49607" + id="path3890-2" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="m 620.07874,272.83462 0,531.49607" + id="path3890-29" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <rect + style="fill:#ffffa0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect3931" + width="35.433071" + height="460.62991" + x="70.866142" + y="308.2677" /> + <rect + style="fill:#ffffa0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect3933" + width="35.433071" + height="354.33069" + x="248.03149" + y="343.70078" /> + <flowRoot + xml:space="preserve" + id="flowRoot3935" + style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + transform="translate(2.992126e-6,177.16535)"><flowRegion + id="flowRegion3937"><rect + id="rect3939" + width="835.71429" + height="147.14285" + x="-1.4285715" + y="-40.494953" /></flowRegion><flowPara + id="flowPara3941"></flowPara></flowRoot> <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)" + d="m 106.29921,343.70076 141.73229,0" + id="path3890-29-5" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <rect + style="fill:#ffffa0;fill-opacity:1;stroke:#000000;stroke-width:3.00000191;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect4643" + width="35.433094" + height="70.866158" + x="602.36218" + y="414.56689" /> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)" + d="m 283.46457,414.56691 318.89763,0" + id="path3890-29-5-2" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6, 6;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)" + d="m 602.36221,485.43305 -318.89764,0" + id="path3890-29-5-2-0-0" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6, 6;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)" + d="m 248.0315,698.03147 -141.73229,0" + id="path3890-29-5-2-0-0-8" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <rect + style="fill:#ffffa0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" + id="rect4767" + width="35.433071" + height="35.433071" + x="602.36218" + y="733.46454" /> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)" + d="m 106.29921,733.46454 496.06299,10e-6" + id="path3890-29-5-4" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)" + d="m 17.716536,308.26769 53.149608,0" + id="path3890-29-5-27" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6, 6;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)" + d="m 70.866142,768.89762 -53.149613,0" + id="path3890-29-5-2-0-0-8-4" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#ff0000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="m 354.33071,715.74801 318.89763,53.14961 0,0 0,0 0,0" + id="path4865" + inkscape:connector-curvature="0" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;stroke:#ff0000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="m 354.3307,768.89762 318.89764,-53.14961 0,0 0,0" + id="path4865-9" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccc" /> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend-0)" + d="m 88.582677,201.96848 c 17.716533,-88.58268 141.732283,-88.58268 159.448823,0" + id="path4914" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend-0)" + d="m 283.46456,201.96848 c 17.71654,-88.58268 141.73229,-88.58268 159.44883,0" + id="path4914-8" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend-0)" + d="m 88.582677,201.96848 c 35.433073,-212.598421 513.779523,-212.598423 549.212603,0" + id="path4914-0" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0;marker-end:url(#Arrow2Lend-0)" + d="m 265.74802,201.96848 c 35.43308,-159.448817 301.18111,-159.448817 336.61418,0" + id="path4914-8-3" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:2,4;stroke-dashoffset:0" + d="m 318.89764,361.4173 c -17.71654,0 -35.43307,0 -35.43307,0 l 0,0" + id="path5443" + inkscape:connector-curvature="0" /> + <text + xml:space="preserve" + style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + x="414.18863" + y="362.88965" + id="text5445" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan5447" + x="414.18863" + y="362.88965" /></text> + <g + id="g5586"> + <rect + rx="0" + ry="17.716547" + y="325.98422" + x="318.89764" + height="70.866142" + width="194.8819" + id="rect5453" + style="fill:#ffffe0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" /> + <text + sodipodi:linespacing="125%" + id="text5455" + y="354.7933" + x="418.09937" + style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + xml:space="preserve"><tspan + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L" + y="354.7933" + x="418.09937" + id="tspan5457" + sodipodi:role="line">#sc-1 detects that #fl-1</tspan><tspan + id="tspan5459" + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L" + y="377.2933" + x="418.09937" + sodipodi:role="line">interrupts the drawing</tspan></text> + </g> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0" + d="m 673.22836,450.00001 c -17.71654,0 -35.43307,0 -35.43307,0 l 0,0" + id="path5443-9" + inkscape:connector-curvature="0" + sodipodi:nodetypes="ccc" /> + <g + id="g5531" + transform="translate(-88.582663,106.29923)"> + <rect + rx="0" + ry="17.716547" + y="414.56689" + x="673.22833" + height="70.866142" + width="194.8819" + id="rect5453-7" + style="fill:#ffffe0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" /> + <text + sodipodi:linespacing="125%" + id="text5455-9" + y="443.37598" + x="770.91229" + style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + xml:space="preserve"><tspan + id="tspan5517" + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L" + y="443.37598" + x="770.91229" + sodipodi:role="line">#fl-1 is drawn as</tspan><tspan + id="tspan5521" + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L" + y="465.87598" + x="770.91229" + sodipodi:role="line">an interruption</tspan></text> + </g> + <flowRoot + xml:space="preserve" + id="flowRoot5523" + style="fill:black;stroke:none;stroke-opacity:1;stroke-width:1px;stroke-linejoin:miter;stroke-linecap:butt;fill-opacity:1;font-family:Nimbus Mono L;font-style:normal;font-weight:normal;font-size:20px;line-height:125%;letter-spacing:0px;word-spacing:0px;-inkscape-font-specification:Nimbus Mono L;font-stretch:normal;font-variant:normal"><flowRegion + id="flowRegion5525"><rect + id="rect5527" + width="230.37166" + height="100.60158" + x="662.54175" + y="400.53543" /></flowRegion><flowPara + id="flowPara5529"></flowPara></flowRoot> <g + id="g5699"> + <rect + rx="0" + ry="17.716547" + y="502.68695" + x="318.90851" + height="70.866142" + width="194.8819" + id="rect5453-70" + style="fill:#ffffe0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" /> + <text + sodipodi:linespacing="125%" + id="text5455-7" + y="531.49603" + x="418.11023" + style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + xml:space="preserve"><tspan + id="tspan5459-7" + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L" + y="531.49603" + x="418.11023" + sodipodi:role="line">drawing of #sc-1</tspan><tspan + id="tspan5584" + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L" + y="553.99603" + x="418.11023" + sodipodi:role="line">is continued</tspan></text> + </g> + <rect + style="fill:#ffffa0;fill-opacity:1;stroke:#000000;stroke-width:3.00000191;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect4643-9" + width="35.433094" + height="70.866158" + x="425.19681" + y="591.73224" /> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)" + d="m 283.46457,591.73226 141.73227,10e-6" + id="path3890-29-5-2-8" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6, 6;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)" + d="m 425.19685,662.59841 -141.73228,-10e-6" + id="path3890-29-5-2-0-0-4" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0" + d="m 673.22835,751.18108 c -17.71654,0 -35.43307,0 -35.43307,0 l 0,0" + id="path5443-8-2" + inkscape:connector-curvature="0" /> + <g + id="g5713" + transform="translate(-88.571786,105.83661)"> + <path + inkscape:connector-curvature="0" + id="path5443-8" + d="m 726.37796,786.61415 c -17.71654,0 -35.43307,0 -35.43307,0 l 0,0" + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0" /> + <rect + rx="0" + ry="17.716547" + y="715.74799" + x="673.22833" + height="70.866142" + width="194.8819" + id="rect5453-70-2" + style="fill:#ffffe0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" /> + <text + sodipodi:linespacing="125%" + id="text5455-7-4" + y="744.55707" + x="772.43005" + style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + xml:space="preserve"><tspan + id="tspan5707" + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L" + y="744.55707" + x="772.43005" + sodipodi:role="line">no need anymore</tspan><tspan + id="tspan5711" + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L" + y="767.05707" + x="772.43005" + sodipodi:role="line">to draw #fl-1</tspan></text> + </g> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0" + d="m 318.89764,538.58266 c -17.71654,0 -35.43307,0 -35.43307,0 l 0,0" + id="path5443-9-0" + inkscape:connector-curvature="0" + sodipodi:nodetypes="ccc" /> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0" + d="m 673.22835,449.99998 0,0" + id="path5443-9-6" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:2,4;stroke-dashoffset:0" + d="m 673.22835,520.86612 c 0,-70.86614 0,-70.86614 0,-70.86614" + id="path5738" + inkscape:connector-curvature="0" /> + <path + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0" + d="m 673.22835,822.04722 c 0,-70.86614 0,-70.86614 0,-70.86614" + id="path5738-7" + inkscape:connector-curvature="0" /> + </g> +</svg> diff --git a/doc/dw-interrupted-drawing.doc b/doc/dw-interrupted-drawing.doc new file mode 100644 index 00000000..c7037666 --- /dev/null +++ b/doc/dw-interrupted-drawing.doc @@ -0,0 +1,121 @@ +/** \page dw-interrupted-drawing Interrupted drawing + +Describing the problem +====================== + +Without interrupting drawing (which is described below), a widget can +define the order in which its parts (background, non-widget content, +child widgets, etc.) are drawn, but it must be drawn as a whole. There +are situations when this is not possible. + +Consider the following simple HTML document: + + <head> + <style> + #sc-1 { position: relative; z-index: 1; background: #ffe0e0; } + #fl-1 { float: right; background: #b0ffb0; } + #sc-2 { position: relative; z-index: 1; background: #f0f0ff; } + </style> + </head> + <body> + <div id="sc-1"> + <div id="fl-1"> + Float, line 1/3<br/> + Float, line 2/3<br/> + Float, line 3/3 + </div> + Stacking Context 1 + <div id="sc-2">Stacking Context 2</div> + </div> + </body> + +The rendering will look like this: + +\image html dw-interrupted-drawing-1.png + +Note the missing "Float, line 2/3" of element #fl-1, which is covered +by element #sc-2. + +As described in \ref dw-out-of-flow, it has to be distinguished +between the *container* hierarchy (equivalent to the hierarchy of +dw::core::Widget.) and the the *generator* hierarchy. In the following +diagram, the former is represented by solid lines, the latter by +dotted lines: + +\dot +digraph G { + node [shape=rect, fontname=Helvetica, fontsize=10]; + edge [arrowhead="vee"]; + + "#sc-1" [fillcolor="#ffe0e0", style="filled"]; + "#fl-1" [fillcolor="#b0ffb0", style="filled"]; + "#sc-2" [fillcolor="#f0f0ff", style="filled"]; + + "body" -> "#sc-1"; + "body" -> "#fl-1"; + { rank=same; "#sc-1" -> "#fl-1" [style=dotted]; } + "#sc-1" -> "#sc-2"; +} +\enddot + + +The drawing order of the four elements (represented by widgets) is: + +- body, +- #sc-1, +- #fl-1, +- #sc-2. + +Since + +1. #sc-2 is a child of #sc-1, but +2. #fl-1 is a child of the body, and +3. a widget can only draw its descendants (not neccessary children, + but drawing siblings is not allowed), + +#sc-1 cannot be drawn as a whole; instead drawing is **interrupted** +by #fl-1. This means: + +1. the background and text of #sc-1 is drawn; +2. drawing of #sc-1 is **interrupted** by #fl-1 (see below for details), +3. drawing of #sc-1 is **continued**, by drawing #sc-2. + +The exact control flow is described in this sequence diagram: + +\image html dw-interrupted-drawing-2.png + + +When is drawing interrupted? +============================ + +A widget out of flow is regarded as part of the stacking context (see +\ref dw-stacking-context) of its *generator* (in the example above: +#fl-1 is part of the stacking context stablished by #sc-1, not the one +established by body). For this reason, a widget out of flow must, in +some cases, drawn while the *gerator* is drawn, as an +interruption. The exact rule: + +A widget out of flow must be drawn as an interruption (while the +*generator* is drawn) if the stacking context of the generator (to +which this widget belongs) is in front of the stacking context of the +container (the parent widget). + +See dw::oof::OOFAwareWidget::doesWidgetOOFInterruptDrawing. + + +How does interruption of drawing work? +====================================== + +When a widget detects that an other widget should be drawn as +interruption (see above), it calls dw::core::Widget::drawInterruption, +which + +1. draws the widget within another "context" (area and reference + widget); for this the original drawing area + (dw::core::DrawingContext::getToplevelArea) is used. +2. Using dw::core::DrawingContext::addWidgetDrawnAsInterruption, and + checking later with + dw::core::DrawingContext::hasWidgetBeenDrawnAsInterruption prevents + these widgets from being drawn twice. + +*/ diff --git a/doc/dw-miscellaneous.doc b/doc/dw-miscellaneous.doc new file mode 100644 index 00000000..d6b3cae3 --- /dev/null +++ b/doc/dw-miscellaneous.doc @@ -0,0 +1,187 @@ +/** \page dw-miscellaneous Miscellaneous Notes on Dw + +This is a barely sorted list of issues which I consider noteworthy, +but have yet to be moved to other parts of the documentation (which is +partly to be created). + +General +======= + +Widget allocation outside of parent allocation +---------------------------------------------- +A widget allocation outside of the allocation of the parent is +allowed, but the part outside is not visible. + +Which widgets may be drawn? +------------------- + +All drawing starts with the toplevel widget +(cf. dw::core::Widget::queueDrawArea, dw::core::Layout::queueDraw, and +dw::core::Layout::expose), and a widget has to draw its children, in a +way consistent with their stacking order. + +There are two exceptions: + +1. Direct descendants, which are not children, may be drawn, if the + parent can distinguish them and so omit drawing them a second + time. See dw::core::StackingContextMgr and \ref dw-stacking-context. + Parents should not draw children in flow for which + dw::core::StackingContextMgr::handledByStackingContextMgr returns + true. +2. Interrupted drawing: via dw::core::Widget::drawInterruption; see + \ref dw-interrupted-drawing. + +Similar rules apply to handling mouse events +(dw::core::Widget::getWidgetAtPoint). + +Interrupted drawing +------------------- +→ \ref dw-interrupted-drawing. + +Similar rules apply to handling mouse events +(dw::core::Widget::getWidgetAtPoint). + + +Floats +====== + +Handling collisions +------------------- +The CSS specification allows two strategies to deal with colliding +floats: placing the second float beside or below the first one. Many +other browsers implement the first approach, while dillo implements +the second one, which may cause problems when the author assumes the +first. Example: the "tabs" at the top of every page at Wikipedia +("Article", "Talk", ...). + +Float containers in flow +------------------------ +Consider the following HTML snippet: + + <body> + <img src="....jpg" style="float:right"> + <p style="overflow:hidden">Text</p> + </body> + +Interestingly, dillo shows "Text" always *below* the image, even if +there is enough space left of it. An investigation shows that the +paragraph (<p>) is regarded as own floats container (because of +*overflow:hidden*), so the floats container above (<body>) +regards this block as widget which must be fit between the floats +(dw::Textblock::mustBorderBeRegarded > +dw::Textblock::getWidgetRegardingBorderForLine). However, since a +textblock in flow always covers (at least) the whole available width, +which is defined *without* considering floats, the space left of the +float will always be to narrow, so that the paragraph is moved below +the float, by inserting an empty line before. + +When searching for a solution, several difficulties show up: + +1. The available width, which is used for the width of the textblock, + is defined independent of floats. Aside from problems when changing + this definition, a dependance on floats would be difficult to + implement, since *sizeRequest* is independent of a position. (See + also \ref dw-out-of-flow.) +2. I must admit that I do not rembember the exact rationale and the + test case behind adding the exception in + dw::Textblock::getWidgetRegardingBorderForLine (see above), but + simply removing this exception will result in a possible + overlapping of floats from both containers, since no collisions are + tested for. +3. On the other hand, mixing the float containers (interaction of two + or more instances of dw::oof::OOFFloatsMgr), e. g. for + collision tests, would become too complex and possibly result in + performance problems. + +Instead, this approach is focussed: + +- Goal: the paragraph is narrowed so it fits, *as a whole*, between + the floats. +- The approach is to remove the exception in + dw::Textblock::getWidgetRegardingBorderForLine. A textblock, which + is a float container in flow (as this paragraph), is returned by + this method and so dw::Textblock::mustBorderBeRegarded returns + *true*. This will put this paragraph again at the correct position. +- To avoid overlappings, the linebreaking width of this paragraph + (which is also used for positioning of floats) is the available + width, minus the maximal float width on either side. (This is an + approach similar to the one dw::Ruler will use soon). Most likely, + some new methods will have to be added to calculate this. +- For paragraphs like this, dw::Textblock::borderChanged must rewrap + all lines; *y* is irrelevant in this case. +- Since the textblock will tend to become taller when getting + narrower, and so possibly cover more (wider) floats, and so become + narrower again etc., there may be multible solutions for calculating + the size. Generally, a smaller height (and so larger width) is + preferred. +- There remains a problem: what if a word is too large? Should a + textblock of this kind then reard the floats in detail, to insert + empty lines when needed? + +**Real-world cases:** *Overflow:hidden* is set for headings in +Wikipedia, and so this case occurs when there is a float (thumb image) +before a heading. See e. g. +<a href="http://de.wikipedia.org/wiki/Emmerich_am_Rhein#Ans.C3.A4ssige_Unternehmen">this page</a> +and scroll a bit up; the company logos should be right of this section. + +**Priority:** Since this is not a regression, compared to not +supporting floats at all, a fix is not urgent for a new release. + +Positioned elements +=================== + +General +------- +(See also *relative positions* below.) + +What about negative positions? + +dw::oof::OutOfFlowMgr::tellPosition1 and +dw::oof::OutOfFlowMgr::tellPosition2 could be combined again, and +called in the respective circumstance, depending on +dw::oof::OutOfFlowMgr::mayAffectBordersAtAll. + +Relative positions +------------------ +**General Overview:** At the original position, a space as large as +the positioned element is left. This is implemented by assigning a +size to the widget *reference*. For this there are two new methods: +dw::oof::OutOfFlowMgr::calcWidgetRefSize and +dw::oof::OOFAwareWidget::widgetRefSizeChanged. + +**Bug:** Since the size of a relatively positioned element should be +calculated as if it was in flow, the available width should be +delegated to the *generator*; however, since +dw::oof::OOFPosRelMgr::dealingWithSizeOfChild returns *false* in all +cases, it is delegated to the *container*. **Idea for fix:** +dw::oof::OOFPosRelMgr::dealingWithSizeOfChild should return *false* if +and only if the generator of the child is the container (to prevent an +endless recursion). In other cases, +dw::oof::OOFPosRelMgr::getAvailWidthOfChild and +dw::oof::OOFPosRelMgr::getAvailHeightOfChild should directly call the +respective methods of the *generator*, which must be made public then. + +**Performance:** In many cases, the first call of +dw::oof::OOFPosRelMgr::sizeAllocateEnd will queue again the resize +idle, since some elements are not considered in +dw::oof::OOFPosRelMgr::getSize. One case could be removed: if a +positioned element has *left* = *top* = 0, and its total size (the +requisition) is equal to the space left at the original position, the +size of the widget *reference* +(dw::oof::OOFAwareWidget::getRequisitionWithoutOOF, see +dw::oof::OOFPosRelMgr::calcWidgetRefSize). + +**Documentation:** Describe why the latter is not covered by +dw::oof::OOFPositionedMgr::doChildrenExceedContainer. (Is this really +the case?) + +**Open:** Stacking order? Furthermore: a relatively positioned element +does not always constitute a containing block (see CSS specification). + +Fixed positions +--------------- +Currently, fixedly positioned elements are positioned relative to the +canvas, not to the viewport. For a complete implementation, see \ref +dw-fixed-positions. + +*/ diff --git a/doc/dw-out-of-flow.doc b/doc/dw-out-of-flow.doc index ea4a52bc..2a43b876 100644 --- a/doc/dw-out-of-flow.doc +++ b/doc/dw-out-of-flow.doc @@ -1,9 +1,34 @@ /** \page dw-out-of-flow Handling Elements Out Of Flow - -<div style="border: 2px solid #ffff00; margin-bottom: 0.5em; -padding: 0.5em 1em; background-color: #ffffe0"><b>Info:</b> -Should be incorporated into dw::Textblock.</div> +<div style="border: 2px solid #ff4040; margin-bottom: 0.5em; +padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b> +Not up to date; incorporate these two changes: (i) there are different +containing blocks for floats and absolutely (furthermore also fixedly) +positioned elements; (ii) dw::OutOfFlowMgr is now only the base class; +floats and absolutely positioned elements are seperated: +\dot +digraph G { + node [shape=record, fontname=Helvetica, fontsize=10]; + edge [arrowhead="none", arrowtail="empty", dir="both"]; + fontname=Helvetica; fontsize=8; + + OutOfFlowMgr [URL="\ref dw::oof::OutOfFlowMgr"]; + OOFFloatsMgr [URL="\ref dw::oof::OOFFloatsMgr"]; + OOFPositionedMgr [URL="\ref dw::oof::OOFPositionedMgr"]; + OOFPosAbsLikeMgr [URL="\ref dw::oof::OOFPosAbsLikeMgr"]; + OOFPosAbsMgr [URL="\ref dw::oof::OOFPosAbsMgr"]; + OOFPosFixedMgr [URL="\ref dw::oof::OOFPosFixedMgr"]; + OOFPosRelMgr [URL="\ref dw::oof::OOFPosRelMgr"]; + + OutOfFlowMgr -> OOFFloatsMgr; + OutOfFlowMgr -> OOFPositionedMgr; + OOFPositionedMgr -> OOFPosAbsLikeMgr; + OOFPosAbsLikeMgr -> OOFPosAbsMgr; + OOFPosAbsLikeMgr -> OOFPosFixedMgr; + OOFPositionedMgr -> OOFPosRelMgr; +} +\enddot +</div> Introduction ============ @@ -209,6 +234,12 @@ Integration of line breaking and floats Absolute and fixed positiones ============================= -See <http://flpsed.org/hgweb/dillo_grows>. +To be documented. + + +See also +======== + +→ \ref dw-miscellaneous. */
\ No newline at end of file diff --git a/doc/dw-stacking-context.doc b/doc/dw-stacking-context.doc new file mode 100644 index 00000000..6138ca5d --- /dev/null +++ b/doc/dw-stacking-context.doc @@ -0,0 +1,114 @@ +/** \page dw-stacking-context Handling stacking contexts + +Stacking Context and dw::core::StackingContextMgr +================================================= + +For the definition of stacking contexts, see CSS 2.1 specification, + +- <a href="http://www.w3.org/TR/CSS2/visuren.html#z-index">section + 9.9.1: Specifying the stack level: the 'z-index' property</a> and +- <a href="http://www.w3.org/TR/CSS2/zindex.html">appendix E</a>. + +A widget establishes a stacking context when it is positioned and its +style value of *z-index* is different from *auto* (see +dw::core::StackingContextMgr::isEstablishingStackingContext). In this +case, it is assigned an instance of dw::core::StackingContextMgr, +which has also access to the widgets establishing the child contexts. + + +Stacking Order +============== + +The stacking order influences + +1. the order in which child widgets are drawn (dw::core::Widget::draw), + and +2. the order in which mouse events are dispatched to child widgets + (dw::core::Widget::getWidgetAtPoint). + +The first is done from bottom to top, the latter from top to bottom. + +I'm here referring to the simplified description in +<a href="http://www.w3.org/TR/CSS2/visuren.html#z-index">section +9.9.1</a>. The table shows a recommended order for the implementations +of dw::core::Widget::draw and dw::core::Widget::getWidgetAtPoint +(for the latter, read from bottom to top): + +<table> +<tr> +<th> CSS specification <th> Drawing <th> Mouse events +<tr> +<td> *1. the background and borders of the element forming the + stacking context.* +<td> dw::core::Widget::drawBox +<td> Nothing necessary. +<tr> +<td> *2. the child stacking contexts with negative stack levels (most + negative first).* +<td> dw::core::StackingContextMgr::drawBottom (when defined) +<td> dw::core::StackingContextMgr::getBottomWidgetAtPoint (when defined) +<tr> +<td> *3. the in-flow, non-inline-level, non-positioned descendants.* + +<td rowspan="4"> When (i) widget specific content is drawn, then (ii) + dw::oof::OOFAwareWidget::drawOOF is called, this will + have this effect: + + 1. all in-flow elements are drawn, + 2. floats are drawn and + 3. positioned elements with *z-index: auto* are drawn + (latter two done by + dw::oof::OOFAwareWidget::drawOOF, in this order). + + This order differs from the specified order, but + since floats and in-flow elements do not overlap, + this difference has no effect. + + Drawing in-line elements, floats and positioned + elements with *z-index: auto* and should avoid + duplicate calls: Widgets drawn by + dw::core::StackingContextMgr::drawBottom and by + dw::core::StackingContextMgr::drawTop should be + excluded here. This can be tested with + dw::core::StackingContextMgr::handledByStackingContextMgr. + +<td rowspan="4"> Likewise, the implementation should (i) test + dw::oof::OOFAwareWidget::getWidgetOOFAtPoint, and + (ii) search through the chilren. Also, duplicate + calls should be avoided using + dw::core::StackingContextMgr::handledByStackingContextMgr. + + There are already the implementations + dw::core::Widget::getWidgetAtPoint (ignoring + dw::oof::OutOfFlowMgr) and + dw::oof::OOFAwareWidget::getWidgetAtPoint (including + dw::oof::OutOfFlowMgr). + +<tr> +<td> *4. the non-positioned floats.* +<tr> +<td> *5. the in-flow, inline-level, non-positioned descendants, + including inline tables and inline blocks.* +<tr> +<td> (What about positioned elements with *z-index: auto*? Seems to be + missing in + <a href="http://www.w3.org/TR/CSS2/visuren.html#z-index">section + 9.9.1</a>, but mentioned in + <a href="http://www.w3.org/TR/CSS2/zindex.html">appendix E</a>, + item 8. +<tr> +<td> *6. the child stacking contexts with stack level 0 and the + positioned descendants with stack level 0.* +<td rowspan="2"> dw::core::StackingContextMgr::drawTop (when defined) +<td rowspan="2"> dw::core::StackingContextMgr::getTopWidgetAtPoint + (when defined) +<tr> +<td> *7. the child stacking contexts with positive stack levels (least + positive first).* +</table> + +Note: This is not quite in conformance with the specification: this +description refers to any widget, not only widgets establishing a +stacking context. Does this make a difference? + +*/
\ No newline at end of file diff --git a/dw/Makefile.am b/dw/Makefile.am index 68410805..ee891234 100644 --- a/dw/Makefile.am +++ b/dw/Makefile.am @@ -23,6 +23,8 @@ libDw_core_a_SOURCES = \ platform.hh \ selection.hh \ selection.cc \ + stackingcontextmgr.hh \ + stackingcontextmgr.cc \ style.cc \ style.hh \ types.cc \ @@ -69,6 +71,21 @@ libDw_widgets_a_SOURCES = \ image.hh \ listitem.cc \ listitem.hh \ + oofawarewidget.cc \ + oofawarewidget_iterator.cc \ + oofawarewidget.hh \ + ooffloatsmgr.cc \ + ooffloatsmgr.hh \ + oofposabslikemgr.cc \ + oofposabslikemgr.hh \ + oofposabsmgr.cc \ + oofposabsmgr.hh \ + oofposfixedmgr.cc \ + oofposfixedmgr.hh \ + oofpositionedmgr.cc \ + oofpositionedmgr.hh \ + oofposrelmgr.cc \ + oofposrelmgr.hh \ outofflowmgr.cc \ outofflowmgr.hh \ regardingborder.cc \ diff --git a/dw/alignedtablecell.cc b/dw/alignedtablecell.cc index e65bbf2c..b5e321b7 100644 --- a/dw/alignedtablecell.cc +++ b/dw/alignedtablecell.cc @@ -17,8 +17,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - - #include "alignedtablecell.hh" #include "table.hh" #include "tablecell.hh" @@ -65,7 +63,7 @@ bool AlignedTableCell::mustBeWidenedToAvailWidth () int AlignedTableCell::getAvailWidthOfChild (Widget *child, bool forceValue) { - DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell/getAvailWidthOfChild", + DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell::getAvailWidthOfChild", "%p, %s", child, forceValue ? "true" : "false"); int width = tablecell::correctAvailWidthOfChild @@ -78,7 +76,7 @@ int AlignedTableCell::getAvailWidthOfChild (Widget *child, bool forceValue) int AlignedTableCell::getAvailHeightOfChild (Widget *child, bool forceValue) { - DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell/getAvailHeightOfChild", + DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell::getAvailHeightOfChild", "%p, %s", child, forceValue ? "true" : "false"); int height = tablecell::correctAvailHeightOfChild @@ -96,7 +94,7 @@ void AlignedTableCell::correctRequisitionOfChild (Widget *child, int*, int*)) { - DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell/correctRequisitionOfChild", + DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell::correctRequisitionOfChild", "%p, %d * (%d + %d), ...", child, requisition->width, requisition->ascent, requisition->descent); @@ -112,7 +110,7 @@ void AlignedTableCell::correctExtremesOfChild (Widget *child, core::Extremes *extremes, bool useAdjustmentWidth) { - DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell/correctExtremesOfChild", + DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell::correctExtremesOfChild", "%p, %d (%d) / %d (%d)", child, extremes->minWidth, extremes->minWidthIntrinsic, extremes->maxWidth, extremes->maxWidthIntrinsic); @@ -137,6 +135,11 @@ int AlignedTableCell::applyPerHeight (int containerHeight, return tablecell::applyPerHeight (this, containerHeight, perHeight); } +bool AlignedTableCell::adjustExtraSpaceWhenCorrectingRequisitionByOOF () +{ + return tablecell::adjustExtraSpaceWhenCorrectingRequisitionByOOF (); +} + int AlignedTableCell::wordWrap(int wordIndex, bool wrapAll) { Textblock::Word *word; @@ -199,7 +202,7 @@ int AlignedTableCell::getValue () void AlignedTableCell::setMaxValue (int maxValue, int value) { line1Offset = maxValue - value; - queueResize (OutOfFlowMgr::createRefNormalFlow (0), true); + queueResize (makeParentRefInFlow (0), true); } } // namespace dw diff --git a/dw/alignedtablecell.hh b/dw/alignedtablecell.hh index b4203047..56196e6d 100644 --- a/dw/alignedtablecell.hh +++ b/dw/alignedtablecell.hh @@ -23,6 +23,8 @@ protected: bool getAdjustMinWidth (); + bool adjustExtraSpaceWhenCorrectingRequisitionByOOF (); + int wordWrap (int wordIndex, bool wrapAll); int getValue (); diff --git a/dw/bullet.cc b/dw/bullet.cc index 40c197e6..98d90672 100644 --- a/dw/bullet.cc +++ b/dw/bullet.cc @@ -56,7 +56,8 @@ void Bullet::containerSizeChangedForChildren () DBG_OBJ_LEAVE (); } -void Bullet::draw (core::View *view, core::Rectangle *area) +void Bullet::draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context) { int x, y, l; bool filled = true; diff --git a/dw/bullet.hh b/dw/bullet.hh index 004187cd..e50fa0eb 100644 --- a/dw/bullet.hh +++ b/dw/bullet.hh @@ -17,7 +17,8 @@ protected: void sizeRequestImpl (core::Requisition *requisition); void getExtremesImpl (core::Extremes *extremes); void containerSizeChangedForChildren (); - void draw (core::View *view, core::Rectangle *area); + void draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context); core::Iterator *iterator (core::Content::Type mask, bool atEnd); public: @@ -25,6 +25,7 @@ class Layout; class View; class Widget; class Iterator; +class StackingContextMgr; // Nothing yet to free. inline void freeall () { } @@ -53,6 +54,7 @@ class ResourceFactory; #include "selection.hh" #include "layout.hh" #include "widget.hh" +#include "stackingcontextmgr.hh" #include "ui.hh" #undef __INCLUDED_FROM_DW_CORE_HH__ diff --git a/dw/fltkui.cc b/dw/fltkui.cc index 51523b95..09965ea7 100644 --- a/dw/fltkui.cc +++ b/dw/fltkui.cc @@ -476,7 +476,8 @@ void FltkResource::sizeAllocate (core::Allocation *allocation) DBG_OBJ_LEAVE (); } -void FltkResource::draw (core::View *view, core::Rectangle *area) +void FltkResource::draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context) { FltkView *fltkView = (FltkView*)view; if (fltkView->usesFltkWidgets () && this->view == fltkView) { @@ -577,9 +578,11 @@ template <class I> void FltkSpecificResource<I>::sizeAllocate (core::Allocation } template <class I> void FltkSpecificResource<I>::draw (core::View *view, - core::Rectangle *area) + core::Rectangle *area, + core::DrawingContext + *context) { - FltkResource::draw (view, area); + FltkResource::draw (view, area, context); } template <class I> void FltkSpecificResource<I>::setStyle (core::style::Style diff --git a/dw/fltkui.hh b/dw/fltkui.hh index 09cdc978..ff6331d1 100644 --- a/dw/fltkui.hh +++ b/dw/fltkui.hh @@ -211,7 +211,8 @@ public: virtual void detachView (FltkView *view); void sizeAllocate (core::Allocation *allocation); - void draw (core::View *view, core::Rectangle *area); + void draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context); void setStyle (core::style::Style *style); @@ -227,7 +228,8 @@ public: ~FltkSpecificResource (); void sizeAllocate (core::Allocation *allocation); - void draw (core::View *view, core::Rectangle *area); + void draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context); void setStyle (core::style::Style *style); bool isEnabled (); diff --git a/dw/image.cc b/dw/image.cc index fd959695..030d4882 100644 --- a/dw/image.cc +++ b/dw/image.cc @@ -337,7 +337,7 @@ void Image::leaveNotifyImpl (core::EventCrossing *event) */ int Image::contentX (core::MousePositionEvent *event) { - int ret = event->xWidget - getStyle()->boxOffsetX(); + int ret = event->xWidget - boxOffsetX(); ret = misc::min(getContentWidth(), misc::max(ret, 0)); return ret; @@ -345,7 +345,7 @@ int Image::contentX (core::MousePositionEvent *event) int Image::contentY (core::MousePositionEvent *event) { - int ret = event->yWidget - getStyle()->boxOffsetY(); + int ret = event->yWidget - boxOffsetY(); ret = misc::min(getContentHeight(), misc::max(ret, 0)); return ret; @@ -407,7 +407,8 @@ bool Image::buttonReleaseImpl (core::EventButton *event) return false; } -void Image::draw (core::View *view, core::Rectangle *area) +void Image::draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context) { int dx, dy; core::Rectangle content, intersection; @@ -415,8 +416,8 @@ void Image::draw (core::View *view, core::Rectangle *area) drawWidgetBox (view, area, false); if (buffer) { - dx = getStyle()->boxOffsetX (); - dy = getStyle()->boxOffsetY (); + dx = boxOffsetX (); + dy = boxOffsetY (); content.x = dx; content.y = dy; content.width = getContentWidth (); @@ -443,31 +444,29 @@ void Image::draw (core::View *view, core::Rectangle *area) (getContentHeight() < getStyle()->font->ascent + getStyle()->font->descent)) { clippingView = usedView = - view->getClippingView (allocation.x + getStyle()->boxOffsetX (), - allocation.y + getStyle()->boxOffsetY (), + view->getClippingView (allocation.x + boxOffsetX (), + allocation.y + boxOffsetY (), getContentWidth(), getContentHeight()); } usedView->drawSimpleWrappedText (getStyle()->font, getStyle()->color, core::style::Color::SHADING_NORMAL, - allocation.x + getStyle()->boxOffsetX (), - allocation.y + getStyle()->boxOffsetY (), + allocation.x + boxOffsetX (), + allocation.y + boxOffsetY (), getContentWidth(), getContentHeight(), altText); if (clippingView) view->mergeClippingView (clippingView); } if (mapKey) { - clippingView = view->getClippingView (allocation.x + - getStyle()->boxOffsetX (), - allocation.y + - getStyle()->boxOffsetY (), + clippingView = view->getClippingView (allocation.x + boxOffsetX (), + allocation.y + boxOffsetY (), getContentWidth(), getContentHeight()); mapList->drawMap(mapKey, clippingView, getStyle(), - allocation.x + getStyle()->boxOffsetX (), - allocation.y + getStyle()->boxOffsetY ()); + allocation.x + boxOffsetX (), + allocation.y + boxOffsetY ()); view->mergeClippingView (clippingView); } } @@ -518,9 +517,8 @@ void Image::drawRow (int row) buffer->getRowArea (row, &area); if (area.width && area.height) - queueDrawArea (area.x + getStyle()->boxOffsetX (), - area.y + getStyle()->boxOffsetY (), - area.width, area.height); + queueDrawArea (area.x + boxOffsetX (), area.y + boxOffsetY (), area.width, + area.height); } void Image::finish () diff --git a/dw/image.hh b/dw/image.hh index b94f647d..a46dd20a 100644 --- a/dw/image.hh +++ b/dw/image.hh @@ -134,8 +134,9 @@ protected: void getExtremesImpl (core::Extremes *extremes); void sizeAllocateImpl (core::Allocation *allocation); void containerSizeChangedForChildren (); - - void draw (core::View *view, core::Rectangle *area); + + void draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context); bool buttonPressImpl (core::EventButton *event); bool buttonReleaseImpl (core::EventButton *event); diff --git a/dw/iterator.cc b/dw/iterator.cc index 0edb580b..dbb779f6 100644 --- a/dw/iterator.cc +++ b/dw/iterator.cc @@ -204,14 +204,6 @@ void Iterator::scrollTo (Iterator *it1, Iterator *it2, int start, int end, } } - -void Iterator::print () -{ - misc::StringBuffer sb; - intoStringBuffer (&sb); - printf ("%s", sb.getChars ()); -} - // ------------------- // EmptyIterator // ------------------- diff --git a/dw/iterator.hh b/dw/iterator.hh index abf31d0b..9460adc4 100644 --- a/dw/iterator.hh +++ b/dw/iterator.hh @@ -86,8 +86,6 @@ public: static void scrollTo (Iterator *it1, Iterator *it2, int start, int end, HPosition hpos, VPosition vpos); - - virtual void print (); }; diff --git a/dw/layout.cc b/dw/layout.cc index c2a53d08..d18bc486 100644 --- a/dw/layout.cc +++ b/dw/layout.cc @@ -374,6 +374,14 @@ void Layout::addWidget (Widget *widget) return; } + // The toplevel widget always establishes a stacking context. It could + // already be set in Widget::setStyle(). + if (widget->stackingContextMgr == NULL) { + widget->stackingContextMgr = new StackingContextMgr (widget); + DBG_OBJ_ASSOC (widget, widget->stackingContextMgr); + widget->stackingContextWidget = widget; + } + topLevel = widget; widget->layout = this; widget->container = NULL; @@ -654,6 +662,9 @@ bool Layout::calcScrollInto (int requestedValue, int requestedSize, void Layout::draw (View *view, Rectangle *area) { + DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d", + area->x, area->y, area->width, area->height); + Rectangle widgetArea, intersection, widgetDrawArea; // First of all, draw background image. (Unlike background *color*, @@ -693,11 +704,14 @@ void Layout::draw (View *view, Rectangle *area) widgetDrawArea.width = intersection.width; widgetDrawArea.height = intersection.height; - topLevel->draw (view, &widgetDrawArea); + DrawingContext context (&widgetArea); + topLevel->draw (view, &widgetDrawArea, &context); view->finishDrawing (&intersection); } } + + DBG_OBJ_LEAVE (); } int Layout::currHScrollbarThickness() @@ -1093,12 +1107,18 @@ void Layout::leaveNotify (View *view, ButtonState state) */ Widget *Layout::getWidgetAtPoint (int x, int y) { - _MSG ("------------------------------------------------------------\n"); - _MSG ("widget at (%d, %d)\n", x, y); - if (topLevel && topLevel->wasAllocated ()) - return topLevel->getWidgetAtPoint (x, y, 0); - else - return NULL; + DBG_OBJ_ENTER ("events", 0, "getWidgetAtPoint", "%d, %d", x, y); + Widget *widget; + + if (topLevel && topLevel->wasAllocated ()) { + GettingWidgetAtPointContext context; + widget = topLevel->getWidgetAtPoint (x, y, &context); + } else + widget = NULL; + + DBG_OBJ_MSGF ("events", 0, "=> %p", widget); + DBG_OBJ_LEAVE (); + return widget; } @@ -1108,12 +1128,16 @@ Widget *Layout::getWidgetAtPoint (int x, int y) */ void Layout::moveToWidget (Widget *newWidgetAtPoint, ButtonState state) { + DBG_OBJ_ENTER ("events", 0, "moveToWidget", "%p, %d", + newWidgetAtPoint, state); + Widget *ancestor, *w; Widget **track; int trackLen, i, i_a; EventCrossing crossingEvent; - _MSG("moveToWidget: wap=%p nwap=%p\n",widgetAtPoint,newWidgetAtPoint); + DBG_OBJ_MSGF ("events", 1, "(old) widgetAtPoint = %p", widgetAtPoint); + if (newWidgetAtPoint != widgetAtPoint) { // The mouse pointer has been moved into another widget. if (newWidgetAtPoint && widgetAtPoint) @@ -1183,6 +1207,8 @@ void Layout::moveToWidget (Widget *newWidgetAtPoint, ButtonState state) widgetAtPoint = newWidgetAtPoint; updateCursor (); } + + DBG_OBJ_LEAVE (); } /** diff --git a/dw/layout.hh b/dw/layout.hh index 32b9a134..16d19979 100644 --- a/dw/layout.hh +++ b/dw/layout.hh @@ -314,7 +314,12 @@ public: /* View */ - inline void expose (View *view, Rectangle *area) { draw (view, area); } + inline void expose (View *view, Rectangle *area) { + DBG_OBJ_ENTER ("draw", 0, "expose", "%d, %d, %d * %d", + area->x, area->y, area->width, area->height); + draw (view, area); + DBG_OBJ_LEAVE (); + } /** * \brief This function is called by a view, to delegate a button press diff --git a/dw/oofawarewidget.cc b/dw/oofawarewidget.cc new file mode 100644 index 00000000..c1398a64 --- /dev/null +++ b/dw/oofawarewidget.cc @@ -0,0 +1,592 @@ +/* + * Dillo Widget + * + * Copyright 2014 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "oofawarewidget.hh" +#include "ooffloatsmgr.hh" +#include "oofposabsmgr.hh" +#include "oofposrelmgr.hh" +#include "oofposfixedmgr.hh" + +using namespace dw; +using namespace dw::core; +using namespace dw::core::style; +using namespace lout::object; +using namespace lout::misc; +using namespace lout::container::typed; + +namespace dw { + +namespace oof { + +const char *OOFAwareWidget::OOFM_NAME[NUM_OOFM] = { + "FLOATS", "ABSOLUTE", "RELATIVE", "FIXED" +}; + +int OOFAwareWidget::CLASS_ID = -1; + +OOFAwareWidget::OOFAwareWidget () +{ + DBG_OBJ_CREATE ("dw::oof::OOFAwareWidget"); + registerName ("dw::oof::OOFAwareWidget", &CLASS_ID); + + for (int i = 0; i < NUM_OOFM; i++) { + oofContainer[i] = NULL; + DBG_OBJ_ARRSET_PTR ("oofContainer", i, oofContainer[i]); + outOfFlowMgr[i] = NULL; + } +} + +OOFAwareWidget::~OOFAwareWidget () +{ + for (int i = 0; i < NUM_OOFM; i++) { + if(outOfFlowMgr[i]) { + // I feel more comfortable by letting the OOF aware widget delete + // these widgets, instead of doing this in ~OutOfFlowMgr. + for (int j = 0; j < outOfFlowMgr[i]->getNumWidgets (); j++) + delete outOfFlowMgr[i]->getWidget (j); + + delete outOfFlowMgr[i]; + } + } + + DBG_OBJ_DELETE (); +} + +const char *OOFAwareWidget::stackingLevelText (int level) +{ + switch (level) { + case SL_START: return "START"; + case SL_BACKGROUND: return "BACKGROUND"; + case SL_SC_BOTTOM: return "SC_BOTTOM"; + case SL_IN_FLOW: return "IN_FLOW"; + case SL_OOF_REF: return "OOF_REF"; + case SL_OOF_CONT: return "OOF_CONT"; + case SL_SC_TOP: return "SC_TOP"; + case SL_END: return "END"; + default: return "???"; + } +} + +void OOFAwareWidget::notifySetAsTopLevel () +{ + for (int i = 0; i < NUM_OOFM; i++) { + oofContainer[i] = this; + DBG_OBJ_ARRSET_PTR ("oofContainer", i, oofContainer[i]); + } +} + +int OOFAwareWidget::getOOFMIndex (Widget *widget) +{ + DBG_OBJ_ENTER_O ("construct", 0, NULL, "getOOFMIndex", "%p", widget); + DBG_OBJ_MSGF_O ("construct", 1, NULL, "position = %s, float = %s", + widget->getStyle()->position + == style::POSITION_STATIC ? "static" : + (widget->getStyle()->position + == style::POSITION_RELATIVE ? "relative" : + (widget->getStyle()->position + == style::POSITION_ABSOLUTE ? "absolute" : + (widget->getStyle()->position + == style::POSITION_FIXED ? "fixed" : "???"))), + widget->getStyle()->vloat == style::FLOAT_NONE ? "none" : + (widget->getStyle()->vloat == style::FLOAT_LEFT ? "left" : + (widget->getStyle()->vloat == style::FLOAT_RIGHT ? + "right" : "???"))); + + int index = -1; + if (testWidgetFloat (widget)) + index = OOFM_FLOATS; + else if (testWidgetAbsolutelyPositioned (widget)) + index = OOFM_ABSOLUTE; + else if (testWidgetRelativelyPositioned (widget)) + index = OOFM_RELATIVE; + else if (testWidgetFixedlyPositioned (widget)) + index = OOFM_FIXED; + else + lout::misc::assertNotReached (); + + DBG_OBJ_LEAVE_VAL_O (NULL, "%d (%s)", index, OOFM_NAME[index]); + return index; +} + +bool OOFAwareWidget::isOOFContainer (Widget *widget, int oofmIndex) +{ + // TODO The methods isPossibleContainer() and isPossibleContainerParent() + // are only used in few cases. Does not matter currently, however. + + switch (oofmIndex) { + case OOFM_FLOATS: + return widget->instanceOf (OOFAwareWidget::CLASS_ID) && + (// For floats, only some OOF aware widgets are considered as + // containers. + ((OOFAwareWidget*)widget)->isPossibleContainer (OOFM_FLOATS) && + // The second condition: that this block is "out of flow", in a + // wider sense. + (// The toplevel widget is "out of flow", since there is no + // parent, and so no context. + widget->getParent() == NULL || + // A similar reasoning applies to a widget with an + // unsuitable parent (typical example: a table cell (this + // is also a text block, so possible float container) + // within a table widget, which is not a suitable float + // container parent). + !(widget->getParent()->instanceOf (OOFAwareWidget::CLASS_ID) && + ((OOFAwareWidget*)widget->getParent()) + ->isPossibleContainerParent (OOFM_FLOATS)) || + // Inline blocks are containing blocks, too. + widget->getStyle()->display == DISPLAY_INLINE_BLOCK || + // Same for blocks with 'overview' set to another value than + // (the default value) 'visible'. + widget->getStyle()->overflow != OVERFLOW_VISIBLE || + // Finally, "out of flow" in a narrower sense: floats; + // absolutely and fixedly positioned elements. (No + // relatively positioned elements; since the latters + // constitute a stacking context, drawing of floats gets + // somewhat more complicated; see "interrupting the drawing + // process" in "dw-stacking-context.doc". + testWidgetOutOfFlow (widget))); + + case OOFM_RELATIVE: + case OOFM_ABSOLUTE: + return widget->instanceOf (OOFAwareWidget::CLASS_ID) && + (widget->getParent() == NULL || + OOFAwareWidget::testWidgetPositioned (widget)); + + + case OOFM_FIXED: + // The single container for fixedly positioned elements is the + // toplevel (canvas; actually the viewport). (The toplevel + // widget should always be a textblock; at least this is the + // case in dillo.) + return widget->getParent() == NULL; + + default: + // compiler happiness + lout::misc::assertNotReached (); + return false; + } +} + +void OOFAwareWidget::notifySetParent () +{ + // Search for containing blocks. + for (int oofmIndex = 0; oofmIndex < NUM_OOFM; oofmIndex++) { + oofContainer[oofmIndex] = NULL; + + for (Widget *widget = this; + widget != NULL && oofContainer[oofmIndex] == NULL; + widget = widget->getParent ()) + if (isOOFContainer (widget, oofmIndex)) { + assert (widget->instanceOf (OOFAwareWidget::CLASS_ID)); + oofContainer[oofmIndex] = (OOFAwareWidget*)widget; + } + + DBG_OBJ_ARRSET_PTR ("oofContainer", oofmIndex, oofContainer[oofmIndex]); + + assert (oofContainer[oofmIndex] != NULL); + } +} + +void OOFAwareWidget::initOutOfFlowMgrs () +{ + if (oofContainer[OOFM_FLOATS]->outOfFlowMgr[OOFM_FLOATS] == NULL) { + oofContainer[OOFM_FLOATS]->outOfFlowMgr[OOFM_FLOATS] = + new OOFFloatsMgr (oofContainer[OOFM_FLOATS]); + DBG_OBJ_ASSOC (oofContainer[OOFM_FLOATS], + oofContainer[OOFM_FLOATS]->outOfFlowMgr[OOFM_FLOATS]); + } + + if (oofContainer[OOFM_ABSOLUTE]->outOfFlowMgr[OOFM_ABSOLUTE] == NULL) { + oofContainer[OOFM_ABSOLUTE]->outOfFlowMgr[OOFM_ABSOLUTE] = + new OOFPosAbsMgr (oofContainer[OOFM_ABSOLUTE]); + DBG_OBJ_ASSOC (oofContainer[OOFM_ABSOLUTE], + oofContainer[OOFM_ABSOLUTE]->outOfFlowMgr[OOFM_ABSOLUTE]); + } + + if (oofContainer[OOFM_RELATIVE]->outOfFlowMgr[OOFM_RELATIVE] == NULL) { + oofContainer[OOFM_RELATIVE]->outOfFlowMgr[OOFM_RELATIVE] = + new OOFPosRelMgr (oofContainer[OOFM_RELATIVE]); + DBG_OBJ_ASSOC (oofContainer[OOFM_RELATIVE], + oofContainer[OOFM_RELATIVE]->outOfFlowMgr[OOFM_RELATIVE]); + } + + if (oofContainer[OOFM_FIXED]->outOfFlowMgr[OOFM_FIXED] == NULL) { + oofContainer[OOFM_FIXED]->outOfFlowMgr[OOFM_FIXED] = + new OOFPosFixedMgr (oofContainer[OOFM_FIXED]); + DBG_OBJ_ASSOC (oofContainer[OOFM_FIXED], + oofContainer[OOFM_FIXED]->outOfFlowMgr[OOFM_FIXED]); + } +} + +void OOFAwareWidget::correctRequisitionByOOF (Requisition *requisition, + void (*splitHeightFun) (int, int*, + int*)) +{ + DBG_OBJ_ENTER ("resize", 0, "correctRequisitionByOOF", "%d * (%d + %d), ...", + requisition->width, requisition->ascent, + requisition->descent); + + requisitionWithoutOOF = *requisition; + + for (int i = 0; i < NUM_OOFM; i++) { + if (outOfFlowMgr[i]) { + DBG_OBJ_MSGF ("resize", 1, "OOFM for %s", OOFM_NAME[i]); + DBG_OBJ_MSG_START (); + + int oofWidth, oofHeight; + + outOfFlowMgr[i]->getSize (requisition, &oofWidth, &oofHeight); + DBG_OBJ_MSGF ("resize", 1, "result: %d * %d", oofWidth, oofHeight); + + if (oofWidth > requisition->width) { + if (outOfFlowMgr[i]->containerMustAdjustExtraSpace () && + adjustExtraSpaceWhenCorrectingRequisitionByOOF ()) { + extraSpace.right = max (extraSpace.right, + oofWidth - requisition->width); + DBG_OBJ_SET_NUM ("extraSpace.right", extraSpace.right); + } + + requisition->width = oofWidth; + } + + if (oofHeight > requisition->ascent + requisition->descent) { + if (outOfFlowMgr[i]->containerMustAdjustExtraSpace () && + adjustExtraSpaceWhenCorrectingRequisitionByOOF ()) { + extraSpace.bottom = max (extraSpace.bottom, + oofHeight - (requisition->ascent + + requisition->descent)); + DBG_OBJ_SET_NUM ("extraSpace.bottom", extraSpace.bottom); + } + + splitHeightFun (oofHeight, + &requisition->ascent, &requisition->descent); + } + + if (!adjustExtraSpaceWhenCorrectingRequisitionByOOF ()) { + requisitionWithoutOOF.width = max (requisitionWithoutOOF.width, + oofWidth); + if (oofHeight > + requisitionWithoutOOF.ascent + requisitionWithoutOOF.descent) + splitHeightFun (oofHeight, &requisitionWithoutOOF.ascent, + &requisitionWithoutOOF.descent); + } + + DBG_OBJ_MSGF ("resize", 1, "after correction: %d * (%d + %d)", + requisition->width, requisition->ascent, + requisition->descent); + DBG_OBJ_MSG_END (); + } else + DBG_OBJ_MSGF ("resize", 1, "no OOFM for %s", OOFM_NAME[i]); + } + + DBG_OBJ_SET_NUM ("requisitionWithoutOOF.width", requisitionWithoutOOF.width); + DBG_OBJ_SET_NUM ("requisitionWithoutOOF.ascent", + requisitionWithoutOOF.ascent); + DBG_OBJ_SET_NUM ("requisitionWithoutOOF.descent", + requisitionWithoutOOF.descent); + + DBG_OBJ_LEAVE (); +} + +void OOFAwareWidget::correctExtremesByOOF (Extremes *extremes) +{ + DBG_OBJ_ENTER ("resize", 0, "correctExtremesByOOF", "%d (%d) / %d (%d)", + extremes->minWidth, extremes->minWidthIntrinsic, + extremes->maxWidth, extremes->maxWidthIntrinsic); + + for (int i = 0; i < NUM_OOFM; i++) { + if (outOfFlowMgr[i]) { + DBG_OBJ_MSGF ("resize", 1, "OOFM for %s", OOFM_NAME[i]); + DBG_OBJ_MSG_START (); + + int oofMinWidth, oofMaxWidth; + outOfFlowMgr[i]->getExtremes (extremes, &oofMinWidth, &oofMaxWidth); + DBG_OBJ_MSGF ("resize", 1, "result: %d / %d", + oofMinWidth, oofMaxWidth); + + extremes->minWidth = max (extremes->minWidth, oofMinWidth); + extremes->minWidthIntrinsic = max (extremes->minWidthIntrinsic, + oofMinWidth); + extremes->maxWidth = max (extremes->maxWidth, oofMaxWidth); + extremes->maxWidthIntrinsic = max (extremes->maxWidthIntrinsic, + oofMinWidth); + extremes->adjustmentWidth = max (extremes->adjustmentWidth, + oofMinWidth); + + DBG_OBJ_MSGF ("resize", 1, "after correction: %d (%d) / %d (%d)", + extremes->minWidth, extremes->minWidthIntrinsic, + extremes->maxWidth, extremes->maxWidthIntrinsic); + DBG_OBJ_MSG_END (); + } else + DBG_OBJ_MSGF ("resize", 1, "no OOFM for %s", OOFM_NAME[i]); + } + + DBG_OBJ_LEAVE (); +} + +void OOFAwareWidget::sizeAllocateStart (Allocation *allocation) +{ + + for (int i = 0; i < NUM_OOFM; i++) + if (oofContainer[i]->outOfFlowMgr[i]) + oofContainer[i]->outOfFlowMgr[i]->sizeAllocateStart (this, allocation); +} + +void OOFAwareWidget::sizeAllocateEnd () +{ + for (int i = 0; i < NUM_OOFM; i++) + if (oofContainer[i]->outOfFlowMgr[i]) + oofContainer[i]->outOfFlowMgr[i]->sizeAllocateEnd (this); +} + +void OOFAwareWidget::containerSizeChangedForChildrenOOF () +{ + for (int i = 0; i < NUM_OOFM; i++) + if (outOfFlowMgr[i]) + outOfFlowMgr[i]->containerSizeChangedForChildren (); +} + +bool OOFAwareWidget::doesWidgetOOFInterruptDrawing (Widget *widget) +{ + DBG_OBJ_ENTER ("draw", 0, "doesWidgetOOFInterruptDrawing", "%p", widget); + + // This is the generator of the widget. + int oofmIndex = getOOFMIndex (widget); + DBG_OBJ_MSGF ("draw", 1, "oofmIndex = %d", oofmIndex); + + int cl = oofContainer[oofmIndex]->stackingContextWidget->getLevel (), + gl = stackingContextWidget->getLevel (); + + DBG_OBJ_MSGF ("draw", 1,"%d < %d => %s", cl, gl, cl < gl ? "true" : "false"); + + DBG_OBJ_LEAVE (); + return cl < gl; +} + +void OOFAwareWidget::draw (View *view, Rectangle *area, DrawingContext *context) +{ + DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d", + area->x, area->y, area->width, area->height); + + for (int level = SL_START + 1; level < SL_END; level++) + drawLevel (view, area, level, context); + + DBG_OBJ_LEAVE (); +} + +void OOFAwareWidget::drawLevel (View *view, Rectangle *area, int level, + DrawingContext *context) +{ + DBG_OBJ_ENTER ("draw", 0, "OOFAwareWidget::drawLevel", + "(%d, %d, %d * %d), %s", + area->x, area->y, area->width, area->height, + stackingLevelText (level)); + + switch (level) { + case SL_START: + break; + + case SL_BACKGROUND: + drawWidgetBox (view, area, false); + break; + + case SL_SC_BOTTOM: + if (stackingContextMgr) + stackingContextMgr->drawBottom (view, area, context); + break; + + case SL_IN_FLOW: + // Should be implemented in the sub class. + break; + + case SL_OOF_REF: + // Should be implemented in the sub class (when references are hold). + break; + + case SL_OOF_CONT: + drawOOF (view, area, context); + break; + + case SL_SC_TOP: + if (stackingContextMgr) + stackingContextMgr->drawTop (view, area, context); + break; + + case SL_END: + break; + } + + DBG_OBJ_LEAVE (); +} + +void OOFAwareWidget::drawOOF (View *view, Rectangle *area, + DrawingContext *context) +{ + for (int i = 0; i < NUM_OOFM; i++) { + if(outOfFlowMgr[i]) + outOfFlowMgr[i]->draw (view, area, context); + } +} + +Widget *OOFAwareWidget::getWidgetAtPoint (int x, int y, + GettingWidgetAtPointContext *context) +{ + DBG_OBJ_ENTER ("events", 0, "getWidgetAtPoint", "%d, %d", x, y); + Widget *widgetAtPoint = NULL; + + if (inAllocation (x, y)) { + for (int level = SL_END - 1; widgetAtPoint == NULL && level > SL_START; + level--) + widgetAtPoint = getWidgetAtPointLevel (x, y, level, context); + } + + DBG_OBJ_MSGF ("events", 1, "=> %p", widgetAtPoint); + DBG_OBJ_LEAVE (); + return widgetAtPoint; +} + +Widget *OOFAwareWidget::getWidgetAtPointLevel (int x, int y, int level, + GettingWidgetAtPointContext + *context) +{ + DBG_OBJ_ENTER ("events", 0, "OOFAwareWidget::getWidgetAtPointLevel", + "%d, %d, %s", x, y, stackingLevelText (level)); + + Widget *widgetAtPoint = NULL; + + switch (level) { + case SL_BACKGROUND: + if (inAllocation (x, y)) + widgetAtPoint = this; + break; + + case SL_SC_BOTTOM: + if (stackingContextMgr) + widgetAtPoint = + stackingContextMgr->getBottomWidgetAtPoint (x, y, context); + break; + + case SL_IN_FLOW: + // Should be implemented in the sub class. + assertNotReached (); + break; + + case SL_OOF_REF: + // Should be implemented in the sub class (when references are hold). + break; + + case SL_OOF_CONT: + widgetAtPoint = getWidgetOOFAtPoint (x, y, context); + break; + + case SL_SC_TOP: + if (stackingContextMgr) + widgetAtPoint = + stackingContextMgr->getTopWidgetAtPoint (x, y, context); + break; + + default: + assertNotReached (); + } + + DBG_OBJ_MSGF ("events", 1, "=> %p", widgetAtPoint); + DBG_OBJ_LEAVE (); + return widgetAtPoint; +} + +Widget *OOFAwareWidget::getWidgetOOFAtPoint (int x, int y, + GettingWidgetAtPointContext + *context) +{ + Widget *widgetAtPoint = NULL; + + for (int i = NUM_OOFM -1; widgetAtPoint == NULL && i >= 0; i--) { + if(outOfFlowMgr[i]) + widgetAtPoint = outOfFlowMgr[i]->getWidgetAtPoint (x, y, context); + } + + return widgetAtPoint; +} + +void OOFAwareWidget::removeChild (Widget *child) +{ + // Sub classes should implement this method (and Textblock and + // Table do so), so this point is only reached from + // ~OOFAwareWidget, which removes widgets out of flow. + assert (isWidgetOOF (child)); +} + +bool OOFAwareWidget::mustBeWidenedToAvailWidth () +{ + // Only used for floats. + assertNotReached (); + return false; +} + +void OOFAwareWidget::borderChanged (int y, Widget *vloat) +{ + assertNotReached (); +} + +void OOFAwareWidget::widgetRefSizeChanged (int externalIndex) +{ + assertNotReached (); +} + +void OOFAwareWidget::clearPositionChanged () +{ + assertNotReached (); +} + +void OOFAwareWidget::oofSizeChanged (bool extremesChanged) +{ + DBG_OBJ_ENTER ("resize", 0, "oofSizeChanged", "%s", + extremesChanged ? "true" : "false"); + queueResize (-1, extremesChanged); + + // Extremes changes may become also relevant for the children. + if (extremesChanged) + containerSizeChanged (); + + DBG_OBJ_LEAVE (); +} + +int OOFAwareWidget::getLineBreakWidth () +{ + assertNotReached (); + return 0; +} + +bool OOFAwareWidget::isPossibleContainer (int oofmIndex) +{ + return oofmIndex != OOFM_FLOATS; +} + +bool OOFAwareWidget::isPossibleContainerParent (int oofmIndex) +{ + return oofmIndex != OOFM_FLOATS; +} + +bool OOFAwareWidget::adjustExtraSpaceWhenCorrectingRequisitionByOOF () +{ + return true; +} + +} // namespace oof + +} // namespace dw diff --git a/dw/oofawarewidget.hh b/dw/oofawarewidget.hh new file mode 100644 index 00000000..3946c72b --- /dev/null +++ b/dw/oofawarewidget.hh @@ -0,0 +1,264 @@ +#ifndef __DW_OOFAWAREWIDGET_HH__ +#define __DW_OOFAWAREWIDGET_HH__ + +#include "core.hh" +#include "outofflowmgr.hh" + +namespace dw { + +namespace oof { + +/** + * \brief Base class for widgets which can act as container and + * generator for widgets out of flow. + * + * (Perhaps it should be diffenciated between the two roles, container + * and generator, but this would make multiple inheritance necessary.) + * + * See \ref dw-out-of-flow for an overview. + * + * Requirements for sub classes (in most cases refer to dw::Textblock + * as a good example): + * + * - A sub class should at least take care to call these methods at the + * respective points: + * + * - dw::oof::OOFAwareWidget::correctRequisitionByOOF (from + * dw::core::Widget::getExtremesImpl) + * - dw::oof::OOFAwareWidget::correctExtremesByOOF (from + * dw::core::Widget::sizeRequestImpl) + * - dw::oof::OOFAwareWidget::sizeAllocateStart + * - dw::oof::OOFAwareWidget::sizeAllocateEnd (latter two from + * dw::core::Widget::sizeAllocateImpl) + * - dw::oof::OOFAwareWidget::containerSizeChangedForChildrenOOF + * (from dw::core::Widget::containerSizeChangedForChildren) + * - dw::oof::OOFAwareWidget::drawOOF (from dw::core::Widget::draw) + * - dw::oof::OOFAwareWidget::getWidgetOOFAtPoint (from + * dw::core::Widget::getWidgetAtPoint) + * + * - Implementations of dw::core::Widget::getAvailWidthOfChild and + * dw::core::Widget::getAvailHeightOfChild have to distinguish + * between widgets in flow and out of flow; general pattern: + * + * \code + * if (isWidgetOOF (child) && getWidgetOutOfFlowMgr(child) && + * getWidgetOutOfFlowMgr(child)->dealingWithSizeOfChild (child)) + * width = + * getWidgetOutOfFlowMgr(child)->getAvailWidthOfChild (child,forceValue); + * else { + * // ... specific implementation ... + * \endcode + * + * See also implementations of dw::Textblock and dw::Table. (Open + * issue: What about dw::core::Widget::correctRequisitionOfChild and + * dw::core::Widget::correctExtremesOfChild? Currently, all widgets + * are used the default implementation.) + * + * - Iterators have to consider widgets out of flow; + * dw::oof::OOFAwareWidget::OOFAwareWidgetIterator is recommended as + * base class. + * + * - dw::core::Widget::parentRef has to be set for widgets in flow; if + * not used further, a simple *makeParentRefInFlow(0)* is sufficient + * (as dw::Table::addCell does). Widgets which are only containers, + * but not generators, do not have to care about widgets out of + * flow in this regard. + * + * For both generators and containers of floats (which is only + * implemented by dw::Textblock) it gets a bit more complicated. + * + * \todo Currently, on the level of dw::oof::OOFAwareWidget, nothing + * is done about dw::core::Widget::markSizeChange and + * dw::core::Widget::markExtremesChange. This does not matter, though: + * dw::Textblock takes care of these, and dw::Table is only connected + * to subclasses of dw::oof::OOFPositionedMgr, which do care about + * these. However, this should be considered for completeness. + */ +class OOFAwareWidget: public core::Widget +{ +protected: + enum { OOFM_FLOATS, OOFM_ABSOLUTE, OOFM_RELATIVE, OOFM_FIXED, NUM_OOFM }; + static const char *OOFM_NAME[NUM_OOFM]; + enum { PARENT_REF_OOFM_BITS = 3, + PARENT_REF_OOFM_MASK = (1 << PARENT_REF_OOFM_BITS) - 1 }; + + class OOFAwareWidgetIterator: public core::Iterator + { + private: + enum { NUM_SECTIONS = NUM_OOFM + 1 }; + int sectionIndex; // 0 means in flow, otherwise OOFM index + 1 + int index; + + int numParts (int sectionIndex, int numContentsInFlow = -1); + void getPart (int sectionIndex, int index, core::Content *content); + + protected: + virtual int numContentsInFlow () = 0; + virtual void getContentInFlow (int index, core::Content *content) = 0; + + void setValues (int sectionIndex, int index); + inline void cloneValues (OOFAwareWidgetIterator *other) + { other->setValues (sectionIndex, index); } + + inline bool inFlow () { return sectionIndex == 0; } + inline int getInFlowIndex () { assert (inFlow ()); return index; } + void highlightOOF (int start, int end, core::HighlightLayer layer); + void unhighlightOOF (int direction, core::HighlightLayer layer); + void getAllocationOOF (int start, int end, core::Allocation *allocation); + + public: + OOFAwareWidgetIterator (OOFAwareWidget *widget, core::Content::Type mask, + bool atEnd, int numContentsInFlow); + + void intoStringBuffer(lout::misc::StringBuffer *sb); + int compareTo(lout::object::Comparable *other); + + bool next (); + bool prev (); + }; + + inline bool isParentRefOOF (int parentRef) + { return parentRef != -1 && (parentRef & PARENT_REF_OOFM_MASK); } + + inline int makeParentRefInFlow (int inFlowSubRef) + { return (inFlowSubRef << PARENT_REF_OOFM_BITS); } + inline int getParentRefInFlowSubRef (int parentRef) + { assert (!isParentRefOOF (parentRef)); + return parentRef >> PARENT_REF_OOFM_BITS; } + + inline int makeParentRefOOF (int oofmIndex, int oofmSubRef) + { return (oofmSubRef << PARENT_REF_OOFM_BITS) | (oofmIndex + 1); } + inline int getParentRefOOFSubRef (int parentRef) + { assert (isParentRefOOF (parentRef)); + return parentRef >> PARENT_REF_OOFM_BITS; } + inline int getParentRefOOFIndex (int parentRef) + { assert (isParentRefOOF (parentRef)); + return (parentRef & PARENT_REF_OOFM_MASK) - 1; } + inline oof::OutOfFlowMgr *getParentRefOutOfFlowMgr (int parentRef) + { return outOfFlowMgr[getParentRefOOFIndex (parentRef)]; } + + inline bool isWidgetOOF (Widget *widget) + { return isParentRefOOF (widget->parentRef); } + + inline int getWidgetInFlowSubRef (Widget *widget) + { return getParentRefInFlowSubRef (widget->parentRef); } + + inline int getWidgetOOFSubRef (Widget *widget) + { return getParentRefOOFSubRef (widget->parentRef); } + inline int getWidgetOOFIndex (Widget *widget) + { return getParentRefOOFIndex (widget->parentRef); } + inline oof::OutOfFlowMgr *getWidgetOutOfFlowMgr (Widget *widget) + { return getParentRefOutOfFlowMgr (widget->parentRef); } + + OOFAwareWidget *oofContainer[NUM_OOFM]; + OutOfFlowMgr *outOfFlowMgr[NUM_OOFM]; + core::Requisition requisitionWithoutOOF; + + inline OutOfFlowMgr *searchOutOfFlowMgr (int oofmIndex) + { return oofContainer[oofmIndex] ? + oofContainer[oofmIndex]->outOfFlowMgr[oofmIndex] : NULL; } + + static int getOOFMIndex (Widget *widget); + + void initOutOfFlowMgrs (); + void correctRequisitionByOOF (core::Requisition *requisition, + void (*splitHeightFun) (int, int*, int*)); + void correctExtremesByOOF (core::Extremes *extremes); + void sizeAllocateStart (core::Allocation *allocation); + void sizeAllocateEnd (); + void containerSizeChangedForChildrenOOF (); + + virtual void drawLevel (core::View *view, core::Rectangle *area, int level, + core::DrawingContext *context); + void drawOOF (core::View *view, core::Rectangle *area, + core::DrawingContext *context); + + Widget *getWidgetAtPoint (int x, int y, + core::GettingWidgetAtPointContext *context); + virtual Widget *getWidgetAtPointLevel (int x, int y, int level, + core::GettingWidgetAtPointContext + *context); + Widget *getWidgetOOFAtPoint (int x, int y, + core::GettingWidgetAtPointContext *context); + + static bool isOOFContainer (Widget *widget, int oofmIndex); + + void notifySetAsTopLevel(); + void notifySetParent(); + + void removeChild (Widget *child); + +public: + enum { + SL_START, SL_BACKGROUND, SL_SC_BOTTOM, SL_IN_FLOW, SL_OOF_REF, + SL_OOF_CONT, SL_SC_TOP, SL_END }; + + static int CLASS_ID; + + OOFAwareWidget (); + ~OOFAwareWidget (); + + static const char *stackingLevelText (int level); + + static inline bool testStyleFloat (core::style::Style *style) + { return style->vloat != core::style::FLOAT_NONE; } + + static inline bool testStyleAbsolutelyPositioned (core::style::Style *style) + { return style->position == core::style::POSITION_ABSOLUTE; } + static inline bool testStyleFixedlyPositioned (core::style::Style *style) + { return style->position == core::style::POSITION_FIXED; } + static inline bool testStyleRelativelyPositioned (core::style::Style *style) + { return style->position == core::style::POSITION_RELATIVE; } + + static inline bool testStylePositioned (core::style::Style *style) + { return testStyleAbsolutelyPositioned (style) || + testStyleRelativelyPositioned (style) || + testStyleFixedlyPositioned (style); } + + static inline bool testStyleOutOfFlow (core::style::Style *style) + { // Second part is equivalent to testStylePositioned(), but we still keep + // the two seperately. + return testStyleFloat (style) || testStyleAbsolutelyPositioned (style) + || testStyleRelativelyPositioned (style) + || testStyleFixedlyPositioned (style); } + + static inline bool testWidgetFloat (Widget *widget) + { return testStyleFloat (widget->getStyle ()); } + + static inline bool testWidgetAbsolutelyPositioned (Widget *widget) + { return testStyleAbsolutelyPositioned (widget->getStyle ()); } + static inline bool testWidgetFixedlyPositioned (Widget *widget) + { return testStyleFixedlyPositioned (widget->getStyle ()); } + static inline bool testWidgetRelativelyPositioned (Widget *widget) + { return testStyleRelativelyPositioned (widget->getStyle ()); } + + static inline bool testWidgetPositioned (Widget *widget) + { return testStylePositioned (widget->getStyle ()); } + + static inline bool testWidgetOutOfFlow (Widget *widget) + { return testStyleOutOfFlow (widget->getStyle ()); } + + inline core::Requisition *getRequisitionWithoutOOF () + { return &requisitionWithoutOOF; } + + bool doesWidgetOOFInterruptDrawing (Widget *widget); + + void draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context); + + virtual bool mustBeWidenedToAvailWidth (); + virtual void borderChanged (int y, core::Widget *vloat); + virtual void widgetRefSizeChanged (int externalIndex); + virtual void clearPositionChanged (); + virtual void oofSizeChanged (bool extremesChanged); + virtual int getLineBreakWidth (); // Should perhaps be renamed. + virtual bool isPossibleContainer (int oofmIndex); + virtual bool isPossibleContainerParent (int oofmIndex); + virtual bool adjustExtraSpaceWhenCorrectingRequisitionByOOF (); +}; + +} // namespace oof + +} // namespace dw + +#endif // __DW_OOFAWAREWIDGET_HH__ diff --git a/dw/oofawarewidget_iterator.cc b/dw/oofawarewidget_iterator.cc new file mode 100644 index 00000000..afafb5d5 --- /dev/null +++ b/dw/oofawarewidget_iterator.cc @@ -0,0 +1,261 @@ +/* + * Dillo Widget + * + * Copyright 2014 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "oofawarewidget.hh" +#include "ooffloatsmgr.hh" +#include "oofposabsmgr.hh" +#include "oofposfixedmgr.hh" + +using namespace dw; +using namespace dw::core; +using namespace lout::misc; +using namespace lout::object; + +namespace dw { + +namespace oof { + +// "numContentsInFlow" is passed here to avoid indirectly callin the (virtual) +// method numContentsInFlow() from the constructor. +OOFAwareWidget::OOFAwareWidgetIterator::OOFAwareWidgetIterator + (OOFAwareWidget *widget, Content::Type mask, bool atEnd, + int numContentsInFlow) : + Iterator (widget, mask, atEnd) +{ + if (atEnd) { + sectionIndex = NUM_SECTIONS - 1; + while (sectionIndex >= 0 && + numParts (sectionIndex, numContentsInFlow) == 0) + sectionIndex--; + index = numParts (sectionIndex, numContentsInFlow); + } else { + sectionIndex = 0; + index = -1; + } + + content.type = atEnd ? core::Content::END : core::Content::START; +} + +void OOFAwareWidget::OOFAwareWidgetIterator::setValues (int sectionIndex, + int index) +{ + this->sectionIndex = sectionIndex; + this->index = index; + + if (sectionIndex == 0 && index < 0) + content.type = core::Content::START; + else if (sectionIndex == NUM_SECTIONS - 1 && + index >= numParts (sectionIndex)) + content.type = core::Content::END; + else + getPart (sectionIndex, index, &content); +} + +int OOFAwareWidget::OOFAwareWidgetIterator::numParts (int sectionIndex, + int numContentsInFlow) +{ + DBG_OBJ_ENTER_O ("iterator", 0, getWidget(), "numParts", "%d, %d", + sectionIndex, numContentsInFlow); + + OOFAwareWidget *widget = (OOFAwareWidget*)getWidget(); + int result; + + if (sectionIndex == 0) + result = numContentsInFlow == -1 ? + this->numContentsInFlow () : numContentsInFlow; + else + result = widget->outOfFlowMgr[sectionIndex - 1] ? + widget->outOfFlowMgr[sectionIndex - 1]->getNumWidgets () : 0; + + DBG_OBJ_MSGF_O ("iterator", 1, getWidget(), "=> %d", result); + DBG_OBJ_LEAVE_O (getWidget()); + return result; +} + +void OOFAwareWidget::OOFAwareWidgetIterator::getPart (int sectionIndex, + int index, + Content *content) +{ + OOFAwareWidget *widget = (OOFAwareWidget*)getWidget(); + + if (sectionIndex == 0) + getContentInFlow (index, content); + else { + content->type = Content::WIDGET_OOF_CONT; + content->widget = + widget->outOfFlowMgr[sectionIndex - 1]->getWidget (index); + } +} + +void OOFAwareWidget::OOFAwareWidgetIterator::intoStringBuffer (StringBuffer *sb) +{ + Iterator::intoStringBuffer (sb); + sb->append (", sectionIndex = "); + sb->appendInt (sectionIndex); + sb->append (", index = "); + sb->appendInt (index); +} + +int OOFAwareWidget::OOFAwareWidgetIterator::compareTo (Comparable *other) +{ + OOFAwareWidgetIterator *otherTI = (OOFAwareWidgetIterator*)other; + + if (sectionIndex != otherTI->sectionIndex) + return sectionIndex - otherTI->sectionIndex; + else + return index - otherTI->index; +} + +bool OOFAwareWidget::OOFAwareWidgetIterator::next () +{ + DBG_OBJ_ENTER0_O ("iterator", 0, getWidget (), + "OOFAwareWidgetIterator/next"); + + DBG_IF_RTFL { + StringBuffer sb; + intoStringBuffer (&sb); + DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "initial value: %s", + sb.getChars ()); + } + + bool r; + + if (content.type == Content::END) + r = false; + else { + r = true; + bool cancel = false; + + do { + index++; + + if (index < numParts(sectionIndex)) + getPart (sectionIndex, index, &content); + else { + sectionIndex++; + while (sectionIndex < NUM_SECTIONS && numParts (sectionIndex) == 0) + sectionIndex++; + + if (sectionIndex == NUM_SECTIONS) { + content.type = Content::END; + r = false; + cancel = true; + } else { + index = 0; + getPart (sectionIndex, index, &content); + } + } + } while (!cancel && (content.type & getMask()) == 0); + } + + DBG_IF_RTFL { + StringBuffer sb; + intoStringBuffer (&sb); + DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "final value: %s", + sb.getChars ()); + DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "return value: %s", + r ? "true" : "false"); + } + + DBG_OBJ_LEAVE_O (getWidget ()); + return r; +} + +bool OOFAwareWidget::OOFAwareWidgetIterator::prev () +{ + DBG_OBJ_ENTER0_O ("iterator", 0, getWidget (), + "OOFAwareWidgetIterator/prev"); + + DBG_IF_RTFL { + StringBuffer sb; + intoStringBuffer (&sb); + DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "initial value: %s", + sb.getChars ()); + } + + bool r; + + if (content.type == Content::START) + r = false; + else { + r = true; + bool cancel = false; + + do { + index--; + + if (index >= 0) + getPart (sectionIndex, index, &content); + else { + sectionIndex--; + while (sectionIndex >= 0 && numParts (sectionIndex) == 0) + sectionIndex--; + + if (sectionIndex < 0) { + content.type = Content::START; + r = false; + cancel = true; + } else { + index = numParts (sectionIndex) - 1; + getPart (sectionIndex, index, &content); + } + } + } while (!cancel && (content.type & getMask()) == 0); + } + + DBG_IF_RTFL { + StringBuffer sb; + intoStringBuffer (&sb); + DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "final value: %s", + sb.getChars ()); + DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "return value: %s", + r ? "true" : "false"); + } + + DBG_OBJ_LEAVE_O (getWidget ()); + return r; +} + +void OOFAwareWidget::OOFAwareWidgetIterator::highlightOOF (int start, int end, + HighlightLayer layer) +{ + // TODO What about OOF widgets? +} + +void OOFAwareWidget::OOFAwareWidgetIterator::unhighlightOOF (int direction, + HighlightLayer + layer) +{ + // TODO What about OOF widgets? +} + +void OOFAwareWidget::OOFAwareWidgetIterator::getAllocationOOF (int start, + int end, + Allocation + *allocation) +{ + // TODO Consider start and end? + OOFAwareWidget *widget = (OOFAwareWidget*)getWidget(); + *allocation = *(widget->outOfFlowMgr[sectionIndex - 1] + ->getWidget(index)->getAllocation()); +} + +} // namespace oof + +} // namespace dw diff --git a/dw/ooffloatsmgr.cc b/dw/ooffloatsmgr.cc new file mode 100644 index 00000000..c2ffc580 --- /dev/null +++ b/dw/ooffloatsmgr.cc @@ -0,0 +1,2373 @@ +/* + * Dillo Widget + * + * Copyright 2013-2014 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "ooffloatsmgr.hh" +#include "oofawarewidget.hh" +#include "../lout/debug.hh" + +#include <limits.h> + +using namespace lout::object; +using namespace lout::container::typed; +using namespace lout::misc; +using namespace dw::core; +using namespace dw::core::style; + +namespace dw { + +namespace oof { + +OOFFloatsMgr::WidgetInfo::WidgetInfo (OOFFloatsMgr *oofm, Widget *widget) +{ + this->oofm = oofm; + this->widget = widget; + wasAllocated = false; + xCB = yCB = width = height = -1; +} + +void OOFFloatsMgr::WidgetInfo::update (bool wasAllocated, int xCB, int yCB, + int width, int height) +{ + DBG_OBJ_ENTER_O ("resize.oofm", 0, widget, "update", "%s, %d, %d, %d, %d", + wasAllocated ? "true" : "false", xCB, yCB, width, height); + + this->wasAllocated = wasAllocated; + this->xCB = xCB; + this->yCB = yCB; + this->width = width; + this->height = height; + + DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.xCB", xCB); + DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.yCB", yCB); + DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.width", width); + DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.height", height); + + DBG_OBJ_LEAVE_O (widget); +} + +// ---------------------------------------------------------------------- + +OOFFloatsMgr::Float::Float (OOFFloatsMgr *oofm, Widget *widget, + OOFAwareWidget *generatingBlock, int externalIndex) + : WidgetInfo (oofm, widget) +{ + this->generatingBlock = generatingBlock; + this->externalIndex = externalIndex; + + yReq = yReal = size.width = size.ascent = size.descent = 0; + dirty = sizeChangedSinceLastAllocation = true; + indexGBList = indexCBList = -1; + + // Sometimes a float with widget = NULL is created as a key; this + // is not interesting for RTFL. + if (widget) { + DBG_OBJ_SET_PTR_O (widget, "<Float>.generatingBlock", generatingBlock); + DBG_OBJ_SET_NUM_O (widget, "<Float>.externalIndex", externalIndex); + DBG_OBJ_SET_NUM_O (widget, "<Float>.yReq", yReq); + DBG_OBJ_SET_NUM_O (widget, "<Float>.yReal", yReal); + DBG_OBJ_SET_NUM_O (widget, "<Float>.size.width", size.width); + DBG_OBJ_SET_NUM_O (widget, "<Float>.size.ascent", size.ascent); + DBG_OBJ_SET_NUM_O (widget, "<Float>.size.descent", size.descent); + DBG_OBJ_SET_BOOL_O (widget, "<Float>.dirty", dirty); + DBG_OBJ_SET_BOOL_O (widget, "<Float>.sizeChangedSinceLastAllocation", + sizeChangedSinceLastAllocation); + } +} + +void OOFFloatsMgr::Float::updateAllocation () +{ + DBG_OBJ_ENTER0_O ("resize.oofm", 0, getWidget (), "updateAllocation"); + + update (isNowAllocated (), getNewXCB (), getNewYCB (), getNewWidth (), + getNewHeight ()); + + DBG_OBJ_LEAVE_O (getWidget ()); +} + +void OOFFloatsMgr::Float::intoStringBuffer(StringBuffer *sb) +{ + sb->append ("{ widget = "); + sb->appendPointer (getWidget ()); + + if (getWidget ()) { + sb->append (" ("); + sb->append (getWidget()->getClassName ()); + sb->append (")"); + } + + sb->append (", indexGBList = "); + sb->appendInt (indexGBList); + sb->append (", indexCBList = "); + sb->appendInt (indexCBList); + sb->append (", sideSpanningIndex = "); + sb->appendInt (sideSpanningIndex); + sb->append (", generatingBlock = "); + sb->appendPointer (generatingBlock); + sb->append (", yReq = "); + sb->appendInt (yReq); + sb->append (", yReal = "); + sb->appendInt (yReal); + sb->append (", size = { "); + sb->appendInt (size.width); + sb->append (" * "); + sb->appendInt (size.ascent); + sb->append (" + "); + sb->appendInt (size.descent); + sb->append (" }, dirty = "); + sb->appendBool (dirty); + sb->append (", sizeChangedSinceLastAllocation = "); + sb->appendBool (sizeChangedSinceLastAllocation); + sb->append (" }"); +} + +bool OOFFloatsMgr::Float::covers (OOFAwareWidget *textblock, int y, int h) +{ + DBG_OBJ_ENTER_O ("border", 0, getOOFFloatsMgr (), "covers", + "%p, %d, %d [vloat: %p]", + textblock, y, h, getWidget ()); + + bool b; + + if (textblock == generatingBlock) { + int reqyGB = y; + int flyGB = yReal; + getOOFFloatsMgr()->ensureFloatSize (this); + int flh = size.ascent + size.descent; + b = flyGB + flh > reqyGB && flyGB < reqyGB + h; + + DBG_OBJ_MSGF_O ("border", 1, getOOFFloatsMgr (), + "for generator: reqyGB = %d, flyGB = %d, " + "flh = %d + %d = %d => %s", + reqyGB, flyGB, size.ascent, size.descent, flh, + b ? "true" : "false"); + } else { + // (If the textblock were not allocated, the GB list would have + // been choosen instead of the CB list, and so this else-branch + // would not have been not executed.) + assert (getOOFFloatsMgr()->wasAllocated (textblock)); + + if (!getWidget()->wasAllocated ()) { + DBG_OBJ_MSG_O ("border", 1, getOOFFloatsMgr (), + "not generator (not allocated) => false"); + b = false; + } else { + Allocation *tba = getOOFFloatsMgr()->getAllocation(textblock), + *fla = getWidget()->getAllocation (); + int reqyCanv = tba->y + y; + int flyCanv = fla->y; + int flh = fla->ascent + fla->descent; + b = flyCanv + flh > reqyCanv && flyCanv < reqyCanv + h; + + DBG_OBJ_MSGF_O ("border", 1, getOOFFloatsMgr (), + "not generator (allocated): reqyCanv = %d + %d = %d, " + "flyCanv = %d, flh = %d + %d = %d => %s", + tba->y, y, reqyCanv, flyCanv, + fla->ascent, fla->descent, flh, b ? "true" : "false"); + } + } + + DBG_OBJ_LEAVE_O (getOOFFloatsMgr ()); + + return b; +} + +int OOFFloatsMgr::Float::ComparePosition::compare (Object *o1, Object *o2) +{ + Float *fl1 = (Float*)o1, *fl2 = (Float*)o2; + int r; + + DBG_OBJ_ENTER_O ("border", 1, oofm, + "ComparePosition/compare", "(#%d, #%d) [refTB = %p]", + fl1->getIndex (type), fl2->getIndex (type), refTB); + + if (refTB == fl1->generatingBlock && refTB == fl2->generatingBlock) { + DBG_OBJ_MSG_O ("border", 2, oofm, "refTB is generating both floats"); + r = fl1->yReal - fl2->yReal; + } else { + DBG_OBJ_MSG_O ("border", 2, oofm, "refTB is not generating both floats"); + DBG_OBJ_MSG_START_O (oofm); + + DBG_OBJ_MSGF_O ("border", 2, oofm, "generators are %p and %p", + fl1->generatingBlock, fl2->generatingBlock); + + // (i) Floats may not yet been allocated. Non-allocated floats + // do not have an effect yet, they are considered "at the end" + // of the list. + + // (ii) Float::widget is NULL for the key used for binary + // search. In this case, Float::yReal is used instead (which is + // set in SortedFloatsVector::find, too). The generator is the + // textblock, and should be allocated. (If not, the GB list + // would have been choosen instead of the CB list, and so this + // else-branch would not have been not executed.) + + bool a1 = fl1->getWidget () ? fl1->getWidget()->wasAllocated () : true; + bool a2 = fl2->getWidget () ? fl2->getWidget()->wasAllocated () : true; + + DBG_OBJ_MSGF_O ("border", 2, oofm, + "float 1 (%p) allocated: %s; float 2 (%p) allocated: %s", + fl1->getWidget (), a1 ? "yes" : "no", fl2->getWidget (), + a2 ? "yes" : "no"); + + if (a1 && a2) { + int fly1, fly2; + + if (fl1->getWidget()) { + fly1 = fl1->getWidget()->getAllocation()->y; + DBG_OBJ_MSGF_O ("border", 2, oofm, "fly1 = %d", fly1); + } else { + assert (oofm->wasAllocated (fl1->generatingBlock)); + fly1 = oofm->getAllocation(fl1->generatingBlock)->y + fl1->yReal; + DBG_OBJ_MSGF_O ("border", 2, oofm, "fly1 = %d + %d = %d", + oofm->getAllocation(fl1->generatingBlock)->y, + fl1->yReal, fly1); + } + + if (fl2->getWidget()) { + fly2 = fl2->getWidget()->getAllocation()->y; + DBG_OBJ_MSGF_O ("border", 2, oofm, "fly2 = %d", fly2); + } else { + assert (oofm->wasAllocated (fl2->generatingBlock)); + fly2 = oofm->getAllocation(fl2->generatingBlock)->y + fl2->yReal; + DBG_OBJ_MSGF_O ("border", 2, oofm, "fly2 = %d + %d = %d", + oofm->getAllocation(fl2->generatingBlock)->y, + fl2->yReal, fly2); + } + + r = fly1 - fly2; + + DBG_OBJ_MSGF_O ("border", 2, oofm, "r = %d - %d = %d", fly1, fly2, r); + } else if (a1 && !a2) + r = -1; + else if (!a1 && a2) + r = +1; + else // if (!a1 && !a2) + return 0; + + DBG_OBJ_MSG_END_O (oofm); + } + + DBG_OBJ_MSGF_O ("border", 1, oofm, "result: %d", r); + DBG_OBJ_LEAVE_O (oofm); + return r; +} + +int OOFFloatsMgr::Float::CompareSideSpanningIndex::compare (Object *o1, + Object *o2) +{ + return ((Float*)o1)->sideSpanningIndex - ((Float*)o2)->sideSpanningIndex; +} + +int OOFFloatsMgr::Float::CompareGBAndExtIndex::compare (Object *o1, Object *o2) +{ + Float *f1 = (Float*)o1, *f2 = (Float*)o2; + int r = -123; // Compiler happiness: GCC 4.7 does not handle this?; + + DBG_OBJ_ENTER_O ("border", 1, oofm, "CompareGBAndExtIndex::compare", + "#%d -> %p/%d, #%d -> %p/#%d", + f1->getIndex (type), f1->generatingBlock, f1->externalIndex, + f2->getIndex (type), f2->generatingBlock, + f2->externalIndex); + + if (f1->generatingBlock == f2->generatingBlock) { + r = f1->externalIndex - f2->externalIndex; + DBG_OBJ_MSGF_O ("border", 2, oofm, + "(a) generating blocks equal => %d - %d = %d", + f1->externalIndex, f2->externalIndex, r); + } else { + TBInfo *t1 = oofm->getOOFAwareWidget (f1->generatingBlock), + *t2 = oofm->getOOFAwareWidget (f2->generatingBlock); + bool rdef = false; + + for (TBInfo *t = t1; t != NULL; t = t->parent) + if (t->parent == t2) { + rdef = true; + r = t->parentExtIndex - f2->externalIndex; + DBG_OBJ_MSGF_O ("border", 2, oofm, + "(b) %p is an achestor of %p; direct child is " + "%p (%d) => %d - %d = %d\n", + t2->getOOFAwareWidget (), t1->getOOFAwareWidget (), + t->getOOFAwareWidget (), t->parentExtIndex, + t->parentExtIndex, f2->externalIndex, r); + } + + for (TBInfo *t = t2; !rdef && t != NULL; t = t->parent) + if (t->parent == t1) { + r = f1->externalIndex - t->parentExtIndex; + rdef = true; + DBG_OBJ_MSGF_O ("border", 2, oofm, + "(c) %p is an achestor of %p; direct child is %p " + "(%d) => %d - %d = %d\n", + t1->getOOFAwareWidget (), t2->getOOFAwareWidget (), + t->getOOFAwareWidget (), t->parentExtIndex, + f1->externalIndex, t->parentExtIndex, r); + } + + if (!rdef) { + r = t1->index - t2->index; + DBG_OBJ_MSGF_O ("border", 2, oofm, "(d) other => %d - %d = %d", + t1->index, t2->index, r); + } + } + + DBG_OBJ_MSGF_O ("border", 2, oofm, "result: %d", r); + DBG_OBJ_LEAVE_O (oofm); + return r; +} + +int OOFFloatsMgr::SortedFloatsVector::findFloatIndex (OOFAwareWidget *lastGB, + int lastExtIndex) +{ + DBG_OBJ_ENTER_O ("border", 0, oofm, "findFloatIndex", "%p, %d", + lastGB, lastExtIndex); + + Float key (oofm, NULL, lastGB, lastExtIndex); + key.setIndex (type, -1); // for debugging + Float::CompareGBAndExtIndex comparator (oofm, type); + int i = bsearch (&key, false, &comparator); + + // At position i is the next larger element, so element i should + // not included, but i - 1 returned; except if the exact element is + // found: then include it and so return i. + int r; + if (i == size()) + r = i - 1; + else { + Float *f = get (i); + if (comparator.compare (f, &key) == 0) + r = i; + else + r = i - 1; + } + + //printf ("[%p] findFloatIndex (%p, %d) => i = %d, r = %d (size = %d); " + // "in %s list %p on the %s side\n", + // oofm->container, lastGB, lastExtIndex, i, r, size (), + // type == GB ? "GB" : "CB", this, side == LEFT ? "left" : "right"); + + //for (int i = 0; i < size (); i++) { + // Float *f = get(i); + // TBInfo *t = oofm->getOOFAwareWidget(f->generatingBlock); + // printf (" %d: (%p [%d, %p], %d)\n", i, f->generatingBlock, + // t->index, t->parent ? t->parent->textblock : NULL, + // get(i)->externalIndex); + //} + + DBG_OBJ_MSGF_O ("border", 1, oofm, "=> r = %d", r); + DBG_OBJ_LEAVE_O (oofm); + return r; +} + +int OOFFloatsMgr::SortedFloatsVector::find (OOFAwareWidget *textblock, int y, + int start, int end) +{ + DBG_OBJ_ENTER_O ("border", 0, oofm, "find", "%p, %d, %d, %d", + textblock, y, start, end); + + Float key (oofm, NULL, NULL, 0); + key.generatingBlock = textblock; + key.yReal = y; + key.setIndex (type, -1); // for debugging + Float::ComparePosition comparator (oofm, textblock, type); + int result = bsearch (&key, false, start, end, &comparator); + + DBG_OBJ_MSGF_O ("border", 1, oofm, "=> result = %d", result); + DBG_OBJ_LEAVE_O (oofm); + return result; +} + +int OOFFloatsMgr::SortedFloatsVector::findFirst (OOFAwareWidget *textblock, + int y, int h, + OOFAwareWidget *lastGB, + int lastExtIndex, + int *lastReturn) +{ + DBG_OBJ_ENTER_O ("border", 0, oofm, "findFirst", "%p, %d, %d, %p, %d", + textblock, y, h, lastGB, lastExtIndex); + + DBG_IF_RTFL { + DBG_OBJ_MSG_O ("border", 2, oofm, "searching in list:"); + DBG_OBJ_MSG_START_O (oofm); + + for (int i = 0; i < size(); i++) { + DBG_OBJ_MSGF_O ("border", 2, oofm, + "%d: (%p, i = %d/%d, y = %d/%d, s = (%d * (%d + %d)), " + "%s, %s, ext = %d, GB = %p); widget at (%d, %d)", + i, get(i)->getWidget (), get(i)->getIndex (type), + get(i)->sideSpanningIndex, get(i)->yReq, get(i)->yReal, + get(i)->size.width, get(i)->size.ascent, + get(i)->size.descent, + get(i)->dirty ? "dirty" : "clean", + get(i)->sizeChangedSinceLastAllocation ? "scsla" + : "sNcsla", + get(i)->externalIndex, get(i)->generatingBlock, + get(i)->getWidget()->getAllocation()->x, + get(i)->getWidget()->getAllocation()->y); + } + + DBG_OBJ_MSG_END_O (oofm); + } + + int last = findFloatIndex (lastGB, lastExtIndex); + DBG_OBJ_MSGF_O ("border", 1, oofm, "last = %d", last); + assert (last < size()); + + // If the caller wants to reuse this value: + if (lastReturn) + *lastReturn = last; + + int i = find (textblock, y, 0, last), result; + DBG_OBJ_MSGF_O ("border", 1, oofm, "i = %d", i); + + // Note: The smallest value of "i" is 0, which means that "y" is before or + // equal to the first float. The largest value is "last + 1", which means + // that "y" is after the last float. In both cases, the first or last, + // respectively, float is a candidate. Generally, both floats, before and + // at the search position, are candidates. + + if (i > 0 && get(i - 1)->covers (textblock, y, h)) + result = i - 1; + else if (i <= last && get(i)->covers (textblock, y, h)) + result = i; + else + result = -1; + + DBG_OBJ_MSGF_O ("border", 1, oofm, "=> result = %d", result); + DBG_OBJ_LEAVE_O (oofm); + return result; +} + +int OOFFloatsMgr::SortedFloatsVector::findLastBeforeSideSpanningIndex + (int sideSpanningIndex) +{ + OOFFloatsMgr::Float::CompareSideSpanningIndex comparator; + Float key (NULL, NULL, NULL, 0); + key.sideSpanningIndex = sideSpanningIndex; + return bsearch (&key, false, &comparator) - 1; +} + +void OOFFloatsMgr::SortedFloatsVector::put (Float *vloat) +{ + lout::container::typed::Vector<Float>::put (vloat); + vloat->setIndex (type, size() - 1); +} + +OOFFloatsMgr::TBInfo::TBInfo (OOFFloatsMgr *oofm, OOFAwareWidget *textblock, + TBInfo *parent, int parentExtIndex) : + WidgetInfo (oofm, textblock) +{ + this->parent = parent; + this->parentExtIndex = parentExtIndex; + + leftFloatsGB = new SortedFloatsVector (oofm, LEFT, GB); + rightFloatsGB = new SortedFloatsVector (oofm, RIGHT, GB); + + wasAllocated = getWidget()->wasAllocated (); + allocation = *(getWidget()->getAllocation ()); + clearPosition = 0; +} + +OOFFloatsMgr::TBInfo::~TBInfo () +{ + delete leftFloatsGB; + delete rightFloatsGB; +} + +void OOFFloatsMgr::TBInfo::updateAllocation () +{ + DBG_OBJ_ENTER0_O ("resize.oofm", 0, getWidget (), "updateAllocation"); + + update (isNowAllocated (), getNewXCB (), getNewYCB (), getNewWidth (), + getNewHeight ()); + + DBG_OBJ_LEAVE_O (getWidget ()); +} + +OOFFloatsMgr::OOFFloatsMgr (OOFAwareWidget *container) +{ + DBG_OBJ_CREATE ("dw::OOFFloatsMgr"); + + this->container = (OOFAwareWidget*)container; + + leftFloatsCB = new SortedFloatsVector (this, LEFT, CB); + rightFloatsCB = new SortedFloatsVector (this, RIGHT, CB); + + DBG_OBJ_SET_NUM ("leftFloatsCB.size", leftFloatsCB->size()); + DBG_OBJ_SET_NUM ("rightFloatsCB.size", rightFloatsCB->size()); + + leftFloatsAll = new Vector<Float> (1, true); + rightFloatsAll = new Vector<Float> (1, true); + + DBG_OBJ_SET_NUM ("leftFloatsAll.size", leftFloatsAll->size()); + DBG_OBJ_SET_NUM ("rightFloatsAll.size", rightFloatsAll->size()); + + floatsByWidget = new HashTable <TypedPointer <Widget>, Float> (true, false); + + tbInfos = new Vector<TBInfo> (1, false); + tbInfosByOOFAwareWidget = + new HashTable <TypedPointer <OOFAwareWidget>, TBInfo> (true, true); + + leftFloatsMark = rightFloatsMark = 0; + lastLeftTBIndex = lastRightTBIndex = 0; + + containerWasAllocated = container->wasAllocated (); + containerAllocation = *(container->getAllocation()); + + addWidgetInFlow (this->container, NULL, 0); +} + +OOFFloatsMgr::~OOFFloatsMgr () +{ + //printf ("OOFFloatsMgr::~OOFFloatsMgr\n"); + + delete leftFloatsCB; + delete rightFloatsCB; + + // Order is important: tbInfosByOOFAwareWidget is owner of the instances + // of TBInfo.tbInfosByOOFAwareWidget + delete tbInfos; + delete tbInfosByOOFAwareWidget; + + delete floatsByWidget; + + // Order is important, since the instances of Float are owned by + // leftFloatsAll and rightFloatsAll, so these should be deleted + // last. + delete leftFloatsAll; + delete rightFloatsAll; + + DBG_OBJ_DELETE (); +} + +void OOFFloatsMgr::sizeAllocateStart (OOFAwareWidget *caller, + Allocation *allocation) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateStart", + "%p, (%d, %d, %d * (%d + %d))", + caller, allocation->x, allocation->y, allocation->width, + allocation->ascent, allocation->descent); + + // Some callers are not registered, especially tables. (Where the + // floats manager is actually empty?) + TBInfo *oofAWInfo = getOOFAwareWidgetWhenRegistered (caller); + if (oofAWInfo) { + oofAWInfo->allocation = *allocation; + oofAWInfo->wasAllocated = true; + } + + if (caller == container) { + // In the size allocation process, the *first* OOFM method + // called is sizeAllocateStart, with the containing block as an + // argument. So this is the correct point to initialize size + // allocation. + + containerWasAllocated = true; + containerAllocation = *allocation; + + // Move floats from GB lists to the one CB list. + moveFromGBToCB (LEFT); + moveFromGBToCB (RIGHT); + + // These attributes are used to keep track which floats have + // been allocated (referring to leftFloatsCB and rightFloatsCB). + lastAllocatedLeftFloat = lastAllocatedRightFloat = -1; + } + + DBG_OBJ_LEAVE (); +} + +void OOFFloatsMgr::sizeAllocateEnd (OOFAwareWidget *caller) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateEnd", "%p", caller); + + if (isOOFAwareWidgetRegistered (caller)) { + if (caller != container) { + // Allocate all floats "before" this textblock. + sizeAllocateFloats (LEFT, leftFloatsCB->findFloatIndex (caller, -1)); + sizeAllocateFloats (RIGHT, rightFloatsCB->findFloatIndex (caller, -1)); + } + + // The checks below do not cover "clear position" in all cases, + // so this is done here separately. This position is stored in + // TBInfo and calculated at this points; changes will be noticed + // to the textblock. + TBInfo *tbInfo = getOOFAwareWidget (caller); + int newClearPosition = calcClearPosition (caller); + if (newClearPosition != tbInfo->clearPosition) { + tbInfo->clearPosition = newClearPosition; + caller->clearPositionChanged (); + } + + if (caller == container) { + // In the size allocation process, the *last* OOFM method called + // is sizeAllocateEnd, with the containing block as an + // argument. So this is the correct point to finish size + // allocation. + + // Allocate all remaining floats. + sizeAllocateFloats (LEFT, leftFloatsCB->size () - 1); + sizeAllocateFloats (RIGHT, rightFloatsCB->size () - 1); + + // Check changes of both textblocks and floats allocation. (All + // is checked by hasRelationChanged (...).) + for (lout::container::typed::Iterator<TypedPointer <OOFAwareWidget> > + it = tbInfosByOOFAwareWidget->iterator (); + it.hasNext (); ) { + TypedPointer <OOFAwareWidget> *key = it.getNext (); + TBInfo *tbInfo = tbInfosByOOFAwareWidget->get (key); + OOFAwareWidget *tb = key->getTypedValue(); + + int minFloatPos; + Widget *minFloat; + if (hasRelationChanged (tbInfo, &minFloatPos, &minFloat)) + tb->borderChanged (minFloatPos, minFloat); + } + + checkAllocatedFloatCollisions (LEFT); + checkAllocatedFloatCollisions (RIGHT); + + // Store some information for later use. + for (lout::container::typed::Iterator<TypedPointer <OOFAwareWidget> > + it = tbInfosByOOFAwareWidget->iterator (); + it.hasNext (); ) { + TypedPointer <OOFAwareWidget> *key = it.getNext (); + TBInfo *tbInfo = tbInfosByOOFAwareWidget->get (key); + OOFAwareWidget *tb = key->getTypedValue(); + + tbInfo->updateAllocation (); + tbInfo->lineBreakWidth = tb->getLineBreakWidth (); + } + + // There are cases where some allocated floats exceed the CB size. + bool sizeChanged = doFloatsExceedCB (LEFT) || doFloatsExceedCB (RIGHT); + + // Similar for extremes. + bool extremesChanged = + haveExtremesChanged (LEFT) || haveExtremesChanged (RIGHT); + + for (int i = 0; i < leftFloatsCB->size(); i++) + leftFloatsCB->get(i)->updateAllocation (); + + for (int i = 0; i < rightFloatsCB->size(); i++) + rightFloatsCB->get(i)->updateAllocation (); + + if (sizeChanged || extremesChanged) + container->oofSizeChanged (extremesChanged); + } + } + + DBG_OBJ_LEAVE (); +} + +void OOFFloatsMgr::containerSizeChangedForChildren () +{ + DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren"); + + for (int i = 0; i < leftFloatsAll->size (); i++) + leftFloatsAll->get(i)->getWidget()->containerSizeChanged (); + for (int i = 0; i < rightFloatsAll->size (); i++) + rightFloatsAll->get(i)->getWidget()->containerSizeChanged (); + + DBG_OBJ_LEAVE (); +} + +bool OOFFloatsMgr::hasRelationChanged (TBInfo *tbInfo, int *minFloatPos, + Widget **minFloat) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "hasRelationChanged", + "<i>widget:</i> %p, ...", tbInfo->getWidget ()); + + int leftMinPos, rightMinPos; + Widget *leftMinFloat, *rightMinFloat; + bool c1 = + hasRelationChanged (tbInfo, LEFT, &leftMinPos, &leftMinFloat); + bool c2 = + hasRelationChanged (tbInfo, RIGHT, &rightMinPos, &rightMinFloat); + if (c1 || c2) { + if (!c1) { + *minFloatPos = rightMinPos; + *minFloat = rightMinFloat; + } else if (!c2) { + *minFloatPos = leftMinPos; + *minFloat = leftMinFloat; + } else { + if (leftMinPos < rightMinPos) { + *minFloatPos = leftMinPos; + *minFloat = leftMinFloat; + } else{ + *minFloatPos = rightMinPos; + *minFloat = rightMinFloat; + } + } + } + + if (c1 || c2) + DBG_OBJ_MSGF ("resize.oofm", 1, + "has changed: minFloatPos = %d, minFloat = %p", + *minFloatPos, *minFloat); + else + DBG_OBJ_MSG ("resize.oofm", 1, "has not changed"); + + DBG_OBJ_LEAVE (); + return c1 || c2; +} + +bool OOFFloatsMgr::hasRelationChanged (TBInfo *tbInfo, Side side, + int *minFloatPos, Widget **minFloat) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "hasRelationChanged", + "<i>widget:</i> %p, %s, ...", + tbInfo->getWidget (), side == LEFT ? "LEFT" : "RIGHT"); + + SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; + bool changed = false; + + for (int i = 0; i < list->size(); i++) { + // TODO binary search? + Float *vloat = list->get(i); + int floatPos; + + if (tbInfo->getOOFAwareWidget () == vloat->generatingBlock) + DBG_OBJ_MSGF ("resize.oofm", 1, + "not checking (generating!) textblock %p against float " + "%p", tbInfo->getWidget (), vloat->getWidget ()); + else { + Allocation *gba = getAllocation (vloat->generatingBlock); + + int newFlx = calcFloatX (vloat, side, + gba->x - containerAllocation.x, + getGBWidthForAllocation (vloat)); + int newFly = vloat->generatingBlock->getAllocation()->y + - containerAllocation.y + vloat->yReal; + + DBG_OBJ_MSGF ("resize.oofm", 1, + "checking textblock %p against float %p", + tbInfo->getWidget (), vloat->getWidget ()); + DBG_OBJ_MSG_START (); + + if (hasRelationChanged (tbInfo->wasThenAllocated (), + tbInfo->getOldXCB (), tbInfo->getOldYCB (), + tbInfo->getNewWidth (), + tbInfo->getNewHeight (), + tbInfo->getNewXCB (), tbInfo->getNewYCB (), + tbInfo->getNewWidth (), + tbInfo->getNewHeight (), + vloat->wasThenAllocated (), + // When not allocated before, these values + // are undefined, but this does not matter, + // since they are neither used. + vloat->getOldXCB (), vloat->getOldYCB (), + vloat->getOldWidth (), vloat->getOldHeight (), + newFlx, newFly, vloat->size.width, + vloat->size.ascent + vloat->size.descent, + side, &floatPos)) { + if (!changed || floatPos < *minFloatPos) { + *minFloatPos = floatPos; + *minFloat = vloat->getWidget (); + } + changed = true; + } else + DBG_OBJ_MSG ("resize.oofm", 0, "No."); + + DBG_OBJ_MSG_END (); + } + + // All floarts are searched, to find the minimum. TODO: Are + // floats sorted, so this can be shortened? (The first is the + // minimum?) + } + + if (changed) + DBG_OBJ_MSGF ("resize.oofm", 1, + "has changed: minFloatPos = %d, minFloat = %p", + *minFloatPos, *minFloat); + else + DBG_OBJ_MSG ("resize.oofm", 1, "has not changed"); + + DBG_OBJ_LEAVE (); + return changed; +} + +/** + * \brief ... + * + * All coordinates are given relative to the CB. *floatPos is relative + * to the TB, and may be negative. + */ +bool OOFFloatsMgr::hasRelationChanged (bool oldTBAlloc, + int oldTBx, int oldTBy, int oldTBw, + int oldTBh, int newTBx, int newTBy, + int newTBw, int newTBh, + bool oldFlAlloc, + int oldFlx, int oldFly, int oldFlw, + int oldFlh, int newFlx, int newFly, + int newFlw, int newFlh, + Side side, int *floatPos) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "hasRelationChanged", + "<i>see below</i>, %s, ...", side == LEFT ? "LEFT" : "RIGHT"); + + if (oldTBAlloc) + DBG_OBJ_MSGF ("resize.oofm", 1, "old TB: %d, %d; %d * %d", + oldTBx, oldTBy, oldTBw, oldTBh); + else + DBG_OBJ_MSG ("resize.oofm", 1, "old TB: undefined"); + DBG_OBJ_MSGF ("resize.oofm", 1, "new TB: %d, %d; %d * %d", + newTBx, newTBy, newTBw, newTBh); + + if (oldFlAlloc) + DBG_OBJ_MSGF ("resize.oofm", 1, "old Fl: %d, %d; %d * %d", + oldFlx, oldFly, oldFlw, oldFlh); + else + DBG_OBJ_MSG ("resize.oofm", 1, "old Fl: undefined"); + DBG_OBJ_MSGF ("resize.oofm", 1, "new Fl: %d, %d; %d * %d", + newFlx, newFly, newFlw, newFlh); + + bool result; + if (oldTBAlloc && oldFlAlloc) { + bool oldCov = oldFly + oldFlh > oldTBy && oldFly < oldTBy + oldTBh; + bool newCov = newFly + newFlh > newTBy && newFly < newTBy + newTBh; + + DBG_OBJ_MSGF ("resize.oofm", 1, "covered? then: %s, now: %s.", + oldCov ? "yes" : "no", newCov ? "yes" : "no"); + DBG_OBJ_MSG_START (); + + if (oldCov && newCov) { + int yOld = oldFly - oldTBy, yNew = newFly - newTBy; + if (yOld == yNew) { + DBG_OBJ_MSGF ("resize.oofm", 2, + "old (%d - %d) and new (%d - %d) position equal: %d", + oldFly, oldTBy, newFly, newTBy, yOld); + + // Float position has not changed, but perhaps the amout + // how far the float reaches into the TB. (TODO: + // Generally, not only here, it could be tested whether + // the float reaches into the TB at all.) + int wOld, wNew; + if (side == LEFT) { + wOld = oldFlx + oldFlw - oldTBx; + wNew = newFlx + newFlw - newTBx; + } else { + wOld = oldTBx + oldTBw - oldFlx; + wNew = newTBx + newTBw - newFlx; + } + + DBG_OBJ_MSGF ("resize.oofm", 2, "wOld = %d, wNew = %d\n", + wOld, wNew); + + if (wOld == wNew) { + if (oldFlh == newFlh) + result = false; + else { + // Only heights of floats changed. Relevant only + // from bottoms of float. + *floatPos = min (yOld + oldFlh, yNew + newFlh); + result = true; + } + } else { + *floatPos = yOld; + result = true; + } + } else { + DBG_OBJ_MSGF ("resize.oofm", 2, + "old (%d - %d = %d) and new (%d - %d = %d) position " + "different", + oldFly, oldTBy, yOld, newFly, newTBy, yNew); + *floatPos = min (yOld, yNew); + result = true; + } + } else if (oldCov) { + *floatPos = oldFly - oldTBy; + result = true; + DBG_OBJ_MSGF ("resize.oofm", 2, + "returning old position: %d - %d = %d", oldFly, oldTBy, + *floatPos); + } else if (newCov) { + *floatPos = newFly - newTBy; + result = true; + DBG_OBJ_MSGF ("resize.oofm", 2, + "returning new position: %d - %d = %d", newFly, newTBy, + *floatPos); + } else + result = false; + + DBG_OBJ_MSG_END (); + } else { + // Not allocated before: ignore all old values, only check whether + // TB is covered by Float. + if (newFly + newFlh > newTBy && newFly < newTBy + newTBh) { + *floatPos = newFly - newTBy; + result = true; + } else + result = false; + } + + if (result) + DBG_OBJ_MSGF ("resize.oofm", 1, "has changed: floatPos = %d", + *floatPos); + else + DBG_OBJ_MSG ("resize.oofm", 1, "has not changed"); + + DBG_OBJ_LEAVE (); + + return result; +} + +void OOFFloatsMgr::checkAllocatedFloatCollisions (Side side) +{ + // In some cases, the collision detection in tellPosition() is + // based on the wrong allocations. Here (just after all Floats have + // been allocated), we correct this. + + // TODO In some cases this approach is rather slow, causing a too + // long queueResize() cascade. + + DBG_OBJ_ENTER ("resize.oofm", 0, "checkAllocatedFloatCollisions", "%s", + side == LEFT ? "LEFT" : "RIGHT"); + + SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; + SortedFloatsVector *oppList = side == LEFT ? rightFloatsCB : leftFloatsCB; + + // While iterating through the list of floats to be checked, we + // iterate equally through the list of the opposite floats, using + // this index: + int oppIndex = 0; + + for (int index = 0; index < list->size (); index++) { + Float *vloat = list->get(index); + bool needsChange = false; + int yRealNew = INT_MAX; + + // Same side. + if (index >= 1) { + Float *other = list->get(index - 1); + DBG_OBJ_MSGF ("resize.oofm", 1, + "same side: checking %p (#%d, GB: %p) against " + "%p (#%d, GB: %p)", + vloat->getWidget (), index, vloat->generatingBlock, + other->getWidget (), index - 1, other->generatingBlock); + + if (vloat->generatingBlock != other->generatingBlock) { + int yRealNewSame; + if (collidesV (vloat, other, CB, &yRealNewSame, true)) { + DBG_OBJ_MSGF ("resize.oofm", 1, + "=> collides, new yReal = %d (old: %d)", + yRealNewSame, vloat->yReal); + if (vloat->yReal != yRealNewSame) { + needsChange = true; + yRealNew = min (yRealNew, yRealNewSame); + } + } else + DBG_OBJ_MSG ("resize.oofm", 1, "=> no collision"); + } + } + + if (oppList->size () > 0) { + // Other side. Iterate to next float on the other side, + // before this float. + while (oppIndex + 1 < oppList->size () && + oppList->get(oppIndex + 1)->sideSpanningIndex + < vloat->sideSpanningIndex) + oppIndex++; + + if (oppList->get(oppIndex)->sideSpanningIndex + < vloat->sideSpanningIndex) { + int oppIndexTmp = oppIndex, yRealNewOpp; + + // Aproach is similar to tellPosition(); see comments + // there. Again, loop as long as the vertical dimensions test + // is positive (and, of course, there are floats), ... + for (bool foundColl = false; + !foundColl && oppIndexTmp >= 0 && + collidesV (vloat, oppList->get (oppIndexTmp), CB, + &yRealNewOpp, true); + oppIndexTmp--) { + DBG_OBJ_MSGF ("resize.oofm", 1, + "opposite side (after collision (v) test): " + "checking %p (#%d/%d, GB: %p) against " + "%p (#%d/%d, GB: %p)", + vloat->getWidget (), index, + vloat->sideSpanningIndex, + vloat->generatingBlock, + oppList->get(oppIndexTmp)->getWidget (), + oppList->get(oppIndexTmp)->getIndex (CB), + oppList->get(oppIndexTmp)->sideSpanningIndex, + oppList->get(oppIndexTmp)->generatingBlock); + + // ... but stop the loop as soon as the horizontal dimensions + // test is positive. + if (collidesH (vloat, oppList->get (oppIndexTmp), CB)) { + DBG_OBJ_MSGF ("resize.oofm", 1, + "=> collides (h), new yReal = %d (old: %d)", + yRealNewOpp, vloat->yReal); + foundColl = true; + if (vloat->yReal != yRealNewOpp) { + needsChange = true; + yRealNew = min (yRealNew, yRealNewOpp); + } + } else + DBG_OBJ_MSG ("resize.oofm", 1, "=> no collision (h)"); + } + } + } + + if (needsChange) + vloat->generatingBlock->borderChanged (min (vloat->yReal, yRealNew), + vloat->getWidget ()); + } + + DBG_OBJ_LEAVE (); +} + +bool OOFFloatsMgr::doFloatsExceedCB (Side side) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "doFloatsExceedCB", "%s", + side == LEFT ? "LEFT" : "RIGHT"); + + // This method is called to determine whether the *requisition* of + // the CB must be recalculated. So, we check the float allocations + // against the *requisition* of the CB, which may (e. g. within + // tables) differ from the new allocation. (Generally, a widget may + // allocated at a different size.) + core::Requisition cbReq; + container->sizeRequest (&cbReq); + + SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; + bool exceeds = false; + + DBG_OBJ_MSG_START (); + + for (int i = 0; i < list->size () && !exceeds; i++) { + Float *vloat = list->get (i); + if (vloat->getWidget()->wasAllocated ()) { + Allocation *fla = vloat->getWidget()->getAllocation (); + DBG_OBJ_MSGF ("resize.oofm", 2, + "Does FlA = (%d, %d, %d * %d) exceed CB req+alloc = " + "(%d, %d, %d * %d)?", + fla->x, fla->y, fla->width, fla->ascent + fla->descent, + containerAllocation.x, containerAllocation.y, + cbReq.width, cbReq.ascent + cbReq.descent); + if (fla->x + fla->width > containerAllocation.x + cbReq.width || + fla->y + fla->ascent + fla->descent + > containerAllocation.y + cbReq.ascent + cbReq.descent) { + exceeds = true; + DBG_OBJ_MSG ("resize.oofm", 2, "Yes."); + } else + DBG_OBJ_MSG ("resize.oofm", 2, "No."); + } + } + + DBG_OBJ_MSG_END (); + + DBG_OBJ_MSGF ("resize.oofm", 1, "=> %s", exceeds ? "true" : "false"); + DBG_OBJ_LEAVE (); + + return exceeds; +} + +bool OOFFloatsMgr::haveExtremesChanged (Side side) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "haveExtremesChanged", "%s", + side == LEFT ? "LEFT" : "RIGHT"); + + // This is quite different from doFloatsExceedCB, since there is no + // counterpart to getExtremes, as sizeAllocate is a counterpart to + // sizeRequest. So we have to determine whether the allocation has + // changed the extremes, which is done by examining the part of the + // allocation which is part of the extremes calculation (see + // getFloatsExtremes). Changes of the extremes are handled by the + // normal queueResize mechanism. + + // (This may refer to the old implementation of doFloatsExceedCB, + // which checked the container *allocation*, not the *requisition*. + // + // TODO: (i) Correct the comment. (ii) Is this implementation still + // correct, considering the reasoning behind the change of + // doFloatsExceedCB? + // + // See also OOFPositionedMgr::haveExtremesChanged.) + + SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; + bool changed = false; + + for (int i = 0; i < list->size () && !changed; i++) { + Float *vloat = list->get (i); + // When the GB is the CB, an allocation change does not play a + // role here. + if (vloat->generatingBlock != container) { + if (!vloat->wasThenAllocated () && vloat->isNowAllocated ()) + changed = true; + else { + // This method is called within sizeAllocateEnd, where + // containinBlock->getAllocation() (old value) and + // containinBlockAllocation (new value) are different. + + Allocation *oldCBA = container->getAllocation (); + Allocation *newCBA = &containerAllocation; + + // Compare also to getFloatsExtremes. The box difference + // of the GB (from style) has not changed in this context, + // so it is ignored. + + int oldDiffLeft = vloat->getOldXCB (); + int newDiffLeft = vloat->getNewXCB (); + int oldDiffRight = + oldCBA->width - (vloat->getOldXCB () + vloat->getOldWidth ()); + int newDiffRight = + newCBA->width - (vloat->getNewXCB () + vloat->getNewWidth ()); + + if (// regarding minimum + (side == LEFT && oldDiffLeft != newDiffLeft) || + (side == RIGHT && oldDiffRight != newDiffRight) || + // regarding maximum + oldDiffLeft + oldDiffRight != newDiffLeft + newDiffRight) + changed = true; + } + } + } + + DBG_OBJ_MSGF ("resize.oofm", 1, "=> %s", changed ? "true" : "false"); + DBG_OBJ_LEAVE (); + + return changed; +} + +void OOFFloatsMgr::moveFromGBToCB (Side side) +{ + DBG_OBJ_ENTER ("oofm.resize", 0, "moveFromGBToCB", "%s", + side == LEFT ? "LEFT" : "RIGHT"); + + SortedFloatsVector *dest = side == LEFT ? leftFloatsCB : rightFloatsCB; + int *floatsMark = side == LEFT ? &leftFloatsMark : &rightFloatsMark; + + for (int mark = 0; mark <= *floatsMark; mark++) + for (lout::container::typed::Iterator<TBInfo> it = tbInfos->iterator (); + it.hasNext (); ) { + TBInfo *tbInfo = it.getNext (); + SortedFloatsVector *src = + side == LEFT ? tbInfo->leftFloatsGB : tbInfo->rightFloatsGB; + for (int i = 0; i < src->size (); i++) { + Float *vloat = src->get (i); + // "vloat->indexCBList == -1": prevent copying the vloat twice. + if (vloat->indexCBList == -1 && vloat->mark == mark) { + dest->put (vloat); + DBG_OBJ_MSGF ("oofm.resize", 1, + "moving float %p (mark %d) to CB list\n", + vloat->getWidget (), vloat->mark); + DBG_OBJ_SET_NUM (side == LEFT ? + "leftFloatsCB.size" : "rightFloatsCB.size", + dest->size()); + DBG_OBJ_ARRATTRSET_PTR (side == LEFT ? + "leftFloatsCB" : "rightFloatsCB", + dest->size() - 1, "widget", + vloat->getWidget ()); + + } + } + } + + *floatsMark = 0; + + DBG_OBJ_LEAVE (); +} + +void OOFFloatsMgr::sizeAllocateFloats (Side side, int newLastAllocatedFloat) +{ + SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; + int *lastAllocatedFloat = + side == LEFT ? &lastAllocatedLeftFloat : &lastAllocatedRightFloat; + + DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateFloats", + "%s, [%d ->] %d [size = %d]", + side == LEFT ? "LEFT" : "RIGHT", *lastAllocatedFloat, + newLastAllocatedFloat, list->size ()); + + Allocation *cba = &containerAllocation; + + for (int i = *lastAllocatedFloat + 1; i <= newLastAllocatedFloat; i++) { + Float *vloat = list->get(i); + ensureFloatSize (vloat); + + Allocation *gba = getAllocation (vloat->generatingBlock); + + Allocation childAllocation; + childAllocation.x = cba->x + calcFloatX (vloat, side, gba->x - cba->x, + getGBWidthForAllocation (vloat)); + childAllocation.y = gba->y + vloat->yReal; + childAllocation.width = vloat->size.width; + childAllocation.ascent = vloat->size.ascent; + childAllocation.descent = vloat->size.descent; + + vloat->getWidget()->sizeAllocate (&childAllocation); + } + + *lastAllocatedFloat = newLastAllocatedFloat; + + DBG_OBJ_LEAVE (); +} + +// Used as argument "gbWidth" for calcFloatX(), in the context of allocation. +int OOFFloatsMgr::getGBWidthForAllocation (Float *vloat) +{ + // See comments in getFloatsSize() for a detailed rationale ... + if (container->mustBeWidenedToAvailWidth ()) + return vloat->generatingBlock->getLineBreakWidth (); + else + // ... but notice this difference: not GB width + float width is + // used, but only GB width, since the float width has already + // been included in getFloatsSize(). + return min (getAllocation(vloat->generatingBlock)->width, + vloat->generatingBlock->getLineBreakWidth ()); +} + +/** + * \brief ... + * + * gbX is given relative to the CB, as is the return value. + */ +int OOFFloatsMgr::calcFloatX (Float *vloat, Side side, int gbX, int gbWidth) +{ + DBG_OBJ_ENTER ("resize.common", 0, "calcFloatX", "%p, %s, %d, %d", + vloat->getWidget (), side == LEFT ? "LEFT" : "RIGHT", gbX, + gbWidth); + int x; + + switch (side) { + case LEFT: + // Left floats are always aligned on the left side of the + // generator (content, not allocation) ... + x = gbX + vloat->generatingBlock->getStyle()->boxOffsetX(); + DBG_OBJ_MSGF ("resize.common", 1, "left: x = %d + %d = %d", + gbX, vloat->generatingBlock->getStyle()->boxOffsetX(), x); + // ... but when the float exceeds the line break width of the + // container, it is corrected (but not left of the container). + // This way, we save space and, especially within tables, avoid + // some problems. + if (wasAllocated (container) && + x + vloat->size.width > container->getLineBreakWidth ()) { + x = max (0, container->getLineBreakWidth () - vloat->size.width); + DBG_OBJ_MSGF ("resize.common", 1, + "corrected to: max (0, %d - %d) = %d", + container->getLineBreakWidth (), vloat->size.width, x); + } + break; + + case RIGHT: + // Similar for right floats, but in this case, floats are + // shifted to the right when they are too big (instead of + // shifting the generator to the right). + + x = max (gbX + gbWidth - vloat->size.width + - vloat->generatingBlock->getStyle()->boxRestWidth(), + // Do not exceed CB allocation: + 0); + DBG_OBJ_MSGF ("resize.common", 1, "x = max (%d + %d - %d - %d, 0) = %d", + gbX, gbWidth, vloat->size.width, + vloat->generatingBlock->getStyle()->boxRestWidth(), x); + break; + + default: + assertNotReached (); + x = 0; + break; + } + + DBG_OBJ_LEAVE (); + return x; +} + +void OOFFloatsMgr::draw (View *view, Rectangle *area, DrawingContext *context) +{ + DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d", + area->x, area->y, area->width, area->height); + + drawFloats (leftFloatsCB, view, area, context); + drawFloats (rightFloatsCB, view, area, context); + + DBG_OBJ_LEAVE (); +} + +void OOFFloatsMgr::drawFloats (SortedFloatsVector *list, View *view, + Rectangle *area, DrawingContext *context) +{ + // This could be improved, since the list is sorted: search the + // first float fitting into the area, and iterate until one is + // found below the area. + + for (int i = 0; i < list->size(); i++) { + Float *vloat = list->get(i); + Widget *childWidget = vloat->getWidget (); + + Rectangle childArea; + if (!context->hasWidgetBeenProcessedAsInterruption (childWidget) && + !StackingContextMgr::handledByStackingContextMgr (childWidget) && + childWidget->intersects (container, area, &childArea)) + childWidget->draw (view, &childArea, context); + } +} + +void OOFFloatsMgr::addWidgetInFlow (OOFAwareWidget *textblock, + OOFAwareWidget *parentBlock, + int externalIndex) +{ + //printf ("[%p] addWidgetInFlow (%p, %p, %d)\n", + // container, textblock, parentBlock, externalIndex); + + TBInfo *tbInfo = + new TBInfo (this, textblock, + parentBlock ? getOOFAwareWidget (parentBlock) : NULL, + externalIndex); + tbInfo->index = tbInfos->size(); + + tbInfos->put (tbInfo); + tbInfosByOOFAwareWidget->put (new TypedPointer<OOFAwareWidget> (textblock), + tbInfo); +} + +int OOFFloatsMgr::addWidgetOOF (Widget *widget, OOFAwareWidget *generatingBlock, + int externalIndex) +{ + DBG_OBJ_ENTER ("construct.oofm", 0, "addWidgetOOF", "%p, %p, %d", + widget, generatingBlock, externalIndex); + + int subRef; + + TBInfo *tbInfo = getOOFAwareWidget (generatingBlock); + Float *vloat = new Float (this, widget, generatingBlock, externalIndex); + + // Note: Putting the float first in the GB list, and then, possibly + // into the CB list (in that order) will trigger setting + // Float::inCBList to the right value. + + switch (widget->getStyle()->vloat) { + case FLOAT_LEFT: + leftFloatsAll->put (vloat); + DBG_OBJ_SET_NUM ("leftFloatsAll.size", leftFloatsAll->size()); + DBG_OBJ_ARRATTRSET_PTR ("leftFloatsAll", leftFloatsAll->size() - 1, + "widget", vloat->getWidget ()); + + subRef = createSubRefLeftFloat (leftFloatsAll->size() - 1); + tbInfo->leftFloatsGB->put (vloat); + + if (wasAllocated (generatingBlock)) { + leftFloatsCB->put (vloat); + DBG_OBJ_SET_NUM ("leftFloatsCB.size", leftFloatsCB->size()); + DBG_OBJ_ARRATTRSET_PTR ("leftFloatsCB", leftFloatsCB->size() - 1, + "widget", vloat->getWidget ()); + } else { + if (tbInfo->index < lastLeftTBIndex) + leftFloatsMark++; + + vloat->mark = leftFloatsMark; + lastLeftTBIndex = tbInfo->index; + } + break; + + case FLOAT_RIGHT: + rightFloatsAll->put (vloat); + DBG_OBJ_SET_NUM ("rightFloatsAll.size", rightFloatsAll->size()); + DBG_OBJ_ARRATTRSET_PTR ("rightFloatsAll", rightFloatsAll->size() - 1, + "widget", vloat->getWidget ()); + + subRef = createSubRefRightFloat (rightFloatsAll->size() - 1); + tbInfo->rightFloatsGB->put (vloat); + + if (wasAllocated (generatingBlock)) { + rightFloatsCB->put (vloat); + DBG_OBJ_SET_NUM ("rightFloatsCB.size", rightFloatsCB->size()); + DBG_OBJ_ARRATTRSET_PTR ("rightFloatsCB", rightFloatsCB->size() - 1, + "widget", vloat->getWidget ()); + } else { + if (tbInfo->index < lastRightTBIndex) + rightFloatsMark++; + + vloat->mark = rightFloatsMark; + lastRightTBIndex = tbInfo->index; + } + + break; + + default: + assertNotReached(); + } + + // "sideSpanningIndex" is only compared, so this simple assignment + // is sufficient; differenciation between GB and CB lists is not + // neccessary. TODO: Can this also be applied to "index", to + // simplify the current code? Check: where is "index" used. + vloat->sideSpanningIndex = + leftFloatsAll->size() + rightFloatsAll->size() - 1; + + floatsByWidget->put (new TypedPointer<Widget> (widget), vloat); + + DBG_OBJ_MSGF ("construct.oofm", 1, "=> %d", subRef); + DBG_OBJ_LEAVE (); + return subRef; +} + +void OOFFloatsMgr::calcWidgetRefSize (Widget *widget, Requisition *size) +{ + size->width = size->ascent = size->descent = 0; +} + +void OOFFloatsMgr::moveExternalIndices (OOFAwareWidget *generatingBlock, + int oldStartIndex, int diff) +{ + TBInfo *tbInfo = getOOFAwareWidget (generatingBlock); + moveExternalIndices (tbInfo->leftFloatsGB, oldStartIndex, diff); + moveExternalIndices (tbInfo->rightFloatsGB, oldStartIndex, diff); +} + +void OOFFloatsMgr::moveExternalIndices (SortedFloatsVector *list, + int oldStartIndex, int diff) +{ + // Could be faster with binary search, but the GB (not CB!) lists + // should be rather small. + for (int i = 0; i < list->size (); i++) { + Float *vloat = list->get (i); + if (vloat->externalIndex >= oldStartIndex) { + vloat->externalIndex += diff; + DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.externalIndex", + vloat->externalIndex); + } + } +} + +OOFFloatsMgr::Float *OOFFloatsMgr::findFloatByWidget (Widget *widget) +{ + TypedPointer <Widget> key (widget); + Float *vloat = floatsByWidget->get (&key); + assert (vloat != NULL); + return vloat; +} + +void OOFFloatsMgr::markSizeChange (int ref) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "markSizeChange", "%d", ref); + + Float *vloat; + + if (isSubRefLeftFloat (ref)) + vloat = leftFloatsAll->get (getFloatIndexFromSubRef (ref)); + else if (isSubRefRightFloat (ref)) + vloat = rightFloatsAll->get (getFloatIndexFromSubRef (ref)); + else { + assertNotReached(); + vloat = NULL; // compiler happiness + } + + vloat->dirty = vloat->sizeChangedSinceLastAllocation = true; + + DBG_OBJ_SET_BOOL_O (vloat->getWidget (), "<Float>.dirty", vloat->dirty); + DBG_OBJ_SET_BOOL_O (vloat->getWidget (), + "<Float>.sizeChangedSinceLastAllocation", + vloat->sizeChangedSinceLastAllocation); + + // The generating block is told directly about this. (Others later, in + // sizeAllocateEnd.) Could be faster (cf. hasRelationChanged, which + // differentiates many special cases), but the size is not known yet, + vloat->generatingBlock->borderChanged (vloat->yReal, vloat->getWidget ()); + + DBG_OBJ_LEAVE (); +} + + +void OOFFloatsMgr::markExtremesChange (int ref) +{ + // Nothing to do here. +} + +Widget *OOFFloatsMgr::getWidgetAtPoint (int x, int y, + GettingWidgetAtPointContext *context) +{ + Widget *widgetAtPoint = NULL; + + widgetAtPoint = getFloatWidgetAtPoint (rightFloatsCB, x, y, context); + if (widgetAtPoint == NULL) + widgetAtPoint = getFloatWidgetAtPoint (leftFloatsCB, x, y, context); + + return widgetAtPoint; +} + +Widget *OOFFloatsMgr::getFloatWidgetAtPoint (SortedFloatsVector *list, int x, + int y, + GettingWidgetAtPointContext + *context) +{ + // Could use binary search to be faster (similar to drawing). + Widget *widgetAtPoint = NULL; + + for (int i = list->size() - 1; widgetAtPoint == NULL && i >= 0; i--) { + Widget *childWidget = list->get(i)->getWidget (); + if (!context->hasWidgetBeenProcessedAsInterruption (childWidget) && + !StackingContextMgr::handledByStackingContextMgr (childWidget)) + widgetAtPoint = childWidget->getWidgetAtPoint (x, y, context); + } + + return widgetAtPoint; +} + +void OOFFloatsMgr::tellPosition1 (Widget *widget, int x, int y) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "tellPosition1", "%p, %d, %d", + widget, x, y); + + assert (y >= 0); + + Float *vloat = findFloatByWidget(widget); + + SortedFloatsVector *listSame, *listOpp; + Side side; + getFloatsListsAndSide (vloat, &listSame, &listOpp, &side); + ensureFloatSize (vloat); + + // "yReal" may change due to collisions (see below). + vloat->yReq = vloat->yReal = y; + + DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.yReq", vloat->yReq); + DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.yReal", vloat->yReal); + + // Test collisions (on this side). Although there are (rare) cases + // where it could make sense, the horizontal dimensions are not + // tested; especially since searching and border calculation would + // be confused. For this reaspn, only the previous float is + // relevant. (Cf. below, collisions on the other side.) + int index = vloat->getIndex (listSame->type), yRealNew; + if (index >= 1 && + collidesV (vloat, listSame->get (index - 1), listSame->type, + &yRealNew, false)) { + vloat->yReal = yRealNew; + DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.yReal", vloat->yReal); + } + + // Test collisions (on the opposite side). There are cases when + // more than one float has to be tested. Consider the following + // HTML snippet ("id" attribute only used for simple reference + // below, as #f1, #f2, and #f3): + // + // <div style="float:left" id="f1"> + // Left left left left left left left left left left. + // </div> + // <div style="float:left" id="f2">Also left.</div> + // <div style="float:right" id="f3">Right.</div> + // + // When displayed with a suitable window width (only slightly wider + // than the text within #f1), this should look like this: + // + // --------------------------------------------------------- + // | Left left left left left left left left left left. | + // | Also left. Right. | + // --------------------------------------------------------- + // + // Consider float #f3: a collision test with #f2, considering + // vertical dimensions, is positive, but not the test with + // horizontal dimensions (because #f2 and #f3 are too + // narrow). However, a collision has to be tested with #f1; + // otherwise #f3 and #f1 would overlap. + + int oppFloatIndex = + listOpp->findLastBeforeSideSpanningIndex (vloat->sideSpanningIndex); + // Generally, the rules are simple: loop as long as the vertical + // dimensions test is positive (and, of course, there are floats), + // ... + for (bool foundColl = false; + !foundColl && oppFloatIndex >= 0 && + collidesV (vloat, listOpp->get (oppFloatIndex), listSame->type, + &yRealNew, false); + oppFloatIndex--) { + // ... but stop the loop as soon as the horizontal dimensions + // test is positive. + if (collidesH (vloat, listOpp->get (oppFloatIndex), listSame->type)) { + vloat->yReal = yRealNew; + DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.yReal", vloat->yReal); + foundColl = true; + } + } + + DBG_OBJ_MSGF ("resize.oofm", 1, "vloat->yReq = %d, vloat->yReal = %d", + vloat->yReq, vloat->yReal); + + DBG_OBJ_LEAVE (); +} + +void OOFFloatsMgr::tellPosition2 (Widget *widget, int x, int y) +{ +} + +bool OOFFloatsMgr::collidesV (Float *vloat, Float *other, SFVType type, + int *yReal, bool useAllocation) +{ + // Only checks vertical (possible) collisions, and only refers to + // vloat->yReal; never to vloat->allocation->y, even when the GBs are + // different. Used only in tellPosition. + + DBG_OBJ_ENTER ("resize.oofm", 0, "collidesV", "#%d [%p], #%d [%p], ...", + vloat->getIndex (type), vloat->getWidget (), + other->getIndex (type), other->getWidget ()); + + bool result; + + DBG_OBJ_MSGF ("resize.oofm", 1, "initial yReal = %d", vloat->yReal); + + if (vloat->generatingBlock == other->generatingBlock) { + ensureFloatSize (other); + int otherBottomGB = + other->yReal + other->size.ascent + other->size.descent; + + DBG_OBJ_MSGF ("resize.oofm", 1, + "same generators: otherBottomGB = %d + (%d + %d) = %d", + other->yReal, other->size.ascent, other->size.descent, + otherBottomGB); + + if (vloat->yReal < otherBottomGB) { + *yReal = otherBottomGB; + result = true; + } else + result = false; + } else { + // If the other float is not allocated, there is no collision. The + // allocation of this float (vloat) is not used at all. + if (!other->getWidget()->wasAllocated ()) + result = false; + else { + assert (wasAllocated (vloat->generatingBlock)); + Allocation *gba = getAllocation (vloat->generatingBlock), + *flaOther = other->getWidget()->getAllocation (); + + // We distinguish two cases (by different values of useAllocation): + // (i) within tellPosition, GB allocation + yReal is used for the + // y position of the other float, while (ii) in checkAllocatedFloat- + // Collisions, the float allocation is used. The latter is necessary + // by the definition of this method, the former increases performance, + // as compared to using the float allocation, in some cases, as in + // this: + // + // When '<div><div style="float:left">[Some text]</div></div>' is + // repeated n times, the resize idle function (Layout::resizeIdle) + // would be repeated roughly n times, when also in case (i) the float + // allocation is used, since for the collision test of float n with + // float n - 1, the allocation of float n - 1 does not yet reflect the + // collision test between n - 1 and n - 2, but yReal does for n - 1. + // + // On the other hand, the GB allocations will most likely more stable + // than the float allocations. + // + // Cases where this is incorrect will hopefully be rare, and, in any + // case, corrected in sizeAllocateEnd, either because hasRelation- + // Changed returns true, or in checkAllocatedFloatCollisions. + + int otherFloatY = useAllocation ? flaOther->y : + getAllocation(other->generatingBlock)->y + other->yReal; + int otherBottomGB = + otherFloatY + flaOther->ascent + flaOther->descent - gba->y; + + DBG_OBJ_MSGF ("resize.oofm", 1, + "different generators: " + "otherBottomGB = %d + (%d + %d) - %d = %d", + otherFloatY, flaOther->ascent, flaOther->descent, gba->y, + otherBottomGB); + + if (vloat->yReal < otherBottomGB) { + *yReal = otherBottomGB; + result = true; + } else + result = false; + } + } + + if (result) + DBG_OBJ_MSGF ("resize.oofm", 1, "collides: new yReal = %d", *yReal); + else + DBG_OBJ_MSG ("resize.oofm", 1, "does not collide"); + + DBG_OBJ_LEAVE (); + return result; +} + + +bool OOFFloatsMgr::collidesH (Float *vloat, Float *other, SFVType type) +{ + // Only checks horizontal collision. For a complete test, use + // collidesV (...) && collidesH (...). + bool collidesH; + + if (vloat->generatingBlock == other->generatingBlock) + collidesH = vloat->size.width + other->size.width + + vloat->generatingBlock->boxDiffWidth() + > vloat->generatingBlock->getLineBreakWidth(); + else { + // Again, if the other float is not allocated, there is no + // collision. Compare to collidesV. (But vloat->size is used + // here.) + if (!other->getWidget()->wasAllocated ()) + collidesH = false; + else { + assert (wasAllocated (vloat->generatingBlock)); + Allocation *gba = getAllocation (vloat->generatingBlock); + int vloatX = + calcFloatX (vloat, + vloat->getWidget()->getStyle()->vloat == FLOAT_LEFT ? + LEFT : RIGHT, + gba->x, getGBWidthForAllocation (vloat)); + + // Generally: right border of the left float > left border of + // the right float (all in canvas coordinates). + if (vloat->getWidget()->getStyle()->vloat == FLOAT_LEFT) + // "vloat" is left, "other" is right + collidesH = vloatX + vloat->size.width + > other->getWidget()->getAllocation()->x; + else + // "other" is left, "vloat" is right + collidesH = other->getWidget()->getAllocation()->x + + other->getWidget()->getAllocation()->width + > vloatX; + } + } + + return collidesH; +} + +void OOFFloatsMgr::getFloatsListsAndSide (Float *vloat, + SortedFloatsVector **listSame, + SortedFloatsVector **listOpp, + Side *side) +{ + TBInfo *tbInfo = getOOFAwareWidget (vloat->generatingBlock); + + switch (vloat->getWidget()->getStyle()->vloat) { + case FLOAT_LEFT: + if (wasAllocated (vloat->generatingBlock)) { + if (listSame) *listSame = leftFloatsCB; + if (listOpp) *listOpp = rightFloatsCB; + } else { + if (listSame) *listSame = tbInfo->leftFloatsGB; + if (listOpp) *listOpp = tbInfo->rightFloatsGB; + } + if (side) *side = LEFT; + break; + + case FLOAT_RIGHT: + if (wasAllocated (vloat->generatingBlock)) { + if (listSame) *listSame = rightFloatsCB; + if (listOpp) *listOpp = leftFloatsCB; + } else { + if (listSame) *listSame = tbInfo->rightFloatsGB; + if (listOpp) *listOpp = tbInfo->leftFloatsGB; + } + if (side) *side = RIGHT; + break; + + default: + assertNotReached(); + } +} + +void OOFFloatsMgr::getSize (Requisition *cbReq, int *oofWidth, int *oofHeight) +{ + DBG_OBJ_ENTER0 ("resize.oofm", 0, "getSize"); + + int oofWidthtLeft, oofWidthRight, oofHeightLeft, oofHeightRight; + getFloatsSize (cbReq, LEFT, &oofWidthtLeft, &oofHeightLeft); + getFloatsSize (cbReq, RIGHT, &oofWidthRight, &oofHeightRight); + + // Floats must be within the *content* area of the containing + // block, not its *margin* area (which is equivalent to the + // requisition / allocation). For this reason, boxRestWidth() and + // boxRestHeight() are added here. + + *oofWidth = + max (oofWidthtLeft, oofWidthRight) + container->boxRestWidth (); + *oofHeight = + max (oofHeightLeft, oofHeightRight) + container->boxRestHeight (); + + DBG_OBJ_MSGF ("resize.oofm", 1, + "=> (l: %d, r: %d => %d) * (l: %d, r: %d => %d)", + oofWidthtLeft, oofWidthRight, *oofWidth, oofHeightLeft, + oofHeightRight, *oofHeight); + DBG_OBJ_LEAVE (); +} + +void OOFFloatsMgr::getFloatsSize (Requisition *cbReq, Side side, int *width, + int *height) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "getFloatsSize", "(%d * (%d + %d), %s, ...", + cbReq->width, cbReq->ascent, cbReq->descent, + side == LEFT ? "LEFT" : "RIGHT"); + + SortedFloatsVector *list = getFloatsListForOOFAwareWidget (container, side); + + *width = *height = 0; + + DBG_OBJ_MSGF ("resize.oofm", 1, "%d floats on this side", list->size()); + + for (int i = 0; i < list->size(); i++) { + Float *vloat = list->get(i); + + DBG_OBJ_MSGF ("resize.oofm", 1, + "float %p has generator %p (container is %p)", + vloat->getWidget (), vloat->generatingBlock, container); + + if (vloat->generatingBlock == container || + wasAllocated (vloat->generatingBlock)) { + ensureFloatSize (vloat); + int x, y; + + int effWidth; + if (container->mustBeWidenedToAvailWidth ()) + // For most textblocks, the line break width is used for + // calculating the x position. (This changed for GROWS, + // where the width of a textblock is often smaller that + // the line break.) + effWidth = vloat->generatingBlock->getLineBreakWidth (); + else + // For some textblocks, like inline blocks, the line break + // width would be too large for right floats in some + // cases. + // + // (i) Consider a small inline block with only a few words + // in one line, narrower that line break width minus + // float width. In this case, the sum should be used. + // + // (ii) If there is more than one line, the line break + // will already be exceeded, and so be smaller that + // GB width + float width. + effWidth = min (cbReq->width + vloat->size.width, + vloat->generatingBlock->getLineBreakWidth ()); + + if (vloat->generatingBlock == container) { + x = calcFloatX (vloat, side, 0, effWidth); + y = vloat->yReal; + } else { + Allocation *gba = getAllocation(vloat->generatingBlock); + x = calcFloatX (vloat, side, gba->x - containerAllocation.x, + effWidth); + y = gba->y - containerAllocation.y + vloat->yReal; + } + + *width = max (*width, x + vloat->size.width); + *height = max (*height, y + vloat->size.ascent + vloat->size.descent); + + DBG_OBJ_MSGF ("resize.oofm", 1, + "considering float %p generated by %p: (%d + %d) * " + "(%d + (%d + %d)) => %d * %d", + vloat->getWidget (), vloat->generatingBlock, + x, vloat->size.width, + y, vloat->size.ascent, vloat->size.descent, + *width, *height); + } else + DBG_OBJ_MSGF ("resize.oofm", 1, + "considering float %p generated by %p: not allocated", + vloat->getWidget (), vloat->generatingBlock); + } + + DBG_OBJ_LEAVE (); +} + +bool OOFFloatsMgr::containerMustAdjustExtraSpace () +{ + return false; +} + +void OOFFloatsMgr::getExtremes (Extremes *cbExtr, int *oofMinWidth, + int *oofMaxWidth) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "getExtremes", "(%d / %d), ...", + cbExtr->minWidth, cbExtr->maxWidth); + + int oofMinWidthtLeft, oofMinWidthRight, oofMaxWidthLeft, oofMaxWidthRight; + getFloatsExtremes (cbExtr, LEFT, &oofMinWidthtLeft, &oofMaxWidthLeft); + getFloatsExtremes (cbExtr, RIGHT, &oofMinWidthRight, &oofMaxWidthRight); + + *oofMinWidth = max (oofMinWidthtLeft, oofMinWidthRight); + *oofMaxWidth = max (oofMaxWidthLeft, oofMaxWidthRight); + + DBG_OBJ_MSGF ("resize.oofm", 1, + "=> (l: %d, r: %d => %d) / (l: %d, r: %d => %d)", + oofMinWidthtLeft, oofMinWidthRight, *oofMinWidth, + oofMaxWidthLeft, oofMaxWidthRight, *oofMaxWidth); + DBG_OBJ_LEAVE (); +} + +void OOFFloatsMgr::getFloatsExtremes (Extremes *cbExtr, Side side, + int *minWidth, int *maxWidth) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "getFloatsExtremes", "(%d / %d), %s, ...", + cbExtr->minWidth, cbExtr->maxWidth, + side == LEFT ? "LEFT" : "RIGHT"); + + *minWidth = *maxWidth = 0; + + SortedFloatsVector *list = getFloatsListForOOFAwareWidget (container, side); + DBG_OBJ_MSGF ("resize.oofm", 1, "%d floats to be examined", list->size()); + + for (int i = 0; i < list->size(); i++) { + Float *vloat = list->get(i); + + DBG_OBJ_MSGF ("resize.oofm", 1, + "float %p has generator %p (container is %p)", + vloat->getWidget (), vloat->generatingBlock, + container); + + if (vloat->generatingBlock == container || + wasAllocated (vloat->generatingBlock)) { + Extremes extr; + vloat->getWidget()->getExtremes (&extr); + + // The calculation of extremes must be kept consistent with + // getFloatsSize(). Especially this means for the *minimal* width: + // + // - The right border (difference between float and + // container) does not have to be considered (see + // getFloatsSize()). + // + // - This is also the case for the left border, as seen in + // calcFloatX() ("... but when the float exceeds the line + // break width" ...). + + *minWidth = max (*minWidth, extr.minWidth); + + // For the maximal width, borders must be considered. + + if (vloat->generatingBlock == container) + *maxWidth = + max (*maxWidth, + extr.maxWidth + + vloat->generatingBlock->getStyle()->boxDiffWidth()); + else { + Allocation *gba = getAllocation (vloat->generatingBlock); + *maxWidth = + max (*maxWidth, + extr.maxWidth + + vloat->generatingBlock->getStyle()->boxDiffWidth() + + max (containerAllocation.width - gba->width, 0)); + } + + DBG_OBJ_MSGF ("resize.oofm", 1, "%d / %d => %d / %d", + extr.minWidth, extr.maxWidth, *minWidth, *maxWidth); + } else + DBG_OBJ_MSG ("resize.oofm", 1, "not allocated"); + } + + DBG_OBJ_LEAVE (); +} + +OOFFloatsMgr::TBInfo *OOFFloatsMgr::getOOFAwareWidgetWhenRegistered + (OOFAwareWidget *widget) +{ + DBG_OBJ_ENTER ("oofm.common", 0, "getOOFAwareWidgetWhenRegistered", "%p", + widget); + TypedPointer<OOFAwareWidget> key (widget); + TBInfo *tbInfo = tbInfosByOOFAwareWidget->get (&key); + DBG_OBJ_MSGF ("oofm.common", 1, "found? %s", tbInfo ? "yes" : "no"); + DBG_OBJ_LEAVE (); + return tbInfo; +} + +OOFFloatsMgr::TBInfo *OOFFloatsMgr::getOOFAwareWidget (OOFAwareWidget *widget) +{ + DBG_OBJ_ENTER ("oofm.common", 0, "getOOFAwareWidget", "%p", widget); + TBInfo *tbInfo = getOOFAwareWidgetWhenRegistered (widget); + assert (tbInfo); + DBG_OBJ_LEAVE (); + return tbInfo; +} + +/** + * Get the left border for the vertical position of *y*, for a height + * of *h", based on floats; relative to the allocation of the calling + * textblock. + * + * The border includes marging/border/padding of the calling textblock + * but is 0 if there is no float, so a caller should also consider + * other borders. + */ +int OOFFloatsMgr::getLeftBorder (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex) +{ + int b = getBorder (textblock, LEFT, y, h, lastGB, lastExtIndex); + DBG_OBJ_MSGF ("border", 0, "left border (%p, %d, %d, %p, %d) => %d", + textblock, y, h, lastGB, lastExtIndex, b); + return b; +} + +/** + * Get the right border for the vertical position of *y*, for a height + * of *h*, based on floats. + * + * See also getLeftBorder(int, int); + */ +int OOFFloatsMgr::getRightBorder (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex) +{ + int b = getBorder (textblock, RIGHT, y, h, lastGB, lastExtIndex); + DBG_OBJ_MSGF ("border", 0, "right border (%p, %d, %d, %p, %d) => %d", + textblock, y, h, lastGB, lastExtIndex, b); + return b; +} + +int OOFFloatsMgr::getBorder (OOFAwareWidget *textblock, Side side, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex) +{ + DBG_OBJ_ENTER ("border", 0, "getBorder", "%p, %s, %d, %d, %p, %d", + textblock, side == LEFT ? "LEFT" : "RIGHT", y, h, + lastGB, lastExtIndex); + + SortedFloatsVector *list = getFloatsListForOOFAwareWidget (textblock, side); + int last; + int first = list->findFirst (textblock, y, h, lastGB, lastExtIndex, &last); + + DBG_OBJ_MSGF ("border", 1, "first = %d", first); + + if (first == -1) { + // No float. + DBG_OBJ_LEAVE (); + return 0; + } else { + // It is not sufficient to find the first float, since a line + // (with height h) may cover the region of multiple float, of + // which the widest has to be choosen. + int border = 0; + bool covers = true; + + // We are not searching until the end of the list, but until the + // float defined by lastGB and lastExtIndex. + for (int i = first; covers && i <= last; i++) { + Float *vloat = list->get(i); + covers = vloat->covers (textblock, y, h); + DBG_OBJ_MSGF ("border", 1, "float %d (%p) covers? %s.", + i, vloat->getWidget(), covers ? "<b>yes</b>" : "no"); + + if (covers) { + int thisBorder; + if (vloat->generatingBlock == textblock) { + int borderIn = side == LEFT ? + vloat->generatingBlock->boxOffsetX() : + vloat->generatingBlock->boxRestWidth(); + thisBorder = vloat->size.width + borderIn; + DBG_OBJ_MSGF ("border", 1, "GB: thisBorder = %d + %d = %d", + vloat->size.width, borderIn, thisBorder); + } else { + assert (wasAllocated (vloat->generatingBlock)); + assert (vloat->getWidget()->wasAllocated ()); + + Allocation *tba = getAllocation(textblock), + *fla = vloat->getWidget()->getAllocation (); + if (side == LEFT) { + thisBorder = fla->x + fla->width - tba->x; + DBG_OBJ_MSGF ("border", 1, + "not GB: thisBorder = %d + %d - %d = %d", + fla->x, fla->width, tba->x, thisBorder); + } else { + // See also calcFloatX. + thisBorder = + tba->x + textblock->getLineBreakWidth () - fla->x; + DBG_OBJ_MSGF ("border", 1, + "not GB: thisBorder = %d + %d - %d " + "= %d", + tba->x, textblock->getLineBreakWidth (), fla->x, + thisBorder); + } + } + + border = max (border, thisBorder); + DBG_OBJ_MSGF ("border", 1, "=> border = %d", border); + } + } + + DBG_OBJ_LEAVE (); + return border; + } +} + + +OOFFloatsMgr::SortedFloatsVector *OOFFloatsMgr::getFloatsListForOOFAwareWidget + (OOFAwareWidget *textblock, Side side) +{ + DBG_OBJ_ENTER ("oofm.common", 1, "getFloatsListForOOFAwareWidget", "%p, %s", + textblock, side == LEFT ? "LEFT" : "RIGHT"); + + OOFFloatsMgr::SortedFloatsVector *list; + + if (wasAllocated (textblock)) { + DBG_OBJ_MSG ("oofm.common", 2, "returning <b>CB</b> list"); + list = side == LEFT ? leftFloatsCB : rightFloatsCB; + } else { + DBG_OBJ_MSG ("oofm.common", 2, "returning <b>GB</b> list"); + TBInfo *tbInfo = getOOFAwareWidget (textblock); + list = side == LEFT ? tbInfo->leftFloatsGB : tbInfo->rightFloatsGB; + } + + DBG_OBJ_LEAVE (); + return list; +} + + +bool OOFFloatsMgr::hasFloatLeft (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex) +{ + bool b = hasFloat (textblock, LEFT, y, h, lastGB, lastExtIndex); + DBG_OBJ_MSGF ("border", 0, "has float left (%p, %d, %d, %p, %d) => %s", + textblock, y, h, lastGB, lastExtIndex, b ? "true" : "false"); + return b; +} + +bool OOFFloatsMgr::hasFloatRight (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex) +{ + bool b = hasFloat (textblock, RIGHT, y, h, lastGB, lastExtIndex); + DBG_OBJ_MSGF ("border", 0, "has float right (%p, %d, %d, %p, %d) => %s", + textblock, y, h, lastGB, lastExtIndex, b ? "true" : "false"); + return b; +} + +bool OOFFloatsMgr::hasFloat (OOFAwareWidget *textblock, Side side, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex) +{ + DBG_OBJ_ENTER ("border", 0, "hasFloat", "%p, %s, %d, %d, %p, %d", + textblock, side == LEFT ? "LEFT" : "RIGHT", y, h, + lastGB, lastExtIndex); + + SortedFloatsVector *list = getFloatsListForOOFAwareWidget (textblock, side); + int first = list->findFirst (textblock, y, h, lastGB, lastExtIndex, NULL); + + DBG_OBJ_MSGF ("border", 1, "first = %d", first); + DBG_OBJ_LEAVE (); + return first != -1; +} + +int OOFFloatsMgr::getLeftFloatHeight (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex) +{ + return getFloatHeight (textblock, LEFT, y, h, lastGB, lastExtIndex); +} + +int OOFFloatsMgr::getRightFloatHeight (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex) +{ + return getFloatHeight (textblock, RIGHT, y, h, lastGB, lastExtIndex); +} + +// Calculate height from the position *y*. +int OOFFloatsMgr::getFloatHeight (OOFAwareWidget *textblock, Side side, int y, + int h, OOFAwareWidget *lastGB, + int lastExtIndex) +{ + DBG_OBJ_ENTER ("border", 0, "getFloatHeight", "%p, %s, %d, %d, %p, %d", + textblock, side == LEFT ? "LEFT" : "RIGHT", y, h, + lastGB, lastExtIndex); + + SortedFloatsVector *list = getFloatsListForOOFAwareWidget (textblock, side); + int first = list->findFirst (textblock, y, h, lastGB, lastExtIndex, NULL); + assert (first != -1); /* This method must not be called when there is no + float on the respective side. */ + + Float *vloat = list->get(first); + int yRelToFloat; + + if (vloat->generatingBlock == textblock) { + yRelToFloat = y - vloat->yReal; + DBG_OBJ_MSGF ("border", 1, "caller is CB: yRelToFloat = %d - %d = %d", + y, vloat->yReal, yRelToFloat); + } else { + // The respective widgets are allocated; otherwise, hasFloat() would have + // returned false. + assert (wasAllocated (textblock)); + assert (vloat->getWidget()->wasAllocated ()); + + Allocation *tba = getAllocation(textblock), + *fla = vloat->getWidget()->getAllocation (); + yRelToFloat = tba->y + y - fla->y; + + DBG_OBJ_MSGF ("border", 1, + "caller is not CB: yRelToFloat = %d + %d - %d = %d", + tba->y, y, fla->y, yRelToFloat); + } + + ensureFloatSize (vloat); + int height = vloat->size.ascent + vloat->size.descent - yRelToFloat; + + DBG_OBJ_MSGF ("border", 1, "=> (%d + %d) - %d = %d", + vloat->size.ascent, vloat->size.descent, yRelToFloat, height); + DBG_OBJ_LEAVE (); + return height; +} + +/** + * Returns position relative to the textblock "tb". + */ +int OOFFloatsMgr::getClearPosition (OOFAwareWidget *textblock) +{ + return getOOFAwareWidget(textblock)->clearPosition; +} + +int OOFFloatsMgr::calcClearPosition (OOFAwareWidget *textblock) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "getClearPosition", "%p", textblock); + + int pos; + + if (textblock->getStyle()) { + bool left = false, right = false; + switch (textblock->getStyle()->clear) { + case CLEAR_NONE: break; + case CLEAR_LEFT: left = true; break; + case CLEAR_RIGHT: right = true; break; + case CLEAR_BOTH: left = right = true; break; + default: assertNotReached (); + } + + pos = max (left ? calcClearPosition (textblock, LEFT) : 0, + right ? calcClearPosition (textblock, RIGHT) : 0); + } else + pos = 0; + + DBG_OBJ_MSGF ("resize.oofm", 1, "=> %d", pos); + DBG_OBJ_LEAVE (); + + return pos; +} + +bool OOFFloatsMgr::affectsLeftBorder (core::Widget *widget) +{ + return widget->getStyle()->vloat == core::style::FLOAT_LEFT; +} + +bool OOFFloatsMgr::affectsRightBorder (core::Widget *widget) +{ + return widget->getStyle()->vloat == core::style::FLOAT_RIGHT; +}; + +bool OOFFloatsMgr::mayAffectBordersAtAll () +{ + return true; +} + +int OOFFloatsMgr::calcClearPosition (OOFAwareWidget *textblock, Side side) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "getClearPosition", "%p, %s", + textblock, side == LEFT ? "LEFT" : "RIGHT"); + + int pos; + + if (!wasAllocated (textblock)) + // There is no relation yet to floats generated by other + // textblocks, and this textblocks floats are unimportant for + // the "clear" property. + pos = 0; + else { + SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; + + // Search the last float before (therfore -1) this textblock. + int i = list->findFloatIndex (textblock, -1); + if (i < 0) { + pos = 0; + DBG_OBJ_MSG ("resize.oofm", 1, "no float"); + } else { + Float *vloat = list->get(i); + assert (vloat->generatingBlock != textblock); + if (!wasAllocated (vloat->generatingBlock)) + pos = 0; // See above. + else { + ensureFloatSize (vloat); + pos = max (getAllocation(vloat->generatingBlock)->y + vloat->yReal + + vloat->size.ascent + vloat->size.descent + - getAllocation(textblock)->y, + 0); + DBG_OBJ_MSGF ("resize.oofm", 1, + "float %p => max (%d + %d + (%d + %d) - %d, 0)", + vloat->getWidget (), + getAllocation(vloat->generatingBlock)->y, + vloat->yReal, vloat->size.ascent, vloat->size.descent, + getAllocation(textblock)->y); + } + } + } + + DBG_OBJ_MSGF ("resize.oofm", 1, "=> %d", pos); + DBG_OBJ_LEAVE (); + + return pos; +} + +void OOFFloatsMgr::ensureFloatSize (Float *vloat) +{ + // Historical note: relative sizes (e. g. percentages) are already + // handled by (at this time) Layout::containerSizeChanged, so + // Float::dirty will be set. + + DBG_OBJ_ENTER ("resize.oofm", 0, "ensureFloatSize", "%p", + vloat->getWidget ()); + + if (vloat->dirty) { + DBG_OBJ_MSG ("resize.oofm", 1, "dirty: recalculation"); + + vloat->getWidget()->sizeRequest (&vloat->size); + vloat->cbLineBreakWidth = container->getLineBreakWidth (); + vloat->dirty = false; + DBG_OBJ_SET_BOOL_O (vloat->getWidget (), "<Float>.dirty", vloat->dirty); + + DBG_OBJ_MSGF ("resize.oofm", 1, "size: %d * (%d + %d)", + vloat->size.width, vloat->size.ascent, vloat->size.descent); + + DBG_OBJ_SET_NUM_O (vloat->getWidget(), "<Float>.size.width", + vloat->size.width); + DBG_OBJ_SET_NUM_O (vloat->getWidget(), "<Float>.size.ascent", + vloat->size.ascent); + DBG_OBJ_SET_NUM_O (vloat->getWidget(), "<Float>.size.descent", + vloat->size.descent); + + // "sizeChangedSinceLastAllocation" is reset in sizeAllocateEnd() + } + + DBG_OBJ_LEAVE (); +} + +bool OOFFloatsMgr::dealingWithSizeOfChild (core::Widget *child) +{ + return false; +} + +int OOFFloatsMgr::getAvailWidthOfChild (Widget *child, bool forceValue) +{ + assertNotReached (); + return 0; +} + +int OOFFloatsMgr::getAvailHeightOfChild (Widget *child, bool forceValue) +{ + assertNotReached (); + return 0; +} + +int OOFFloatsMgr::getNumWidgets () +{ + return leftFloatsAll->size() + rightFloatsAll->size(); +} + +Widget *OOFFloatsMgr::getWidget (int i) +{ + if (i < leftFloatsAll->size()) + return leftFloatsAll->get(i)->getWidget (); + else + return rightFloatsAll->get(i - leftFloatsAll->size())->getWidget (); +} + +} // namespace oof + +} // namespace dw diff --git a/dw/ooffloatsmgr.hh b/dw/ooffloatsmgr.hh new file mode 100644 index 00000000..1a66e2ff --- /dev/null +++ b/dw/ooffloatsmgr.hh @@ -0,0 +1,402 @@ +#ifndef __DW_OOFFLOATSMGR_HH__ +#define __DW_OOFFLOATSMGR_HH__ + +#include "outofflowmgr.hh" + +namespace dw { + +namespace oof { + +/** + * \brief OutOfFlowMgr implementation dealing with floats. + * + * Note: The identifiers and comments of this class still refer to + * "Textblock" instead of "OOFAwareWidget"; should be cleaned up some + * day. (OTOH, these widgets are always textblocks.) + */ +class OOFFloatsMgr: public OutOfFlowMgr +{ + friend class WidgetInfo; + +private: + enum Side { LEFT, RIGHT }; + enum SFVType { GB, CB }; + + OOFAwareWidget *container; + + // These two values are redundant to TBInfo::wasAllocated and + // TBInfo::allocation, for some special cases. + bool containerWasAllocated; + core::Allocation containerAllocation; + + class WidgetInfo: public lout::object::Object + { + private: + bool wasAllocated; + int xCB, yCB; // relative to the containing block + int width, height; + + OOFFloatsMgr *oofm; + core::Widget *widget; + + protected: + OOFFloatsMgr *getOOFFloatsMgr () { return oofm; } + + public: + WidgetInfo (OOFFloatsMgr *oofm, core::Widget *widget); + + inline bool wasThenAllocated () { return wasAllocated; } + inline int getOldXCB () { return xCB; } + inline int getOldYCB () { return yCB; } + inline int getOldWidth () { return width; } + inline int getOldHeight () { return height; } + + + void update (bool wasAllocated, int xCB, int yCB, int width, int height); + + inline core::Widget *getWidget () { return widget; } + }; + + class Float: public WidgetInfo + { + public: + class ComparePosition: public lout::object::Comparator + { + private: + OOFFloatsMgr *oofm; + OOFAwareWidget *refTB; + SFVType type; // actually only used for debugging + + public: + ComparePosition (OOFFloatsMgr *oofm, OOFAwareWidget *refTB, + SFVType type) + { this->oofm = oofm; this->refTB = refTB; this->type = type; } + int compare(Object *o1, Object *o2); + }; + + class CompareSideSpanningIndex: public lout::object::Comparator + { + public: + int compare(Object *o1, Object *o2); + }; + + class CompareGBAndExtIndex: public lout::object::Comparator + { + private: + OOFFloatsMgr *oofm; + SFVType type; // actually only used for debugging + + public: + CompareGBAndExtIndex (OOFFloatsMgr *oofm, SFVType type) + { this->oofm = oofm; this->type = type; } + int compare(Object *o1, Object *o2); + }; + + OOFAwareWidget *generatingBlock; + int externalIndex; + int yReq, yReal; // relative to generator, not container + int indexGBList; /* Refers to TBInfo::leftFloatsGB or + TBInfo::rightFloatsGB, respectively. -1 + initially. */ + int indexCBList; /* Refers to leftFloatsCB or rightFloatsCB, + respectively. -1 initially. */ + int sideSpanningIndex, mark; + core::Requisition size; + int cbLineBreakWidth; /* On which the calculation of relative sizes + is based. Height not yet used, and probably + not added before size redesign. */ + bool dirty, sizeChangedSinceLastAllocation; + + Float (OOFFloatsMgr *oofm, core::Widget *widget, + OOFAwareWidget *generatingBlock, int externalIndex); + + inline bool isNowAllocated () { return getWidget()->wasAllocated (); } + inline int getNewXCB () { return getWidget()->getAllocation()->x - + getOOFFloatsMgr()->containerAllocation.x; } + inline int getNewYCB () { return getWidget()->getAllocation()->y - + getOOFFloatsMgr()->containerAllocation.y; } + inline int getNewWidth () { return getWidget()->getAllocation()->width; } + inline int getNewHeight () { return getWidget()->getAllocation()->ascent + + getWidget()->getAllocation()->descent; } + void updateAllocation (); + + inline int *getIndexRef (SFVType type) { + return type == GB ? &indexGBList : &indexCBList; } + inline int getIndex (SFVType type) { return *(getIndexRef (type)); } + inline void setIndex (SFVType type, int value) { + *(getIndexRef (type)) = value; } + + void intoStringBuffer(lout::misc::StringBuffer *sb); + + bool covers (OOFAwareWidget *textblock, int y, int h); + }; + + /** + * This list is kept sorted. + * + * To prevent accessing methods of the base class in an + * uncontrolled way, the inheritance is private, not public; this + * means that all methods must be delegated (see iterator(), size() + * etc. below.) + * + * TODO Update comment: still sorted, but ... + * + * More: add() and change() may check order again. + */ + class SortedFloatsVector: private lout::container::typed::Vector<Float> + { + public: + SFVType type; + + private: + OOFFloatsMgr *oofm; + Side side; + + public: + inline SortedFloatsVector (OOFFloatsMgr *oofm, Side side, SFVType type) : + lout::container::typed::Vector<Float> (1, false) + { this->oofm = oofm; this->side = side; this->type = type; } + + int findFloatIndex (OOFAwareWidget *lastGB, int lastExtIndex); + int find (OOFAwareWidget *textblock, int y, int start, int end); + int findFirst (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex, int *lastReturn); + int findLastBeforeSideSpanningIndex (int sideSpanningIndex); + void put (Float *vloat); + + inline lout::container::typed::Iterator<Float> iterator() + { return lout::container::typed::Vector<Float>::iterator (); } + inline int size () + { return lout::container::typed::Vector<Float>::size (); } + inline Float *get (int pos) + { return lout::container::typed::Vector<Float>::get (pos); } + inline void clear () + { lout::container::typed::Vector<Float>::clear (); } + }; + + class TBInfo: public WidgetInfo + { + public: + int lineBreakWidth; + int index; // position within "tbInfos" + + TBInfo *parent; + int parentExtIndex; + + // These two values are set by sizeAllocateStart(), and they are + // accessable also within sizeAllocateEnd() for the same + // textblock, for which allocation and WAS_ALLOCATED is set + // *after* sizeAllocateEnd(). See the two functions + // wasAllocated(Widget*) and getAllocation(Widget*) (further + // down) for usage. + bool wasAllocated; + core::Allocation allocation; + int clearPosition; + + // These two lists store all floats generated by this textblock, + // as long as this textblock is not allocates. + SortedFloatsVector *leftFloatsGB, *rightFloatsGB; + + TBInfo (OOFFloatsMgr *oofm, OOFAwareWidget *textblock, + TBInfo *parent, int parentExtIndex); + ~TBInfo (); + + inline bool isNowAllocated () { + return getOOFFloatsMgr()->wasAllocated (getOOFAwareWidget ()); } + inline int getNewXCB () { + return getOOFFloatsMgr()->getAllocation (getOOFAwareWidget ())->x - + getOOFFloatsMgr()->containerAllocation.x; } + inline int getNewYCB () { + return getOOFFloatsMgr()->getAllocation (getOOFAwareWidget ())->y - + getOOFFloatsMgr()->containerAllocation.y; } + inline int getNewWidth () { + return getOOFFloatsMgr()->getAllocation (getOOFAwareWidget ())->width; } + inline int getNewHeight () { + core::Allocation *allocation = + getOOFFloatsMgr()->getAllocation (getOOFAwareWidget ()); + return allocation->ascent + allocation->descent; } + void updateAllocation (); + + inline OOFAwareWidget *getOOFAwareWidget () + { return (OOFAwareWidget*)getWidget (); } + }; + + // These two lists store all floats, in the order in which they are + // defined. Only used for iterators. + lout::container::typed::Vector<Float> *leftFloatsAll, *rightFloatsAll; + + // These two lists store all floats whose generators are already + // allocated. + SortedFloatsVector *leftFloatsCB, *rightFloatsCB; + + // These two attributes are used in the size allocation process; + // see sizeAllocateStart and sizeAllocateEnd. + int lastAllocatedLeftFloat, lastAllocatedRightFloat; + + lout::container::typed::HashTable<lout::object::TypedPointer + <dw::core::Widget>, Float> *floatsByWidget; + + lout::container::typed::Vector<TBInfo> *tbInfos; + lout::container::typed::HashTable<lout::object::TypedPointer<OOFAwareWidget>, + TBInfo> *tbInfosByOOFAwareWidget; + + int lastLeftTBIndex, lastRightTBIndex, leftFloatsMark, rightFloatsMark; + + /** + * Variant of Widget::wasAllocated(), which can also be used within + * OOFM::sizeAllocateEnd(). + */ + inline bool wasAllocated (OOFAwareWidget *textblock) { + return getOOFAwareWidget(textblock)->wasAllocated; + } + + /** + * Variant of Widget::getAllocation(), which can also be used + * within OOFM::sizeAllocateEnd(). + */ + inline core::Allocation *getAllocation (OOFAwareWidget *textblock) { + return &(getOOFAwareWidget(textblock)->allocation); + } + + void moveExternalIndices (SortedFloatsVector *list, int oldStartIndex, + int diff); + Float *findFloatByWidget (core::Widget *widget); + + void moveFromGBToCB (Side side); + void sizeAllocateFloats (Side side, int newLastAllocatedFloat); + int getGBWidthForAllocation (Float *vloat); + int calcFloatX (Float *vloat, Side side, int gbX, int gbWidth); + + bool hasRelationChanged (TBInfo *tbInfo,int *minFloatPos, + core::Widget **minFloat); + bool hasRelationChanged (TBInfo *tbInfo, Side side, int *minFloatPos, + core::Widget **minFloat); + bool hasRelationChanged (bool oldTBAlloc, + int oldTBx, int oldTBy, int oldTBw, int oldTBh, + int newTBx, int newTBy, int newTBw, int newTBh, + bool oldFlAlloc, + int oldFlx, int oldFly, int oldFlw, int oldFlh, + int newFlx, int newFly, int newFlw, int newFlh, + Side side, int *floatPos); + + void checkAllocatedFloatCollisions (Side side); + + bool doFloatsExceedCB (Side side); + bool haveExtremesChanged (Side side); + + void drawFloats (SortedFloatsVector *list, core::View *view, + core::Rectangle *area, core::DrawingContext *context); + core::Widget *getFloatWidgetAtPoint (SortedFloatsVector *list, int x, int y, + core::GettingWidgetAtPointContext + *context); + + bool collidesV (Float *vloat, Float *other, SFVType type, int *yReal, + bool useAllocation); + bool collidesH (Float *vloat, Float *other, SFVType type); + + void getFloatsListsAndSide (Float *vloat, SortedFloatsVector **listSame, + SortedFloatsVector **listOpp, Side *side); + + void getFloatsSize (core::Requisition *cbReq, Side side, int *width, + int *height); + void getFloatsExtremes (core::Extremes *cbExtr, Side side, int *minWidth, + int *maxWidth); + + TBInfo *getOOFAwareWidget (OOFAwareWidget *widget); + TBInfo *getOOFAwareWidgetWhenRegistered (OOFAwareWidget *widget); + inline bool isOOFAwareWidgetRegistered (OOFAwareWidget *widget) + { return getOOFAwareWidgetWhenRegistered (widget) != NULL; } + int getBorder (OOFAwareWidget *textblock, Side side, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex); + SortedFloatsVector *getFloatsListForOOFAwareWidget (OOFAwareWidget + *textblock, + Side side); + bool hasFloat (OOFAwareWidget *textblock, Side side, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex); + + int getFloatHeight (OOFAwareWidget *textblock, Side side, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex); + + int calcClearPosition (OOFAwareWidget *textblock); + int calcClearPosition (OOFAwareWidget *textblock, Side side); + + void ensureFloatSize (Float *vloat); + + inline static int createSubRefLeftFloat (int index) { return index << 1; } + inline static int createSubRefRightFloat (int index) + { return (index << 1) | 1; } + + inline static bool isSubRefLeftFloat (int ref) + { return ref != -1 && (ref & 1) == 0; } + inline static bool isSubRefRightFloat (int ref) + { return ref != -1 && (ref & 1) == 1; } + + inline static int getFloatIndexFromSubRef (int ref) + { return ref == -1 ? ref : (ref >> 1); } + +public: + OOFFloatsMgr (OOFAwareWidget *container); + ~OOFFloatsMgr (); + + void sizeAllocateStart (OOFAwareWidget *caller, + core::Allocation *allocation); + void sizeAllocateEnd (OOFAwareWidget *caller); + void containerSizeChangedForChildren (); + void draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context); + + void markSizeChange (int ref); + void markExtremesChange (int ref); + core::Widget *getWidgetAtPoint (int x, int y, + core::GettingWidgetAtPointContext *context); + + void addWidgetInFlow (OOFAwareWidget *textblock, OOFAwareWidget *parentBlock, + int externalIndex); + int addWidgetOOF (core::Widget *widget, OOFAwareWidget *generatingBlock, + int externalIndex); + void calcWidgetRefSize (core::Widget *widget,core::Requisition *size); + void moveExternalIndices (OOFAwareWidget *generatingBlock, int oldStartIndex, + int diff); + + void tellPosition1 (core::Widget *widget, int x, int y); + void tellPosition2 (core::Widget *widget, int x, int y); + + void getSize (core::Requisition *cbReq, int *oofWidth, int *oofHeight); + bool containerMustAdjustExtraSpace (); + void getExtremes (core::Extremes *cbExtr, + int *oofMinWidth, int *oofMaxWidth); + + int getLeftBorder (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex); + int getRightBorder (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex); + + bool hasFloatLeft (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex); + bool hasFloatRight (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex); + + int getLeftFloatHeight (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex); + int getRightFloatHeight (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex); + + bool affectsLeftBorder (core::Widget *widget); + bool affectsRightBorder (core::Widget *widget); + bool mayAffectBordersAtAll (); + + int getClearPosition (OOFAwareWidget *textblock); + + bool dealingWithSizeOfChild (core::Widget *child); + int getAvailWidthOfChild (core::Widget *child, bool forceValue); + int getAvailHeightOfChild (core::Widget *child, bool forceValue); + + int getNumWidgets (); + core::Widget *getWidget (int i); +}; + +} // namespace oof + +} // namespace dw + +#endif // __DW_OOFFLOATSMGR_HH__ diff --git a/dw/oofposabslikemgr.cc b/dw/oofposabslikemgr.cc new file mode 100644 index 00000000..bde9d71b --- /dev/null +++ b/dw/oofposabslikemgr.cc @@ -0,0 +1,427 @@ +/* + * Dillo Widget + * + * Copyright 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "oofposabslikemgr.hh" + +using namespace dw::core; +using namespace lout::misc; + +namespace dw { + +namespace oof { + +OOFPosAbsLikeMgr::OOFPosAbsLikeMgr (OOFAwareWidget *container) : + OOFPositionedMgr (container) +{ + DBG_OBJ_CREATE ("dw::OOFPosAbsLikeMgr"); +} + +OOFPosAbsLikeMgr::~OOFPosAbsLikeMgr () +{ + DBG_OBJ_DELETE (); +} + +void OOFPosAbsLikeMgr::calcWidgetRefSize (Widget *widget, Requisition *size) +{ + size->width = size->ascent = size->descent = 0; +} + +void OOFPosAbsLikeMgr::sizeAllocateChildren () +{ + DBG_OBJ_ENTER0 ("resize.oofm", 0, "sizeAllocateChildren"); + + int refWidth = container->getAvailWidth (true) - containerBoxDiffWidth (); + int refHeight = container->getAvailHeight (true) - containerBoxDiffHeight (); + + for (int i = 0; i < children->size(); i++) { + Child *child = children->get (i); + + int x, y, width, ascent, descent; + calcPosAndSizeChildOfChild (child, refWidth, refHeight, &x, &y, &width, + &ascent, &descent); + + Allocation childAllocation; + childAllocation.x = containerAllocation.x + x + containerBoxOffsetX (); + childAllocation.y = containerAllocation.y + y + containerBoxOffsetY (); + childAllocation.width = width; + childAllocation.ascent = ascent; + childAllocation.descent = descent; + + child->widget->sizeAllocate (&childAllocation); + } + + DBG_OBJ_LEAVE (); +} + +void OOFPosAbsLikeMgr::getSize (Requisition *containerReq, int *oofWidth, + int *oofHeight) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "getSize", "%d * (%d + %d)", + containerReq->width, containerReq->ascent, + containerReq->descent); + + *oofWidth = *oofHeight = 0; + + int refWidth = container->getAvailWidth (true); + int refHeight = container->getAvailHeight (true); + + for (int i = 0; i < children->size(); i++) { + Child *child = children->get(i); + + // Children whose position cannot be determined will be + // considered later in sizeAllocateEnd. + if (posXDefined (child) && posYDefined (child)) { + int x, y, width, ascent, descent; + calcPosAndSizeChildOfChild (child, refWidth, refHeight, &x, &y, &width, + &ascent, &descent); + *oofWidth = max (*oofWidth, x + width) + containerBoxDiffWidth (); + *oofHeight = + max (*oofHeight, y + ascent + descent) + containerBoxDiffHeight (); + + child->consideredForSize = true; + } else + child->consideredForSize = false; + } + + DBG_OBJ_LEAVE_VAL ("%d * %d", *oofWidth, *oofHeight); +} + +void OOFPosAbsLikeMgr::getExtremes (Extremes *containerExtr, int *oofMinWidth, + int *oofMaxWidth) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "getExtremes", "(%d / %d), ...", + containerExtr->minWidth, containerExtr->maxWidth); + + *oofMinWidth = *oofMaxWidth = 0; + + for (int i = 0; i < children->size(); i++) { + Child *child = children->get(i); + + // Children whose position cannot be determined will be + // considered later in sizeAllocateEnd. + if (posXDefined (child)) { + int x, width; + Extremes childExtr; + child->widget->getExtremes (&childExtr); + + // Here, we put the extremes of the container in relation to + // the extremes of the child, as sizes are put in relation + // for calculating the size. In one case, the allocation is + // used: when neither "left" nor "right" is set, and so the + // position told by the generator is used. + // + // If you look at the Textblock widget, you'll find that this + // is always boxOffsetX(), and the horizontal position of a + // textblock within its parent is also constant; so this is + // not a problem. + // + // (TODO What about a table cell within a table?) + + calcHPosAndSizeChildOfChild (child, containerExtr->minWidth, + childExtr.minWidth, &x, &width); + *oofMinWidth = max (*oofMinWidth, x + width); + + calcHPosAndSizeChildOfChild (child, containerExtr->maxWidth, + childExtr.maxWidth, &x, &width); + *oofMaxWidth = max (*oofMaxWidth, x + width); + + child->consideredForExtremes = true; + } else + child->consideredForExtremes = false; + } + + *oofMinWidth += containerBoxDiffWidth (); + *oofMaxWidth += containerBoxDiffWidth (); + + DBG_OBJ_MSGF ("resize.oofm", 0, "=> %d / %d", *oofMinWidth, *oofMaxWidth); + DBG_OBJ_LEAVE (); +} + +int OOFPosAbsLikeMgr::getAvailWidthOfChild (Widget *child, bool forceValue) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, + "OOFPositionedMgr/getAvailWidthOfChild", "%p, %s", + child, forceValue ? "true" : "false"); + + int width; + + if (child->getStyle()->width == style::LENGTH_AUTO && + child->getStyle()->minWidth == style::LENGTH_AUTO && + child->getStyle()->maxWidth == style::LENGTH_AUTO) { + // TODO This should (perhaps?) only used when 'width' is undefined. + // TODO Is "boxDiffWidth()" correct here? + DBG_OBJ_MSG ("resize.oofm", 1, "no specification"); + if (forceValue) { + int availWidth = container->getAvailWidth (true), left, right; + + // Regard undefined values as 0: + if (!getPosLeft (child, availWidth, &left)) left = 0; + if (!getPosRight (child, availWidth, &right)) right = 0; + + width = max (availWidth - containerBoxDiffWidth () - left - right, 0); + } else + width = -1; + } else { + if (forceValue) { + int availWidth = container->getAvailWidth (true); + child->calcFinalWidth (child->getStyle(), + availWidth - containerBoxDiffWidth (), NULL, + 0, true, &width); + } else + width = -1; + } + + if (width != -1) + width = max (width, child->getMinWidth (NULL, forceValue)); + + DBG_OBJ_MSGF ("resize.oofm", 1, "=> %d", width); + DBG_OBJ_LEAVE (); + + return width; +} + +int OOFPosAbsLikeMgr::getAvailHeightOfChild (Widget *child, bool forceValue) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, + "OOFPositionedMgr/getAvailHeightOfChild", "%p, %s", + child, forceValue ? "true" : "false"); + + int height; + + if (child->getStyle()->height == style::LENGTH_AUTO && + child->getStyle()->minHeight == style::LENGTH_AUTO && + child->getStyle()->maxHeight == style::LENGTH_AUTO) { + // TODO This should (perhaps?) only used when 'height' is undefined. + // TODO Is "boxDiffHeight()" correct here? + DBG_OBJ_MSG ("resize.oofm", 1, "no specification"); + if (forceValue) { + int availHeight = container->getAvailHeight (true), top, bottom; + + // Regard undefined values as 0: + if (!getPosTop (child, availHeight, &top)) top = 0; + if (!getPosBottom (child, availHeight, &bottom)) bottom = 0; + + height = + max (availHeight - containerBoxDiffHeight () - top - bottom, 0); + } else + height = -1; + } else { + if (forceValue) { + int availHeight = container->getAvailHeight (true); + height = child->calcHeight (child->getStyle()->height, true, + availHeight - containerBoxDiffHeight (), + NULL, true); + } else + height = -1; + } + + DBG_OBJ_MSGF ("resize.oofm", 1, "=> %d", height); + DBG_OBJ_LEAVE (); + + return height; +} + +bool OOFPosAbsLikeMgr::posXAbsolute (Child *child) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "posXAbsolute", "[%p]", child->widget); + bool b = + (style::isAbsLength (child->widget->getStyle()->left) || + style::isPerLength (child->widget->getStyle()->left)) && + (style::isAbsLength (child->widget->getStyle()->right) || + style::isPerLength (child->widget->getStyle()->right)); + DBG_OBJ_LEAVE_VAL ("%s", boolToStr (b)); + return b; +} + +bool OOFPosAbsLikeMgr::posYAbsolute (Child *child) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "posYAbsolute", "[%p]", child->widget); + bool b = + (style::isAbsLength (child->widget->getStyle()->top) || + style::isPerLength (child->widget->getStyle()->top)) && + (style::isAbsLength (child->widget->getStyle()->bottom) || + style::isPerLength (child->widget->getStyle()->bottom)); + DBG_OBJ_LEAVE_VAL ("%s", boolToStr (b)); + return b; +} + +void OOFPosAbsLikeMgr::calcPosAndSizeChildOfChild (Child *child, int refWidth, + int refHeight, int *xPtr, + int *yPtr, int *widthPtr, + int *ascentPtr, + int *descentPtr) +{ + // *xPtr and *yPtr refer to reference area; caller must adjust them. + + DBG_OBJ_ENTER ("resize.oofm", 0, "calcPosAndSizeChildOfChild", + "[%p], %d, %d, ...", child->widget, refWidth, refHeight); + + // TODO (i) Consider {min|max}-{width|heigt}. (ii) Height is always + // apportioned to descent (ascent is preserved), which makes sense + // when the children are textblocks. (iii) Consider minimal width + // (getMinWidth)? + + Requisition childRequisition; + child->widget->sizeRequest (&childRequisition); + + calcHPosAndSizeChildOfChild (child, refWidth, childRequisition.width, + xPtr, widthPtr); + calcVPosAndSizeChildOfChild (child, refHeight, childRequisition.ascent, + childRequisition.descent, yPtr, ascentPtr, + descentPtr); + + DBG_OBJ_LEAVE (); +} + +void OOFPosAbsLikeMgr::calcHPosAndSizeChildOfChild (Child *child, int refWidth, + int origChildWidth, + int *xPtr, int *widthPtr) +{ + assert (refWidth != -1 || (xPtr == NULL && widthPtr == NULL)); + + int width; + bool widthDefined; + if (style::isAbsLength (child->widget->getStyle()->width)) { + DBG_OBJ_MSGF ("resize.oofm", 1, "absolute width: %dpx", + style::absLengthVal (child->widget->getStyle()->width)); + width = style::absLengthVal (child->widget->getStyle()->width) + + child->widget->boxDiffWidth (); + widthDefined = true; + } else if (style::isPerLength (child->widget->getStyle()->width)) { + DBG_OBJ_MSGF ("resize.oofm", 1, "percentage width: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (child->widget->getStyle()->width)); + width = style::multiplyWithPerLength (refWidth, + child->widget->getStyle()->width) + + child->widget->boxDiffWidth (); + widthDefined = true; + } else { + DBG_OBJ_MSG ("resize.oofm", 1, "width not specified"); + width = origChildWidth; + widthDefined = false; + } + + int left, right; + bool leftDefined = getPosLeft (child->widget, refWidth, &left), + rightDefined = getPosRight (child->widget, refWidth, &right); + DBG_OBJ_MSGF ("resize.oofm", 1, + "=> left = %d, right = %d, width = %d (defined: %s)", + left, right, width, widthDefined ? "true" : "false"); + + if (xPtr) { + if (!leftDefined && !rightDefined) + *xPtr = generatorPosX (child) + child->x; + else { + if (!leftDefined && rightDefined) + *xPtr = refWidth - width - right - containerBoxRestWidth (); + else if (leftDefined && !rightDefined) + *xPtr = left + containerBoxOffsetX (); + else { + *xPtr = left; + if (!widthDefined) { + width = refWidth - (left + right + containerBoxDiffWidth ()); + DBG_OBJ_MSGF ("resize.oofm", 0, "=> width (corrected) = %d", + width); + } + } + } + + DBG_OBJ_MSGF ("resize.oofm", 0, "=> x = %d", *xPtr); + } + + if (widthPtr) + *widthPtr = width; +} + +void OOFPosAbsLikeMgr::calcVPosAndSizeChildOfChild (Child *child, int refHeight, + int origChildAscent, + int origChildDescent, + int *yPtr, int *ascentPtr, + int *descentPtr) +{ + assert (refHeight != -1 || + (yPtr == NULL && ascentPtr == NULL && descentPtr == NULL)); + + int ascent = origChildAscent, descent = origChildDescent; + bool heightDefined; + + if (style::isAbsLength (child->widget->getStyle()->height)) { + DBG_OBJ_MSGF ("resize.oofm", 1, "absolute height: %dpx", + style::absLengthVal (child->widget->getStyle()->height)); + int height = style::absLengthVal (child->widget->getStyle()->height) + + child->widget->boxDiffHeight (); + splitHeightPreserveAscent (height, &ascent, &descent); + heightDefined = true; + } else if (style::isPerLength (child->widget->getStyle()->height)) { + DBG_OBJ_MSGF ("resize.oofm", 1, "percentage height: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (child->widget->getStyle()->height)); + int height = + style::multiplyWithPerLength (refHeight, + child->widget->getStyle()->height) + + child->widget->boxDiffHeight (); + splitHeightPreserveAscent (height, &ascent, &descent); + heightDefined = true; + } else { + DBG_OBJ_MSG ("resize.oofm", 1, "height not specified"); + heightDefined = false; + } + + int top, bottom; + bool topDefined = getPosTop (child->widget, refHeight, &top), + bottomDefined = getPosBottom (child->widget, refHeight, &bottom); + DBG_OBJ_MSGF ("resize.oofm", 1, + "=> top = %d, bottom = %d, height = %d + %d (defined: %s)", + top, bottom, ascent, descent, + heightDefined ? "true" : "false"); + + if (yPtr) { + if (!topDefined && !bottomDefined) + *yPtr = generatorPosY (child) + child->y; + else { + if (!topDefined && bottomDefined) + *yPtr = refHeight - (ascent + descent) - bottom + - containerBoxDiffHeight (); + else if (topDefined && !bottomDefined) + *yPtr = top + containerBoxOffsetY (); + else { + *yPtr = top; + if (!heightDefined) { + int height = + refHeight - (top + bottom + containerBoxDiffHeight ()); + splitHeightPreserveAscent (height, &ascent, &descent); + DBG_OBJ_MSGF ("resize.oofm", 0, + "=> ascent + descent (corrected) = %d + %d", + ascent, descent); + } + } + } + + DBG_OBJ_MSGF ("resize.oofm", 0, "=> y = %d", *yPtr); + } + + if (ascentPtr) + *ascentPtr = ascent; + if (descentPtr) + *descentPtr = descent; +} + +} // namespace oof + +} // namespace dw diff --git a/dw/oofposabslikemgr.hh b/dw/oofposabslikemgr.hh new file mode 100644 index 00000000..f9e94a5d --- /dev/null +++ b/dw/oofposabslikemgr.hh @@ -0,0 +1,61 @@ +#ifndef __DW_OOFPOSABSLIKEMGR_HH__ +#define __DW_OOFPOSABSLIKEMGR_HH__ + +#include "oofpositionedmgr.hh" + +namespace dw { + +namespace oof { + +class OOFPosAbsLikeMgr: public OOFPositionedMgr +{ +protected: + virtual int containerBoxOffsetX () = 0; + virtual int containerBoxOffsetY () = 0; + virtual int containerBoxRestWidth () = 0; + virtual int containerBoxRestHeight () = 0; + + inline int containerBoxDiffWidth () + { return containerBoxOffsetX () + containerBoxRestWidth (); } + inline int containerBoxDiffHeight () + { return containerBoxOffsetY () + containerBoxRestHeight (); } + + bool haveExtremesChanged (); + + void sizeAllocateChildren (); + + bool posXAbsolute (Child *child); + bool posYAbsolute (Child *child); + + void calcPosAndSizeChildOfChild (Child *child, int refWidth, int refHeight, + int *xPtr, int *yPtr, int *widthPtr, + int *ascentPtr, int *descentPtr); + void calcHPosAndSizeChildOfChild (Child *child, int refWidth, + int origChildWidth, int *xPtr, + int *widthPtr); + void calcVPosAndSizeChildOfChild (Child *child, int refHeight, + int origChildAscent, int origChildDescent, + int *yPtr, int *ascentPtr, + int *descentPtr); + + +public: + OOFPosAbsLikeMgr (OOFAwareWidget *container); + ~OOFPosAbsLikeMgr (); + + void calcWidgetRefSize (core::Widget *widget, core::Requisition *size); + + void getSize (core::Requisition *containerReq, int *oofWidth, + int *oofHeight); + void getExtremes (core::Extremes *containerExtr, + int *oofMinWidth, int *oofMaxWidth); + + int getAvailWidthOfChild (core::Widget *child, bool forceValue); + int getAvailHeightOfChild (core::Widget *child, bool forceValue); +}; + +} // namespace oof + +} // namespace dw + +#endif // __DW_OOFPOSABSLIKEMGR_HH__ diff --git a/dw/oofposabsmgr.cc b/dw/oofposabsmgr.cc new file mode 100644 index 00000000..c8bc64b1 --- /dev/null +++ b/dw/oofposabsmgr.cc @@ -0,0 +1,68 @@ +/* + * Dillo Widget + * + * Copyright 2014 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "oofposabsmgr.hh" + +namespace dw { + +namespace oof { + +OOFPosAbsMgr::OOFPosAbsMgr (OOFAwareWidget *container) : + OOFPosAbsLikeMgr (container) +{ + DBG_OBJ_CREATE ("dw::OOFPosAbsMgr"); +} + +OOFPosAbsMgr::~OOFPosAbsMgr () +{ + DBG_OBJ_DELETE (); +} + +// Comment for all containerBox* implementations: for the toplevel +// widget, assume margin = border = 0 (should perhaps set so when +// widgets are constructed), so that the padding area is actually the +// allocation. + +int OOFPosAbsMgr::containerBoxOffsetX () +{ + return container->getParent () ? + container->boxOffsetX () - container->getStyle()->padding.left : 0; +} + +int OOFPosAbsMgr::containerBoxOffsetY () +{ + return container->getParent () ? + container->boxOffsetY () - container->getStyle()->padding.top : 0; +} + +int OOFPosAbsMgr::containerBoxRestWidth () +{ + return container->getParent () ? + container->boxRestWidth () - container->getStyle()->padding.right : 0; +} + +int OOFPosAbsMgr::containerBoxRestHeight () +{ + return container->getParent () ? + container->boxRestHeight () - container->getStyle()->padding.bottom : 0; +} + +} // namespace oof + +} // namespace dw diff --git a/dw/oofposabsmgr.hh b/dw/oofposabsmgr.hh new file mode 100644 index 00000000..153f8ff2 --- /dev/null +++ b/dw/oofposabsmgr.hh @@ -0,0 +1,27 @@ +#ifndef __DW_OOFPOSABSMGR_HH__ +#define __DW_OOFPOSABSMGR_HH__ + +#include "oofposabslikemgr.hh" + +namespace dw { + +namespace oof { + +class OOFPosAbsMgr: public OOFPosAbsLikeMgr +{ +protected: + int containerBoxOffsetX (); + int containerBoxOffsetY (); + int containerBoxRestWidth (); + int containerBoxRestHeight (); + +public: + OOFPosAbsMgr (OOFAwareWidget *container); + ~OOFPosAbsMgr (); +}; + +} // namespace oof + +} // namespace dw + +#endif // __DW_OOFPOSABSMGR_HH__ diff --git a/dw/oofposfixedmgr.cc b/dw/oofposfixedmgr.cc new file mode 100644 index 00000000..1b36cd5c --- /dev/null +++ b/dw/oofposfixedmgr.cc @@ -0,0 +1,59 @@ +/* + * Dillo Widget + * + * Copyright 2014 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "oofposfixedmgr.hh" + +namespace dw { + +namespace oof { + +OOFPosFixedMgr::OOFPosFixedMgr (OOFAwareWidget *container) : + OOFPosAbsLikeMgr (container) +{ + DBG_OBJ_CREATE ("dw::OOFPosFixedMgr"); +} + +OOFPosFixedMgr::~OOFPosFixedMgr () +{ + DBG_OBJ_DELETE (); +} + +int OOFPosFixedMgr::containerBoxOffsetX () +{ + return 0; +} + +int OOFPosFixedMgr::containerBoxOffsetY () +{ + return 0; +} + +int OOFPosFixedMgr::containerBoxRestWidth () +{ + return 0; +} + +int OOFPosFixedMgr::containerBoxRestHeight () +{ + return 0; +} + +} // namespace oof + +} // namespace dw diff --git a/dw/oofposfixedmgr.hh b/dw/oofposfixedmgr.hh new file mode 100644 index 00000000..3f7b9683 --- /dev/null +++ b/dw/oofposfixedmgr.hh @@ -0,0 +1,27 @@ +#ifndef __DW_OOFPOSFIXEDMGR_HH__ +#define __DW_OOFPOSFIXEDMGR_HH__ + +#include "oofposabslikemgr.hh" + +namespace dw { + +namespace oof { + +class OOFPosFixedMgr: public OOFPosAbsLikeMgr +{ +protected: + int containerBoxOffsetX (); + int containerBoxOffsetY (); + int containerBoxRestWidth (); + int containerBoxRestHeight (); + +public: + OOFPosFixedMgr (OOFAwareWidget *container); + ~OOFPosFixedMgr (); +}; + +} // namespace oof + +} // namespace dw + +#endif // __DW_OOFPOSFIXEDMGR_HH__ diff --git a/dw/oofpositionedmgr.cc b/dw/oofpositionedmgr.cc new file mode 100644 index 00000000..28852258 --- /dev/null +++ b/dw/oofpositionedmgr.cc @@ -0,0 +1,384 @@ +/* + * Dillo Widget + * + * Copyright 2013-2014 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "oofpositionedmgr.hh" +#include "../lout/debug.hh" + +using namespace lout::object; +using namespace lout::container::typed; +using namespace lout::misc; +using namespace dw::core; +using namespace dw::core::style; + +namespace dw { + +namespace oof { + +OOFPositionedMgr::Child::Child (core::Widget *widget, OOFAwareWidget *generator, + int externalIndex) +{ + this->widget = widget; + this->generator = generator; + this->externalIndex = externalIndex; + + x = y = 0; + + // Initially, this child does not actually have been considered, + // but since adding a new element will force a size/extremes + // calculation, this is equivalent. + consideredForSize = consideredForExtremes = true; +} + +OOFPositionedMgr::OOFPositionedMgr (OOFAwareWidget *container) +{ + DBG_OBJ_CREATE ("dw::OOFPositionedMgr"); + + this->container = (OOFAwareWidget*)container; + children = new Vector<Child> (1, false); + childrenByWidget = new HashTable<TypedPointer<Widget>, Child> (true, true); + + containerAllocation = *(container->getAllocation()); + + DBG_OBJ_SET_NUM ("children.size", children->size()); +} + +OOFPositionedMgr::~OOFPositionedMgr () +{ + delete children; + delete childrenByWidget; + + DBG_OBJ_DELETE (); +} + +void OOFPositionedMgr::sizeAllocateStart (OOFAwareWidget *caller, + Allocation *allocation) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateStart", + "%p, (%d, %d, %d * (%d + %d))", + caller, allocation->x, allocation->y, allocation->width, + allocation->ascent, allocation->descent); + + if (caller == container) { + if (containerAllocationState == NOT_ALLOCATED) + containerAllocationState = IN_ALLOCATION; + containerAllocation = *allocation; + } + + DBG_OBJ_LEAVE (); +} + + +void OOFPositionedMgr::sizeAllocateEnd (OOFAwareWidget *caller) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateEnd", "%p", caller); + + if (caller == container) { + sizeAllocateChildren (); + + bool extremesChanged = !allChildrenConsideredForExtremes (); + if (extremesChanged || doChildrenExceedContainer () || + !allChildrenConsideredForSize ()) + container->oofSizeChanged (extremesChanged); + + containerAllocationState = WAS_ALLOCATED; + } + + DBG_OBJ_LEAVE (); +} + +bool OOFPositionedMgr::doChildrenExceedContainer () +{ + DBG_OBJ_ENTER0 ("resize.oofm", 0, "doChildrenExceedContainer"); + + // This method is called to determine whether the *requisition* of + // the container must be recalculated. So, we check the allocations + // of the children against the *requisition* of the container, + // which may (e. g. within tables) differ from the new allocation. + // (Generally, a widget may allocated at a different size.) + + Requisition containerReq; + container->sizeRequest (&containerReq); + bool exceeds = false; + + DBG_OBJ_MSG_START (); + + for (int i = 0; i < children->size () && !exceeds; i++) { + Child *child = children->get (i); + Allocation *childAlloc = child->widget->getAllocation (); + DBG_OBJ_MSGF ("resize.oofm", 2, + "Does childAlloc = (%d, %d, %d * %d) exceed container " + "alloc+req = (%d, %d, %d * %d)?", + childAlloc->x, childAlloc->y, childAlloc->width, + childAlloc->ascent + childAlloc->descent, + containerAllocation.x, containerAllocation.y, + containerReq.width, + containerReq.ascent + containerReq.descent); + if (childAlloc->x + childAlloc->width + > containerAllocation.x + containerReq.width || + childAlloc->y + childAlloc->ascent + childAlloc->descent + > containerAllocation.y + + containerReq.ascent + containerReq.descent) { + exceeds = true; + DBG_OBJ_MSG ("resize.oofm", 2, "Yes."); + } else + DBG_OBJ_MSG ("resize.oofm", 2, "No."); + } + + DBG_OBJ_MSG_END (); + + DBG_OBJ_MSGF ("resize.oofm", 1, "=> %s", exceeds ? "true" : "false"); + DBG_OBJ_LEAVE (); + + return exceeds; +} + +void OOFPositionedMgr::containerSizeChangedForChildren () +{ + DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren"); + + for (int i = 0; i < children->size(); i++) + children->get(i)->widget->containerSizeChanged (); + + DBG_OBJ_LEAVE (); +} + +void OOFPositionedMgr::draw (View *view, Rectangle *area, + DrawingContext *context) +{ + DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d", + area->x, area->y, area->width, area->height); + + for (int i = 0; i < children->size(); i++) { + Child *child = children->get(i); + + Rectangle childArea; + if (!context->hasWidgetBeenProcessedAsInterruption (child->widget) && + !StackingContextMgr::handledByStackingContextMgr (child->widget) && + child->widget->intersects (container, area, &childArea)) + child->widget->draw (view, &childArea, context); + } + + DBG_OBJ_LEAVE (); +} + +void OOFPositionedMgr::addWidgetInFlow (OOFAwareWidget *widget, + OOFAwareWidget *parent, + int externalIndex) +{ +} + +int OOFPositionedMgr::addWidgetOOF (Widget *widget, OOFAwareWidget *generator, + int externalIndex) +{ + DBG_OBJ_ENTER ("construct.oofm", 0, "addWidgetOOF", "%p, %p, %d", + widget, generator, externalIndex); + + Child *child = new Child (widget, generator, externalIndex); + children->put (child); + childrenByWidget->put (new TypedPointer<Widget> (widget), child); + + int subRef = children->size() - 1; + DBG_OBJ_SET_NUM ("children.size", children->size()); + DBG_OBJ_ARRSET_PTR ("children", children->size() - 1, widget); + + DBG_OBJ_SET_PTR_O (widget, "<Positioned>.generator", generator); + DBG_OBJ_SET_NUM_O (widget, "<Positioned>.externalIndex", externalIndex); + + DBG_OBJ_MSGF ("construct.oofm", 1, "=> %d", subRef); + DBG_OBJ_LEAVE (); + return subRef; +} + +void OOFPositionedMgr::moveExternalIndices (OOFAwareWidget *generator, + int oldStartIndex, int diff) +{ + for (int i = 0; i < children->size (); i++) { + Child *child = children->get (i); + if (child->externalIndex >= oldStartIndex) { + child->externalIndex += diff; + DBG_OBJ_SET_NUM_O (child->widget, "<Positioned>.externalIndex", + child->externalIndex); + } + } +} + +void OOFPositionedMgr::markSizeChange (int ref) +{ +} + + +void OOFPositionedMgr::markExtremesChange (int ref) +{ +} + +Widget *OOFPositionedMgr::getWidgetAtPoint (int x, int y, + GettingWidgetAtPointContext + *context) +{ + DBG_OBJ_ENTER ("events", 0, "getWidgetAtPoint", "%d, %d", x, y); + + Widget *widgetAtPoint = NULL; + + for (int i = children->size() - 1; widgetAtPoint == NULL && i >= 0; i--) { + Widget *childWidget = children->get(i)->widget; + if (!context->hasWidgetBeenProcessedAsInterruption (childWidget) && + !StackingContextMgr::handledByStackingContextMgr (childWidget)) + widgetAtPoint = childWidget->getWidgetAtPoint (x, y, context); + } + + DBG_OBJ_MSGF ("events", 0, "=> %p", widgetAtPoint); + DBG_OBJ_LEAVE (); + + return widgetAtPoint; +} + +void OOFPositionedMgr::tellPosition1 (Widget *widget, int x, int y) +{ +} + +void OOFPositionedMgr::tellPosition2 (Widget *widget, int x, int y) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "tellPosition2", "%p, %d, %d", + widget, x, y); + + TypedPointer<Widget> key (widget); + Child *child = childrenByWidget->get (&key); + assert (child); + + child->x = x; + child->y = y; + + DBG_OBJ_SET_NUM_O (child->widget, "<Positioned>.x", x); + DBG_OBJ_SET_NUM_O (child->widget, "<Positioned>.y", y); + + DBG_OBJ_LEAVE (); +} + +bool OOFPositionedMgr::containerMustAdjustExtraSpace () +{ + return true; +} + +int OOFPositionedMgr::getLeftBorder (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex) +{ + return 0; +} + +int OOFPositionedMgr::getRightBorder (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex) +{ + return 0; +} + +bool OOFPositionedMgr::hasFloatLeft (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex) +{ + return false; +} + +bool OOFPositionedMgr::hasFloatRight (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex) +{ + return false; +} + + +int OOFPositionedMgr::getLeftFloatHeight (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, + int lastExtIndex) +{ + return 0; +} + +int OOFPositionedMgr::getRightFloatHeight (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, + int lastExtIndex) +{ + return 0; +} + +int OOFPositionedMgr::getClearPosition (OOFAwareWidget *widget) +{ + return 0; +} + +bool OOFPositionedMgr::affectsLeftBorder (Widget *widget) +{ + return false; +} + +bool OOFPositionedMgr::affectsRightBorder (Widget *widget) +{ + return false; +} + +bool OOFPositionedMgr::mayAffectBordersAtAll () +{ + return false; +} + +bool OOFPositionedMgr::dealingWithSizeOfChild (Widget *child) +{ + return true; +} + +int OOFPositionedMgr::getNumWidgets () +{ + return children->size(); +} + +Widget *OOFPositionedMgr::getWidget (int i) +{ + return children->get(i)->widget; +} + +bool OOFPositionedMgr::getPosBorder (style::Length cssValue, int refLength, + int *result) +{ + if (style::isAbsLength (cssValue)) { + *result = style::absLengthVal (cssValue); + return true; + } else if (style::isPerLength (cssValue)) { + *result = style::multiplyWithPerLength (refLength, cssValue); + return true; + } else + // "false" means "undefined": + return false; +} + +bool OOFPositionedMgr::allChildrenConsideredForSize () +{ + for (int i = 0; i < children->size(); i++) + if (!children->get(i)->consideredForSize) + return false; + return true; +} + +bool OOFPositionedMgr::allChildrenConsideredForExtremes () +{ + for (int i = 0; i < children->size(); i++) + if (!children->get(i)->consideredForExtremes) + return false; + return true; +} + +} // namespace oof + +} // namespace dw diff --git a/dw/oofpositionedmgr.hh b/dw/oofpositionedmgr.hh new file mode 100644 index 00000000..4edf11e8 --- /dev/null +++ b/dw/oofpositionedmgr.hh @@ -0,0 +1,138 @@ + #ifndef __DW_OOFPOSITIONEDMGR_HH__ +#define __DW_OOFPOSITIONEDMGR_HH__ + +#include "outofflowmgr.hh" +#include "oofawarewidget.hh" + +namespace dw { + +namespace oof { + +class OOFPositionedMgr: public OutOfFlowMgr +{ +protected: + class Child: public lout::object::Object + { + public: + core::Widget *widget; + OOFAwareWidget *generator; + int externalIndex, x, y; + bool consideredForSize, consideredForExtremes; + + Child (core::Widget *widget, OOFAwareWidget *generator, + int externalIndex); + }; + + OOFAwareWidget *container; + core::Allocation containerAllocation; + enum { NOT_ALLOCATED, IN_ALLOCATION, WAS_ALLOCATED } + containerAllocationState; + + lout::container::typed::Vector<Child> *children; + lout::container::typed::HashTable<lout::object::TypedPointer + <dw::core::Widget>, + Child> *childrenByWidget; + + inline bool getPosLeft (core::Widget *child, int availWidth, int *result) + { return getPosBorder (child->getStyle()->left, availWidth, result); } + inline bool getPosRight (core::Widget *child, int availWidth, int *result) + { return getPosBorder (child->getStyle()->right, availWidth, result); } + inline bool getPosTop (core::Widget *child, int availHeight, int *result) + { return getPosBorder (child->getStyle()->top, availHeight, result); } + inline bool getPosBottom (core::Widget *child, int availHeight, int *result) + { return getPosBorder (child->getStyle()->bottom, availHeight, result); } + + bool getPosBorder (core::style::Length cssValue, int refLength, int *result); + + bool allChildrenConsideredForSize (); + bool allChildrenConsideredForExtremes (); + + bool doChildrenExceedContainer (); + + virtual void sizeAllocateChildren () = 0; + virtual bool posXAbsolute (Child *child) = 0; + virtual bool posYAbsolute (Child *child) = 0; + + inline bool generatorPosDefined (Child *child) { + return child->generator == container || + (containerAllocationState != NOT_ALLOCATED + && child->generator->wasAllocated ()); + } + inline int generatorPosX (Child *child) { + assert (generatorPosDefined (child)); + return child->generator == container ? 0 : + child->generator->getAllocation()->x - containerAllocation.x; + } + inline int generatorPosY (Child *child) { + assert (generatorPosDefined (child)); + return child->generator == container ? 0 : + child->generator->getAllocation()->y - containerAllocation.y; + } + + inline bool posXDefined (Child *child) + { return posXAbsolute (child) || generatorPosDefined (child); } + + inline bool posYDefined (Child *child) + { return posYAbsolute (child) || generatorPosDefined (child); } + +public: + OOFPositionedMgr (OOFAwareWidget *container); + ~OOFPositionedMgr (); + + void sizeAllocateStart (OOFAwareWidget *caller, + core::Allocation *allocation); + void sizeAllocateEnd (OOFAwareWidget *caller); + + void containerSizeChangedForChildren (); + void draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context); + + void markSizeChange (int ref); + void markExtremesChange (int ref); + core::Widget *getWidgetAtPoint (int x, int y, + core::GettingWidgetAtPointContext *context); + + void addWidgetInFlow (OOFAwareWidget *widget, OOFAwareWidget *parent, + int externalIndex); + int addWidgetOOF (core::Widget *widget, OOFAwareWidget *generator, + int externalIndex); + void moveExternalIndices (OOFAwareWidget *generator, int oldStartIndex, + int diff); + + void tellPosition1 (core::Widget *widget, int x, int y); + void tellPosition2 (core::Widget *widget, int x, int y); + + bool containerMustAdjustExtraSpace (); + + int getLeftBorder (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex); + int getRightBorder (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex); + + bool hasFloatLeft (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex); + bool hasFloatRight (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex); + + int getLeftFloatHeight (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex); + int getRightFloatHeight (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex); + + int getClearPosition (OOFAwareWidget *widget); + + bool affectsLeftBorder (core::Widget *widget); + bool affectsRightBorder (core::Widget *widget); + bool mayAffectBordersAtAll (); + + bool dealingWithSizeOfChild (core::Widget *child); + + int getNumWidgets (); + core::Widget *getWidget (int i); +}; + +} // namespace oof + +} // namespace dw + +#endif // __DW_OOFPOSITIONEDMGR_HH__ diff --git a/dw/oofposrelmgr.cc b/dw/oofposrelmgr.cc new file mode 100644 index 00000000..c19eb2e7 --- /dev/null +++ b/dw/oofposrelmgr.cc @@ -0,0 +1,249 @@ +/* + * Dillo Widget + * + * Copyright 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "oofposrelmgr.hh" + +using namespace dw::core; +using namespace lout::object; +using namespace lout::misc; + +namespace dw { + +namespace oof { + +OOFPosRelMgr::OOFPosRelMgr (OOFAwareWidget *container) : + OOFPositionedMgr (container) +{ + DBG_OBJ_CREATE ("dw::OOFPosRelMgr"); +} + +OOFPosRelMgr::~OOFPosRelMgr () +{ + DBG_OBJ_DELETE (); +} + + +void OOFPosRelMgr::markSizeChange (int ref) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "markSizeChange", "%d", ref); + Child *child = children->get(ref); + DBG_OBJ_MSGF ("resize.oofm", 1, "generator = %p, externalIndex = %d", + child->generator, child->externalIndex); + child->generator->widgetRefSizeChanged (child->externalIndex); + DBG_OBJ_LEAVE (); +} + +void OOFPosRelMgr::markExtremesChange (int ref) +{ +} + +void OOFPosRelMgr::calcWidgetRefSize (Widget *widget, Requisition *size) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "calcWidgetRefSize", "%p", widget); + + widget->sizeRequest (size); + + // In some cases, the widget has been enlarged for widgets out of + // flow. Partly, this is done by adding "extra space"; however, at + // this point, the extra space is not relevant here. See + // "oofawarewidget.cc" for a calculation of RequisitionWithoutOOF. + // (Notice also that Widget::sizeRequest has to be called in all + // cases.) + + if (widget->instanceOf (OOFAwareWidget::CLASS_ID)) + *size = *((OOFAwareWidget*)widget)->getRequisitionWithoutOOF (); + + + DBG_OBJ_LEAVE_VAL ("%d * (%d + %d)", + size->width, size->ascent, size->descent); +} + + +void OOFPosRelMgr::sizeAllocateChildren () +{ + DBG_OBJ_ENTER0 ("resize.oofm", 0, "sizeAllocateChildren"); + + for (int i = 0; i < children->size (); i++) { + Child *child = children->get(i); + + Requisition childReq; + child->widget->sizeRequest (&childReq); + + Allocation childAlloc; + childAlloc.x = containerAllocation.x + getChildPosX (child); + childAlloc.y = containerAllocation.y + getChildPosY (child); + childAlloc.width = childReq.width; + childAlloc.ascent = childReq.ascent; + childAlloc.descent = childReq.descent; + child->widget->sizeAllocate (&childAlloc); + } + + DBG_OBJ_LEAVE (); +} + +void OOFPosRelMgr::getSize (Requisition *containerReq, int *oofWidth, + int *oofHeight) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "getSize", "%d * (%d + %d)", + containerReq->width, containerReq->ascent, + containerReq->descent); + + *oofWidth = *oofHeight = 0; + + for (int i = 0; i < children->size (); i++) { + Child *child = children->get(i); + + // Children whose position cannot be determined will be + // considered later in sizeAllocateEnd. + if (posXDefined (child) && posYDefined (child)) { + Requisition childReq; + child->widget->sizeRequest (&childReq); + *oofWidth = max (*oofWidth, getChildPosX (child) + childReq.width); + *oofHeight = max (*oofHeight, + getChildPosY (child) + childReq.ascent + + childReq.descent); + + child->consideredForSize = true; + } else + child->consideredForSize = false; + } + + DBG_OBJ_LEAVE_VAL ("%d * %d", *oofWidth, *oofHeight); +} + +void OOFPosRelMgr::getExtremes (Extremes *containerExtr, int *oofMinWidth, + int *oofMaxWidth) +{ + *oofMinWidth = *oofMaxWidth = 0; + + for (int i = 0; i < children->size (); i++) { + Child *child = children->get(i); + + // Children whose position cannot be determined will be + // considered later in sizeAllocateEnd. + if (posXDefined (child)) { + Extremes childExtr; + child->widget->getExtremes (&childExtr); + + // Put the extremes of the container in relation to the extremes + // of the child, as in OOFPosAbsLikeMgr::getExtremes (see + // comment there). + *oofMinWidth = max (*oofMinWidth, + getChildPosX (child, containerExtr->minWidth) + + childExtr.minWidth); + *oofMaxWidth = max (*oofMaxWidth, + getChildPosX (child, containerExtr->maxWidth) + + childExtr.maxWidth); + + child->consideredForExtremes = true; + } else + child->consideredForExtremes = false; + } +} + +bool OOFPosRelMgr::posXAbsolute (Child *child) +{ + return false; +} + +bool OOFPosRelMgr::posYAbsolute (Child *child) +{ + return false; +} + +int OOFPosRelMgr::getChildPosX (Child *child, int refWidth) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "getChildPosX", "[%p], %d", + child->widget, refWidth); + + int gx = generatorPosX (child); + int dim = getChildPosDim (child->widget->getStyle()->left, + child->widget->getStyle()->right, + child->x, + refWidth + - child->widget->getStyle()->boxDiffWidth ()); + + DBG_OBJ_LEAVE_VAL ("%d + %d = %d", gx, dim, gx + dim); + return gx + dim; +} + + +int OOFPosRelMgr::getChildPosY (Child *child, int refHeight) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "getChildPosY", "[%p], %d", + child->widget, refHeight); + + int gy = generatorPosY (child); + int dim = getChildPosDim (child->widget->getStyle()->top, + child->widget->getStyle()->bottom, + child->y, + refHeight + - child->widget->getStyle()->boxDiffHeight ()); + + DBG_OBJ_LEAVE_VAL ("%d + %d = %d", gy, dim, gy + dim); + return gy + dim; +} + +int OOFPosRelMgr::getChildPosDim (style::Length posCssValue, + style::Length negCssValue, int refPos, + int refLength) +{ + // posCssValue refers to "left" or "top", negCssValue refers to "right" or + // "bottom". The former values are preferred ("left" over "right" etc.), + // which should later depend on the CSS value "direction". + + DBG_OBJ_ENTER ("resize.oofm", 0, "getChildPosDim", + "<i>%d</i>, <i>%d</i>, %d, %d", + posCssValue, negCssValue, refPos, refLength); + + int diff; + if (getPosBorder (posCssValue, refLength, &diff)) + DBG_OBJ_MSGF ("resize.oofm", 1, "posCssValue: diff = %d", diff); + else { + if (getPosBorder (negCssValue, refLength, &diff)) { + DBG_OBJ_MSGF ("resize.oofm", 1, "negCssValue: diff = %d", diff); + diff *= -1; + } else + diff = 0; + } + + DBG_OBJ_LEAVE_VAL ("%d + %d = %d", refPos, diff, refPos + diff); + return refPos + diff; +} + +bool OOFPosRelMgr::dealingWithSizeOfChild (Widget *child) +{ + return false; +} + +int OOFPosRelMgr::getAvailWidthOfChild (Widget *child, bool forceValue) +{ + assertNotReached (); + return 0; +} + +int OOFPosRelMgr::getAvailHeightOfChild (Widget *child, bool forceValue) +{ + assertNotReached (); + return 0; +} + +} // namespace oof + +} // namespace dw diff --git a/dw/oofposrelmgr.hh b/dw/oofposrelmgr.hh new file mode 100644 index 00000000..d61c15d9 --- /dev/null +++ b/dw/oofposrelmgr.hh @@ -0,0 +1,50 @@ +#ifndef __DW_OOFPOSRELMGR_HH__ +#define __DW_OOFPOSRELMGR_HH__ + +#include "oofpositionedmgr.hh" + +namespace dw { + +namespace oof { + +class OOFPosRelMgr: public OOFPositionedMgr +{ +protected: + void sizeAllocateChildren (); + bool posXAbsolute (Child *child); + bool posYAbsolute (Child *child); + + int getChildPosX (Child *child, int refWidth); + int getChildPosY (Child *child, int refHeight); + int getChildPosDim (core::style::Length posCssValue, + core::style::Length negCssValue, int refPos, + int refLength); + + inline int getChildPosX (Child *child) + { return getChildPosX (child, container->getAvailWidth (true)); } + inline int getChildPosY (Child *child) + { return getChildPosY (child, container->getAvailHeight (true)); } + +public: + OOFPosRelMgr (OOFAwareWidget *container); + ~OOFPosRelMgr (); + + void markSizeChange (int ref); + void markExtremesChange (int ref); + void calcWidgetRefSize (core::Widget *widget, core::Requisition *size); + + void getSize (core::Requisition *containerReq, int *oofWidth, + int *oofHeight); + void getExtremes (core::Extremes *containerExtr, int *oofMinWidth, + int *oofMaxWidth); + + bool dealingWithSizeOfChild (core::Widget *child); + int getAvailWidthOfChild (core::Widget *child, bool forceValue); + int getAvailHeightOfChild (core::Widget *child, bool forceValue); +}; + +} // namespace oof + +} // namespace dw + +#endif // __DW_OOFPOSRELMGR_HH__ diff --git a/dw/outofflowmgr.cc b/dw/outofflowmgr.cc index 5081a2cb..405c5e62 100644 --- a/dw/outofflowmgr.cc +++ b/dw/outofflowmgr.cc @@ -19,2275 +19,22 @@ #include "outofflowmgr.hh" -#include "textblock.hh" +#include "oofawarewidget.hh" #include "../lout/debug.hh" -using namespace lout::object; -using namespace lout::container::typed; -using namespace lout::misc; -using namespace dw::core; -using namespace dw::core::style; namespace dw { -OutOfFlowMgr::WidgetInfo::WidgetInfo (OutOfFlowMgr *oofm, Widget *widget) -{ - this->oofm = oofm; - this->widget = widget; - wasAllocated = false; - xCB = yCB = width = height = -1; -} - -void OutOfFlowMgr::WidgetInfo::update (bool wasAllocated, int xCB, int yCB, - int width, int height) -{ - DBG_OBJ_ENTER_O ("resize.oofm", 0, widget, "update", "%s, %d, %d, %d, %d", - wasAllocated ? "true" : "false", xCB, yCB, width, height); - - this->wasAllocated = wasAllocated; - this->xCB = xCB; - this->yCB = yCB; - this->width = width; - this->height = height; - - DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.xCB", xCB); - DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.yCB", yCB); - DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.width", width); - DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.height", height); - - DBG_OBJ_LEAVE_O (widget); -} - -// ---------------------------------------------------------------------- - -OutOfFlowMgr::Float::Float (OutOfFlowMgr *oofm, Widget *widget, - Textblock *generatingBlock, int externalIndex) : - WidgetInfo (oofm, widget) -{ - this->generatingBlock = generatingBlock; - this->externalIndex = externalIndex; - - yReq = yReal = size.width = size.ascent = size.descent = 0; - dirty = sizeChangedSinceLastAllocation = true; - indexGBList = indexCBList = -1; - - // Sometimes a float with widget = NULL is created as a key; this - // is not interesting for RTFL. - if (widget) { - DBG_OBJ_SET_PTR_O (widget, "<Float>.generatingBlock", generatingBlock); - DBG_OBJ_SET_NUM_O (widget, "<Float>.externalIndex", externalIndex); - DBG_OBJ_SET_NUM_O (widget, "<Float>.yReq", yReq); - DBG_OBJ_SET_NUM_O (widget, "<Float>.yReal", yReal); - DBG_OBJ_SET_NUM_O (widget, "<Float>.size.width", size.width); - DBG_OBJ_SET_NUM_O (widget, "<Float>.size.ascent", size.ascent); - DBG_OBJ_SET_NUM_O (widget, "<Float>.size.descent", size.descent); - DBG_OBJ_SET_BOOL_O (widget, "<Float>.dirty", dirty); - DBG_OBJ_SET_BOOL_O (widget, "<Float>.sizeChangedSinceLastAllocation", - sizeChangedSinceLastAllocation); - } -} - -void OutOfFlowMgr::Float::updateAllocation () -{ - DBG_OBJ_ENTER0_O ("resize.oofm", 0, getWidget (), "updateAllocation"); - - update (isNowAllocated (), getNewXCB (), getNewYCB (), getNewWidth (), - getNewHeight ()); - - DBG_OBJ_LEAVE_O (getWidget ()); -} - -void OutOfFlowMgr::Float::intoStringBuffer(StringBuffer *sb) -{ - sb->append ("{ widget = "); - sb->appendPointer (getWidget ()); - - if (getWidget ()) { - sb->append (" ("); - sb->append (getWidget()->getClassName ()); - sb->append (")"); - } - - sb->append (", indexGBList = "); - sb->appendInt (indexGBList); - sb->append (", indexCBList = "); - sb->appendInt (indexCBList); - sb->append (", sideSpanningIndex = "); - sb->appendInt (sideSpanningIndex); - sb->append (", generatingBlock = "); - sb->appendPointer (generatingBlock); - sb->append (", yReq = "); - sb->appendInt (yReq); - sb->append (", yReal = "); - sb->appendInt (yReal); - sb->append (", size = { "); - sb->appendInt (size.width); - sb->append (" * "); - sb->appendInt (size.ascent); - sb->append (" + "); - sb->appendInt (size.descent); - sb->append (" }, dirty = "); - sb->appendBool (dirty); - sb->append (", sizeChangedSinceLastAllocation = "); - sb->appendBool (sizeChangedSinceLastAllocation); - sb->append (" }"); -} - -bool OutOfFlowMgr::Float::covers (Textblock *textblock, int y, int h) -{ - DBG_OBJ_ENTER_O ("border", 0, getOutOfFlowMgr (), "covers", - "%p, %d, %d [vloat: %p]", - textblock, y, h, getWidget ()); - - bool b; - - if (textblock == generatingBlock) { - int reqyGB = y; - int flyGB = yReal; - getOutOfFlowMgr()->ensureFloatSize (this); - int flh = size.ascent + size.descent; - b = flyGB + flh > reqyGB && flyGB < reqyGB + h; - - DBG_OBJ_MSGF_O ("border", 1, getOutOfFlowMgr (), - "for generator: reqyGB = %d, flyGB = %d, " - "flh = %d + %d = %d => %s", - reqyGB, flyGB, size.ascent, size.descent, flh, - b ? "true" : "false"); - } else { - // (If the textblock were not allocated, the GB list would have - // been choosen instead of the CB list, and so this else-branch - // would not have been not executed.) - assert (getOutOfFlowMgr()->wasAllocated (textblock)); - - if (!getWidget()->wasAllocated ()) { - DBG_OBJ_MSG_O ("border", 1, getOutOfFlowMgr (), - "not generator (not allocated) => false"); - b = false; - } else { - Allocation *tba = getOutOfFlowMgr()->getAllocation(textblock), - *fla = getWidget()->getAllocation (); - int reqyCanv = tba->y + y; - int flyCanv = fla->y; - int flh = fla->ascent + fla->descent; - b = flyCanv + flh > reqyCanv && flyCanv < reqyCanv + h; - - DBG_OBJ_MSGF_O ("border", 1, getOutOfFlowMgr (), - "not generator (allocated): reqyCanv = %d + %d = %d, " - "flyCanv = %d, flh = %d + %d = %d => %s", - tba->y, y, reqyCanv, flyCanv, - fla->ascent, fla->descent, flh, b ? "true" : "false"); - } - } - - DBG_OBJ_LEAVE_O (getOutOfFlowMgr ()); - - return b; -} - -int OutOfFlowMgr::Float::ComparePosition::compare (Object *o1, Object *o2) -{ - Float *fl1 = (Float*)o1, *fl2 = (Float*)o2; - int r; - - DBG_OBJ_ENTER_O ("border", 1, oofm, - "ComparePosition/compare", "(#%d, #%d) [refTB = %p]", - fl1->getIndex (type), fl2->getIndex (type), refTB); - - if (refTB == fl1->generatingBlock && refTB == fl2->generatingBlock) { - DBG_OBJ_MSG_O ("border", 2, oofm, "refTB is generating both floats"); - r = fl1->yReal - fl2->yReal; - } else { - DBG_OBJ_MSG_O ("border", 2, oofm, "refTB is not generating both floats"); - DBG_OBJ_MSG_START_O (oofm); - - DBG_OBJ_MSGF_O ("border", 2, oofm, "generators are %p and %p", - fl1->generatingBlock, fl2->generatingBlock); - - // (i) Floats may not yet been allocated. Non-allocated floats - // do not have an effect yet, they are considered "at the end" - // of the list. - - // (ii) Float::widget is NULL for the key used for binary - // search. In this case, Float::yReal is used instead (which is - // set in SortedFloatsVector::find, too). The generator is the - // textblock, and should be allocated. (If not, the GB list - // would have been choosen instead of the CB list, and so this - // else-branch would not have been not executed.) - - bool a1 = fl1->getWidget () ? fl1->getWidget()->wasAllocated () : true; - bool a2 = fl2->getWidget () ? fl2->getWidget()->wasAllocated () : true; - - DBG_OBJ_MSGF_O ("border", 2, oofm, - "float 1 (%p) allocated: %s; float 2 (%p) allocated: %s", - fl1->getWidget (), a1 ? "yes" : "no", fl2->getWidget (), - a2 ? "yes" : "no"); - - if (a1 && a2) { - int fly1, fly2; - - if (fl1->getWidget()) { - fly1 = fl1->getWidget()->getAllocation()->y; - DBG_OBJ_MSGF_O ("border", 2, oofm, "fly1 = %d", fly1); - } else { - assert (oofm->wasAllocated (fl1->generatingBlock)); - fly1 = oofm->getAllocation(fl1->generatingBlock)->y + fl1->yReal; - DBG_OBJ_MSGF_O ("border", 2, oofm, "fly1 = %d + %d = %d", - oofm->getAllocation(fl1->generatingBlock)->y, - fl1->yReal, fly1); - } - - if (fl2->getWidget()) { - fly2 = fl2->getWidget()->getAllocation()->y; - DBG_OBJ_MSGF_O ("border", 2, oofm, "fly2 = %d", fly2); - } else { - assert (oofm->wasAllocated (fl2->generatingBlock)); - fly2 = oofm->getAllocation(fl2->generatingBlock)->y + fl2->yReal; - DBG_OBJ_MSGF_O ("border", 2, oofm, "fly2 = %d + %d = %d", - oofm->getAllocation(fl2->generatingBlock)->y, - fl2->yReal, fly2); - } - - r = fly1 - fly2; - - DBG_OBJ_MSGF_O ("border", 2, oofm, "r = %d - %d = %d", fly1, fly2, r); - } else if (a1 && !a2) - r = -1; - else if (!a1 && a2) - r = +1; - else // if (!a1 && !a2) - return 0; - - DBG_OBJ_MSG_END_O (oofm); - } - - DBG_OBJ_MSGF_O ("border", 1, oofm, "result: %d", r); - DBG_OBJ_LEAVE_O (oofm); - return r; -} - -int OutOfFlowMgr::Float::CompareSideSpanningIndex::compare (Object *o1, - Object *o2) -{ - return ((Float*)o1)->sideSpanningIndex - ((Float*)o2)->sideSpanningIndex; -} - -int OutOfFlowMgr::Float::CompareGBAndExtIndex::compare (Object *o1, Object *o2) -{ - Float *f1 = (Float*)o1, *f2 = (Float*)o2; - int r = -123; // Compiler happiness: GCC 4.7 does not handle this?; - - DBG_OBJ_ENTER_O ("border", 1, oofm, "CompareGBAndExtIndex/compare", - "#%d -> %p/%d, #%d -> %p/#%d", - f1->getIndex (type), f1->generatingBlock, f1->externalIndex, - f2->getIndex (type), f2->generatingBlock, - f2->externalIndex); - - if (f1->generatingBlock == f2->generatingBlock) { - r = f1->externalIndex - f2->externalIndex; - DBG_OBJ_MSGF_O ("border", 2, oofm, - "(a) generating blocks equal => %d - %d = %d", - f1->externalIndex, f2->externalIndex, r); - } else { - TBInfo *t1 = oofm->getTextblock (f1->generatingBlock), - *t2 = oofm->getTextblock (f2->generatingBlock); - bool rdef = false; - - for (TBInfo *t = t1; t != NULL; t = t->parent) - if (t->parent == t2) { - rdef = true; - r = t->parentExtIndex - f2->externalIndex; - DBG_OBJ_MSGF_O ("border", 2, oofm, - "(b) %p is an achestor of %p; direct child is " - "%p (%d) => %d - %d = %d\n", - t2->getTextblock (), t1->getTextblock (), - t->getTextblock (), t->parentExtIndex, - t->parentExtIndex, f2->externalIndex, r); - } - - for (TBInfo *t = t2; !rdef && t != NULL; t = t->parent) - if (t->parent == t1) { - r = f1->externalIndex - t->parentExtIndex; - rdef = true; - DBG_OBJ_MSGF_O ("border", 2, oofm, - "(c) %p is an achestor of %p; direct child is %p " - "(%d) => %d - %d = %d\n", - t1->getTextblock (), t2->getTextblock (), - t->getTextblock (), t->parentExtIndex, - f1->externalIndex, t->parentExtIndex, r); - } - - if (!rdef) { - r = t1->index - t2->index; - DBG_OBJ_MSGF_O ("border", 2, oofm, "(d) other => %d - %d = %d", - t1->index, t2->index, r); - } - } - - DBG_OBJ_MSGF_O ("border", 2, oofm, "result: %d", r); - DBG_OBJ_LEAVE_O (oofm); - return r; -} - -int OutOfFlowMgr::SortedFloatsVector::findFloatIndex (Textblock *lastGB, - int lastExtIndex) -{ - DBG_OBJ_ENTER_O ("border", 0, oofm, "findFloatIndex", "%p, %d", - lastGB, lastExtIndex); - - Float key (oofm, NULL, lastGB, lastExtIndex); - key.setIndex (type, -1); // for debugging - Float::CompareGBAndExtIndex comparator (oofm, type); - int i = bsearch (&key, false, &comparator); - - // At position i is the next larger element, so element i should - // not included, but i - 1 returned; except if the exact element is - // found: then include it and so return i. - int r; - if (i == size()) - r = i - 1; - else { - Float *f = get (i); - if (comparator.compare (f, &key) == 0) - r = i; - else - r = i - 1; - } - - //printf ("[%p] findFloatIndex (%p, %d) => i = %d, r = %d (size = %d); " - // "in %s list %p on the %s side\n", - // oofm->containingBlock, lastGB, lastExtIndex, i, r, size (), - // type == GB ? "GB" : "CB", this, side == LEFT ? "left" : "right"); - - //for (int i = 0; i < size (); i++) { - // Float *f = get(i); - // TBInfo *t = oofm->getTextblock(f->generatingBlock); - // printf (" %d: (%p [%d, %p], %d)\n", i, f->generatingBlock, - // t->index, t->parent ? t->parent->textblock : NULL, - // get(i)->externalIndex); - //} - - DBG_OBJ_MSGF_O ("border", 1, oofm, "=> r = %d", r); - DBG_OBJ_LEAVE_O (oofm); - return r; -} +namespace oof { -int OutOfFlowMgr::SortedFloatsVector::find (Textblock *textblock, int y, - int start, int end) +OutOfFlowMgr::OutOfFlowMgr () { - DBG_OBJ_ENTER_O ("border", 0, oofm, "find", "%p, %d, %d, %d", - textblock, y, start, end); - - Float key (oofm, NULL, NULL, 0); - key.generatingBlock = textblock; - key.yReal = y; - key.setIndex (type, -1); // for debugging - Float::ComparePosition comparator (oofm, textblock, type); - int result = bsearch (&key, false, start, end, &comparator); - - DBG_OBJ_MSGF_O ("border", 1, oofm, "=> result = %d", result); - DBG_OBJ_LEAVE_O (oofm); - return result; -} - -int OutOfFlowMgr::SortedFloatsVector::findFirst (Textblock *textblock, - int y, int h, - Textblock *lastGB, - int lastExtIndex, - int *lastReturn) -{ - DBG_OBJ_ENTER_O ("border", 0, oofm, "findFirst", "%p, %d, %d, %p, %d", - textblock, y, h, lastGB, lastExtIndex); - - DBG_IF_RTFL { - DBG_OBJ_MSG_O ("border", 2, oofm, "searching in list:"); - DBG_OBJ_MSG_START_O (oofm); - - for (int i = 0; i < size(); i++) { - DBG_OBJ_MSGF_O ("border", 2, oofm, - "%d: (%p, i = %d/%d, y = %d/%d, s = (%d * (%d + %d)), " - "%s, %s, ext = %d, GB = %p); widget at (%d, %d)", - i, get(i)->getWidget (), get(i)->getIndex (type), - get(i)->sideSpanningIndex, get(i)->yReq, get(i)->yReal, - get(i)->size.width, get(i)->size.ascent, - get(i)->size.descent, - get(i)->dirty ? "dirty" : "clean", - get(i)->sizeChangedSinceLastAllocation ? "scsla" - : "sNcsla", - get(i)->externalIndex, get(i)->generatingBlock, - get(i)->getWidget()->getAllocation()->x, - get(i)->getWidget()->getAllocation()->y); - } - - DBG_OBJ_MSG_END_O (oofm); - } - - int last = findFloatIndex (lastGB, lastExtIndex); - DBG_OBJ_MSGF_O ("border", 1, oofm, "last = %d", last); - assert (last < size()); - - // If the caller wants to reuse this value: - if (lastReturn) - *lastReturn = last; - - int i = find (textblock, y, 0, last), result; - DBG_OBJ_MSGF_O ("border", 1, oofm, "i = %d", i); - - // Note: The smallest value of "i" is 0, which means that "y" is before or - // equal to the first float. The largest value is "last + 1", which means - // that "y" is after the last float. In both cases, the first or last, - // respectively, float is a candidate. Generally, both floats, before and - // at the search position, are candidates. - - if (i > 0 && get(i - 1)->covers (textblock, y, h)) - result = i - 1; - else if (i <= last && get(i)->covers (textblock, y, h)) - result = i; - else - result = -1; - - DBG_OBJ_MSGF_O ("border", 1, oofm, "=> result = %d", result); - DBG_OBJ_LEAVE_O (oofm); - return result; -} - -int OutOfFlowMgr::SortedFloatsVector::findLastBeforeSideSpanningIndex - (int sideSpanningIndex) -{ - OutOfFlowMgr::Float::CompareSideSpanningIndex comparator; - Float key (NULL, NULL, NULL, 0); - key.sideSpanningIndex = sideSpanningIndex; - return bsearch (&key, false, &comparator) - 1; -} - -void OutOfFlowMgr::SortedFloatsVector::put (Float *vloat) -{ - lout::container::typed::Vector<Float>::put (vloat); - vloat->setIndex (type, size() - 1); -} - -OutOfFlowMgr::TBInfo::TBInfo (OutOfFlowMgr *oofm, Textblock *textblock, - TBInfo *parent, int parentExtIndex) : - WidgetInfo (oofm, textblock) -{ - this->parent = parent; - this->parentExtIndex = parentExtIndex; - - leftFloatsGB = new SortedFloatsVector (oofm, LEFT, GB); - rightFloatsGB = new SortedFloatsVector (oofm, RIGHT, GB); - - wasAllocated = getWidget()->wasAllocated (); - allocation = *(getWidget()->getAllocation ()); - clearPosition = 0; -} - -OutOfFlowMgr::TBInfo::~TBInfo () -{ - delete leftFloatsGB; - delete rightFloatsGB; -} - -void OutOfFlowMgr::TBInfo::updateAllocation () -{ - DBG_OBJ_ENTER0_O ("resize.oofm", 0, getWidget (), "updateAllocation"); - - update (isNowAllocated (), getNewXCB (), getNewYCB (), getNewWidth (), - getNewHeight ()); - - DBG_OBJ_LEAVE_O (getWidget ()); -} - -OutOfFlowMgr::OutOfFlowMgr (Textblock *containingBlock) -{ - DBG_OBJ_CREATE ("dw::OutOfFlowMgr"); - - this->containingBlock = containingBlock; - - leftFloatsCB = new SortedFloatsVector (this, LEFT, CB); - rightFloatsCB = new SortedFloatsVector (this, RIGHT, CB); - - DBG_OBJ_SET_NUM ("leftFloatsCB.size", leftFloatsCB->size()); - DBG_OBJ_SET_NUM ("rightFloatsCB.size", rightFloatsCB->size()); - - leftFloatsAll = new Vector<Float> (1, true); - rightFloatsAll = new Vector<Float> (1, true); - - DBG_OBJ_SET_NUM ("leftFloatsAll.size", leftFloatsAll->size()); - DBG_OBJ_SET_NUM ("rightFloatsAll.size", rightFloatsAll->size()); - - floatsByWidget = new HashTable <TypedPointer <Widget>, Float> (true, false); - - tbInfos = new Vector<TBInfo> (1, false); - tbInfosByTextblock = - new HashTable <TypedPointer <Textblock>, TBInfo> (true, true); - - leftFloatsMark = rightFloatsMark = 0; - lastLeftTBIndex = lastRightTBIndex = 0; - - containingBlockWasAllocated = containingBlock->wasAllocated (); - containingBlockAllocation = *(containingBlock->getAllocation()); - - addWidgetInFlow (containingBlock, NULL, 0); } OutOfFlowMgr::~OutOfFlowMgr () { - //printf ("OutOfFlowMgr::~OutOfFlowMgr\n"); - - delete leftFloatsCB; - delete rightFloatsCB; - - // Order is important: tbInfosByTextblock is owner of the instances - // of TBInfo.tbInfosByTextblock - delete tbInfos; - delete tbInfosByTextblock; - - delete floatsByWidget; - - // Order is important, since the instances of Float are owned by - // leftFloatsAll and rightFloatsAll, so these should be deleted - // last. - delete leftFloatsAll; - delete rightFloatsAll; - - DBG_OBJ_DELETE (); -} - -void OutOfFlowMgr::sizeAllocateStart (Textblock *caller, Allocation *allocation) -{ - DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateStart", - "%p, (%d, %d, %d * (%d + %d))", - caller, allocation->x, allocation->y, allocation->width, - allocation->ascent, allocation->descent); - - getTextblock(caller)->allocation = *allocation; - getTextblock(caller)->wasAllocated = true; - - if (caller == containingBlock) { - // In the size allocation process, the *first* OOFM method - // called is sizeAllocateStart, with the containing block as an - // argument. So this is the correct point to initialize size - // allocation. - - containingBlockAllocation = *allocation; - containingBlockWasAllocated = true; - - // Move floats from GB lists to the one CB list. - moveFromGBToCB (LEFT); - moveFromGBToCB (RIGHT); - - // These attributes are used to keep track which floats have - // been allocated (referring to leftFloatsCB and rightFloatsCB). - lastAllocatedLeftFloat = lastAllocatedRightFloat = -1; - } - - DBG_OBJ_LEAVE (); -} - -void OutOfFlowMgr::sizeAllocateEnd (Textblock *caller) -{ - DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateEnd", "%p", caller); - - // (Later, absolutely positioned blocks have to be allocated.) - - if (caller != containingBlock) { - // Allocate all floats "before" this textblock. - sizeAllocateFloats (LEFT, leftFloatsCB->findFloatIndex (caller, -1)); - sizeAllocateFloats (RIGHT, rightFloatsCB->findFloatIndex (caller, -1)); - } - - // The checks below do not cover "clear position" in all cases, so - // this is done here separately. This position is stored in TBInfo - // and calculated at this points; changes will be noticed to the - // textblock. - TBInfo *tbInfo = getTextblock (caller); - int newClearPosition = calcClearPosition (caller); - if (newClearPosition != tbInfo->clearPosition) { - tbInfo->clearPosition = newClearPosition; - caller->clearPositionChanged (); - } - - if (caller == containingBlock) { - // In the size allocation process, the *last* OOFM method called - // is sizeAllocateEnd, with the containing block as an - // argument. So this is the correct point to finish size - // allocation. - - // Allocate all remaining floats. - sizeAllocateFloats (LEFT, leftFloatsCB->size () - 1); - sizeAllocateFloats (RIGHT, rightFloatsCB->size () - 1); - - // Check changes of both textblocks and floats allocation. (All - // is checked by hasRelationChanged (...).) - for (lout::container::typed::Iterator<TypedPointer <Textblock> > it = - tbInfosByTextblock->iterator (); - it.hasNext (); ) { - TypedPointer <Textblock> *key = it.getNext (); - TBInfo *tbInfo = tbInfosByTextblock->get (key); - Textblock *tb = key->getTypedValue(); - - int minFloatPos; - Widget *minFloat; - if (hasRelationChanged (tbInfo, &minFloatPos, &minFloat)) - tb->borderChanged (minFloatPos, minFloat); - } - - checkAllocatedFloatCollisions (LEFT); - checkAllocatedFloatCollisions (RIGHT); - - // Store some information for later use. - for (lout::container::typed::Iterator<TypedPointer <Textblock> > it = - tbInfosByTextblock->iterator (); - it.hasNext (); ) { - TypedPointer <Textblock> *key = it.getNext (); - TBInfo *tbInfo = tbInfosByTextblock->get (key); - Textblock *tb = key->getTypedValue(); - - tbInfo->updateAllocation (); - tbInfo->lineBreakWidth = tb->getLineBreakWidth (); - } - - // There are cases where some allocated floats (TODO: later also - // absolutely positioned elements?) exceed the CB allocation. - bool sizeChanged = doFloatsExceedCB (LEFT) || doFloatsExceedCB (RIGHT); - - // Similar for extremes. (TODO: here also absolutely positioned - // elements?) - bool extremesChanged = - haveExtremesChanged (LEFT) || haveExtremesChanged (RIGHT); - - for (int i = 0; i < leftFloatsCB->size(); i++) - leftFloatsCB->get(i)->updateAllocation (); - - for (int i = 0; i < rightFloatsCB->size(); i++) - rightFloatsCB->get(i)->updateAllocation (); - - if (sizeChanged || extremesChanged) - containingBlock->oofSizeChanged (extremesChanged); - } - - DBG_OBJ_LEAVE (); -} - -void OutOfFlowMgr::containerSizeChangedForChildren () -{ - DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren"); - - DBG_OBJ_MSGF ("resize", 0, "%d left floats, %d right floats", - leftFloatsAll->size (), rightFloatsAll->size ()); - - for (int i = 0; i < leftFloatsAll->size (); i++) - leftFloatsAll->get(i)->getWidget()->containerSizeChanged (); - for (int i = 0; i < rightFloatsAll->size (); i++) - rightFloatsAll->get(i)->getWidget()->containerSizeChanged (); - - DBG_OBJ_LEAVE (); -} - -bool OutOfFlowMgr::hasRelationChanged (TBInfo *tbInfo, int *minFloatPos, - Widget **minFloat) -{ - DBG_OBJ_ENTER ("resize.oofm", 0, "hasRelationChanged", - "<i>widget:</i> %p, ...", tbInfo->getWidget ()); - - int leftMinPos, rightMinPos; - Widget *leftMinFloat, *rightMinFloat; - bool c1 = - hasRelationChanged (tbInfo, LEFT, &leftMinPos, &leftMinFloat); - bool c2 = - hasRelationChanged (tbInfo, RIGHT, &rightMinPos, &rightMinFloat); - if (c1 || c2) { - if (!c1) { - *minFloatPos = rightMinPos; - *minFloat = rightMinFloat; - } else if (!c2) { - *minFloatPos = leftMinPos; - *minFloat = leftMinFloat; - } else { - if (leftMinPos < rightMinPos) { - *minFloatPos = leftMinPos; - *minFloat = leftMinFloat; - } else{ - *minFloatPos = rightMinPos; - *minFloat = rightMinFloat; - } - } - } - - if (c1 || c2) - DBG_OBJ_MSGF ("resize.oofm", 1, - "has changed: minFloatPos = %d, minFloat = %p", - *minFloatPos, *minFloat); - else - DBG_OBJ_MSG ("resize.oofm", 1, "has not changed"); - - DBG_OBJ_LEAVE (); - return c1 || c2; -} - -bool OutOfFlowMgr::hasRelationChanged (TBInfo *tbInfo, Side side, - int *minFloatPos, Widget **minFloat) -{ - DBG_OBJ_ENTER ("resize.oofm", 0, "hasRelationChanged", - "<i>widget:</i> %p, %s, ...", - tbInfo->getWidget (), side == LEFT ? "LEFT" : "RIGHT"); - - SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; - bool changed = false; - - for (int i = 0; i < list->size(); i++) { - // TODO binary search? - Float *vloat = list->get(i); - int floatPos; - - if (tbInfo->getTextblock () == vloat->generatingBlock) - DBG_OBJ_MSGF ("resize.oofm", 1, - "not checking (generating!) textblock %p against float " - "%p", tbInfo->getWidget (), vloat->getWidget ()); - else { - Allocation *gba = getAllocation (vloat->generatingBlock); - - int newFlx = calcFloatX (vloat, side, - gba->x - containingBlockAllocation.x, - getGBWidthForAllocation (vloat)); - int newFly = vloat->generatingBlock->getAllocation()->y - - containingBlockAllocation.y + vloat->yReal; - - DBG_OBJ_MSGF ("resize.oofm", 1, - "checking textblock %p against float %p", - tbInfo->getWidget (), vloat->getWidget ()); - DBG_OBJ_MSG_START (); - - if (hasRelationChanged (tbInfo->wasThenAllocated (), - tbInfo->getOldXCB (), tbInfo->getOldYCB (), - tbInfo->getNewWidth (), - tbInfo->getNewHeight (), - tbInfo->getNewXCB (), tbInfo->getNewYCB (), - tbInfo->getNewWidth (), - tbInfo->getNewHeight (), - vloat->wasThenAllocated (), - // When not allocated before, these values - // are undefined, but this does not matter, - // since they are neither used. - vloat->getOldXCB (), vloat->getOldYCB (), - vloat->getOldWidth (), vloat->getOldHeight (), - newFlx, newFly, vloat->size.width, - vloat->size.ascent + vloat->size.descent, - side, &floatPos)) { - if (!changed || floatPos < *minFloatPos) { - *minFloatPos = floatPos; - *minFloat = vloat->getWidget (); - } - changed = true; - } else - DBG_OBJ_MSG ("resize.oofm", 0, "No."); - - DBG_OBJ_MSG_END (); - } - - // All floarts are searched, to find the minimum. TODO: Are - // floats sorted, so this can be shortened? (The first is the - // minimum?) - } - - if (changed) - DBG_OBJ_MSGF ("resize.oofm", 1, - "has changed: minFloatPos = %d, minFloat = %p", - *minFloatPos, *minFloat); - else - DBG_OBJ_MSG ("resize.oofm", 1, "has not changed"); - - DBG_OBJ_LEAVE (); - return changed; -} - -/** - * \brief ... - * - * All coordinates are given relative to the CB. *floatPos is relative - * to the TB, and may be negative. - */ -bool OutOfFlowMgr::hasRelationChanged (bool oldTBAlloc, - int oldTBx, int oldTBy, int oldTBw, - int oldTBh, int newTBx, int newTBy, - int newTBw, int newTBh, - bool oldFlAlloc, - int oldFlx, int oldFly, int oldFlw, - int oldFlh, int newFlx, int newFly, - int newFlw, int newFlh, - Side side, int *floatPos) -{ - DBG_OBJ_ENTER ("resize.oofm", 0, "hasRelationChanged", - "<i>see below</i>, %s, ...", side == LEFT ? "LEFT" : "RIGHT"); - - if (oldTBAlloc) - DBG_OBJ_MSGF ("resize.oofm", 1, "old TB: %d, %d; %d * %d", - oldTBx, oldTBy, oldTBw, oldTBh); - else - DBG_OBJ_MSG ("resize.oofm", 1, "old TB: undefined"); - DBG_OBJ_MSGF ("resize.oofm", 1, "new TB: %d, %d; %d * %d", - newTBx, newTBy, newTBw, newTBh); - - if (oldFlAlloc) - DBG_OBJ_MSGF ("resize.oofm", 1, "old Fl: %d, %d; %d * %d", - oldFlx, oldFly, oldFlw, oldFlh); - else - DBG_OBJ_MSG ("resize.oofm", 1, "old Fl: undefined"); - DBG_OBJ_MSGF ("resize.oofm", 1, "new Fl: %d, %d; %d * %d", - newFlx, newFly, newFlw, newFlh); - - bool result; - if (oldTBAlloc && oldFlAlloc) { - bool oldCov = oldFly + oldFlh > oldTBy && oldFly < oldTBy + oldTBh; - bool newCov = newFly + newFlh > newTBy && newFly < newTBy + newTBh; - - DBG_OBJ_MSGF ("resize.oofm", 1, "covered? then: %s, now: %s.", - oldCov ? "yes" : "no", newCov ? "yes" : "no"); - DBG_OBJ_MSG_START (); - - if (oldCov && newCov) { - int yOld = oldFly - oldTBy, yNew = newFly - newTBy; - if (yOld == yNew) { - DBG_OBJ_MSGF ("resize.oofm", 2, - "old (%d - %d) and new (%d - %d) position equal: %d", - oldFly, oldTBy, newFly, newTBy, yOld); - - // Float position has not changed, but perhaps the amout - // how far the float reaches into the TB. (TODO: - // Generally, not only here, it could be tested whether - // the float reaches into the TB at all.) - int wOld, wNew; - if (side == LEFT) { - wOld = oldFlx + oldFlw - oldTBx; - wNew = newFlx + newFlw - newTBx; - } else { - wOld = oldTBx + oldTBw - oldFlx; - wNew = newTBx + newTBw - newFlx; - } - - DBG_OBJ_MSGF ("resize.oofm", 2, "wOld = %d, wNew = %d\n", - wOld, wNew); - - if (wOld == wNew) { - if (oldFlh == newFlh) - result = false; - else { - // Only heights of floats changed. Relevant only - // from bottoms of float. - *floatPos = min (yOld + oldFlh, yNew + newFlh); - result = true; - } - } else { - *floatPos = yOld; - result = true; - } - } else { - DBG_OBJ_MSGF ("resize.oofm", 2, - "old (%d - %d = %d) and new (%d - %d = %d) position " - "different", - oldFly, oldTBy, yOld, newFly, newTBy, yNew); - *floatPos = min (yOld, yNew); - result = true; - } - } else if (oldCov) { - *floatPos = oldFly - oldTBy; - result = true; - DBG_OBJ_MSGF ("resize.oofm", 2, - "returning old position: %d - %d = %d", oldFly, oldTBy, - *floatPos); - } else if (newCov) { - *floatPos = newFly - newTBy; - result = true; - DBG_OBJ_MSGF ("resize.oofm", 2, - "returning new position: %d - %d = %d", newFly, newTBy, - *floatPos); - } else - result = false; - - DBG_OBJ_MSG_END (); - } else { - // Not allocated before: ignore all old values, only check whether - // TB is covered by Float. - if (newFly + newFlh > newTBy && newFly < newTBy + newTBh) { - *floatPos = newFly - newTBy; - result = true; - } else - result = false; - } - - if (result) - DBG_OBJ_MSGF ("resize.oofm", 1, "has changed: floatPos = %d", - *floatPos); - else - DBG_OBJ_MSG ("resize.oofm", 1, "has not changed"); - - DBG_OBJ_LEAVE (); - - return result; -} - -void OutOfFlowMgr::checkAllocatedFloatCollisions (Side side) -{ - // In some cases, the collision detection in tellPosition() is - // based on the wrong allocations. Here (just after all Floats have - // been allocated), we correct this. - - // TODO In some cases this approach is rather slow, causing a too - // long queueResize() cascade. - - DBG_OBJ_ENTER ("resize.oofm", 0, "checkAllocatedFloatCollisions", "%s", - side == LEFT ? "LEFT" : "RIGHT"); - - SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; - SortedFloatsVector *oppList = side == LEFT ? rightFloatsCB : leftFloatsCB; - - // While iterating through the list of floats to be checked, we - // iterate equally through the list of the opposite floats, using - // this index: - int oppIndex = 0; - - for (int index = 0; index < list->size (); index++) { - Float *vloat = list->get(index); - bool needsChange = false; - int yRealNew = INT_MAX; - - // Same side. - if (index >= 1) { - Float *other = list->get(index - 1); - DBG_OBJ_MSGF ("resize.oofm", 1, - "same side: checking %p (#%d, GB: %p) against " - "%p (#%d, GB: %p)", - vloat->getWidget (), index, vloat->generatingBlock, - other->getWidget (), index - 1, other->generatingBlock); - - if (vloat->generatingBlock != other->generatingBlock) { - int yRealNewSame; - if (collidesV (vloat, other, CB, &yRealNewSame, true)) { - DBG_OBJ_MSGF ("resize.oofm", 1, - "=> collides, new yReal = %d (old: %d)", - yRealNewSame, vloat->yReal); - if (vloat->yReal != yRealNewSame) { - needsChange = true; - yRealNew = min (yRealNew, yRealNewSame); - } - } else - DBG_OBJ_MSG ("resize.oofm", 1, "=> no collision"); - } - } - - if (oppList->size () > 0) { - // Other side. Iterate to next float on the other side, - // before this float. - while (oppIndex + 1 < oppList->size () && - oppList->get(oppIndex + 1)->sideSpanningIndex - < vloat->sideSpanningIndex) - oppIndex++; - - if (oppList->get(oppIndex)->sideSpanningIndex - < vloat->sideSpanningIndex) { - int oppIndexTmp = oppIndex, yRealNewOpp; - - // Aproach is similar to tellPosition(); see comments - // there. Again, loop as long as the vertical dimensions test - // is positive (and, of course, there are floats), ... - for (bool foundColl = false; - !foundColl && oppIndexTmp >= 0 && - collidesV (vloat, oppList->get (oppIndexTmp), CB, - &yRealNewOpp, true); - oppIndexTmp--) { - DBG_OBJ_MSGF ("resize.oofm", 1, - "opposite side (after collision (v) test): " - "checking %p (#%d/%d, GB: %p) against " - "%p (#%d/%d, GB: %p)", - vloat->getWidget (), index, - vloat->sideSpanningIndex, - vloat->generatingBlock, - oppList->get(oppIndexTmp)->getWidget (), - oppList->get(oppIndexTmp)->getIndex (CB), - oppList->get(oppIndexTmp)->sideSpanningIndex, - oppList->get(oppIndexTmp)->generatingBlock); - - // ... but stop the loop as soon as the horizontal dimensions - // test is positive. - if (collidesH (vloat, oppList->get (oppIndexTmp), CB)) { - DBG_OBJ_MSGF ("resize.oofm", 1, - "=> collides (h), new yReal = %d (old: %d)", - yRealNewOpp, vloat->yReal); - foundColl = true; - if (vloat->yReal != yRealNewOpp) { - needsChange = true; - yRealNew = min (yRealNew, yRealNewOpp); - } - } else - DBG_OBJ_MSG ("resize.oofm", 1, "=> no collision (h)"); - } - } - } - - if (needsChange) - vloat->generatingBlock->borderChanged (min (vloat->yReal, yRealNew), - vloat->getWidget ()); - } - - DBG_OBJ_LEAVE (); -} - -bool OutOfFlowMgr::doFloatsExceedCB (Side side) -{ - DBG_OBJ_ENTER ("resize.oofm", 0, "doFloatsExceedCB", "%s", - side == LEFT ? "LEFT" : "RIGHT"); - - // This method is called to determine whether the *requisition* of - // the CB must be recalculated. So, we check the float allocations - // against the *requisition* of the CB, which may (e. g. within - // tables) differ from the new allocation. (Generally, a widget may - // allocated at a different size.) - core::Requisition cbReq; - containingBlock->sizeRequest (&cbReq); - - SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; - bool exceeds = false; - - DBG_OBJ_MSG_START (); - - for (int i = 0; i < list->size () && !exceeds; i++) { - Float *vloat = list->get (i); - if (vloat->getWidget()->wasAllocated ()) { - Allocation *fla = vloat->getWidget()->getAllocation (); - DBG_OBJ_MSGF ("resize.oofm", 2, - "Does FlA = (%d, %d, %d * %d) exceed CBA = " - "(%d, %d, %d * %d)?", - fla->x, fla->y, fla->width, fla->ascent + fla->descent, - containingBlockAllocation.x, containingBlockAllocation.y, - cbReq.width, cbReq.ascent + cbReq.descent); - if (fla->x + fla->width > containingBlockAllocation.x + cbReq.width || - fla->y + fla->ascent + fla->descent - > containingBlockAllocation.y + cbReq.ascent + cbReq.descent) { - exceeds = true; - DBG_OBJ_MSG ("resize.oofm", 2, "Yes."); - } else - DBG_OBJ_MSG ("resize.oofm", 2, "No."); - } - } - - DBG_OBJ_MSG_END (); - - DBG_OBJ_MSGF ("resize.oofm", 1, "=> %s", exceeds ? "true" : "false"); - DBG_OBJ_LEAVE (); - - return exceeds; -} - -bool OutOfFlowMgr::haveExtremesChanged (Side side) -{ - DBG_OBJ_ENTER ("resize.oofm", 0, "haveExtremesChanged", "%s", - side == LEFT ? "LEFT" : "RIGHT"); - - // This is quite different from doFloatsExceedCB, since there is no - // counterpart to getExtremes, as sizeAllocate is a counterpart to - // sizeRequest. So we have to determine whether the allocation has - // changed the extremes, which is done by examining the part of the - // allocation which is part of the extremes calculation (see - // getFloatsExtremes). Changes of the extremes are handled by the - // normal queueResize mechanism. - - SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; - bool changed = false; - - for (int i = 0; i < list->size () && !changed; i++) { - Float *vloat = list->get (i); - // When the GB is the CB, an allocation change does not play a - // role here. - if (vloat->generatingBlock != containingBlock) { - if (!vloat->wasThenAllocated () && vloat->isNowAllocated ()) - changed = true; - else { - // This method is called within sizeAllocateEnd, where - // containinBlock->getAllocation() (old value) and - // containinBlockAllocation (new value) are different. - - Allocation *oldCBA = containingBlock->getAllocation (); - Allocation *newCBA = &containingBlockAllocation; - - // Compare also to getFloatsExtremes. The box difference - // of the GB (from style) has not changed in this context, - // so it is ignored. - - int oldDiffLeft = vloat->getOldXCB (); - int newDiffLeft = vloat->getNewXCB (); - int oldDiffRight = - oldCBA->width - (vloat->getOldXCB () + vloat->getOldWidth ()); - int newDiffRight = - newCBA->width - (vloat->getNewXCB () + vloat->getNewWidth ()); - - if (// regarding minimum - (side == LEFT && oldDiffLeft != newDiffLeft) || - (side == RIGHT && oldDiffRight != newDiffRight) || - // regarding maximum - oldDiffLeft + oldDiffRight != newDiffLeft + newDiffRight) - changed = true; - } - } - } - - DBG_OBJ_MSGF ("resize.oofm", 1, "=> %s", changed ? "true" : "false"); - DBG_OBJ_LEAVE (); - - return changed; -} - -void OutOfFlowMgr::moveFromGBToCB (Side side) -{ - DBG_OBJ_ENTER ("oofm.resize", 0, "moveFromGBToCB", "%s", - side == LEFT ? "LEFT" : "RIGHT"); - - SortedFloatsVector *dest = side == LEFT ? leftFloatsCB : rightFloatsCB; - int *floatsMark = side == LEFT ? &leftFloatsMark : &rightFloatsMark; - - for (int mark = 0; mark <= *floatsMark; mark++) - for (lout::container::typed::Iterator<TBInfo> it = tbInfos->iterator (); - it.hasNext (); ) { - TBInfo *tbInfo = it.getNext (); - SortedFloatsVector *src = - side == LEFT ? tbInfo->leftFloatsGB : tbInfo->rightFloatsGB; - for (int i = 0; i < src->size (); i++) { - Float *vloat = src->get (i); - // "vloat->indexCBList == -1": prevent copying the vloat twice. - if (vloat->indexCBList == -1 && vloat->mark == mark) { - dest->put (vloat); - DBG_OBJ_MSGF ("oofm.resize", 1, - "moving float %p (mark %d) to CB list\n", - vloat->getWidget (), vloat->mark); - DBG_OBJ_SET_NUM (side == LEFT ? - "leftFloatsCB.size" : "rightFloatsCB.size", - dest->size()); - DBG_OBJ_ARRATTRSET_PTR (side == LEFT ? - "leftFloatsCB" : "rightFloatsCB", - dest->size() - 1, "widget", - vloat->getWidget ()); - - } - } - } - - *floatsMark = 0; - - DBG_OBJ_LEAVE (); -} - -void OutOfFlowMgr::sizeAllocateFloats (Side side, int newLastAllocatedFloat) -{ - SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; - int *lastAllocatedFloat = - side == LEFT ? &lastAllocatedLeftFloat : &lastAllocatedRightFloat; - - DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateFloats", - "%s, [%d ->] %d [size = %d]", - side == LEFT ? "LEFT" : "RIGHT", *lastAllocatedFloat, - newLastAllocatedFloat, list->size ()); - - Allocation *cba = &containingBlockAllocation; - - for (int i = *lastAllocatedFloat + 1; i <= newLastAllocatedFloat; i++) { - Float *vloat = list->get(i); - ensureFloatSize (vloat); - - Allocation *gba = getAllocation (vloat->generatingBlock); - - Allocation childAllocation; - childAllocation.x = cba->x + calcFloatX (vloat, side, gba->x - cba->x, - getGBWidthForAllocation (vloat)); - childAllocation.y = gba->y + vloat->yReal; - childAllocation.width = vloat->size.width; - childAllocation.ascent = vloat->size.ascent; - childAllocation.descent = vloat->size.descent; - - vloat->getWidget()->sizeAllocate (&childAllocation); - } - - *lastAllocatedFloat = newLastAllocatedFloat; - - DBG_OBJ_LEAVE (); -} - -// Used as argument "gbWidth" for calcFloatX(), in the context of allocation. -int OutOfFlowMgr::getGBWidthForAllocation (Float *vloat) -{ - // See comments in getFloatsSize() for a detailed rationale ... - if (containingBlock->mustBeWidenedToAvailWidth ()) - return vloat->generatingBlock->getLineBreakWidth (); - else - // ... but notice this difference: not GB width + float width is - // used, but only GB width, since the float width has already - // been included in getFloatsSize(). - return min (getAllocation(vloat->generatingBlock)->width, - vloat->generatingBlock->getLineBreakWidth ()); -} - -/** - * \brief ... - * - * gbX is given relative to the CB, as is the return value. - */ -int OutOfFlowMgr::calcFloatX (Float *vloat, Side side, int gbX, int gbWidth) -{ - DBG_OBJ_ENTER ("resize.common", 0, "calcFloatX", "%p, %s, %d, %d", - vloat->getWidget (), side == LEFT ? "LEFT" : "RIGHT", gbX, - gbWidth); - int x; - - switch (side) { - case LEFT: - // Left floats are always aligned on the left side of the - // generator (content, not allocation) ... - x = gbX + vloat->generatingBlock->getStyle()->boxOffsetX(); - DBG_OBJ_MSGF ("resize.common", 1, "left: x = %d + %d = %d", - gbX, vloat->generatingBlock->getStyle()->boxOffsetX(), x); - // ... but when the float exceeds the line break width of the - // container, it is corrected (but not left of the container). - // This way, we save space and, especially within tables, avoid - // some problems. - if (wasAllocated (containingBlock) && - x + vloat->size.width > containingBlock->getLineBreakWidth ()) { - x = max (0, containingBlock->getLineBreakWidth () - vloat->size.width); - DBG_OBJ_MSGF ("resize.common", 1, - "corrected to: max (0, %d - %d) = %d", - containingBlock->getLineBreakWidth (), vloat->size.width, - x); - } - break; - - case RIGHT: - // Similar for right floats, but in this case, floats are - // shifted to the right when they are too big (instead of - // shifting the generator to the right). - - x = max (gbX + gbWidth - vloat->size.width - - vloat->generatingBlock->getStyle()->boxRestWidth(), - // Do not exceed CB allocation: - 0); - DBG_OBJ_MSGF ("resize.common", 1, "x = max (%d + %d - %d - %d, 0) = %d", - gbX, gbWidth, vloat->size.width, - vloat->generatingBlock->getStyle()->boxRestWidth(), x); - break; - - default: - assertNotReached (); - x = 0; - break; - } - - DBG_OBJ_LEAVE (); - return x; -} - -void OutOfFlowMgr::draw (View *view, Rectangle *area) -{ - drawFloats (leftFloatsCB, view, area); - drawFloats (rightFloatsCB, view, area); -} - -void OutOfFlowMgr::drawFloats (SortedFloatsVector *list, View *view, - Rectangle *area) -{ - // This could be improved, since the list is sorted: search the - // first float fitting into the area, and iterate until one is - // found below the area. - for (int i = 0; i < list->size(); i++) { - Float *vloat = list->get(i); - Rectangle childArea; - if (vloat->getWidget()->intersects (area, &childArea)) - vloat->getWidget()->draw (view, &childArea); - } -} - -void OutOfFlowMgr::addWidgetInFlow (Textblock *textblock, - Textblock *parentBlock, int externalIndex) -{ - //printf ("[%p] addWidgetInFlow (%p, %p, %d)\n", - // containingBlock, textblock, parentBlock, externalIndex); - - TBInfo *tbInfo = - new TBInfo (this, textblock, - parentBlock ? getTextblock (parentBlock) : NULL, - externalIndex); - tbInfo->index = tbInfos->size(); - - tbInfos->put (tbInfo); - tbInfosByTextblock->put (new TypedPointer<Textblock> (textblock), tbInfo); -} - -void OutOfFlowMgr::addWidgetOOF (Widget *widget, Textblock *generatingBlock, - int externalIndex) -{ - DBG_OBJ_ENTER ("construct.oofm", 0, "addWidgetOOF", "%p, %p, %d", - widget, generatingBlock, externalIndex); - - if (isWidgetFloat (widget)) { - TBInfo *tbInfo = getTextblock (generatingBlock); - - Float *vloat = new Float (this, widget, generatingBlock, externalIndex); - - // Note: Putting the float first in the GB list, and then, - // possibly into the CB list (in that order) will trigger - // setting Float::inCBList to the right value. - - switch (widget->getStyle()->vloat) { - case FLOAT_LEFT: - leftFloatsAll->put (vloat); - DBG_OBJ_SET_NUM ("leftFloatsAll.size", leftFloatsAll->size()); - DBG_OBJ_ARRATTRSET_PTR ("leftFloatsAll", leftFloatsAll->size() - 1, - "widget", vloat->getWidget ()); - - widget->parentRef = createRefLeftFloat (leftFloatsAll->size() - 1); - DBG_OBJ_SET_NUM_O (widget, "parentRef", widget->parentRef); - tbInfo->leftFloatsGB->put (vloat); - - if (wasAllocated (generatingBlock)) { - leftFloatsCB->put (vloat); - DBG_OBJ_SET_NUM ("leftFloatsCB.size", leftFloatsCB->size()); - DBG_OBJ_ARRATTRSET_PTR ("leftFloatsCB", leftFloatsCB->size() - 1, - "widget", vloat->getWidget ()); - } else { - if (tbInfo->index < lastLeftTBIndex) - leftFloatsMark++; - - vloat->mark = leftFloatsMark; - //printf ("[%p] adding left float %p (%s %p, mark %d) to GB list " - // "(index %d, last = %d)\n", - // containingBlock, vloat, widget->getClassName(), widget, - // vloat->mark, tbInfo->index, lastLeftTBIndex); - - lastLeftTBIndex = tbInfo->index; - } - break; - - case FLOAT_RIGHT: - rightFloatsAll->put (vloat); - DBG_OBJ_SET_NUM ("rightFloatsAll.size", rightFloatsAll->size()); - DBG_OBJ_ARRATTRSET_PTR ("rightFloatsAll", rightFloatsAll->size() - 1, - "widget", vloat->getWidget ()); - - widget->parentRef = createRefRightFloat (rightFloatsAll->size() - 1); - DBG_OBJ_SET_NUM_O (widget, "parentRef", widget->parentRef); - tbInfo->rightFloatsGB->put (vloat); - - if (wasAllocated (generatingBlock)) { - rightFloatsCB->put (vloat); - DBG_OBJ_SET_NUM ("rightFloatsCB.size", rightFloatsCB->size()); - DBG_OBJ_ARRATTRSET_PTR ("rightFloatsCB", rightFloatsCB->size() - 1, - "widget", vloat->getWidget ()); - } else { - if (tbInfo->index < lastRightTBIndex) - rightFloatsMark++; - - vloat->mark = rightFloatsMark; - //printf ("[%p] adding right float %p (%s %p, mark %d) to GB list " - // "(index %d, last = %d)\n", - // containingBlock, vloat, widget->getClassName(), widget, - // vloat->mark, tbInfo->index, lastRightTBIndex); - - lastRightTBIndex = tbInfo->index; - } - - break; - - default: - assertNotReached(); - } - - // "sideSpanningIndex" is only compared, so this simple - // assignment is sufficient; differenciation between GB and CB - // lists is not neccessary. TODO: Can this also be applied to - // "index", to simplify the current code? Check: where is - // "index" used. - vloat->sideSpanningIndex = - leftFloatsAll->size() + rightFloatsAll->size() - 1; - - floatsByWidget->put (new TypedPointer<Widget> (widget), vloat); - } else - // May be extended. - assertNotReached(); - - DBG_OBJ_LEAVE (); -} - -void OutOfFlowMgr::moveExternalIndices (Textblock *generatingBlock, - int oldStartIndex, int diff) -{ - TBInfo *tbInfo = getTextblock (generatingBlock); - moveExternalIndices (tbInfo->leftFloatsGB, oldStartIndex, diff); - moveExternalIndices (tbInfo->rightFloatsGB, oldStartIndex, diff); -} - -void OutOfFlowMgr::moveExternalIndices (SortedFloatsVector *list, - int oldStartIndex, int diff) -{ - // Could be faster with binary search, but the GB (not CB!) lists - // should be rather small. - for (int i = 0; i < list->size(); i++) { - Float *vloat = list->get(i); - if (vloat->externalIndex >= oldStartIndex) { - vloat->externalIndex += diff; - DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.externalIndex", - vloat->externalIndex); - } - } -} - -OutOfFlowMgr::Float *OutOfFlowMgr::findFloatByWidget (Widget *widget) -{ - TypedPointer <Widget> key (widget); - Float *vloat = floatsByWidget->get (&key); - assert (vloat != NULL); - return vloat; -} - -void OutOfFlowMgr::markSizeChange (int ref) -{ - DBG_OBJ_ENTER ("resize.oofm", 0, "markSizeChange", "%d", ref); - - if (isRefFloat (ref)) { - Float *vloat; - - if (isRefLeftFloat (ref)) { - int i = getFloatIndexFromRef (ref); - vloat = leftFloatsAll->get (i); - //printf (" => left float %d\n", i); - } else if (isRefRightFloat (ref)) { - int i = getFloatIndexFromRef (ref); - vloat = rightFloatsAll->get (i); - //printf (" => right float %d\n", i); - } else { - assertNotReached(); - vloat = NULL; // compiler happiness - } - - vloat->dirty = vloat->sizeChangedSinceLastAllocation = true; - - DBG_OBJ_SET_BOOL_O (vloat->getWidget (), "<Float>.dirty", vloat->dirty); - DBG_OBJ_SET_BOOL_O (vloat->getWidget (), - "<Float>.sizeChangedSinceLastAllocation", - vloat->sizeChangedSinceLastAllocation); - - // The generating block is told directly about this. (Others later, in - // sizeAllocateEnd.) Could be faster (cf. hasRelationChanged, which - // differentiates many special cases), but the size is not known yet, - vloat->generatingBlock->borderChanged (vloat->yReal, vloat->getWidget ()); - } else - assertNotReached(); - - DBG_OBJ_LEAVE (); -} - - -void OutOfFlowMgr::markExtremesChange (int ref) -{ - // Nothing to do here. -} - -Widget *OutOfFlowMgr::getWidgetAtPoint (int x, int y, int level) -{ - Widget *childAtPoint = getFloatWidgetAtPoint (leftFloatsCB, x, y, level); - if (childAtPoint == NULL) - childAtPoint = getFloatWidgetAtPoint (rightFloatsCB, x, y, level); - return childAtPoint; -} - -Widget *OutOfFlowMgr::getFloatWidgetAtPoint (SortedFloatsVector *list, - int x, int y, int level) -{ - for (int i = 0; i < list->size(); i++) { - // Could use binary search to be faster. - Float *vloat = list->get(i); - if (vloat->getWidget()->wasAllocated ()) { - Widget *childAtPoint = - vloat->getWidget()->getWidgetAtPoint (x, y, level + 1); - if (childAtPoint) - return childAtPoint; - } - } - - return NULL; -} - -void OutOfFlowMgr::tellPosition (Widget *widget, int yReq) -{ - if (isWidgetFloat (widget)) - tellFloatPosition (widget, yReq); - - // Nothing to do for absolutely positioned blocks. -} - - -void OutOfFlowMgr::tellFloatPosition (Widget *widget, int yReq) -{ - DBG_OBJ_ENTER ("resize.oofm", 0, "tellFloatPosition", "%p, %d", - widget, yReq); - - assert (yReq >= 0); - - Float *vloat = findFloatByWidget(widget); - - SortedFloatsVector *listSame, *listOpp; - Side side; - getFloatsListsAndSide (vloat, &listSame, &listOpp, &side); - ensureFloatSize (vloat); - - // "yReal" may change due to collisions (see below). - vloat->yReq = vloat->yReal = yReq; - - DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.yReq", vloat->yReq); - DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.yReal", vloat->yReal); - - // Test collisions (on this side). Although there are (rare) cases - // where it could make sense, the horizontal dimensions are not - // tested; especially since searching and border calculation would - // be confused. For this reaspn, only the previous float is - // relevant. (Cf. below, collisions on the other side.) - int index = vloat->getIndex (listSame->type), yRealNew; - if (index >= 1 && - collidesV (vloat, listSame->get (index - 1), listSame->type, - &yRealNew, false)) { - vloat->yReal = yRealNew; - DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.yReal", vloat->yReal); - } - - // Test collisions (on the opposite side). There are cases when - // more than one float has to be tested. Consider the following - // HTML snippet ("id" attribute only used for simple reference - // below, as #f1, #f2, and #f3): - // - // <div style="float:left" id="f1"> - // Left left left left left left left left left left. - // </div> - // <div style="float:left" id="f2">Also left.</div> - // <div style="float:right" id="f3">Right.</div> - // - // When displayed with a suitable window width (only slightly wider - // than the text within #f1), this should look like this: - // - // --------------------------------------------------------- - // | Left left left left left left left left left left. | - // | Also left. Right. | - // --------------------------------------------------------- - // - // Consider float #f3: a collision test with #f2, considering - // vertical dimensions, is positive, but not the test with - // horizontal dimensions (because #f2 and #f3 are too - // narrow). However, a collision has to be tested with #f1; - // otherwise #f3 and #f1 would overlap. - - int oppFloatIndex = - listOpp->findLastBeforeSideSpanningIndex (vloat->sideSpanningIndex); - // Generally, the rules are simple: loop as long as the vertical - // dimensions test is positive (and, of course, there are floats), - // ... - for (bool foundColl = false; - !foundColl && oppFloatIndex >= 0 && - collidesV (vloat, listOpp->get (oppFloatIndex), listSame->type, - &yRealNew, false); - oppFloatIndex--) { - // ... but stop the loop as soon as the horizontal dimensions - // test is positive. - if (collidesH (vloat, listOpp->get (oppFloatIndex), listSame->type)) { - vloat->yReal = yRealNew; - DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.yReal", vloat->yReal); - foundColl = true; - } - } - - DBG_OBJ_MSGF ("resize.oofm", 1, "vloat->yReq = %d, vloat->yReal = %d", - vloat->yReq, vloat->yReal); - - DBG_OBJ_LEAVE (); } -bool OutOfFlowMgr::collidesV (Float *vloat, Float *other, SFVType type, - int *yReal, bool useAllocation) -{ - // Only checks vertical (possible) collisions, and only refers to - // vloat->yReal; never to vloat->allocation->y, even when the GBs are - // different. Used only in tellPosition. - - DBG_OBJ_ENTER ("resize.oofm", 0, "collidesV", "#%d [%p], #%d [%p], ...", - vloat->getIndex (type), vloat->getWidget (), - other->getIndex (type), other->getWidget ()); - - bool result; - - DBG_OBJ_MSGF ("resize.oofm", 1, "initial yReal = %d", vloat->yReal); - - if (vloat->generatingBlock == other->generatingBlock) { - ensureFloatSize (other); - int otherBottomGB = - other->yReal + other->size.ascent + other->size.descent; - - DBG_OBJ_MSGF ("resize.oofm", 1, - "same generators: otherBottomGB = %d + (%d + %d) = %d", - other->yReal, other->size.ascent, other->size.descent, - otherBottomGB); - - if (vloat->yReal < otherBottomGB) { - *yReal = otherBottomGB; - result = true; - } else - result = false; - } else { - // If the other float is not allocated, there is no collision. The - // allocation of this float (vloat) is not used at all. - if (!other->getWidget()->wasAllocated ()) - result = false; - else { - assert (wasAllocated (vloat->generatingBlock)); - Allocation *gba = getAllocation (vloat->generatingBlock), - *flaOther = other->getWidget()->getAllocation (); - - // We distinguish two cases (by different values of useAllocation): - // (i) within tellPosition, GB allocation + yReal is used for the - // y position of the other float, while (ii) in checkAllocatedFloat- - // Collisions, the float allocation is used. The latter is necessary - // by the definition of this method, the former increases performance, - // as compared to using the float allocation, in some cases, as in - // this: - // - // When '<div><div style="float:left">[Some text]</div></div>' is - // repeated n times, the resize idle function (Layout::resizeIdle) - // would be repeated roughly n times, when also in case (i) the float - // allocation is used, since for the collision test of float n with - // float n - 1, the allocation of float n - 1 does not yet reflect the - // collision test between n - 1 and n - 2, but yReal does for n - 1. - // - // On the other hand, the GB allocations will most likely more stable - // than the float allocations. - // - // Cases where this is incorrect will hopefully be rare, and, in any - // case, corrected in sizeAllocateEnd, either because hasRelation- - // Changed returns true, or in checkAllocatedFloatCollisions. - - int otherFloatY = useAllocation ? flaOther->y : - getAllocation(other->generatingBlock)->y + other->yReal; - int otherBottomGB = - otherFloatY + flaOther->ascent + flaOther->descent - gba->y; - - DBG_OBJ_MSGF ("resize.oofm", 1, - "different generators: " - "otherBottomGB = %d + (%d + %d) - %d = %d", - otherFloatY, flaOther->ascent, flaOther->descent, gba->y, - otherBottomGB); - - if (vloat->yReal < otherBottomGB) { - *yReal = otherBottomGB; - result = true; - } else - result = false; - } - } - - if (result) - DBG_OBJ_MSGF ("resize.oofm", 1, "collides: new yReal = %d", *yReal); - else - DBG_OBJ_MSG ("resize.oofm", 1, "does not collide"); - - DBG_OBJ_LEAVE (); - return result; -} - - -bool OutOfFlowMgr::collidesH (Float *vloat, Float *other, SFVType type) -{ - // Only checks horizontal collision. For a complete test, use - // collidesV (...) && collidesH (...). - bool collidesH; - - if (vloat->generatingBlock == other->generatingBlock) - collidesH = vloat->size.width + other->size.width - + vloat->generatingBlock->getStyle()->boxDiffWidth() - > vloat->generatingBlock->getLineBreakWidth(); - else { - // Again, if the other float is not allocated, there is no - // collision. Compare to collidesV. (But vloat->size is used - // here.) - if (!other->getWidget()->wasAllocated ()) - collidesH = false; - else { - assert (wasAllocated (vloat->generatingBlock)); - Allocation *gba = getAllocation (vloat->generatingBlock); - int vloatX = - calcFloatX (vloat, - vloat->getWidget()->getStyle()->vloat == FLOAT_LEFT ? - LEFT : RIGHT, - gba->x, getGBWidthForAllocation (vloat)); - - // Generally: right border of the left float > left border of - // the right float (all in canvas coordinates). - if (vloat->getWidget()->getStyle()->vloat == FLOAT_LEFT) - // "vloat" is left, "other" is right - collidesH = vloatX + vloat->size.width - > other->getWidget()->getAllocation()->x; - else - // "other" is left, "vloat" is right - collidesH = other->getWidget()->getAllocation()->x - + other->getWidget()->getAllocation()->width - > vloatX; - } - } - - return collidesH; -} - -void OutOfFlowMgr::getFloatsListsAndSide (Float *vloat, - SortedFloatsVector **listSame, - SortedFloatsVector **listOpp, - Side *side) -{ - TBInfo *tbInfo = getTextblock (vloat->generatingBlock); - - switch (vloat->getWidget()->getStyle()->vloat) { - case FLOAT_LEFT: - if (wasAllocated (vloat->generatingBlock)) { - if (listSame) *listSame = leftFloatsCB; - if (listOpp) *listOpp = rightFloatsCB; - } else { - if (listSame) *listSame = tbInfo->leftFloatsGB; - if (listOpp) *listOpp = tbInfo->rightFloatsGB; - } - if (side) *side = LEFT; - break; - - case FLOAT_RIGHT: - if (wasAllocated (vloat->generatingBlock)) { - if (listSame) *listSame = rightFloatsCB; - if (listOpp) *listOpp = leftFloatsCB; - } else { - if (listSame) *listSame = tbInfo->rightFloatsGB; - if (listOpp) *listOpp = tbInfo->leftFloatsGB; - } - if (side) *side = RIGHT; - break; - - default: - assertNotReached(); - } -} - -void OutOfFlowMgr::getSize (Requisition *cbReq, int *oofWidth, int *oofHeight) -{ - DBG_OBJ_ENTER0 ("resize.oofm", 0, "getSize"); - - int oofWidthtLeft, oofWidthRight, oofHeightLeft, oofHeightRight; - getFloatsSize (cbReq, LEFT, &oofWidthtLeft, &oofHeightLeft); - getFloatsSize (cbReq, RIGHT, &oofWidthRight, &oofHeightRight); - - *oofWidth = max (oofWidthtLeft, oofWidthRight); - *oofHeight = max (oofHeightLeft, oofHeightRight); - - DBG_OBJ_MSGF ("resize.oofm", 1, - "=> (l: %d, r: %d => %d) * (l: %d, r: %d => %d)", - oofWidthtLeft, oofWidthRight, *oofWidth, - oofHeightLeft, oofHeightRight, *oofHeight); - DBG_OBJ_LEAVE (); -} - -void OutOfFlowMgr::getFloatsSize (Requisition *cbReq, Side side, int *width, - int *height) -{ - DBG_OBJ_ENTER ("resize.oofm", 0, "getFloatsSize", "(%d * (%d + %d), %s, ...", - cbReq->width, cbReq->ascent, cbReq->descent, - side == LEFT ? "LEFT" : "RIGHT"); - - SortedFloatsVector *list = getFloatsListForTextblock (containingBlock, side); - - *width = *height = 0; - - DBG_OBJ_MSGF ("resize.oofm", 1, "%d floats on this side", list->size()); - - for (int i = 0; i < list->size(); i++) { - Float *vloat = list->get(i); - - DBG_OBJ_MSGF ("resize.oofm", 1, - "float %p has generator %p (container is %p)", - vloat->getWidget (), vloat->generatingBlock, - containingBlock); - - if (vloat->generatingBlock == containingBlock || - wasAllocated (vloat->generatingBlock)) { - ensureFloatSize (vloat); - int x, y; - - int effWidth; - if (containingBlock->mustBeWidenedToAvailWidth ()) - // For most textblocks, the line break width is used for - // calculating the x position. (This changed for GROWS, - // where the width of a textblock is often smaller that - // the line break.) - effWidth = vloat->generatingBlock->getLineBreakWidth (); - else - // For some textblocks, like inline blocks, the line break - // width would be too large for right floats in some - // cases. - // - // (i) Consider a small inline block with only a few words - // in one line, narrower that line break width minus - // float width. In this case, the sum should be used. - // - // (ii) If there is more than one line, the line break - // will already be exceeded, and so be smaller that - // GB width + float width. - effWidth = min (cbReq->width + vloat->size.width, - vloat->generatingBlock->getLineBreakWidth ()); - - if (vloat->generatingBlock == containingBlock) { - x = calcFloatX (vloat, side, 0, effWidth); - y = vloat->yReal; - } else { - Allocation *gba = getAllocation(vloat->generatingBlock); - x = calcFloatX (vloat, side, gba->x - containingBlockAllocation.x, - effWidth); - y = gba->y - containingBlockAllocation.y + vloat->yReal; - } - - *width = max (*width, x + vloat->size.width); - *height = max (*height, y + vloat->size.ascent + vloat->size.descent); - - DBG_OBJ_MSGF ("resize.oofm", 1, - "considering float %p generated by %p: (%d + %d) * " - "(%d + (%d + %d)) => %d * %d", - vloat->getWidget (), vloat->generatingBlock, - x, vloat->size.width, - y, vloat->size.ascent, vloat->size.descent, - *width, *height); - } else - DBG_OBJ_MSGF ("resize.oofm", 1, - "considering float %p generated by %p: not allocated", - vloat->getWidget (), vloat->generatingBlock); - } - - DBG_OBJ_LEAVE (); -} - -void OutOfFlowMgr::getExtremes (Extremes *cbExtr, int *oofMinWidth, - int *oofMaxWidth) -{ - DBG_OBJ_ENTER ("resize.oofm", 0, "getExtremes", "(%d / %d), ...", - cbExtr->minWidth, cbExtr->maxWidth); - - int oofMinWidthtLeft, oofMinWidthRight, oofMaxWidthLeft, oofMaxWidthRight; - getFloatsExtremes (cbExtr, LEFT, &oofMinWidthtLeft, &oofMaxWidthLeft); - getFloatsExtremes (cbExtr, RIGHT, &oofMinWidthRight, &oofMaxWidthRight); - - *oofMinWidth = max (oofMinWidthtLeft, oofMinWidthRight); - *oofMaxWidth = max (oofMaxWidthLeft, oofMaxWidthRight); - - DBG_OBJ_MSGF ("resize.oofm", 1, - "=> (l: %d, r: %d => %d) / (l: %d, r: %d => %d)", - oofMinWidthtLeft, oofMinWidthRight, *oofMinWidth, - oofMaxWidthLeft, oofMaxWidthRight, *oofMaxWidth); - DBG_OBJ_LEAVE (); -} - -void OutOfFlowMgr::getFloatsExtremes (Extremes *cbExtr, Side side, - int *minWidth, int *maxWidth) -{ - DBG_OBJ_ENTER ("resize.oofm", 0, "getFloatsExtremes", "(%d / %d), %s, ...", - cbExtr->minWidth, cbExtr->maxWidth, - side == LEFT ? "LEFT" : "RIGHT"); - - *minWidth = *maxWidth = 0; - - SortedFloatsVector *list = getFloatsListForTextblock (containingBlock, side); - DBG_OBJ_MSGF ("resize.oofm", 1, "%d floats to be examined", list->size()); - - for (int i = 0; i < list->size(); i++) { - Float *vloat = list->get(i); - - DBG_OBJ_MSGF ("resize.oofm", 1, - "float %p has generator %p (container is %p)", - vloat->getWidget (), vloat->generatingBlock, - containingBlock); - - if (vloat->generatingBlock == containingBlock || - wasAllocated (vloat->generatingBlock)) { - Extremes extr; - vloat->getWidget()->getExtremes (&extr); - - // The calculation of extremes must be kept consistent with - // getFloatsSize(). Especially this means for the *minimal* width: - // - // - The right border (difference between float and - // container) does not have to be considered (see - // getFloatsSize(). - // - // - This is also the case for the left border, as seen in - // calcFloatX() ("... but when the float exceeds the line - // break width" ...). - - *minWidth = max (*minWidth, extr.minWidth); - - // For the maximal width, borders must be considered. - - if (vloat->generatingBlock == containingBlock) - *maxWidth = - max (*maxWidth, - extr.maxWidth - + vloat->generatingBlock->getStyle()->boxDiffWidth()); - else { - Allocation *gba = getAllocation (vloat->generatingBlock); - *maxWidth = - max (*maxWidth, - extr.maxWidth - + vloat->generatingBlock->getStyle()->boxDiffWidth() - + max (containingBlockAllocation.width - gba->width, 0)); - } - - DBG_OBJ_MSGF ("resize.oofm", 1, "%d / %d => %d / %d", - extr.minWidth, extr.maxWidth, *minWidth, *maxWidth); - } else - DBG_OBJ_MSG ("resize.oofm", 1, "not allocated"); - } - - DBG_OBJ_LEAVE (); -} - -OutOfFlowMgr::TBInfo *OutOfFlowMgr::getTextblock (Textblock *textblock) -{ - TypedPointer<Textblock> key (textblock); - TBInfo *tbInfo = tbInfosByTextblock->get (&key); - assert (tbInfo); - return tbInfo; -} - -/** - * Get the left border for the vertical position of *y*, for a height - * of *h", based on floats; relative to the allocation of the calling - * textblock. - * - * The border includes marging/border/padding of the calling textblock - * but is 0 if there is no float, so a caller should also consider - * other borders. - */ -int OutOfFlowMgr::getLeftBorder (Textblock *textblock, int y, int h, - Textblock *lastGB, int lastExtIndex) -{ - int b = getBorder (textblock, LEFT, y, h, lastGB, lastExtIndex); - DBG_OBJ_MSGF ("border", 0, "left border (%p, %d, %d, %p, %d) => %d", - textblock, y, h, lastGB, lastExtIndex, b); - return b; -} - -/** - * Get the right border for the vertical position of *y*, for a height - * of *h*, based on floats. - * - * See also getLeftBorder(int, int); - */ -int OutOfFlowMgr::getRightBorder (Textblock *textblock, int y, int h, - Textblock *lastGB, int lastExtIndex) -{ - int b = getBorder (textblock, RIGHT, y, h, lastGB, lastExtIndex); - DBG_OBJ_MSGF ("border", 0, "right border (%p, %d, %d, %p, %d) => %d", - textblock, y, h, lastGB, lastExtIndex, b); - return b; -} - -int OutOfFlowMgr::getBorder (Textblock *textblock, Side side, int y, int h, - Textblock *lastGB, int lastExtIndex) -{ - DBG_OBJ_ENTER ("border", 0, "getBorder", "%p, %s, %d, %d, %p, %d", - textblock, side == LEFT ? "LEFT" : "RIGHT", y, h, - lastGB, lastExtIndex); - - SortedFloatsVector *list = getFloatsListForTextblock (textblock, side); - int last; - int first = list->findFirst (textblock, y, h, lastGB, lastExtIndex, &last); - - DBG_OBJ_MSGF ("border", 1, "first = %d", first); - - if (first == -1) { - // No float. - DBG_OBJ_LEAVE (); - return 0; - } else { - // It is not sufficient to find the first float, since a line - // (with height h) may cover the region of multiple float, of - // which the widest has to be choosen. - int border = 0; - bool covers = true; - - // We are not searching until the end of the list, but until the - // float defined by lastGB and lastExtIndex. - for (int i = first; covers && i <= last; i++) { - Float *vloat = list->get(i); - covers = vloat->covers (textblock, y, h); - DBG_OBJ_MSGF ("border", 1, "float %d (%p) covers? %s.", - i, vloat->getWidget(), covers ? "<b>yes</b>" : "no"); - - if (covers) { - int thisBorder; - if (vloat->generatingBlock == textblock) { - int borderIn = side == LEFT ? - vloat->generatingBlock->getStyle()->boxOffsetX() : - vloat->generatingBlock->getStyle()->boxRestWidth(); - thisBorder = vloat->size.width + borderIn; - DBG_OBJ_MSGF ("border", 1, "GB: thisBorder = %d + %d = %d", - vloat->size.width, borderIn, thisBorder); - } else { - assert (wasAllocated (vloat->generatingBlock)); - assert (vloat->getWidget()->wasAllocated ()); - - Allocation *tba = getAllocation(textblock), - *fla = vloat->getWidget()->getAllocation (); - if (side == LEFT) { - thisBorder = fla->x + fla->width - tba->x; - DBG_OBJ_MSGF ("border", 1, - "not GB: thisBorder = %d + %d - %d = %d", - fla->x, fla->width, tba->x, thisBorder); - } else { - // See also calcFloatX. - thisBorder = - tba->x + textblock->getLineBreakWidth () - fla->x; - DBG_OBJ_MSGF ("border", 1, - "not GB: thisBorder = %d + %d - %d " - "= %d", - tba->x, textblock->getLineBreakWidth (), fla->x, - thisBorder); - } - } - - border = max (border, thisBorder); - DBG_OBJ_MSGF ("border", 1, "=> border = %d", border); - } - } - - DBG_OBJ_LEAVE (); - return border; - } -} - - -OutOfFlowMgr::SortedFloatsVector *OutOfFlowMgr::getFloatsListForTextblock - (Textblock *textblock, Side side) -{ - DBG_OBJ_ENTER ("oofm.common", 1, "getFloatsListForTextblock", "%p, %s", - textblock, side == LEFT ? "LEFT" : "RIGHT"); - - OutOfFlowMgr::SortedFloatsVector *list; - - if (wasAllocated (textblock)) { - DBG_OBJ_MSG ("oofm.common", 2, "returning <b>CB</b> list"); - list = side == LEFT ? leftFloatsCB : rightFloatsCB; - } else { - DBG_OBJ_MSG ("oofm.common", 2, "returning <b>GB</b> list"); - TBInfo *tbInfo = getTextblock (textblock); - list = side == LEFT ? tbInfo->leftFloatsGB : tbInfo->rightFloatsGB; - } - - DBG_OBJ_LEAVE (); - return list; -} - - -bool OutOfFlowMgr::hasFloatLeft (Textblock *textblock, int y, int h, - Textblock *lastGB, int lastExtIndex) -{ - bool b = hasFloat (textblock, LEFT, y, h, lastGB, lastExtIndex); - DBG_OBJ_MSGF ("border", 0, "has float left (%p, %d, %d, %p, %d) => %s", - textblock, y, h, lastGB, lastExtIndex, b ? "true" : "false"); - return b; -} - -bool OutOfFlowMgr::hasFloatRight (Textblock *textblock, int y, int h, - Textblock *lastGB, int lastExtIndex) -{ - bool b = hasFloat (textblock, RIGHT, y, h, lastGB, lastExtIndex); - DBG_OBJ_MSGF ("border", 0, "has float right (%p, %d, %d, %p, %d) => %s", - textblock, y, h, lastGB, lastExtIndex, b ? "true" : "false"); - return b; -} - -bool OutOfFlowMgr::hasFloat (Textblock *textblock, Side side, int y, int h, - Textblock *lastGB, int lastExtIndex) -{ - DBG_OBJ_ENTER ("border", 0, "hasFloat", "%p, %s, %d, %d, %p, %d", - textblock, side == LEFT ? "LEFT" : "RIGHT", y, h, - lastGB, lastExtIndex); - - SortedFloatsVector *list = getFloatsListForTextblock (textblock, side); - int first = list->findFirst (textblock, y, h, lastGB, lastExtIndex, NULL); - - DBG_OBJ_MSGF ("border", 1, "first = %d", first); - DBG_OBJ_LEAVE (); - return first != -1; -} - -int OutOfFlowMgr::getLeftFloatHeight (Textblock *textblock, int y, int h, - Textblock *lastGB, int lastExtIndex) -{ - return getFloatHeight (textblock, LEFT, y, h, lastGB, lastExtIndex); -} - -int OutOfFlowMgr::getRightFloatHeight (Textblock *textblock, int y, int h, - Textblock *lastGB, int lastExtIndex) -{ - return getFloatHeight (textblock, RIGHT, y, h, lastGB, lastExtIndex); -} - -// Calculate height from the position *y*. -int OutOfFlowMgr::getFloatHeight (Textblock *textblock, Side side, int y, int h, - Textblock *lastGB, int lastExtIndex) -{ - DBG_OBJ_ENTER ("border", 0, "getFloatHeight", "%p, %s, %d, %d, %p, %d", - textblock, side == LEFT ? "LEFT" : "RIGHT", y, h, - lastGB, lastExtIndex); - - SortedFloatsVector *list = getFloatsListForTextblock (textblock, side); - int first = list->findFirst (textblock, y, h, lastGB, lastExtIndex, NULL); - assert (first != -1); /* This method must not be called when there is no - float on the respective side. */ - - Float *vloat = list->get(first); - int yRelToFloat; - - if (vloat->generatingBlock == textblock) { - yRelToFloat = y - vloat->yReal; - DBG_OBJ_MSGF ("border", 1, "caller is CB: yRelToFloat = %d - %d = %d", - y, vloat->yReal, yRelToFloat); - } else { - // The respective widgets are allocated; otherwise, hasFloat() would have - // returned false. - assert (wasAllocated (textblock)); - assert (vloat->getWidget()->wasAllocated ()); - - Allocation *tba = getAllocation(textblock), - *fla = vloat->getWidget()->getAllocation (); - yRelToFloat = tba->y + y - fla->y; - - DBG_OBJ_MSGF ("border", 1, - "caller is not CB: yRelToFloat = %d + %d - %d = %d", - tba->y, y, fla->y, yRelToFloat); - } - - ensureFloatSize (vloat); - int height = vloat->size.ascent + vloat->size.descent - yRelToFloat; - - DBG_OBJ_MSGF ("border", 1, "=> (%d + %d) - %d = %d", - vloat->size.ascent, vloat->size.descent, yRelToFloat, height); - DBG_OBJ_LEAVE (); - return height; -} - -/** - * Returns position relative to the textblock "tb". - */ -int OutOfFlowMgr::getClearPosition (Textblock *tb) -{ - return getTextblock(tb)->clearPosition; -} - -int OutOfFlowMgr::calcClearPosition (Textblock *tb) -{ - DBG_OBJ_ENTER ("resize.oofm", 0, "getClearPosition", "%p", tb); - - int pos; - - if (tb->getStyle()) { - bool left = false, right = false; - switch (tb->getStyle()->clear) { - case CLEAR_NONE: break; - case CLEAR_LEFT: left = true; break; - case CLEAR_RIGHT: right = true; break; - case CLEAR_BOTH: left = right = true; break; - default: assertNotReached (); - } - - pos = max (left ? calcClearPosition (tb, LEFT) : 0, - right ? calcClearPosition (tb, RIGHT) : 0); - } else - pos = 0; - - DBG_OBJ_MSGF ("resize.oofm", 1, "=> %d", pos); - DBG_OBJ_LEAVE (); - - return pos; -} - -int OutOfFlowMgr::calcClearPosition (Textblock *tb, Side side) -{ - DBG_OBJ_ENTER ("resize.oofm", 0, "getClearPosition", "%p, %s", - tb, side == LEFT ? "LEFT" : "RIGHT"); - - int pos; - - if (!wasAllocated (tb)) - // There is no relation yet to floats generated by other - // textblocks, and this textblocks floats are unimportant for - // the "clear" property. - pos = 0; - else { - SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; - - // Search the last float before (therfore -1) this textblock. - int i = list->findFloatIndex (tb, -1); - if (i < 0) { - pos = 0; - DBG_OBJ_MSG ("resize.oofm", 1, "no float"); - } else { - Float *vloat = list->get(i); - assert (vloat->generatingBlock != tb); - if (!wasAllocated (vloat->generatingBlock)) - pos = 0; // See above. - else { - ensureFloatSize (vloat); - pos = max (getAllocation(vloat->generatingBlock)->y + vloat->yReal - + vloat->size.ascent + vloat->size.descent - - getAllocation(tb)->y, - 0); - DBG_OBJ_MSGF ("resize.oofm", 1, - "float %p => max (%d + %d + (%d + %d) - %d, 0)", - vloat->getWidget (), - getAllocation(vloat->generatingBlock)->y, - vloat->yReal, vloat->size.ascent, vloat->size.descent, - getAllocation(tb)->y); - } - } - } - - DBG_OBJ_MSGF ("resize.oofm", 1, "=> %d", pos); - DBG_OBJ_LEAVE (); - - return pos; -} - -void OutOfFlowMgr::ensureFloatSize (Float *vloat) -{ - // Historical note: relative sizes (e. g. percentages) are already - // handled by (at this time) Layout::containerSizeChanged, so - // Float::dirty will be set. - - DBG_OBJ_ENTER ("resize.oofm", 0, "ensureFloatSize", "%p", - vloat->getWidget ()); - - if (vloat->dirty) { - DBG_OBJ_MSG ("resize.oofm", 1, "dirty: recalculation"); - - vloat->getWidget()->sizeRequest (&vloat->size); - vloat->cbLineBreakWidth = containingBlock->getLineBreakWidth (); - vloat->dirty = false; - DBG_OBJ_SET_BOOL_O (vloat->getWidget (), "<Float>.dirty", vloat->dirty); - - DBG_OBJ_MSGF ("resize.oofm", 1, "size: %d * (%d + %d)", - vloat->size.width, vloat->size.ascent, vloat->size.descent); - - DBG_OBJ_SET_NUM_O (vloat->getWidget(), "<Float>.size.width", - vloat->size.width); - DBG_OBJ_SET_NUM_O (vloat->getWidget(), "<Float>.size.ascent", - vloat->size.ascent); - DBG_OBJ_SET_NUM_O (vloat->getWidget(), "<Float>.size.descent", - vloat->size.descent); - - // "sizeChangedSinceLastAllocation" is reset in sizeAllocateEnd() - } - - DBG_OBJ_LEAVE (); -} +} // namespace oof } // namespace dw diff --git a/dw/outofflowmgr.hh b/dw/outofflowmgr.hh index 78146d08..9f8bf870 100644 --- a/dw/outofflowmgr.hh +++ b/dw/outofflowmgr.hh @@ -5,431 +5,99 @@ namespace dw { -class Textblock; +/** + * \brief Out Of Flow. See \ref dw-out-of-flow. + */ +namespace oof { + +class OOFAwareWidget; /** - * \brief Represents additional data for containing blocks. + * \brief Represents additional data for OOF containers. */ class OutOfFlowMgr { - friend class WidgetInfo; - -private: - enum Side { LEFT, RIGHT }; - enum SFVType { GB, CB }; - - Textblock *containingBlock; - - // These two values are redundant to TBInfo::wasAllocated and - // TBInfo::allocation, for some special cases. - bool containingBlockWasAllocated; - core::Allocation containingBlockAllocation; - - class WidgetInfo: public lout::object::Object - { - private: - bool wasAllocated; - int xCB, yCB; // relative to the containing block - int width, height; - - OutOfFlowMgr *oofm; - core::Widget *widget; - - protected: - OutOfFlowMgr *getOutOfFlowMgr () { return oofm; } - - public: - WidgetInfo (OutOfFlowMgr *oofm, core::Widget *widget); - - inline bool wasThenAllocated () { return wasAllocated; } - inline int getOldXCB () { return xCB; } - inline int getOldYCB () { return yCB; } - inline int getOldWidth () { return width; } - inline int getOldHeight () { return height; } - - - void update (bool wasAllocated, int xCB, int yCB, int width, int height); - - inline core::Widget *getWidget () { return widget; } - }; - - class Float: public WidgetInfo - { - public: - class ComparePosition: public lout::object::Comparator - { - private: - OutOfFlowMgr *oofm; - Textblock *refTB; - SFVType type; // actually only used for debugging - - public: - ComparePosition (OutOfFlowMgr *oofm, Textblock *refTB, SFVType type) - { this->oofm = oofm; this->refTB = refTB; this->type = type; } - int compare(Object *o1, Object *o2); - }; - - class CompareSideSpanningIndex: public lout::object::Comparator - { - public: - int compare(Object *o1, Object *o2); - }; - - class CompareGBAndExtIndex: public lout::object::Comparator - { - private: - OutOfFlowMgr *oofm; - SFVType type; // actually only used for debugging - - public: - CompareGBAndExtIndex (OutOfFlowMgr *oofm, SFVType type) - { this->oofm = oofm; this->type = type; } - int compare(Object *o1, Object *o2); - }; - - Textblock *generatingBlock; - int externalIndex; - int yReq, yReal; // relative to generator, not container - int indexGBList; /* Refers to TBInfo::leftFloatsGB or - TBInfo::rightFloatsGB, respectively. -1 - initially. */ - int indexCBList; /* Refers to leftFloatsCB or rightFloatsCB, - respectively. -1 initially. */ - int sideSpanningIndex, mark; - core::Requisition size; - int cbLineBreakWidth; /* On which the calculation of relative sizes - is based. Height not yet used, and probably - not added before size redesign. */ - bool dirty, sizeChangedSinceLastAllocation; - - Float (OutOfFlowMgr *oofm, core::Widget *widget, - Textblock *generatingBlock, int externalIndex); - - inline bool isNowAllocated () { return getWidget()->wasAllocated (); } - inline int getNewXCB () { return getWidget()->getAllocation()->x - - getOutOfFlowMgr()->containingBlockAllocation.x; } - inline int getNewYCB () { return getWidget()->getAllocation()->y - - getOutOfFlowMgr()->containingBlockAllocation.y; } - inline int getNewWidth () { return getWidget()->getAllocation()->width; } - inline int getNewHeight () { return getWidget()->getAllocation()->ascent + - getWidget()->getAllocation()->descent; } - void updateAllocation (); - - inline int *getIndexRef (SFVType type) { - return type == GB ? &indexGBList : &indexCBList; } - inline int getIndex (SFVType type) { return *(getIndexRef (type)); } - inline void setIndex (SFVType type, int value) { - *(getIndexRef (type)) = value; } - - void intoStringBuffer(lout::misc::StringBuffer *sb); - - bool covers (Textblock *textblock, int y, int h); - }; - - /** - * This list is kept sorted. - * - * To prevent accessing methods of the base class in an - * uncontrolled way, the inheritance is private, not public; this - * means that all methods must be delegated (see iterator(), size() - * etc. below.) - * - * TODO Update comment: still sorted, but ... - * - * More: add() and change() may check order again. - */ - class SortedFloatsVector: private lout::container::typed::Vector<Float> - { - public: - SFVType type; - - private: - OutOfFlowMgr *oofm; - Side side; - - public: - inline SortedFloatsVector (OutOfFlowMgr *oofm, Side side, SFVType type) : - lout::container::typed::Vector<Float> (1, false) - { this->oofm = oofm; this->side = side; this->type = type; } - - int findFloatIndex (Textblock *lastGB, int lastExtIndex); - int find (Textblock *textblock, int y, int start, int end); - int findFirst (Textblock *textblock, int y, int h, Textblock *lastGB, - int lastExtIndex, int *lastReturn); - int findLastBeforeSideSpanningIndex (int sideSpanningIndex); - void put (Float *vloat); - - inline lout::container::typed::Iterator<Float> iterator() - { return lout::container::typed::Vector<Float>::iterator (); } - inline int size () - { return lout::container::typed::Vector<Float>::size (); } - inline Float *get (int pos) - { return lout::container::typed::Vector<Float>::get (pos); } - inline void clear () - { lout::container::typed::Vector<Float>::clear (); } - }; - - class TBInfo: public WidgetInfo - { - public: - int lineBreakWidth; - int index; // position within "tbInfos" - - TBInfo *parent; - int parentExtIndex; - - // These two values are set by sizeAllocateStart(), and they are - // accessable also within sizeAllocateEnd() for the same - // textblock, for which allocation and WAS_ALLOCATED is set - // *after* sizeAllocateEnd(). See the two functions - // wasAllocated(Widget*) and getAllocation(Widget*) (further - // down) for usage. - bool wasAllocated; - core::Allocation allocation; - int clearPosition; - - // These two lists store all floats generated by this textblock, - // as long as this textblock is not allocates. - SortedFloatsVector *leftFloatsGB, *rightFloatsGB; - - TBInfo (OutOfFlowMgr *oofm, Textblock *textblock, - TBInfo *parent, int parentExtIndex); - ~TBInfo (); - - inline bool isNowAllocated () { - return getOutOfFlowMgr()->wasAllocated (getTextblock ()); } - inline int getNewXCB () { - return getOutOfFlowMgr()->getAllocation (getTextblock ())->x - - getOutOfFlowMgr()->containingBlockAllocation.x; } - inline int getNewYCB () { - return getOutOfFlowMgr()->getAllocation (getTextblock ())->y - - getOutOfFlowMgr()->containingBlockAllocation.y; } - inline int getNewWidth () { - return getOutOfFlowMgr()->getAllocation (getTextblock ())->width; } - inline int getNewHeight () { - core::Allocation *allocation = - getOutOfFlowMgr()->getAllocation (getTextblock ()); - return allocation->ascent + allocation->descent; } - void updateAllocation (); - - inline Textblock *getTextblock () { return (Textblock*)getWidget (); } - }; - - // These two lists store all floats, in the order in which they are - // defined. Only used for iterators. - lout::container::typed::Vector<Float> *leftFloatsAll, *rightFloatsAll; - - // These two lists store all floats whose generators are already - // allocated. - SortedFloatsVector *leftFloatsCB, *rightFloatsCB; - - // These two attributes are used in the size allocation process; - // see sizeAllocateStart and sizeAllocateEnd. - int lastAllocatedLeftFloat, lastAllocatedRightFloat; - - lout::container::typed::HashTable<lout::object::TypedPointer - <dw::core::Widget>, Float> *floatsByWidget; - - lout::container::typed::Vector<TBInfo> *tbInfos; - lout::container::typed::HashTable<lout::object::TypedPointer <Textblock>, - TBInfo> *tbInfosByTextblock; - - int lastLeftTBIndex, lastRightTBIndex, leftFloatsMark, rightFloatsMark; - +public: + OutOfFlowMgr (); + virtual ~OutOfFlowMgr (); + + virtual void sizeAllocateStart (OOFAwareWidget *caller, + core::Allocation *allocation) = 0; + virtual void sizeAllocateEnd (OOFAwareWidget *caller) = 0; + virtual void containerSizeChangedForChildren () = 0; + virtual void draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context) = 0; + + virtual void markSizeChange (int ref) = 0; + virtual void markExtremesChange (int ref) = 0; + virtual core::Widget *getWidgetAtPoint (int x, int y, + core::GettingWidgetAtPointContext + *context) = 0; + + virtual void addWidgetInFlow (OOFAwareWidget *widget, + OOFAwareWidget *parent, int externalIndex) = 0; + virtual int addWidgetOOF (core::Widget *widget, OOFAwareWidget *generator, + int externalIndex) = 0; + virtual void calcWidgetRefSize (core::Widget *widget, + core::Requisition *size) = 0; + virtual void moveExternalIndices (OOFAwareWidget *generator, + int oldStartIndex, int diff) = 0; + /** - * Variant of Widget::wasAllocated(), which can also be used within - * OOFM::sizeAllocateEnd(). + * \brief Called before tellPosition2, see there for more. */ - inline bool wasAllocated (Textblock *textblock) { - return getTextblock(textblock)->wasAllocated; - } + virtual void tellPosition1 (core::Widget *widget, int x, int y) = 0; /** - * Variant of Widget::getAllocation(), which can also be used - * within OOFM::sizeAllocateEnd(). - */ - inline core::Allocation *getAllocation (Textblock *textblock) { - return &(getTextblock(textblock)->allocation); - } - - void moveExternalIndices (SortedFloatsVector *list, int oldStartIndex, - int diff); - Float *findFloatByWidget (core::Widget *widget); - - void moveFromGBToCB (Side side); - void sizeAllocateFloats (Side side, int newLastAllocatedFloat); - int getGBWidthForAllocation (Float *vloat); - int calcFloatX (Float *vloat, Side side, int gbX, int gbWidth); - - bool hasRelationChanged (TBInfo *tbInfo,int *minFloatPos, - core::Widget **minFloat); - bool hasRelationChanged (TBInfo *tbInfo, Side side, int *minFloatPos, - core::Widget **minFloat); - bool hasRelationChanged (bool oldTBAlloc, - int oldTBx, int oldTBy, int oldTBw, int oldTBh, - int newTBx, int newTBy, int newTBw, int newTBh, - bool oldFlAlloc, - int oldFlx, int oldFly, int oldFlw, int oldFlh, - int newFlx, int newFly, int newFlw, int newFlh, - Side side, int *floatPos); - - void checkAllocatedFloatCollisions (Side side); - - bool doFloatsExceedCB (Side side); - bool haveExtremesChanged (Side side); - - void drawFloats (SortedFloatsVector *list, core::View *view, - core::Rectangle *area); - core::Widget *getFloatWidgetAtPoint (SortedFloatsVector *list, int x, int y, - int level); - - bool collidesV (Float *vloat, Float *other, SFVType type, int *yReal, - bool useAllocation); - bool collidesH (Float *vloat, Float *other, SFVType type); - - void getFloatsListsAndSide (Float *vloat, SortedFloatsVector **listSame, - SortedFloatsVector **listOpp, Side *side); - - void getFloatsSize (core::Requisition *cbReq, Side side, int *width, - int *height); - void getFloatsExtremes (core::Extremes *cbExtr, Side side, int *minWidth, - int *maxWidth); - - TBInfo *getTextblock (Textblock *textblock); - int getBorder (Textblock *textblock, Side side, int y, int h, - Textblock *lastGB, int lastExtIndex); - SortedFloatsVector *getFloatsListForTextblock (Textblock *textblock, - Side side); - bool hasFloat (Textblock *textblock, Side side, int y, int h, - Textblock *lastGB, int lastExtIndex); - - int getFloatHeight (Textblock *textblock, Side side, int y, int h, - Textblock *lastGB, int lastExtIndex); - - int calcClearPosition (Textblock *tb, Side side); - int calcClearPosition (Textblock *tb); - - void ensureFloatSize (Float *vloat); - - void tellFloatPosition (core::Widget *widget, int yReq); - - static inline bool isStyleFloat (core::style::Style *style) - { return style->vloat != core::style::FLOAT_NONE; } - static inline bool isWidgetFloat (core::Widget *widget) - { return isStyleFloat (widget->getStyle()); } - - /* - * Format for parent ref (see also below for isRefOutOfFlow, - * createRefNormalFlow, and getLineNoFromRef. - * - * Widget in flow: - * - * +---+ - - - +---+---+- - - - - -+---+---+---+---+ - * | line number | 0 | - * +---+ - - - +---+---+- - - - - -+---+---+---+---+ - * - * So, anything with the least signifant bit set to 1 is out of flow. + * \brief Called after tellPosition1. * - * Floats: - * - * +---+ - - - +---+---+- - - - - -+---+---+---+---+ - * | left float index | 0 | 0 | 1 | - * +---+ - - - +---+---+- - - - - -+---+---+---+---+ - * - * +---+ - - - +---+---+- - - - - -+---+---+---+---+ - * | right float index | 1 | 0 | 1 | - * +---+ - - - +---+---+- - - - - -+---+---+---+---+ - * - * Absolutely positioned blocks: solved differently in the - * "dillo_grows" repository. + * An implementation should only implement either tellPosition1 or + * tellPosition2. Coordinates are relative to the allocation of the + * generator. */ - - inline static bool isRefFloat (int ref) - { return ref != -1 && (ref & 3) == 1; } - inline static bool isRefLeftFloat (int ref) - { return ref != -1 && (ref & 7) == 1; } - inline static bool isRefRightFloat (int ref) - { return ref != -1 && (ref & 7) == 5; } - - inline static int createRefLeftFloat (int index) - { return (index << 3) | 1; } - inline static int createRefRightFloat (int index) - { return (index << 3) | 5; } - - inline static int getFloatIndexFromRef (int ref) - { return ref == -1 ? ref : (ref >> 3); } - -public: - OutOfFlowMgr (Textblock *containingBlock); - ~OutOfFlowMgr (); - - void sizeAllocateStart (Textblock *caller, core::Allocation *allocation); - void sizeAllocateEnd (Textblock *caller); - void containerSizeChangedForChildren (); - void draw (core::View *view, core::Rectangle *area); - - void markSizeChange (int ref); - void markExtremesChange (int ref); - core::Widget *getWidgetAtPoint (int x, int y, int level); - - static bool isStyleOutOfFlow (core::style::Style *style) - { return isStyleFloat (style); } - static inline bool isWidgetOutOfFlow (core::Widget *widget) - { return isStyleOutOfFlow (widget->getStyle()); } - - void addWidgetInFlow (Textblock *textblock, Textblock *parentBlock, - int externalIndex); - void addWidgetOOF (core::Widget *widget, Textblock *generatingBlock, - int externalIndex); - void moveExternalIndices (Textblock *generatingBlock, int oldStartIndex, - int diff); - - void tellPosition (core::Widget *widget, int yReq); - - void getSize (core::Requisition *cbReq, int *oofWidth, int *oofHeight); - void getExtremes (core::Extremes *cbExtr, - int *oofMinWidth, int *oofMaxWidth); - - int getLeftBorder (Textblock *textblock, int y, int h, Textblock *lastGB, - int lastExtIndex); - int getRightBorder (Textblock *textblock, int y, int h, Textblock *lastGB, - int lastExtIndex); - - bool hasFloatLeft (Textblock *textblock, int y, int h, Textblock *lastGB, - int lastExtIndex); - bool hasFloatRight (Textblock *textblock, int y, int h, Textblock *lastGB, - int lastExtIndex); - - int getLeftFloatHeight (Textblock *textblock, int y, int h, - Textblock *lastGB, int lastExtIndex); - int getRightFloatHeight (Textblock *textblock, int y, int h, - Textblock *lastGB, int lastExtIndex); - - int getClearPosition (Textblock *tb); - - inline static bool isRefOutOfFlow (int ref) - { return ref != -1 && (ref & 1) != 0; } - inline static int createRefNormalFlow (int lineNo) { return lineNo << 1; } - inline static int getLineNoFromRef (int ref) - { return ref == -1 ? ref : (ref >> 1); } - + virtual void tellPosition2 (core::Widget *widget, int x, int y) = 0; + + virtual void getSize (core::Requisition *containerReq, int *oofWidth, + int *oofHeight) = 0; + virtual bool containerMustAdjustExtraSpace ()= 0; + virtual void getExtremes (core::Extremes *containerExtr, int *oofMinWidth, + int *oofMaxWidth) = 0; + + + virtual int getLeftBorder (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex) = 0; + virtual int getRightBorder (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex) = 0; + + virtual bool hasFloatLeft (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex) = 0; + virtual bool hasFloatRight (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex) = 0; + + virtual int getLeftFloatHeight (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex) + = 0; + virtual int getRightFloatHeight (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex) + = 0; + + virtual bool affectsLeftBorder (core::Widget *widget) = 0; + virtual bool affectsRightBorder (core::Widget *widget) = 0; + virtual bool mayAffectBordersAtAll () = 0; + + virtual int getClearPosition (OOFAwareWidget *widget) = 0; + + virtual bool dealingWithSizeOfChild (core::Widget *child) = 0; + virtual int getAvailWidthOfChild (core::Widget *child, bool forceValue) = 0; + virtual int getAvailHeightOfChild (core::Widget *child, bool forceValue) = 0; + // for iterators - inline int getNumWidgets () - { return leftFloatsAll->size() + rightFloatsAll->size(); } - - inline core::Widget *getWidget (int i) { - if (i < leftFloatsAll->size()) - return leftFloatsAll->get(i)->getWidget (); - else - return rightFloatsAll->get(i - leftFloatsAll->size())->getWidget (); - } - - inline bool affectsLeftBorder (core::Widget *widget) { - return widget->getStyle()->vloat == core::style::FLOAT_LEFT; } - inline bool affectsRightBorder (core::Widget *widget) { - return widget->getStyle()->vloat == core::style::FLOAT_RIGHT; } + virtual int getNumWidgets () = 0; + virtual core::Widget *getWidget (int i) = 0; }; +} // namespace oof + } // namespace dw #endif // __DW_OUTOFFLOWMGR_HH__ diff --git a/dw/regardingborder.hh b/dw/regardingborder.hh index 4c8951ad..1cb6c25f 100644 --- a/dw/regardingborder.hh +++ b/dw/regardingborder.hh @@ -1,7 +1,7 @@ #ifndef __DW_REGARDINGBORDER_HH__ #define __DW_REGARDINGBORDER_HH__ -#include "core.hh" +#include "oofawarewidget.hh" namespace dw { @@ -9,11 +9,8 @@ namespace dw { * \brief Base class (rather a tag interface) for those widgets * regarding borders defined by floats, and so allocated on the * full width. - * - * Will, when integrated to the "dillo_grows" repository, become a sub - * class of OOFAwareWidget. */ -class RegardingBorder: public core::Widget +class RegardingBorder: public oof::OOFAwareWidget { public: static int CLASS_ID; diff --git a/dw/ruler.cc b/dw/ruler.cc index ccf58baa..c34d8ecf 100644 --- a/dw/ruler.cc +++ b/dw/ruler.cc @@ -41,15 +41,14 @@ Ruler::~Ruler () void Ruler::sizeRequestImpl (core::Requisition *requisition) { - requisition->width = - lout::misc::max (getAvailWidth (true), getStyle()->boxDiffWidth ()); - requisition->ascent = getStyle()->boxOffsetY (); - requisition->descent = getStyle()->boxRestHeight (); + requisition->width = lout::misc::max (getAvailWidth (true), boxDiffWidth ()); + requisition->ascent = boxOffsetY (); + requisition->descent = boxRestHeight (); } void Ruler::getExtremesImpl (core::Extremes *extremes) { - extremes->minWidth = extremes->maxWidth = getStyle()->boxDiffWidth (); + extremes->minWidth = extremes->maxWidth = boxDiffWidth (); extremes->minWidthIntrinsic = extremes->minWidth; extremes->maxWidthIntrinsic = extremes->maxWidth; correctExtremes (extremes, false); @@ -74,7 +73,8 @@ bool Ruler::usesAvailWidth () return true; } -void Ruler::draw (core::View *view, core::Rectangle *area) +void Ruler::draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context) { drawWidgetBox (view, area, false); } diff --git a/dw/ruler.hh b/dw/ruler.hh index cd4f63f4..c90afccf 100644 --- a/dw/ruler.hh +++ b/dw/ruler.hh @@ -14,9 +14,8 @@ namespace dw { * Ruler implements RegardingBorder; this way, it is simpler to fit * the ruler exactly within the space between floats. Currently, the * drawn area of the ruler is too large (but most of the superfluous - * part is hidden by the floats); this problem will not solved but in - * "dillo_grows", where RegardingBorder is a sub class of - * OOFAwareWidget. + * part is hidden by the floats); this problem will soon solved here + * in the "dillo_grows" repository. */ class Ruler: public RegardingBorder { @@ -25,7 +24,8 @@ protected: void getExtremesImpl (core::Extremes *extremes); void containerSizeChangedForChildren (); bool usesAvailWidth (); - void draw (core::View *view, core::Rectangle *area); + void draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context); public: static int CLASS_ID; diff --git a/dw/selection.cc b/dw/selection.cc index b67f4a6d..a69eb82a 100644 --- a/dw/selection.cc +++ b/dw/selection.cc @@ -93,63 +93,79 @@ void SelectionState::resetLink () bool SelectionState::buttonPress (Iterator *it, int charPos, int linkNo, EventButton *event) { + DBG_IF_RTFL { + misc::StringBuffer sb; + it->intoStringBuffer (&sb); + DBG_OBJ_ENTER ("events", 0, "buttonPress", "[%s], %d, %d, ...", + sb.getChars (), charPos, linkNo); + } + Widget *itWidget = it->getWidget (); bool ret = false; - if (!event) return ret; - - if (event->button == 3) { - // menu popup - layout->emitLinkPress (itWidget, linkNo, -1, -1, -1, event); - ret = true; - } else if (linkNo != -1) { - // link handling - (void) layout->emitLinkPress (itWidget, linkNo, -1, -1, -1, event); - resetLink (); - linkState = LINK_PRESSED; - linkButton = event->button; - DeepIterator *newLink = new DeepIterator (it); - if (newLink->isEmpty ()) { - delete newLink; + if (event) { + if (event->button == 3) { + // menu popup + layout->emitLinkPress (itWidget, linkNo, -1, -1, -1, event); + ret = true; + } else if (linkNo != -1) { + // link handling + (void) layout->emitLinkPress (itWidget, linkNo, -1, -1, -1, event); resetLink (); - } else { - link = newLink; - // It may be that the user has pressed on something activatable - // (linkNo != -1), but there is no contents, e.g. with images - // without ALTernative text. - if (link) { - linkChar = correctCharPos (link, charPos); - linkNumber = linkNo; + linkState = LINK_PRESSED; + linkButton = event->button; + DeepIterator *newLink = new DeepIterator (it); + if (newLink->isEmpty ()) { + delete newLink; + resetLink (); + } else { + link = newLink; + // It may be that the user has pressed on something activatable + // (linkNo != -1), but there is no contents, e.g. with images + // without ALTernative text. + if (link) { + linkChar = correctCharPos (link, charPos); + linkNumber = linkNo; + } } - } - // We do not return the value of the signal method, - // but we do actually process this event. - ret = true; - } else if (event->button == 1) { - // normal selection handling - highlight (false, 0); - resetSelection (); - - selectionState = SELECTING; - DeepIterator *newFrom = new DeepIterator (it); - if (newFrom->isEmpty ()) { - delete newFrom; + // We do not return the value of the signal method, + // but we do actually process this event. + ret = true; + } else if (event->button == 1) { + // normal selection handling + highlight (false, 0); resetSelection (); - } else { - from = newFrom; - fromChar = correctCharPos (from, charPos); - to = from->cloneDeepIterator (); - toChar = correctCharPos (to, charPos); + + selectionState = SELECTING; + DeepIterator *newFrom = new DeepIterator (it); + if (newFrom->isEmpty ()) { + delete newFrom; + resetSelection (); + } else { + from = newFrom; + fromChar = correctCharPos (from, charPos); + to = from->cloneDeepIterator (); + toChar = correctCharPos (to, charPos); + } + ret = true; } - ret = true; } + DBG_OBJ_MSGF ("events", 1, "=> %s", ret ? "true" : "false"); + DBG_OBJ_LEAVE (); return ret; } bool SelectionState::buttonRelease (Iterator *it, int charPos, int linkNo, EventButton *event) { + DBG_IF_RTFL { + misc::StringBuffer sb; + it->intoStringBuffer (&sb); + DBG_OBJ_ENTER ("events", 0, "buttonRelease", "[%s], %d, %d, ...", + sb.getChars (), charPos, linkNo); + } + Widget *itWidget = it->getWidget (); bool ret = false; @@ -186,6 +202,8 @@ bool SelectionState::buttonRelease (Iterator *it, int charPos, int linkNo, } } + DBG_OBJ_MSGF ("events", 1, "=> %s", ret ? "true" : "false"); + DBG_OBJ_LEAVE (); return ret; } diff --git a/dw/simpletablecell.cc b/dw/simpletablecell.cc index feec46c8..f39d94b5 100644 --- a/dw/simpletablecell.cc +++ b/dw/simpletablecell.cc @@ -57,7 +57,7 @@ bool SimpleTableCell::mustBeWidenedToAvailWidth () int SimpleTableCell::getAvailWidthOfChild (Widget *child, bool forceValue) { - DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell/getAvailWidthOfChild", + DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell::getAvailWidthOfChild", "%p, %s", child, forceValue ? "true" : "false"); int width = tablecell::correctAvailWidthOfChild @@ -70,7 +70,7 @@ int SimpleTableCell::getAvailWidthOfChild (Widget *child, bool forceValue) int SimpleTableCell::getAvailHeightOfChild (Widget *child, bool forceValue) { - DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell/getAvailHeightOfChild", + DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell::getAvailHeightOfChild", "%p, %s", child, forceValue ? "true" : "false"); int height = tablecell::correctAvailHeightOfChild @@ -87,7 +87,7 @@ void SimpleTableCell::correctRequisitionOfChild (Widget *child, int*, int*)) { - DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell/correctRequisitionOfChild", + DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell::correctRequisitionOfChild", "%p, %d * (%d + %d), ...", child, requisition->width, requisition->ascent, requisition->descent); @@ -102,7 +102,7 @@ void SimpleTableCell::correctExtremesOfChild (Widget *child, core::Extremes *extremes, bool useAdjustmentWidth) { - DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell/correctExtremesOfChild", + DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell::correctExtremesOfChild", "%p, %d (%d) / %d (%d)", child, extremes->minWidth, extremes->minWidthIntrinsic, extremes->maxWidth, extremes->maxWidthIntrinsic); @@ -126,4 +126,9 @@ int SimpleTableCell::applyPerHeight (int containerHeight, return tablecell::applyPerHeight (this, containerHeight, perHeight); } +bool SimpleTableCell::adjustExtraSpaceWhenCorrectingRequisitionByOOF () +{ + return tablecell::adjustExtraSpaceWhenCorrectingRequisitionByOOF (); +} + } // namespace dw diff --git a/dw/simpletablecell.hh b/dw/simpletablecell.hh index 60715d90..90f24ae4 100644 --- a/dw/simpletablecell.hh +++ b/dw/simpletablecell.hh @@ -19,6 +19,8 @@ protected: bool getAdjustMinWidth (); + bool adjustExtraSpaceWhenCorrectingRequisitionByOOF (); + public: static int CLASS_ID; diff --git a/dw/stackingcontextmgr.cc b/dw/stackingcontextmgr.cc new file mode 100644 index 00000000..98cef164 --- /dev/null +++ b/dw/stackingcontextmgr.cc @@ -0,0 +1,195 @@ +/* + * Dillo Widget + * + * Copyright 2014 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "core.hh" +#include "../lout/debug.hh" + +using namespace lout::misc; +using namespace lout::container::typed; + +namespace dw { + +namespace core { + +StackingContextMgr::StackingContextMgr (Widget *widget) +{ + DBG_OBJ_CREATE ("dw::core::StackingContextMgr"); + + this->widget = widget; + + childSCWidgets = new Vector<Widget> (1, false); + DBG_OBJ_SET_NUM ("childSCWidgets.size", childSCWidgets->size()); + + numZIndices = 0; + zIndices = NULL; + DBG_OBJ_SET_NUM ("numZIndices", numZIndices); +} + +StackingContextMgr::~StackingContextMgr () +{ + delete childSCWidgets; + if (zIndices) + free (zIndices); + DBG_OBJ_DELETE (); +} + +void StackingContextMgr::addChildSCWidget (Widget *widget) +{ + DBG_OBJ_ENTER ("common.scm", 0, "addChildSCWidget", "%p [z-index = %d]", + widget, widget->getStyle()->zIndex); + + int pos = findZIndex (widget->getStyle()->zIndex, true); + DBG_OBJ_MSGF ("common.scm", 1, "pos = %d", pos); + if (pos == -1) { + pos = findZIndex (widget->getStyle()->zIndex, false); + DBG_OBJ_MSGF ("common.scm", 1, "pos = %d", pos); + + numZIndices++; + DBG_OBJ_SET_NUM ("numZIndices", numZIndices); + zIndices = (int*)(zIndices ? + realloc (zIndices, numZIndices * sizeof (int)) : + malloc (numZIndices * sizeof (int))); + + for (int i = numZIndices - 1; i >= pos + 1; i--) { + zIndices[i] = zIndices[i - 1]; + DBG_OBJ_ARRSET_NUM ("zIndex", i, zIndices[i]); + } + + zIndices[pos] = widget->getStyle()->zIndex; + DBG_OBJ_ARRSET_NUM ("zIndex", pos, zIndices[pos]); + } + + childSCWidgets->put (widget); + DBG_OBJ_SET_NUM ("childSCWidgets.size", childSCWidgets->size()); + DBG_OBJ_ARRSET_PTR ("childSCWidgets", childSCWidgets->size() - 1, widget); + + DBG_OBJ_LEAVE (); +} + +int StackingContextMgr::findZIndex (int zIndex, bool mustExist) +{ + int result = -123; // Compiler happiness: GCC 4.7 does not handle this? + + if (numZIndices == 0) + result = mustExist ? -1 : 0; + else { + int low = 0, high = numZIndices - 1; + bool found = false; + + while (!found) { + int index = (low + high) / 2; + if (zIndex == zIndices[index]) { + found = true; + result = index; + } else { + if (low >= high) { + if (mustExist) { + found = true; + result = -1; + } else { + found = true; + result = zIndex > zIndices[index] ? index + 1 : index; + } + } + + if (zIndex < zIndices[index]) + high = index - 1; + else + low = index + 1; + } + } + } + + return result; +} + +void StackingContextMgr::draw (View *view, Rectangle *area, int startZIndex, + int endZIndex, DrawingContext *context) +{ + DBG_OBJ_ENTER ("draw", 0, "draw", "[%d, %d, %d * %d], %d, %d", + area->x, area->y, area->width, area->height, startZIndex, + endZIndex); + + for (int zIndexIndex = 0; zIndexIndex < numZIndices; zIndexIndex++) { + // Wrong region of z-indices (top or bottom) is simply ignored + // (as well as non-defined zIndices). + if (zIndices != NULL && zIndices[zIndexIndex] >= startZIndex && + zIndices[zIndexIndex] <= endZIndex) { + DBG_OBJ_MSGF ("draw", 1, "drawing zIndex = %d", zIndices[zIndexIndex]); + DBG_OBJ_MSG_START (); + + for (int i = 0; i < childSCWidgets->size (); i++) { + Widget *child = childSCWidgets->get (i); + DBG_OBJ_MSGF ("draw", 2, "widget %p has zIndex = %d", + child, child->getStyle()->zIndex); + + Rectangle childArea; + if (child->getStyle()->zIndex == zIndices[zIndexIndex] && + child->intersects (widget, area, &childArea)) + child->draw (view, &childArea, context); + } + + DBG_OBJ_MSG_END (); + } + } + + DBG_OBJ_LEAVE (); +} + +Widget *StackingContextMgr::getWidgetAtPoint (int x, int y, + GettingWidgetAtPointContext + *context, + int startZIndex, int endZIndex) +{ + DBG_OBJ_ENTER ("events", 0, "getWidgetAtPoint", "%d, %d", x, y); + + Widget *widgetAtPoint = NULL; + + for (int zIndexIndex = numZIndices - 1; + widgetAtPoint == NULL && zIndexIndex >= 0; zIndexIndex--) { + // Wrong region of z-indices (top or bottom) is simply ignored + // (as well as non-defined zIndices). + if (zIndices != NULL && zIndices[zIndexIndex] >= startZIndex && + zIndices[zIndexIndex] <= endZIndex) { + DBG_OBJ_MSGF ("events", 1, "searching zIndex = %d", + zIndices[zIndexIndex]); + DBG_OBJ_MSG_START (); + + for (int i = childSCWidgets->size () - 1; + widgetAtPoint == NULL && i >= 0; i--) { + Widget *child = childSCWidgets->get (i); + DBG_OBJ_MSGF ("events", 2, "widget %p has zIndex = %d", + child, child->getStyle()->zIndex); + if (child->getStyle()->zIndex == zIndices[zIndexIndex]) + widgetAtPoint = child->getWidgetAtPoint (x, y, context); + } + + DBG_OBJ_MSG_END (); + } + } + + DBG_OBJ_MSGF ("events", 0, "=> %p", widgetAtPoint); + DBG_OBJ_LEAVE (); + return widgetAtPoint; +} + +} // namespace core + +} // namespace dw diff --git a/dw/stackingcontextmgr.hh b/dw/stackingcontextmgr.hh new file mode 100644 index 00000000..3a877031 --- /dev/null +++ b/dw/stackingcontextmgr.hh @@ -0,0 +1,75 @@ +#ifndef __DW_STACKINGCONTEXTMGR_HH__ +#define __DW_STACKINGCONTEXTMGR_HH__ + +#ifndef __INCLUDED_FROM_DW_CORE_HH__ +# error Do not include this file directly, use "core.hh" instead. +#endif + +#include "../lout/container.hh" + +#include <limits.h> + +namespace dw { + +namespace core { + +/** + * \brief See \ref dw-stacking-context. + */ +class StackingContextMgr +{ +private: + Widget *widget; + lout::container::typed::Vector<Widget> *childSCWidgets; + int *zIndices, numZIndices; + + int findZIndex (int zIndex, bool mustExist); + void draw (View *view, Rectangle *area, int startZIndex, int endZIndex, + DrawingContext *context); + Widget *getWidgetAtPoint (int x, int y, + core::GettingWidgetAtPointContext *context, + int startZIndex, int endZIndex); + +public: + StackingContextMgr (Widget *widget); + ~StackingContextMgr (); + + inline static bool isEstablishingStackingContext (Widget *widget) { + return widget->getStyle()->position != style::POSITION_STATIC && + widget->getStyle()->zIndex != style::Z_INDEX_AUTO; + } + + inline static bool handledByStackingContextMgr (Widget *widget) { + // Each widget establishing a stacking context is child of another + // stacking context, so drawn by StackingContextMgr::drawTop or + // StackingContextMgr::drawBottom etc. + return widget->getParent () != NULL + && isEstablishingStackingContext (widget); + } + + void addChildSCWidget (Widget *widget); + + inline int getNumZIndices () { return numZIndices; } + inline int getNumChildSCWidgets () { return childSCWidgets->size (); } + + inline void drawBottom (View *view, Rectangle *area, DrawingContext *context) + { draw (view, area, INT_MIN, -1, context); } + void drawTop (View *view, Rectangle *area, DrawingContext *context) + { draw (view, area, 0, INT_MAX, context); } + + inline Widget *getTopWidgetAtPoint (int x, int y, + core::GettingWidgetAtPointContext + *context) + { return getWidgetAtPoint (x, y, context, 0, INT_MAX); } + + inline Widget *getBottomWidgetAtPoint (int x, int y, + core::GettingWidgetAtPointContext + *context) + { return getWidgetAtPoint (x, y, context, INT_MIN, -1); } +}; + +} // namespace core + +} // namespace dw + +#endif // __DW_STACKINGCONTEXTMGR_HH__ diff --git a/dw/style.cc b/dw/style.cc index 54d9af4b..32a33a9e 100644 --- a/dw/style.cc +++ b/dw/style.cc @@ -93,6 +93,7 @@ void StyleAttrs::initValues () display = DISPLAY_INLINE; whiteSpace = WHITE_SPACE_NORMAL; cursor = CURSOR_DEFAULT; + zIndex = Z_INDEX_AUTO; } /** @@ -203,6 +204,7 @@ bool StyleAttrs::equals (object::Object *other) { listStylePosition == otherAttrs->listStylePosition && listStyleType == otherAttrs->listStyleType && cursor == otherAttrs->cursor && + zIndex == otherAttrs->zIndex && x_link == otherAttrs->x_link && x_lang[0] == otherAttrs->x_lang[0] && x_lang[1] == otherAttrs->x_lang[1] && @@ -260,6 +262,7 @@ int StyleAttrs::hashValue () { listStylePosition + listStyleType + cursor + + zIndex + x_link + x_lang[0] + x_lang[1] + x_img + @@ -381,6 +384,7 @@ void Style::copyAttrs (StyleAttrs *attrs) listStylePosition = attrs->listStylePosition; listStyleType = attrs->listStyleType; cursor = attrs->cursor; + zIndex = attrs->zIndex; x_link = attrs->x_link; x_lang[0] = attrs->x_lang[0]; x_lang[1] = attrs->x_lang[1]; diff --git a/dw/style.hh b/dw/style.hh index 230baa24..12ca1664 100644 --- a/dw/style.hh +++ b/dw/style.hh @@ -7,6 +7,8 @@ # error Do not include this file directly, use "core.hh" instead. #endif +#include <limits.h> + #include "../lout/signal.hh" #include "../lout/debug.hh" @@ -374,6 +376,17 @@ enum ClearType { CLEAR_NONE }; +enum { + /** + * \brief 'z-index' is stored as int; use this for the value 'auto'. + * + * Only some random value, which has to be checked explicitly; do + * not compare this (less or greater) to integer values of + * 'z-index'. + */ + Z_INDEX_AUTO = INT_MAX +}; + /** * \brief Type for representing all lengths within dw::core::style. * @@ -553,6 +566,7 @@ public: ListStylePosition listStylePosition; ListStyleType listStyleType; Cursor cursor; + int zIndex; int x_link; int x_img; diff --git a/dw/table.cc b/dw/table.cc index 4ceb4020..3e7d30df 100644 --- a/dw/table.cc +++ b/dw/table.cc @@ -112,18 +112,20 @@ void Table::sizeRequestImpl (core::Requisition *requisition) /** * \bug Baselines are not regarded here. */ - requisition->width = getStyle()->boxDiffWidth () - + (numCols + 1) * getStyle()->hBorderSpacing; + requisition->width = + boxDiffWidth () + (numCols + 1) * getStyle()->hBorderSpacing; for (int col = 0; col < numCols; col++) requisition->width += colWidths->get (col); requisition->ascent = - getStyle()->boxDiffHeight () + cumHeight->get (numRows) - + getStyle()->vBorderSpacing; + boxDiffHeight () + cumHeight->get (numRows) + getStyle()->vBorderSpacing; requisition->descent = 0; correctRequisition (requisition, core::splitHeightPreserveDescent); + // For the order, see similar reasoning for dw::Textblock. + correctRequisitionByOOF (requisition, core::splitHeightPreserveDescent); + DBG_OBJ_LEAVE (); } @@ -154,6 +156,9 @@ void Table::getExtremesImpl (core::Extremes *extremes) correctExtremes (extremes, true); + // For the order, see similar reasoning for dw::Textblock. + correctExtremesByOOF (extremes); + DBG_OBJ_LEAVE (); } @@ -163,16 +168,16 @@ void Table::sizeAllocateImpl (core::Allocation *allocation) allocation->x, allocation->y, allocation->width, allocation->ascent, allocation->descent); + sizeAllocateStart (allocation); + calcCellSizes (true); /** * \bug Baselines are not regarded here. */ - int offy = - allocation->y + getStyle()->boxOffsetY () + getStyle()->vBorderSpacing; - int x = - allocation->x + getStyle()->boxOffsetX () + getStyle()->hBorderSpacing; + int offy = allocation->y + boxOffsetY () + getStyle()->vBorderSpacing; + int x = allocation->x + boxOffsetX () + getStyle()->hBorderSpacing; for (int col = 0; col < numCols; col++) { for (int row = 0; row < numRows; row++) { @@ -203,6 +208,8 @@ void Table::sizeAllocateImpl (core::Allocation *allocation) x += colWidths->get (col) + getStyle()->hBorderSpacing; } + sizeAllocateEnd (); + DBG_OBJ_LEAVE (); } @@ -220,34 +227,40 @@ int Table::getAvailWidthOfChild (Widget *child, bool forceValue) child, forceValue ? "true" : "false"); int width; + oof::OutOfFlowMgr *oofm; - // We do not calculate the column widths at this point, because - // this tends to be rather inefficient for tables with many - // cells: - // - // For each of the n cells, some text is added (say, only one word - // per cell). Textblock::addText will eventually (via addText0 - // etc.) call this method, Table::getAvailWidthOfChild. If - // calcCellSizes() is called here, this will call - // forceCalcCellSizes(), since the last call, sizes have to be - // re-calculated (because cells have been added). This will - // calculate the extremes for each existing cell, so - // Widget::getExtremes is called n * (n + 1) / 2 times. Even if the - // extremes are cached (so that getExtremesImpl does not have to be - // called in each case), this would make rendering tables with more - // than a few hundred cells unacceptably slow. - // - // Instead, column widths are calculated in Table::sizeRequestImpl. - // - // An alternative would be incremental resizing for tables; this - // approach resembles the behaviour before GROWS. - - // TODO Does it still make sence to return -1 when forceValue is - // set? - if (forceValue) - width = calcAvailWidthForDescendant (child); - else - width = -1; + if (isWidgetOOF(child) && (oofm = getWidgetOutOfFlowMgr(child)) && + oofm->dealingWithSizeOfChild (child)) + width = oofm->getAvailWidthOfChild (child, forceValue); + else { + // We do not calculate the column widths at this point, because + // this tends to be rather inefficient for tables with many + // cells: + // + // For each of the n cells, some text is added (say, only one word + // per cell). Textblock::addText will eventually (via addText0 + // etc.) call this method, Table::getAvailWidthOfChild. If + // calcCellSizes() is called here, this will call + // forceCalcCellSizes(), since the last call, sizes have to be + // re-calculated (because cells have been added). This will + // calculate the extremes for each existing cell, so + // Widget::getExtremes is called n * (n + 1) / 2 times. Even if the + // extremes are cached (so that getExtremesImpl does not have to be + // called in each case), this would make rendering tables with more + // than a few hundred cells unacceptably slow. + // + // Instead, column widths are calculated in Table::sizeRequestImpl. + // + // An alternative would be incremental resizing for tables; this + // approach resembles the behaviour before GROWS. + + // TODO Does it still make sence to return -1 when forceValue is + // set? + if (forceValue) + width = calcAvailWidthForDescendant (child); + else + width = -1; + } DBG_OBJ_MSGF ("resize", 1, "=> %d", width); DBG_OBJ_LEAVE (); @@ -266,10 +279,17 @@ int Table::calcAvailWidthForDescendant (Widget *child) assert (actualChild != NULL); - // ActualChild->parentRef contains the position in the children - // array (see addCell()), so the column can be easily determined. - int col = actualChild->parentRef % numCols; - int colspanEff = children->get(actualChild->parentRef)->cell.colspanEff; + // ActualChild->parentRef contains (indirectly) the position in the + // children array (see addCell()), so the column can be easily + // determined. + int childNo = getParentRefInFlowSubRef (actualChild->parentRef); + int col = childNo % numCols; + DBG_OBJ_MSGF ("resize", 1, "actualChild = %p, " + "childNo = getParentRefInFlowSubRef (%d) = %d, " + "column = %d %% %d = %d", + actualChild, actualChild->parentRef, childNo, childNo, + numCols, col); + int colspanEff = children->get(childNo)->cell.colspanEff; DBG_OBJ_MSGF ("resize", 1, "calculated from column %d, colspanEff = %d", col, colspanEff); @@ -318,6 +338,8 @@ void Table::containerSizeChangedForChildren () } } + containerSizeChangedForChildrenOOF (); + DBG_OBJ_LEAVE (); } @@ -353,18 +375,21 @@ bool Table::isBlockLevel () return true; } -void Table::draw (core::View *view, core::Rectangle *area) +void Table::drawLevel (core::View *view, core::Rectangle *area, int level, + core::DrawingContext *context) { - // Can be optimized, by iterating on the lines in area. - drawWidgetBox (view, area, false); + DBG_OBJ_ENTER ("draw", 0, "Table::drawLevel", "[%d, %d, %d * %d], %s", + area->x, area->y, area->width, area->height, + stackingLevelText (level)); #if 0 + // This old code belongs perhaps to the background. Check when reactivated. int offx = getStyle()->boxOffsetX () + getStyle()->hBorderSpacing; int offy = getStyle()->boxOffsetY () + getStyle()->vBorderSpacing; int width = getContentWidth (); - + // This part seems unnecessary. It also segfaulted sometimes when - // cumHeight size was less than numRows. --jcid + // cumHeight size was less than numRows. --jcid for (int row = 0; row < numRows; row++) { if (rowStyle->get (row)) drawBox (view, rowStyle->get (row), area, @@ -375,14 +400,58 @@ void Table::draw (core::View *view, core::Rectangle *area) } #endif - for (int i = 0; i < children->size (); i++) { - if (childDefined (i)) { - Widget *child = children->get(i)->cell.widget; - core::Rectangle childArea; - if (child->intersects (area, &childArea)) - child->draw (view, &childArea); + switch (level) { + case SL_IN_FLOW: + for (int i = 0; i < children->size (); i++) { + if (childDefined (i)) { + Widget *child = children->get(i)->cell.widget; + core::Rectangle childArea; + if (!core::StackingContextMgr::handledByStackingContextMgr (child) + && child->intersects (this, area, &childArea)) + child->draw (view, &childArea, context); + } + } + break; + + default: + OOFAwareWidget::drawLevel (view, area, level, context); + break; + } + + DBG_OBJ_LEAVE (); +} + +core::Widget *Table::getWidgetAtPointLevel (int x, int y, int level, + core::GettingWidgetAtPointContext + *context) +{ + DBG_OBJ_ENTER ("events", 0, "Table::getWidgetAtPointLevel", "%d, %d, %s", + x, y, stackingLevelText (level)); + + Widget *widgetAtPoint = NULL; + + switch (level) { + case SL_IN_FLOW: + for (int i = children->size () - 1; widgetAtPoint == NULL && i >= 0; + i--) { + if (childDefined (i)) { + Widget *child = children->get(i)->cell.widget; + if (!core::StackingContextMgr::handledByStackingContextMgr (child)) + widgetAtPoint = child->getWidgetAtPoint (x, y, context); + } } + break; + + default: + widgetAtPoint = + OOFAwareWidget::getWidgetAtPointLevel (x, y, level, context); + break; } + + DBG_OBJ_MSGF ("events", 1, "=> %p", widgetAtPoint); + DBG_OBJ_LEAVE (); + + return widgetAtPoint; } void Table::removeChild (Widget *child) @@ -475,14 +544,14 @@ void Table::addCell (Widget *widget, int colspan, int rowspan) child->cell.rowspan = rowspan; children->set (curRow * numCols + curCol, child); - // The position in the children array is assigned to parentRef, - // although incremental resizing is not implemented. Useful, e. g., - // in calcAvailWidthForDescendant(). See also reallocChildren(). - widget->parentRef = curRow * numCols + curCol; + // The position in the children array is (indirectly) assigned to parentRef, + // although incremental resizing is not implemented. Useful, e. g., in + // calcAvailWidthForDescendant(). See also reallocChildren(). + widget->parentRef = makeParentRefInFlow (curRow * numCols + curCol); DBG_OBJ_SET_NUM_O (widget, "parentRef", widget->parentRef); curCol += colspanEff; - + widget->setParent (this); if (rowStyle->get (curRow)) widget->setBgColor (rowStyle->get(curRow)->backgroundColor); @@ -722,7 +791,7 @@ void Table::reallocChildren (int newNumCols, int newNumRows) int n = row * newNumCols + col; Child *child = children->get (n); if (child != NULL && child->type == Child::CELL) { - child->cell.widget->parentRef = n; + child->cell.widget->parentRef = makeParentRefInFlow (n); DBG_OBJ_SET_NUM_O (child->cell.widget, "parentRef", child->cell.widget->parentRef); } @@ -768,6 +837,9 @@ void Table::calcCellSizes (bool calcHeights) void Table::forceCalcCellSizes (bool calcHeights) { + DBG_OBJ_ENTER ("resize", 0, "forceCalcCellSizes", "%s", + calcHeights ? "true" : "false"); + // Since Table::getAvailWidthOfChild does not calculate the column // widths, and so initially a random value (100) is returned, a // correction is necessary. The old values are temporary preserved @@ -822,11 +894,14 @@ void Table::forceCalcCellSizes (bool calcHeights) } } } + + DBG_OBJ_LEAVE (); } void Table::actuallyCalcCellSizes (bool calcHeights) { - DBG_OBJ_ENTER0 ("resize", 0, "forceCalcCellSizes"); + DBG_OBJ_ENTER ("resize", 0, "actuallyCalcCellSizes", "%s", + calcHeights ? "true" : "false"); int childHeight; core::Extremes extremes; diff --git a/dw/table.hh b/dw/table.hh index ed433594..c5806535 100644 --- a/dw/table.hh +++ b/dw/table.hh @@ -1,7 +1,7 @@ #ifndef __DW_TABLE_HH__ #define __DW_TABLE_HH__ -#include "core.hh" +#include "oofawarewidget.hh" #include "alignedtablecell.hh" #include "../lout/misc.hh" @@ -319,7 +319,7 @@ namespace dw { * Here, \em foo-bar refers to the attribute \em bar of the tag \em foo foo. * Look at the HTML parser for more details. */ -class Table: public core::Widget +class Table: public oof::OOFAwareWidget { private: struct Child @@ -339,20 +339,17 @@ private: }; }; - class TableIterator: public core::Iterator + class TableIterator: public OOFAwareWidgetIterator { - private: - int index; + protected: + int numContentsInFlow (); + void getContentInFlow (int index, core::Content *content); public: TableIterator (Table *table, core::Content::Type mask, bool atEnd); - TableIterator (Table *table, core::Content::Type mask, int index); lout::object::Object *clone(); - int compareTo(lout::object::Comparable *other); - bool next (); - bool prev (); void highlight (int start, int end, core::HighlightLayer layer); void unhighlight (int direction, core::HighlightLayer layer); void getAllocation (int start, int end, core::Allocation *allocation); @@ -477,7 +474,11 @@ protected: bool isBlockLevel (); - void draw (core::View *view, core::Rectangle *area); + void drawLevel (core::View *view, core::Rectangle *area, int level, + core::DrawingContext *context); + + Widget *getWidgetAtPointLevel (int x, int y, int level, + core::GettingWidgetAtPointContext *context); //bool buttonPressImpl (core::EventButton *event); //bool buttonReleaseImpl (core::EventButton *event); diff --git a/dw/table_iterator.cc b/dw/table_iterator.cc index 4da0ef4f..9ba61619 100644 --- a/dw/table_iterator.cc +++ b/dw/table_iterator.cc @@ -28,107 +28,62 @@ namespace dw { Table::TableIterator::TableIterator (Table *table, core::Content::Type mask, bool atEnd): - core::Iterator (table, mask, atEnd) + OOFAwareWidgetIterator (table, mask, atEnd, table->children->size ()) { - index = atEnd ? table->children->size () : -1; - content.type = atEnd ? core::Content::END : core::Content::START; -} - -Table::TableIterator::TableIterator (Table *table, - core::Content::Type mask, int index): - core::Iterator (table, mask, false) -{ - this->index = index; - - if (index < 0) - content.type = core::Content::START; - else if (index >= table->children->size ()) - content.type = core::Content::END; - else { - content.type = core::Content::WIDGET_IN_FLOW; - content.widget = table->children->get(index)->cell.widget; - } } object::Object *Table::TableIterator::clone() { - return new TableIterator ((Table*)getWidget(), getMask(), index); -} - -int Table::TableIterator::compareTo(object::Comparable *other) -{ - return index - ((TableIterator*)other)->index; + TableIterator *tIt = + new TableIterator ((Table*)getWidget(), getMask(), false); + cloneValues (tIt); + return tIt; } -bool Table::TableIterator::next () -{ - Table *table = (Table*)getWidget(); - - if (content.type == core::Content::END) - return false; - - // tables only contain widgets (in flow): - if ((getMask() & core::Content::WIDGET_IN_FLOW) == 0) { - content.type = core::Content::END; - return false; - } - - do { - index++; - if (index >= table->children->size ()) { - content.type = core::Content::END; - return false; - } - } while (table->children->get(index) == NULL || - table->children->get(index)->type != Child::CELL); - - content.type = core::Content::WIDGET_IN_FLOW; - content.widget = table->children->get(index)->cell.widget; - return true; -} - -bool Table::TableIterator::prev () -{ - Table *table = (Table*)getWidget(); - - if (content.type == core::Content::START) - return false; - - // tables only contain widgets (in flow): - if ((getMask() & core::Content::WIDGET_IN_FLOW) == 0) { - content.type = core::Content::START; - return false; - } - - do { - index--; - if (index < 0) { - content.type = core::Content::START; - return false; - } - } while (table->children->get(index) == NULL || - table->children->get(index)->type != Child::CELL); - - content.type = core::Content::WIDGET_IN_FLOW; - content.widget = table->children->get(index)->cell.widget; - return true; -} void Table::TableIterator::highlight (int start, int end, core::HighlightLayer layer) { - /** todo Needs this an implementation? */ + if (inFlow ()) { + /** todo Needs this an implementation? */ + } else + highlightOOF (start, end, layer); } void Table::TableIterator::unhighlight (int direction, core::HighlightLayer layer) { + if (inFlow ()) { + // ??? + } else + unhighlightOOF (direction, layer); } void Table::TableIterator::getAllocation (int start, int end, core::Allocation *allocation) { - /** \bug Not implemented. */ + if (inFlow ()) { + /** \bug Not implemented. */ + } else + getAllocationOOF (start, end, allocation); +} + +int Table::TableIterator::numContentsInFlow () +{ + return ((Table*)getWidget())->children->size (); +} + +void Table::TableIterator::getContentInFlow (int index, + core::Content *content) +{ + Table *table = (Table*)getWidget(); + + if (table->children->get(index) != NULL && + table->children->get(index)->type == Child::CELL) { + content->type = core::Content::WIDGET_IN_FLOW; + content->widget = table->children->get(index)->cell.widget; + } else + content->type = core::Content::INVALID; } } // namespace dw diff --git a/dw/tablecell.cc b/dw/tablecell.cc index 3e143c96..a7b16776 100644 --- a/dw/tablecell.cc +++ b/dw/tablecell.cc @@ -45,7 +45,7 @@ bool isBlockLevel () int correctAvailWidthOfChild (core::Widget *widget, core::Widget *child, int width, bool forceValue) { - DBG_OBJ_ENTER_O ("resize", 0, widget, "tablecell/correctAvailWidthOfChild", + DBG_OBJ_ENTER_O ("resize", 0, widget, "tablecell::correctAvailWidthOfChild", "%p, %d, %s", child, width, forceValue ? "true" : "false"); // Make sure that this width does not exceed the width of the table @@ -80,7 +80,7 @@ void correctCorrectedRequisitionOfChild (core::Widget *widget, void (*splitHeightFun) (int, int*, int*)) { - DBG_OBJ_ENTER_O ("resize", 0, widget, "tablecell/correctRequisitionOfChild", + DBG_OBJ_ENTER_O ("resize", 0, widget, "tablecell::correctRequisitionOfChild", "%p, %d * (%d + %d), ...", child, requisition->width, requisition->ascent, requisition->descent); diff --git a/dw/tablecell.hh b/dw/tablecell.hh index 257f87c7..1300a944 100644 --- a/dw/tablecell.hh +++ b/dw/tablecell.hh @@ -31,6 +31,8 @@ int applyPerWidth (core::Widget *widget, int containerWidth, int applyPerHeight (core::Widget *widget, int containerHeight, core::style::Length perHeight); +inline bool adjustExtraSpaceWhenCorrectingRequisitionByOOF () { return false; } + } // namespace dw } // namespace dw diff --git a/dw/textblock.cc b/dw/textblock.cc index 21de1991..d1d2fb26 100644 --- a/dw/textblock.cc +++ b/dw/textblock.cc @@ -227,7 +227,6 @@ Textblock::Textblock (bool limitTextWidth) registerName ("dw::Textblock", &CLASS_ID); setButtonSensitive(true); - containingBlock = NULL; hasListitemValue = false; leftInnerPadding = 0; line1Offset = 0; @@ -252,7 +251,6 @@ Textblock::Textblock (bool limitTextWidth) nonTemporaryLines = 0; words = new misc::NotSoSimpleVector <Word> (1); anchors = new misc::SimpleVector <Anchor> (1); - outOfFlowMgr = NULL; wrapRefLines = wrapRefParagraphs = -1; @@ -267,9 +265,6 @@ Textblock::Textblock (bool limitTextWidth) lineBreakWidth = -1; DBG_OBJ_SET_NUM ("lineBreakWidth", lineBreakWidth); - verticalOffset = 0; - DBG_OBJ_SET_NUM ("verticalOffset", verticalOffset); - this->limitTextWidth = limitTextWidth; for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) { @@ -278,6 +273,11 @@ Textblock::Textblock (bool limitTextWidth) hlStart[layer].nChar = 0; hlEnd[layer].index = 0; hlEnd[layer].nChar = 0; + + DBG_OBJ_ARRATTRSET_NUM ("hlStart", layer, "index", hlStart[layer].index); + DBG_OBJ_ARRATTRSET_NUM ("hlStart", layer, "nChar", hlStart[layer].nChar); + DBG_OBJ_ARRATTRSET_NUM ("hlEnd", layer, "index", hlEnd[layer].index); + DBG_OBJ_ARRATTRSET_NUM ("hlEnd", layer, "nChar", hlEnd[layer].nChar); } initNewLine (); @@ -285,8 +285,6 @@ Textblock::Textblock (bool limitTextWidth) Textblock::~Textblock () { - _MSG("Textblock::~Textblock\n"); - /* make sure not to call a free'd tooltip (very fast overkill) */ hoverTooltip = NULL; @@ -304,16 +302,6 @@ Textblock::~Textblock () delete words; delete anchors; - if(outOfFlowMgr) { - // I feel more comfortable by letting the textblock delete these - // widgets, instead of doing this in ~OutOfFlowMgr. - - for (int i = 0; i < outOfFlowMgr->getNumWidgets (); i++) - delete outOfFlowMgr->getWidget (i); - - delete outOfFlowMgr; - } - /* Make sure we don't own widgets anymore. Necessary before call of parent class destructor. (???) */ words = NULL; @@ -347,14 +335,15 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) // Note: the breakSpace of the last line is ignored, so breaks // at the end of a textblock are not visible. - requisition->width = lastLine->maxLineWidth + leftInnerPadding - + getStyle()->boxDiffWidth (); + requisition->width = + lastLine->maxLineWidth + leftInnerPadding + boxDiffWidth (); // Also regard collapsing of this widget top margin and the top // margin of the first line box: requisition->ascent = calcVerticalBorder (getStyle()->padding.top, getStyle()->borderWidth.top, - getStyle()->margin.top, + getStyle()->margin.top + + extraSpace.top, firstLine->borderAscent, firstLine->marginAscent); @@ -367,16 +356,14 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) // for this case is not necessary.) calcVerticalBorder (getStyle()->padding.bottom, getStyle()->borderWidth.bottom, - getStyle()->margin.bottom, + getStyle()->margin.bottom + extraSpace.bottom, lastLine->borderDescent, lastLine->marginDescent); } else { - requisition->width = leftInnerPadding + getStyle()->boxDiffWidth (); - requisition->ascent = getStyle()->boxOffsetY (); - requisition->descent = getStyle()->boxRestHeight ();; + requisition->width = leftInnerPadding + boxDiffWidth (); + requisition->ascent = boxOffsetY (); + requisition->descent = boxRestHeight (); } - requisition->ascent += verticalOffset; - if (mustBeWidenedToAvailWidth ()) { DBG_OBJ_MSGF ("resize", 1, "before considering lineBreakWidth (= %d): %d * (%d + %d)", @@ -407,25 +394,7 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) // Is this really what we want? An alternative could be that // OutOfFlowMgr::getSize honours CSS attributes an corrected sizes. - DBG_OBJ_MSGF ("resize", 1, "before considering OOF widgets: %d * (%d + %d)", - requisition->width, requisition->ascent, requisition->descent); - - if (outOfFlowMgr) { - int oofWidth, oofHeight; - outOfFlowMgr->getSize (requisition, &oofWidth, &oofHeight); - - // Floats must be within the *content* area, not the *margin* - // area (which is equivalent to the requisition / - // allocation). For this reason, boxRestWidth() and - // boxRestHeight() must be considered. - - if (oofWidth + boxRestWidth () > requisition->width) - requisition->width = oofWidth + boxRestWidth (); - if (oofHeight + boxRestHeight () - > requisition->ascent + requisition->descent) - requisition->descent = - oofHeight + boxRestHeight () - requisition->ascent; - } + correctRequisitionByOOF (requisition, core::splitHeightPreserveAscent); DBG_OBJ_MSGF ("resize", 1, "final: %d * (%d + %d)", requisition->width, requisition->ascent, requisition->descent); @@ -515,7 +484,7 @@ void Textblock::getExtremesImpl (core::Extremes *extremes) extremes->minWidth, extremes->minWidthIntrinsic, extremes->maxWidth, extremes->maxWidthIntrinsic); - int diff = leftInnerPadding + getStyle()->boxDiffWidth (); + int diff = leftInnerPadding + boxDiffWidth (); extremes->minWidth += diff; extremes->minWidthIntrinsic += diff; extremes->maxWidth += diff; @@ -534,22 +503,7 @@ void Textblock::getExtremesImpl (core::Extremes *extremes) extremes->minWidth, extremes->minWidthIntrinsic, extremes->maxWidth, extremes->maxWidthIntrinsic); - if (outOfFlowMgr) { - int oofMinWidth, oofMaxWidth; - outOfFlowMgr->getExtremes (extremes, &oofMinWidth, &oofMaxWidth); - - DBG_OBJ_MSGF ("resize", 1, "OOFM correction: %d / %d", - oofMinWidth, oofMaxWidth); - - extremes->minWidth = misc::max (extremes->minWidth, oofMinWidth); - extremes->minWidthIntrinsic = - misc::max (extremes->minWidthIntrinsic, oofMinWidth); - extremes->maxWidth = misc::max (extremes->maxWidth, oofMaxWidth); - extremes->maxWidthIntrinsic = - misc::max (extremes->maxWidthIntrinsic, oofMinWidth); - extremes->adjustmentWidth = - misc::max (extremes->adjustmentWidth, oofMinWidth); - } + correctExtremesByOOF (extremes); DBG_OBJ_MSGF ("resize", 0, "finally, after considering OOFM: %d (%d) / %d (%d)", @@ -601,10 +555,10 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) (lines->size () > 0 ? calcVerticalBorder (getStyle()->padding.top, getStyle()->borderWidth.top, - getStyle()->margin.top, + getStyle()->margin.top + extraSpace.top, lines->getRef(0)->borderAscent, lines->getRef(0)->marginAscent) : - getStyle()->boxOffsetY ()) + verticalOffset); + boxOffsetY ())); childBaseAllocation.descent = allocation->ascent + allocation->descent - childBaseAllocation.ascent; @@ -614,8 +568,7 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) DBG_OBJ_SET_NUM ("childBaseAllocation.ascent", childBaseAllocation.ascent); DBG_OBJ_SET_NUM ("childBaseAllocation.descent", childBaseAllocation.descent); - if (containingBlock->outOfFlowMgr) - containingBlock->outOfFlowMgr->sizeAllocateStart (this, allocation); + sizeAllocateStart (allocation); int lineIndex, wordIndex; Line *line; @@ -755,9 +708,8 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) DBG_OBJ_MSG_END (); - if (containingBlock->outOfFlowMgr) - containingBlock->outOfFlowMgr->sizeAllocateEnd (this); - + sizeAllocateEnd (); + for (int i = 0; i < anchors->size(); i++) { Anchor *anchor = anchors->getRef(i); int y; @@ -777,32 +729,52 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) DBG_OBJ_LEAVE (); } +void Textblock::calcExtraSpaceImpl () +{ + OOFAwareWidget::calcExtraSpaceImpl (); + + int clearPosition = 0; + for (int i = 0; i < NUM_OOFM; i++) + if (searchOutOfFlowMgr (i)) + clearPosition = + misc::max (clearPosition, + searchOutOfFlowMgr(i)->getClearPosition (this)); + + extraSpace.top = misc::max (extraSpace.top, clearPosition); +} + int Textblock::getAvailWidthOfChild (Widget *child, bool forceValue) { - DBG_OBJ_ENTER ("resize", 0, "Textblock/getAvailWidthOfChild", "%p, %s", + DBG_OBJ_ENTER ("resize", 0, "Textblock::getAvailWidthOfChild", "%p, %s", child, forceValue ? "true" : "false"); int width; - if (child->getStyle()->width == core::style::LENGTH_AUTO) { - // No width specified: similar to standard implementation (see - // there), but "leftInnerPadding" has to be considered, too. - DBG_OBJ_MSG ("resize", 1, "no specification"); - if (forceValue) - width = misc::max (getAvailWidth (true) - boxDiffWidth () - - leftInnerPadding, - 0); - else - width = -1; - } else - width = Widget::getAvailWidthOfChild (child, forceValue); - - if (forceValue && this == child->getContainer () && - !mustBeWidenedToAvailWidth ()) { - core::Extremes extremes; - getExtremes (&extremes); - if (width > extremes.maxWidth - boxDiffWidth () - leftInnerPadding) - width = extremes.maxWidth - boxDiffWidth () - leftInnerPadding; + if (isWidgetOOF (child) && getWidgetOutOfFlowMgr(child) && + getWidgetOutOfFlowMgr(child)->dealingWithSizeOfChild (child)) + width = + getWidgetOutOfFlowMgr(child)->getAvailWidthOfChild (child,forceValue); + else { + if (child->getStyle()->width == core::style::LENGTH_AUTO) { + // No width specified: similar to standard implementation (see + // there), but "leftInnerPadding" has to be considered, too. + DBG_OBJ_MSG ("resize", 1, "no specification"); + if (forceValue) + width = misc::max (getAvailWidth (true) - boxDiffWidth () + - leftInnerPadding, + 0); + else + width = -1; + } else + width = Widget::getAvailWidthOfChild (child, forceValue); + + if (forceValue && this == child->getContainer () && + !mustBeWidenedToAvailWidth ()) { + core::Extremes extremes; + getExtremes (&extremes); + if (width > extremes.maxWidth - boxDiffWidth () - leftInnerPadding) + width = extremes.maxWidth - boxDiffWidth () - leftInnerPadding; + } } DBG_OBJ_MSGF ("resize", 1, "=> %d", width); @@ -810,7 +782,15 @@ int Textblock::getAvailWidthOfChild (Widget *child, bool forceValue) return width; } - +int Textblock::getAvailHeightOfChild (core::Widget *child, bool forceValue) +{ + if (isWidgetOOF(child) && getWidgetOutOfFlowMgr(child) && + getWidgetOutOfFlowMgr(child)->dealingWithSizeOfChild (child)) + return getWidgetOutOfFlowMgr(child)->getAvailHeightOfChild (child, + forceValue); + else + return Widget::getAvailHeightOfChild (child, forceValue); +} void Textblock::containerSizeChangedForChildren () { @@ -822,9 +802,8 @@ void Textblock::containerSizeChangedForChildren () word->content.widget->containerSizeChanged (); } - if (outOfFlowMgr) - outOfFlowMgr->containerSizeChangedForChildren (); - + containerSizeChangedForChildrenOOF (); + DBG_OBJ_LEAVE (); } @@ -833,7 +812,7 @@ bool Textblock::affectsSizeChangeContainerChild (Widget *child) DBG_OBJ_ENTER ("resize", 0, "Textblock/affectsSizeChangeContainerChild", "%p", child); - // See Textblock::getAvailWidthForChild() and Textblock::oofSizeChanged(): + // See Textblock::getAvailWidthOfChild() and Textblock::oofSizeChanged(): // Extremes changes affect the size of the child, too: bool ret; if (!mustBeWidenedToAvailWidth () && @@ -876,13 +855,10 @@ void Textblock::markSizeChange (int ref) { DBG_OBJ_ENTER ("resize", 0, "markSizeChange", "%d", ref); - if (OutOfFlowMgr::isRefOutOfFlow (ref)) { - assert (outOfFlowMgr != NULL); - outOfFlowMgr->markSizeChange (ref); - } else { - PRINTF ("[%p] MARK_SIZE_CHANGE (%d): %d => ...\n", - this, ref, wrapRefLines); - + if (isParentRefOOF (ref)) + getParentRefOutOfFlowMgr(ref) + ->markSizeChange (getParentRefOOFSubRef (ref)); + else { /* By the way: ref == -1 may have two different causes: (i) flush() calls "queueResize (-1, true)", when no rewrapping is necessary; and (ii) a word may have parentRef == -1 , when it is not yet @@ -890,10 +866,10 @@ void Textblock::markSizeChange (int ref) now, but addLine(...) will do everything necessary. */ if (ref != -1) { if (wrapRefLines == -1) - wrapRefLines = OutOfFlowMgr::getLineNoFromRef (ref); + wrapRefLines = getParentRefInFlowSubRef (ref); else wrapRefLines = misc::min (wrapRefLines, - OutOfFlowMgr::getLineNoFromRef (ref)); + getParentRefInFlowSubRef (ref)); } DBG_OBJ_SET_NUM ("wrapRefLines", wrapRefLines); @@ -913,13 +889,10 @@ void Textblock::markExtremesChange (int ref) { DBG_OBJ_ENTER ("resize", 1, "markExtremesChange", "%d", ref); - if (OutOfFlowMgr::isRefOutOfFlow (ref)) { - assert (outOfFlowMgr != NULL); - outOfFlowMgr->markExtremesChange (ref); - } else { - PRINTF ("[%p] MARK_EXTREMES_CHANGE (%d): %d => ...\n", - this, ref, wrapRefParagraphs); - + if (isParentRefOOF (ref)) + getParentRefOutOfFlowMgr(ref) + ->markExtremesChange (getParentRefOOFSubRef (ref)); + else { /* By the way: ref == -1 may have two different causes: (i) flush() calls "queueResize (-1, true)", when no rewrapping is necessary; and (ii) a word may have parentRef == -1 , when it is not yet @@ -927,11 +900,10 @@ void Textblock::markExtremesChange (int ref) now, but addLine(...) will do everything necessary. */ if (ref != -1) { if (wrapRefParagraphs == -1) - wrapRefParagraphs = OutOfFlowMgr::getLineNoFromRef (ref); + wrapRefParagraphs = getParentRefInFlowSubRef (ref); else wrapRefParagraphs = - misc::min (wrapRefParagraphs, - OutOfFlowMgr::getLineNoFromRef (ref)); + misc::min (wrapRefParagraphs, getParentRefInFlowSubRef (ref)); } DBG_OBJ_SET_NUM ("wrapRefParagraphs", wrapRefParagraphs); @@ -940,61 +912,6 @@ void Textblock::markExtremesChange (int ref) DBG_OBJ_LEAVE (); } -void Textblock::notifySetAsTopLevel() -{ - PRINTF ("%p becomes toplevel\n", this); - containingBlock = this; - PRINTF ("-> %p is its own containing block\n", this); -} - -bool Textblock::isContainingBlock (Widget *widget) -{ - return - // Of course, only textblocks are considered as containing - // blocks. - widget->instanceOf (Textblock::CLASS_ID) && - // The second condition: that this block is "out of flow", in a - // wider sense. - (// The toplevel widget is "out of flow", since there is no - // parent, and so no context. - widget->getParent() == NULL || - // A similar reasoning applies to a widget with another parent - // than a textblock (typical example: a table cell (this is - // also a text block) within a table widget). - !widget->getParent()->instanceOf (Textblock::CLASS_ID) || - // Inline blocks are containing blocks, too. - widget->getStyle()->display == core::style::DISPLAY_INLINE_BLOCK || - // Same for blocks with 'overview' set to another value than - // (the default value) 'visible'. - widget->getStyle()->overflow != core::style::OVERFLOW_VISIBLE || - // Finally, "out of flow" in a narrower sense: floats and - // absolute positions. - OutOfFlowMgr::isWidgetOutOfFlow (widget)); -} - -void Textblock::notifySetParent () -{ - PRINTF ("%p becomes a child of %p\n", this, getParent()); - - // Search for containing Box. - containingBlock = NULL; - - for (Widget *widget = this; widget != NULL && containingBlock == NULL; - widget = widget->getParent()) - if (isContainingBlock (widget)) { - containingBlock = (Textblock*)widget; - - if (containingBlock == this) { - PRINTF ("-> %p is its own containing block\n", this); - } else { - PRINTF ("-> %p becomes containing block of %p\n", - containingBlock, this); - } - } - - assert (containingBlock != NULL); -} - bool Textblock::isBlockLevel () { return true; @@ -1084,6 +1001,8 @@ void Textblock::leaveNotifyImpl (core::EventCrossing *event) bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType, core::MousePositionEvent *event) { + DBG_OBJ_ENTER0 ("events", 0, "sendSelectionEvent"); + core::Iterator *it; int wordIndex; int charPos = 0, link = -1; @@ -1213,11 +1132,17 @@ bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType, } } } - - it = new TextblockIterator (this, core::Content::maskForSelection (true), - false, wordIndex); + + DBG_OBJ_MSGF ("events", 1, "wordIndex = %d", wordIndex); + DBG_MSG_WORD ("events", 1, "<i>this is:</i> ", wordIndex, ""); + + it = TextblockIterator::createWordIndexIterator + (this, core::Content::maskForSelection (true), wordIndex); r = selectionHandleEvent (eventType, it, charPos, link, event); it->unref (); + + DBG_OBJ_MSGF ("events", 1, "=> %s", r ? "true" : "false"); + DBG_OBJ_LEAVE (); return r; } @@ -1538,24 +1463,29 @@ void Textblock::drawSpace(int wordIndex, core::View *view, * - area is used always (ev. set it to event->area) * - event is only used when is_expose */ -void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area) +void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area, + core::DrawingContext *context) { - DBG_OBJ_ENTER ("draw", 0, "drawLine", "..., %d, %d, %d * %d", + DBG_OBJ_ENTER ("draw", 0, "drawLine", "..., [%d, %d, %d * %d]", area->x, area->y, area->width, area->height); - + int xWidget = line->textOffset; int yWidgetBase = lineYOffsetWidget (line) + line->borderAscent; DBG_OBJ_MSGF ("draw", 1, "line from %d to %d (%d words), at (%d, %d)", line->firstWord, line->lastWord, words->size (), xWidget, yWidgetBase); - DBG_MSG_WORD ("draw", 0, "<i>line starts with: </i>", line->firstWord, ""); - DBG_MSG_WORD ("draw", 0, "<i>line ends with: </i>", line->lastWord, ""); + DBG_MSG_WORD ("draw", 1, "<i>line starts with: </i>", line->firstWord, ""); + DBG_MSG_WORD ("draw", 1, "<i>line ends with: </i>", line->lastWord, ""); + + DBG_OBJ_MSG_START (); for (int wordIndex = line->firstWord; wordIndex <= line->lastWord && xWidget < area->x + area->width; wordIndex++) { - Word *word = words->getRef(wordIndex); + DBG_MSG_WORD ("draw", 2, "<i>drawing: </i>", wordIndex, ""); + + Word *word = words->getRef (wordIndex); int wordSize = word->size.width; if (xWidget + wordSize + word->hyphenWidth + word->effSpace >= area->x) { @@ -1566,9 +1496,10 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area) if (word->content.type == core::Content::WIDGET_IN_FLOW) { core::Widget *child = word->content.widget; core::Rectangle childArea; - - if (child->intersects (area, &childArea)) - child->draw (view, &childArea); + if (!core::StackingContextMgr::handledByStackingContextMgr + (child) && + child->intersects (this, area, &childArea)) + child->draw (view, &childArea, context); } else { int wordIndex2 = wordIndex; while (wordIndex2 < line->lastWord && @@ -1596,8 +1527,8 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area) xWidget + wordSize, yWidgetBase - line->borderAscent, word->effSpace, line->borderAscent + line->borderDescent, false); - drawSpace(wordIndex, view, area, xWidget + wordSize, - yWidgetBase); + drawSpace (wordIndex, view, area, xWidget + wordSize, + yWidgetBase); } } @@ -1605,6 +1536,8 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area) xWidget += wordSize + word->effSpace; } + DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } @@ -1626,7 +1559,8 @@ int Textblock::findLineIndexWhenNotAllocated (int y) return findLineIndex (y, calcVerticalBorder (getStyle()->padding.top, getStyle()->borderWidth.top, - getStyle()->margin.top, + getStyle()->margin.top + + extraSpace.top, lines->getRef(0)->borderAscent, lines->getRef(0)->marginAscent)); } @@ -1772,38 +1706,45 @@ Textblock::Word *Textblock::findWord (int x, int y, bool *inSpace) return NULL; } -void Textblock::draw (core::View *view, core::Rectangle *area) +void Textblock::drawLevel (core::View *view, core::Rectangle *area, + int level, core::DrawingContext *context) { - DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d", - area->x, area->y, area->width, area->height); + DBG_OBJ_ENTER ("draw", 0, "Textblock::drawLevel", "(%d, %d, %d * %d), %s", + area->x, area->y, area->width, area->height, + stackingLevelText (level)); - int lineIndex; - Line *line; - - // Instead of drawWidgetBox, use drawBox to include verticalOffset. - if (getParent() == NULL) { - // The toplevel (parent == NULL) widget is a special case, which - // we leave to drawWidgetBox; verticalOffset will here always 0. - assert (verticalOffset == 0); - drawWidgetBox (view, area, false); - } else - drawBox (view, getStyle(), area, 0, verticalOffset, allocation.width, - getHeight() - verticalOffset, false); + switch (level) { + case SL_IN_FLOW: + for (int lineIndex = findLineIndexWhenAllocated (area->y); + lineIndex < lines->size (); lineIndex++) { + Line *line = lines->getRef (lineIndex); + if (lineYOffsetWidget (line) >= area->y + area->height) + break; - lineIndex = findLineIndexWhenAllocated (area->y); + DBG_OBJ_MSGF ("draw", 0, "line %d (of %d)", lineIndex, lines->size ()); + drawLine (line, view, area, context); + } + break; - for (; lineIndex < lines->size (); lineIndex++) { - line = lines->getRef (lineIndex); - if (lineYOffsetWidget (line) >= area->y + area->height) - break; + case SL_OOF_REF: + // TODO Inefficient. Perhaps store OOF references in seperate + // (much smaller!) list. + for (int oofmIndex = 0; oofmIndex < NUM_OOFM; oofmIndex++) { + for (int wordIndex = 0; wordIndex < words->size (); wordIndex++) { + Word *word = words->getRef (wordIndex); + if (word->content.type == core::Content::WIDGET_OOF_REF && + getOOFMIndex (word->content.widget) == oofmIndex && + doesWidgetOOFInterruptDrawing (word->content.widget)) + word->content.widget->drawInterruption (view, area, context); + } + } + break; - DBG_OBJ_MSGF ("draw", 0, "line %d (of %d)", lineIndex, lines->size ()); - drawLine (line, view, area); + default: + OOFAwareWidget::drawLevel (view, area, level, context); + break; } - if(outOfFlowMgr) - outOfFlowMgr->draw(view, area); - DBG_OBJ_LEAVE (); } @@ -1914,6 +1855,7 @@ void Textblock::fillWord (int wordNo, int width, int ascent, int descent, word->size.width = width; word->size.ascent = ascent; word->size.descent = descent; + DBG_SET_WORD_SIZE (wordNo); word->origSpace = word->effSpace = 0; word->hyphenWidth = 0; word->badnessAndPenalty.setPenalty (PENALTY_PROHIBIT_BREAK); @@ -2364,22 +2306,26 @@ void Textblock::addWidget (core::Widget *widget, core::style::Style *style) widget->setStyle (style); - PRINTF ("adding the %s %p to %p (word %d) ...\n", - widget->getClassName(), widget, this, words->size()); - - if (containingBlock->outOfFlowMgr == NULL) { - containingBlock->outOfFlowMgr = new OutOfFlowMgr (containingBlock); - DBG_OBJ_ASSOC (containingBlock, containingBlock->outOfFlowMgr); - } + initOutOfFlowMgrs (); - if (OutOfFlowMgr::isWidgetOutOfFlow (widget)) { - PRINTF (" -> out of flow.\n"); + if (testWidgetOutOfFlow (widget)) { + int oofmIndex = getOOFMIndex (widget); + DBG_OBJ_MSGF ("construct.word", 1, "ouf of flow: oofmIndex = %d (%s)", + oofmIndex, OOFM_NAME[oofmIndex]); - widget->setParent (containingBlock); + widget->setParent (oofContainer[oofmIndex]); widget->setGenerator (this); - containingBlock->outOfFlowMgr->addWidgetOOF (widget, this, - words->size ()); - Word *word = addWord (0, 0, 0, 0, style); + + oof::OutOfFlowMgr *oofm = searchOutOfFlowMgr (oofmIndex); + int oofmSubRef = oofm->addWidgetOOF (widget, this, words->size ()); + widget->parentRef = makeParentRefOOF (oofmIndex, oofmSubRef); + + DBG_OBJ_MSGF ("construct.word", 1, "oofmSubRef = %d => parentRef = %d", + oofmSubRef, widget->parentRef); + + core::Requisition size; + oofm->calcWidgetRefSize (widget, &size); + Word *word = addWord (size.width, size.ascent, size.descent, 0, style); word->content.type = core::Content::WIDGET_OOF_REF; word->content.widget = widget; @@ -2387,14 +2333,16 @@ void Textblock::addWidget (core::Widget *widget, core::style::Style *style) // problems with breaking near float definitions.) setBreakOption (word, style, 0, 0, false); } else { - PRINTF (" -> within flow.\n"); + DBG_OBJ_MSG ("construct.word", 1, "in flow"); widget->setParent (this); // TODO Replace (perhaps) later "textblock" by "OOF aware widget". - if (widget->instanceOf (Textblock::CLASS_ID)) - containingBlock->outOfFlowMgr->addWidgetInFlow ((Textblock*)widget, - this, words->size ()); + if (widget->instanceOf (Textblock::CLASS_ID)) { + for (int i = 0; i < NUM_OOFM; i++) + searchOutOfFlowMgr(i)->addWidgetInFlow ((Textblock*)widget, this, + words->size ()); + } core::Requisition size; widget->sizeRequest (&size); @@ -2632,7 +2580,7 @@ void Textblock::addParbreak (int space, core::style::Style *style) for (Widget *widget = this; widget->getParent() != NULL && widget->getParent()->instanceOf (Textblock::CLASS_ID) && - !OutOfFlowMgr::isRefOutOfFlow (widget->parentRef); + !isWidgetOOF (widget); widget = widget->getParent ()) { Textblock *textblock2 = (Textblock*)widget->getParent (); int index = textblock2->hasListitemValue ? 1 : 0; @@ -2643,7 +2591,7 @@ void Textblock::addParbreak (int space, core::style::Style *style) if (!isfirst) { /* The text block we searched for has been found. */ Word *word2; - int lineno = OutOfFlowMgr::getLineNoFromRef (widget->parentRef); + int lineno = getWidgetInFlowSubRef (widget); if (lineno > 0 && (word2 = @@ -2652,8 +2600,7 @@ void Textblock::addParbreak (int space, core::style::Style *style) word2->content.type == core::Content::BREAK) { if (word2->content.breakSpace < space) { word2->content.breakSpace = space; - textblock2->queueResize - (OutOfFlowMgr::createRefNormalFlow (lineno), false); + textblock2->queueResize (makeParentRefInFlow (lineno), false); textblock2->mustQueueResize = false; } } @@ -2752,63 +2699,66 @@ void Textblock::breakAdded () words->getRef(words->size () - 2)->effSpace = 0; } -/** - * \brief Search recursively through widget. - * - * This is an optimized version of the general - * dw::core::Widget::getWidgetAtPoint method. - */ -core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level) -{ - //printf ("%*s-> examining the %s %p (%d, %d, %d x (%d + %d))\n", - // 3 * level, "", getClassName (), this, allocation.x, allocation.y, - // allocation.width, allocation.ascent, allocation.descent); - - int lineIndex, wordIndex; - Line *line; - - if (x < allocation.x || - y < allocation.y || - x > allocation.x + allocation.width || - y > allocation.y + getHeight ()) { - return NULL; - } - - // First, search for widgets out of flow, notably floats, since - // there are cases where they overlap child textblocks. Should - // later be refined using z-index. - Widget *oofWidget = - outOfFlowMgr ? outOfFlowMgr->getWidgetAtPoint (x, y, level) : NULL; - if (oofWidget) - return oofWidget; - - lineIndex = findLineIndexWhenAllocated (y - allocation.y); - - if (lineIndex < 0 || lineIndex >= lines->size ()) { - return this; - } - - line = lines->getRef (lineIndex); - - for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) { - Word *word = words->getRef (wordIndex); - - if (word->content.type == core::Content::WIDGET_IN_FLOW) { - core::Widget * childAtPoint; - if (word->content.widget->wasAllocated ()) { - childAtPoint = word->content.widget->getWidgetAtPoint (x, y, - level + 1); - if (childAtPoint) { - return childAtPoint; +core::Widget *Textblock::getWidgetAtPointLevel (int x, int y, int level, + core::GettingWidgetAtPointContext + *context) +{ + DBG_OBJ_ENTER ("events", 0, "Textblock::getWidgetAtPointLevel", "%d, %d, %s", + x, y, stackingLevelText (level)); + + Widget *widgetAtPoint = NULL; + + switch (level) { + case SL_IN_FLOW: + { + int lineIndex = findLineIndexWhenAllocated (y - allocation.y); + + if (lineIndex >= 0 && lineIndex < lines->size ()) { + Line *line = lines->getRef (lineIndex); + + for (int wordIndex = line->lastWord; + widgetAtPoint == NULL && wordIndex >= line->firstWord; + wordIndex--) { + Word *word = words->getRef (wordIndex); + if (word->content.type == core::Content::WIDGET_IN_FLOW && + !core::StackingContextMgr::handledByStackingContextMgr + (word->content.widget)) + widgetAtPoint = + word->content.widget->getWidgetAtPoint (x, y, context); } } } + break; + + case SL_OOF_REF: + // TODO Inefficient. Perhaps store OOF references in seperate + // (much smaller!) list. + for (int oofmIndex = NUM_OOFM; widgetAtPoint == NULL && oofmIndex >= 0; + oofmIndex--) { + for (int wordIndex = words->size () - 1; + widgetAtPoint == NULL && wordIndex >= 0; wordIndex--) { + Word *word = words->getRef (wordIndex); + if (word->content.type == core::Content::WIDGET_OOF_REF && + getOOFMIndex (word->content.widget) == oofmIndex && + doesWidgetOOFInterruptDrawing (word->content.widget)) + widgetAtPoint = + word->content.widget->getWidgetAtPointInterrupted (x, y, + context); + } + } + break; + + default: + widgetAtPoint = + OOFAwareWidget::getWidgetAtPointLevel (x, y, level, context); + break; } - return this; + DBG_OBJ_MSGF ("events", 1, "=> %p", widgetAtPoint); + DBG_OBJ_LEAVE (); + return widgetAtPoint; } - /** * This function "hands" the last break of a page "over" to a parent * page. This is used for "collapsing spaces". @@ -2909,6 +2859,8 @@ void Textblock::changeWordStyle (int from, int to, core::style::Style *style, void Textblock::queueDrawRange (int index1, int index2) { + DBG_OBJ_ENTER ("draw", 0, "queueDrawRange", "%d, %d", index1, index2); + int from = misc::min (index1, index2); int to = misc::max (index1, index2); @@ -2930,18 +2882,6 @@ void Textblock::queueDrawRange (int index1, int index2) queueDrawArea (0, y, allocation.width, h); } -} - -void Textblock::setVerticalOffset (int verticalOffset) -{ - DBG_OBJ_ENTER ("resize", 0, "setVerticalOffset", "%d", verticalOffset); - - if (this->verticalOffset != verticalOffset) { - this->verticalOffset = verticalOffset; - DBG_OBJ_SET_NUM ("verticalOffset", verticalOffset); - mustQueueResize = true; - queueDraw (); // Could perhaps be optimized. - } DBG_OBJ_LEAVE (); } @@ -2951,18 +2891,24 @@ bool Textblock::mustBeWidenedToAvailWidth () DBG_OBJ_ENTER0 ("resize", 0, "mustBeWidenedToAvailWidth"); bool toplevel = getParent () == NULL, block = getStyle()->display == core::style::DISPLAY_BLOCK, - vloat = getStyle()->vloat != core::style::FLOAT_NONE, - result = toplevel || (block && !vloat); + vloat = testWidgetFloat (this), + abspos = testWidgetAbsolutelyPositioned (this), + fixpos = testWidgetFixedlyPositioned (this), + // In detail, this depends on what the respective OOFM does + // with the child widget: + result = toplevel || (block && !(vloat || abspos || fixpos)); DBG_OBJ_MSGF ("resize", 0, - "=> %s (toplevel: %s, block: %s, float: %s)", + "=> %s (toplevel: %s, block: %s, float: %s, abspos: %s, " + "fixpos: %s)", result ? "true" : "false", toplevel ? "true" : "false", - block ? "true" : "false", vloat ? "true" : "false"); + block ? "true" : "false", vloat ? "true" : "false", + abspos ? "true" : "false", fixpos ? "true" : "false"); DBG_OBJ_LEAVE (); return result; } /** - * Called by dw::OutOfFlowMgr when the border has changed due to a + * Called by dw::OOFFloatsMgr when the border has changed due to a * float (or some floats). * * "y", which given in widget coordinates, denotes the minimal @@ -3091,7 +3037,7 @@ void Textblock::borderChanged (int y, Widget *vloat) minWrapLineIndex, maxWrapLineIndex, vloat->getGenerator() == this ? "yes" : "no"); - queueResize (OutOfFlowMgr::createRefNormalFlow (realWrapLineIndex), true); + queueResize (makeParentRefInFlow (realWrapLineIndex), true); // Notice that the line no. realWrapLineIndex may not exist yet. if (realWrapLineIndex == 0) @@ -3112,6 +3058,13 @@ void Textblock::borderChanged (int y, Widget *vloat) DBG_OBJ_LEAVE (); } +void Textblock::widgetRefSizeChanged (int externalIndex) +{ + int lineNo = findLineOfWord (externalIndex); + if (lineNo >= 0 && lineNo < lines->size ()) + queueResize (makeParentRefInFlow (lineNo), true); +} + void Textblock::clearPositionChanged () { DBG_OBJ_ENTER0 ("resize", 0, "clearPositionChanged"); @@ -3127,7 +3080,7 @@ void Textblock::oofSizeChanged (bool extremesChanged) extremesChanged ? "true" : "false"); queueResize (-1, extremesChanged); - // See Textblock::getAvailWidthForChild(): Extremes changes may become also + // See Textblock::getAvailWidthOfChild(): Extremes changes may become also // relevant for the children, under certain conditions: if (extremesChanged && !mustBeWidenedToAvailWidth ()) containerSizeChanged (); @@ -3135,6 +3088,21 @@ void Textblock::oofSizeChanged (bool extremesChanged) DBG_OBJ_LEAVE (); } +int Textblock::getLineBreakWidth () +{ + return lineBreakWidth; +} + +bool Textblock::isPossibleContainer (int oofmIndex) +{ + return true; +} + +bool Textblock::isPossibleContainerParent (int oofmIndex) +{ + return true; +} + RegardingBorder *Textblock::getWidgetRegardingBorderForLine (Line *line) { return getWidgetRegardingBorderForLine (line->firstWord, line->lastWord); @@ -3168,16 +3136,10 @@ RegardingBorder *Textblock::getWidgetRegardingBorderForLine (int firstWord, if (word->content.type == core::Content::WIDGET_IN_FLOW) { Widget *widget = word->content.widget; if (widget->instanceOf (RegardingBorder::CLASS_ID) && - // Exclude some cases where a widget constitutes a new - // container (see definition of float container in - // Textblock::isContainingBlock). - widget->getStyle()->display != core::style::DISPLAY_INLINE_BLOCK && - widget->getStyle()->overflow == core::style::OVERFLOW_VISIBLE) + // Exclude cases where a textblock constitutes a new floats + // container. + !isOOFContainer (widget, OOFM_FLOATS)) widgetRegardingBorder = (RegardingBorder*)widget; - - // (TODO: It would look nicer if there is one common place - // for such definitions. Will be fixed in "dillo_grows", not - // here.) } } @@ -3209,18 +3171,17 @@ int Textblock::yOffsetOfLineToBeCreated () int result; if (lines->size () == 0) { - result = verticalOffset + calcVerticalBorder (getStyle()->padding.top, - getStyle()->borderWidth.top, - getStyle()->margin.top, - 0, 0); + result = calcVerticalBorder (getStyle()->padding.top, + getStyle()->borderWidth.top + extraSpace.top, + getStyle()->margin.top, 0, 0); DBG_OBJ_MSGF ("line.yoffset", 1, "first line: ... = %d", result); } else { Line *firstLine = lines->getRef (0), *lastLine = lines->getLastRef (); - result = verticalOffset + calcVerticalBorder (getStyle()->padding.top, - getStyle()->borderWidth.top, - getStyle()->margin.top, - firstLine->borderAscent, - firstLine->marginAscent) + result = calcVerticalBorder (getStyle()->padding.top, + getStyle()->borderWidth.top, + getStyle()->margin.top + extraSpace.top, + firstLine->borderAscent, + firstLine->marginAscent) - firstLine->borderAscent + lastLine->top + lastLine->totalHeight (0); DBG_OBJ_MSGF ("line.yoffset", 1, "other line: ... = %d", result); } @@ -3230,4 +3191,27 @@ int Textblock::yOffsetOfLineToBeCreated () return result; } +/** + * Includes margin, border, and padding. Can be used without allocation. + */ +int Textblock::yOffsetOfLineCreated (Line *line) +{ + // Similar applies (regarding exactness) as to yOffsetOfLineToBeCreated. + + DBG_OBJ_ENTER0 ("line.yoffset", 0, "yOffsetOfLineToBeCreated"); + + int result; + + Line *firstLine = lines->getRef (0); + result = calcVerticalBorder (getStyle()->padding.top, + getStyle()->borderWidth.top, + getStyle()->margin.top + extraSpace.top, + firstLine->borderAscent, + firstLine->marginAscent) + - firstLine->borderAscent + line->top; + + DBG_OBJ_LEAVE_VAL ("%d", result); + return result; +} + } // namespace dw diff --git a/dw/textblock.hh b/dw/textblock.hh index 162088dc..db5d845f 100644 --- a/dw/textblock.hh +++ b/dw/textblock.hh @@ -4,7 +4,6 @@ #include <limits.h> #include "regardingborder.hh" -#include "outofflowmgr.hh" #include "../lout/misc.hh" // These were used when improved line breaking and hyphenation were implemented. @@ -24,10 +23,9 @@ namespace dw { * * <div style="border: 2px solid #ffff00; margin-top: 0.5em; * margin-bottom: 0.5em; padding: 0.5em 1em; background-color: - * #ffffe0"><b>Info:</b> The recent changes (line breaking and - * hyphenation on one hand, floats on the other hand) have not yet - * been incorporated into this documentation. See \ref - * dw-line-breaking and \ref dw-out-of-flow.</div> + * #ffffe0"><b>Info:</b> Some (not so) recent changes, line breaking + * and hyphenation, have not yet been incorporated into this + * documentation. See \ref dw-line-breaking.</div> * * <h3>Signals</h3> * @@ -164,11 +162,11 @@ namespace dw { * Anchors associate the anchor name with the index of the next word at * the point of the anchor. * - * <h4>Incremental Resizing</h4> + * <h3>Incremental Resizing</h3> * * dw::Textblock makes use of incremental resizing as described in \ref * dw-widget-sizes. The parentRef is, for children of a dw::Textblock, simply - * the number of the line. + * the number of the line. [<b>Update:</b> Incorrect; see \ref dw-out-of-flow.] * * Generally, there are three cases which may change the size of the * widget: @@ -195,6 +193,12 @@ namespace dw { * dw::Textblock, which has the value -1 if no rewrapping of lines * necessary, or otherwise the line from which a rewrap is necessary. * + * <h3>Widgets Ouf Of Flow</h3> + * + * See + * + * - dw::oof::OOFAwareWidget (base class) and + * - \ref dw-out-of-flow. */ class Textblock: public RegardingBorder { @@ -290,10 +294,8 @@ private: static const char *hyphenDrawChar; - Textblock *containingBlock; - OutOfFlowMgr *outOfFlowMgr; - protected: + /** * \brief Implementation used for words. */ @@ -507,27 +509,24 @@ protected: int wordIndex; }; - class TextblockIterator: public core::Iterator + class TextblockIterator: public OOFAwareWidgetIterator { - private: - bool oofm; - int index; + protected: + int numContentsInFlow (); + void getContentInFlow (int index, core::Content *content); public: TextblockIterator (Textblock *textblock, core::Content::Type mask, bool atEnd); - TextblockIterator (Textblock *textblock, core::Content::Type mask, - bool oofm, int index); + + static TextblockIterator *createWordIndexIterator + (Textblock *textblock, core::Content::Type mask, int wordIndex); lout::object::Object *clone(); - int compareTo(lout::object::Comparable *other); - bool next (); - bool prev (); void highlight (int start, int end, core::HighlightLayer layer); void unhighlight (int direction, core::HighlightLayer layer); void getAllocation (int start, int end, core::Allocation *allocation); - void print (); }; friend class TextblockIterator; @@ -585,9 +584,6 @@ protected: /* This value is (currently) set by setAscent(). */ int lineBreakWidth; - // Additional vertical offset, used for the "clear" attribute. - int verticalOffset; - int wrapRefLines, wrapRefParagraphs; /* 0-based. Important: Both are the line numbers, not the value stored in @@ -637,7 +633,6 @@ protected: void calcBorders (int lastOofRef, int height); void showMissingLines (); void removeTemporaryLines (); - void setVerticalOffset (int verticalOffset); void decorateText (core::View *view, core::style::Style *style, core::style::Color::Shading shading, @@ -654,7 +649,9 @@ protected: core::Rectangle *area, int xWidget, int yWidgetBase); void drawSpace (int wordIndex, core::View *view, core::Rectangle *area, int xWidget, int yWidgetBase); - void drawLine (Line *line, core::View *view, core::Rectangle *area); + void drawLine (Line *line, core::View *view, core::Rectangle *area, + core::DrawingContext *context); + int findLineIndex (int y); int findLineIndexWhenNotAllocated (int y); int findLineIndexWhenAllocated (int y); @@ -768,6 +765,7 @@ protected: void printBorderChangedErrorAndAbort (int y, Widget *vloat, int wrapLineIndex); int yOffsetOfLineToBeCreated (); + int yOffsetOfLineCreated (Line *line); bool sendSelectionEvent (core::SelectionState::EventType eventType, core::MousePositionEvent *event); @@ -775,6 +773,7 @@ protected: void processWord (int wordIndex); virtual int wordWrap (int wordIndex, bool wrapAll); int wrapWordInFlow (int wordIndex, bool wrapAll); + int wrapWordOofRef (int wordIndex, bool wrapAll); void balanceBreakPosAndHeight (int wordIndex, int firstIndex, int *searchUntil, bool tempNewLine, int penaltyIndex, bool borderIsCalculated, @@ -809,10 +808,21 @@ protected: void alignLine (int lineIndex); void calcTextOffset (int lineIndex, int totalWidth); + void drawLevel (core::View *view, core::Rectangle *area, int level, + core::DrawingContext *context); + + Widget *getWidgetAtPointLevel (int x, int y, int level, + core::GettingWidgetAtPointContext *context); + void sizeRequestImpl (core::Requisition *requisition); void getExtremesImpl (core::Extremes *extremes); void sizeAllocateImpl (core::Allocation *allocation); - int getAvailWidthOfChild (Widget *child, bool forceValue); + + void calcExtraSpaceImpl (); + + int getAvailWidthOfChild (core::Widget *child, bool forceValue); + int getAvailHeightOfChild (core::Widget *child, bool forceValue); + void containerSizeChangedForChildren (); bool affectsSizeChangeContainerChild (Widget *child); bool usesAvailWidth (); @@ -821,13 +831,8 @@ protected: void markSizeChange (int ref); void markExtremesChange (int ref); - void notifySetAsTopLevel(); - void notifySetParent(); - bool isBlockLevel (); - void draw (core::View *view, core::Rectangle *area); - bool buttonPressImpl (core::EventButton *event); bool buttonReleaseImpl (core::EventButton *event); bool motionNotifyImpl (core::EventMotion *event); @@ -842,7 +847,6 @@ protected: core::style::Style *style, int numBreaks, int *breakPos, core::Requisition *wordSize); - static bool isContainingBlock (Widget *widget); public: static int CLASS_ID; @@ -854,8 +858,12 @@ public: static void setPenaltyEmDashRight2 (int penaltyRightEmDash2); static void setStretchabilityFactor (int stretchabilityFactor); - Textblock(bool limitTextWidth); - ~Textblock(); + static inline bool mustAddBreaks (core::style::Style *style) + { return !testStyleOutOfFlow (style) || + testStyleRelativelyPositioned (style); } + + Textblock (bool limitTextWidth); + ~Textblock (); core::Iterator *iterator (core::Content::Type mask, bool atEnd); @@ -864,8 +872,6 @@ public: void addText (const char *text, size_t len, core::style::Style *style); inline void addText (const char *text, core::style::Style *style) { addText (text, strlen(text), style); } - static bool isStyleOutOfFlow (core::style::Style *style) - { return OutOfFlowMgr::isStyleOutOfFlow (style); } void addWidget (core::Widget *widget, core::style::Style *style); bool addAnchor (const char *name, core::style::Style *style); void addSpace (core::style::Style *style); @@ -873,21 +879,22 @@ public: void addParbreak (int space, core::style::Style *style); void addLinebreak (core::style::Style *style); - core::Widget *getWidgetAtPoint (int x, int y, int level); void handOverBreak (core::style::Style *style); void changeLinkColor (int link, int newColor); void changeWordStyle (int from, int to, core::style::Style *style, bool includeFirstSpace, bool includeLastSpace); - virtual bool mustBeWidenedToAvailWidth (); - + bool mustBeWidenedToAvailWidth (); void borderChanged (int y, core::Widget *vloat); + void widgetRefSizeChanged (int externalIndex); void clearPositionChanged (); void oofSizeChanged (bool extremesChanged); - inline int getLineBreakWidth () { return lineBreakWidth; } + int getLineBreakWidth (); + bool isPossibleContainer (int oofmIndex); + bool isPossibleContainerParent (int oofmIndex); }; -#define DBG_SET_WORD_PENALTY(n, i, is) \ +#define DBG_SET_WORD_PENALTY(n, i, is) \ D_STMT_START { \ if (words->getRef(n)->badnessAndPenalty.getPenalty (i) == INT_MIN) \ DBG_OBJ_ARRATTRSET_SYM ("words", n, "penalty." is, "-inf"); \ @@ -896,7 +903,7 @@ public: else \ DBG_OBJ_ARRATTRSET_NUM ("words", n, "penalty." is, \ words->getRef(n)->badnessAndPenalty \ - .getPenalty (i)); \ + .getPenalty (i)); \ } D_STMT_END #define DBG_SET_WORD(n) \ @@ -930,6 +937,16 @@ public: DBG_SET_WORD_PENALTY (n, 1, "1"); \ } D_STMT_END +#define DBG_SET_WORD_SIZE(n) \ + D_STMT_START { \ + DBG_OBJ_ARRATTRSET_NUM ("words", n, "size.width", \ + words->getRef(n)->size.width); \ + DBG_OBJ_ARRATTRSET_NUM ("words", n, "size.ascent", \ + words->getRef(n)->size.ascent); \ + DBG_OBJ_ARRATTRSET_NUM ("words", n, "size.descent", \ + words->getRef(n)->size.descent); \ + } D_STMT_END + #define DBG_MSG_WORD(aspect, prio, prefix, n, suffix) \ D_STMT_START { \ if ((n) < 0 || (n) >= words->size ()) \ diff --git a/dw/textblock_iterator.cc b/dw/textblock_iterator.cc index b6423a29..14631eba 100644 --- a/dw/textblock_iterator.cc +++ b/dw/textblock_iterator.cc @@ -33,173 +33,48 @@ namespace dw { Textblock::TextblockIterator::TextblockIterator (Textblock *textblock, core::Content::Type mask, bool atEnd): - core::Iterator (textblock, mask, atEnd) + OOFAwareWidgetIterator (textblock, mask, atEnd, textblock->words->size ()) { - if (atEnd) { - if (textblock->outOfFlowMgr) { - oofm = true; - index = textblock->outOfFlowMgr->getNumWidgets(); - } else { - oofm = false; - index = textblock->words->size(); - } - } else { - oofm = false; - index = -1; - } - - content.type = atEnd ? core::Content::END : core::Content::START; } -Textblock::TextblockIterator::TextblockIterator (Textblock *textblock, - core::Content::Type mask, - bool oofm, int index): - core::Iterator (textblock, mask, false) +Textblock::TextblockIterator + *Textblock::TextblockIterator::createWordIndexIterator (Textblock *textblock, + core::Content::Type + mask, + int wordIndex) { - this->oofm = oofm; - this->index = index; - - // TODO To be completely exact, oofm should be considered here. - if (index < 0) - content.type = core::Content::START; - else if (index >= textblock->words->size()) - content.type = core::Content::END; - else - content = textblock->words->getRef(index)->content; + TextblockIterator *tbIt = new TextblockIterator (textblock, mask, false); + tbIt->setValues (0, wordIndex); + return tbIt; } object::Object *Textblock::TextblockIterator::clone() { - return - new TextblockIterator ((Textblock*)getWidget(), getMask(), oofm, index); -} - -int Textblock::TextblockIterator::compareTo(object::Comparable *other) -{ - TextblockIterator *otherTI = (TextblockIterator*)other; - - if (oofm && !otherTI->oofm) - return +1; - else if (!oofm && otherTI->oofm) - return -1; - else - return index - otherTI->index; -} - -bool Textblock::TextblockIterator::next () -{ - Textblock *textblock = (Textblock*)getWidget(); - - if (content.type == core::Content::END) - return false; - - short type; - - do { - index++; - - if (oofm) { - // Iterating over OOFM. - if (index >= textblock->outOfFlowMgr->getNumWidgets()) { - // End of OOFM list reached. - content.type = core::Content::END; - return false; - } - type = core::Content::WIDGET_OOF_CONT; - } else { - // Iterating over words list. - if (index < textblock->words->size ()) - // Still words left. - type = textblock->words->getRef(index)->content.type; - else { - // End of words list reached. - if (textblock->outOfFlowMgr) { - oofm = true; - index = 0; - if (textblock->outOfFlowMgr->getNumWidgets() > 0) - // Start with OOFM widgets. - type = core::Content::WIDGET_OOF_CONT; - else { - // No OOFM widgets (number is 0). - content.type = core::Content::END; - return false; - } - } else { - // No OOFM widgets (no OOFM agt all). - content.type = core::Content::END; - return false; - } - } - } - } while ((type & getMask()) == 0); - - if (oofm) { - content.type = core::Content::WIDGET_OOF_CONT; - content.widget = textblock->outOfFlowMgr->getWidget (index); - } else - content = textblock->words->getRef(index)->content; - - return true; -} - -bool Textblock::TextblockIterator::prev () -{ - Textblock *textblock = (Textblock*)getWidget(); - - if (content.type == core::Content::START) - return false; - - short type; - - do { - index--; - - if (oofm) { - // Iterating over OOFM. - if (index >= 0) - // Still widgets left. - type = core::Content::WIDGET_OOF_CONT; - else { - // Beginning of OOFM list reached. Continue with words. - oofm = false; - index = textblock->words->size() - 1; - if (index < 0) { - // There are no words. (Actually, this case should not - // happen: When there are OOF widgets, ther must be OOF - // references, or widgets in flow, which contain - // references. - content.type = core::Content::END; - return false; - } - type = textblock->words->getRef(index)->content.type; - } - } else { - // Iterating over words list. - if (index < 0) { - // Beginning of words list reached. - content.type = core::Content::START; - return false; - } - type = textblock->words->getRef(index)->content.type; - } - } while ((type & getMask()) == 0); - - if (oofm) { - content.type = core::Content::WIDGET_OOF_CONT; - content.type = false; - content.widget = textblock->outOfFlowMgr->getWidget (index); - } else - content = textblock->words->getRef(index)->content; - - return true; + TextblockIterator *tbIt = + new TextblockIterator ((Textblock*)getWidget(), getMask(), false); + cloneValues (tbIt); + return tbIt; } void Textblock::TextblockIterator::highlight (int start, int end, core::HighlightLayer layer) { - if (!oofm) { + DBG_OBJ_ENTER_O ("iterator", 0, getWidget (), "TextblockIterator::highlight", + "..., %d, %d, %d", start, end, layer); + + DBG_IF_RTFL { + misc::StringBuffer sb; + intoStringBuffer (&sb); + DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "iterator: %s", + sb.getChars ()); + } + + if (inFlow ()) { + DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "in-flow index: %d", + getInFlowIndex ()); + Textblock *textblock = (Textblock*)getWidget(); - int index1 = index, index2 = index; + int index = getInFlowIndex (), index1 = index, index2 = index; int oldStartIndex = textblock->hlStart[layer].index; int oldStartChar = textblock->hlStart[layer].nChar; @@ -224,22 +99,46 @@ void Textblock::TextblockIterator::highlight (int start, int end, textblock->hlEnd[layer].nChar = end; } + DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlStart", layer, "index", + textblock->hlStart[layer].index); + DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlStart", layer, "nChar", + textblock->hlStart[layer].nChar); + DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlEnd", layer, "index", + textblock->hlEnd[layer].index); + DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlEnd", layer, "nChar", + textblock->hlEnd[layer].nChar); + if (oldStartIndex != textblock->hlStart[layer].index || oldStartChar != textblock->hlStart[layer].nChar || oldEndIndex != textblock->hlEnd[layer].index || oldEndChar != textblock->hlEnd[layer].nChar) textblock->queueDrawRange (index1, index2); - } + } else + highlightOOF (start, end, layer); - // TODO What about OOF widgets? + DBG_OBJ_LEAVE_O (getWidget ()); } void Textblock::TextblockIterator::unhighlight (int direction, core::HighlightLayer layer) { - if (!oofm) { + DBG_OBJ_ENTER_O ("iterator", 0, getWidget (), + "TextblockIterator/unhighlight", "..., %d, %d", + direction, layer); + + DBG_IF_RTFL { + misc::StringBuffer sb; + intoStringBuffer (&sb); + DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "iterator: %s", + sb.getChars ()); + } + + if (inFlow ()) { + DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "in-flow index: %d", + getInFlowIndex ()); + Textblock *textblock = (Textblock*)getWidget(); - int index1 = index, index2 = index; + int index = getInFlowIndex (), index1 = index, index2 = index; if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) return; @@ -264,26 +163,33 @@ void Textblock::TextblockIterator::unhighlight (int direction, textblock->hlEnd[layer].nChar = INT_MAX; } + DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlStart", layer, "index", + textblock->hlStart[layer].index); + DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlStart", layer, "nChar", + textblock->hlStart[layer].nChar); + DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlEnd", layer, "index", + textblock->hlEnd[layer].index); + DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlEnd", layer, "nChar", + textblock->hlEnd[layer].nChar); + if (oldStartIndex != textblock->hlStart[layer].index || oldStartChar != textblock->hlStart[layer].nChar || oldEndIndex != textblock->hlEnd[layer].index || oldEndChar != textblock->hlEnd[layer].nChar) textblock->queueDrawRange (index1, index2); - } + } else + unhighlightOOF (direction, layer); - // TODO What about OOF widgets? + DBG_OBJ_LEAVE_O (getWidget ()); } void Textblock::TextblockIterator::getAllocation (int start, int end, core::Allocation *allocation) { - Textblock *textblock = (Textblock*)getWidget(); + if (inFlow ()) { + Textblock *textblock = (Textblock*)getWidget(); - if (oofm) { - // TODO Consider start and end? - *allocation = - *(textblock->outOfFlowMgr->getWidget(index)->getAllocation()); - } else { + int index = getInFlowIndex (); Word *word = textblock->words->getRef (index); int firstWordOfLine, textOffset, lineYOffsetCanvas, lineBorderAscent; @@ -360,14 +266,19 @@ void Textblock::TextblockIterator::getAllocation (int start, int end, } allocation->ascent = word->size.ascent; allocation->descent = word->size.descent; - } + } else + getAllocationOOF (start, end, allocation); } -void Textblock::TextblockIterator::print () +int Textblock::TextblockIterator::numContentsInFlow () { - Iterator::print (); - printf (", oofm = %s, index = %d", oofm ? "true" : "false", index); + return ((Textblock*)getWidget())->words->size (); +} +void Textblock::TextblockIterator::getContentInFlow (int index, + core::Content *content) +{ + *content = ((Textblock*)getWidget())->words->getRef(index)->content; } } // namespace dw diff --git a/dw/textblock_linebreaking.cc b/dw/textblock_linebreaking.cc index 3c181388..55577e87 100644 --- a/dw/textblock_linebreaking.cc +++ b/dw/textblock_linebreaking.cc @@ -495,20 +495,28 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, mustQueueResize = true; - //printWordShort (words->getRef (line->firstWord)); - //printf (" ... "); - //printWordShort (words->getRef (line->lastWord)); - //printf (": "); - //words->getRef(line->lastWord)->badnessAndPenalty.print (); - //printf ("\n"); - int xWidget = line->textOffset; + int yLine = yOffsetOfLineCreated (line); for (int i = firstWord; i <= lastWord; i++) { Word *word = words->getRef (i); if (word->wordImgRenderer) word->wordImgRenderer->setData (xWidget, lines->size () - 1); if (word->spaceImgRenderer) word->spaceImgRenderer->setData (xWidget, lines->size () - 1); + + if (word->content.type == core::Content::WIDGET_OOF_REF) { + Widget *widget = word->content.widget; + oof::OutOfFlowMgr *oofm = + searchOutOfFlowMgr (getWidgetOOFIndex (widget)); + // See also Textblock::sizeAllocate, and notes there about + // vertical alignment. Calculating the vertical position + // should probably be centralized. + if (oofm) + oofm->tellPosition2 (widget, xWidget, + yLine + (line->borderAscent + - word->size.ascent)); + } + xWidget += word->size.width + word->effSpace; } @@ -601,7 +609,7 @@ int Textblock::wordWrap (int wordIndex, bool wrapAll) int n; if (word->content.type == core::Content::WIDGET_OOF_REF) - n = 0; + n = wrapWordOofRef (wordIndex, wrapAll); else n = wrapWordInFlow (wordIndex, wrapAll); @@ -764,14 +772,15 @@ int Textblock::wrapWordInFlow (int wordIndex, bool wrapAll) startSearch, breakPos); for (int i = startSearch; newFloatPos == -1 && i <= breakPos; i++) { core::Content *content = &(words->getRef(i)->content); - if (content->type == core::Content::WIDGET_OOF_REF && - // Later, absolutepositioned elements (which do not affect - // borders) can be ignored at this point. - (containingBlock->outOfFlowMgr->affectsLeftBorder - (content->widget) || - containingBlock->outOfFlowMgr->affectsRightBorder - (content->widget))) - newFloatPos = i; + if (content->type == core::Content::WIDGET_OOF_REF) { + for (int j = 0; newFloatPos == -1 && j < NUM_OOFM; j++) { + if ((searchOutOfFlowMgr(j)->affectsLeftBorder(content + ->widget) || + searchOutOfFlowMgr(j)->affectsRightBorder (content + ->widget))) + newFloatPos = i; + } + } } DBG_OBJ_MSGF ("construct.word", 2, "newFloatPos = %d", newFloatPos); @@ -782,10 +791,17 @@ int Textblock::wrapWordInFlow (int wordIndex, bool wrapAll) floatHandled = true; // Step 2: position the float and re-calculate the line. - lastFloatPos = newFloatPos; - containingBlock->outOfFlowMgr->tellPosition - (words->getRef(lastFloatPos)->content.widget, yNewLine); + // TODO "x" is not quite correct, but this does not matter + // (currently?). + + lastFloatPos = newFloatPos; + + Widget *widget = words->getRef(lastFloatPos)->content.widget; + oof::OutOfFlowMgr *oofm = + searchOutOfFlowMgr (getWidgetOOFIndex (widget)); + if (oofm && oofm->mayAffectBordersAtAll ()) + oofm->tellPosition1 (widget, boxOffsetX (), yNewLine); balanceBreakPosAndHeight (wordIndex, firstIndex, &searchUntil, tempNewLine, penaltyIndex, false, @@ -851,8 +867,7 @@ int Textblock::wrapWordInFlow (int wordIndex, bool wrapAll) firstWordWithoutLine = lines->getLastRef()->lastWord + 1; if (wordIndex >= firstWordWithoutLine) { - word->content.widget->parentRef = - OutOfFlowMgr::createRefNormalFlow (lines->size ()); + word->content.widget->parentRef = makeParentRefInFlow (lines->size ()); DBG_OBJ_SET_NUM_O (word->content.widget, "parentRef", word->content.widget->parentRef); } @@ -863,6 +878,30 @@ int Textblock::wrapWordInFlow (int wordIndex, bool wrapAll) return diffWords; } +int Textblock::wrapWordOofRef (int wordIndex, bool wrapAll) +{ + DBG_OBJ_ENTER ("construct.word", 0, "wrapWordOofRef", "%d, %s", + wordIndex, wrapAll ? "true" : "false"); + + Word *word = words->getRef (wordIndex); + Widget *widget = word->content.widget; + int yNewLine = yOffsetOfLineToBeCreated (); + + // Floats, which affect either border, are handled in wrapWordInFlow; this + // is rather for positioned elements (but only for completeness: + // tellPosition1 is not implemented for positioned elements). + oof::OutOfFlowMgr *oofm = searchOutOfFlowMgr (getWidgetOOFIndex (widget)); + DBG_OBJ_MSGF ("construct.word", 1, "parentRef = %d, oofm = %p", + widget->parentRef, oofm); + if (oofm && !oofm->mayAffectBordersAtAll ()) + // TODO Again, "x" is not correct (see above). + oofm->tellPosition1 (widget, boxOffsetX (), yNewLine); + + DBG_OBJ_LEAVE (); + + return 0; // Words list not changed. +} + // *height must be initialized, but not *breakPos. // *wordIndexEnd must be initialized (initially to wordIndex) void Textblock::balanceBreakPosAndHeight (int wordIndex, int firstIndex, @@ -1469,8 +1508,9 @@ void Textblock::moveWordIndices (int wordIndex, int num, int *addIndex1) DBG_OBJ_ENTER ("construct.word", 0, "moveWordIndices", "%d, %d", wordIndex, num); - if (containingBlock->outOfFlowMgr) - containingBlock->outOfFlowMgr->moveExternalIndices (this, wordIndex, num); + for (int i = 0; i < NUM_OOFM; i++) + if (searchOutOfFlowMgr(i)) + searchOutOfFlowMgr(i)->moveExternalIndices (this, wordIndex, num); for (int i = lines->size () - 1; i >= 0; i--) { Line *line = lines->getRef (i); @@ -1542,8 +1582,7 @@ void Textblock::accumulateWordForLine (int lineIndex, int wordIndex) borderDescent = marginDescent - word->content.widget->getStyle()->margin.bottom; - word->content.widget->parentRef = - OutOfFlowMgr::createRefNormalFlow (lineIndex); + word->content.widget->parentRef = makeParentRefInFlow (lineIndex); DBG_OBJ_SET_NUM_O (word->content.widget, "parentRef", word->content.widget->parentRef); } else { @@ -1669,8 +1708,7 @@ int Textblock::calcLineBreakWidth (int lineIndex) if (limitTextWidth && layout->getUsesViewport () && // margin/border/padding will be subtracted later, via OOFM. - lineBreakWidth - getStyle()->boxDiffWidth() - > layout->getWidthViewport () - 10) + lineBreakWidth - boxDiffWidth() > layout->getWidthViewport () - 10) lineBreakWidth = layout->getWidthViewport () - 10; if (lineIndex == 0) lineBreakWidth -= line1OffsetEff; @@ -1682,8 +1720,8 @@ int Textblock::calcLineBreakWidth (int lineIndex) } else leftBorder = rightBorder = 0; - leftBorder = misc::max (leftBorder, getStyle()->boxOffsetX()); - rightBorder = misc::max (rightBorder, getStyle()->boxRestWidth()); + leftBorder = misc::max (leftBorder, boxOffsetX()); + rightBorder = misc::max (rightBorder, boxRestWidth()); lineBreakWidth -= (leftBorder + rightBorder); @@ -1885,8 +1923,24 @@ void Textblock::rewrap () for (int i = firstWord; i < words->size (); i++) { Word *word = words->getRef (i); - if (word->content.type == core::Content::WIDGET_IN_FLOW) + switch (word->content.type) { + case core::Content::WIDGET_IN_FLOW: word->content.widget->sizeRequest (&word->size); + DBG_SET_WORD_SIZE (i); + break; + + case core::Content::WIDGET_OOF_REF: + { + int oofmIndex = getOOFMIndex (word->content.widget); + oof::OutOfFlowMgr *oofm = searchOutOfFlowMgr (oofmIndex); + oofm->calcWidgetRefSize (word->content.widget, &(word->size)); + DBG_SET_WORD_SIZE (i); + } + break; + + default: + break; + } wordWrap (i, false); @@ -1972,17 +2026,6 @@ void Textblock::initNewLine () { DBG_OBJ_ENTER0 ("construct.line", 0, "initNewLine"); - // At the very beginning, in Textblock::Textblock, where this - // method is called, containingBlock is not yet defined. - - if (containingBlock && containingBlock->outOfFlowMgr) { - if (lines->size () == 0) { - int clearPosition = - containingBlock->outOfFlowMgr->getClearPosition (this); - setVerticalOffset (misc::max (clearPosition, 0)); - } - } - calcBorders (lines->size() > 0 ? lines->getLastRef()->lastOofRefPositionedBeforeThisLine : -1, 1); @@ -2000,81 +2043,97 @@ void Textblock::calcBorders (int lastOofRef, int height) DBG_OBJ_ENTER ("construct.line", 0, "calcBorders", "%d, %d", lastOofRef, height); - if (containingBlock && containingBlock->outOfFlowMgr) { - // Consider the example: - // - // <div> - // Some text A ... - // <p> Some text B ... <img style="float:right" ...> </p> - // Some more text C ... - // </div> - // - // If the image is large enough, it should float around the last - // paragraph, "Some more text C ...": - // - // Some more text A ... - // - // Some more ,---------. - // text B ... | | - // | <img> | - // Some more | | <---- Consider this line! - // text C ... '---------' - // - // Since this float is generated in the <p> element, not in the- - // <div> element, and since they are represented by different - // instances of dw::Textblock, lastOofRefPositionedBeforeThisLine, - // and so lastOofRef, is -1 for the line marked with an arrow; - // this would result in ignoring the float, because -1 is - // equivalent to the very beginning of the <div> element ("Some - // more text A ..."), which is not affected by the float. - // - // On the other hand, the only relevant values of - // Line::lastOofRefPositionedBeforeThisLine are those greater - // than the first word of the new line, so a solution is to use - // the maximum of both. + newLineHasFloatLeft = newLineHasFloatRight = false; + newLineLeftBorder = newLineRightBorder = 0; + newLineLeftFloatHeight = newLineRightFloatHeight = 0; + bool oofmDefined = false; + for (int i = 0; i < NUM_OOFM && !oofmDefined; i++) + if (searchOutOfFlowMgr(i)) + oofmDefined = true; - int firstWordOfLine = lines->size() > 0 ? - lines->getLastRef()->lastWord + 1 : 0; + if (oofmDefined) { + int firstWordOfLine = + lines->size() > 0 ? lines->getLastRef()->lastWord + 1 : 0; int effOofRef = misc::max (lastOofRef, firstWordOfLine - 1); - int y = yOffsetOfLineToBeCreated (); - - newLineHasFloatLeft = - containingBlock->outOfFlowMgr->hasFloatLeft (this, y, height, this, - effOofRef); - newLineHasFloatRight = - containingBlock->outOfFlowMgr->hasFloatRight (this, y, height, this, - effOofRef); - newLineLeftBorder = - containingBlock->outOfFlowMgr->getLeftBorder (this, y, height, this, - effOofRef); - newLineRightBorder = - containingBlock->outOfFlowMgr->getRightBorder (this, y, height, this, - effOofRef); - newLineLeftFloatHeight = newLineHasFloatLeft ? - containingBlock->outOfFlowMgr->getLeftFloatHeight (this, y, height, - this, effOofRef) : - 0; - newLineRightFloatHeight = newLineHasFloatRight ? - containingBlock->outOfFlowMgr->getRightFloatHeight (this, y, height, - this, effOofRef) : - 0; - - DBG_OBJ_MSGF ("construct.line", 1, - "%d * %d (%s) / %d * %d (%s), at %d (%d), until %d = " - "max (%d, %d - 1)", - newLineLeftBorder, newLineLeftFloatHeight, - newLineHasFloatLeft ? "true" : "false", - newLineRightBorder, newLineRightFloatHeight, - newLineHasFloatRight ? "true" : "false", - y, height, effOofRef, lastOofRef, firstWordOfLine); - } else { - newLineHasFloatLeft = newLineHasFloatRight = false; - newLineLeftBorder = newLineRightBorder = 0; - newLineLeftFloatHeight = newLineRightFloatHeight = 0; - - DBG_OBJ_MSG ("construct.line", 0, "<i>no CB of OOFM</i>"); + + for (int i = 0; i < NUM_OOFM; i++) { + oof::OutOfFlowMgr *oofm = searchOutOfFlowMgr(i); + if (oofm) { + // Consider the example: + // + // <div> + // Some text A ... + // <p> Some text B ... <img style="float:right" ...> </p> + // Some more text C ... + // </div> + // + // If the image is large enough, it should float around the last + // paragraph, "Some more text C ...": + // + // Some more text A ... + // + // Some more ,---------. + // text B ... | | + // | <img> | + // Some more | | <---- Consider this line! + // text C ... '---------' + // + // Since this float is generated in the <p> element, not in the- + // <div> element, and since they are represented by different + // instances of dw::Textblock, lastOofRefPositionedBeforeThisLine, + // and so lastOofRef, is -1 for the line marked with an arrow; + // this would result in ignoring the float, because -1 is + // equivalent to the very beginning of the <div> element ("Some + // more text A ..."), which is not affected by the float. + // + // On the other hand, the only relevant values of + // Line::lastOofRefPositionedBeforeThisLine are those greater + // than the first word of the new line, so a solution is to use + // the maximum of both. + + bool thisHasLeft, thisHasRight; + + thisHasLeft = oofm->hasFloatLeft (this, y, height, this, effOofRef); + newLineHasFloatLeft = newLineHasFloatLeft || thisHasLeft; + thisHasRight = oofm->hasFloatRight (this, y, height, this, + effOofRef); + newLineHasFloatRight = newLineHasFloatRight || thisHasRight; + + newLineLeftBorder = + misc::max (newLineLeftBorder, + oofm->getLeftBorder (this, y, height, this, + effOofRef)); + newLineRightBorder = + misc::max (newLineRightBorder, + oofm->getRightBorder (this, y, height, this, + effOofRef)); + + // TODO "max" is not really correct for the heights. (Does + // not matter, since only one, the float manager, returns + // meaningful values.) + if (thisHasLeft) + newLineLeftFloatHeight = + misc::max (newLineLeftFloatHeight, + oofm->getLeftFloatHeight (this, y, height, this, + effOofRef)); + if (thisHasRight) + newLineRightFloatHeight = + misc::max (newLineRightFloatHeight, + oofm->getRightFloatHeight (this, y, height, this, + effOofRef)); + + DBG_OBJ_MSGF ("construct.line", 1, + "OOFM #%d: %d * %d (%s) / %d * %d (%s), at %d (%d), " + "until %d = max (%d, %d - 1)", + i, newLineLeftBorder, newLineLeftFloatHeight, + newLineHasFloatLeft ? "true" : "false", + newLineRightBorder, newLineRightFloatHeight, + newLineHasFloatRight ? "true" : "false", + y, height, effOofRef, lastOofRef, firstWordOfLine); + } + } } DBG_OBJ_SET_BOOL ("newLineHasFloatLeft", newLineHasFloatLeft); diff --git a/dw/types.hh b/dw/types.hh index b6b4ca0b..481f4804 100644 --- a/dw/types.hh +++ b/dw/types.hh @@ -208,6 +208,10 @@ struct Content WIDGET_OOF_REF = 1 << 5, BREAK = 1 << 6, + /** \brief can be used internally, but should never be exposed, + e. g. by iterators */ + INVALID = 1 << 7, + ALL = 0xff, REAL_CONTENT = 0xff ^ (START | END), SELECTION_CONTENT = TEXT | BREAK, // WIDGET_* must be set additionally @@ -233,6 +237,67 @@ struct Content static void printMask (Type mask); }; +/** + * \brief Base class for dw::core::DrawingContext and + * dw::core::GettingWidgetAtPointContext. + * + * Not to be confused with the *stacking context* as defined by CSS. + */ +class StackingProcessingContext +{ +private: + lout::container::typed::HashSet<lout::object::TypedPointer<Widget> > + *widgetsProcessedAsInterruption; + +public: + inline StackingProcessingContext () { + widgetsProcessedAsInterruption = + new lout::container::typed::HashSet<lout::object:: + TypedPointer<Widget> > (true); + } + + inline ~StackingProcessingContext () + { delete widgetsProcessedAsInterruption; } + + inline bool hasWidgetBeenProcessedAsInterruption (Widget *widget) { + lout::object::TypedPointer<Widget> key (widget); + return widgetsProcessedAsInterruption->contains (&key); + } + + inline void addWidgetProcessedAsInterruption (Widget *widget) { + lout::object::TypedPointer<Widget> *key = + new lout::object::TypedPointer<Widget> (widget); + return widgetsProcessedAsInterruption->put (key); + } +}; + +/** + * \brief Set at the top when drawing. + * + * See \ref dw-interrupted-drawing for details. + */ +class DrawingContext: public StackingProcessingContext +{ +private: + Rectangle toplevelArea; + +public: + inline DrawingContext (Rectangle *toplevelArea) { + this->toplevelArea = *toplevelArea; + } + + inline Rectangle *getToplevelArea () { return &toplevelArea; } +}; + +/** + * \brief Set at the top when getting the widget at the point. + * + * Similar to dw::core::DrawingContext. + */ +class GettingWidgetAtPointContext: public StackingProcessingContext +{ +}; + } // namespace core } // namespace dw @@ -131,10 +131,10 @@ void Embed::setEnabled (bool enabled) resource->setEnabled (enabled); } -void Embed::draw (View *view, Rectangle *area) +void Embed::draw (View *view, Rectangle *area, DrawingContext *context) { drawWidgetBox (view, area, false); - resource->draw (view, area); + resource->draw (view, area, context); } Iterator *Embed::iterator (Content::Type mask, bool atEnd) @@ -266,7 +266,7 @@ void Resource::setDisplayed (bool displayed) { } -void Resource::draw (View *view, Rectangle *area) +void Resource::draw (View *view, Rectangle *area, DrawingContext *context) { } @@ -317,7 +317,7 @@ Iterator *LabelButtonResource::iterator (Content::Type mask, bool atEnd) void ComplexButtonResource::LayoutReceiver::resizeQueued (bool extremesChanged) { - DBG_OBJ_ENTER ("resize", 0, "LayoutReceiver/resizeQueued", "%s", + DBG_OBJ_ENTER ("resize", 0, "LayoutReceiver::resizeQueued", "%s", extremesChanged ? "true" : "false"); resource->queueResize (extremesChanged); DBG_OBJ_LEAVE (); @@ -254,7 +254,7 @@ public: void setDisplayed (bool displayed); void setEnabled (bool enabled); - void draw (View *view, Rectangle *area); + void draw (View *view, Rectangle *area, DrawingContext *context); Iterator *iterator (Content::Type mask, bool atEnd); void setStyle (style::Style *style); @@ -369,7 +369,7 @@ public: virtual void containerSizeChangedForChildren (); virtual void setDisplayed (bool displayed); - virtual void draw (View *view, Rectangle *area); + virtual void draw (View *view, Rectangle *area, DrawingContext *context); virtual Iterator *iterator (Content::Type mask, bool atEnd) = 0; virtual void setStyle (style::Style *style); diff --git a/dw/widget.cc b/dw/widget.cc index 464459ba..191feda9 100644 --- a/dw/widget.cc +++ b/dw/widget.cc @@ -91,6 +91,8 @@ Widget::Widget () deleteCallbackFunc = NULL; widgetImgRenderer = NULL; + + stackingContextMgr = NULL; } Widget::~Widget () @@ -104,6 +106,9 @@ Widget::~Widget () delete widgetImgRenderer; } + if (stackingContextMgr) + delete stackingContextMgr; + if (style) style->unref (); @@ -117,35 +122,111 @@ Widget::~Widget () /** - * \brief Calculates the intersection of widget->allocation and area, returned - * in intersection (in widget coordinates!). + * \brief Calculates the intersection of the visible allocation + * (i. e. the intersection with the visible parent allocation) and + * "area" (in widget coordinates referring to "refWidget"), + * returned in intersection (in widget coordinates). * - * Typically used by containers when - * drawing their children. Returns whether intersection is not empty. + * Typically used by containers when drawing their children (passing + * "this" as "refWidget"). Returns whether intersection is not empty. */ -bool Widget::intersects (Rectangle *area, Rectangle *intersection) +bool Widget::intersects (Widget *refWidget, Rectangle *area, + Rectangle *intersection) { - Rectangle parentArea, childArea; + DBG_OBJ_ENTER ("draw", 0, "intersects", "%p, [%d, %d, %d * %d]", + refWidget, area->x, area->y, area->width, area->height); + bool r; - parentArea = *area; - parentArea.x += parent->allocation.x; - parentArea.y += parent->allocation.y; + if (wasAllocated ()) { + *intersection = *area; + intersection->x += refWidget->allocation.x; + intersection->y += refWidget->allocation.y; + + r = true; + // "RefWidget" is excluded; it is assumed that "area" its already within + // its allocation. + for (Widget *widget = this; r && widget != refWidget; + widget = widget->parent) { + assert (widget != NULL); // refWidget must be ancestor. + + Rectangle widgetArea, newIntersection; + widgetArea.x = widget->allocation.x; + widgetArea.y = widget->allocation.y; + widgetArea.width = widget->allocation.width; + widgetArea.height = widget->getHeight (); + + if (intersection->intersectsWith (&widgetArea, &newIntersection)) { + DBG_OBJ_MSGF ("draw", 1, "new intersection: %d, %d, %d * %d", + newIntersection.x, newIntersection.y, + newIntersection.width, newIntersection.height); + *intersection = newIntersection; + } else { + DBG_OBJ_MSG ("draw", 1, "no new intersection"); + r = false; + } + } - childArea.x = allocation.x; - childArea.y = allocation.y; - childArea.width = allocation.width; - childArea.height = getHeight (); + if (r) { + intersection->x -= allocation.x; + intersection->y -= allocation.y; - if (parentArea.intersectsWith (&childArea, intersection)) { - intersection->x -= allocation.x; - intersection->y -= allocation.y; - return true; - } else - return false; + DBG_OBJ_MSGF ("draw", 1, "final intersection: %d, %d, %d * %d", + intersection->x, intersection->y, + intersection->width, intersection->height); + } + } else { + r = false; + DBG_OBJ_MSG ("draw", 1, "not allocated"); + } + + if (r) + DBG_OBJ_MSGF ("draw", 1, "=> true: %d, %d, %d * %d", + intersection->x, intersection->y, + intersection->width, intersection->height); + else + DBG_OBJ_MSG ("draw", 1, "=> false"); + + DBG_OBJ_LEAVE (); + return r; +} + +/** + * See \ref dw-interrupted-drawing for details. + */ +void Widget::drawInterruption (View *view, Rectangle *area, + DrawingContext *context) +{ + Rectangle thisArea; + if (intersects (layout->topLevel, context->getToplevelArea (), &thisArea)) + draw (view, &thisArea, context); + + context->addWidgetProcessedAsInterruption (this); +} + +Widget *Widget::getWidgetAtPoint (int x, int y, + GettingWidgetAtPointContext *context) +{ + // Suitable for simple widgets, without children. + + if (inAllocation (x, y)) + return this; + else + return NULL; +} + +Widget *Widget::getWidgetAtPointInterrupted (int x, int y, + GettingWidgetAtPointContext + *context) +{ + Widget *widgetAtPoint = getWidgetAtPoint (x, y, context); + context->addWidgetProcessedAsInterruption (this); + return widgetAtPoint; } void Widget::setParent (Widget *parent) { + DBG_OBJ_ENTER ("construct", 0, "setParent", "%p", parent); + this->parent = parent; layout = parent->layout; @@ -170,7 +251,21 @@ void Widget::setParent (Widget *parent) // Textblock. DBG_OBJ_SET_PTR ("container", container); + // If at all, stackingContextMgr should have set *before*, see also + // Widget::setStyle() and Layout::addWidget(). + if (stackingContextMgr) { + Widget *stackingContextWidget = parent; + while (stackingContextWidget && + stackingContextWidget->stackingContextMgr == NULL) + stackingContextWidget = stackingContextWidget->parent; + assert (stackingContextWidget); + stackingContextWidget->stackingContextMgr->addChildSCWidget (this); + } else + stackingContextWidget = parent->stackingContextWidget; + notifySetParent(); + + DBG_OBJ_LEAVE (); } void Widget::setQuasiParent (Widget *quasiParent) @@ -490,6 +585,7 @@ void Widget::sizeRequest (Requisition *requisition) } if (needsResize ()) { + calcExtraSpace (); /** \todo Check requisition == &(this->requisition) and do what? */ sizeRequestImpl (requisition); this->requisition = *requisition; @@ -519,7 +615,7 @@ int Widget::getMinWidth (Extremes *extremes, bool forceValue) { DBG_IF_RTFL { if (extremes) - DBG_OBJ_ENTER ("resize", 0, "getMinWidth", "[%d (%d) / %d (%d), %s", + DBG_OBJ_ENTER ("resize", 0, "getMinWidth", "[%d (%d) / %d (%d)], %s", extremes->minWidth, extremes->minWidthIntrinsic, extremes->maxWidth, extremes->maxWidthIntrinsic, forceValue ? "true" : "false"); @@ -627,7 +723,7 @@ int Widget::getAvailHeight (bool forceValue) } else if (style::isPerLength (getStyle()->height)) { DBG_OBJ_MSGF ("resize", 1, "percentage height: %g%%", 100 * style::perLengthVal_useThisOnlyForDebugging - (getStyle()->height)); + (getStyle()->height)); // Notice that here -- unlike getAvailWidth() -- // layout->hScrollbarThickness is not considered here; // something like canvasWidthGreater (analogue to @@ -909,6 +1005,8 @@ void Widget::getExtremes (Extremes *extremes) } if (extremesChanged ()) { + calcExtraSpace (); + // For backward compatibility (part 1/2): extremes->minWidthIntrinsic = extremes->maxWidthIntrinsic = -1; @@ -939,6 +1037,24 @@ void Widget::getExtremes (Extremes *extremes) } /** + * \brief Calculates dw::core::Widget::extraSpace. + * + * Delegated to dw::core::Widget::calcExtraSpaceImpl. Called both from + * dw::core::Widget::sizeRequest and dw::core::Widget::getExtremes. + */ +void Widget::calcExtraSpace () +{ + extraSpace.top = extraSpace.right = extraSpace.bottom = extraSpace.left = 0; + calcExtraSpaceImpl (); + + DBG_OBJ_SET_NUM ("extraSpace.top", extraSpace.top); + DBG_OBJ_SET_NUM ("extraSpace.bottom", extraSpace.bottom); + DBG_OBJ_SET_NUM ("extraSpace.left", extraSpace.left); + DBG_OBJ_SET_NUM ("extraSpace.right", extraSpace.right); + +} + +/** * \brief Wrapper for Widget::sizeAllocateImpl, calls the latter only when * needed. */ @@ -1082,6 +1198,16 @@ void Widget::setStyle (style::Style *style) layout->updateCursor (); } + // After Layout::addWidget() (as toplevel widget) or Widget::setParent() + // (which also sets layout), changes of the style cannot be considered + // anymore. (Should print a warning?) + if (layout == NULL && + StackingContextMgr::isEstablishingStackingContext (this)) { + stackingContextMgr = new StackingContextMgr (this); + DBG_OBJ_ASSOC_CHILD (stackingContextMgr); + stackingContextWidget = this; + } + if (sizeChanged) queueResize (0, true); else @@ -1196,7 +1322,7 @@ void Widget::drawBox (View *view, style::Style *style, Rectangle *area, // TODO Handle inverse drawing the same way as in drawWidgetBox? // Maybe this method (drawBox) is anyway obsolete when extraSpace - // is fully supported (as in the "dillo_grows" repository). + // is fully supported (as here, in the "dillo_grows" repository). int xPad, yPad, widthPad, heightPad; getPaddingArea (&xPad, &yPad, &widthPad, &heightPad); @@ -1226,8 +1352,10 @@ void Widget::drawWidgetBox (View *view, Rectangle *area, bool inverse) canvasArea.width = area->width; canvasArea.height = area->height; - style::drawBorder (view, layout, &canvasArea, allocation.x, allocation.y, - allocation.width, getHeight (), style, inverse); + int xMar, yMar, widthMar, heightMar; + getMarginArea (&xMar, &yMar, &widthMar, &heightMar); + style::drawBorder (view, layout, &canvasArea, xMar, yMar, widthMar, + heightMar, style, inverse); int xPad, yPad, widthPad, heightPad; getPaddingArea (&xPad, &yPad, &widthPad, &heightPad); @@ -1363,53 +1491,6 @@ Widget *Widget::getNearestCommonAncestor (Widget *otherWidget) return widget1; } - -/** - * \brief Search recursively through widget. - * - * Used by dw::core::Layout:getWidgetAtPoint. - */ -Widget *Widget::getWidgetAtPoint (int x, int y, int level) -{ - Iterator *it; - Widget *childAtPoint; - - //printf ("%*s-> examining the %s %p (%d, %d, %d x (%d + %d))\n", - // 3 * level, "", getClassName (), this, allocation.x, allocation.y, - // allocation.width, allocation.ascent, allocation.descent); - - if (x >= allocation.x && - y >= allocation.y && - x <= allocation.x + allocation.width && - y <= allocation.y + getHeight ()) { - //_MSG ("%*s -> inside\n", 3 * level, ""); - /* - * Iterate over the children of this widget. Test recursively, whether - * the point is within the child (or one of its children...). If there - * is such a child, it is returned. Otherwise, this widget is returned. - */ - childAtPoint = NULL; - it = iterator ((Content::Type) - (Content::WIDGET_IN_FLOW | Content::WIDGET_OOF_CONT), - false); - - while (childAtPoint == NULL && it->next ()) { - Widget *child = it->getContent()->widget; - if (child->wasAllocated ()) - childAtPoint = child->getWidgetAtPoint (x, y, level + 1); - } - - it->unref (); - - if (childAtPoint) - return childAtPoint; - else - return this; - } else - return NULL; -} - - void Widget::scrollTo (HPosition hpos, VPosition vpos, int x, int y, int width, int height) { @@ -1417,6 +1498,24 @@ void Widget::scrollTo (HPosition hpos, VPosition vpos, x + allocation.x, y + allocation.y, width, height); } +void Widget::getMarginArea (int *xMar, int *yMar, int *widthMar, int *heightMar) +{ + *xMar = allocation.x + extraSpace.left; + *yMar = allocation.y + extraSpace.top; + *widthMar = allocation.width - (extraSpace.left + extraSpace.right); + *heightMar = getHeight () - (extraSpace.top + extraSpace.bottom); +} + +void Widget::getBorderArea (int *xBor, int *yBor, int *widthBor, int *heightBor) +{ + getMarginArea (xBor, yBor, widthBor, heightBor); + + *xBor += style->margin.left; + *yBor += style->margin.top; + *widthBor -= style->margin.left + style->margin.right; + *heightBor -= style->margin.top + style->margin.bottom; +} + /** * \brief Return the padding area (content plus padding). * @@ -1426,18 +1525,31 @@ void Widget::scrollTo (HPosition hpos, VPosition vpos, void Widget::getPaddingArea (int *xPad, int *yPad, int *widthPad, int *heightPad) { - *xPad = allocation.x + style->margin.left + style->borderWidth.left; - *yPad = allocation.y + style->margin.top + style->borderWidth.top; - *widthPad = allocation.width - style->margin.left - style->borderWidth.left - - style->margin.right - style->borderWidth.right; - *heightPad = getHeight () - style->margin.top - style->borderWidth.top - - style->margin.bottom - style->borderWidth.bottom; + getBorderArea (xPad, yPad, widthPad, heightPad); + + *xPad += style->borderWidth.left; + *yPad += style->borderWidth.top; + *widthPad -= style->borderWidth.left + style->borderWidth.right; + *heightPad -= style->borderWidth.top + style->borderWidth.bottom; } void Widget::sizeAllocateImpl (Allocation *allocation) { } +/** + * \brief The actual implementation for calculating + * dw::core::Widget::extraSpace. + * + * The implementation gets a clean value of + * dw::core::Widget::extraSpace, which is only corrected. To make sure + * all possible influences are considered, the implementation of the + * base class should be called, too. + */ +void Widget::calcExtraSpaceImpl () +{ +} + void Widget::markSizeChange (int ref) { } @@ -1753,6 +1865,7 @@ void Widget::leaveNotifyImpl (EventCrossing *) tooltip->onLeave(); } + void Widget::removeChild (Widget *child) { // Should be implemented. diff --git a/dw/widget.hh b/dw/widget.hh index f9d1293c..176fed08 100644 --- a/dw/widget.hh +++ b/dw/widget.hh @@ -180,19 +180,36 @@ protected: Allocation allocation; inline int getHeight () { return allocation.ascent + allocation.descent; } - inline int getContentWidth() { return allocation.width - - style->boxDiffWidth (); } - inline int getContentHeight() { return getHeight () - - style->boxDiffHeight (); } + inline int getContentWidth() { return allocation.width - boxDiffWidth (); } + inline int getContentHeight() { return getHeight () - boxDiffHeight (); } Layout *layout; /** * \brief Space around the margin box. Allocation is extraSpace + - * margin + border + padding + contents; + * margin + border + padding + contents. + * + * See also dw::core::Widget::calcExtraSpace and + * dw::core::Widget::calcExtraSpaceImpl. Also, it is feasible to + * correct this value within dw::core::Widget::sizeRequestImpl. */ style::Box extraSpace; + /** + * \brief Set iff this widget constitutes a stacking context, as defined by + * CSS. + */ + StackingContextMgr *stackingContextMgr; + + /** + * \brief The bottom-most ancestor (or this) for which stackingContextMgr is + * set. + */ + Widget *stackingContextWidget; + + inline StackingContextMgr *getNextStackingContextMgr () + { return stackingContextWidget->stackingContextMgr; } + /*inline void printFlags () { DBG_IF_RTFL { char buf[10 * 3 - 1 + 1]; @@ -254,7 +271,6 @@ protected: inline void unsetFlags (Flags f) { flags = (Flags)(flags & ~f); printFlag (f); } - inline void queueDraw () { queueDrawArea (0, 0, allocation.width, getHeight()); } void queueDrawArea (int x, int y, int width, int height); @@ -271,6 +287,8 @@ protected: */ virtual void getExtremesImpl (Extremes *extremes) = 0; + virtual void calcExtraSpaceImpl (); + /** * \brief See \ref dw-widget-sizes. */ @@ -292,8 +310,6 @@ protected: */ virtual void markExtremesChange (int ref); - int getMinWidth (Extremes *extremes, bool forceValue); - virtual int getAvailWidthOfChild (Widget *child, bool forceValue); virtual int getAvailHeightOfChild (Widget *child, bool forceValue); virtual void correctRequisitionOfChild (Widget *child, @@ -410,6 +426,11 @@ public: inline style::Style *getStyle () { return style; } /** \todo I do not like this. */ inline Allocation *getAllocation () { return &allocation; } + inline bool inAllocation (int x, int y) { + return wasAllocated () && x >= allocation.x && y >= allocation.y && + x <= allocation.x + allocation.width && + y <= allocation.y + getHeight (); + } inline int boxOffsetX () { return extraSpace.left + getStyle()->boxOffsetX (); } @@ -426,6 +447,8 @@ public: void getExtremes (Extremes *extremes); void sizeAllocate (Allocation *allocation); + void calcExtraSpace (); + int getAvailWidth (bool forceValue); int getAvailHeight (bool forceValue); virtual bool getAdjustMinWidth () { return Widget::adjustMinWidth; } @@ -442,15 +465,24 @@ public: virtual int applyPerWidth (int containerWidth, style::Length perWidth); virtual int applyPerHeight (int containerHeight, style::Length perHeight); + int getMinWidth (Extremes *extremes, bool forceValue); + virtual bool isBlockLevel (); virtual bool isPossibleContainer (); void containerSizeChanged (); - bool intersects (Rectangle *area, Rectangle *intersection); + bool intersects (Widget *refWidget, Rectangle *area, + Rectangle *intersection); /** Area is given in widget coordinates. */ - virtual void draw (View *view, Rectangle *area) = 0; + virtual void draw (View *view, Rectangle *area, DrawingContext *context) = 0; + void drawInterruption (View *view, Rectangle *area, DrawingContext *context); + + virtual Widget *getWidgetAtPoint (int x, int y, + GettingWidgetAtPointContext *context); + Widget *getWidgetAtPointInterrupted (int x, int y, + GettingWidgetAtPointContext *context); bool buttonPress (EventButton *event); bool buttonRelease (EventButton *event); @@ -481,11 +513,11 @@ public: inline Layout *getLayout () { return layout; } - virtual Widget *getWidgetAtPoint (int x, int y, int level); - void scrollTo (HPosition hpos, VPosition vpos, int x, int y, int width, int height); + void getMarginArea (int *xMar, int *yMar, int *widthMar, int *heightMar); + void getBorderArea (int *xBor, int *yBor, int *widthBor, int *heightBor); void getPaddingArea (int *xPad, int *yPad, int *widthPad, int *heightPad); /** @@ -502,6 +534,7 @@ public: * dw::core::Iterator::prev in this case. */ virtual Iterator *iterator (Content::Type mask, bool atEnd) = 0; + virtual void removeChild (Widget *child); }; diff --git a/lout/debug.hh b/lout/debug.hh index e2839196..5c0169e3 100644 --- a/lout/debug.hh +++ b/lout/debug.hh @@ -29,359 +29,7 @@ # define DEBUG_MSG(level, ...) # endif /* DEBUG_LEVEL */ - - -/* - * See <http://home.gna.org/rtfl/>. - */ - -#ifdef DBG_RTFL - -#include <unistd.h> -#include <stdio.h> - -#define DBG_IF_RTFL if(1) - -// "\n" at the beginning just in case that the previous line is not finished -// yet. -#define RTFL_PREFIX_FMT "\n[rtfl]%s:%d:%d:" -#define RTFL_PREFIX_ARGS CUR_WORKING_DIR "/" __FILE__, __LINE__, getpid() - -#define DBG_OBJ_MSG(aspect, prio, msg) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-msg:%p:%s:%d:%s\n", \ - RTFL_PREFIX_ARGS, this, aspect, prio, msg); \ - fflush (stdout); \ - } D_STMT_END - -// Variant which does not use "this", but an explicitly passed -// object. Should be applied to other macros. (Also, for use in C.) -#define DBG_OBJ_MSG_O(aspect, prio, obj, msg) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-msg:%p:%s:%d:%s\n", \ - RTFL_PREFIX_ARGS, obj, aspect, prio, msg); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_MSGF(aspect, prio, fmt, ...) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-msg:%p:%s:%d:" fmt "\n", \ - RTFL_PREFIX_ARGS, this, aspect, prio, __VA_ARGS__); \ - fflush (stdout); \ - } D_STMT_END - -// See DBG_OBJ_MSG_O. -#define DBG_OBJ_MSGF_O(aspect, prio, obj, fmt, ...) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-msg:%p:%s:%d:" fmt "\n", \ - RTFL_PREFIX_ARGS, obj, aspect, prio, __VA_ARGS__); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_MSG_START() \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-msg-start:%p\n", \ - RTFL_PREFIX_ARGS, this); \ - fflush (stdout); \ - } D_STMT_END - -// See DBG_OBJ_MSG_O. -#define DBG_OBJ_MSG_START_O(obj) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-msg-start:%p\n", \ - RTFL_PREFIX_ARGS, obj); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_MSG_END() \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-msg-end:%p\n", \ - RTFL_PREFIX_ARGS, this); \ - fflush (stdout); \ - } D_STMT_END - -// See DBG_OBJ_MSG_O. -#define DBG_OBJ_MSG_END_O(obj) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-msg-end:%p\n", \ - RTFL_PREFIX_ARGS, obj); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_ENTER0(aspect, prio, funname) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-enter:%p:%s:%d:%s:\n", \ - RTFL_PREFIX_ARGS, this, aspect, prio, funname); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_ENTER0_O(aspect, prio, obj, funname) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-enter:%p:%s:%d:%s:\n", \ - RTFL_PREFIX_ARGS, obj, aspect, prio, funname); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_ENTER(aspect, prio, funname, fmt, ...) \ - D_STMT_START { \ - fflush (stdout); \ - printf (RTFL_PREFIX_FMT "obj-enter:%p:%s:%d:%s:" fmt "\n", \ - RTFL_PREFIX_ARGS, this, aspect, prio, funname, __VA_ARGS__); \ - } D_STMT_END - -#define DBG_OBJ_ENTER_O(aspect, prio, obj, funname, fmt, ...) \ - D_STMT_START { \ - fflush (stdout); \ - printf (RTFL_PREFIX_FMT "obj-enter:%p:%s:%d:%s:" fmt "\n", \ - RTFL_PREFIX_ARGS, obj, aspect, prio, funname, __VA_ARGS__); \ - } D_STMT_END - -#define DBG_OBJ_LEAVE() \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-leave:%p\n", \ - RTFL_PREFIX_ARGS, this); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_LEAVE_O(obj) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-leave:%p\n", \ - RTFL_PREFIX_ARGS, obj); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_CREATE(klass) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-create:%p:%s\n", \ - RTFL_PREFIX_ARGS, this, klass); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_DELETE() \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-delete:%p\n", \ - RTFL_PREFIX_ARGS, this); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_BASECLASS(klass) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-ident:%p:%p\n", \ - RTFL_PREFIX_ARGS, this, (klass*)this); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_ASSOC(parent, child) \ - D_STMT_START { \ - if (child) { \ - printf (RTFL_PREFIX_FMT "obj-assoc:%p:%p\n", \ - RTFL_PREFIX_ARGS, parent, child); \ - fflush (stdout); \ - } \ - } D_STMT_END - -#define DBG_OBJ_ASSOC_PARENT(parent) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-assoc:%p:%p\n", \ - RTFL_PREFIX_ARGS, parent, this); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_ASSOC_CHILD(child) \ - D_STMT_START { \ - if (child) { \ - printf (RTFL_PREFIX_FMT "obj-assoc:%p:%p\n", \ - RTFL_PREFIX_ARGS, this, child); \ - fflush (stdout); \ - } \ - } D_STMT_END - -#define DBG_OBJ_SET_NUM(var, val) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%d\n", \ - RTFL_PREFIX_ARGS, this, var, val); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_SET_NUM_O(obj, var, val) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%d\n", \ - RTFL_PREFIX_ARGS, obj, var, val); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_SET_SYM(var, val) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%s\n", \ - RTFL_PREFIX_ARGS, this, var, val); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_SET_STR(var, val) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-set:%p:%s:\"%s\"\n", \ - RTFL_PREFIX_ARGS, this, var, val); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_SET_PTR(var, val) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%p\n", \ - RTFL_PREFIX_ARGS, this, var, val); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_SET_PTR_O(obj, var, val) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%p\n", \ - RTFL_PREFIX_ARGS, obj, var, val); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_SET_BOOL(var, val) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%s\n", \ - RTFL_PREFIX_ARGS, this, var, val ? "true" : "false"); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_SET_BOOL_O(obj, var, val) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%s\n", \ - RTFL_PREFIX_ARGS, obj, var, val ? "true" : "false"); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_SET_COL(var, val) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-set:%p:%s:#%06x\n", \ - RTFL_PREFIX_ARGS, this, var, val); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_ARRSET_NUM(var, ind, val) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d:%d\n", \ - RTFL_PREFIX_ARGS, this, var, ind, val); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_ARRSET_SYM(var, ind, val) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d:%s\n", \ - RTFL_PREFIX_ARGS, this, var, ind, val); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_ARRSET_BOOL(var, ind, val) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d:%s\n", \ - RTFL_PREFIX_ARGS, this, var, ind, val ? "true" : "false"); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_ARRSET_STR(var, ind, val) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d:\"%s\"\n", \ - RTFL_PREFIX_ARGS, this, var, ind, val); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_ARRSET_PTR(var, ind, val) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d:%p\n", \ - RTFL_PREFIX_ARGS, this, var, ind, val); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_ARRATTRSET_NUM(var, ind, attr, val) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d.%s:%d\n", \ - RTFL_PREFIX_ARGS, this, var, ind, attr, val); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_ARRATTRSET_SYM(var, ind, attr, val) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d.%s:%s\n", \ - RTFL_PREFIX_ARGS, this, var, ind, attr, val); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_ARRATTRSET_BOOL(var, ind, attr, val) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d.%s:%s\n", \ - RTFL_PREFIX_ARGS, this, var, ind, attr, val ? "true" : "false"); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_ARRATTRSET_STR(var, ind, attr, val) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d.%s:\"%s\"\n", \ - RTFL_PREFIX_ARGS, this, var, ind, attr, val); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_ARRATTRSET_PTR(var, ind, attr, val) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d.%s:%p\n", \ - RTFL_PREFIX_ARGS, this, var, ind, attr, val); \ - fflush (stdout); \ - } D_STMT_END - -#define DBG_OBJ_COLOR(klass, color) \ - D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-color:%s:%s\n", \ - RTFL_PREFIX_ARGS, color, klass); \ - fflush (stdout); \ - } D_STMT_END - -#else /* DBG_RTFL */ - -#define DBG_IF_RTFL if(0) - -#define DBG_OBJ_MSG(aspect, prio, msg) D_STMT_NOP -#define DBG_OBJ_MSG_O(aspect, prio, obj, msg) D_STMT_NOP -#define DBG_OBJ_MSGF(aspect, prio, fmt, ...) D_STMT_NOP -#define DBG_OBJ_MSGF_O(aspect, prio, obj, fmt, ...) D_STMT_NOP -#define DBG_OBJ_MSG_START() D_STMT_NOP -#define DBG_OBJ_MSG_START_O(obj) D_STMT_NOP -#define DBG_OBJ_MSG_END() D_STMT_NOP -#define DBG_OBJ_MSG_END_O(obj) D_STMT_NOP -#define DBG_OBJ_ENTER0(aspect, prio, funname) D_STMT_NOP -#define DBG_OBJ_ENTER0_O(aspect, prio, obj, funname) D_STMT_NOP -#define DBG_OBJ_ENTER(aspect, prio, funname, fmt, ...) D_STMT_NOP -#define DBG_OBJ_ENTER_O(aspect, prio, obj, funname, fmt, ...) D_STMT_NOP -#define DBG_OBJ_LEAVE() D_STMT_NOP -#define DBG_OBJ_LEAVE_O(obj) D_STMT_NOP -#define DBG_OBJ_CREATE(klass) D_STMT_NOP -#define DBG_OBJ_DELETE() D_STMT_NOP -#define DBG_OBJ_BASECLASS(klass) D_STMT_NOP -#define DBG_OBJ_ASSOC_PARENT(parent) D_STMT_NOP -#define DBG_OBJ_ASSOC_CHILD(child) D_STMT_NOP -#define DBG_OBJ_ASSOC(parent, child) D_STMT_NOP -#define DBG_OBJ_SET_NUM(var, val) D_STMT_NOP -#define DBG_OBJ_SET_NUM_O(obj, var, val) D_STMT_NOP -#define DBG_OBJ_SET_SYM(var, val) D_STMT_NOP -#define DBG_OBJ_SET_STR(var, val) D_STMT_NOP -#define DBG_OBJ_SET_PTR(var, val) D_STMT_NOP -#define DBG_OBJ_SET_PTR_O(obj, var, val) D_STMT_NOP -#define DBG_OBJ_SET_BOOL(var, val) D_STMT_NOP -#define DBG_OBJ_SET_BOOL_O(obj, var, val) D_STMT_NOP -#define DBG_OBJ_SET_COL(var, val) D_STMT_NOP -#define DBG_OBJ_ARRSET_NUM(var, ind, val) D_STMT_NOP -#define DBG_OBJ_ARRSET_SYM(var, ind, val) D_STMT_NOP -#define DBG_OBJ_ARRSET_STR(var, ind, val) D_STMT_NOP -#define DBG_OBJ_ARRSET_PTR(var, ind, val) D_STMT_NOP -#define DBG_OBJ_ARRSET_BOOL(var, ind, val) D_STMT_NOP -#define DBG_OBJ_ARRATTRSET_NUM(var, ind, attr, val) D_STMT_NOP -#define DBG_OBJ_ARRATTRSET_SYM(var, ind, attr, val) D_STMT_NOP -#define DBG_OBJ_ARRATTRSET_STR(var, ind, attr, val) D_STMT_NOP -#define DBG_OBJ_ARRATTRSET_PTR(var, ind, attr, val) D_STMT_NOP -#define DBG_OBJ_ARRATTRSET_BOOL(var, ind, attr, val) D_STMT_NOP -#define DBG_OBJ_COLOR(klass, color) D_STMT_NOP - -#endif /* DBG_RTFL */ +#include "debug_rtfl.hh" #endif /* __LOUT_DEBUG_H__ */ diff --git a/lout/debug_rtfl.hh b/lout/debug_rtfl.hh new file mode 100644 index 00000000..616424c2 --- /dev/null +++ b/lout/debug_rtfl.hh @@ -0,0 +1,410 @@ +// WARNING: This file has been generated. Do not edit! + +/* + * This file is part of RTFL, see <http://home.gna.org/rtfl/> + * for details. + * + * This file (but not RTFL itself) is in the public domain, since it is only a + * simple implementation of a protocol, containing nothing more than trivial + * work. However, it would be nice to keep this notice, along with the URL + * above. + * + * ---------------------------------------------------------------------------- + * + * Defines macros for printing RTFL commands. See documentation for detail + * (online at <http://home.gna.org/rtfl/doc/rtfl.html>). These macros are only + * active, when the pre-processor variable DBG_RTFL is defined. If not, + * alternatives are defined, which have no effect. + * + * This variant assumes that __FILE__ is only the base of the source file name, + * so, to get the full path, CUR_WORKING_DIR has to be defined. See RTFL + * documentation for more details. + */ + +#ifndef __DEBUG_RTFL_HH__ +#define __DEBUG_RTFL_HH__ + +#ifdef DBG_RTFL + +#include <stdio.h> +#include <stdarg.h> +#include <unistd.h> + +#define DBG_IF_RTFL if(1) + +#define STMT_START do +#define STMT_END while (0) + +// Prints an RTFL message to stdout. "fmt" contains simple format +// characters how to deal with the additional arguments (no "%" +// preceeding, as in printf), or "c" (short for "#%06x" and used for +// colors), or other characters, which are simply printed. No quoting: +// this function cannot be used to print the characters "d", "p", and +// "s" directly. + +inline void rtfl_print (const char *module, const char *version, + const char *file, int line, const char *fmt, ...) +{ + // "\n" at the beginning just in case that the previous line is not + // finished yet. + printf ("\n[rtfl-%s-%s]%s:%d:%d:", module, version, file, line, getpid ()); + + va_list args; + va_start (args, fmt); + + for (int i = 0; fmt[i]; i++) { + int n; + void *p; + char *s; + + switch (fmt[i]) { + case 'd': + n = va_arg(args, int); + printf ("%d", n); + break; + + case 'p': + p = va_arg(args, void*); + printf ("%p", p); + break; + + case 's': + s = va_arg (args, char*); + for (int j = 0; s[j]; j++) { + if (s[j] == ':' || s[j] == '\\') + putchar ('\\'); + putchar (s[j]); + } + break; + + case 'c': + n = va_arg(args, int); + printf ("#%06x", n); + break; + + default: + putchar (fmt[i]); + break; + } + } + + va_end (args); + + putchar ('\n'); + fflush (stdout); +} + +#define RTFL_PRINT(module, version, cmd, fmt, ...) \ + rtfl_print (module, version, CUR_WORKING_DIR "/" __FILE__, __LINE__, \ + "s:" fmt, cmd, __VA_ARGS__) + +#define RTFL_OBJ_VERSION "1.0" + +#define RTFL_OBJ_PRINT(cmd, fmt, ...) \ + RTFL_PRINT ("obj", RTFL_OBJ_VERSION, cmd, fmt, __VA_ARGS__) + +#define DBG_OBJ_MSG(aspect, prio, msg) \ + DBG_OBJ_MSG_O (aspect, prio, this, msg) + +#define DBG_OBJ_MSG_O(aspect, prio, obj, msg) \ + RTFL_OBJ_PRINT ("msg", "p:s:d:s", obj, aspect, prio, msg) + +#define DBG_OBJ_MSGF(aspect, prio, fmt, ...) \ + STMT_START { \ + char msg[256]; \ + snprintf (msg, sizeof (msg), fmt, __VA_ARGS__); \ + DBG_OBJ_MSG (aspect, prio, msg); \ + } STMT_END + +#define DBG_OBJ_MSGF_O(aspect, prio, obj, fmt, ...) \ + STMT_START { \ + char msg[256]; \ + snprintf (msg, sizeof (msg), fmt, __VA_ARGS__); \ + DBG_OBJ_MSG_O (aspect, prio, obj, msg); \ + } STMT_END + +#define DBG_OBJ_MARK(aspect, prio, mark) \ + DBG_OBJ_MARK_O (aspect, prio, this, mark) + +#define DBG_OBJ_MARK_O(aspect, prio, obj, mark) \ + RTFL_OBJ_PRINT ("mark", "p:s:d:s", obj, aspect, prio, mark) + +#define DBG_OBJ_MARKF(aspect, prio, fmt, ...) \ + STMT_START { \ + char mark[256]; \ + snprintf (mark, sizeof (mark), fmt, __VA_ARGS__); \ + DBG_OBJ_MARK (aspect, prio, mark); \ + } STMT_END + +#define DBG_OBJ_MARKF_O(aspect, prio, obj, fmt, ...) \ + STMT_START { \ + char mark[256]; \ + snprintf (mark, sizeof (mark), fmt, __VA_ARGS__); \ + DBG_OBJ_MARK_O (aspect, prio, obj, mark); \ + } STMT_END + +#define DBG_OBJ_MSG_START() \ + DBG_OBJ_MSG_START_O (this) + +#define DBG_OBJ_MSG_START_O(obj) \ + RTFL_OBJ_PRINT ("msg-start", "p", obj) + +#define DBG_OBJ_MSG_END() \ + DBG_OBJ_MSG_END_O (this) + +#define DBG_OBJ_MSG_END_O(obj) \ + RTFL_OBJ_PRINT ("msg-end", "p", obj) + +#define DBG_OBJ_ENTER0(aspect, prio, funname) \ + DBG_OBJ_ENTER0_O (aspect, prio, this, funname) + +#define DBG_OBJ_ENTER0_O(aspect, prio, obj, funname) \ + RTFL_OBJ_PRINT ("enter", "p:s:d:s:", obj, aspect, prio, funname); + +#define DBG_OBJ_ENTER(aspect, prio, funname, fmt, ...) \ + STMT_START { \ + char args[256]; \ + snprintf (args, sizeof (args), fmt, __VA_ARGS__); \ + RTFL_OBJ_PRINT ("enter", "p:s:d:s:s", this, aspect, prio, funname, \ + args); \ + } STMT_END + +#define DBG_OBJ_ENTER_O(aspect, prio, obj, funname, fmt, ...) \ + STMT_START { \ + char args[256]; \ + snprintf (args, sizeof (args), fmt, __VA_ARGS__); \ + RTFL_OBJ_PRINT ("enter", "p:s:d:s:s", obj, aspect, prio, funname, \ + args); \ + } STMT_END + +#define DBG_OBJ_LEAVE() \ + DBG_OBJ_LEAVE_O (this) + +#define DBG_OBJ_LEAVE_O(obj) \ + RTFL_OBJ_PRINT ("leave", "p", obj); + +#define DBG_OBJ_LEAVE_VAL(fmt, ...) \ + STMT_START { \ + char vals[256]; \ + snprintf (vals, sizeof (vals), fmt, __VA_ARGS__); \ + RTFL_OBJ_PRINT ("leave", "p:s", this, vals); \ + } STMT_END + +#define DBG_OBJ_LEAVE_VAL_O(obj, fmt, ...) \ + STMT_START { \ + char vals[256]; \ + snprintf (vals, sizeof (vals), fmt, __VA_ARGS__); \ + RTFL_OBJ_PRINT ("leave", "p:s", obj, vals); \ + } STMT_END + +#define DBG_OBJ_CREATE(klass) \ + DBG_OBJ_CREATE_O (this, klass) + +#define DBG_OBJ_CREATE_O(obj, klass) \ + RTFL_OBJ_PRINT ("create", "p:s", obj, klass); + +#define DBG_OBJ_DELETE() \ + DBG_OBJ_DELETE_O (this) + +#define DBG_OBJ_DELETE_O(obj) \ + RTFL_OBJ_PRINT ("delete", "p", obj); + +#define DBG_OBJ_BASECLASS(klass) \ + RTFL_OBJ_PRINT ("ident", "p:p", this, (klass*)this); + +#define DBG_OBJ_ASSOC(parent, child) \ + RTFL_OBJ_PRINT ("assoc", "p:p", parent, child); \ + +#define DBG_OBJ_ASSOC_PARENT(parent) \ + DBG_OBJ_ASSOC (parent, this); + +#define DBG_OBJ_ASSOC_CHILD(child) \ + DBG_OBJ_ASSOC (this, child); + +#define DBG_OBJ_SET_NUM(var, val) \ + DBG_OBJ_SET_NUM_O (this, var, val) + +#define DBG_OBJ_SET_NUM_O(obj, var, val) \ + RTFL_OBJ_PRINT ("set", "p:s:d", obj, var, val) + +#define DBG_OBJ_SET_SYM(var, val) \ + DBG_OBJ_SET_SYM_O (this, var, val) + +#define DBG_OBJ_SET_SYM_O(obj, var, val) \ + RTFL_OBJ_PRINT ("set", "p:s:s", obj, var, val) + +#define DBG_OBJ_SET_BOOL(var, val) \ + DBG_OBJ_SET_BOOL_O (this, var, val) + +#define DBG_OBJ_SET_BOOL_O(obj, var, val) \ + RTFL_OBJ_PRINT ("set", "p:s:s", obj, var, (val) ? "true" : "false") + +#define DBG_OBJ_SET_STR(var, val) \ + DBG_OBJ_SET_STR_O (this, var, val) + +#define DBG_OBJ_SET_STR_O(obj, var, val) \ + RTFL_OBJ_PRINT ("set", "p:s:\"s\"", obj, var, val) + +#define DBG_OBJ_SET_PTR(var, val) \ + DBG_OBJ_SET_PTR_O (this, var, val) + +#define DBG_OBJ_SET_PTR_O(obj, var, val) \ + RTFL_OBJ_PRINT ("set", "p:s:p", obj, var, val) + +#define DBG_OBJ_SET_COL(var, val) \ + DBG_OBJ_SET_COL_O (this, var, val) + +#define DBG_OBJ_SET_COL_O(obj, var, val) \ + RTFL_OBJ_PRINT ("set", "p:s:c", obj, var, val) + +#define DBG_OBJ_ARRSET_NUM(var, ind, val) \ + DBG_OBJ_ARRSET_NUM_O (this, var, ind, val) + +#define DBG_OBJ_ARRSET_NUM_O(obj, var, ind, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d:d", obj, var, ind, val) + +#define DBG_OBJ_ARRSET_SYM(var, ind, val) \ + DBG_OBJ_ARRSET_SYM_O (this, var, ind, val) + +#define DBG_OBJ_ARRSET_SYM_O(obj, var, ind, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d:s", obj, var, ind, val) + +#define DBG_OBJ_ARRSET_BOOL(var, ind, val) \ + DBG_OBJ_ARRSET_BOOL_O (this, var, ind, val) + +#define DBG_OBJ_ARRSET_BOOL_O(obj, var, ind, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d:s", obj, var, ind, (val) ? "true" : "false") + +#define DBG_OBJ_ARRSET_STR(var, ind, val) \ + DBG_OBJ_ARRSET_STR_O (this, var, ind, val) + +#define DBG_OBJ_ARRSET_STR_O(obj, var, ind, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d:\"s\"", obj, var, ind, val) + +#define DBG_OBJ_ARRSET_PTR(var, ind, val) \ + DBG_OBJ_ARRSET_PTR_O (this, var, ind, val) + +#define DBG_OBJ_ARRSET_PTR_O(obj, var, ind, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d:p", obj, var, ind, val) + +#define DBG_OBJ_ARRSET_COL(var, ind, val) \ + DBG_OBJ_ARRSET_COL_O (this, var, ind, val) + +#define DBG_OBJ_ARRSET_COL_O(obj, var, ind, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d:c", obj, var, ind, val) + +#define DBG_OBJ_ARRATTRSET_NUM(var, ind, attr, val) \ + DBG_OBJ_ARRATTRSET_NUM_O (this, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_NUM_O(obj, var, ind, attr, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d.s:d", obj, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_SYM(var, ind, attr, val) \ + DBG_OBJ_ARRATTRSET_SYM_O (this, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_SYM_O(obj, var, ind, attr, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d.s:s", obj, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_BOOL(var, ind, attr, val) \ + DBG_OBJ_ARRATTRSET_BOOL_O (this, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_BOOL_O(obj, var, ind, attr, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d.s:s", obj, var, ind, attr, \ + (val) ? "true" : "false") + +#define DBG_OBJ_ARRATTRSET_STR(var, ind, attr, val) \ + DBG_OBJ_ARRATTRSET_STR_O (this, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_STR_O(obj, var, ind, attr, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d.s:\"s\"", obj, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_PTR(var, ind, attr, val) \ + DBG_OBJ_ARRATTRSET_PTR_O (this, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_PTR_O(obj, var, ind, attr, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d.s:p", obj, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_COL(var, ind, attr, val) \ + DBG_OBJ_ARRATTRSET_COL_O (this, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_COL_O(obj, var, ind, attr, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d.s:c", obj, var, ind, attr, val) + +#define DBG_OBJ_CLASS_COLOR(klass, color) \ + RTFL_OBJ_PRINT ("class-color", "s:s", klass, color) + +#else /* DBG_RTFL */ + +#define STMT_NOP do { } while (0) + +#define DBG_IF_RTFL if(0) + +#define DBG_OBJ_MSG(aspect, prio, msg) STMT_NOP +#define DBG_OBJ_MSG_O(aspect, prio, obj, msg) STMT_NOP +#define DBG_OBJ_MSGF(aspect, prio, fmt, ...) STMT_NOP +#define DBG_OBJ_MSGF_O(aspect, prio, obj, fmt, ...) STMT_NOP +#define DBG_OBJ_MARK(aspect, prio, mark) STMT_NOP +#define DBG_OBJ_MARK_O(aspect, prio, obj, mark) STMT_NOP +#define DBG_OBJ_MARKF(aspect, prio, fmt, ...) STMT_NOP +#define DBG_OBJ_MARKF_O(aspect, prio, obj, fmt, ...) STMT_NOP +#define DBG_OBJ_MSG_START() STMT_NOP +#define DBG_OBJ_MSG_START_O(obj) STMT_NOP +#define DBG_OBJ_MSG_END() STMT_NOP +#define DBG_OBJ_MSG_END_O(obj) STMT_NOP +#define DBG_OBJ_ENTER0(aspect, prio, funname) STMT_NOP +#define DBG_OBJ_ENTER0_O(aspect, prio, obj, funname) STMT_NOP +#define DBG_OBJ_ENTER(aspect, prio, funname, fmt, ...) STMT_NOP +#define DBG_OBJ_ENTER_O(aspect, prio, obj, funname, fmt, ...) STMT_NOP +#define DBG_OBJ_LEAVE() STMT_NOP +#define DBG_OBJ_LEAVE_O(obj) STMT_NOP +#define DBG_OBJ_LEAVE_VAL(fmt, ...) STMT_NOP +#define DBG_OBJ_LEAVE_VAL_O(obj, fmt, ...) STMT_NOP +#define DBG_OBJ_CREATE(klass) STMT_NOP +#define DBG_OBJ_CREATE_O(obj, klass) STMT_NOP +#define DBG_OBJ_DELETE() STMT_NOP +#define DBG_OBJ_DELETE_O(obj) STMT_NOP +#define DBG_OBJ_BASECLASS(klass) STMT_NOP +#define DBG_OBJ_ASSOC(parent, child) STMT_NOP +#define DBG_OBJ_ASSOC_PARENT(parent) STMT_NOP +#define DBG_OBJ_ASSOC_CHILD(child) STMT_NOP +#define DBG_OBJ_SET_NUM(var, val) STMT_NOP +#define DBG_OBJ_SET_NUM_O(obj, var, val) STMT_NOP +#define DBG_OBJ_SET_SYM(var, val) STMT_NOP +#define DBG_OBJ_SET_SYM_O(obj, var, val) STMT_NOP +#define DBG_OBJ_SET_BOOL(var, val) STMT_NOP +#define DBG_OBJ_SET_BOOL_O(obj, var, val) STMT_NOP +#define DBG_OBJ_SET_STR(var, val) STMT_NOP +#define DBG_OBJ_SET_STR_O(obj, var, val) STMT_NOP +#define DBG_OBJ_SET_PTR(var, val) STMT_NOP +#define DBG_OBJ_SET_PTR_O(obj, var, val) STMT_NOP +#define DBG_OBJ_SET_COL(var, val) STMT_NOP +#define DBG_OBJ_SET_COL_O(obj, var, val) STMT_NOP +#define DBG_OBJ_ARRSET_NUM(var, ind, val) STMT_NOP +#define DBG_OBJ_ARRSET_NUM_O(obj, var, ind, val) STMT_NOP +#define DBG_OBJ_ARRSET_SYM(var, ind, val) STMT_NOP +#define DBG_OBJ_ARRSET_SYM_O(obj, var, ind, val) STMT_NOP +#define DBG_OBJ_ARRSET_BOOL(var, ind, val) STMT_NOP +#define DBG_OBJ_ARRSET_BOOL_O(obj, var, ind, val) STMT_NOP +#define DBG_OBJ_ARRSET_STR(var, ind, val) STMT_NOP +#define DBG_OBJ_ARRSET_STR_O(obj, var, ind, val) STMT_NOP +#define DBG_OBJ_ARRSET_PTR(var, ind, val) STMT_NOP +#define DBG_OBJ_ARRSET_PTR_O(obj, var, ind, val) STMT_NOP +#define DBG_OBJ_ARRSET_COL(var, ind, val) STMT_NOP +#define DBG_OBJ_ARRSET_COL_O(obj, var, ind, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_NUM(var, ind, attr, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_NUM_O(obj, var, ind, attr, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_SYM(var, ind, attr, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_SYM_O(obj, var, ind, attr, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_BOOL(var, ind, attr, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_BOOL_O(obj, var, ind, attr, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_STR(var, ind, attr, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_STR_O(obj, var, ind, attr, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_PTR(var, ind, attr, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_PTR_O(obj, var, ind, attr, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_COL(var, ind, attr, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_COL_O(obj, var, ind, attr, val) STMT_NOP +#define DBG_OBJ_CLASS_COLOR(klass, color) STMT_NOP + +#endif /* DBG_RTFL */ + +#endif /* __DEBUG_RTFL_HH__ */ diff --git a/lout/misc.hh b/lout/misc.hh index 0a05db31..ac9e42e6 100644 --- a/lout/misc.hh +++ b/lout/misc.hh @@ -64,6 +64,8 @@ inline int AsciiStrcasecmp(const char *s1, const char *s2) return ret; } +inline const char *boolToStr (bool b) { return b ? "true" : "false"; } + /** * \brief Simple (simpler than container::untyped::Vector and * container::typed::Vector) template based vector. diff --git a/src/cssparser.cc b/src/cssparser.cc index 1487a605..f4291797 100644 --- a/src/cssparser.cc +++ b/src/cssparser.cc @@ -198,7 +198,7 @@ const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = { Css_border_style_enum_vals}, {"border-top-width", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, Css_border_width_enum_vals}, - {"bottom", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL}, + {"bottom", {CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED}, NULL}, {"caption-side", {CSS_TYPE_UNUSED}, NULL}, {"clear", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_clear_enum_vals}, {"clip", {CSS_TYPE_UNUSED}, NULL}, @@ -222,7 +222,7 @@ const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = { {"font-weight", {CSS_TYPE_ENUM, CSS_TYPE_FONT_WEIGHT, CSS_TYPE_UNUSED}, Css_font_weight_enum_vals}, {"height", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_AUTO, CSS_TYPE_UNUSED}, NULL}, - {"left", {CSS_TYPE_UNUSED}, NULL}, + {"left", {CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED}, NULL}, {"letter-spacing", {CSS_TYPE_ENUM, CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED}, Css_letter_spacing_enum_vals}, {"line-height", @@ -261,7 +261,7 @@ const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = { {"padding-top", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL}, {"position", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_position_enum_vals}, {"quotes", {CSS_TYPE_UNUSED}, NULL}, - {"right", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL}, + {"right", {CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED}, NULL}, {"text-align", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_text_align_enum_vals}, {"text-decoration", {CSS_TYPE_MULTI_ENUM, CSS_TYPE_UNUSED}, Css_text_decoration_enum_vals}, @@ -269,7 +269,7 @@ const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = { {"text-shadow", {CSS_TYPE_UNUSED}, NULL}, {"text-transform", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_text_transform_enum_vals}, - {"top", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL}, + {"top", {CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED}, NULL}, {"unicode-bidi", {CSS_TYPE_UNUSED}, NULL}, {"vertical-align",{CSS_TYPE_ENUM, CSS_TYPE_UNUSED},Css_vertical_align_vals}, {"visibility", {CSS_TYPE_UNUSED}, NULL}, @@ -277,7 +277,7 @@ const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = { {"width", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_AUTO, CSS_TYPE_UNUSED}, NULL}, {"word-spacing", {CSS_TYPE_ENUM, CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED}, Css_word_spacing_enum_vals}, - {"z-index", {CSS_TYPE_UNUSED}, NULL}, + {"z-index", {CSS_TYPE_INTEGER, CSS_TYPE_AUTO, CSS_TYPE_UNUSED}, NULL}, /* These are extensions, for internal used, and never parsed. */ {"x-link", {CSS_TYPE_INTEGER, CSS_TYPE_UNUSED}, NULL}, @@ -793,9 +793,12 @@ bool CssParser::tokenMatchesProperty(CssPropertyName prop, CssValueType *type) return true; break; - case CSS_TYPE_UNUSED: case CSS_TYPE_INTEGER: - /* Not used for parser values. */ + if (ttype == CSS_TK_DECINT) + return true; + break; + + case CSS_TYPE_UNUSED: default: assert(false); break; @@ -1174,12 +1177,18 @@ bool CssParser::parseValue(CssPropertyName prop, } break; + case CSS_TYPE_INTEGER: + if (ttype == CSS_TK_DECINT) { + val->intVal = strtol(tval, NULL, 10); + ret = true; + nextToken(); + } + break; + case CSS_TYPE_UNUSED: /* nothing */ break; - case CSS_TYPE_INTEGER: - /* Not used for parser values. */ default: assert(false); /* not reached */ } diff --git a/src/dillo.cc b/src/dillo.cc index 2bfab238..847c9d63 100644 --- a/src/dillo.cc +++ b/src/dillo.cc @@ -380,18 +380,18 @@ static DilloUrl *makeStartUrl(char *str, bool local) */ int main(int argc, char **argv) { - DBG_OBJ_COLOR ("dw::*", "#c0ff80"); - DBG_OBJ_COLOR ("dw::fltk::*", "#c0c0ff"); - DBG_OBJ_COLOR ("dw::core::*", "#ffa0a0"); - DBG_OBJ_COLOR ("dw::core::style::*", "#ffe0a0"); - - DBG_OBJ_COLOR ("dw::Image", "#80ffa0"); - DBG_OBJ_COLOR ("dw::Textblock", "#f0ff80"); - DBG_OBJ_COLOR ("dw::OutOfFlowMgr", "#d0ff80"); - DBG_OBJ_COLOR ("dw::AlignedTextblock", "#e0ff80"); - DBG_OBJ_COLOR ("dw::ListItem", "#b0ff80"); - DBG_OBJ_COLOR ("dw::TableCell", "#80ff80"); - DBG_OBJ_COLOR ("dw::Table", "#80ffc0"); + DBG_OBJ_CLASS_COLOR ("dw::*", "#c0ff80"); + DBG_OBJ_CLASS_COLOR ("dw::fltk::*", "#c0c0ff"); + DBG_OBJ_CLASS_COLOR ("dw::core::*", "#ffa0a0"); + DBG_OBJ_CLASS_COLOR ("dw::core::style::*", "#ffe0a0"); + + DBG_OBJ_CLASS_COLOR ("dw::Image", "#80ffa0"); + DBG_OBJ_CLASS_COLOR ("dw::Textblock", "#f0ff80"); + DBG_OBJ_CLASS_COLOR ("dw::OutOfFlowMgr", "#d0ff80"); + DBG_OBJ_CLASS_COLOR ("dw::AlignedTextblock", "#e0ff80"); + DBG_OBJ_CLASS_COLOR ("dw::ListItem", "#b0ff80"); + DBG_OBJ_CLASS_COLOR ("dw::TableCell", "#80ff80"); + DBG_OBJ_CLASS_COLOR ("dw::Table", "#80ffc0"); uint_t opt_id; uint_t options_got = 0; diff --git a/src/html.cc b/src/html.cc index d6b64a19..3e4f27a8 100644 --- a/src/html.cc +++ b/src/html.cc @@ -380,9 +380,9 @@ static void Html_add_textblock(DilloHtml *html, bool addBreaks, int breakSpace, S_TOP(html)->hand_over_break = true; } -static bool Html_will_textblock_be_out_of_flow(DilloHtml *html) +static bool Html_must_add_breaks(DilloHtml *html) { - return HT2TB(html)->isStyleOutOfFlow (html->style ()); + return HT2TB(html)->mustAddBreaks (html->style ()); } /* @@ -3915,7 +3915,7 @@ static void Html_check_html5_obsolete(DilloHtml *html, int ni) static void Html_display_block(DilloHtml *html) { - Html_add_textblock(html, !Html_will_textblock_be_out_of_flow (html), 0, + Html_add_textblock(html, Html_must_add_breaks (html), 0, false /* Perhaps true for widgets oof? */); } diff --git a/src/styleengine.cc b/src/styleengine.cc index c005f881..97ca417e 100644 --- a/src/styleengine.cc +++ b/src/styleengine.cc @@ -726,6 +726,12 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props, case CSS_PROPERTY_MAX_HEIGHT: computeLength (&attrs->maxHeight, p->value.intVal, attrs->font); break; + case CSS_PROPERTY_Z_INDEX: + if (p->type == CSS_LENGTH_TYPE_AUTO) + attrs->zIndex = dw::core::style::Z_INDEX_AUTO; + else + attrs->zIndex = p->value.intVal; + break; case PROPERTY_X_LINK: attrs->x_link = p->value.intVal; break; diff --git a/test/Makefile.am b/test/Makefile.am index 3b474466..fd3252dc 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -25,6 +25,7 @@ noinst_PROGRAMS = \ dw-resource-test \ dw-ui-test \ containers \ + identity \ shapes \ cookies \ liang \ @@ -191,6 +192,9 @@ shapes_LDADD = \ containers_SOURCES = containers.cc containers_LDADD = $(top_builddir)/lout/liblout.a +identity_SOURCES = identity.cc +identity_LDADD = $(top_builddir)/lout/liblout.a + cookies_SOURCES = cookies.c cookies_LDADD = \ $(top_builddir)/dpip/libDpip.a \ diff --git a/test/dw_simple_container.cc b/test/dw_simple_container.cc index ed7849dc..2e9ed6ae 100644 --- a/test/dw_simple_container.cc +++ b/test/dw_simple_container.cc @@ -32,7 +32,7 @@ int SimpleContainer::CLASS_ID = -1; // ---------------------------------------------------------------------- SimpleContainer::SimpleContainerIterator::SimpleContainerIterator - (SimpleContainer *simpleContainer, Content::Type mask, bool atEnd) : +(SimpleContainer *simpleContainer, Content::Type mask, bool atEnd) : Iterator (simpleContainer, mask, atEnd) { content.type = atEnd ? Content::END : Content::START; @@ -63,7 +63,7 @@ int SimpleContainer::SimpleContainerIterator::index () } int SimpleContainer::SimpleContainerIterator::compareTo - (lout::object::Comparable *other) +(lout::object::Comparable *other) { return index () - ((SimpleContainerIterator*)other)->index (); } @@ -208,12 +208,13 @@ void SimpleContainer::sizeAllocateImpl (Allocation *allocation) } } -void SimpleContainer::draw (View *view, Rectangle *area) +void SimpleContainer::draw (View *view, Rectangle *area, + DrawingContext *context) { drawWidgetBox (view, area, false); Rectangle childArea; - if (child && child->intersects (area, &childArea)) - child->draw (view, &childArea); + if (child && child->intersects (this, area, &childArea)) + child->draw (view, &childArea, context); } Iterator *SimpleContainer::iterator (Content::Type mask, bool atEnd) diff --git a/test/dw_simple_container.hh b/test/dw_simple_container.hh index fdb67bec..021f5277 100644 --- a/test/dw_simple_container.hh +++ b/test/dw_simple_container.hh @@ -44,7 +44,8 @@ public: SimpleContainer (); ~SimpleContainer (); - void draw (core::View *view, core::Rectangle *area); + void draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context); core::Iterator *iterator (core::Content::Type mask, bool atEnd); void removeChild (Widget *child); diff --git a/test/identity.cc b/test/identity.cc new file mode 100644 index 00000000..a1a136eb --- /dev/null +++ b/test/identity.cc @@ -0,0 +1,55 @@ +/* + * This small program tests how IdentifiableObject works with multiple + * inheritance ("diamond" inheritance, more precisely, since all + * classes have there root in IdentifiableObject.) + * + * Current status: With virtual superclasses, you get a class + * hierarchie "root -> A -> B -> C", so that the first part of this + * example works actually (C is a subclass of A and of B), but the + * second fails (it should print "false", but it is erroneously + * assumed that B is a subclass of A.) + */ + +#include "../lout/identity.hh" + +using namespace lout::identity; + +class A: virtual public IdentifiableObject +{ +public: + static int CLASS_ID; + inline A () { registerName ("A", &CLASS_ID); } +}; + +class B: virtual public IdentifiableObject +{ +public: + static int CLASS_ID; + inline B () { registerName ("B", &CLASS_ID); } +}; + +class C: public A, public B +{ +public: + static int CLASS_ID; + inline C () { registerName ("C", &CLASS_ID); } +}; + +int A::CLASS_ID = -1, B::CLASS_ID = -1, C::CLASS_ID = -1; + +int main (int argc, char *argv[]) +{ + printf ("A: %d, B: %d, C: %d\n", A::CLASS_ID, B::CLASS_ID, C::CLASS_ID); + + C x; + assert (x.instanceOf (A::CLASS_ID)); + assert (x.instanceOf (B::CLASS_ID)); + assert (x.instanceOf (C::CLASS_ID)); + printf ("x: %d\n", x.getClassId ()); + + B y; + printf ("y: %d; instance of A: %s\n", + y.getClassId (), y.instanceOf (B::CLASS_ID) ? "true" : "false"); + + return 0; +} |