Autopsy 4.22.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
WrapLayout.java
Go to the documentation of this file.
1/*
2 * Autopsy Forensic Browser
3 *
4 * Copyright 2019-2020 Basis Technology Corp.
5 * Contact: carrier <at> sleuthkit <dot> org
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19package org.sleuthkit.autopsy.guiutils;
20
21import java.awt.Component;
22import java.awt.Container;
23import java.awt.Dimension;
24import java.awt.Insets;
25import java.awt.LayoutManager;
26import java.util.ArrayList;
27import java.util.Arrays;
28import java.util.Collection;
29import java.util.Collections;
30import java.util.HashSet;
31import java.util.List;
32import java.util.Set;
33import java.util.stream.Collectors;
34import javax.swing.JScrollPane;
35import javax.swing.SwingUtilities;
36
48public class WrapLayout implements LayoutManager, java.io.Serializable {
49
50 private static final long serialVersionUID = 1L;
51
60 private int horizontalGap = 0;
61
69 private int verticalGap = 0;
70
75 private boolean alignOnBaseline = false;
76
82 private final Set<Component> oppositeAlignedItems = new HashSet<>();
83
88 public WrapLayout() {
89 this(5, 5);
90 }
91
103 this.verticalGap = verticalGap;
104 this.horizontalGap = horizontalGap;
105 }
106
115 public void setOppositeAligned(Collection<Component> oppAlignedComponents) {
116 this.oppositeAlignedItems.clear();
117 this.oppositeAlignedItems.addAll(oppAlignedComponents);
118 }
119
127 public Collection<Component> getOppositeAlignedItems() {
128 return Collections.unmodifiableCollection(oppositeAlignedItems);
129 }
130
139 public int getHorizontalGap() {
140 return horizontalGap;
141 }
142
152 this.horizontalGap = horizontalGap;
153 }
154
162 public int getVerticalGap() {
163 return verticalGap;
164 }
165
174 public void setVerticalGap(int verticalGap) {
175 this.verticalGap = verticalGap;
176 }
177
186 public void setAlignOnBaseline(boolean alignOnBaseline) {
187 this.alignOnBaseline = alignOnBaseline;
188 }
189
198 public boolean isAlignOnBaseline() {
199 return alignOnBaseline;
200 }
201
209 @Override
210 public void addLayoutComponent(String name, Component comp) {
211 //Empty
212 }
213
220 @Override
221 public void removeLayoutComponent(Component comp) {
222 //Empty
223 }
224
236 private int getComponentY(int rowY, boolean alignBaseline, int rowHeight,
237 int itemHeight) {
238 return alignBaseline
239 ? rowY + rowHeight - itemHeight
240 : rowY;
241 }
242
255 private int getComponentX(int leftX, int rightX, boolean ltr, int xPos,
256 int componentWidth) {
257 return ltr ? leftX + xPos : rightX - xPos - componentWidth;
258 }
259
277 private int setComponentDims(Component comp, boolean alignBaseline,
278 boolean ltr, int rowY, int rowHeight, int leftX, int rightX,
279 int xPos) {
280
281 Dimension d = comp.getPreferredSize();
282 comp.setSize(d);
283
284 int x = getComponentX(leftX, rightX, ltr, xPos, d.width);
285 int y = getComponentY(rowY, alignBaseline, rowHeight, d.height);
286 comp.setLocation(x, y);
287
288 return d.width;
289 }
290
291 @Override
292 public void layoutContainer(Container target) {
293 ParentDimensions targetDims = getTargetDimensions(target);
294 List<Component> components = Arrays.asList(target.getComponents());
295 List<WrapLayoutRow> rows = getAllRows(components, true, targetDims.getInnerWidth());
296
297 boolean ltr = target.getComponentOrientation().isLeftToRight();
298 boolean useBaseline = isAlignOnBaseline();
299
300 int rowY = targetDims.getInsets().top + getVerticalGap();
301 int leftX = targetDims.getInsets().left + getHorizontalGap();
302 int rightX = targetDims.getOuterWidth() - targetDims.getInsets().right - getHorizontalGap();
303
304 for (WrapLayoutRow row : rows) {
305 int rowHeight = row.getHeight();
306
307 int curX = 0;
308 if (row.getComponents() != null) {
309 for (Component origComp : row.getComponents()) {
310 curX += setComponentDims(origComp, useBaseline, ltr, rowY, rowHeight, leftX, rightX, curX) + getHorizontalGap();
311 }
312 }
313
314 if (row.getOppositeAligned() != null) {
315 curX = 0;
316 // reverse opposite aligned for layout purposes since flipping ltr
317 Collections.reverse(row.getOppositeAligned());
318 for (Component oppAlignedComp : row.getOppositeAligned()) {
319 curX += setComponentDims(oppAlignedComp, useBaseline, !ltr, rowY, rowHeight, leftX, rightX, curX) + getHorizontalGap();
320 }
321 }
322
323 rowY += rowHeight + getVerticalGap();
324 }
325 }
326
327 @Override
328 public Dimension preferredLayoutSize(Container target) {
329 return layoutSize(target, true);
330 }
331
332 @Override
333 public Dimension minimumLayoutSize(Container target) {
334 Dimension minimum = layoutSize(target, false);
335 minimum.width -= (getHorizontalGap() + 1);
336 return minimum;
337 }
338
342 private static class ParentDimensions {
343
344 private final int outerWidth;
345 private final int innerWidth;
346 private final Insets insets;
347
355 ParentDimensions(int outerWidth, int innerWidth, Insets insets) {
356 this.outerWidth = outerWidth;
357 this.innerWidth = innerWidth;
358 this.insets = insets;
359 }
360
366 int getOuterWidth() {
367 return outerWidth;
368 }
369
376 int getInnerWidth() {
377 return innerWidth;
378 }
379
385 Insets getInsets() {
386 return insets;
387 }
388 }
389
398 private ParentDimensions getTargetDimensions(Container target) {
399 Container container = target;
400
401 while (container.getSize().width == 0 && container.getParent() != null) {
402 container = container.getParent();
403 }
404
405 int targetWidth = container.getSize().width;
406
407 if (targetWidth == 0) {
408 targetWidth = Integer.MAX_VALUE;
409 }
410
411 Insets insets = container.getInsets();
412 int horizontalInsetsAndGap = insets.left + insets.right + (getHorizontalGap() * 2);
413 int maxWidth = targetWidth - horizontalInsetsAndGap;
414
415 return new ParentDimensions(targetWidth, maxWidth, insets);
416 }
417
427 private Dimension layoutSize(Container target, boolean preferred) {
428 ParentDimensions targetDims = getTargetDimensions(target);
429 List<Component> components = Arrays.asList(target.getComponents());
430 List<WrapLayoutRow> rows = getAllRows(components, preferred, targetDims.getInnerWidth());
431
432 Integer containerHeight = rows.stream().map((r) -> r.getHeight()).reduce(0, Integer::sum);
433 // add in vertical gap between rows
434 if (rows.size() > 1) {
435 containerHeight += (rows.size() - 1) * getVerticalGap();
436 }
437
438 containerHeight += targetDims.getInsets().top + targetDims.getInsets().bottom;
439
440 Integer containerWidth = rows.stream().map((r) -> r.getWidth()).reduce(0, Math::max);
441 containerWidth += targetDims.getInsets().left + targetDims.getInsets().right + (getHorizontalGap() * 2);
442
443 // When using a scroll pane or the DecoratedLookAndFeel we need to
444 // make sure the preferred size is less than the size of the
445 // target containter so shrinking the container size works
446 // correctly. Removing the horizontal gap is an easy way to do this.
447 Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target);
448
449 if (scrollPane != null && target.isValid()) {
450 containerWidth -= (getHorizontalGap() + 1);
451 }
452
453 return new Dimension(containerWidth, containerHeight);
454 }
455
459 private class WrapLayoutRow {
460
461 private final List<Component> components;
462 private final List<Component> oppositeAligned;
463 private final int height;
464 private final int width;
465
477 WrapLayoutRow(List<Component> components, List<Component> oppositeAligned,
478 int height, int width) {
479 this.components = components;
480 this.oppositeAligned = oppositeAligned;
481 this.height = height;
482 this.width = width;
483 }
484
492 List<Component> getComponents() {
493 return components;
494 }
495
503 List<Component> getOppositeAligned() {
504 return oppositeAligned;
505 }
506
514 int getHeight() {
515 return height;
516 }
517
525 int getWidth() {
526 return width;
527 }
528
529 }
530
541 private List<WrapLayoutRow> getAllRows(List<Component> components,
542 boolean preferred, int maxWidth) {
543 List<Component> originalComp
544 = components
545 .stream()
546 .filter((comp) -> !this.oppositeAlignedItems.contains(comp))
547 .collect(Collectors.toList());
548
549 List<WrapLayoutRow> originalRowSet = getRowSet(originalComp, preferred, maxWidth);
550
551 List<Component> oppositeAlignedComp
552 = components
553 .stream()
554 .filter((comp) -> this.oppositeAlignedItems.contains(comp))
555 .collect(Collectors.toList());
556
557 // go in reverse order and then revert so we can use same getRowSet method
558 Collections.reverse(oppositeAlignedComp);
559 List<WrapLayoutRow> oppositeRowSet = getRowSet(oppositeAlignedComp, preferred, maxWidth)
560 .stream()
561 .map((WrapLayoutRow row) -> {
562 Collections.reverse(row.getComponents());
563 return new WrapLayoutRow(null, row.getComponents(), row.getHeight(), row.getWidth());
564 })
565 .collect(Collectors.toList());
566 Collections.reverse(oppositeRowSet);
567
568 List<WrapLayoutRow> toReturn = new ArrayList<>();
569
570 // if there is a row of components that will have both normal and opposite aligned
571 // components, create the corresponding row.
572 if (!originalRowSet.isEmpty() && !oppositeRowSet.isEmpty()) {
573 WrapLayoutRow lastOrig = originalRowSet.get(originalRowSet.size() - 1);
574 WrapLayoutRow firstOpp = oppositeRowSet.get(0);
575
576 int proposedRowWidth = lastOrig.getWidth() + firstOpp.getWidth() + getHorizontalGap();
577 if (proposedRowWidth <= maxWidth) {
578 WrapLayoutRow middleRow = new WrapLayoutRow(lastOrig.getComponents(), firstOpp.getOppositeAligned(),
579 Math.max(lastOrig.getHeight(), firstOpp.getHeight()), proposedRowWidth);
580
581 toReturn.addAll(originalRowSet.subList(0, originalRowSet.size() - 1));
582 toReturn.add(middleRow);
583 toReturn.addAll(oppositeRowSet.subList(1, oppositeRowSet.size()));
584 return toReturn;
585 }
586 }
587
588 toReturn.addAll(originalRowSet);
589 toReturn.addAll(oppositeRowSet);
590 return toReturn;
591 }
592
605 private List<WrapLayoutRow> getRowSet(List<Component> components,
606 boolean preferred, int maxWidth) {
607 List<WrapLayoutRow> rows = new ArrayList<>();
608
609 List<Component> rowComponents = new ArrayList<>();
610 int rowWidth = 0;
611 int rowHeight = 0;
612
613 for (Component m : components) {
614 if (m.isVisible()) {
615 Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize();
616
617 // Can't add the component to current row. Start a new row.
618 if (rowWidth + d.width > maxWidth) {
619 rows.add(new WrapLayoutRow(rowComponents, null, rowHeight, rowWidth));
620 rowComponents = new ArrayList<>();
621 rowWidth = 0;
622 rowHeight = 0;
623 }
624
625 // Add a horizontal gap for all components after the first
626 if (rowWidth != 0) {
627 rowWidth += getHorizontalGap();
628 }
629
630 rowComponents.add(m);
631 rowWidth += d.width;
632 rowHeight = Math.max(rowHeight, d.height);
633 }
634 }
635
636 if (!rowComponents.isEmpty()) {
637 rows.add(new WrapLayoutRow(rowComponents, null, rowHeight, rowWidth));
638 }
639
640 return rows;
641 }
642}
int setComponentDims(Component comp, boolean alignBaseline, boolean ltr, int rowY, int rowHeight, int leftX, int rightX, int xPos)
final Set< Component > oppositeAlignedItems
void setAlignOnBaseline(boolean alignOnBaseline)
List< WrapLayoutRow > getAllRows(List< Component > components, boolean preferred, int maxWidth)
List< WrapLayoutRow > getRowSet(List< Component > components, boolean preferred, int maxWidth)
void setOppositeAligned(Collection< Component > oppAlignedComponents)
WrapLayout(int verticalGap, int horizontalGap)
Dimension layoutSize(Container target, boolean preferred)
ParentDimensions getTargetDimensions(Container target)
Dimension preferredLayoutSize(Container target)
int getComponentX(int leftX, int rightX, boolean ltr, int xPos, int componentWidth)
Collection< Component > getOppositeAlignedItems()
Dimension minimumLayoutSize(Container target)
void addLayoutComponent(String name, Component comp)
int getComponentY(int rowY, boolean alignBaseline, int rowHeight, int itemHeight)

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.