View Javadoc

1   /*
2    * Copyright (c) 2003-2008 by Cosylab d. d.
3    *
4    * This file is part of CosyBeans-Common.
5    *
6    * CosyBeans-Common is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU General Public License as published by
8    * the Free Software Foundation, either version 3 of the License, or
9    * (at your option) any later version.
10   *
11   * CosyBeans-Common is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU General Public License for more details.
15   *
16   * You should have received a copy of the GNU General Public License
17   * along with CosyBeans-Common.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  
20  package com.cosylab.gui.components;
21  
22  import java.awt.BorderLayout;
23  import java.awt.Color;
24  import java.awt.Dimension;
25  import java.awt.event.MouseAdapter;
26  import java.awt.event.MouseEvent;
27  import java.awt.event.ComponentAdapter;
28  
29  
30  import java.awt.Container;
31  import java.awt.event.ComponentEvent;
32  
33  import javax.swing.JFrame;
34  import javax.swing.JLabel;
35  import javax.swing.SwingUtilities;
36  
37  
38  import com.cosylab.gui.components.ledder.BitDescriptor;
39  import com.cosylab.gui.components.ledder.BitmaskField;
40  import com.cosylab.gui.components.ledder.Led;
41  import com.cosylab.gui.components.util.ColorHelper;
42  
43  
44  
45  public class Ledder extends javax.swing.JPanel{
46  
47  	private static final long serialVersionUID = 1L;
48  	/**
49  	 * @param
50  	 */
51  	private int columns=0;
52  	private BitmaskField bitmask;
53  	private boolean isEditable=false;
54  	private Led[] leds=null;
55  	private boolean reverseLedsOrder=false;
56  	private static JLabel maskNotSet;
57  	public Ledder(){
58  		super();
59  		setBackground(ColorHelper.getCosyControl());
60  		addComponentListener(new ResizeListener());
61  	
62  		maskNotSet = new JLabel(" < Ledder is empty, mask is 0 >");
63  		maskNotSet.setHorizontalAlignment(JLabel.CENTER);
64  	
65  	}
66  	
67  	public static void main(String[] args) {
68  		final Ledder ledder = new Ledder();
69  		final long mask= Integer.MAX_VALUE;
70  	
71  		//ledder.setColumns(5);
72  		BitDescriptor bd= new BitDescriptor() {
73  			
74  			public Color getColorWhenOff(int index) {
75  				return Color.YELLOW;
76  			}
77  		
78  			public Color getColorWhenOn(int index) {
79  				return Color.BLACK;
80  			}
81  		
82  			public String getDescription(int index) {
83  				return "D"+index;
84  			}
85  		
86  		};
87  		ledder.setEditable(true);
88  		ledder.setBits(2);
89  		ledder.setMask(mask);
90  		ledder.setModel(bd);
91  /*		ledder.setReverseLedsOrder(false);
92  		for(int i=0; i<ledder.leds.length; i++){
93  			ledder.leds[i].setText("Test"+i);
94  		}
95  */	
96  		
97  		/*
98  		ledder.addPropertyChangeListener("bits", new PropertyChangeListener() {
99  			public void propertyChange(PropertyChangeEvent evt) {
100 				System.out.println("BITS "+evt.getNewValue()+ " "+ledder.getBits());
101 			}
102 		});
103 		*/
104 		
105 		JFrame f = new JFrame();            
106 		f.setSize(new Dimension(200,200));
107 	
108 	
109 		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
110 		Container con = f.getContentPane();
111 		con.add(ledder);
112 		f.setVisible(true);
113 		/*
114 		new Thread() {
115 			public void run() {
116 				while(true) {
117 					ledder.setBits((long)(Math.random()*mask));
118 					try {
119 						sleep(100);
120 					} catch (Exception e) {
121 						// TODO: handle exception
122 					}
123 				} 
124 			};
125 		}.start();
126 		*/
127 	}
128 	
129 	
130 	private final class ResizeListener extends ComponentAdapter {
131 		/**
132 		 * @param e ComponentEvent
133 		 * @see java.awt.event.ComponentListener#componentResized(ComponentEvent)
134 		 */
135 		public final void componentResized(ComponentEvent e) {
136 	
137 		
138 			if(Ledder.this.isMaskSet())
139 				Ledder.this.updateLayout();
140 			else{
141 				Ledder.this.updateNoMaskLayout();
142 				
143 			}
144 		}
145 	}
146 
147 	
148 
149 	private BitmaskField getBitmask() {
150 		if (bitmask == null) {
151 			bitmask = new BitmaskField();
152 		}
153 
154 		return bitmask;
155 	}
156 
157 	/**
158 	 * returns if ledder is editable
159 	 * @return
160 	 */
161 	public  boolean isEditable(){
162 	    return isEditable;
163 	}
164 	
165 	/**
166 	 * sets the ledder to editable mode (user can by clicking sets the new value...)
167 	 * @param editable
168 	 */
169 	public void setEditable(boolean editable){
170 	    isEditable=editable;
171 	    // TODO invoke in runnable
172 	   SwingUtilities.invokeLater(new Runnable(){
173 		   public void run(){
174 			   if(leds != null){
175 	    			for(int i=0; i<leds.length;i++){
176 	    				leds[i].setEditable(isEditable);
177 	    			}
178 			   }
179 		   }
180 	   });
181 	    
182 	}
183 	
184 	
185 	
186 	
187 	
188 	/**
189 	 * The binary representation of returned long value defines the status of the leds.
190 	 * Example:
191 	 * long = 11 -> binary = 1011
192 	 *                      leds     = XX0X
193 	 * 
194 	 * The last value in binary representation sets ON/OFF the first led, etc.
195 	 *  
196 	 * @return long 
197 	 */
198 	public long getBits() {
199 		return getBitmask().getBits();
200 	}
201 
202 	public long getBitCount(){
203 		return getBitmask().getBitCount();
204 	}
205 	
206 	/**
207 	 * @return long
208 	 */
209 	public long getMask() {
210 		return getBitmask().getMask();
211 	}
212 
213 	/**
214 	 * @return Dimension
215 	 */
216 	public Dimension getMinimumSize() {
217 		return getLayout().minimumLayoutSize(this);
218 	}
219 
220 	/**
221 	 * @return BitDescriptor
222 	 */
223 	public BitDescriptor getModel() {
224 		return getBitmask().getBitDescriptor();
225 	}
226 
227 	/**
228 	 * @param value long
229 	 */
230 	public synchronized void setBits(long value) {
231 			long oldBits = getBits();
232 			getBitmask().setBits(value);
233 			firePropertyChange("bits", oldBits, value);
234 			updateLeds();
235 	}
236 
237 	
238 	private void _updateLeds(){
239 		
240 		long l = getBitmask().getCompactValue();
241 			if(leds != null){
242 				for(int i=0; i<leds.length; i++) {
243 					leds[i].setOn( (l & 1) == 1);
244 					l >>= 1;
245 				}
246 			}
247 	}
248 	
249 	private void updateLeds(){
250 		SwingUtilities.invokeLater(new Runnable(){
251 			public void run(){
252 				_updateLeds();
253 	
254 			}});		
255 	}
256 	
257 
258 	/**
259 	 * @param value long
260 	 */
261 	public void setMask(long value) {
262 		long currentValue = getBitmask().getMask();
263 		
264 		if (currentValue == value) {
265 			return;
266 		}
267 		
268 		else if(value== 0){
269 			updateNoMaskLayout();
270 			getBitmask().setMask(value);
271 			firePropertyChange("mask", currentValue, value);
272 			
273 		}
274 		else{
275 		
276 			getBitmask().setMask(value);
277 			firePropertyChange("mask", currentValue, value);
278 			setLeds();
279 			//the isEditable value has now an value
280 			setEditable(isEditable);
281 			updateLayout();
282 		}
283 	}
284 
285 	/**
286 	 * @param model BitDescriptor
287 	 */
288 	public void setModel(BitDescriptor model) {
289 
290 		getBitmask().setBitDescriptor(model);
291 		setLeds();
292 		
293 	}
294 
295 	/**
296 	 * Sets the number of culmns. Value larger than 0 indicates, the component 
297 	 * should display leds in specified number of columns. Value of 0 indicates
298 	 * no preference and the component will distribute the leds in an optimal 
299 	 * way.
300 	 * 
301 	 * @param numberOfColumns int
302 	 */
303 	public void setColumns(int col) {
304 		columns=col;
305 	}
306 	
307 	public int getColumns() {
308 		return columns;
309 	}
310 	
311 	private boolean isMaskSet() {
312 		return getMask()>0;
313 	}
314 	
315 	public boolean isReverseLedsOrder() {
316 		return reverseLedsOrder;
317 	}
318 	
319 	public void setReverseLedsOrder(boolean b) {
320 		if(b == reverseLedsOrder) {
321 			return;
322 		}
323 			
324 		boolean oldValue = reverseLedsOrder;	
325 		reverseLedsOrder = b;
326 		firePropertyChange("reverseLedsOrder", oldValue, b);
327 		setLeds();
328 	}
329 	
330 	
331 	private void updateLayout(){
332 		SwingUtilities.invokeLater(new Runnable(){
333 			public void run(){
334 				
335 				_updateLayout();
336 				_updateLeds();
337 		}});
338 
339 	}
340 
341 	private void _updateLayout(){
342 		
343 		if(getColumns()> 0){
344 			if(leds != null){
345 				setLayout(new java.awt.GridLayout(0,columns));
346 			}
347 		}	
348 	
349 		else{
350 			if(leds != null){
351 				int maxw = 0;
352 				int maxh = 0;
353 				for (int i = 0; i < leds.length; i++) {
354 				
355 					Dimension d = leds[i].getPreferredSize();
356 					maxw = Math.max(maxw, d.width);
357 					maxh = Math.max(maxh, d.height);
358 				}
359 				int nCols = (int)Math.ceil(1.0*getWidth() / maxw);
360 				nCols = Math.max(1, nCols);
361 				int nRows = leds.length / nCols +1;
362 				setLayout(new java.awt.GridLayout(nRows, nCols));
363 		
364 				repaint();
365 				doLayout();
366 			}
367 		}
368 	}
369 	
370 	
371 	private void _updateNoMaskLayout(){
372 		
373 				removeAll();
374 				setLayout(new BorderLayout());
375 				add(maskNotSet);
376 				doLayout();
377 				repaint();
378 				
379 		
380 	}
381 	
382 	private void updateNoMaskLayout(){
383 		SwingUtilities.invokeLater(new Runnable(){
384 			public void run(){
385 				_updateNoMaskLayout();
386 			}
387 		});
388 	} 
389 	
390 	private void checkBits(int ledindex){
391 	    long bits = getBits();
392 	    boolean isLedAllreadyOn=false;
393 	    for(int i=0;i<ledindex;i++){
394 	        bits  =bits >> 1;
395 	    }
396 	    if(bits%2==1){
397 	        isLedAllreadyOn = true;
398 	    }
399 	  
400 	    if(isLedAllreadyOn==leds[ledindex].isOn()){
401 	        //do nothing...
402 	    }else  {
403 	        /*
404 	         * setBits to new value,depending on bit(led) , that has changed... 1 or 0!!!
405 	         */
406 	        long addValueToBits=1;
407 	        long oldValue=getBits();
408 	        for (int i=0;i<ledindex;i++){
409 	               addValueToBits*=2;
410         	}
411 	        if(isLedAllreadyOn){
412 	            getBitmask().setBits(getBits()-addValueToBits);
413 	        }else{
414 	            getBitmask().setBits(getBits()+addValueToBits);
415 	        }
416 	        firePropertyChange("bits",oldValue,getBits());
417 	    }
418 	    
419 	}
420 	
421 	private void _updateBits(){
422 		
423 	    if(isEditable){
424             for(int i=0;i<leds.length;i++){
425             	checkBits(i);
426             }
427         }
428 
429 	}
430 	
431 
432 	
433 	private void updateBits() {
434 		//Thread.dumpStack();
435 		SwingUtilities.invokeLater(new Runnable() {
436 			public void run() {
437 				_updateBits();
438 			}
439 		});
440 	}
441 
442 	private void setLeds(){
443 		SwingUtilities.invokeLater(new Runnable(){
444 			public void run(){
445 				_setLeds();
446 			}
447 		});
448 	}
449 		
450 	private void _setLeds(){
451 		BitDescriptor lm = getModel();
452 		int ledCount = getBitmask().getBitCount();
453 		leds = new Led[ledCount];
454 		
455 		//creating and adding mouse listener to leds
456 		for(int i=0; i<ledCount; i++){
457 			leds[i]=new Led(
458 				lm.getDescription(i),
459 				lm.getColorWhenOn(i),
460 				lm.getColorWhenOff(i));
461 			leds[i].setEditable(isEditable);
462 			//adding mouse listener to all leds
463 			leds[i].addMouseListener(new MouseAdapter(){
464 			public void mouseClicked(MouseEvent e){
465 				if(SwingUtilities.isLeftMouseButton(e)){	
466 					updateBits();
467 				}
468 			}});
469 		}
470 		Led[] l= leds;
471 
472 		if(getComponents() != null)
473 			removeAll();
474 		
475 		//adding leds to ledder if not reverse order
476 		if(!isReverseLedsOrder()) {
477 			for(int i=0; i<l.length; i++){
478 				add(l[i]);
479 			}
480 		}else{
481 			for (int i = l.length-1; i >= 0; i--) 
482 				add(l[i]);
483 		}
484 	
485 		
486 		repaint();
487 		updateLayout();
488 	}
489 }
490