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.gui.component.sequence; 020 021import icy.common.listener.AcceptListener; 022import icy.gui.main.GlobalSequenceListener; 023import icy.gui.util.ComponentUtil; 024import icy.main.Icy; 025import icy.sequence.Sequence; 026import icy.util.StringUtil; 027 028import java.awt.Component; 029import java.awt.event.ActionEvent; 030import java.lang.ref.WeakReference; 031import java.util.ArrayList; 032import java.util.List; 033 034import javax.swing.DefaultComboBoxModel; 035import javax.swing.JComboBox; 036import javax.swing.JLabel; 037import javax.swing.JList; 038import javax.swing.ListCellRenderer; 039 040/** 041 * The sequence chooser is a component derived from JComboBox. <br> 042 * The combo auto refresh its content regarding to the sequence opened in ICY.<br> 043 * You can get it with getSequenceSelected() 044 * 045 * @author Fabrice de Chaumont & Stephane<br> 046 */ 047 048public class SequenceChooser extends JComboBox implements GlobalSequenceListener 049{ 050 public interface SequenceChooserListener 051 { 052 /** 053 * Called when the sequence chooser selection changed for specified sequence. 054 */ 055 public void sequenceChanged(Sequence sequence); 056 } 057 058 private class SequenceComboModel extends DefaultComboBoxModel 059 { 060 /** 061 * 062 */ 063 private static final long serialVersionUID = -1402261337279171323L; 064 065 /** 066 * cached items list 067 */ 068 final List<WeakReference<Sequence>> cachedList; 069 070 public SequenceComboModel() 071 { 072 super(); 073 074 cachedList = new ArrayList<WeakReference<Sequence>>(); 075 updateList(); 076 } 077 078 public void updateList() 079 { 080 // save selected item 081 final Object selected = getSelectedItem(); 082 083 final int oldSize = cachedList.size(); 084 085 cachedList.clear(); 086 087 // add null entry at first position 088 if (nullEntryName != null) 089 cachedList.add(new WeakReference<Sequence>(null)); 090 091 final List<Sequence> sequences = Icy.getMainInterface().getSequences(); 092 093 // add active sequence entry at second position 094 if ((sequences.size() > 0) && (activeSequence != null)) 095 cachedList.add(new WeakReference<Sequence>(activeSequence)); 096 097 // add others sequence 098 for (Sequence seq : sequences) 099 if ((filter == null) || filter.accept(seq)) 100 cachedList.add(new WeakReference<Sequence>(seq)); 101 102 final int newSize = cachedList.size(); 103 104 // some elements has been removed 105 if (newSize < oldSize) 106 fireIntervalRemoved(this, newSize, oldSize - 1); 107 // some elements has been added 108 else if (newSize > oldSize) 109 fireIntervalAdded(this, oldSize, newSize - 1); 110 111 // and some elements changed 112 fireContentsChanged(this, 0, newSize - 1); 113 114 // restore selected item 115 setSelectedItem(selected); 116 } 117 118 @Override 119 public Object getElementAt(int index) 120 { 121 return cachedList.get(index).get(); 122 } 123 124 @Override 125 public int getSize() 126 { 127 return cachedList.size(); 128 } 129 } 130 131 private static final long serialVersionUID = -6108163762809540675L; 132 133 public static final String SEQUENCE_SELECT_CMD = "sequence_select"; 134 135 /** 136 * var 137 */ 138 AcceptListener filter; 139 final String nullEntryName; 140 141 /** 142 * listeners 143 */ 144 protected final List<SequenceChooserListener> listeners; 145 146 /** 147 * internals 148 */ 149 protected WeakReference<Sequence> previousSelectedSequence; 150 protected final SequenceComboModel model; 151 protected final Sequence activeSequence; 152 153 /** 154 * Create a new Sequence chooser component (JComboBox for sequence selection). 155 * 156 * @param activeSequenceEntry 157 * If true the combobox will display an <i>Active Sequence</i> entry so when we select it 158 * the {@link #getSelectedSequence()} method returns the current active sequence. 159 * @param nullEntryName 160 * If this parameter is not <code>null</code> the combobox will display an extra entry 161 * with the given string to define <code>null</code> sequence selection so when this 162 * entry will be selected the {@link #getSelectedSequence()} will return <code>null</code>. 163 * @param nameMaxLength 164 * Maximum authorized length for the sequence name display in the combobox (extra 165 * characters are truncated).<br> 166 * That prevent the combobox to be resized to very large width. 167 */ 168 public SequenceChooser(final boolean activeSequenceEntry, final String nullEntryName, final int nameMaxLength) 169 { 170 super(); 171 172 this.nullEntryName = nullEntryName; 173 174 if (activeSequenceEntry) 175 activeSequence = new Sequence("active sequence"); 176 else 177 activeSequence = null; 178 179 model = new SequenceComboModel(); 180 setModel(model); 181 setRenderer(new ListCellRenderer() 182 { 183 @Override 184 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, 185 boolean cellHasFocus) 186 { 187 final JLabel result = new JLabel(); 188 189 if (value instanceof Sequence) 190 { 191 final String name = ((Sequence) value).getName(); 192 193 result.setText(StringUtil.limit(name, nameMaxLength)); 194 result.setToolTipText(name); 195 } 196 else if (value == null) 197 result.setText(nullEntryName); 198 199 return result; 200 } 201 }); 202 203 addActionListener(this); 204 205 // default 206 listeners = new ArrayList<SequenceChooserListener>(); 207 setActionCommand(SEQUENCE_SELECT_CMD); 208 previousSelectedSequence = new WeakReference<Sequence>(null); 209 setSelectedItem(null); 210 211 // fix height 212 ComponentUtil.setFixedHeight(this, 26); 213 } 214 215 /** 216 * @deprecated Use {@link #SequenceChooser(boolean, String, int)} instead. 217 */ 218 @SuppressWarnings("unused") 219 @Deprecated 220 public SequenceChooser(final int sequenceNameMaxLength, final boolean nullEntry, final boolean autoSelectIfNull, 221 final String nullEntryName) 222 { 223 this(false, nullEntry ? nullEntryName : null, sequenceNameMaxLength); 224 } 225 226 /** 227 * @deprecated Use {@link #SequenceChooser(boolean, String, int)} instead. 228 */ 229 @SuppressWarnings("unused") 230 @Deprecated 231 public SequenceChooser(int maxLength, boolean nullEntry, boolean autoSelectIfNull) 232 { 233 this(false, nullEntry ? "no sequence" : null, maxLength); 234 } 235 236 /** 237 * @deprecated Use {@link #SequenceChooser(boolean, String, int)} instead. 238 */ 239 @Deprecated 240 public SequenceChooser(int maxLength, boolean nullEntry) 241 { 242 this(false, nullEntry ? "no sequence" : null, maxLength); 243 } 244 245 /** 246 * Create a new Sequence chooser component (JComboBox for sequence selection). 247 * 248 * @param activeSequenceEntry 249 * If true the combobox will display an <i>Active Sequence</i> entry so when we select it 250 * the {@link #getSelectedSequence()} method returns the current active sequence. 251 * @param nullEntryName 252 * If this parameter is not <code>null</code> the combobox will display an extra entry 253 * with the given string to define <code>null</code> sequence selection so when this 254 * entry will be selected the {@link #getSelectedSequence()} will return <code>null</code>. 255 */ 256 public SequenceChooser(boolean activeSequenceEntry, String nullEntryName) 257 { 258 this(activeSequenceEntry, nullEntryName, 64); 259 } 260 261 /** 262 * @deprecated Use {@link #SequenceChooser(boolean, String, int)} instead. 263 */ 264 @Deprecated 265 public SequenceChooser(int nameMaxLength) 266 { 267 this(false, "no sequence", nameMaxLength); 268 } 269 270 /** 271 * Create a new Sequence chooser component (JComboBox for sequence selection). 272 */ 273 public SequenceChooser() 274 { 275 this(true, null, 64); 276 } 277 278 @Override 279 public void addNotify() 280 { 281 super.addNotify(); 282 283 Icy.getMainInterface().addGlobalSequenceListener(this); 284 } 285 286 @Override 287 public void removeNotify() 288 { 289 Icy.getMainInterface().removeGlobalSequenceListener(this); 290 291 super.removeNotify(); 292 } 293 294 /** 295 * @return the filter 296 */ 297 public AcceptListener getFilter() 298 { 299 return filter; 300 } 301 302 /** 303 * Set a filter for sequence display.<br> 304 * Only Sequence accepted by the filter will appear in the combobox. 305 */ 306 public void setFilter(AcceptListener filter) 307 { 308 if (this.filter != filter) 309 { 310 this.filter = filter; 311 model.updateList(); 312 } 313 } 314 315 /** 316 * @return current selected sequence. 317 */ 318 public Sequence getSelectedSequence() 319 { 320 final Sequence result = (Sequence) getSelectedItem(); 321 322 // special case for active sequence 323 if (result == activeSequence) 324 return Icy.getMainInterface().getActiveSequence(); 325 326 return result; 327 } 328 329 /** 330 * Select the <i>Active sequence</i> entry if enable. 331 */ 332 public void setActiveSequenceSelected() 333 { 334 if (activeSequence != null) 335 setSelectedItem(activeSequence); 336 } 337 338 /** 339 * @param sequence 340 * The sequence to select in the combo box 341 */ 342 public void setSelectedSequence(Sequence sequence) 343 { 344 if (sequence != getSelectedSequence()) 345 setSelectedItem(sequence); 346 } 347 348 /** 349 * @deprecated 350 * use {@link #setSelectedSequence(Sequence)} instead 351 */ 352 @Deprecated 353 public void setSequenceSelected(Sequence sequence) 354 { 355 setSelectedSequence(sequence); 356 } 357 358 // called when sequence selection has changed 359 private void sequenceChanged(Sequence sequence) 360 { 361 fireSequenceChanged(sequence); 362 } 363 364 private void fireSequenceChanged(Sequence sequence) 365 { 366 for (SequenceChooserListener listener : getListeners()) 367 listener.sequenceChanged(sequence); 368 } 369 370 public ArrayList<SequenceChooserListener> getListeners() 371 { 372 return new ArrayList<SequenceChooserListener>(listeners); 373 } 374 375 public void addListener(SequenceChooserListener listener) 376 { 377 if (!listeners.contains(listener)) 378 listeners.add(listener); 379 } 380 381 public void removeListener(SequenceChooserListener listener) 382 { 383 listeners.remove(listener); 384 } 385 386 @Override 387 public void actionPerformed(ActionEvent e) 388 { 389 final Sequence selected = getSelectedSequence(); 390 391 if (previousSelectedSequence.get() != selected) 392 { 393 previousSelectedSequence = new WeakReference<Sequence>(selected); 394 // sequence changed 395 sequenceChanged(selected); 396 } 397 } 398 399 @Override 400 public void sequenceOpened(Sequence sequence) 401 { 402 model.updateList(); 403 } 404 405 @Override 406 public void sequenceClosed(Sequence sequence) 407 { 408 model.updateList(); 409 } 410}