Autopsy  4.11.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
ImageTag.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2019 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.contentviewers.imagetagging;
20 
21 import com.sun.javafx.event.EventDispatchChainImpl;
22 import java.beans.PropertyChangeEvent;
23 import java.beans.PropertyChangeListener;
24 import java.beans.PropertyChangeSupport;
25 import javafx.collections.ListChangeListener;
26 import javafx.scene.Cursor;
27 import javafx.scene.Group;
28 import javafx.scene.Node;
29 import javafx.scene.control.Tooltip;
30 import javafx.scene.image.ImageView;
31 import javafx.scene.input.MouseEvent;
32 import javafx.scene.paint.Color;
33 import javafx.scene.shape.Circle;
34 import javafx.scene.shape.Rectangle;
36 
46 public final class ImageTag extends Group {
47 
48  // Used to tell the 8 edit handles to hide if this tag is no longer selected
49  private final EventDispatchChainImpl ALL_CHILDREN;
50 
51  //Notifies listeners that the user has editted the tag boundaries
52  private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
53 
54  //The underlying presistent tag details that this image tag originates from
56 
57  public ImageTag(ContentViewerTag<ImageTagRegion> contentViewerTag, ImageView image) {
58  ALL_CHILDREN = new EventDispatchChainImpl();
59  this.appTag = contentViewerTag;
60 
61  this.getChildren().addListener((ListChangeListener<Node>) change -> {
62  change.next();
63  change.getAddedSubList().forEach((node) -> ALL_CHILDREN.append(node.getEventDispatcher()));
64  });
65 
66  ImageTagRegion details = contentViewerTag.getDetails();
67  PhysicalTag physicalTag = new PhysicalTag(details);
68 
69  //Defines the max allowable boundary that a user may drag any given handle.
70  Boundary dragBoundary = (x, y) -> {
71  double boundingX = image.getX();
72  double boundingY = image.getY();
73  double width = image.getImage().getWidth();
74  double height = image.getImage().getHeight();
75 
76  return x > boundingX + details.getStrokeThickness() / 2
77  && x < boundingX + width - details.getStrokeThickness() / 2
78  && y > boundingY + details.getStrokeThickness() / 2
79  && y < boundingY + height - details.getStrokeThickness() / 2;
80  };
81 
82  EditHandle bottomLeft = new EditHandle(physicalTag)
83  .setPosition(Position.bottom(), Position.left())
84  .setDrag(dragBoundary, Draggable.bottom(), Draggable.left());
85 
86  EditHandle bottomRight = new EditHandle(physicalTag)
87  .setPosition(Position.bottom(), Position.right())
88  .setDrag(dragBoundary, Draggable.bottom(), Draggable.right());
89 
90  EditHandle topLeft = new EditHandle(physicalTag)
91  .setPosition(Position.top(), Position.left())
92  .setDrag(dragBoundary, Draggable.top(), Draggable.left());
93 
94  EditHandle topRight = new EditHandle(physicalTag)
95  .setPosition(Position.top(), Position.right())
96  .setDrag(dragBoundary, Draggable.top(), Draggable.right());
97 
98  EditHandle bottomMiddle = new EditHandle(physicalTag)
99  .setPosition(Position.bottom(), Position.xMiddle())
100  .setDrag(dragBoundary, Draggable.bottom());
101 
102  EditHandle topMiddle = new EditHandle(physicalTag)
103  .setPosition(Position.top(), Position.xMiddle())
104  .setDrag(dragBoundary, Draggable.top());
105 
106  EditHandle rightMiddle = new EditHandle(physicalTag)
107  .setPosition(Position.right(), Position.yMiddle())
108  .setDrag(dragBoundary, Draggable.right());
109 
110  EditHandle leftMiddle = new EditHandle(physicalTag)
111  .setPosition(Position.left(), Position.yMiddle())
112  .setDrag(dragBoundary, Draggable.left());
113 
114  //The "logical" tag is the Group
115  this.getChildren().addAll(physicalTag, bottomLeft, bottomRight, topLeft,
116  topRight, bottomMiddle, topMiddle, rightMiddle, leftMiddle);
117 
118  Tooltip.install(this, new Tooltip(contentViewerTag.getContentTag()
119  .getName().getDisplayName()));
120 
121  this.addEventHandler(ImageTagControls.NOT_FOCUSED, event -> ALL_CHILDREN.dispatchEvent(event));
122  this.addEventHandler(ImageTagControls.FOCUSED, event -> ALL_CHILDREN.dispatchEvent(event));
123  }
124 
131  public void subscribeToEditEvents(PropertyChangeListener listener) {
132  pcs.addPropertyChangeListener(listener);
133  }
134 
141  return appTag;
142  }
143 
147  class PhysicalTag extends Rectangle {
148 
149  public PhysicalTag(ImageTagRegion details) {
150  this.setStroke(Color.RED);
151  this.setFill(Color.RED.deriveColor(0, 0, 0, 0));
152  this.setStrokeWidth(details.getStrokeThickness());
153 
154  setX(details.getX());
155  setY(details.getY());
156  setWidth(details.getWidth());
157  setHeight(details.getHeight());
158 
159  this.addEventHandler(ImageTagControls.NOT_FOCUSED, event -> this.setOpacity(1));
160  this.addEventHandler(ImageTagControls.FOCUSED, event -> this.setOpacity(0.5));
161  }
162 
168  public ImageTagRegion getState() {
169  return new ImageTagRegion()
170  .setX(this.getX())
171  .setY(this.getY())
172  .setWidth(this.getWidth())
173  .setHeight(this.getHeight())
174  .setStrokeThickness(this.getStrokeWidth());
175  }
176  }
177 
181  class EditHandle extends Circle {
182 
183  private final PhysicalTag parent;
184 
185  public EditHandle(PhysicalTag parent) {
186  this.setVisible(false);
187 
188  //Hide when the tag is not selected.
189  this.addEventHandler(ImageTagControls.NOT_FOCUSED, event -> this.setVisible(false));
190  this.addEventHandler(ImageTagControls.FOCUSED, event -> this.setVisible(true));
191 
192  this.setRadius(parent.getStrokeWidth());
193  this.setFill(parent.getStroke());
194 
195  this.setOnDragDetected(event -> {
196  this.getParent().setCursor(Cursor.CLOSED_HAND);
197  });
198 
199  this.setOnMouseReleased(event -> {
200  this.getParent().setCursor(Cursor.DEFAULT);
201  pcs.firePropertyChange(new PropertyChangeEvent(this, "Tag Edit", null, parent.getState()));
202  });
203 
204  this.parent = parent;
205  }
206 
213  public EditHandle setPosition(Position... vals) {
214  for (Position pos : vals) {
215  parent.widthProperty().addListener((obs, oldVal, newVal) -> pos.set(parent, this));
216  parent.heightProperty().addListener((obs, oldVal, newVal) -> pos.set(parent, this));
217  pos.set(parent, this);
218  }
219  return this;
220  }
221 
229  public EditHandle setDrag(Boundary bounds, Draggable... vals) {
230  this.setOnMouseDragged((event) -> {
231  for (Draggable drag : vals) {
232  drag.perform(parent, event, bounds);
233  }
234  });
235  return this;
236  }
237  }
238 
243  static interface Position {
244 
245  void set(PhysicalTag parent, Circle knob);
246 
247  static Position left() {
248  return (parent, knob) -> knob.centerXProperty().bind(parent.xProperty());
249  }
250 
251  static Position right() {
252  return (parent, knob) -> knob.centerXProperty().bind(parent.xProperty().add(parent.getWidth()));
253  }
254 
255  static Position top() {
256  return (parent, knob) -> knob.centerYProperty().bind(parent.yProperty());
257  }
258 
259  static Position bottom() {
260  return (parent, knob) -> knob.centerYProperty().bind(parent.yProperty().add(parent.getHeight()));
261  }
262 
263  static Position xMiddle() {
264  return (parent, knob) -> knob.centerXProperty().bind(parent.xProperty().add(parent.getWidth() / 2));
265  }
266 
267  static Position yMiddle() {
268  return (parent, knob) -> knob.centerYProperty().bind(parent.yProperty().add(parent.getHeight() / 2));
269  }
270  }
271 
275  @FunctionalInterface
276  static interface Boundary {
277 
278  boolean isPointInBounds(double x, double y);
279  }
280 
285  static interface Draggable {
286 
287  void perform(PhysicalTag parent, MouseEvent event, Boundary b);
288 
289  static Draggable bottom() {
290  return (parent, event, bounds) -> {
291  if (!bounds.isPointInBounds(event.getX(), event.getY())) {
292  return;
293  }
294 
295  double deltaY = event.getY() - parent.getY();
296  if (deltaY > 0) {
297  parent.setHeight(deltaY);
298  }
299  };
300  }
301 
302  static Draggable top() {
303  return (parent, event, bounds) -> {
304  if (!bounds.isPointInBounds(event.getX(), event.getY())) {
305  return;
306  }
307 
308  double deltaY = parent.getY() + parent.getHeight() - event.getY();
309  if (deltaY < parent.getY() + parent.getHeight() && deltaY > 0) {
310  parent.setHeight(deltaY);
311  parent.setY(event.getY());
312  }
313  };
314  }
315 
316  static Draggable left() {
317  return (parent, event, bounds) -> {
318  if (!bounds.isPointInBounds(event.getX(), event.getY())) {
319  return;
320  }
321 
322  double deltaX = parent.getX() + parent.getWidth() - event.getX();
323  if (deltaX < parent.getX() + parent.getWidth() && deltaX > 0) {
324  parent.setWidth(deltaX);
325  parent.setX(event.getX());
326  }
327  };
328  }
329 
330  static Draggable right() {
331  return (parent, event, bounds) -> {
332  if (!bounds.isPointInBounds(event.getX(), event.getY())) {
333  return;
334  }
335 
336  double deltaX = event.getX() - parent.getX();
337  if (deltaX > 0) {
338  parent.setWidth(deltaX);
339  }
340  };
341  }
342  }
343 }
ContentViewerTag< ImageTagRegion > getContentViewerTag()
Definition: ImageTag.java:140
final ContentViewerTag< ImageTagRegion > appTag
Definition: ImageTag.java:55
ImageTag(ContentViewerTag< ImageTagRegion > contentViewerTag, ImageView image)
Definition: ImageTag.java:57
void subscribeToEditEvents(PropertyChangeListener listener)
Definition: ImageTag.java:131

Copyright © 2012-2018 Basis Technology. Generated on: Fri Jun 21 2019
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.