Autopsy  4.19.3
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  */
19 package org.sleuthkit.autopsy.guiutils;
20 
21 import java.awt.Component;
22 import java.awt.Container;
23 import java.awt.Dimension;
24 import java.awt.Insets;
25 import java.awt.LayoutManager;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.HashSet;
31 import java.util.List;
32 import java.util.Set;
33 import java.util.stream.Collectors;
34 import javax.swing.JScrollPane;
35 import javax.swing.SwingUtilities;
36 
48 public 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 
102  public WrapLayout(int verticalGap, int horizontalGap) {
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 
151  public void setHorizontalGap(int horizontalGap) {
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 getComponentX(int leftX, int rightX, boolean ltr, int xPos, int componentWidth)
int setComponentDims(Component comp, boolean alignBaseline, boolean ltr, int rowY, int rowHeight, int leftX, int rightX, int xPos)
Dimension layoutSize(Container target, boolean preferred)
ParentDimensions getTargetDimensions(Container target)
WrapLayout(int verticalGap, int horizontalGap)
void setAlignOnBaseline(boolean alignOnBaseline)
List< WrapLayoutRow > getAllRows(List< Component > components, boolean preferred, int maxWidth)
Dimension minimumLayoutSize(Container target)
Dimension preferredLayoutSize(Container target)
Collection< Component > getOppositeAlignedItems()
List< WrapLayoutRow > getRowSet(List< Component > components, boolean preferred, int maxWidth)
final Set< Component > oppositeAlignedItems
Definition: WrapLayout.java:82
void setHorizontalGap(int horizontalGap)
void addLayoutComponent(String name, Component comp)
void setOppositeAligned(Collection< Component > oppAlignedComponents)
int getComponentY(int rowY, boolean alignBaseline, int rowHeight, int itemHeight)

Copyright © 2012-2022 Basis Technology. Generated on: Tue Jun 27 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.