source: trunk/src/org/expeditee/io/flowlayout/XGroupItem.java@ 1415

Last change on this file since 1415 was 1415, checked in by bln4, 5 years ago

Renamed Frame.getItems() to Frame.getSortedItems() to better represent its functionality.

-> org.apollo.ApolloGestureActions
-> org.apollo.ApolloSystem
-> org.expeditee.actions.Actions
-> org.expeditee.actions.Debug
-> org.expeditee.actions.ExploratorySearchActions
-> org.expeditee.actions.JfxBrowserActions
-> org.expeditee.actions.Misc
-> org.expeditee.actions.Navigation
-> org.expeditee.actions.ScriptBase
-> org.expeditee.actions.Simple
-> org.expeditee.agents.ComputeTree
-> org.expeditee.agents.CopyTree
-> org.expeditee.agents.DisplayComet
-> org.expeditee.agents.DisplayTree
-> org.expeditee.agents.DisplayTreeLeaves
-> org.expeditee.agents.GraphFramesetLinks
-> org.expeditee.agents.TreeProcessor
-> org.expeditee.gio.gesture.StandardGestureActions
-> org.expeditee.gui.DisplayController
-> org.expeditee.gui.FrameCreator
-> org.expeditee.gui.FrameIO
-> org.expeditee.io.DefaultTreeWriter
-> org.expeditee.io.JavaWriter
-> org.expeditee.io.PDF2Writer
-> org.expeditee.io.TXTWriter
-> org.expeditee.io.WebParser
-> org.expeditee.io.flowlayout.XGroupItem
-> org.expeditee.items.Dot
-> org.expeditee.items.Item
-> org.expeditee.items.ItemUtils
-> org.expeditee.network.FrameShare
-> org.expeditee.stats.TreeStats


Created ItemsList class to wrap ArrayList<Item>. Frames now use this new class to store its body list (used for display) as well as its primaryBody and surrogateBody.

-> org.expeditee.agents.Format
-> org.expeditee.agents.HFormat
-> org.expeditee.gio.gesture.StandardGestureActions
-> org.expeditee.gui.Frame
-> org.expeditee.gui.FrameUtils


Refactorted Frame.setResort(bool) to Frame.invalidateSorted() to better function how it is intended to with a more accurate name.

-> org.expeditee.agents.Sort


When writing out .exp files and getting attributes to respond to LEFT + RIGHT click, boolean items are by default true. This has always been the case. An ammendment to this is that defaults can now be established.
Also added 'EnterClick' functionality. If cursored over a item with this property and you press enter, it acts as if you have clicked on it instead.

-> org.expeditee.assets.resources-public.framesets.authentication.1.exp to 6.exp
-> org.expeditee.gio.gesture.StandardGestureActions
-> org.expeditee.gio.input.KBMInputEvent
-> org.expeditee.gio.javafx.JavaFXConversions
-> org.expeditee.gio.swing.SwingConversions
-> org.expeditee.gui.AttributeUtils
-> org.expeditee.io.Conversion
-> org.expeditee.io.DefaultFrameWriter
-> org.expeditee.items.Item


Fixed a bug caused by calling Math.abs on Integer.MIN_VALUE returning unexpected result. Due to zero being a thing, you cannot represent Math.abs(Integer.MIN_VALUE) in a Integer object. The solution is to use Integer.MIN_VALUE + 1 instead of Integer.MIN_VALUE.

-> org.expeditee.core.bounds.CombinationBounds
-> org.expeditee.io.flowlayout.DimensionExtent


Recoded the contains function in EllipticalBounds so that intersection tests containing circles work correctly.

-> org.expeditee.core.bounds.EllipticalBounds


Added toString() to PolygonBounds to allow for useful printing during debugging.

-> org.expeditee.core.bounds.PolygonBounds

Implemented Surrogate Mode!

-> org.expeditee.encryption.io.EncryptedExpReader
-> org.expeditee.encryption.io.EncryptedExpWriter
-> org.expeditee.encryption.items.surrogates.EncryptionDetail
-> org.expeditee.encryption.items.surrogates.Label
-> org.expeditee.gui.FrameUtils
-> org.expeditee.gui.ItemsList
-> org.expeditee.items.Item
-> org.expeditee.items.Text


???? Use Integer.MAX_VALUE cast to a float instead of Float.MAX_VALUE. This fixed some bug which I cannot remember.

-> org.expeditee.gio.TextLayoutManager
-> org.expeditee.gio.swing.SwingTextLayoutManager


Improved solution for dealing with the F10 key taking focus away from Expeditee due to it being a assessibility key.

-> org.expeditee.gio.swing.SwingInputManager


Renamed variable visibleItems in FrameGraphics.paintFrame to itemsToPaintCanditates to better represent functional intent.

-> org.expeditee.gui.FrameGraphics


Improved checking for if personal resources exist before recreating them

-> org.expeditee.gui.FrameIO


Repeated messages to message bay now have a visual feedback instead of just a beep. This visual feedback is in the form of a count of the amount of times it has repeated.

-> org.expeditee.gui.MessageBay


Updated comment on the Vector class to explain what vectors are.

-> org.expeditee.gui.Vector


Added constants to represent all of the property keys in DefaultFrameReader and DefaultFrameWriter.

-> org.expeditee.io.DefaultFrameReader
-> org.expeditee.io.DefaultFrameWriter


Updated the KeyList setting to be more heirarcial with how users will store their Secrets.

-> org.expeditee.settings.identity.secrets.KeyList

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