1
2
3
4
5
6
7
8
9
10
11 package com.timjohnstondev.unitconverter.view;
12
13 import java.awt.Color;
14 import java.awt.Component;
15 import java.awt.Font;
16 import java.awt.Point;
17 import java.awt.event.MouseAdapter;
18 import java.awt.event.MouseEvent;
19 import java.awt.event.MouseListener;
20 import java.awt.event.MouseMotionListener;
21 import java.util.ArrayList;
22 import java.util.List;
23 import java.util.ResourceBundle;
24 import javax.swing.DefaultListModel;
25 import javax.swing.JList;
26 import javax.swing.JPanel;
27 import javax.swing.ListModel;
28 import javax.swing.ListSelectionModel;
29 import javax.swing.UIManager;
30 import javax.swing.border.LineBorder;
31 import com.timjohnstondev.unitconverter.controller.LogicController;
32 import com.timjohnstondev.unitconverter.controller.ModelController;
33 import com.timjohnstondev.unitconverter.view.listener.FactorChangeEvent;
34 import com.timjohnstondev.unitconverter.view.listener.FactorChangeListener;
35 import com.timjohnstondev.unitconverter.view.listener.ReorderAdapter;
36
37
38
39
40
41
42 public class UnitPanel extends JPanel implements MouseMotionListener, MouseListener, Configuration
43 {
44 private LogicController logicController;
45 private ModelController modelController;
46 private JList propertyList;
47 private JList fromUnitList;
48 private JList toUnitList;
49 private MolarMassPanel molarMassPanel;
50 private List <FactorChangeListener> listeners;
51 private int cellHeight;
52 private String propertyListName;
53 private String fromUnitListName;
54 private String toUnitListName;
55 private String toolTipHtmlPrefix;
56 private String toolTipHtmlSuffix;
57 private boolean isMolUnitDivisor;
58 private boolean isPropertySelected;
59 private boolean isFromUnitSelected;
60 private boolean isToUnitSelected;
61
62
63
64
65
66
67 public UnitPanel(final ModelController newController)
68 {
69 logicController = new LogicController();
70 modelController = newController;
71 loadResources(ResourceBundle.getBundle("com.timjohnstondev.unitconverter.view.View"));
72 listeners = new ArrayList <FactorChangeListener>();
73
74 setLayout(null);
75
76 propertyList = new JList();
77 fromUnitList = new JList();
78 toUnitList = new JList();
79 molarMassPanel = new MolarMassPanel(this);
80
81 configureList(propertyList, propertyListName);
82 configureList(fromUnitList, fromUnitListName);
83 configureList(toUnitList, toUnitListName);
84 setPropertyListData();
85 setReorderListener(propertyList);
86
87 propertyList.setBounds(10, 0, 170, 500);
88 fromUnitList.setBounds(180, 0, 105, 500);
89 toUnitList.setBounds(285, 0, 105, 500);
90 molarMassPanel.setBounds(390, 0, 105, 150);
91 molarMassPanel.setVisible(false);
92
93 add(propertyList);
94 add(fromUnitList);
95 add(toUnitList);
96 add(molarMassPanel);
97
98 addMouseListener(this);
99 }
100
101 private void setReorderListener(final JList list)
102 {
103 final MouseAdapter listener = new ReorderAdapter(modelController, list);
104 list.addMouseListener(listener);
105 list.addMouseMotionListener(listener);
106 }
107
108 private void setPropertyListData()
109 {
110 final DefaultListModel model = new DefaultListModel();
111 for (String propertyName : modelController.getPropertyNames())
112 {
113 model.addElement(propertyName);
114 }
115 propertyList.setModel(model);
116 }
117
118 private void loadResources(final ResourceBundle resources)
119 {
120 cellHeight = Integer.parseInt(resources.getString("UnitPanel.cellHeight"));
121 propertyListName = resources.getString("UnitPanel.propertyListName");
122 fromUnitListName = resources.getString("UnitPanel.fromUnitListName");
123 toUnitListName = resources.getString("UnitPanel.toUnitListName");
124 toolTipHtmlPrefix = resources.getString("UnitPanel.toolTipHtmlPrefix");
125 toolTipHtmlSuffix = resources.getString("UnitPanel.toolTipHtmlSuffix");
126 }
127
128 private void configureList(final JList list, final String listName)
129 {
130 list.setName(listName);
131 list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
132 list.addMouseMotionListener(this);
133 list.addMouseListener(this);
134 list.setFixedCellHeight(cellHeight);
135 list.setBackground(getBackground());
136 }
137
138 @Override
139 public final void mouseDragged(final MouseEvent event)
140 {}
141
142 @Override
143 public final void mouseMoved(final MouseEvent event)
144 {
145 final String originatorName = event.getComponent().getName();
146 final Point point = new Point(event.getX(), event.getY());
147
148 if (originatorName.equals(propertyListName) && !isPropertySelected && hasChangedSelection(propertyList, point))
149 {
150 newPropertySelected(point);
151 }
152 else if (originatorName.equals(fromUnitListName) && !isFromUnitSelected && hasChangedSelection(fromUnitList, point))
153 {
154 newFromUnitSelected(point);
155 }
156 else if (originatorName.equals(toUnitListName) && !isToUnitSelected && hasChangedSelection(toUnitList, point))
157 {
158 newToUnitSelected(point);
159 }
160 }
161
162
163
164
165 final void updateMassToMoleLabels()
166 {
167 insertMol(toUnitList);
168 removeMol(fromUnitList);
169 setMolUnitPosition(fromUnitList);
170 }
171
172
173
174
175 final void updateMoleToMassLabels()
176 {
177 insertMol(fromUnitList);
178 removeMol(toUnitList);
179 setMolUnitPosition(fromUnitList);
180 }
181
182 private void setMolUnitPosition(final JList list)
183 {
184 isMolUnitDivisor = logicController.isMolUnitDivisor(list.getModel());
185 }
186
187
188
189
190 final boolean isMolUnitDivisor()
191 {
192 return isMolUnitDivisor;
193 }
194
195
196
197
198 final void clearMassMoleLabels()
199 {
200 removeMol(fromUnitList);
201 removeMol(toUnitList);
202 }
203
204 private void removeMol(final JList list)
205 {
206 final int selectedIndex = list.getSelectedIndex();
207 final ListModel model = list.getModel();
208 final String[] newData = new String[model.getSize()];
209 for (int i = 0; i < model.getSize(); i++)
210 {
211 newData[i] = removeMol(model.getElementAt(i));
212 }
213 list.setListData(newData);
214 list.setSelectedIndex(selectedIndex);
215 }
216
217 private void insertMol(final JList list)
218 {
219 logicController.insertMol(list, modelController.getUnitSeparator());
220 }
221
222 private void positionMolarMassPanel()
223 {
224 if (modelController.usesMoles(getSelectedValue(propertyList)))
225 {
226 final Point pointFromIndex = toUnitList.indexToLocation(toUnitList.getSelectedIndex());
227 final int y = getY(pointFromIndex.getY() + toUnitList.getY(), molarMassPanel);
228 molarMassPanel.setBounds(molarMassPanel.getX(), y, molarMassPanel.getWidth(), molarMassPanel.getHeight());
229 }
230 }
231
232 private void positionToUnitList()
233 {
234 final int y = getY(fromUnitList.getY(), toUnitList);
235 toUnitList.setBounds(toUnitList.getX(), y, toUnitList.getWidth(), toUnitList.getModel().getSize() * cellHeight);
236 }
237
238 private void populateToUnitList(final String propertyName, final String fromUnitName)
239 {
240 final boolean hasMoles = logicController.hasMoles(toUnitList.getModel());
241 toUnitList.setListData(modelController.getSymbols(propertyName, fromUnitName).toArray());
242 if (hasMoles)
243 {
244 insertMol(toUnitList);
245 }
246 }
247
248 private void showMolarMassPanel()
249 {
250 if (modelController.usesMoles(getSelectedValue(propertyList)))
251 {
252 molarMassPanel.showPanel(fromUnitList.getY());
253 }
254 else
255 {
256 molarMassPanel.hidePanel();
257 }
258 }
259
260 private void positionFromUnitList()
261 {
262 fromUnitList.setListData(modelController.getSymbols(getSelectedValue(propertyList)).toArray());
263 final Point pointFromIndex = propertyList.indexToLocation(propertyList.getSelectedIndex());
264 final int y = getY(pointFromIndex.getY(), fromUnitList);
265 fromUnitList.setBounds(fromUnitList.getX(), y, fromUnitList.getWidth(), fromUnitList.getModel().getSize()
266 * cellHeight);
267 }
268
269 private int getY(final double yOfEvent, final int componentHeight)
270 {
271 final int panelHeight = componentHeight;
272 final int frameHeight = getHeight();
273 final int y = (int) ((yOfEvent + panelHeight) < frameHeight ? yOfEvent : (frameHeight - panelHeight));
274 return y;
275 }
276
277 private int getY(final double yOfEvent, final JPanel panel)
278 {
279 return getY(yOfEvent, panel.getHeight());
280 }
281
282 private int getY(final double yOfEvent, final JList list)
283 {
284 final int listHeight = list.getModel().getSize() * cellHeight;
285 return getY(yOfEvent, listHeight);
286 }
287
288 private void clearFactor()
289 {
290 for (FactorChangeListener listener : listeners)
291 {
292 listener.factorChanged(new FactorChangeEvent(this, FactorChangeEvent.CLEAR_FACTOR_ACTION));
293 }
294 }
295
296
297
298
299
300 final void updateFields()
301 {
302 final String[] conversionParts = loadConversionFactorParts();
303 if (conversionParts != null)
304 {
305 final String conversion = modelController.getConversion(conversionParts[0], conversionParts[1],
306 conversionParts[2]);
307 for (FactorChangeListener listener : listeners)
308 {
309 listener.factorChanged(new FactorChangeEvent(this, conversion, molarMassPanel.getMolecularWeightCorrection()));
310 }
311 }
312 }
313
314 private String[] loadConversionFactorParts()
315 {
316 String[] conversionParts = null;
317 if (propertyList.getSelectedValue() != null && fromUnitList.getSelectedValue() != null
318 && toUnitList.getSelectedValue() != null)
319 {
320 conversionParts = new String[3];
321 conversionParts[0] = (String) propertyList.getSelectedValue();
322 conversionParts[1] = removeMol(fromUnitList.getSelectedValue());
323 conversionParts[2] = removeMol(toUnitList.getSelectedValue());
324 }
325 return conversionParts;
326 }
327
328 private String removeMol(final Object unit)
329 {
330 return ((String) unit).replaceAll(modelController.getUnitSeparator() + "mol", "");
331 }
332
333 private String getSelectedValue(final JList list)
334 {
335 return list.getSelectedValue().toString();
336 }
337
338 private void setSelectedIndex(final JList list, final Point point)
339 {
340 final int index = list.locationToIndex(point);
341 list.setSelectedIndex(index);
342 }
343
344 private boolean hasChangedSelection(final JList list, final Point point)
345 {
346 boolean changed = false;
347 final int index = list.locationToIndex(point);
348 if (index != list.getSelectedIndex())
349 {
350 list.setSelectedIndex(index);
351 changed = true;
352 }
353 return changed;
354 }
355
356
357
358
359
360
361 public final void addFactorChangeListener(final FactorChangeListener listener)
362 {
363 if (!listeners.contains(listener))
364 {
365 listeners.add(listener);
366 }
367 }
368
369 @Override
370 public final void setBackgroundColor(final Color color)
371 {
372 setBackground(color);
373 molarMassPanel.setBackground(color);
374 final Component[] children = getComponents();
375 for (Component child : children)
376 {
377 if (child instanceof JList)
378 {
379 ((JList) child).setBackground(color);
380 ((JList) child).setSelectionBackground(color.brighter());
381 }
382 }
383 UIManager.put("ToolTip.background", color.brighter());
384 UIManager.put("ToolTip.border", new LineBorder(color.darker(), 1, true));
385 updateUI();
386 }
387
388 @Override
389 public final void setForegroundColor(final Color color)
390 {
391 setForeground(color);
392 molarMassPanel.setForegroundColor(color);
393 final Component[] children = getComponents();
394 for (Component child : children)
395 {
396 if (child instanceof JList)
397 {
398 ((JList) child).setForeground(color);
399 ((JList) child).setSelectionForeground(color);
400 }
401 }
402 UIManager.put("ToolTip.foreground", color);
403 updateUI();
404 }
405
406 @Override
407 public final void setFontSize(final float fontSize)
408 {
409 final Font font = getFont().deriveFont(fontSize);
410 setFont(font);
411 molarMassPanel.setFontSize(fontSize);
412 final Component[] children = getComponents();
413 for (Component child : children)
414 {
415 child.setFont(font);
416 }
417 if (fontSize < 13)
418 {
419 toolTipHtmlPrefix = toolTipHtmlPrefix.replace('4', '3');
420 }
421 if (fontSize >= 13)
422 {
423 toolTipHtmlPrefix = toolTipHtmlPrefix.replace('3', '4');
424 }
425 }
426
427 public void mousePressed(final MouseEvent event)
428 {}
429
430 public final void mouseReleased(final MouseEvent event)
431 {
432 final String originatorName = event.getComponent().getName();
433 final Point point = new Point(event.getX(), event.getY());
434
435 if (originatorName == null)
436 {
437 isPropertySelected = false;
438 isFromUnitSelected = false;
439 isToUnitSelected = false;
440 }
441 else if (originatorName.equals(propertyListName))
442 {
443 newPropertySelected(point);
444 isPropertySelected = true;
445 isFromUnitSelected = false;
446 isToUnitSelected = false;
447 }
448 else if (originatorName.equals(fromUnitListName))
449 {
450 newFromUnitSelected(point);
451 isPropertySelected = true;
452 isFromUnitSelected = true;
453 isToUnitSelected = false;
454 }
455 else if (originatorName.equals(toUnitListName) && toUnitList.getModel().getSize() > 0)
456 {
457 newToUnitSelected(point);
458 isPropertySelected = true;
459 isFromUnitSelected = true;
460 isToUnitSelected = true;
461 }
462 }
463
464 public void mouseEntered(final MouseEvent event)
465 {}
466
467 public void mouseExited(final MouseEvent event)
468 {}
469
470 public void mouseClicked(final MouseEvent event)
471 {}
472
473 private void newPropertySelected(final Point point)
474 {
475 setSelectedIndex(propertyList, point);
476 clearFactor();
477 toUnitList.setListData(new Object[0]);
478 toUnitList.setToolTipText("");
479 positionFromUnitList();
480 showMolarMassPanel();
481 }
482
483 private void newFromUnitSelected(final Point point)
484 {
485 setSelectedIndex(fromUnitList, point);
486 clearFactor();
487 final String propertyName = getSelectedValue(propertyList);
488 final String fromUnitName = removeMol(getSelectedValue(fromUnitList));
489 fromUnitList.setToolTipText(toolTipHtmlPrefix + modelController.getUnitName(propertyName, fromUnitName)
490 + toolTipHtmlSuffix);
491 populateToUnitList(propertyName, fromUnitName);
492 positionToUnitList();
493 }
494
495 private void newToUnitSelected(final Point point)
496 {
497 setSelectedIndex(toUnitList, point);
498 updateFields();
499 final String propertyName = getSelectedValue(propertyList);
500 final String toUnitName = removeMol(getSelectedValue(toUnitList));
501 toUnitList.setToolTipText(toolTipHtmlPrefix + modelController.getUnitName(propertyName, toUnitName)
502 + toolTipHtmlSuffix);
503 positionMolarMassPanel();
504 }
505
506 }