Source code: com/tuneology/irremote/IRRemotePanel.java
1 /*
2 IRRemotePanel.java
3
4 Copyright (C) 2002 Fran Taylor
5
6 This library is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
10
11 This library 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 GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20 $Id: IRRemotePanel.java,v 1.2 2002/11/05 08:32:59 xnarf Exp $
21
22 */
23
24 package com.tuneology.irremote;
25
26 import java.awt.*;
27 import java.awt.event.*;
28 import java.io.*;
29 import java.util.*;
30 import javax.swing.*;
31 import javax.swing.table.*;
32 import javax.swing.event.*;
33
34 /**
35 * This class represents a Swing component used to edit IR bindings.
36 *
37 * @version $Id: IRRemotePanel.java,v 1.2 2002/11/05 08:32:59 xnarf Exp $
38 *
39 * @author Fran Taylor
40 */
41 public class IRRemotePanel extends Box {
42 /**
43 *
44 * @param l the IR component to be configured.
45 */
46 public IRRemotePanel(IRComponent l) {
47 super(BoxLayout.Y_AXIS);
48 ir = l;
49 /* internally here we have two arrays:
50 an array of strings, each representing a possible IR command
51 an array of actions, each representing a possible action to take
52 The IR command list is not editable.
53 The action is editable via the ActionEditor class
54 */
55 commands = l.getCommands();
56 actions = new AbstractAction[commands.length];
57 prevActions = new AbstractAction[commands.length];
58
59 add(new JLabel(getResourceString("irBoxLabel")));
60 Vector irListeners = ir.getListeners();
61 if (!irListeners.contains(nullAction))
62 irListeners.add(nullAction);
63 /* labels is a list of strings, one for each event, containing ite name */
64 String[] labels = new String[irListeners.size()];
65 for(int i = 0; i < irListeners.size(); i++)
66 labels[i] = (String) ((AbstractAction) irListeners.get(i)).getValue(IRComponent.IR_COMMAND);
67 /* instantiate our table with combo box for selecting an action */
68 actionEditor = new ActionEditor(labels, irListeners);
69 ActionRenderer actionRenderer = new ActionRenderer();
70 tbl = new JTable(new AbstractTableModel() {
71 public String getColumnName(int col) { return getResourceString((col == 0) ? "commandLabel" : "actionLabel"); }
72 public int getColumnCount() { return 2; }
73 public int getRowCount() { return commands.length; }
74 public boolean isCellEditable(int row, int col) { return (col == 1) ? true : false; }
75 /**
76 * These two methods pass data to and receive edited data from
77 * the ActionEditor combo box class.
78 */
79 public Object getValueAt(int row, int column) {
80 return (column == 0) ? ((Object) commands[row]) : ((Object) getAction(row) ); }
81 public void setValueAt(Object val, int row, int col) {
82 if (col == 1) {
83 /* hang onto the previous value */
84 if (prevActions[row] == null)
85 prevActions[row] = actions[row];
86 actions[row] = (AbstractAction) val;
87 }
88 }
89 public Class getColumnClass(int col) { return (col == 0) ? String.class : AbstractAction.class; }
90 } );
91 tbl.setDefaultRenderer(AbstractAction.class, actionRenderer);
92 tbl.setDefaultEditor(AbstractAction.class, actionEditor);
93 tbl.setRowHeight(18);
94 JScrollPane jsp = new JScrollPane(tbl);
95 jsp.setPreferredSize(new Dimension(325, 200));
96 jsp.setColumnHeaderView(tbl.getTableHeader());
97 add(jsp);
98 revert();
99 }
100 /**
101 * Set the user interface to reflect the current state.
102 */
103 public void revert() {
104 Vector v = ir.getListeners();
105 for(int i = 0; i < v.size(); i++) {
106 AbstractAction act = ((AbstractAction) v.get(i));
107 String bindings = (String) act.getValue(IRComponent.IR_BINDING);
108 String action = (String) act.getValue(IRComponent.IR_COMMAND);
109 if (bindings != null) {
110 StringTokenizer tok = new StringTokenizer(bindings, ",");
111 while(tok.hasMoreTokens()) {
112 String binding = tok.nextToken();
113 for(int j = 0; j < commands.length; j++) {
114 if (commands[j].equals(binding)) {
115 actions[j] = act;
116 break;
117 }
118 }
119 }
120 }
121 }
122 }
123 /**
124 * Set the current state to reflect the user interface.
125 */
126 public void update() {
127 actionEditor.stopCellEditing();
128 for(int i = 0; i < actions.length; i++) {
129 /* edit this event out of the previous listener's list */
130 AbstractAction prevListener = prevActions[i];
131 if ((prevListener != null) & (prevListener != nullAction)) {
132 String cmd = (String) prevListener.getValue(IRComponent.IR_COMMAND);
133 String events = (String) prevListener.getValue(IRComponent.IR_BINDING);
134 /* we have to remove a listener before we can edit its binding list */
135 ir.removeActionListener(prevListener);
136 StringTokenizer tok = new StringTokenizer((events == null) ? "" : events, ",");
137 events = "";
138 while(tok.hasMoreTokens()) {
139 String str = tok.nextToken();
140 /* put all events except this one back in the string */
141 if (!str.equals(commands[i])) {
142 /* add a comma if necessary */
143 if (!events.equals("")) events += ",";
144 events += str;
145 } else {
146 }
147 }
148 prevListener.putValue(IRComponent.IR_BINDING, events);
149 ir.addActionListener(prevListener);
150 }
151 /* add this event to the new listener's list */
152 AbstractAction listener = actions[i];
153 if ((listener != null) & (listener != nullAction)) {
154 String cmd = (String) listener.getValue(IRComponent.IR_COMMAND);
155 String events = (String) listener.getValue(IRComponent.IR_BINDING);
156 /* we have to remove a listener before we can edit its binding list */
157 ir.removeActionListener(listener);
158 StringTokenizer tok = new StringTokenizer((events == null) ? "" : events);
159 events = commands[i];
160 while(tok.hasMoreTokens()) {
161 String str = tok.nextToken();
162 /* it's always necessary to add a comma */
163 if (!str.equals(commands[i]))
164 events += "," + str;
165 }
166 listener.putValue(IRComponent.IR_BINDING, events);
167 ir.addActionListener(listener);
168 }
169 }
170 }
171 /**
172 * Add a listener callback to the remote control.
173 *
174 * @param listener
175 */
176 public void addActionListener(AbstractAction listener) {
177 String action = (String) listener.getValue(IRComponent.IR_COMMAND);
178 String cmd = (String) listener.getValue(IRComponent.IR_BINDING);
179 ir.addActionListener(listener);
180 for(int i = 0; i < actions.length; i++) if (commands[i].equals(cmd)) actions[i] = listener;
181 }
182 /**
183 * Remove a listener callback from the remote control.
184 *
185 * @param listener
186 */
187 public void removeActionListener(AbstractAction listener) {
188 String action = (String) listener.getValue(IRComponent.IR_COMMAND);
189 String cmd = (String) listener.getValue(IRComponent.IR_BINDING);
190 ir.removeActionListener(listener);
191 for(int i = 0; i < actions.length; i++) if (commands[i].equals(cmd)) actions[i] = null;
192 }
193 /**
194 * Returns an array of all the defined IR actions.
195 *
196 * @return An array of all the defined IR actions.
197 */
198 public AbstractAction[] getActions() { return actions; }
199
200 /**
201 * Returns an array of all the possible IR commands.
202 *
203 * @return An array of all the possible IR commands.
204 */
205 public String[] getCommands() { return commands; }
206
207 private class ActionRenderer extends JLabel implements TableCellRenderer {
208 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
209 boolean hasFocus, int row, int column) {
210 AbstractAction act = (AbstractAction) value;
211 String txt = "";
212 switch(column) {
213 case 0:
214 return new JLabel(commands[row]);
215 case 1:
216 if (act != null)
217 return new JLabel((String) act.getValue(IRComponent.IR_COMMAND));
218 else
219 return new JLabel("");
220 default:
221 return null;
222 }
223 }
224 }
225 /**
226 * This is the combo box embedded in the IR command mapping table.
227 */
228 private class ActionEditor extends JComboBox implements TableCellEditor {
229 public ActionEditor(String[] labels, Vector values) {
230 super(labels);
231 this.values = values;
232 listeners = new Vector();
233 }
234 public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int col) {
235 // set the combo box value for the action
236 AbstractAction act = (AbstractAction) value;
237 if (act != null) {
238 String str = (String) act.getValue(IRComponent.IR_COMMAND);
239 setSelectedItem(str);
240 table.setRowSelectionInterval(row, row);
241 table.setColumnSelectionInterval(col, col);
242 origValue = act;
243 } else {
244 System.out.println("getTableCellEditorComponent: value at (" + row + ", " + col + ") is null");
245 }
246 return this;
247 }
248 public Object getCellEditorValue() {
249 int i = getSelectedIndex();
250 return ((i == -1) ? nullAction : (AbstractAction) values.get(i));
251 }
252 public boolean isCellEditable(EventObject eo) { return true; }
253 public boolean shouldSelectCell(EventObject eo) { return true; }
254 public boolean stopCellEditing() { fireEditingStopped(); return true; }
255 public void cancelCellEditing() { fireEditingCanceled(); }
256 public void addCellEditorListener(CellEditorListener cel) { listeners.add(cel); }
257 public void removeCellEditorListener(CellEditorListener cel) { listeners.remove(cel); }
258 protected void fireEditingStopped() {
259 ChangeEvent ce = new ChangeEvent(this);
260 for(int i = 0; i < listeners.size(); i++)
261 ((CellEditorListener)listeners.get(i)).editingStopped(ce);
262 }
263 protected void fireEditingCanceled() {
264 setSelectedItem(origValue);
265 ChangeEvent ce = new ChangeEvent(this);
266 for(int i = 0; i < listeners.size(); i++)
267 ((CellEditorListener)listeners.get(i)).editingCanceled(ce);
268 }
269 private Vector values;
270 private Vector listeners;
271 private AbstractAction origValue;
272 }
273 private AbstractAction getAction(int i) {
274 AbstractAction a = actions[i];
275 return (a == null) ? nullAction : a;
276 }
277 private String getUIAction(int i) {
278 AbstractAction a = actions[i];
279 if ((a == null) || (a == nullAction)) return null;
280 return (String) a.getValue(IRComponent.IR_COMMAND);
281 }
282 private static String getResourceString(String nm) {
283 String str;
284 try {
285 str = resources.getString(nm);
286 } catch (MissingResourceException mre) {
287 str = null;
288 }
289 return str;
290 }
291 private JTable tbl;
292 private IRComponent ir;
293 private String[] commands;
294 private AbstractAction[] prevActions;
295 private AbstractAction[] actions;
296 private ActionEditor actionEditor;
297 private static AbstractAction nullAction;
298 private static ResourceBundle resources;
299 static {
300 try {
301 resources = ResourceBundle.getBundle("resources.IRRemotePanel");
302 } catch (MissingResourceException e) {
303 System.err.println("Can't find resource");
304 System.exit(1);
305 }
306 nullAction = new AbstractAction() {
307 public void actionPerformed(ActionEvent e) { }
308 public String toString() { return getResourceString("noActionString"); }
309 };
310 nullAction.putValue(IRComponent.IR_COMMAND, getResourceString("noActionString"));
311 }
312 }
313 /*
314 Local Variables:
315 mode:java
316 indent-tabs-mode:nil
317 c-basic-offset:4
318 c-indent-level:4
319 c-continued-statement-offset:4
320 c-brace-offset:-4
321 c-brace-imaginary-offset:-4
322 c-argdecl-indent:0
323 c-label-offset:0
324 End:
325 */