PageRenderTime 64ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/projects/netbeans-7.3/form/src/org/netbeans/modules/form/layoutdesign/LayoutOperations.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 1124 lines | 909 code | 78 blank | 137 comment | 444 complexity | a28e4b92b6e2ac1316df9cd0bdce6bea MD5 | raw file
  1. /*
  2. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3. *
  4. * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
  5. *
  6. * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
  7. * Other names may be trademarks of their respective owners.
  8. *
  9. * The contents of this file are subject to the terms of either the GNU
  10. * General Public License Version 2 only ("GPL") or the Common
  11. * Development and Distribution License("CDDL") (collectively, the
  12. * "License"). You may not use this file except in compliance with the
  13. * License. You can obtain a copy of the License at
  14. * http://www.netbeans.org/cddl-gplv2.html
  15. * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
  16. * specific language governing permissions and limitations under the
  17. * License. When distributing the software, include this License Header
  18. * Notice in each file and include the License file at
  19. * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
  20. * particular file as subject to the "Classpath" exception as provided
  21. * by Oracle in the GPL Version 2 section of the License file that
  22. * accompanied this code. If applicable, add the following below the
  23. * License Header, with the fields enclosed by brackets [] replaced by
  24. * your own identifying information:
  25. * "Portions Copyrighted [year] [name of copyright owner]"
  26. *
  27. * Contributor(s):
  28. *
  29. * The Original Software is NetBeans. The Initial Developer of the Original
  30. * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
  31. * Microsystems, Inc. All Rights Reserved.
  32. *
  33. * If you wish your version of this file to be governed by only the CDDL
  34. * or only the GPL Version 2, indicate your decision by adding
  35. * "[Contributor] elects to include this software in this distribution
  36. * under the [CDDL or GPL Version 2] license." If you do not indicate a
  37. * single choice of license, a recipient has the option to distribute
  38. * your version of this file under either the CDDL, the GPL Version 2 or
  39. * to extend the choice of license to its licensees as provided above.
  40. * However, if you add GPL Version 2 code and therefore, elected the GPL
  41. * Version 2 license, then the option applies only if the new code is
  42. * made subject to such option by the copyright holder.
  43. */
  44. package org.netbeans.modules.form.layoutdesign;
  45. import java.awt.Dimension;
  46. import java.util.*;
  47. /**
  48. * This class serves as a library of various useful and well-defined operations
  49. * on the layout model.
  50. *
  51. * @author Tomas Pavek
  52. */
  53. class LayoutOperations implements LayoutConstants {
  54. private LayoutModel layoutModel;
  55. private VisualMapper visualMapper;
  56. private static final boolean PREFER_ZERO_GAPS = true;
  57. private static final boolean SYMETRIC_ZERO_GAPS = true;
  58. LayoutOperations(LayoutModel model, VisualMapper mapper) {
  59. layoutModel = model;
  60. visualMapper = mapper;
  61. }
  62. LayoutModel getModel() {
  63. return layoutModel;
  64. }
  65. VisualMapper getMapper() {
  66. return visualMapper;
  67. }
  68. // -----
  69. /**
  70. * Extracts surroundings of given interval (placed in a sequential group).
  71. * Extracted intervals are removed and go to the 'restLeading' and
  72. * 'restTrailing' lists. Does not extract/remove the interval itself.
  73. */
  74. int extract(LayoutInterval interval, int alignment, boolean closed,
  75. List<List> restLeading, List<List> restTrailing) {
  76. return extract(interval, interval, alignment, closed, restLeading, restTrailing);
  77. }
  78. int extract(LayoutInterval leading, LayoutInterval trailing, int alignment, boolean closed,
  79. List<List> restLeading, List<List> restTrailing)
  80. {
  81. LayoutInterval seq = leading.getParent();
  82. assert seq.isSequential();
  83. int leadingIndex = seq.indexOf(leading);
  84. int trailingIndex = seq.indexOf(trailing);
  85. int count = seq.getSubIntervalCount();
  86. int extractCount;
  87. if (closed) {
  88. extractCount = trailingIndex - leadingIndex + 1;
  89. } else if (alignment != LEADING && alignment != TRAILING) {
  90. extractCount = 1;
  91. }
  92. else {
  93. extractCount = alignment == LEADING ? count - leadingIndex : leadingIndex + 1;
  94. }
  95. if (extractCount < seq.getSubIntervalCount()) {
  96. List<Object/*Integer or LayoutInterval*/> toRemainL = null;
  97. List<Object/*Integer or LayoutInterval*/> toRemainT = null;
  98. int startIndex = alignment == LEADING ? leadingIndex : leadingIndex - extractCount + 1;
  99. int endIndex = alignment == LEADING ? trailingIndex + extractCount - 1 : trailingIndex;
  100. Iterator it = seq.getSubIntervals();
  101. for (int idx=0; it.hasNext(); idx++) {
  102. LayoutInterval li = (LayoutInterval) it.next();
  103. if (idx < startIndex) {
  104. if (toRemainL == null) {
  105. toRemainL = new LinkedList<Object>();
  106. toRemainL.add(new Integer(LayoutInterval.getEffectiveAlignment(li)));
  107. }
  108. toRemainL.add(li);
  109. }
  110. else if (idx > endIndex) {
  111. if (toRemainT == null) {
  112. toRemainT = new LinkedList<Object>();
  113. toRemainT.add(new Integer(LayoutInterval.getEffectiveAlignment(li)));
  114. }
  115. toRemainT.add(li);
  116. }
  117. }
  118. if (toRemainL != null) {
  119. it = toRemainL.iterator();
  120. it.next();
  121. do {
  122. layoutModel.removeInterval((LayoutInterval)it.next());
  123. }
  124. while (it.hasNext());
  125. restLeading.add(toRemainL);
  126. }
  127. if (toRemainT != null) {
  128. it = toRemainT.iterator();
  129. it.next();
  130. do {
  131. layoutModel.removeInterval((LayoutInterval)it.next());
  132. }
  133. while (it.hasNext());
  134. restTrailing.add(toRemainT);
  135. }
  136. }
  137. return extractCount;
  138. }
  139. /**
  140. * Adds parallel content of a group specified in List to given sequence.
  141. * Used to create a remainder parallel group to a group of aligned intervals.
  142. * @param list the content of the group, output from 'extract' method
  143. * @param seq a sequential group where to add to
  144. * @param index the index in the sequence where to add
  145. * @param dimension
  146. * @param position the position of the remainder group relative to the main
  147. * group (LEADING or TRAILING)
  148. // * @param mainAlignment effective alignment of the main group (LEADING or
  149. // * TRAILING or something else meaning not aligned)
  150. * @return parallel group if it has been created, or null
  151. */
  152. LayoutInterval addGroupContent(List<List> list, LayoutInterval seq,
  153. int index, int dimension, int position/*, int mainAlignment*/)
  154. {
  155. assert seq.isSequential() && (position == LEADING || position == TRAILING);
  156. boolean resizingFillGap = false;
  157. LayoutInterval commonGap = null;
  158. boolean onlyGaps = true;
  159. // Remove sequences just with one gap
  160. for (int i=list.size()-1; i >= 0; i--) {
  161. List subList = list.get(i);
  162. assert subList.size() >= 2;
  163. if (subList.size() == 2) { // there is just one interval
  164. LayoutInterval li = (LayoutInterval) subList.get(1);
  165. if (li.isEmptySpace()) {
  166. if (commonGap == null || li.getPreferredSize() > commonGap.getPreferredSize())
  167. commonGap = li;
  168. if (LayoutInterval.canResize(li))
  169. resizingFillGap = true;
  170. list.remove(i);
  171. }
  172. else onlyGaps = false;
  173. }
  174. else onlyGaps = false;
  175. }
  176. if (onlyGaps) { // just one gap
  177. if (resizingFillGap && !LayoutInterval.canResize(commonGap))
  178. layoutModel.setIntervalSize(commonGap, NOT_EXPLICITLY_DEFINED,
  179. commonGap.getPreferredSize(),
  180. Short.MAX_VALUE);
  181. insertGapIntoSequence(commonGap, seq, index, dimension);
  182. return null;
  183. }
  184. if (list.size() == 1) { // just one sequence
  185. List subList = list.get(0);
  186. for (int n=subList.size(),i=n-1; i > 0; i--) { // skip alignment at 0
  187. LayoutInterval li = (LayoutInterval) subList.get(i);
  188. if (resizingFillGap && li.isEmptySpace() && !LayoutInterval.canResize(li)
  189. && ((i == 1 && position == TRAILING) || (i == n-1 && position == LEADING)))
  190. { // make the end gap resizing
  191. layoutModel.setIntervalSize(
  192. li, NOT_EXPLICITLY_DEFINED, li.getPreferredSize(), Short.MAX_VALUE);
  193. }
  194. if (li.isEmptySpace()
  195. && ((i == 1 && position == LEADING) || (i == n-1 && position == TRAILING))) {
  196. insertGapIntoSequence(li, seq, index, dimension);
  197. } else {
  198. layoutModel.addInterval(li, seq, index);
  199. }
  200. }
  201. return null;
  202. }
  203. // create parallel group for multiple intervals/sequences
  204. LayoutInterval group = new LayoutInterval(PARALLEL);
  205. // if (position == mainAlignment) {
  206. // // [but this should eliminate resizability only for gaps...]
  207. // group.setMinimumSize(USE_PREFERRED_SIZE);
  208. // group.setMaximumSize(USE_PREFERRED_SIZE);
  209. // }
  210. //// group.setGroupAlignment(alignment);
  211. // fill the group
  212. for (Iterator it=list.iterator(); it.hasNext(); ) {
  213. List subList = (List) it.next();
  214. LayoutInterval interval;
  215. if (subList.size() == 2) { // there is just one interval - use it directly
  216. int alignment = ((Integer)subList.get(0)).intValue();
  217. interval = (LayoutInterval) subList.get(1);
  218. if (alignment == LEADING || alignment == TRAILING)
  219. layoutModel.setIntervalAlignment(interval, alignment);
  220. }
  221. else { // there are more intervals - create sequence
  222. interval = new LayoutInterval(SEQUENTIAL);
  223. int alignment = ((Integer)subList.get(0)).intValue();
  224. if (alignment == LEADING || alignment == TRAILING)
  225. interval.setAlignment(alignment);
  226. for (int i=1,n=subList.size(); i < n; i++) {
  227. LayoutInterval li = (LayoutInterval) subList.get(i);
  228. if (resizingFillGap && li.isEmptySpace() && !LayoutInterval.canResize(li)
  229. && ((i == 1 && position == TRAILING) || (i == n-1 && position == LEADING)))
  230. { // make the end gap resizing
  231. layoutModel.setIntervalSize(
  232. li, NOT_EXPLICITLY_DEFINED, li.getPreferredSize(), Short.MAX_VALUE);
  233. }
  234. layoutModel.addInterval(li, interval, -1);
  235. }
  236. }
  237. layoutModel.addInterval(interval, group, -1);
  238. }
  239. layoutModel.addInterval(group, seq, index);
  240. return group;
  241. }
  242. /**
  243. * Adds 'interval' to 'target'. If needed, 'interval' is dismounted and
  244. * instead its sub-intervals are added. This is done e.g. when adding a
  245. * sequence to another sequence, or if adding a parallel group to another
  246. * parallel group with same alignment. Also redundant groups are canceled
  247. * (containing just one interval).
  248. * @return the increase of sub-intervals in target
  249. */
  250. int addContent(LayoutInterval interval, LayoutInterval target, int index) {
  251. return addContent(interval, target, index, -1);
  252. }
  253. /**
  254. * Adds 'interval' (or its content) to 'target'. In addition to the other
  255. * addContent method this one takes care of merging consecutive gaps that
  256. * may occur when adding content of a sequence to another sequence.
  257. */
  258. int addContent(LayoutInterval interval, LayoutInterval target, int index, int dimension) {
  259. int count = target.getSubIntervalCount();
  260. while (interval.isGroup() && interval.getSubIntervalCount() == 1) {
  261. interval = layoutModel.removeInterval(interval, 0);
  262. }
  263. if (interval.isSequential() && target.isSequential()) {
  264. if (index < 0) {
  265. index = target.getSubIntervalCount();
  266. }
  267. int startIndex = index;
  268. while (interval.getSubIntervalCount() > 0) {
  269. LayoutInterval li = layoutModel.removeInterval(interval, 0);
  270. layoutModel.addInterval(li, target, index++);
  271. }
  272. if (dimension == HORIZONTAL || dimension == VERTICAL) {
  273. // consecutive gaps may happen where the sequence was inserted
  274. startIndex--; // before first added
  275. index--; // last added
  276. if (startIndex >= 0 && mergeConsecutiveGaps(target, startIndex, dimension)) {
  277. index--; // one less
  278. }
  279. mergeConsecutiveGaps(target, index, dimension);
  280. }
  281. } else if (interval.isParallel() && target.isParallel()) {
  282. layoutModel.addInterval(interval, target, index);
  283. dissolveRedundantGroup(interval);
  284. } else {
  285. if (target.isSequential() && interval.getRawAlignment() != DEFAULT) {
  286. layoutModel.setIntervalAlignment(interval, DEFAULT);
  287. }
  288. layoutModel.addInterval(interval, target, index);
  289. }
  290. return target.getSubIntervalCount() - count;
  291. }
  292. void resizeInterval(LayoutInterval interval, int size) {
  293. assert size >= 0 || size == NOT_EXPLICITLY_DEFINED;
  294. int min = (interval.getMinimumSize() == interval.getPreferredSize()
  295. && interval.getMaximumSize() < Short.MAX_VALUE) ?
  296. size : interval.getMinimumSize();
  297. int max = interval.getMaximumSize() == interval.getPreferredSize() ?
  298. ((size == NOT_EXPLICITLY_DEFINED) ? USE_PREFERRED_SIZE : size) : interval.getMaximumSize();
  299. layoutModel.setIntervalSize(interval, min, size, max);
  300. }
  301. void setIntervalResizing(LayoutInterval interval, boolean resizing) {
  302. layoutModel.setIntervalSize(interval,
  303. resizing ? NOT_EXPLICITLY_DEFINED : USE_PREFERRED_SIZE,
  304. interval.getPreferredSize(),
  305. resizing ? Short.MAX_VALUE : USE_PREFERRED_SIZE);
  306. }
  307. void suppressGroupResizing(LayoutInterval group) {
  308. // don't for root group
  309. if (group.getParent() != null) {
  310. layoutModel.setIntervalSize(group, group.getMinimumSize(),
  311. group.getPreferredSize(),
  312. USE_PREFERRED_SIZE);
  313. }
  314. }
  315. void enableGroupResizing(LayoutInterval group) {
  316. layoutModel.setIntervalSize(group, group.getMinimumSize(),
  317. group.getPreferredSize(),
  318. NOT_EXPLICITLY_DEFINED);
  319. }
  320. void eliminateResizing(LayoutInterval interval, int dimension, Collection<LayoutInterval> eliminated) {
  321. if (interval.isSingle()) {
  322. if (LayoutInterval.wantResize(interval)) {
  323. int pref = LayoutRegion.UNKNOWN;
  324. if (interval.hasAttribute(LayoutInterval.ATTR_SIZE_DIFF)) {
  325. pref = LayoutInterval.getCurrentSize(interval, dimension);
  326. }
  327. if (pref == LayoutRegion.UNKNOWN) {
  328. pref = interval.getPreferredSize();
  329. }
  330. layoutModel.setIntervalSize(interval, USE_PREFERRED_SIZE, pref, USE_PREFERRED_SIZE);
  331. if (eliminated != null) {
  332. eliminated.add(interval);
  333. }
  334. }
  335. } else {
  336. for (Iterator<LayoutInterval> it=interval.getSubIntervals(); it.hasNext(); ) {
  337. eliminateResizing(it.next(), dimension, eliminated);
  338. }
  339. }
  340. }
  341. Collection<LayoutInterval> eliminateRedundantSuppressedResizing(LayoutInterval group, int dimension) {
  342. if (!group.isParallel()) {
  343. group = group.getParent();
  344. }
  345. if (group.getParent() != null && !LayoutInterval.canResize(group)) {
  346. int groupSize = group.getCurrentSpace().size(dimension);
  347. if (groupSize <= 0) {
  348. return Collections.EMPTY_LIST; // unknown current size
  349. }
  350. LayoutInterval oneResizing = null;
  351. boolean span = false;
  352. for (Iterator<LayoutInterval> it=group.getSubIntervals(); it.hasNext(); ) {
  353. LayoutInterval li = it.next();
  354. if (LayoutInterval.wantResize(li)) {
  355. if (oneResizing == null) {
  356. oneResizing = li;
  357. } else {
  358. return Collections.EMPTY_LIST; // more than one is not redundant
  359. }
  360. } else if (!span && !li.isEmptySpace()
  361. && li.getCurrentSpace().size(dimension) == groupSize) {
  362. span = true;
  363. }
  364. }
  365. if (oneResizing != null && !span) {
  366. List<LayoutInterval> l = new LinkedList<LayoutInterval>();
  367. if (!oneResizing.isGroup() || !suppressResizingInSubgroup(oneResizing, l)) {
  368. eliminateResizing(oneResizing, dimension, l);
  369. }
  370. enableGroupResizing(group);
  371. return l;
  372. }
  373. }
  374. return Collections.EMPTY_LIST;
  375. }
  376. /**
  377. * @return true if group either does not want to resize, or was set to
  378. * suppressed resizing when parallel with more than one resizing sub interval
  379. */
  380. private boolean suppressResizingInSubgroup(LayoutInterval group, Collection<LayoutInterval> eliminated) {
  381. LayoutInterval oneResizing = null;
  382. for (Iterator<LayoutInterval> it=group.getSubIntervals(); it.hasNext(); ) {
  383. LayoutInterval li = it.next();
  384. boolean res;
  385. if (LayoutInterval.canResize(li)) {
  386. if (li.isGroup()) {
  387. res = !suppressResizingInSubgroup(li, eliminated);
  388. } else {
  389. res = true;
  390. }
  391. } else {
  392. res = false;
  393. }
  394. if (res) {
  395. if (oneResizing == null) {
  396. oneResizing = li;
  397. } else if (group.isParallel()) { // more than 1 resizing
  398. suppressGroupResizing(group);
  399. eliminated.add(group);
  400. return true;
  401. } else {
  402. return false; // can't suppress a sequence
  403. }
  404. }
  405. }
  406. return oneResizing == null;
  407. }
  408. boolean completeGroupResizing(LayoutInterval group, int dimension) {
  409. if (!PREFER_ZERO_GAPS || !group.isParallel() || !LayoutInterval.canResize(group)
  410. || !LayoutInterval.contentWantResize(group)) {
  411. return false;
  412. }
  413. boolean gapAdded = false;
  414. List<LayoutInterval> list = null;
  415. for (Iterator<LayoutInterval> it=group.getSubIntervals(); it.hasNext(); ) {
  416. LayoutInterval li = it.next();
  417. if (!li.isEmptySpace() && !LayoutInterval.wantResize(li)
  418. && (li.getAlignment() == LEADING || li.getAlignment() == TRAILING)) {
  419. if (list == null) {
  420. list = new LinkedList<LayoutInterval>();
  421. }
  422. list.add(li);
  423. }
  424. }
  425. if (list != null) {
  426. for (LayoutInterval li : list) {
  427. LayoutInterval gap = new LayoutInterval(SINGLE);
  428. gap.setMinimumSize(0);
  429. gap.setPreferredSize(0);
  430. gap.setMaximumSize(Short.MAX_VALUE);
  431. gap.setAttribute(LayoutInterval.ATTR_FLEX_SIZEDEF);
  432. insertGap(gap, li,
  433. li.getCurrentSpace().positions[dimension][li.getAlignment()^1],
  434. dimension, li.getAlignment()^1);
  435. if (gap.getParent() != null) {
  436. gapAdded = true;
  437. }
  438. }
  439. }
  440. return gapAdded;
  441. }
  442. /**
  443. * Sets all components in a parallel group that have the same size with
  444. * 'aligned' to resizing so they all accommodate to same size.
  445. */
  446. void setParallelSameSize(LayoutInterval group, LayoutInterval aligned, int dimension) {
  447. assert group.isParallel();
  448. LayoutInterval alignedComp = getOneNonEmpty(aligned);
  449. for (Iterator it=group.getSubIntervals(); it.hasNext(); ) {
  450. LayoutInterval li = (LayoutInterval) it.next();
  451. if (li == aligned) {
  452. continue;
  453. }
  454. if (li.isParallel()) {
  455. setParallelSameSize(li, alignedComp, dimension);
  456. } else {
  457. LayoutInterval sub = getOneNonEmpty(li);
  458. if (sub != null
  459. && LayoutRegion.sameSpace(alignedComp.getCurrentSpace(), sub.getCurrentSpace(), dimension)
  460. && !LayoutInterval.wantResize(li)) {
  461. // viusally aligned subinterval
  462. if (sub.isParallel()) {
  463. setParallelSameSize(sub, alignedComp, dimension);
  464. } else if (!isPressurizedComponent(sub)) {
  465. // make this component filling the group - effectively keeping same size
  466. if (!LayoutInterval.isAlignedAtBorder(li, aligned.getAlignment())) {
  467. layoutModel.setIntervalAlignment(li, aligned.getAlignment());
  468. }
  469. int min = sub.getMinimumSize();
  470. layoutModel.setIntervalSize(sub,
  471. min != USE_PREFERRED_SIZE ? min : NOT_EXPLICITLY_DEFINED,
  472. sub.getPreferredSize(),
  473. Short.MAX_VALUE);
  474. }
  475. }
  476. }
  477. }
  478. }
  479. private static LayoutInterval getOneNonEmpty(LayoutInterval interval) {
  480. if (!interval.isSequential()) {
  481. return interval;
  482. }
  483. LayoutInterval nonEmpty = null;
  484. for (Iterator it=interval.getSubIntervals(); it.hasNext(); ) {
  485. LayoutInterval li = (LayoutInterval) it.next();
  486. if (!li.isEmptySpace()) {
  487. if (nonEmpty == null) {
  488. nonEmpty = li;
  489. } else {
  490. return null;
  491. }
  492. }
  493. }
  494. return nonEmpty;
  495. }
  496. /**
  497. * Is the component set to explicit fixed size smaller than its natural
  498. * minimum size?
  499. */
  500. private boolean isPressurizedComponent(LayoutInterval interval) {
  501. int pref = interval.getPreferredSize();
  502. if (interval.isComponent() && !LayoutInterval.canResize(interval)
  503. && pref != NOT_EXPLICITLY_DEFINED) {
  504. LayoutComponent comp = interval.getComponent();
  505. Dimension minSize = visualMapper.getComponentMinimumSize(comp.getId());
  506. if (minSize != null) {
  507. int min = comp.getLayoutInterval(HORIZONTAL) == interval ? minSize.width : minSize.height;
  508. if (pref < min) {
  509. return true;
  510. }
  511. }
  512. }
  513. return false;
  514. }
  515. void mergeParallelGroups(LayoutInterval group) {
  516. for (int i=group.getSubIntervalCount()-1; i >= 0; i--) {
  517. LayoutInterval sub = group.getSubInterval(i);
  518. if (sub.isGroup()) {
  519. mergeParallelGroups(sub);
  520. dissolveRedundantGroup(sub);
  521. }
  522. }
  523. }
  524. /**
  525. * Dissolves given group to parent group in case it is redundant.
  526. * @return true if the group was dissolved
  527. */
  528. boolean dissolveRedundantGroup(LayoutInterval group) {
  529. LayoutInterval parent = group.getParent();
  530. if (parent == null)
  531. return false;
  532. boolean justOne = (group.getSubIntervalCount() == 1);
  533. boolean dissolve = false;
  534. if (justOne) {
  535. dissolve = true;
  536. } else if (group.isSequential() && parent.isSequential()) {
  537. dissolve = true;
  538. } else if (group.isParallel() && parent.isParallel()) {
  539. int gA = group.getGroupAlignment();
  540. int pA = parent.getGroupAlignment();
  541. if (parent.getSubIntervalCount() == 1
  542. && (gA == pA || (gA != BASELINE && gA != CENTER))) {
  543. dissolve = true;
  544. } else { // check for compatible alignment and resizability
  545. int align = group.getAlignment();
  546. boolean sameAlign = true;
  547. boolean subResizing = false;
  548. Iterator it = group.getSubIntervals();
  549. while (it.hasNext()) {
  550. LayoutInterval li = (LayoutInterval) it.next();
  551. if (!subResizing && LayoutInterval.wantResize(li)) {
  552. subResizing = true;
  553. }
  554. if (li.getAlignment() != align) {
  555. sameAlign = false;
  556. }
  557. }
  558. boolean compatible;
  559. if (subResizing && (sameAlign || gA != BASELINE)) {
  560. compatible = false;
  561. boolean resizingAlreadyContained;
  562. if (LayoutInterval.canResize(group)) {
  563. resizingAlreadyContained = true;
  564. } else if (!LayoutInterval.canResize(parent)) {
  565. int dim = LayoutUtils.determineDimension(parent);
  566. resizingAlreadyContained = dim < 0
  567. || LayoutInterval.getCurrentSize(parent, dim) == LayoutInterval.getCurrentSize(group, dim);
  568. } else {
  569. resizingAlreadyContained = false;
  570. }
  571. if (resizingAlreadyContained) {
  572. it = parent.getSubIntervals();
  573. while (it.hasNext()) {
  574. LayoutInterval li = (LayoutInterval) it.next();
  575. if (li != group && LayoutInterval.wantResize(li)) {
  576. compatible = true;
  577. break;
  578. }
  579. }
  580. if (!compatible && (align == LEADING || align == TRAILING)) {
  581. LayoutInterval neighbor = LayoutInterval.getNeighbor(
  582. parent, group.getAlignment()^1, false, true, true);
  583. if (neighbor != null && neighbor.isEmptySpace()
  584. && neighbor.getPreferredSize() == NOT_EXPLICITLY_DEFINED)
  585. { // default fixed padding means there is no space for
  586. // independent size change, so the subgroup can be merged
  587. compatible = true;
  588. }
  589. }
  590. }
  591. }
  592. else compatible = sameAlign;
  593. dissolve = compatible;
  594. }
  595. }
  596. if (dissolve) { // the sub-group can be dissolved into parent group
  597. int alignmentInParent = group.getAlignment();
  598. int index = layoutModel.removeInterval(group);
  599. while (group.getSubIntervalCount() > 0) {
  600. LayoutInterval li = group.getSubInterval(0);
  601. int align = li.getAlignment();
  602. layoutModel.removeInterval(li);
  603. if (justOne && !LayoutInterval.canResize(group)) {
  604. }
  605. if (parent.isParallel()) { // moving to parallel group
  606. if (justOne) {
  607. if ((align != DEFAULT && align != alignmentInParent)
  608. || (align == DEFAULT && alignmentInParent != parent.getGroupAlignment())) {
  609. layoutModel.setIntervalAlignment(li, group.getRawAlignment());
  610. }
  611. if ((!LayoutInterval.canResize(group) || align == BASELINE)
  612. && LayoutInterval.wantResize(li)) {
  613. // resizing interval in fixed group - make it fixed
  614. if (li.isGroup()) {
  615. suppressGroupResizing(li);
  616. } else {
  617. layoutModel.setIntervalSize(li, USE_PREFERRED_SIZE, li.getPreferredSize(), USE_PREFERRED_SIZE);
  618. }
  619. }
  620. } else if (group.isParallel()) { // from parallel group
  621. if (li.getRawAlignment() == DEFAULT
  622. && group.getGroupAlignment() != parent.getGroupAlignment())
  623. { // force alignment explicitly
  624. layoutModel.setIntervalAlignment(li, align);
  625. }
  626. }
  627. layoutModel.addInterval(li, parent, index++);
  628. } else { // moving to sequential group
  629. index += addContent(li, parent, index, LayoutUtils.determineDimension(li));
  630. }
  631. }
  632. if (parent.getSubIntervalCount() == 1) {
  633. dissolveRedundantGroup(parent.getSubInterval(0));
  634. }
  635. return true;
  636. }
  637. return false;
  638. }
  639. /** NOT USED
  640. * This method goes through a sequential group and moves each interval next
  641. * to an open edge of a parallel group into the group.
  642. * @param parent sequential group to process
  643. * @param dimension
  644. */
  645. void moveInsideSequential(LayoutInterval parent, int dimension) {
  646. assert parent.isSequential();
  647. if (!parent.isSequential())
  648. return;
  649. int alignment = LEADING;
  650. do {
  651. LayoutInterval extend = findIntervalToExtend(parent, dimension, alignment);
  652. if (extend == null) {
  653. if (alignment == LEADING) {
  654. alignment = TRAILING;
  655. extend = findIntervalToExtend(parent, dimension, alignment);
  656. }
  657. if (extend == null)
  658. break;
  659. }
  660. LayoutInterval inGroup = extend.getParent(); // group to infiltrate
  661. LayoutInterval outGroup = inGroup;
  662. while (outGroup.getParent() != parent) {
  663. outGroup = outGroup.getParent();
  664. }
  665. int index = parent.indexOf(outGroup);
  666. int d = alignment == LEADING ? -1 : 1;
  667. // will the group remain open at the opposite edge?
  668. boolean commonEndingGap = true;
  669. for (int i=index-d, n=parent.getSubIntervalCount(); i >= 0 && i < n; i-=d) {
  670. LayoutInterval li = parent.getSubInterval(i);
  671. if ((!li.isEmptySpace() || (i-d >= 0 && i-d < n)) // ignore last gap
  672. && LayoutInterval.wantResize(li))
  673. { // resizing interval will close the group
  674. // possibly need to separate the rest of the group not to be influenced
  675. LayoutInterval endGap = parent.getSubInterval(alignment == LEADING ? n-1 : 0);
  676. if (endGap == null || endGap.getPreferredSize() != NOT_EXPLICITLY_DEFINED) {
  677. commonEndingGap = false;
  678. LayoutInterval closing = extend;
  679. int borderPos = parent.getCurrentSpace().positions[dimension][alignment^1];
  680. do {
  681. LayoutInterval par = closing.getParent();
  682. if (par.isParallel()) {
  683. separateGroupContent(closing, borderPos, dimension, alignment^1);
  684. }
  685. closing = par;
  686. }
  687. while (closing != outGroup);
  688. }
  689. break;
  690. }
  691. }
  692. int extendPos = extend.getCurrentSpace().positions[dimension][alignment^1];
  693. if (!extend.isSequential()) {
  694. LayoutInterval seq = new LayoutInterval(SEQUENTIAL);
  695. seq.setAlignment(extend.getAlignment());
  696. layoutModel.addInterval(seq, inGroup, layoutModel.removeInterval(extend));
  697. layoutModel.setIntervalAlignment(extend, DEFAULT);
  698. layoutModel.addInterval(extend, seq, 0);
  699. extend = seq;
  700. }
  701. // move the intervals from outside inside the group, next to found interval (extend)
  702. LayoutInterval connectingGap = null;
  703. int idx, addIdx;
  704. if (alignment == LEADING) {
  705. idx = index + 1; // start behind the group
  706. addIdx = extend.getSubIntervalCount(); // add behind the interval
  707. }
  708. else {
  709. idx = index - 1; // start before the group
  710. addIdx = 0; // add before the interval
  711. }
  712. while (idx >= 0 && idx < parent.getSubIntervalCount()) {
  713. LayoutInterval li = parent.getSubInterval(idx);
  714. if (li.isEmptySpace()) {
  715. if (connectingGap == null) { // first gap
  716. if (extendPos != outGroup.getCurrentSpace().positions[dimension][alignment^1]) {
  717. // need to extend the first gap (extended interval inside group is smaller than the group)
  718. int neighborPos = parent.getSubInterval(idx-d).getCurrentSpace().positions[dimension][alignment];
  719. int distance = d * (extendPos - neighborPos);
  720. if (distance > 0)
  721. resizeInterval(li, distance);
  722. }
  723. connectingGap = li;
  724. }
  725. else if ((idx == 0 || idx == parent.getSubIntervalCount()-1)
  726. && commonEndingGap)
  727. { // keep the last gap out
  728. break;
  729. }
  730. }
  731. layoutModel.removeInterval(li);
  732. layoutModel.addInterval(li, extend, addIdx);
  733. if (alignment == LEADING)
  734. addIdx++;
  735. else
  736. idx--;
  737. }
  738. // check if the sequence was not whole moved into the group
  739. if (parent.getSubIntervalCount() == 1) { // only neighborGroup remained, eliminate the parent group
  740. assert outGroup == parent.getSubInterval(0);
  741. layoutModel.removeInterval(outGroup);
  742. LayoutInterval superParent = parent.getParent();
  743. addContent(outGroup, superParent, layoutModel.removeInterval(parent));
  744. break;
  745. }
  746. }
  747. while (true);
  748. }
  749. private LayoutInterval findIntervalToExtend(LayoutInterval parent, int dimension, int alignment) {
  750. int d = alignment == LEADING ? -1 : 1;
  751. int count = parent.getSubIntervalCount();
  752. int idx = alignment == LEADING ? count-1 : 0;
  753. boolean atBorder = true;
  754. boolean gap = false;
  755. while (idx >= 0 && idx < parent.getSubIntervalCount()) {
  756. LayoutInterval sub = parent.getSubInterval(idx);
  757. if (sub.isEmptySpace()) {
  758. gap = true;
  759. }
  760. else {
  761. if (!atBorder && gap && sub.isParallel()
  762. && !LayoutInterval.isClosedGroup(sub, alignment^1))
  763. { // this open parallel sub-group might be a candidate to move inside to
  764. int startIndex, endIndex;
  765. if (alignment == LEADING) {
  766. startIndex = idx + 1;
  767. endIndex = parent.getSubIntervalCount() - 1;
  768. }
  769. else {
  770. startIndex = 0;
  771. endIndex = idx - 1;
  772. }
  773. LayoutInterval extend = prepareGroupExtension(
  774. sub, parent, startIndex, endIndex, dimension, alignment^1);
  775. if (extend != null)
  776. return extend;
  777. }
  778. gap = false;
  779. atBorder = false;
  780. }
  781. idx += d;
  782. }
  783. return null;
  784. }
  785. private LayoutInterval prepareGroupExtension(LayoutInterval group,
  786. LayoutInterval parent, int startIndex, int endIndex,
  787. int dimension, int alignment)
  788. {
  789. boolean allOverlapping = true;
  790. LayoutInterval singleOverlap = null;
  791. List<LayoutInterval> overlapList = null;
  792. // looking for all intervals the given space is located next to
  793. Iterator it = group.getSubIntervals();
  794. while (it.hasNext()) {
  795. LayoutInterval li = (LayoutInterval) it.next();
  796. if (!li.isEmptySpace()) {
  797. if (LayoutUtils.contentOverlap(li, parent, startIndex, endIndex, dimension^1)) {
  798. // interval overlaps orthogonally
  799. if (singleOverlap == null) {
  800. singleOverlap = li;
  801. }
  802. else {
  803. if (overlapList == null) {
  804. overlapList = new LinkedList<LayoutInterval>();
  805. overlapList.add(singleOverlap);
  806. }
  807. overlapList.add(li);
  808. }
  809. }
  810. else allOverlapping = false;
  811. }
  812. }
  813. if (allOverlapping || singleOverlap == null)
  814. return null; // spans whole group or nothing
  815. if (overlapList != null) { // overlaps multiple intervals
  816. LayoutInterval subGroup = new LayoutInterval(PARALLEL);
  817. subGroup.setGroupAlignment(alignment^1);
  818. subGroup.setAlignment(alignment^1);
  819. int index = -1;
  820. do {
  821. LayoutInterval li = overlapList.remove(0);
  822. int idx = layoutModel.removeInterval(li);
  823. if (index < 0) {
  824. index = idx;
  825. }
  826. layoutModel.addInterval(li, subGroup, -1);
  827. subGroup.getCurrentSpace().expand(li.getCurrentSpace());
  828. }
  829. while (overlapList.size() > 0);
  830. layoutModel.addInterval(subGroup, group, index);
  831. singleOverlap = subGroup;
  832. }
  833. else {
  834. LayoutInterval subParallel;
  835. if (singleOverlap.isSequential()) {
  836. subParallel = singleOverlap.getSubInterval(
  837. alignment == LEADING ? 0 : singleOverlap.getSubIntervalCount()-1);
  838. if (!subParallel.isParallel())
  839. subParallel = null;
  840. }
  841. else if (singleOverlap.isParallel()) {
  842. subParallel = singleOverlap;
  843. }
  844. else subParallel = null;
  845. if (subParallel != null && !LayoutInterval.isClosedGroup(subParallel, alignment)) {
  846. LayoutInterval subOverlap = prepareGroupExtension(
  847. subParallel, parent, startIndex, endIndex, dimension, alignment);
  848. if (subOverlap != null)
  849. singleOverlap = subOverlap;
  850. }
  851. }
  852. return singleOverlap;
  853. }
  854. // [couldn't parallelizeWithParentSequence be used instead? or LayoutFeeder.separateSequence?]
  855. private void separateGroupContent(LayoutInterval separate, int outPos, int dimension, int alignment) {
  856. LayoutInterval group = separate.getParent();
  857. assert group.isParallel();
  858. LayoutInterval remainder = null;
  859. LayoutInterval remainderGroup = null;
  860. LayoutRegion remainderSpace = null;
  861. for (int i=0; i < group.getSubIntervalCount(); ) {
  862. LayoutInterval li = group.getSubInterval(i);
  863. if (li != separate) {
  864. assert li.getAlignment() == (alignment^1);
  865. layoutModel.removeInterval(li);
  866. if (remainder == null) {
  867. remainder = li;
  868. }
  869. else {
  870. if (remainderGroup == null) {
  871. remainderGroup = new LayoutInterval(PARALLEL);
  872. remainderGroup.setAlignment(alignment^1);
  873. remainderGroup.setGroupAlignment(alignment^1);
  874. layoutModel.addInterval(remainder, remainderGroup, 0);
  875. remainder = remainderGroup;
  876. }
  877. layoutModel.addInterval(li, remainderGroup, -1);
  878. }
  879. if (!li.isEmptySpace()) {
  880. if (remainderSpace == null) {
  881. remainderSpace = new LayoutRegion();
  882. }
  883. remainderSpace.expand(li.getCurrentSpace());
  884. }
  885. }
  886. else i++;
  887. }
  888. remainder.setCurrentSpace(remainderSpace);
  889. LayoutInterval remainderGap;
  890. int remainderPos = remainderSpace.positions[dimension][alignment];
  891. if (LayoutRegion.isValidCoordinate(outPos)) {
  892. int gapSize = alignment == LEADING ? remainderPos - outPos : outPos - remainderPos;
  893. remainderGap = new LayoutInterval(SINGLE);
  894. remainderGap.setSizes(NOT_EXPLICITLY_DEFINED, gapSize, Short.MAX_VALUE);
  895. }
  896. else { // take the existing gap next to group [this case is not used currently]
  897. remainderGap = LayoutInterval.getDirectNeighbor(group, alignment, false);
  898. if (remainderGap != null && remainderGap.isEmptySpace()) {
  899. layoutModel.removeInterval(remainderGap);
  900. // [should check for last interval in parent]
  901. LayoutInterval neighbor = LayoutInterval.getDirectNeighbor(group, alignment, true);
  902. outPos = neighbor != null ?
  903. neighbor.getCurrentSpace().positions[dimension][alignment^1] :
  904. group.getParent().getCurrentSpace().positions[dimension][alignment];
  905. int gapSize = alignment == LEADING ? remainderPos - outPos : outPos - remainderPos;
  906. resizeInterval(remainderGap, gapSize);
  907. }
  908. else remainderGap = null;
  909. }
  910. if (remainderGap != null) {
  911. LayoutInterval seq;
  912. if (remainder.isSequential()) {
  913. seq = remainder;
  914. }
  915. else {
  916. seq = new LayoutInterval(SEQUENTIAL);
  917. layoutModel.setIntervalAlignment(remainder, DEFAULT);
  918. layoutModel.addInterval(remainder, seq, 0);
  919. }
  920. layoutModel.addInterval(remainderGap, seq, alignment == LEADING ? 0 : -1);
  921. layoutModel.addInterval(seq, group, -1);
  922. group.getCurrentSpace().positions[dimension][alignment] = outPos;
  923. }
  924. else {
  925. layoutModel.addInterval(remainder, group, -1);
  926. }
  927. }
  928. /**
  929. * Makes given interval parallel with part of its parent sequence.
  930. */
  931. void parallelizeWithParentSequence(LayoutInterval interval, int endIndex, int dimension) {
  932. LayoutInterval parent = interval.getParent();
  933. assert parent.isParallel();
  934. LayoutInterval parParent = parent;
  935. while (!parParent.getParent().isSequential()) {
  936. parParent = parParent.getParent();
  937. }
  938. LayoutInterval parentSeq = parParent.getParent();
  939. int startIndex = parentSeq.indexOf(parParent);
  940. if (endIndex < 0)
  941. endIndex = parentSeq.getSubIntervalCount() - 1;
  942. else if (startIndex > endIndex) {
  943. int temp = startIndex;
  944. startIndex = endIndex;
  945. endIndex = temp;
  946. }
  947. layoutModel.removeInterval(interval);
  948. // TODO compensate possible group shrinking when removing the biggest interval
  949. if (interval.getAlignment() == DEFAULT) {
  950. layoutModel.setIntervalAlignment(interval, parent.getGroupAlignment());
  951. }
  952. addParallelWithSequence(interval, parentSeq, startIndex, endIndex, dimension);
  953. if (parent.getSubIntervalCount() == 1) {
  954. addContent(layoutModel.removeInterval(parent, 0),
  955. parent.getParent(),
  956. layoutModel.removeInterval(parent),
  957. dimension);
  958. }
  959. else if (parent.getSubIntervalCount() == 0) {
  960. layoutModel.removeInterval(parent);
  961. }
  962. }
  963. void addParallelWithSequence(LayoutInterval interval, LayoutInterval seq, int startIndex, int endIndex, int dimension) {
  964. LayoutInterval group;
  965. if (startIndex > 0 || endIndex < seq.getSubIntervalCount()-1) {
  966. group = new LayoutInterval(PARALLEL);
  967. if (interval.getAlignment() != DEFAULT) {
  968. group.setGroupAlignment(interval.getAlignment());
  969. }
  970. int startPos = LayoutUtils.getVisualPosition(seq.getSubInterval(startIndex), dimension, LEADING);
  971. int endPos = LayoutUtils.getVisualPosition(seq.getSubInterval(endIndex), dimension, TRAILING);
  972. group.getCurrentSpace().set(dimension, startPos, endPos);
  973. if (startIndex != endIndex) {
  974. LayoutInterval subSeq = new LayoutInterval(SEQUENTIAL);
  975. subSeq.setAlignment(interval.getAlignment()); // [was: seq.getAlignment()]
  976. for (int n=endIndex-startIndex+1; n > 0; n--) {
  977. layoutModel.addInterval(layoutModel.removeInterval(seq, startIndex), subSeq, -1);
  978. }
  979. layoutModel.addInterval(subSeq, group, 0);
  980. }
  981. else {
  982. layoutModel.addInterval(layoutModel.removeInterval(seq, startIndex), group, 0);
  983. }
  984. layoutModel.addInterval(group, seq, startIndex);
  985. group.getCurrentSpace().expand(interval.getCurrentSpace(), dimension);
  986. }
  987. else {
  988. group = seq.getParent();
  989. }
  990. layoutModel.addInterval(interval, group, -1);
  991. }
  992. int optimizeGaps(LayoutInterval group, int dimension) {
  993. for (int i=0; i < group.getSubIntervalCount(); i++) {
  994. LayoutInterval li = group.getSubInterval(i);
  995. if (li.isEmptySpace() && group.getSubIntervalCount() > 1) {
  996. // remove container supporting gap
  997. layoutModel.removeInterval(group, i);
  998. i--;
  999. }
  1000. }
  1001. if (group.getSubIntervalCount() <= 1) {
  1002. return -1;
  1003. }
  1004. // 1) Determine which intervals have ending gaps for optimization, what's
  1005. // their alignment and resizability, and whether whole group should be
  1006. // processed or just a part (subgroup).
  1007. boolean anyAlignedLeading = false; // if false the group is open at leading edge
  1008. boolean anyAlignedTrailing = false; // if false the group is open at trailing edge
  1009. boolean contentResizing = false;
  1010. IntervalSet processLeading = null;
  1011. IntervalSet processTrailing = null;
  1012. boolean subGroupLeading = false;
  1013. boolean subGroupTrailing = false;
  1014. {
  1015. // 1a) Analyze where the ending gaps are and how aligned.
  1016. IntervalSet[] alignedGaps = new IntervalSet[] { new IntervalSet(), new IntervalSet() };
  1017. IntervalSet[] alignedNoGaps = new IntervalSet[] { new IntervalSet(), new IntervalSet() };
  1018. IntervalSet[] unalignedFixedGaps = new IntervalSet[] { new IntervalSet(), new IntervalSet() };
  1019. IntervalSet[] unalignedResGaps = new IntervalSet[] { new IntervalSet(), new IntervalSet() };
  1020. IntervalSet[] unalignedNoGaps = new IntervalSet[] { new IntervalSet(), new IntervalSet() };
  1021. for (int i=0; i < group.getSubIntervalCount(); i++) {
  1022. LayoutInterval li = group.getSubInterval(i);
  1023. boolean leadAlign = false;
  1024. boolean trailAlign = false;
  1025. LayoutInterval leadingGap = null;
  1026. LayoutInterval trailingGap = null;
  1027. boolean leadGapRes = false;
  1028. boolean trailGapRes = false;
  1029. boolean contentRes = false;
  1030. boolean noResizing = false;
  1031. if (li.isSequential()) {
  1032. // find out effective alignment of the sequence content without border gaps
  1033. for (int j=0; j < li.getSubIntervalCount(); j++) {
  1034. LayoutInterval sub = li.getSubInterval(j);
  1035. if (j == 0 && sub.isEmptySpace()) {
  1036. leadingGap = sub;
  1037. leadGapRes = LayoutInterval.wantResize(sub);
  1038. } else if (j+1 == li.getSubIntervalCount() && sub.isEmptySpace()) {
  1039. trailingGap = sub;
  1040. trailGapRes = LayoutInterval.wantResize(sub);
  1041. } else if (!contentRes && LayoutInterval.wantResize(sub)) {
  1042. contentRes = true;
  1043. }
  1044. }
  1045. if (!contentRes) {
  1046. if (lead