1 | /**
|
---|
2 | * XGroupItem.java
|
---|
3 | * Copyright (C) 2010 New Zealand Digital Library, http://expeditee.org
|
---|
4 | *
|
---|
5 | * This program is free software: you can redistribute it and/or modify
|
---|
6 | * it under the terms of the GNU General Public License as published by
|
---|
7 | * the Free Software Foundation, either version 3 of the License, or
|
---|
8 | * (at your option) any later version.
|
---|
9 | *
|
---|
10 | * This program is distributed in the hope that it will be useful,
|
---|
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
13 | * GNU General Public License for more details.
|
---|
14 | *
|
---|
15 | * You should have received a copy of the GNU General Public License
|
---|
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
---|
17 | */
|
---|
18 |
|
---|
19 | package org.expeditee.io.flowlayout;
|
---|
20 |
|
---|
21 | import java.awt.Point;
|
---|
22 | import java.awt.Polygon;
|
---|
23 | import java.awt.Rectangle;
|
---|
24 | import java.util.ArrayList;
|
---|
25 | import java.util.Collection;
|
---|
26 | import java.util.Collections;
|
---|
27 | import java.util.Comparator;
|
---|
28 | import java.util.HashSet;
|
---|
29 | import java.util.Iterator;
|
---|
30 | import java.util.LinkedList;
|
---|
31 | import java.util.List;
|
---|
32 |
|
---|
33 | import org.expeditee.gui.Frame;
|
---|
34 | import org.expeditee.gui.FrameUtils;
|
---|
35 | import org.expeditee.items.Item;
|
---|
36 | import org.expeditee.items.Line;
|
---|
37 | import org.expeditee.items.Text;
|
---|
38 |
|
---|
39 | public class XGroupItem extends XItem {
|
---|
40 | public static final Text GROUPSEP_END = new Text('2' + ""); // ASCII '2' is
|
---|
41 | // start of text
|
---|
42 |
|
---|
43 | public static final Text GROUPSEP_START = new Text('3' + ""); // ASCII '3'
|
---|
44 | // is end of
|
---|
45 | // text
|
---|
46 |
|
---|
47 | enum FlowType {
|
---|
48 | in_flow, out_of_flow_original, out_of_flow_faked_position
|
---|
49 | };
|
---|
50 |
|
---|
51 | public static boolean doImplicitBoxing = true;
|
---|
52 |
|
---|
53 | class MultiArrowHeadComparable implements Comparator<Item> {
|
---|
54 |
|
---|
55 | @Override
|
---|
56 | public int compare(Item o1, Item o2) {
|
---|
57 | int o1y = o1.getY();
|
---|
58 | int o2y = o2.getY();
|
---|
59 |
|
---|
60 | int order = 0; // default to assume they are identical
|
---|
61 |
|
---|
62 | if (o1y < o2y) {
|
---|
63 | order = -1; // done, o1 is higher up the frame than o2 => our
|
---|
64 | // definition of 'before'
|
---|
65 | } else if (o2y < o1y) {
|
---|
66 | order = +1; // also done, o1 is lower down the frame than o2 =>
|
---|
67 | // our definition of 'after'
|
---|
68 |
|
---|
69 | } else {
|
---|
70 | // have identical 'y' values, so look to 'x' to tie-break
|
---|
71 |
|
---|
72 | int o1x = o1.getX();
|
---|
73 | int o2x = o2.getX();
|
---|
74 |
|
---|
75 | if (o1x < o2x) {
|
---|
76 | order = -1; // done, o1 is to the left of o2 => our
|
---|
77 | // definition of 'before'
|
---|
78 | } else if (o2x < o1x) {
|
---|
79 | order = +1; // also done, o1 is to the right of o2 => our
|
---|
80 | // definition of 'after'
|
---|
81 | }
|
---|
82 | }
|
---|
83 |
|
---|
84 | return order;
|
---|
85 | }
|
---|
86 | }
|
---|
87 |
|
---|
88 | Frame frame;
|
---|
89 |
|
---|
90 | protected int y_span_height;
|
---|
91 | protected YOverlappingItemsSpan[] yitems_span_array;
|
---|
92 |
|
---|
93 | List<Text> raw_text_item_list;
|
---|
94 | List<XGroupItem> grouped_item_list;
|
---|
95 | List<Item> remaining_item_list;
|
---|
96 |
|
---|
97 | FlowType out_of_flow;
|
---|
98 |
|
---|
99 | protected XGroupItem() {
|
---|
100 | frame = null;
|
---|
101 |
|
---|
102 | y_span_height = 0;
|
---|
103 | yitems_span_array = null;
|
---|
104 |
|
---|
105 | raw_text_item_list = null;
|
---|
106 | grouped_item_list = null;
|
---|
107 | remaining_item_list = null;
|
---|
108 |
|
---|
109 | out_of_flow = FlowType.in_flow;
|
---|
110 | }
|
---|
111 |
|
---|
112 | public XGroupItem(Frame frame, List<Item> y_ordered_items,
|
---|
113 | Polygon enclosing_polygon) {
|
---|
114 | this.frame = frame;
|
---|
115 | this.out_of_flow = FlowType.in_flow;
|
---|
116 | ;
|
---|
117 |
|
---|
118 | if (enclosing_polygon == null) {
|
---|
119 | // e.g. when the top-level case, with no enclosing polygon at this
|
---|
120 | // stage
|
---|
121 | // => generate one based on the bounding box of the y_ordered_items
|
---|
122 | enclosing_polygon = DimensionExtent
|
---|
123 | .boundingBoxPolygon(y_ordered_items);
|
---|
124 | }
|
---|
125 |
|
---|
126 | Rectangle enclosing_bounding_rect = enclosing_polygon.getBounds();
|
---|
127 | initSpanArray(enclosing_bounding_rect);
|
---|
128 |
|
---|
129 | // Step 1: Separate out the raw-text-items, the grouped-items and
|
---|
130 | // 'remaining' (e.g. points and lines)
|
---|
131 |
|
---|
132 | raw_text_item_list = new ArrayList<Text>();
|
---|
133 | grouped_item_list = new ArrayList<XGroupItem>();
|
---|
134 | remaining_item_list = new ArrayList<Item>();
|
---|
135 |
|
---|
136 | separateYOverlappingItems(frame, y_ordered_items, enclosing_polygon,
|
---|
137 | raw_text_item_list, grouped_item_list, remaining_item_list);
|
---|
138 |
|
---|
139 | // Step 2: Add in the raw-text items
|
---|
140 | for (Text text_item : raw_text_item_list) {
|
---|
141 |
|
---|
142 | XRawItem x_raw_item = new XRawItem(text_item, this);
|
---|
143 | // overspill can occur (and is acceptable) when raw-text item spills
|
---|
144 | // out of enclosing shape (such as a rectangle)
|
---|
145 | if(x_raw_item.bounding_rect == null) {
|
---|
146 | final StringBuilder errorMsg =
|
---|
147 | new StringBuilder("Was about to try mapInItem(XRawItem) but found a null bounding_rect. Item details: ");
|
---|
148 | final String nl = System.getProperty("line.separator");
|
---|
149 | errorMsg.append("\t Item parent: " + text_item.getParent() + nl);
|
---|
150 | errorMsg.append("\t Item position: " + text_item.getPosition() + nl);
|
---|
151 | errorMsg.append("\t Item text content: " + text_item.getText() + nl);
|
---|
152 | System.err.println(errorMsg.toString());
|
---|
153 | }
|
---|
154 | else mapInItem(x_raw_item);
|
---|
155 | }
|
---|
156 |
|
---|
157 | }
|
---|
158 |
|
---|
159 | public XGroupItem(Frame frame, List<Item> y_ordered_items) {
|
---|
160 | this(frame, y_ordered_items, null);
|
---|
161 | }
|
---|
162 |
|
---|
163 | protected XGroupItem(XGroupItem imprint, Rectangle copy_to_bounding_rect) {
|
---|
164 | super();
|
---|
165 |
|
---|
166 | // Implement a shallow copy => share references to frame, and item list,
|
---|
167 | // and y-span
|
---|
168 | // Only the bounding box is changed => set to the
|
---|
169 | // 'copy_to_bounding_rect' passed in
|
---|
170 |
|
---|
171 | this.frame = imprint.frame;
|
---|
172 |
|
---|
173 | y_span_height = imprint.y_span_height;
|
---|
174 | yitems_span_array = imprint.yitems_span_array;
|
---|
175 |
|
---|
176 | this.raw_text_item_list = imprint.raw_text_item_list;
|
---|
177 | this.grouped_item_list = imprint.grouped_item_list;
|
---|
178 |
|
---|
179 | // int offX = imprint.getBoundingRect().x - copy_to_bounding_rect.x;
|
---|
180 | // int offY = imprint.getBoundingRect().y - copy_to_bounding_rect.y;
|
---|
181 | // this.grouped_item_list = new ArrayList<XGroupItem>();
|
---|
182 | // for(XGroupItem newChild : imprint.grouped_item_list) {
|
---|
183 | // Rectangle newRect = newChild.getBoundingRect();
|
---|
184 | // newRect.x -= offX;
|
---|
185 | // newRect.y -= offY;
|
---|
186 | // this.grouped_item_list.add(new XGroupItem(newChild, newRect));
|
---|
187 | // }
|
---|
188 | this.remaining_item_list = imprint.remaining_item_list;
|
---|
189 |
|
---|
190 | this.out_of_flow = imprint.out_of_flow; // Or perhaps set it to
|
---|
191 | // FlowType.out_of_flow_fake_position
|
---|
192 | // straight away?
|
---|
193 |
|
---|
194 | this.bounding_rect = new Rectangle(copy_to_bounding_rect); // deep copy
|
---|
195 | // to be on
|
---|
196 | // the safe
|
---|
197 | // side
|
---|
198 | }
|
---|
199 |
|
---|
200 | protected void initSpanArray(Rectangle bounding_rect) {
|
---|
201 | this.bounding_rect = bounding_rect;
|
---|
202 |
|
---|
203 | int y_top = getBoundingYTop();
|
---|
204 | int y_bot = getBoundingYBot();
|
---|
205 |
|
---|
206 | if (y_top <= y_bot) {
|
---|
207 | // => have non-trivial span
|
---|
208 |
|
---|
209 | y_span_height = (y_bot - y_top) + 2; // Polygon in Java excludes
|
---|
210 | // right and bottom sides so
|
---|
211 | // need extra "+1" in calc
|
---|
212 |
|
---|
213 | yitems_span_array = new YOverlappingItemsSpan[y_span_height];
|
---|
214 | } else {
|
---|
215 | y_span_height = 0;
|
---|
216 | yitems_span_array = null;
|
---|
217 | }
|
---|
218 | }
|
---|
219 |
|
---|
220 | public YOverlappingItemsSpan getSpanItemAt(int index) {
|
---|
221 | return yitems_span_array[index];
|
---|
222 | }
|
---|
223 |
|
---|
224 | public void setOutOfFlow(FlowType flow_type) {
|
---|
225 | out_of_flow = flow_type;
|
---|
226 | }
|
---|
227 |
|
---|
228 | public FlowType getFlowItem() {
|
---|
229 | return out_of_flow;
|
---|
230 | }
|
---|
231 |
|
---|
232 | public boolean isOriginalOutOfFlowItem() {
|
---|
233 | return out_of_flow == FlowType.out_of_flow_original;
|
---|
234 | }
|
---|
235 |
|
---|
236 | public boolean isFakedOutOfFlowItem() {
|
---|
237 | return out_of_flow == FlowType.out_of_flow_faked_position;
|
---|
238 | }
|
---|
239 |
|
---|
240 | public List<Text> getRawTextItemList() {
|
---|
241 | return raw_text_item_list;
|
---|
242 | }
|
---|
243 |
|
---|
244 | public List<Text[]> getRawTextLines() {
|
---|
245 | final List<Text> rawText = getRawTextItemList();
|
---|
246 | final List<Text> lineStarts = new LinkedList<Text>();
|
---|
247 | for (final YOverlappingItemsSpan span : this.yitems_span_array) {
|
---|
248 | if (span instanceof YOverlappingItemsTopEdge) {
|
---|
249 | final YOverlappingItemsTopEdge topEdge = (YOverlappingItemsTopEdge) span;
|
---|
250 | final List<XItem> xLine = topEdge.getXOrderedLine()
|
---|
251 | .getXItemList();
|
---|
252 | for (final XItem xitem : xLine) {
|
---|
253 | if (xitem instanceof XRawItem) {
|
---|
254 | final Text item = (Text) ((XRawItem) xitem).getItem();
|
---|
255 | lineStarts.add(item);
|
---|
256 | break;
|
---|
257 | }
|
---|
258 | }
|
---|
259 | }
|
---|
260 | }
|
---|
261 | final List<Integer> indexes = new LinkedList<Integer>();
|
---|
262 | for (final Text lineStart : lineStarts)
|
---|
263 | indexes.add(rawText.indexOf(lineStart));
|
---|
264 | final List<Text[]> ret = new LinkedList<Text[]>();
|
---|
265 | int rangeEnd = indexes.get(0);
|
---|
266 | int last = rangeEnd;
|
---|
267 | for (int i = 1; i < indexes.size(); i++) {
|
---|
268 | rangeEnd = indexes.get(i);
|
---|
269 | final List<Text> part = new LinkedList<Text>(rawText.subList(0,
|
---|
270 | rangeEnd - last));
|
---|
271 | last = rangeEnd;
|
---|
272 | rawText.removeAll(part);
|
---|
273 | part.toArray(new Text[] {});
|
---|
274 | ret.add(part.toArray(new Text[] {}));
|
---|
275 | }
|
---|
276 | ret.add(rawText.toArray(new Text[] {}));
|
---|
277 | return ret;
|
---|
278 | }
|
---|
279 |
|
---|
280 | public List<XGroupItem> getGroupedItemList() {
|
---|
281 | return grouped_item_list;
|
---|
282 | }
|
---|
283 |
|
---|
284 | public List<Item> getRemainingItemList() {
|
---|
285 | return remaining_item_list;
|
---|
286 | }
|
---|
287 |
|
---|
288 | public int getItemSpanLength() {
|
---|
289 | return yitems_span_array.length;
|
---|
290 | }
|
---|
291 |
|
---|
292 | public YOverlappingItemsSpan getYOverlappingItemsSpan(int y) {
|
---|
293 | int y_index = y - getBoundingYTop();
|
---|
294 |
|
---|
295 | if ((y_index < 0) || (y_index >= yitems_span_array.length)) {
|
---|
296 | int y_top = getBoundingYTop();
|
---|
297 | int y_bot = y_top + yitems_span_array.length - 1;
|
---|
298 |
|
---|
299 | System.err
|
---|
300 | .println("Error in getYOverlappingItemsSpan(): index out of bounds for value "
|
---|
301 | + y);
|
---|
302 | System.err.println(" => Operation mapped into local array: y-top="
|
---|
303 | + y_top + ", y-bot=" + y_bot);
|
---|
304 |
|
---|
305 | return null;
|
---|
306 | }
|
---|
307 | return yitems_span_array[y_index];
|
---|
308 | }
|
---|
309 |
|
---|
310 | protected int cropToTop(int y) {
|
---|
311 | int y_index = y - getBoundingYTop();
|
---|
312 |
|
---|
313 | if (y_index < 0) {
|
---|
314 | y = getBoundingYTop();
|
---|
315 | }
|
---|
316 |
|
---|
317 | return y;
|
---|
318 | }
|
---|
319 |
|
---|
320 | protected int cropToBot(int y) {
|
---|
321 | int y_index = y - getBoundingYTop();
|
---|
322 |
|
---|
323 | if (y_index >= yitems_span_array.length) {
|
---|
324 | y = getBoundingYBot();
|
---|
325 | }
|
---|
326 |
|
---|
327 | return y;
|
---|
328 | }
|
---|
329 |
|
---|
330 | public void setYOverlappingItemsSpan(int y,
|
---|
331 | YOverlappingItemsSpan yitems_span) {
|
---|
332 | int y_index = y - getBoundingYTop();
|
---|
333 |
|
---|
334 | if ((y_index < 0) || (y_index >= yitems_span_array.length)) {
|
---|
335 |
|
---|
336 | int y_top = getBoundingYTop();
|
---|
337 | int y_bot = y_top + yitems_span_array.length - 1;
|
---|
338 |
|
---|
339 | System.err
|
---|
340 | .println("Error in setYOverlappingItemsSpan(): index out of bounds for value "
|
---|
341 | + y);
|
---|
342 | System.err.println(" => Operation mapped into local array: y-top="
|
---|
343 | + y_top + ", y-bot=" + y_bot);
|
---|
344 |
|
---|
345 | return;
|
---|
346 | }
|
---|
347 | yitems_span_array[y_index] = yitems_span;
|
---|
348 | }
|
---|
349 |
|
---|
350 | protected List<Item> followLinesToArrowHeads(Collection<Item> visited,
|
---|
351 | Item anchor_item, List<Line> used_in_lines) {
|
---|
352 | List<Item> arrow_head_endpoints = new ArrayList<Item>();
|
---|
353 |
|
---|
354 | for (Line line : used_in_lines) {
|
---|
355 |
|
---|
356 | Item start_item = line.getStartItem();
|
---|
357 |
|
---|
358 | if (start_item == anchor_item) {
|
---|
359 | // the line we're considering is heading in the right direction
|
---|
360 |
|
---|
361 | Item end_item = line.getEndItem();
|
---|
362 |
|
---|
363 | if (!visited.contains(end_item)) {
|
---|
364 | // Needs processing
|
---|
365 | visited.add(end_item);
|
---|
366 |
|
---|
367 | List<Line> follow_lines = end_item.getLines();
|
---|
368 |
|
---|
369 | if (follow_lines.size() == 1) {
|
---|
370 | // reached an end-point
|
---|
371 | if (end_item.hasVisibleArrow()) {
|
---|
372 | arrow_head_endpoints.add(end_item);
|
---|
373 | }
|
---|
374 | } else if (follow_lines.size() > 1) {
|
---|
375 |
|
---|
376 | List<Item> followed_arrow_heads = followLinesToArrowHeads(
|
---|
377 | visited, end_item, follow_lines);
|
---|
378 | arrow_head_endpoints.addAll(followed_arrow_heads);
|
---|
379 | }
|
---|
380 | }
|
---|
381 | }
|
---|
382 |
|
---|
383 | }
|
---|
384 | return arrow_head_endpoints;
|
---|
385 | }
|
---|
386 |
|
---|
387 | protected XGroupItem innermostXGroup(Item arrow_head,
|
---|
388 | List<XGroupItem> grouped_item_list) {
|
---|
389 | XGroupItem innermost_item = null;
|
---|
390 |
|
---|
391 | for (XGroupItem xgroup_item : grouped_item_list) {
|
---|
392 | if (xgroup_item.containsItem(arrow_head)) {
|
---|
393 |
|
---|
394 | innermost_item = xgroup_item;
|
---|
395 |
|
---|
396 | // Now see if it is in any of the nested ones?
|
---|
397 |
|
---|
398 | List<XGroupItem> nested_group_item_list = xgroup_item.grouped_item_list;
|
---|
399 |
|
---|
400 | XGroupItem potentially_better_item = innermostXGroup(
|
---|
401 | arrow_head, nested_group_item_list);
|
---|
402 |
|
---|
403 | if (potentially_better_item != null) {
|
---|
404 | innermost_item = potentially_better_item;
|
---|
405 | }
|
---|
406 | break;
|
---|
407 | }
|
---|
408 |
|
---|
409 | }
|
---|
410 |
|
---|
411 | return innermost_item;
|
---|
412 | }
|
---|
413 |
|
---|
414 | protected void removeXGroupItem(XGroupItem remove_item,
|
---|
415 | List<XGroupItem> grouped_item_list) {
|
---|
416 |
|
---|
417 | for (XGroupItem xgroup_item : grouped_item_list) {
|
---|
418 |
|
---|
419 | if (xgroup_item == remove_item) {
|
---|
420 | grouped_item_list.remove(xgroup_item);
|
---|
421 | return;
|
---|
422 | } else {
|
---|
423 | List<XGroupItem> nested_group_item_list = xgroup_item.grouped_item_list;
|
---|
424 | removeXGroupItem(remove_item, nested_group_item_list);
|
---|
425 |
|
---|
426 | }
|
---|
427 | }
|
---|
428 | }
|
---|
429 |
|
---|
430 | protected boolean daisyChainContainsLoop(final XGroupItem toplevel_xgroup,
|
---|
431 | final Item exitingDot, Collection<XGroupItem> groupsSeen) {
|
---|
432 | final Item start_item = exitingDot;
|
---|
433 | final Item end_item = start_item.getLines().get(0).getEndItem();
|
---|
434 | final XGroupItem xgroup = innermostXGroup(end_item,
|
---|
435 | toplevel_xgroup.getGroupedItemList());
|
---|
436 |
|
---|
437 | if(xgroup == null) return false;
|
---|
438 |
|
---|
439 | if (groupsSeen.contains(xgroup))
|
---|
440 | return true;
|
---|
441 |
|
---|
442 | groupsSeen.add(xgroup);
|
---|
443 |
|
---|
444 | for (final Item i : xgroup.remaining_item_list) {
|
---|
445 | final List<Line> lines = i.getLines();
|
---|
446 | if (lines.size() == 1 && lines.get(0).getStartItem() == i) {
|
---|
447 | return daisyChainContainsLoop(toplevel_xgroup, i, groupsSeen);
|
---|
448 | }
|
---|
449 | }
|
---|
450 | return false;
|
---|
451 | }
|
---|
452 |
|
---|
453 | protected void repositionOutOfFlowGroupsFollowLine(
|
---|
454 | XGroupItem toplevel_xgroup, Item remaining_item,
|
---|
455 | Collection<XGroupItem> out_of_flow) {
|
---|
456 | // See if this item is the start of a line
|
---|
457 | // Follow it if it is
|
---|
458 | // For each end of the line (potentially a multi-split poly-line) that
|
---|
459 | // has an arrow head:
|
---|
460 | // See if the arrow-head falls inside an XGroup area
|
---|
461 | // => Ignore endings that are in the same XGroupItem as the line started
|
---|
462 | // in
|
---|
463 | List<Line> used_in_lines = remaining_item.getLines();
|
---|
464 | if (used_in_lines.size() == 1) {
|
---|
465 | // at the start (or end) of a line
|
---|
466 |
|
---|
467 | Item start_item = used_in_lines.get(0).getStartItem();
|
---|
468 |
|
---|
469 | if (remaining_item == start_item) {
|
---|
470 |
|
---|
471 | // found the start of a line
|
---|
472 |
|
---|
473 | Collection<Item> visited = new HashSet<Item>();
|
---|
474 | visited.add(remaining_item);
|
---|
475 |
|
---|
476 | List<Item> arrow_head_endpoints = followLinesToArrowHeads(
|
---|
477 | visited, remaining_item, used_in_lines);
|
---|
478 |
|
---|
479 | // System.out.println("**** For Xgroup " + this +
|
---|
480 | // " with dot starting at " + remaining_item + " has " +
|
---|
481 | // arrow_head_endpoints.size() + " arrow head endpoints");
|
---|
482 |
|
---|
483 | Collections.sort(arrow_head_endpoints,
|
---|
484 | new MultiArrowHeadComparable());
|
---|
485 |
|
---|
486 | for (Item arrow_head : arrow_head_endpoints) {
|
---|
487 | // find the inner-most group it falls within
|
---|
488 |
|
---|
489 | List<XGroupItem> toplevel_grouped_item_list = toplevel_xgroup
|
---|
490 | .getGroupedItemList();
|
---|
491 |
|
---|
492 | XGroupItem xgroup_item = innermostXGroup(arrow_head,
|
---|
493 | toplevel_grouped_item_list);
|
---|
494 |
|
---|
495 | if (xgroup_item != null) {
|
---|
496 |
|
---|
497 | // Ignore if the found 'xgroup_item' is 'this'
|
---|
498 | // (i.e. the found arrow head is at the same XGroupItem
|
---|
499 | // level we are currently processing)
|
---|
500 |
|
---|
501 | if (xgroup_item != this) {
|
---|
502 |
|
---|
503 | if (/* out_of_flow.contains(xgroup_item) && */daisyChainContainsLoop(
|
---|
504 | toplevel_xgroup, start_item,
|
---|
505 | new ArrayList<XGroupItem>())) {
|
---|
506 | System.err.println("#Found infinite loop; not following. On frame: " +
|
---|
507 | start_item.getParent().getName() + " At position: " +
|
---|
508 | start_item.getPosition());
|
---|
509 | continue;
|
---|
510 | }
|
---|
511 |
|
---|
512 | if (!out_of_flow.contains(xgroup_item)) {
|
---|
513 | for (Item remaining_item_deeper : xgroup_item
|
---|
514 | .getRemainingItemList()) {
|
---|
515 | xgroup_item
|
---|
516 | .repositionOutOfFlowGroupsFollowLine(
|
---|
517 | toplevel_xgroup,
|
---|
518 | remaining_item_deeper,
|
---|
519 | out_of_flow);
|
---|
520 | }
|
---|
521 | }
|
---|
522 |
|
---|
523 | out_of_flow.add(xgroup_item);
|
---|
524 |
|
---|
525 | // Can't delete here, as it causes a concurrent
|
---|
526 | // exception => add to 'out_of_flow' hashmap and
|
---|
527 | // perform removal later
|
---|
528 |
|
---|
529 | // System.out.println("**** innermost XGroupItem = "
|
---|
530 | // + xgroup_item);
|
---|
531 |
|
---|
532 | // Artificially rework its (x,y) org and dimension
|
---|
533 | // to make it appear where the start of the arrow is
|
---|
534 |
|
---|
535 | Rectangle start_rect = start_item.getArea()
|
---|
536 | .getBounds();
|
---|
537 |
|
---|
538 | XGroupItem xgroup_item_shallow_copy = new XGroupItem(
|
---|
539 | xgroup_item, start_rect);
|
---|
540 |
|
---|
541 | // Perhaps the following two lines should be moved
|
---|
542 | // to inside the constructor??
|
---|
543 |
|
---|
544 | xgroup_item
|
---|
545 | .setOutOfFlow(FlowType.out_of_flow_original);
|
---|
546 | xgroup_item_shallow_copy
|
---|
547 | .setOutOfFlow(FlowType.out_of_flow_faked_position);
|
---|
548 |
|
---|
549 | // xgroup_item.setBoundingRect(start_rect);
|
---|
550 | // xgroup_item.setOutOfFlow();
|
---|
551 |
|
---|
552 | // Finally add it in
|
---|
553 | // mapInItem(xgroup_item);
|
---|
554 | mapInItem(xgroup_item_shallow_copy);
|
---|
555 | }
|
---|
556 | }
|
---|
557 | }
|
---|
558 | }
|
---|
559 |
|
---|
560 | }
|
---|
561 | }
|
---|
562 |
|
---|
563 | /**
|
---|
564 | * Look for any 'out-of-flow' XGroup boxes, signalled by the user drawing an
|
---|
565 | * arrow line to it => force an artificial change to such boxes so its
|
---|
566 | * dimensions becomes the starting point of the arrow line. This will make
|
---|
567 | * it sort to the desired spot in the YOverlapping span
|
---|
568 | */
|
---|
569 |
|
---|
570 | public void repositionOutOfFlowGroupsRecursive(XGroupItem toplevel_xgroup,
|
---|
571 | Collection<XGroupItem> out_of_flow) {
|
---|
572 | // Map in all the items in the given list:
|
---|
573 | for (Item remaining_item : remaining_item_list) {
|
---|
574 |
|
---|
575 | repositionOutOfFlowGroupsFollowLine(toplevel_xgroup,
|
---|
576 | remaining_item, out_of_flow);
|
---|
577 | }
|
---|
578 |
|
---|
579 | // Now recursively work through each item's nested x-groups
|
---|
580 | for (XGroupItem xgroup_item : grouped_item_list) {
|
---|
581 |
|
---|
582 | if (!out_of_flow.contains(xgroup_item)) {
|
---|
583 | xgroup_item.repositionOutOfFlowGroupsRecursive(toplevel_xgroup,
|
---|
584 | out_of_flow);
|
---|
585 | }
|
---|
586 | }
|
---|
587 | }
|
---|
588 |
|
---|
589 | public void repositionOutOfFlowGroups(XGroupItem toplevel_xgroup) {
|
---|
590 | // Collection<XGroupItem> out_of_flow = new HashSet<XGroupItem>();
|
---|
591 | Collection<XGroupItem> out_of_flow = new ArrayList<XGroupItem>();
|
---|
592 |
|
---|
593 | repositionOutOfFlowGroupsRecursive(toplevel_xgroup, out_of_flow);
|
---|
594 |
|
---|
595 | // List<XGroupItem >toplevel_grouped_item_list =
|
---|
596 | // toplevel_xgroup.getGroupedItemList();
|
---|
597 |
|
---|
598 | // ****
|
---|
599 |
|
---|
600 | // Want to remove the original "out-of-position" blocks that were found,
|
---|
601 | // as (for each arrow
|
---|
602 | // point to such an original) shallow-copies now exist with 'faked'
|
---|
603 | // positions that correspond
|
---|
604 | // to where the arrows start.
|
---|
605 |
|
---|
606 | // No longer remove them from the nested grouped structure, rather, rely
|
---|
607 | // on these items being
|
---|
608 | // tagged "isOutOfFLow()" to be skipped by subsequent traversal calls
|
---|
609 | // (e.g., to generate
|
---|
610 | // the normal "in-flow" nested blocks)
|
---|
611 |
|
---|
612 | // Structuring things this way, means that it is now possible to have
|
---|
613 | // multiple arrows
|
---|
614 | // pointing to the same block of code, and both "out-of-flow" arrows
|
---|
615 | // will be honoured,
|
---|
616 | // replicating the code at their respective start points
|
---|
617 |
|
---|
618 | /*
|
---|
619 | * for (XGroupItem xgroup_item: out_of_flow) {
|
---|
620 | * removeXGroupItem(xgroup_item,toplevel_grouped_item_list); }
|
---|
621 | */
|
---|
622 |
|
---|
623 | }
|
---|
624 |
|
---|
625 | /**
|
---|
626 | * Focusing only on enclosures that fit within the given polygon *and* have
|
---|
627 | * this item in it, the method finds the largest of these enclosures and
|
---|
628 | * returns all the items it contains
|
---|
629 | *
|
---|
630 | * @return
|
---|
631 | */
|
---|
632 | public Collection<Item> getItemsInNestedEnclosure(Item given_item,
|
---|
633 | AreaPolygon outer_polygon) {
|
---|
634 | Collection<Item> sameEnclosure = null;
|
---|
635 | Collection<Item> seen = new HashSet<Item>();
|
---|
636 |
|
---|
637 | double enclosureArea = 0.0;
|
---|
638 |
|
---|
639 | for (Item i : frame.getItems()) {
|
---|
640 |
|
---|
641 | // Go through all the enclosures ...
|
---|
642 |
|
---|
643 | if (!seen.contains(i) && i.isEnclosed()) {
|
---|
644 |
|
---|
645 | Collection<Item> i_enclosing_dots_coll = i.getEnclosingDots();
|
---|
646 | List<Item> i_enclosing_dots = new ArrayList<Item>(
|
---|
647 | i_enclosing_dots_coll); // Change to List of items
|
---|
648 |
|
---|
649 | seen.addAll(i_enclosing_dots);
|
---|
650 |
|
---|
651 | Polygon i_polygon = new Polygon();
|
---|
652 | for (int di = 0; di < i_enclosing_dots.size(); di++) {
|
---|
653 | Item d = i_enclosing_dots.get(di);
|
---|
654 |
|
---|
655 | i_polygon.addPoint(d.getX(), d.getY());
|
---|
656 | }
|
---|
657 |
|
---|
658 | // ... looking for one that is completely contained in the
|
---|
659 | // 'outer_polygon' and ...
|
---|
660 | if (outer_polygon.completelyContains(i_polygon)) {
|
---|
661 |
|
---|
662 | Collection<Item> enclosed = i.getEnclosedItems();
|
---|
663 |
|
---|
664 | // ... includes this item
|
---|
665 | if (enclosed.contains(given_item)) {
|
---|
666 |
|
---|
667 | // Remember it if it is larger than anything we've
|
---|
668 | // encountered before
|
---|
669 | if ((i.getEnclosedArea() > enclosureArea)) {
|
---|
670 | enclosureArea = i.getEnclosedArea();
|
---|
671 | sameEnclosure = enclosed;
|
---|
672 | }
|
---|
673 | }
|
---|
674 | }
|
---|
675 |
|
---|
676 | }
|
---|
677 | }
|
---|
678 |
|
---|
679 | if (sameEnclosure == null)
|
---|
680 | return new LinkedList<Item>();
|
---|
681 |
|
---|
682 | return sameEnclosure;
|
---|
683 | }
|
---|
684 |
|
---|
685 | public void separateYOverlappingItems(Frame frame, List<Item> item_list,
|
---|
686 | Polygon enclosing_polygon, List<Text> raw_text_item_list,
|
---|
687 | List<XGroupItem> grouped_item_list, List<Item> remaining_item_list) {
|
---|
688 | final List<Item> origonal_item_list = new ArrayList<Item>(item_list);
|
---|
689 | // raw_text_items_list => all the non-enclosed text items
|
---|
690 | // grouped_items_list => list of all enclosures containing text items
|
---|
691 | // remaining_item_list => whatever is left: mostly dots that form part
|
---|
692 | // of lines/polylines/polygons
|
---|
693 |
|
---|
694 | AreaPolygon area_enclosing_polygon = new AreaPolygon(enclosing_polygon);
|
---|
695 |
|
---|
696 | List<AreaPolygon> area_enclosed_polygon_list = new ArrayList<AreaPolygon>();
|
---|
697 |
|
---|
698 | while (item_list.size() > 0) {
|
---|
699 | Item item = item_list.remove(0); // shift
|
---|
700 |
|
---|
701 | if (item instanceof Text) {
|
---|
702 | Text text = (Text) item;
|
---|
703 |
|
---|
704 | Collection<Item> items_in_nested_enclosure_coll = getItemsInNestedEnclosure(
|
---|
705 | text, area_enclosing_polygon);
|
---|
706 | List<Item> items_in_nested_enclosure = new ArrayList<Item>(
|
---|
707 | items_in_nested_enclosure_coll); // Change to handling
|
---|
708 | // it as a list
|
---|
709 |
|
---|
710 | if (items_in_nested_enclosure.size() == 0) {
|
---|
711 | // if(text.getPixelBoundsUnion().x >=
|
---|
712 | // this.getBoundingXLeft() && text.getPixelBoundsUnion().y
|
---|
713 | // >= this.getBoundingYTop())
|
---|
714 | // raw_text_item_list.add(text);
|
---|
715 | // else remaining_item_list.add(text);
|
---|
716 | raw_text_item_list.add(text);
|
---|
717 | } else {
|
---|
718 |
|
---|
719 | // Something other than just this text-item is around
|
---|
720 |
|
---|
721 | while (items_in_nested_enclosure.size() > 0) {
|
---|
722 |
|
---|
723 | Item enclosure_item = items_in_nested_enclosure
|
---|
724 | .remove(0); // shift
|
---|
725 |
|
---|
726 | Polygon enclosed_polygon = enclosure_item
|
---|
727 | .getEnclosedShape();
|
---|
728 |
|
---|
729 | if (enclosed_polygon != null) {
|
---|
730 | // Got a group
|
---|
731 | // => Remove any items-in-nested-enclosure from:
|
---|
732 | //
|
---|
733 | // 'item_list' (so we don't waste our time
|
---|
734 | // discovering and processing again these related
|
---|
735 | // items)
|
---|
736 | // and
|
---|
737 | // 'raw_text_item_list' and 'remaining_item_lst' (as
|
---|
738 | // we now know they are part of the nested enclosed
|
---|
739 | // group)
|
---|
740 |
|
---|
741 | AreaPolygon area_enclosed_polygon = new AreaPolygon(
|
---|
742 | enclosed_polygon);
|
---|
743 | area_enclosed_polygon_list
|
---|
744 | .add(area_enclosed_polygon);
|
---|
745 |
|
---|
746 | item_list.remove(enclosure_item);
|
---|
747 | item_list.removeAll(items_in_nested_enclosure);
|
---|
748 |
|
---|
749 | raw_text_item_list.remove(enclosure_item);
|
---|
750 | raw_text_item_list
|
---|
751 | .removeAll(items_in_nested_enclosure);
|
---|
752 |
|
---|
753 | remaining_item_list.remove(enclosure_item);
|
---|
754 | remaining_item_list
|
---|
755 | .removeAll(items_in_nested_enclosure);
|
---|
756 |
|
---|
757 | // Now remove any of the just used enclosed-items if
|
---|
758 | // they formed part of the perimeter
|
---|
759 | List<Item> items_on_perimeter = new ArrayList<Item>();
|
---|
760 |
|
---|
761 | Iterator<Item> item_iterator = items_in_nested_enclosure
|
---|
762 | .iterator();
|
---|
763 | while (item_iterator.hasNext()) {
|
---|
764 | Item item_to_check = item_iterator.next();
|
---|
765 | Point pt_to_check = new Point(
|
---|
766 | item_to_check.getX(),
|
---|
767 | item_to_check.getY());
|
---|
768 |
|
---|
769 | if (area_enclosed_polygon
|
---|
770 | .isPerimeterPoint(pt_to_check)) {
|
---|
771 | items_on_perimeter.add(item_to_check);
|
---|
772 | }
|
---|
773 | }
|
---|
774 |
|
---|
775 | items_in_nested_enclosure
|
---|
776 | .removeAll(items_on_perimeter);
|
---|
777 | }
|
---|
778 |
|
---|
779 | else {
|
---|
780 | // Text or single point (with no enclosure)
|
---|
781 |
|
---|
782 | // This item doesn't feature at this level
|
---|
783 | // => will be subsequently capture by the group
|
---|
784 | // polygon below
|
---|
785 | // and processed by the recursive call
|
---|
786 |
|
---|
787 | item_list.remove(enclosure_item);
|
---|
788 | }
|
---|
789 | }
|
---|
790 |
|
---|
791 | }
|
---|
792 |
|
---|
793 | } // end of Text test
|
---|
794 | else {
|
---|
795 | // non-text item => add to remaining_item_list
|
---|
796 | remaining_item_list.add(item);
|
---|
797 | }
|
---|
798 |
|
---|
799 | }
|
---|
800 |
|
---|
801 | // Sort areas, smallest to largest
|
---|
802 | Collections.sort(area_enclosed_polygon_list,
|
---|
803 | new Comparator<AreaPolygon>() {
|
---|
804 |
|
---|
805 | public int compare(AreaPolygon ap1, AreaPolygon ap2) {
|
---|
806 | Double ap1_area = ap1.getArea();
|
---|
807 | Double ap2_area = ap2.getArea();
|
---|
808 |
|
---|
809 | return ap2_area.compareTo(ap1_area);
|
---|
810 | }
|
---|
811 | });
|
---|
812 |
|
---|
813 | // Remove any enclosed polygon areas that are completely contained in a
|
---|
814 | // larger one
|
---|
815 |
|
---|
816 | for (int ri = 0; ri < area_enclosed_polygon_list.size(); ri++) {
|
---|
817 | // ri = remove index pos
|
---|
818 |
|
---|
819 | AreaPolygon rpoly = area_enclosed_polygon_list.get(ri);
|
---|
820 |
|
---|
821 | for (int ci = ri + 1; ci < area_enclosed_polygon_list.size(); ci++) {
|
---|
822 | // ci = check index pos
|
---|
823 | AreaPolygon cpoly = area_enclosed_polygon_list.get(ci);
|
---|
824 | if (rpoly.completelyContains(cpoly)) {
|
---|
825 | area_enclosed_polygon_list.remove(ci);
|
---|
826 | ri--; // to offset to the outside loop increment
|
---|
827 | break;
|
---|
828 | }
|
---|
829 | }
|
---|
830 | }
|
---|
831 |
|
---|
832 | // By this point, guaranteed that the remaining polygons are the largest
|
---|
833 | // ones
|
---|
834 | // that capture groups of items
|
---|
835 | //
|
---|
836 | // There may be sub-groupings within them, which is the reason for the
|
---|
837 | // recursive call below
|
---|
838 |
|
---|
839 | for (AreaPolygon area_polygon : area_enclosed_polygon_list) {
|
---|
840 |
|
---|
841 | Collection<Item> enclosed_items = FrameUtils.getItemsEnclosedBy(
|
---|
842 | frame, area_polygon);
|
---|
843 | List<Item> enclosed_item_list = new ArrayList<Item>(enclosed_items);
|
---|
844 |
|
---|
845 | int i = 0;
|
---|
846 | while (i < enclosed_item_list.size()) {
|
---|
847 | Item enclosed_item = enclosed_item_list.get(i);
|
---|
848 |
|
---|
849 | // Filter out enclosed-items points that are part of the
|
---|
850 | // polygon's perimeter
|
---|
851 | if (area_polygon.isPerimeterPoint(enclosed_item.getPosition())) {
|
---|
852 | enclosed_item_list.remove(i);
|
---|
853 | // Don't include items the user hasn't asked us to.
|
---|
854 | }
|
---|
855 | // Only include items the user has asked to to include.
|
---|
856 | else if (!origonal_item_list.contains(enclosed_item))
|
---|
857 | enclosed_item_list.remove(i);
|
---|
858 | else {
|
---|
859 | i++;
|
---|
860 | }
|
---|
861 | }
|
---|
862 |
|
---|
863 | // Recursively work on the identified sub-group
|
---|
864 |
|
---|
865 | XGroupItem xgroup_item = new XGroupItem(frame, enclosed_item_list,
|
---|
866 | area_polygon);
|
---|
867 |
|
---|
868 | grouped_item_list.add(xgroup_item);
|
---|
869 | }
|
---|
870 | }
|
---|
871 |
|
---|
872 | protected void castShadowIntoEmptySpace(
|
---|
873 | YOverlappingItemsTopEdge y_item_span_top_edge, int yt, int yb) {
|
---|
874 | // Assumes that all entries cast into are currently null
|
---|
875 | // => Use the more general castShadow() below, if this is not guaranteed
|
---|
876 | // to be the case
|
---|
877 |
|
---|
878 | YOverlappingItemsShadow y_span_shadow = new YOverlappingItemsShadow(
|
---|
879 | y_item_span_top_edge);
|
---|
880 |
|
---|
881 | for (int y = yt; y <= yb; y++) {
|
---|
882 |
|
---|
883 | setYOverlappingItemsSpan(y, y_span_shadow);
|
---|
884 | }
|
---|
885 | }
|
---|
886 |
|
---|
887 | protected void castShadow(YOverlappingItemsTopEdge y_item_span_top_edge,
|
---|
888 | int yt, int yb) {
|
---|
889 | // Cast shadows only in places where there are currently no shadow
|
---|
890 | // entries
|
---|
891 |
|
---|
892 | YOverlappingItemsShadow y_span_shadow = new YOverlappingItemsShadow(
|
---|
893 | y_item_span_top_edge);
|
---|
894 |
|
---|
895 | int y = yt;
|
---|
896 |
|
---|
897 | while (y <= yb) {
|
---|
898 | YOverlappingItemsSpan y_item_span = getYOverlappingItemsSpan(y);
|
---|
899 |
|
---|
900 | if (y_item_span == null) {
|
---|
901 | setYOverlappingItemsSpan(y, y_span_shadow);
|
---|
902 | y++;
|
---|
903 | } else if (y_item_span instanceof YOverlappingItemsTopEdge) {
|
---|
904 | // Our shadow has run into another top-edged zone
|
---|
905 | // => need to extend the shadow to include this zone as well
|
---|
906 | // (making this encountered top-edge obsolete)
|
---|
907 |
|
---|
908 | // Merge the newly encountered top-line in with the current one
|
---|
909 | // and change all its shadow references to our (dominant) one
|
---|
910 |
|
---|
911 | XOrderedLine dominant_x_line = y_item_span_top_edge
|
---|
912 | .getXOrderedLine();
|
---|
913 |
|
---|
914 | YOverlappingItemsTopEdge obsolete_top_edge = (YOverlappingItemsTopEdge) y_item_span;
|
---|
915 | XOrderedLine obsolete_x_line = obsolete_top_edge
|
---|
916 | .getXOrderedLine();
|
---|
917 |
|
---|
918 | for (XItem xitem : obsolete_x_line.getXItemList()) {
|
---|
919 |
|
---|
920 | dominant_x_line.orderedMergeItem(xitem);
|
---|
921 | }
|
---|
922 |
|
---|
923 | while (y_item_span != null) {
|
---|
924 |
|
---|
925 | setYOverlappingItemsSpan(y, y_span_shadow);
|
---|
926 |
|
---|
927 | y++;
|
---|
928 |
|
---|
929 | if (y <= yb) {
|
---|
930 | y_item_span = getYOverlappingItemsSpan(y);
|
---|
931 | } else {
|
---|
932 | y_item_span = null;
|
---|
933 | }
|
---|
934 | }
|
---|
935 | } else {
|
---|
936 | y++;
|
---|
937 | }
|
---|
938 | }
|
---|
939 | }
|
---|
940 |
|
---|
941 | protected int autocropToTop(XItem xitem) {
|
---|
942 | // top-edge over-spill can occur (and is acceptable) when (for example)
|
---|
943 | // the given xitem is a raw-text item
|
---|
944 | // that doesn't neatly fit in an enclosed area (i,e., is poking out the
|
---|
945 | // top of an enclosing rectangle)
|
---|
946 | // => cut down to the top of 'this' xgroupitem
|
---|
947 |
|
---|
948 | int yt = xitem.getBoundingYTop();
|
---|
949 |
|
---|
950 | yt = cropToTop(yt); // Only changes yt if in an over-spill situation
|
---|
951 |
|
---|
952 | return yt;
|
---|
953 |
|
---|
954 | }
|
---|
955 |
|
---|
956 | protected int autocropToBot(XItem xitem) {
|
---|
957 |
|
---|
958 | // similar to autocropToTop(), bottom-edge over-spill can occur (and is
|
---|
959 | // acceptable) with
|
---|
960 | // a less than precise placed raw-text item
|
---|
961 |
|
---|
962 | int yb = xitem.getBoundingYBot();
|
---|
963 |
|
---|
964 | yb = cropToBot(yb); // Only changes yb if in an over-spill situation
|
---|
965 |
|
---|
966 | return yb;
|
---|
967 | }
|
---|
968 |
|
---|
969 | protected boolean yExtentIntersection(XGroupItem xgroup_item1,
|
---|
970 | XGroupItem xgroup_item2) {
|
---|
971 | // Computation applied to y-extent of each rectangle
|
---|
972 |
|
---|
973 | // To intersect, either a point in item1 falls inside item2,
|
---|
974 | // or else item2 is completely within item1
|
---|
975 |
|
---|
976 | int yt1 = xgroup_item1.getBoundingYTop();
|
---|
977 | int yb1 = xgroup_item1.getBoundingYBot();
|
---|
978 |
|
---|
979 | int yt2 = xgroup_item2.getBoundingYTop();
|
---|
980 |
|
---|
981 | // yt1 insides item2?
|
---|
982 | if (xgroup_item2.containedInYExtent(yt1)) {
|
---|
983 | return true;
|
---|
984 | }
|
---|
985 |
|
---|
986 | // yb1 inside item2?
|
---|
987 | if (xgroup_item2.containedInYExtent(yb1)) {
|
---|
988 | return true;
|
---|
989 | }
|
---|
990 |
|
---|
991 | // item2 completely inside item1?
|
---|
992 | //
|
---|
993 | // With the previous testing, this can be determined
|
---|
994 | // by checking only one of the values is inside.
|
---|
995 | // Doesn't matter which => choose yt2
|
---|
996 |
|
---|
997 | if (xgroup_item1.containedInYExtent(yt2)) {
|
---|
998 | return true;
|
---|
999 | }
|
---|
1000 |
|
---|
1001 | // Get to here if there is no intersection
|
---|
1002 | return false;
|
---|
1003 |
|
---|
1004 | }
|
---|
1005 |
|
---|
1006 | protected ArrayList<XGroupItem> calcXGroupYExtentIntersection(
|
---|
1007 | XGroupItem xgroup_pivot_item, List<XGroupItem> xgroup_item_list) {
|
---|
1008 | ArrayList<XGroupItem> intersect_list = new ArrayList<XGroupItem>();
|
---|
1009 |
|
---|
1010 | for (XGroupItem xgroup_item : xgroup_item_list) {
|
---|
1011 |
|
---|
1012 | if (xgroup_item == xgroup_pivot_item) {
|
---|
1013 | // Don't bother working out the intersection with itself!
|
---|
1014 | continue;
|
---|
1015 | }
|
---|
1016 |
|
---|
1017 | if (yExtentIntersection(xgroup_pivot_item, xgroup_item)) {
|
---|
1018 | intersect_list.add(xgroup_item);
|
---|
1019 | }
|
---|
1020 | }
|
---|
1021 |
|
---|
1022 | return intersect_list;
|
---|
1023 | }
|
---|
1024 |
|
---|
1025 | protected ArrayList<YOverlappingItemsTopEdge> calcYSpanOverlap(XItem xitem) {
|
---|
1026 | int yt = autocropToTop(xitem);
|
---|
1027 | int yb = autocropToBot(xitem);
|
---|
1028 |
|
---|
1029 | ArrayList<YOverlappingItemsTopEdge> top_edge_list = new ArrayList<YOverlappingItemsTopEdge>();
|
---|
1030 |
|
---|
1031 | for (int y = yt; y <= yb; y++) {
|
---|
1032 |
|
---|
1033 | YOverlappingItemsSpan item_span = getYOverlappingItemsSpan(y);
|
---|
1034 |
|
---|
1035 | if (item_span != null) {
|
---|
1036 |
|
---|
1037 | if (item_span instanceof YOverlappingItemsTopEdge) {
|
---|
1038 |
|
---|
1039 | YOverlappingItemsTopEdge y_item_span_top_edge = (YOverlappingItemsTopEdge) item_span;
|
---|
1040 |
|
---|
1041 | top_edge_list.add(y_item_span_top_edge);
|
---|
1042 | }
|
---|
1043 | }
|
---|
1044 |
|
---|
1045 | }
|
---|
1046 |
|
---|
1047 | return top_edge_list;
|
---|
1048 | }
|
---|
1049 |
|
---|
1050 | protected boolean multipleYSpanOverlap(XItem xitem) {
|
---|
1051 | return calcYSpanOverlap(xitem).size() > 0;
|
---|
1052 | }
|
---|
1053 |
|
---|
1054 | public void mapInItem(XItem xitem) {
|
---|
1055 |
|
---|
1056 | int yt = autocropToTop(xitem);
|
---|
1057 | int yb = autocropToBot(xitem);
|
---|
1058 | /*
|
---|
1059 | * int yt = xitem.getBoundingYTop(); int yb = xitem.getBoundingYBot();
|
---|
1060 | *
|
---|
1061 | * // top-edge over-spill can occur (and is acceptable) when (for
|
---|
1062 | * example) the given xitem is a raw-text item // that doesn't neatly
|
---|
1063 | * fit in an enclosed area (i,e., is poking out the top of an enclosing
|
---|
1064 | * rectangle) yt = cropToTop(yt); // Only changes yt if in an over-spill
|
---|
1065 | * situation
|
---|
1066 | *
|
---|
1067 | * // similarly, bottom-edge over-spill can occur (and is acceptable)
|
---|
1068 | * with a less than precise placed raw-text item yb = cropToBot(yb); //
|
---|
1069 | * Only changes yb if in an over-spill situation
|
---|
1070 | */
|
---|
1071 |
|
---|
1072 | // Merge into 'items_span' checking for overlaps (based on xitem's
|
---|
1073 | // y-span)
|
---|
1074 |
|
---|
1075 | boolean merged_item = false;
|
---|
1076 | for (int y = yt; y <= yb; y++) {
|
---|
1077 |
|
---|
1078 | YOverlappingItemsSpan item_span = getYOverlappingItemsSpan(y);
|
---|
1079 |
|
---|
1080 | if (item_span != null) {
|
---|
1081 |
|
---|
1082 | if (item_span instanceof YOverlappingItemsTopEdge) {
|
---|
1083 |
|
---|
1084 | // Hit a top edge of an existing item
|
---|
1085 |
|
---|
1086 | // Need to:
|
---|
1087 | // 1. *Required* Insert new item into current x-ordered line
|
---|
1088 | // 2. *Conditionally* Move entire x-ordered line up to
|
---|
1089 | // higher 'y' top edge
|
---|
1090 | // 3. *Required* Cast shadow for new item
|
---|
1091 |
|
---|
1092 | // Note for Step 2:
|
---|
1093 | // i) No need to do the move if y == yt
|
---|
1094 | // (the case when the new top edge is exactly the same
|
---|
1095 | // height as the existing one)
|
---|
1096 | // ii) If moving top-edge (the case when y > yt) then no
|
---|
1097 | // need to recalculate the top edge for existing shadows, as
|
---|
1098 | // this 'connection' is stored as a reference
|
---|
1099 |
|
---|
1100 | // Step 1: insert into existing top-edge
|
---|
1101 |
|
---|
1102 | YOverlappingItemsTopEdge y_item_span_top_edge = (YOverlappingItemsTopEdge) item_span;
|
---|
1103 | XOrderedLine xitem_span = y_item_span_top_edge
|
---|
1104 | .getXOrderedLine();
|
---|
1105 |
|
---|
1106 | xitem_span
|
---|
1107 | .orderedMergeItem(xitem.getBoundingXLeft(), xitem);
|
---|
1108 |
|
---|
1109 | // Step 2: if our new top-edge is higher than the existing
|
---|
1110 | // one, then need to move existing top-edge
|
---|
1111 | if (y > yt) {
|
---|
1112 |
|
---|
1113 | // Move to new position
|
---|
1114 | setYOverlappingItemsSpan(yt, y_item_span_top_edge);
|
---|
1115 |
|
---|
1116 | // Old position needs to become a shadow reference
|
---|
1117 | YOverlappingItemsShadow y_span_shadow = new YOverlappingItemsShadow(
|
---|
1118 | y_item_span_top_edge);
|
---|
1119 | setYOverlappingItemsSpan(y, y_span_shadow);
|
---|
1120 | }
|
---|
1121 |
|
---|
1122 | // Step 3: Cast shadow
|
---|
1123 | castShadow(y_item_span_top_edge, yt + 1, yb);
|
---|
1124 |
|
---|
1125 | } else {
|
---|
1126 | // Top edge to our new item has hit a shadow entry (straight
|
---|
1127 | // off)
|
---|
1128 | // => Look up what the shadow references, and then add in to
|
---|
1129 | // that
|
---|
1130 |
|
---|
1131 | // Effectively after the shadow reference lookup this is the
|
---|
1132 | // same
|
---|
1133 | // as the above, without the need to worry about Step 2 (as
|
---|
1134 | // no move is needed)
|
---|
1135 |
|
---|
1136 | YOverlappingItemsShadow y_item_span_shadow = (YOverlappingItemsShadow) item_span;
|
---|
1137 | YOverlappingItemsTopEdge y_item_span_top_edge = y_item_span_shadow
|
---|
1138 | .getTopEdge();
|
---|
1139 |
|
---|
1140 | XOrderedLine xitem_span = y_item_span_top_edge
|
---|
1141 | .getXOrderedLine();
|
---|
1142 |
|
---|
1143 | // merge with this item list, preserving x ordering
|
---|
1144 | xitem_span
|
---|
1145 | .orderedMergeItem(xitem.getBoundingXLeft(), xitem);
|
---|
1146 |
|
---|
1147 | // Now Cast shadow
|
---|
1148 | castShadow(y_item_span_top_edge, yt + 1, yb);
|
---|
1149 | }
|
---|
1150 |
|
---|
1151 | merged_item = true;
|
---|
1152 | break;
|
---|
1153 |
|
---|
1154 | }
|
---|
1155 |
|
---|
1156 | }
|
---|
1157 |
|
---|
1158 | // Having checked all the y-location's ('yt' to 'yb') of this x-item, if
|
---|
1159 | // all y-span entries were found to be null
|
---|
1160 | // => 'merged_item' is still false
|
---|
1161 |
|
---|
1162 | if (!merged_item) {
|
---|
1163 | // xitem didn't intersect with any existing y-spans
|
---|
1164 | // => simple case for add (i.e. all entries affected by map are
|
---|
1165 | // currently null)
|
---|
1166 |
|
---|
1167 | // Start up a new x-ordered-line (consisting of only 'xitem'), add
|
---|
1168 | // in top edge and cast shadow
|
---|
1169 |
|
---|
1170 | XOrderedLine xitem_line = new XOrderedLine(xitem);
|
---|
1171 |
|
---|
1172 | YOverlappingItemsTopEdge y_item_span_top_edge = new YOverlappingItemsTopEdge(
|
---|
1173 | xitem_line);
|
---|
1174 |
|
---|
1175 | setYOverlappingItemsSpan(yt, y_item_span_top_edge);
|
---|
1176 |
|
---|
1177 | castShadowIntoEmptySpace(y_item_span_top_edge, yt + 1, yb);
|
---|
1178 | }
|
---|
1179 |
|
---|
1180 | }
|
---|
1181 |
|
---|
1182 | private ArrayList<DimensionExtent> calcXGroupXGaps(
|
---|
1183 | XGroupItem xgroup_pivot_item, List<XGroupItem> xgroup_item_list) {
|
---|
1184 | // 'xgroup_pivot_item' current not used!!
|
---|
1185 |
|
---|
1186 | int enclosing_xl = getBoundingXLeft();
|
---|
1187 | int enclosing_xr = getBoundingXRight();
|
---|
1188 |
|
---|
1189 | int enclosing_x_dim = enclosing_xr - enclosing_xl + 1;
|
---|
1190 | boolean is_x_shadow[] = new boolean[enclosing_x_dim]; // defaults all
|
---|
1191 | // values to
|
---|
1192 | // false
|
---|
1193 |
|
---|
1194 | ArrayList<DimensionExtent> x_gap_list = new ArrayList<DimensionExtent>();
|
---|
1195 |
|
---|
1196 | for (XGroupItem xgroup_item : xgroup_item_list) {
|
---|
1197 | int xl = xgroup_item.getBoundingXLeft();
|
---|
1198 | int xr = xgroup_item.getBoundingXRight();
|
---|
1199 |
|
---|
1200 | for (int x = xl; x <= xr; x++) {
|
---|
1201 | int x_offset = x - enclosing_xl;
|
---|
1202 | is_x_shadow[x_offset] = true;
|
---|
1203 | }
|
---|
1204 | }
|
---|
1205 |
|
---|
1206 | // New find where the contiguous runs of 'false' are, as they point to
|
---|
1207 | // the gaps
|
---|
1208 |
|
---|
1209 | int x = 1;
|
---|
1210 | int start_x_gap = enclosing_xl;
|
---|
1211 | boolean in_gap = true;
|
---|
1212 |
|
---|
1213 | while (x < is_x_shadow.length) {
|
---|
1214 | boolean prev_is_shadow = is_x_shadow[x - 1];
|
---|
1215 | boolean this_is_shadow = is_x_shadow[x];
|
---|
1216 |
|
---|
1217 | if (prev_is_shadow && !this_is_shadow) {
|
---|
1218 | // reached end of shadow, record start of a gap
|
---|
1219 | start_x_gap = enclosing_xl + x;
|
---|
1220 | in_gap = true;
|
---|
1221 | } else if (!prev_is_shadow && this_is_shadow) {
|
---|
1222 | // reached the end of a gap, record it
|
---|
1223 | int end_x_gap = enclosing_xl + x - 1;
|
---|
1224 | DimensionExtent de = new DimensionExtent(start_x_gap, end_x_gap);
|
---|
1225 | x_gap_list.add(de);
|
---|
1226 | in_gap = false;
|
---|
1227 | }
|
---|
1228 | x++;
|
---|
1229 | }
|
---|
1230 |
|
---|
1231 | if (in_gap) {
|
---|
1232 | int end_x_gap = enclosing_xl + x - 1;
|
---|
1233 | DimensionExtent de = new DimensionExtent(start_x_gap, end_x_gap);
|
---|
1234 | x_gap_list.add(de);
|
---|
1235 | }
|
---|
1236 |
|
---|
1237 | return x_gap_list;
|
---|
1238 | }
|
---|
1239 |
|
---|
1240 | protected void boxMultipleXRawItemsInGaps(XGroupItem xgroup_pivot_item,
|
---|
1241 | ArrayList<DimensionExtent> x_text_gap_list) {
|
---|
1242 | ArrayList<YOverlappingItemsTopEdge> y_span_overlap = calcYSpanOverlap(xgroup_pivot_item);
|
---|
1243 |
|
---|
1244 | // work out where continuous runs of raw-text items intersect with the
|
---|
1245 | // gaps occurring between boxed items
|
---|
1246 |
|
---|
1247 | // foreach x-gap,
|
---|
1248 | // foreach y-span's list of x-sorted objects,
|
---|
1249 | // => see how many raw-text items fit into it
|
---|
1250 |
|
---|
1251 | ArrayList<ArrayList<XRawItem>> implicit_box_list = new ArrayList<ArrayList<XRawItem>>();
|
---|
1252 |
|
---|
1253 | for (DimensionExtent de_gap : x_text_gap_list) {
|
---|
1254 |
|
---|
1255 | int deg_xl = de_gap.min;
|
---|
1256 | int deg_xr = de_gap.max;
|
---|
1257 |
|
---|
1258 | ArrayList<XRawItem> grouped_raw_text_list = new ArrayList<XRawItem>();
|
---|
1259 |
|
---|
1260 | int y_span_line_count = 0;
|
---|
1261 |
|
---|
1262 | for (YOverlappingItemsTopEdge y_top_edge : y_span_overlap) {
|
---|
1263 |
|
---|
1264 | List<XItem> x_item_list = y_top_edge.x_items.getXItemList();
|
---|
1265 |
|
---|
1266 | boolean used_x_raw_text = false;
|
---|
1267 |
|
---|
1268 | for (XItem x_item : x_item_list) {
|
---|
1269 |
|
---|
1270 | if (x_item instanceof XRawItem) {
|
---|
1271 | XRawItem x_raw_item = (XRawItem) x_item;
|
---|
1272 |
|
---|
1273 | int xri_xl = x_raw_item.getBoundingXLeft();
|
---|
1274 | int xri_xr = x_raw_item.getBoundingXRight();
|
---|
1275 |
|
---|
1276 | if ((xri_xl >= deg_xl) && (xri_xr <= deg_xr)) {
|
---|
1277 | grouped_raw_text_list.add(x_raw_item);
|
---|
1278 | used_x_raw_text = true;
|
---|
1279 | }
|
---|
1280 | }
|
---|
1281 | }
|
---|
1282 |
|
---|
1283 | if (used_x_raw_text) {
|
---|
1284 | y_span_line_count++;
|
---|
1285 | }
|
---|
1286 | }
|
---|
1287 |
|
---|
1288 | // Only interested in implicitly boxing if the formed group is used
|
---|
1289 | // over 2 or more y-span lines
|
---|
1290 | if (y_span_line_count >= 2) {
|
---|
1291 | implicit_box_list.add(grouped_raw_text_list);
|
---|
1292 | }
|
---|
1293 | }
|
---|
1294 |
|
---|
1295 | // System.err.println("*** Number of implicit groups found: " +
|
---|
1296 | // implicit_box_list.size());
|
---|
1297 |
|
---|
1298 | for (ArrayList<XRawItem> implicit_x_item_list : implicit_box_list) {
|
---|
1299 |
|
---|
1300 | for (YOverlappingItemsTopEdge y_top_edge : y_span_overlap) {
|
---|
1301 |
|
---|
1302 | List<XItem> yspan_x_item_list = y_top_edge.x_items
|
---|
1303 | .getXItemList();
|
---|
1304 |
|
---|
1305 | // Remove all the XRawItems from the current y-span
|
---|
1306 | yspan_x_item_list.removeAll(implicit_x_item_list);
|
---|
1307 |
|
---|
1308 | }
|
---|
1309 |
|
---|
1310 | // Create a list of original Text items
|
---|
1311 | // (OK that they are not y-ordered)
|
---|
1312 | ArrayList<Item> implicit_item_list = new ArrayList<Item>();
|
---|
1313 | for (XRawItem x_raw_item : implicit_x_item_list) {
|
---|
1314 | Item item = (Text) x_raw_item.item;
|
---|
1315 | implicit_item_list.add(item);
|
---|
1316 | }
|
---|
1317 |
|
---|
1318 | // Now insert them as their own boxed up items
|
---|
1319 | XGroupItem implicit_xgroup = new XGroupItem(frame,
|
---|
1320 | implicit_item_list);
|
---|
1321 | this.mapInItem(implicit_xgroup);
|
---|
1322 | }
|
---|
1323 |
|
---|
1324 | }
|
---|
1325 |
|
---|
1326 | protected void implicitBoxing(XGroupItem xgroup_pivot_item,
|
---|
1327 | List<XGroupItem> xgroup_item_list) {
|
---|
1328 |
|
---|
1329 | // Implicit boxing is needed if there are two or more raw-text items
|
---|
1330 | // that overlap in y-span-map of the given xgroup_item
|
---|
1331 |
|
---|
1332 | // First establish if there is any kind of multiple overlap
|
---|
1333 | if (multipleYSpanOverlap(xgroup_pivot_item)) {
|
---|
1334 |
|
---|
1335 | // Overlap could be with other boxed items, so need to investigate
|
---|
1336 | // further
|
---|
1337 |
|
---|
1338 | // Do this by working out if there are any raw-text items lurking
|
---|
1339 | // between the pivot xgroup_item
|
---|
1340 | // and the list of other xgroup_items
|
---|
1341 |
|
---|
1342 | // Step 1: Get list intersection (y-dim) of this group item, with
|
---|
1343 | // any
|
---|
1344 | // other group items in xgroup_item_list
|
---|
1345 | ArrayList<XGroupItem> y_overlapping_xgroup_item_list = calcXGroupYExtentIntersection(
|
---|
1346 | xgroup_pivot_item, xgroup_item_list);
|
---|
1347 |
|
---|
1348 | // Step 2: Work out where the gaps are between the y-overlapping
|
---|
1349 | // boxes in the x-dimension
|
---|
1350 | ArrayList<DimensionExtent> x_text_gap_list = calcXGroupXGaps(
|
---|
1351 | xgroup_pivot_item, y_overlapping_xgroup_item_list);
|
---|
1352 |
|
---|
1353 | // Step 3: Remove any sequences of raw-text items that fall in the
|
---|
1354 | // gaps from YSpan, box them up, and reinsert
|
---|
1355 | boxMultipleXRawItemsInGaps(xgroup_pivot_item, x_text_gap_list);
|
---|
1356 |
|
---|
1357 | }
|
---|
1358 | }
|
---|
1359 |
|
---|
1360 | public void mapInXGroupItemsRecursive(List<XGroupItem> xgroup_item_list) {
|
---|
1361 |
|
---|
1362 | // Map in all the items in the given list:
|
---|
1363 | for (XGroupItem xgroup_item : xgroup_item_list) {
|
---|
1364 | if (!xgroup_item.isOriginalOutOfFlowItem()) {
|
---|
1365 |
|
---|
1366 | // See if any implicit boxing of raw-text items is needed
|
---|
1367 | if (doImplicitBoxing) {
|
---|
1368 | implicitBoxing(xgroup_item, xgroup_item_list);
|
---|
1369 | }
|
---|
1370 | mapInItem(xgroup_item); // Map in this x-group-item
|
---|
1371 | }
|
---|
1372 | }
|
---|
1373 |
|
---|
1374 | // Now recursively work through each item's nested x-groups
|
---|
1375 | for (XGroupItem xgroup_item : xgroup_item_list) {
|
---|
1376 |
|
---|
1377 | List<XGroupItem> nested_xgroup_item_list = xgroup_item
|
---|
1378 | .getGroupedItemList();
|
---|
1379 |
|
---|
1380 | if (nested_xgroup_item_list.size() > 0) {
|
---|
1381 | xgroup_item.mapInXGroupItemsRecursive(nested_xgroup_item_list);
|
---|
1382 | }
|
---|
1383 | }
|
---|
1384 | }
|
---|
1385 |
|
---|
1386 | public List<Item[]> getYXOverlappingItemListLines() {
|
---|
1387 | final List<XRawItem> yxOverlappingXRawItemList = getYXOverlappingXRawItemList(true);
|
---|
1388 | final List<Item[]> lines = new ArrayList<Item[]>();
|
---|
1389 | final List<XRawItem> currentLineXItem = new ArrayList<XRawItem>();
|
---|
1390 | for (final XRawItem xitem : yxOverlappingXRawItemList) {
|
---|
1391 | if (xitem.getItem() == GROUPSEP_START)
|
---|
1392 | continue;
|
---|
1393 | else if (xitem.getItem() == GROUPSEP_END) {
|
---|
1394 | if (currentLineXItem.isEmpty())
|
---|
1395 | continue;
|
---|
1396 | else {
|
---|
1397 | final List<Item> ln = new ArrayList<Item>();
|
---|
1398 | for (final XRawItem xrawitem : currentLineXItem)
|
---|
1399 | ln.add(xrawitem.getItem());
|
---|
1400 | lines.add(ln.toArray(new Item[] {}));
|
---|
1401 | currentLineXItem.clear();
|
---|
1402 | }
|
---|
1403 | } else {
|
---|
1404 | if (currentLineXItem.isEmpty())
|
---|
1405 | currentLineXItem.add(xitem);
|
---|
1406 | else {
|
---|
1407 | final YOverlappingItemsSpan[] itemSpanArray = xitem
|
---|
1408 | .getGroup().yitems_span_array;
|
---|
1409 | final int index = xitem.getBoundingYTop()
|
---|
1410 | - xitem.getGroup().getBoundingYTop();
|
---|
1411 | final YOverlappingItemsSpan itemSpan = itemSpanArray[index];
|
---|
1412 | final List<XItem> xOrderedList = itemSpan instanceof YOverlappingItemsTopEdge ? ((YOverlappingItemsTopEdge) itemSpan)
|
---|
1413 | .getXOrderedLine().getXItemList()
|
---|
1414 | : ((YOverlappingItemsShadow) itemSpan).getTopEdge()
|
---|
1415 | .getXOrderedLine().getXItemList();
|
---|
1416 | if (!xOrderedList.contains(currentLineXItem.get(0))) {
|
---|
1417 | if (!currentLineXItem.isEmpty()) {
|
---|
1418 | final List<Item> ln = new ArrayList<Item>();
|
---|
1419 | for (final XRawItem xrawitem : currentLineXItem)
|
---|
1420 | ln.add(xrawitem.getItem());
|
---|
1421 | lines.add(ln.toArray(new Item[] {}));
|
---|
1422 | currentLineXItem.clear();
|
---|
1423 | }
|
---|
1424 | currentLineXItem.add(xitem);
|
---|
1425 | } else {
|
---|
1426 | currentLineXItem.add(xitem);
|
---|
1427 | }
|
---|
1428 | }
|
---|
1429 | }
|
---|
1430 | }
|
---|
1431 | if (!currentLineXItem.isEmpty()) {
|
---|
1432 | final List<Item> ln = new ArrayList<Item>();
|
---|
1433 | for (final XRawItem xrawitem : currentLineXItem)
|
---|
1434 | ln.add(xrawitem.getItem());
|
---|
1435 | lines.add(ln.toArray(new Item[] {}));
|
---|
1436 | currentLineXItem.clear();
|
---|
1437 | }
|
---|
1438 | return lines;
|
---|
1439 | }
|
---|
1440 |
|
---|
1441 | public ArrayList<Item> getYXOverlappingItemList(boolean separateGroups) {
|
---|
1442 | final List<XRawItem> yxOverlappingXItemList = getYXOverlappingXRawItemList(separateGroups);
|
---|
1443 | final ArrayList<Item> yxOverlappingItemList = new ArrayList<Item>();
|
---|
1444 | for (final XRawItem xitem : yxOverlappingXItemList)
|
---|
1445 | yxOverlappingItemList.add(xitem.getItem());
|
---|
1446 | return yxOverlappingItemList;
|
---|
1447 | }
|
---|
1448 |
|
---|
1449 | public List<XRawItem> getYXOverlappingXRawItemList(
|
---|
1450 | final boolean separateGroups) {
|
---|
1451 | ArrayList<XRawItem> overlapping_y_ordered_items = new ArrayList<XRawItem>();
|
---|
1452 |
|
---|
1453 | for (int y = 0; y < y_span_height; y++) {
|
---|
1454 |
|
---|
1455 | YOverlappingItemsSpan item_span = yitems_span_array[y];
|
---|
1456 |
|
---|
1457 | if (item_span != null) {
|
---|
1458 |
|
---|
1459 | if (item_span instanceof YOverlappingItemsTopEdge) {
|
---|
1460 |
|
---|
1461 | YOverlappingItemsTopEdge item_span_top_edge = (YOverlappingItemsTopEdge) item_span;
|
---|
1462 | XOrderedLine xitem_line = item_span_top_edge
|
---|
1463 | .getXOrderedLine();
|
---|
1464 |
|
---|
1465 | for (XItem xspan : xitem_line.getXItemList()) {
|
---|
1466 | if (xspan instanceof XRawItem) {
|
---|
1467 | XRawItem xitem_span = (XRawItem) xspan;
|
---|
1468 | // Item item = xitem_span.getItem();
|
---|
1469 | //
|
---|
1470 | // overlapping_y_ordered_items.add(item);
|
---|
1471 | overlapping_y_ordered_items.add(xitem_span);
|
---|
1472 | } else {
|
---|
1473 | // Must be an XGroupItem => recursive call on xspan
|
---|
1474 | // item
|
---|
1475 |
|
---|
1476 | XGroupItem nested_group_item = (XGroupItem) xspan;
|
---|
1477 | List<XRawItem> nested_overlapping_items = nested_group_item
|
---|
1478 | .getYXOverlappingXRawItemList(separateGroups);
|
---|
1479 | if (separateGroups) {
|
---|
1480 | overlapping_y_ordered_items.add(new XRawItem(
|
---|
1481 | GROUPSEP_START, this));
|
---|
1482 | }
|
---|
1483 | overlapping_y_ordered_items
|
---|
1484 | .addAll(nested_overlapping_items);
|
---|
1485 | if (separateGroups) {
|
---|
1486 | overlapping_y_ordered_items.add(new XRawItem(
|
---|
1487 | GROUPSEP_END, this));
|
---|
1488 | }
|
---|
1489 | }
|
---|
1490 | }
|
---|
1491 | }
|
---|
1492 | }
|
---|
1493 | }
|
---|
1494 |
|
---|
1495 | return overlapping_y_ordered_items;
|
---|
1496 | }
|
---|
1497 |
|
---|
1498 | public ArrayList<Item> getYXOverlappingItemList() {
|
---|
1499 | return getYXOverlappingItemList(false);
|
---|
1500 | }
|
---|
1501 |
|
---|
1502 | public String toString() {
|
---|
1503 | return "XGroupItem";
|
---|
1504 | }
|
---|
1505 | }
|
---|