19 package org.sleuthkit.autopsy.discovery;
21 import static java.awt.BorderLayout.CENTER;
22 import java.awt.Color;
23 import java.beans.PropertyChangeEvent;
24 import java.beans.PropertyChangeListener;
25 import java.util.EnumSet;
26 import java.util.List;
28 import java.util.logging.Level;
29 import org.apache.commons.lang.StringUtils;
30 import org.openide.util.NbBundle.Messages;
31 import org.openide.windows.WindowManager;
44 final class DiscoveryDialog
extends javax.swing.JDialog {
46 private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE,
47 Case.Events.DATA_SOURCE_ADDED, Case.Events.DATA_SOURCE_DELETED);
48 private static final long serialVersionUID = 1L;
49 private final static Logger logger = Logger.getLogger(DiscoveryDialog.class.getName());
50 private ImageFilterPanel imageFilterPanel = null;
51 private VideoFilterPanel videoFilterPanel = null;
52 private DocumentFilterPanel documentFilterPanel = null;
53 private static final Color SELECTED_COLOR =
new Color(216, 230, 242);
54 private static final Color UNSELECTED_COLOR =
new Color(240, 240, 240);
55 private SearchWorker searchWorker = null;
56 private static DiscoveryDialog discDialog;
57 private FileSearchData.FileType fileType = FileSearchData.FileType.IMAGE;
58 private final PropertyChangeListener listener;
65 static synchronized DiscoveryDialog getDiscoveryDialogInstance() {
66 if (discDialog == null) {
67 discDialog =
new DiscoveryDialog();
75 @Messages(
"DiscoveryDialog.name.text=Discovery")
76 private DiscoveryDialog() {
77 super(WindowManager.getDefault().getMainWindow(), Bundle.DiscoveryDialog_name_text(),
true);
79 listener =
new PropertyChangeListener() {
81 public void propertyChange(PropertyChangeEvent evt) {
82 if (evt.getPropertyName().equals(
"FilterError") && evt.getNewValue() != null) {
83 setValid(evt.getNewValue().toString());
87 for (GroupSortingAlgorithm groupSortAlgorithm : GroupSortingAlgorithm.values()) {
88 groupSortingComboBox.addItem(groupSortAlgorithm);
90 updateSearchSettings();
91 Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, this.
new CasePropertyChangeListener());
97 void updateSearchSettings() {
99 imageFilterPanel = null;
100 videoFilterPanel = null;
101 documentFilterPanel = null;
102 imageFilterPanel =
new ImageFilterPanel();
103 videoFilterPanel =
new VideoFilterPanel();
104 documentFilterPanel =
new DocumentFilterPanel();
105 imagesButton.setSelected(
true);
106 imagesButton.setEnabled(
false);
107 imagesButton.setBackground(SELECTED_COLOR);
108 imagesButton.setForeground(Color.BLACK);
109 videosButton.setSelected(
false);
110 videosButton.setEnabled(
true);
111 videosButton.setBackground(UNSELECTED_COLOR);
112 documentsButton.setSelected(
false);
113 documentsButton.setEnabled(
true);
114 documentsButton.setBackground(UNSELECTED_COLOR);
115 fileType = FileSearchData.FileType.IMAGE;
116 add(imageFilterPanel, CENTER);
117 imageFilterPanel.addPropertyChangeListener(listener);
126 private void updateComboBoxes() {
127 groupByCombobox.removeAllItems();
129 for (FileSearch.GroupingAttributeType type : FileSearch.GroupingAttributeType.getOptionsForGrouping()) {
130 addTypeToGroupByComboBox(type);
132 orderByCombobox.removeAllItems();
134 for (FileSorter.SortingMethod method : FileSorter.SortingMethod.getOptionsForOrdering()) {
135 if (method != SortingMethod.BY_FREQUENCY || CentralRepository.isEnabled()) {
136 orderByCombobox.addItem(method);
139 groupSortingComboBox.setSelectedIndex(0);
148 private void addTypeToGroupByComboBox(GroupingAttributeType type) {
151 if (!CentralRepository.isEnabled()) {
155 case OBJECT_DETECTED:
156 if (!imageFilterPanel.isObjectsFilterSupported()) {
160 case INTERESTING_ITEM_SET:
161 if (!imageFilterPanel.isInterestingItemsFilterSupported()) {
166 if (!imageFilterPanel.isHashSetFilterSupported()) {
173 groupByCombobox.addItem(type);
179 synchronized void validateDialog() {
182 if (imageFilterPanel != null) {
183 imageFilterPanel.validateFields();
187 if (videoFilterPanel != null) {
188 videoFilterPanel.validateFields();
192 if (documentFilterPanel != null) {
193 documentFilterPanel.validateFields();
206 @SuppressWarnings(
"unchecked")
208 private
void initComponents() {
210 javax.swing.JPanel toolBarPanel =
new javax.swing.JPanel();
211 imagesButton =
new javax.swing.JButton();
212 videosButton =
new javax.swing.JButton();
213 documentsButton =
new javax.swing.JButton();
214 javax.swing.JLabel step1Label =
new javax.swing.JLabel();
215 javax.swing.Box.Filler filler1 =
new javax.swing.Box.Filler(
new java.awt.Dimension(104, 0),
new java.awt.Dimension(104, 0),
new java.awt.Dimension(104, 32767));
216 javax.swing.JPanel displaySettingsPanel =
new javax.swing.JPanel();
217 searchButton =
new javax.swing.JButton();
218 errorLabel =
new javax.swing.JLabel();
219 javax.swing.JPanel sortingPanel =
new javax.swing.JPanel();
220 groupByCombobox =
new javax.swing.JComboBox<>();
221 orderByCombobox =
new javax.swing.JComboBox<>();
222 javax.swing.JLabel orderGroupsByLabel =
new javax.swing.JLabel();
223 javax.swing.JLabel orderByLabel =
new javax.swing.JLabel();
224 javax.swing.JLabel groupByLabel =
new javax.swing.JLabel();
225 groupSortingComboBox =
new javax.swing.JComboBox<>();
227 setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
228 setMinimumSize(
new java.awt.Dimension(600, 300));
229 setPreferredSize(
new java.awt.Dimension(1000, 650));
231 imagesButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/pictures-icon.png")));
232 org.openide.awt.Mnemonics.setLocalizedText(imagesButton,
org.openide.util.NbBundle.getMessage(DiscoveryDialog.class,
"DiscoveryDialog.imagesButton.text"));
233 imagesButton.setDisabledIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/pictures-icon.png")));
234 imagesButton.setFocusable(
false);
235 imagesButton.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
236 imagesButton.setMaximumSize(
new java.awt.Dimension(90, 43));
237 imagesButton.setMinimumSize(
new java.awt.Dimension(90, 43));
238 imagesButton.setPreferredSize(
new java.awt.Dimension(90, 43));
239 imagesButton.addActionListener(
new java.awt.event.ActionListener() {
240 public void actionPerformed(java.awt.event.ActionEvent evt) {
241 imagesButtonActionPerformed(evt);
245 videosButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/video-icon.png")));
246 org.openide.awt.Mnemonics.setLocalizedText(videosButton,
org.openide.util.NbBundle.getMessage(DiscoveryDialog.class,
"DiscoveryDialog.videosButton.text"));
247 videosButton.setDisabledIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/video-icon.png")));
248 videosButton.setDisabledSelectedIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/video-icon.png")));
249 videosButton.setFocusable(
false);
250 videosButton.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
251 videosButton.setMaximumSize(
new java.awt.Dimension(90, 43));
252 videosButton.setMinimumSize(
new java.awt.Dimension(90, 43));
253 videosButton.setPreferredSize(
new java.awt.Dimension(90, 43));
254 videosButton.addActionListener(
new java.awt.event.ActionListener() {
255 public void actionPerformed(java.awt.event.ActionEvent evt) {
256 videosButtonActionPerformed(evt);
260 documentsButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/documents-icon.png")));
261 org.openide.awt.Mnemonics.setLocalizedText(documentsButton,
org.openide.util.NbBundle.getMessage(DiscoveryDialog.class,
"DiscoveryDialog.documentsButton.text"));
262 documentsButton.setDisabledIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/documents-icon.png")));
263 documentsButton.setDisabledSelectedIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/documents-icon.png")));
264 documentsButton.setFocusable(
false);
265 documentsButton.addActionListener(
new java.awt.event.ActionListener() {
266 public void actionPerformed(java.awt.event.ActionEvent evt) {
267 documentsButtonActionPerformed(evt);
271 org.openide.awt.Mnemonics.setLocalizedText(step1Label,
org.openide.util.NbBundle.getMessage(DiscoveryDialog.class,
"DiscoveryDialog.step1Label.text"));
273 javax.swing.GroupLayout toolBarPanelLayout =
new javax.swing.GroupLayout(toolBarPanel);
274 toolBarPanel.setLayout(toolBarPanelLayout);
275 toolBarPanelLayout.setHorizontalGroup(
276 toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
277 .addGroup(toolBarPanelLayout.createSequentialGroup()
279 .addGroup(toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
280 .addGroup(toolBarPanelLayout.createSequentialGroup()
282 .addComponent(imagesButton, javax.swing.GroupLayout.PREFERRED_SIZE, 110, javax.swing.GroupLayout.PREFERRED_SIZE)
283 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
284 .addComponent(videosButton, javax.swing.GroupLayout.PREFERRED_SIZE, 110, javax.swing.GroupLayout.PREFERRED_SIZE)
285 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
286 .addComponent(documentsButton)
287 .addContainerGap(370, Short.MAX_VALUE))
288 .addGroup(toolBarPanelLayout.createSequentialGroup()
289 .addComponent(step1Label, javax.swing.GroupLayout.PREFERRED_SIZE, 243, javax.swing.GroupLayout.PREFERRED_SIZE)
290 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
291 .addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
292 .addContainerGap(391, Short.MAX_VALUE))))
294 toolBarPanelLayout.setVerticalGroup(
295 toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
296 .addGroup(toolBarPanelLayout.createSequentialGroup()
298 .addGroup(toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
299 .addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
300 .addComponent(step1Label))
302 .addGroup(toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
303 .addComponent(videosButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
304 .addComponent(imagesButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
305 .addComponent(documentsButton, javax.swing.GroupLayout.PREFERRED_SIZE, 43, javax.swing.GroupLayout.PREFERRED_SIZE))
309 getContentPane().add(toolBarPanel, java.awt.BorderLayout.PAGE_START);
311 org.openide.awt.Mnemonics.setLocalizedText(searchButton,
org.openide.util.NbBundle.getMessage(DiscoveryDialog.class,
"DiscoveryDialog.searchButton.text"));
312 searchButton.addActionListener(
new java.awt.event.ActionListener() {
313 public void actionPerformed(java.awt.event.ActionEvent evt) {
314 searchButtonActionPerformed(evt);
318 errorLabel.setForeground(
new java.awt.Color(255, 0, 0));
320 sortingPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(
org.openide.util.NbBundle.getMessage(DiscoveryDialog.class,
"DiscoveryDialog.sortingPanel.border.title")));
321 sortingPanel.setPreferredSize(
new java.awt.Dimension(345, 112));
323 org.openide.awt.Mnemonics.setLocalizedText(orderGroupsByLabel,
org.openide.util.NbBundle.getMessage(DiscoveryDialog.class,
"DiscoveryDialog.orderGroupsByLabel.text"));
325 org.openide.awt.Mnemonics.setLocalizedText(orderByLabel,
org.openide.util.NbBundle.getMessage(DiscoveryDialog.class,
"DiscoveryDialog.orderByLabel.text"));
327 org.openide.awt.Mnemonics.setLocalizedText(groupByLabel,
org.openide.util.NbBundle.getMessage(DiscoveryDialog.class,
"DiscoveryDialog.groupByLabel.text"));
329 javax.swing.GroupLayout sortingPanelLayout =
new javax.swing.GroupLayout(sortingPanel);
330 sortingPanel.setLayout(sortingPanelLayout);
331 sortingPanelLayout.setHorizontalGroup(
332 sortingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
333 .addGroup(sortingPanelLayout.createSequentialGroup()
335 .addGroup(sortingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
336 .addComponent(orderGroupsByLabel)
337 .addComponent(groupByLabel))
338 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
339 .addGroup(sortingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
340 .addComponent(groupSortingComboBox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
341 .addComponent(groupByCombobox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
342 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
343 .addComponent(orderByLabel)
344 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
345 .addComponent(orderByCombobox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
348 sortingPanelLayout.setVerticalGroup(
349 sortingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
350 .addGroup(sortingPanelLayout.createSequentialGroup()
352 .addGroup(sortingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
353 .addComponent(groupByCombobox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
354 .addComponent(groupByLabel)
355 .addComponent(orderByCombobox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
356 .addComponent(orderByLabel))
358 .addGroup(sortingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
359 .addComponent(groupSortingComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
360 .addComponent(orderGroupsByLabel))
364 javax.swing.GroupLayout displaySettingsPanelLayout =
new javax.swing.GroupLayout(displaySettingsPanel);
365 displaySettingsPanel.setLayout(displaySettingsPanelLayout);
366 displaySettingsPanelLayout.setHorizontalGroup(
367 displaySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
368 .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, displaySettingsPanelLayout.createSequentialGroup()
370 .addGroup(displaySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
371 .addComponent(sortingPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 741, Short.MAX_VALUE)
372 .addGroup(displaySettingsPanelLayout.createSequentialGroup()
373 .addComponent(errorLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 575, Short.MAX_VALUE)
375 .addComponent(searchButton, javax.swing.GroupLayout.PREFERRED_SIZE, 101, javax.swing.GroupLayout.PREFERRED_SIZE)))
378 displaySettingsPanelLayout.setVerticalGroup(
379 displaySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
380 .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, displaySettingsPanelLayout.createSequentialGroup()
382 .addComponent(sortingPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 89, javax.swing.GroupLayout.PREFERRED_SIZE)
384 .addGroup(displaySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
385 .addComponent(errorLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
386 .addComponent(searchButton))
390 getContentPane().add(displaySettingsPanel, java.awt.BorderLayout.PAGE_END);
395 private void imagesButtonActionPerformed(java.awt.event.ActionEvent evt) {
397 add(imageFilterPanel, CENTER);
398 imagesButton.setSelected(
true);
399 imagesButton.setEnabled(
false);
400 imagesButton.setBackground(SELECTED_COLOR);
401 imagesButton.setForeground(Color.BLACK);
402 videosButton.setSelected(
false);
403 videosButton.setEnabled(
true);
404 videosButton.setBackground(UNSELECTED_COLOR);
405 documentsButton.setSelected(
false);
406 documentsButton.setEnabled(
true);
407 documentsButton.setBackground(UNSELECTED_COLOR);
408 fileType = FileSearchData.FileType.IMAGE;
409 imageFilterPanel.addPropertyChangeListener(listener);
415 private void videosButtonActionPerformed(java.awt.event.ActionEvent evt) {
417 add(videoFilterPanel, CENTER);
418 imagesButton.setSelected(
false);
419 imagesButton.setEnabled(
true);
420 imagesButton.setBackground(UNSELECTED_COLOR);
421 videosButton.setSelected(
true);
422 videosButton.setEnabled(
false);
423 videosButton.setBackground(SELECTED_COLOR);
424 videosButton.setForeground(Color.BLACK);
425 documentsButton.setSelected(
false);
426 documentsButton.setEnabled(
true);
427 documentsButton.setBackground(UNSELECTED_COLOR);
428 videoFilterPanel.addPropertyChangeListener(listener);
429 fileType = FileSearchData.FileType.VIDEO;
435 private void documentsButtonActionPerformed(java.awt.event.ActionEvent evt) {
437 add(documentFilterPanel, CENTER);
438 documentsButton.setSelected(
true);
439 documentsButton.setEnabled(
false);
440 documentsButton.setBackground(SELECTED_COLOR);
441 documentsButton.setForeground(Color.BLACK);
442 videosButton.setSelected(
false);
443 videosButton.setEnabled(
true);
444 videosButton.setBackground(UNSELECTED_COLOR);
445 imagesButton.setSelected(
false);
446 imagesButton.setEnabled(
true);
447 imagesButton.setBackground(UNSELECTED_COLOR);
448 fileType = FileSearchData.FileType.DOCUMENTS;
449 documentFilterPanel.addPropertyChangeListener(listener);
458 private void removeAllPanels() {
459 if (imageFilterPanel != null) {
460 remove(imageFilterPanel);
461 imageFilterPanel.removePropertyChangeListener(listener);
463 if (documentFilterPanel != null) {
464 remove(documentFilterPanel);
465 documentFilterPanel.removePropertyChangeListener(listener);
467 if (videoFilterPanel != null) {
468 remove(videoFilterPanel);
469 videoFilterPanel.removePropertyChangeListener(listener);
473 private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {
475 final DiscoveryTopComponent tc = DiscoveryTopComponent.getTopComponent();
477 setValid(
"No Top Component Found");
480 if (tc.isOpened() ==
false) {
483 tc.resetTopComponent();
484 List<FileSearchFiltering.FileFilter> filters;
485 if (videosButton.isSelected()) {
486 filters = videoFilterPanel.getFilters();
487 }
else if (documentsButton.isSelected()) {
488 filters = documentFilterPanel.getFilters();
490 filters = imageFilterPanel.getFilters();
492 DiscoveryEventUtils.getDiscoveryEventBus().post(
new DiscoveryEventUtils.SearchStartedEvent(fileType));
495 FileSearch.AttributeType groupingAttr = groupByCombobox.getItemAt(groupByCombobox.getSelectedIndex()).getAttributeType();
496 FileGroup.GroupSortingAlgorithm groupSortAlgorithm = groupSortingComboBox.getItemAt(groupSortingComboBox.getSelectedIndex());
499 FileSorter.SortingMethod fileSort = (FileSorter.SortingMethod) orderByCombobox.getSelectedItem();
500 CentralRepository centralRepoDb = null;
501 if (CentralRepository.isEnabled()) {
503 centralRepoDb = CentralRepository.getInstance();
504 }
catch (CentralRepoException ex) {
505 centralRepoDb = null;
506 logger.log(Level.SEVERE,
"Error loading central repository database, no central repository options will be available for Discovery", ex);
509 searchWorker =
new SearchWorker(centralRepoDb, filters, groupingAttr, groupSortAlgorithm, fileSort);
510 searchWorker.execute();
517 public void dispose() {
524 void cancelSearch() {
525 if (searchWorker != null) {
526 searchWorker.cancel(
true);
536 private void setValid(String error) {
537 if (StringUtils.isBlank(error)) {
538 errorLabel.setText(
"");
539 searchButton.setEnabled(
true);
541 errorLabel.setText(error);
542 searchButton.setEnabled(
false);
548 private javax.swing.JButton documentsButton;
549 private javax.swing.JLabel errorLabel;
550 private javax.swing.JComboBox<GroupingAttributeType> groupByCombobox;
551 private javax.swing.JComboBox<GroupSortingAlgorithm> groupSortingComboBox;
552 private javax.swing.JButton imagesButton;
553 private javax.swing.JComboBox<SortingMethod> orderByCombobox;
554 private javax.swing.JButton searchButton;
555 private javax.swing.JButton videosButton;
565 @SuppressWarnings(
"fallthrough")
567 switch (
Case.
Events.valueOf(evt.getPropertyName())) {
569 if (evt.getNewValue() == null) {
575 case DATA_SOURCE_ADDED:
577 case DATA_SOURCE_DELETED:
578 updateSearchSettings();
void propertyChange(PropertyChangeEvent evt)