001/* 002 * Copyright 2010-2015 Institut Pasteur. 003 * 004 * This file is part of Icy. 005 * 006 * Icy is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * Icy is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with Icy. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package icy.common; 020 021import icy.common.listener.ChangeListener; 022import icy.system.thread.ThreadUtil; 023 024import java.util.ArrayList; 025import java.util.Collection; 026import java.util.LinkedHashMap; 027import java.util.List; 028 029/** 030 * Utility class to handle <code>Update</code> type event. 031 * 032 * @author stephane 033 */ 034public class UpdateEventHandler 035{ 036 ChangeListener parent; 037 038 /** 039 * dispatch in AWT dispatch thread 040 */ 041 private boolean awtDispatch; 042 /** 043 * internal update counter 044 */ 045 private int updateCnt; 046 /** 047 * internal pending change events 048 */ 049 private final LinkedHashMap<CollapsibleEvent, CollapsibleEvent> pendingChanges; 050 051 /** 052 * 053 */ 054 public UpdateEventHandler(ChangeListener parent, boolean awtDispatch) 055 { 056 super(); 057 058 this.parent = parent; 059 this.awtDispatch = awtDispatch; 060 061 updateCnt = 0; 062 pendingChanges = new LinkedHashMap<CollapsibleEvent, CollapsibleEvent>(); 063 } 064 065 /** 066 * 067 */ 068 public UpdateEventHandler(ChangeListener parent) 069 { 070 this(parent, false); 071 } 072 073 /** 074 * @return the awtDispatch 075 */ 076 public boolean isAwtDispatch() 077 { 078 return awtDispatch; 079 } 080 081 /** 082 * @param awtDispatch 083 * the awtDispatch to set 084 */ 085 public void setAwtDispatch(boolean awtDispatch) 086 { 087 this.awtDispatch = awtDispatch; 088 } 089 090 public Collection<CollapsibleEvent> getPendingChanges() 091 { 092 return pendingChanges.values(); 093 } 094 095 public void beginUpdate() 096 { 097 updateCnt++; 098 } 099 100 public void endUpdate() 101 { 102 updateCnt--; 103 if (updateCnt <= 0) 104 { 105 final List<CollapsibleEvent> events; 106 107 synchronized (pendingChanges) 108 { 109 events = new ArrayList<CollapsibleEvent>(pendingChanges.values()); 110 pendingChanges.clear(); 111 } 112 113 // dispatch all contained events (use copy to avoid concurrent changes) 114 for (CollapsibleEvent event : events) 115 dispatchOnChanged(event); 116 } 117 } 118 119 public boolean isUpdating() 120 { 121 return updateCnt > 0; 122 } 123 124 public boolean hasPendingChanges() 125 { 126 return !pendingChanges.isEmpty(); 127 } 128 129 protected void addPendingChange(CollapsibleEvent change) 130 { 131 final CollapsibleEvent previousChange; 132 133 // TODO: can take sometime (select all on many ROI) 134 // TODO: check how fast is it now... 135 synchronized (pendingChanges) 136 { 137 // search in pending changes if we have an equivalent change 138 previousChange = pendingChanges.get(change); 139 140 // not already existing ? --> just add the new change 141 if (previousChange == null) 142 pendingChanges.put(change, change); 143 } 144 145 // found an equivalent previous change ? --> collapse the new change into the old one 146 if (previousChange != null) 147 previousChange.collapse(change); 148 } 149 150 public void changed(CollapsibleEvent event) 151 { 152 if (isUpdating()) 153 addPendingChange(event); 154 else 155 dispatchOnChanged(event); 156 } 157 158 protected void dispatchOnChanged(CollapsibleEvent event) 159 { 160 final CollapsibleEvent e = event; 161 162 if (awtDispatch) 163 { 164 // dispatch on AWT Dispatch Thread now 165 ThreadUtil.invokeNow(new Runnable() 166 { 167 @Override 168 public void run() 169 { 170 parent.onChanged(e); 171 } 172 }); 173 } 174 else 175 parent.onChanged(e); 176 } 177}