View Javadoc

1   /**
2    * The contents of this file are subject to the Mozilla Public License Version 1.1
3    * (the "License"); you may not use this file except in compliance with the License.
4    * You may obtain a copy of the License at http://www.mozilla.org/MPL/
5    * Software distributed under the License is distributed on an "AS IS" basis,
6    * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
7    * specific language governing rights and limitations under the License.
8    *
9    * The Original Code is ""  Description:
10   * ""
11   *
12   * The Initial Developer of the Original Code is University Health Network. Copyright (C)
13   * 2001.  All Rights Reserved.
14   *
15   * Contributor(s): ______________________________________.
16   *
17   * Alternatively, the contents of this file may be used under the terms of the
18   * GNU General Public License (the  "GPL"), in which case the provisions of the GPL are
19   * applicable instead of those above.  If you wish to allow use of your version of this
20   * file only under the terms of the GPL and not to allow others to use your version
21   * of this file under the MPL, indicate your decision by deleting  the provisions above
22   * and replace  them with the notice and other provisions required by the GPL License.
23   * If you do not delete the provisions above, a recipient may use your version of
24   * this file under either the MPL or the GPL.
25   */
26  package ca.uhn.hl7v2.testpanel.ui.editor;
27  
28  import java.awt.BorderLayout;
29  import java.awt.Color;
30  import java.awt.Component;
31  import java.awt.Dimension;
32  import java.awt.EventQueue;
33  import java.awt.Font;
34  import java.awt.Frame;
35  import java.awt.GridBagConstraints;
36  import java.awt.GridBagLayout;
37  import java.awt.Insets;
38  import java.awt.Point;
39  import java.awt.SystemColor;
40  import java.awt.Toolkit;
41  import java.awt.datatransfer.Clipboard;
42  import java.awt.datatransfer.StringSelection;
43  import java.awt.event.ActionEvent;
44  import java.awt.event.ActionListener;
45  import java.awt.event.MouseAdapter;
46  import java.awt.event.MouseEvent;
47  import java.beans.PropertyChangeEvent;
48  import java.beans.PropertyChangeListener;
49  import java.util.ArrayList;
50  import java.util.List;
51  
52  import javax.swing.Box;
53  import javax.swing.ButtonGroup;
54  import javax.swing.DefaultComboBoxModel;
55  import javax.swing.ImageIcon;
56  import javax.swing.JButton;
57  import javax.swing.JComboBox;
58  import javax.swing.JEditorPane;
59  import javax.swing.JLabel;
60  import javax.swing.JMenuItem;
61  import javax.swing.JPanel;
62  import javax.swing.JPopupMenu;
63  import javax.swing.JRadioButton;
64  import javax.swing.JScrollBar;
65  import javax.swing.JScrollPane;
66  import javax.swing.JSplitPane;
67  import javax.swing.JTabbedPane;
68  import javax.swing.JToggleButton;
69  import javax.swing.JToolBar;
70  import javax.swing.SwingConstants;
71  import javax.swing.SwingUtilities;
72  import javax.swing.event.CaretEvent;
73  import javax.swing.event.CaretListener;
74  import javax.swing.event.DocumentEvent;
75  import javax.swing.event.DocumentListener;
76  import javax.swing.text.Highlighter;
77  
78  import jsyntaxpane.DefaultSyntaxKit;
79  
80  import org.apache.commons.lang.StringUtils;
81  import org.apache.commons.lang.Validate;
82  import org.slf4j.Logger;
83  import org.slf4j.LoggerFactory;
84  
85  import ca.uhn.hl7v2.conf.ProfileException;
86  import ca.uhn.hl7v2.testpanel.controller.Controller;
87  import ca.uhn.hl7v2.testpanel.controller.Prefs;
88  import ca.uhn.hl7v2.testpanel.model.conf.ProfileFileList;
89  import ca.uhn.hl7v2.testpanel.model.conf.ProfileGroup;
90  import ca.uhn.hl7v2.testpanel.model.conn.OutboundConnection;
91  import ca.uhn.hl7v2.testpanel.model.conn.OutboundConnectionList;
92  import ca.uhn.hl7v2.testpanel.model.msg.Hl7V2MessageCollection;
93  import ca.uhn.hl7v2.testpanel.ui.ActivityTable;
94  import ca.uhn.hl7v2.testpanel.ui.BaseMainPanel;
95  import ca.uhn.hl7v2.testpanel.ui.Er7SyntaxKit;
96  import ca.uhn.hl7v2.testpanel.ui.HoverButtonMouseAdapter;
97  import ca.uhn.hl7v2.testpanel.ui.IDestroyable;
98  import ca.uhn.hl7v2.testpanel.ui.ShowEnum;
99  import ca.uhn.hl7v2.testpanel.ui.v2tree.Hl7V2MessageTree;
100 import ca.uhn.hl7v2.testpanel.ui.v2tree.Hl7V2MessageTree.IWorkingListener;
101 import ca.uhn.hl7v2.testpanel.util.IOkCancelCallback;
102 import ca.uhn.hl7v2.testpanel.util.Range;
103 import ca.uhn.hl7v2.testpanel.xsd.Hl7V2EncodingTypeEnum;
104 import ca.uhn.hl7v2.validation.impl.DefaultValidation;
105 
106 public class Hl7V2MessageEditorPanel extends BaseMainPanel implements IDestroyable {
107 	private static final String CREATE_NEW_CONNECTION = "Create New Connection...";
108 	private static final String NO_CONNECTIONS = "No Connections";
109 	private static final Logger ourLog = LoggerFactory.getLogger(Hl7V2MessageEditorPanel.class);
110 	private SendOptionsPopupDialog mySendOptionsPopupDialog;
111 
112 	static {
113 
114 		System.setProperty("DefaultFont", "ARIAL-PLAIN-13");
115 
116 		try {
117 			DefaultSyntaxKit.initKit();
118 			DefaultSyntaxKit.registerContentType("text/er7", Er7SyntaxKit.class.getName());
119 			ourLog.info("Registered syntaxKit");
120 		} catch (Throwable e) {
121 			e.printStackTrace();
122 		}
123 		
124 	}
125 
126 	private boolean myDontRespondToSourceMessageChanges;
127 	private Component myhorizontalStrut_4;
128 	private JPanel bottomPanel;
129 	private JPanel messageEditorContainerPanel;
130 	private JComboBox myShowCombo;
131 	private Controller myController;
132 	private boolean myDisableCaretUpdateHandling;
133 	private DocumentListener myDocumentListener;
134 	private JToggleButton myFollowToggle;
135 	private JLabel mylabel;
136 	private JLabel mylabel_1;
137 	private JLabel mylabel_2;
138 	private JLabel mylabel_3;
139 	private Hl7V2MessageCollection myMessage;
140 	private JEditorPane myMessageEditor;
141 	private PropertyChangeListener myMessageListeneer;
142 	private JScrollPane myMessageScrollPane;
143 	private PropertyChangeListener myOutboundConnectionsListener;
144 	private JComboBox myOutboundInterfaceCombo;
145 	private DefaultComboBoxModel myOutboundInterfaceComboModel;
146 	private JComboBox myProfileCombobox;
147 	private ProfileComboModel myProfileComboboxModel;
148 	private PropertyChangeListener myRangeListener;
149 	private JRadioButton myRdbtnEr7;
150 	private JRadioButton myRdbtnXml;
151 	private PropertyChangeListener mySelectedPathListener;
152 	private JButton mySendButton;
153 	private JSplitPane mysplitPane;
154 	private JLabel myTerserPathTextField;
155 	private JToolBar mytoolBar;
156 	private Hl7V2MessageTree myTreePanel;
157 	private JScrollPane myTreeScrollPane;
158 	private JPanel treeContainerPanel;
159 	private JTabbedPane myTopTabBar;
160 	// private JPanel mySendingPanel;
161 	private ActivityTable mySendingActivityTable;
162 	private PropertyChangeListener myWindowTitleListener;
163 	private Component myhorizontalStrut;
164 	private Component myhorizontalStrut_1;
165 	private boolean myOutboundInterfaceComboModelIsUpdating;
166 	private ArrayList<OutboundConnection> myOutboundInterfaceComboModelShadow;
167 	private final JPopupMenu myTerserPathPopupMenu = new JPopupMenu();
168 	private boolean myHandlingProfileComboboxChange;
169 	private JLabel mylabel_4;
170 	private JToolBar mytoolBar_1;
171 	private TablesComboModel myTablesComboModel;
172 	private PropertyChangeListener myProfilesListener;
173 	private PropertyChangeListener myProfilesNamesListener;
174 	private JButton mySpinner;
175 	private Component myhorizontalGlue;
176 	private ImageIcon mySpinnerIconOn;
177 	private ImageIcon mySpinnerIconOff;
178 	private JButton collapseAllButton;
179 	private JButton expandAllButton;
180 	private Component myhorizontalStrut_2;
181 	private JButton mySendOptionsButton;
182 
183 	/**
184 	 * Create the panel.
185 	 */
186 	public Hl7V2MessageEditorPanel(final Controller theController) {
187 		setBorder(null);
188 		myController = theController;
189 
190 		ButtonGroup encGrp = new ButtonGroup();
191 		setLayout(new BorderLayout(0, 0));
192 
193 		mysplitPane = new JSplitPane();
194 		mysplitPane.setResizeWeight(0.5);
195 		mysplitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
196 		add(mysplitPane);
197 
198 		mysplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() {
199 			public void propertyChange(PropertyChangeEvent theEvt) {
200 				double ratio = (double) mysplitPane.getDividerLocation() / mysplitPane.getHeight();
201 				ourLog.debug("Resizing split to ratio: {}", ratio);
202 				Prefs.getInstance().setHl7EditorSplit(ratio);
203 			}
204 		});
205 
206 		EventQueue.invokeLater(new Runnable() {
207 			public void run() {
208 				mysplitPane.setDividerLocation(Prefs.getInstance().getHl7EditorSplit());
209 			}
210 		});
211 
212 		messageEditorContainerPanel = new JPanel();
213 		messageEditorContainerPanel.setBorder(null);
214 		mysplitPane.setRightComponent(messageEditorContainerPanel);
215 		messageEditorContainerPanel.setLayout(new BorderLayout(0, 0));
216 
217 		myMessageEditor = new JEditorPane();
218 		Highlighter h = new UnderlineHighlighter();
219 		myMessageEditor.setHighlighter(h);
220 		// myMessageEditor.setFont(Prefs.getHl7EditorFont());
221 		myMessageEditor.setSelectedTextColor(Color.black);
222 
223 		myMessageEditor.setCaret(new EditorCaret());
224 		
225 		myMessageScrollPane = new JScrollPane(myMessageEditor);
226 		messageEditorContainerPanel.add(myMessageScrollPane);
227 
228 		JToolBar toolBar = new JToolBar();
229 		messageEditorContainerPanel.add(toolBar, BorderLayout.NORTH);
230 		toolBar.setFloatable(false);
231 		toolBar.setRollover(true);
232 
233 		myFollowToggle = new JToggleButton("Follow");
234 		myFollowToggle.setToolTipText("Keep the message tree (above) and the message editor (below) in sync");
235 		myFollowToggle.addActionListener(new ActionListener() {
236 			public void actionPerformed(ActionEvent e) {
237 				theController.setMessageEditorInFollowMode(myFollowToggle.isSelected());
238 			}
239 		});
240 		myFollowToggle.setIcon(new ImageIcon(Hl7V2MessageEditorPanel.class.getResource("/ca/uhn/hl7v2/testpanel/images/updown.png")));
241 		myFollowToggle.setSelected(theController.isMessageEditorInFollowMode());
242 		toolBar.add(myFollowToggle);
243 
244 		myhorizontalStrut = Box.createHorizontalStrut(20);
245 		toolBar.add(myhorizontalStrut);
246 
247 		mylabel_4 = new JLabel("Encoding");
248 		toolBar.add(mylabel_4);
249 
250 		myRdbtnEr7 = new JRadioButton("ER7");
251 		myRdbtnEr7.setMargin(new Insets(1, 2, 0, 1));
252 		toolBar.add(myRdbtnEr7);
253 
254 		myRdbtnXml = new JRadioButton("XML");
255 		myRdbtnXml.setMargin(new Insets(1, 5, 0, 1));
256 		toolBar.add(myRdbtnXml);
257 		encGrp.add(myRdbtnEr7);
258 		encGrp.add(myRdbtnXml);
259 
260 		treeContainerPanel = new JPanel();
261 		mysplitPane.setLeftComponent(treeContainerPanel);
262 		treeContainerPanel.setLayout(new BorderLayout(0, 0));
263 
264 		mySpinnerIconOn = new ImageIcon(Hl7V2MessageEditorPanel.class.getResource("/ca/uhn/hl7v2/testpanel/images/spinner.gif"));
265 		mySpinnerIconOff = new ImageIcon();
266 
267 		myTreePanel = new Hl7V2MessageTree(theController);
268 		myTreePanel.setWorkingListener(new IWorkingListener() {
269 
270 			public void startedWorking() {
271 				mySpinner.setText("");
272 				mySpinner.setIcon(mySpinnerIconOn);
273 				mySpinnerIconOn.setImageObserver(mySpinner);
274 			}
275 
276 
277 			public void finishedWorking(String theStatus) {
278 				mySpinner.setText(theStatus);
279 
280 				mySpinner.setIcon(mySpinnerIconOff);
281 				mySpinnerIconOn.setImageObserver(null);
282 			}
283 		});
284 		myTreeScrollPane = new JScrollPane(myTreePanel);
285 
286 		myTopTabBar = new JTabbedPane();
287 		treeContainerPanel.add(myTopTabBar);
288 		myTopTabBar.setBorder(null);
289 
290 		JPanel treeContainer = new JPanel();
291 		treeContainer.setLayout(new BorderLayout(0, 0));
292 		treeContainer.add(myTreeScrollPane);
293 
294 		myTopTabBar.add("Message Tree", treeContainer);
295 
296 		mytoolBar_1 = new JToolBar();
297 		mytoolBar_1.setFloatable(false);
298 		treeContainer.add(mytoolBar_1, BorderLayout.NORTH);
299 
300 		mylabel_3 = new JLabel("Show");
301 		mytoolBar_1.add(mylabel_3);
302 
303 		myShowCombo = new JComboBox();
304 		mytoolBar_1.add(myShowCombo);
305 		myShowCombo.setPreferredSize(new Dimension(130, 27));
306 		myShowCombo.setMinimumSize(new Dimension(130, 27));
307 		myShowCombo.setMaximumSize(new Dimension(130, 32767));
308 
309 		collapseAllButton = new JButton();
310 		collapseAllButton.setBorderPainted(false);
311 		collapseAllButton.addMouseListener(new HoverButtonMouseAdapter(collapseAllButton));
312 		collapseAllButton.addActionListener(new ActionListener() {
313 			public void actionPerformed(ActionEvent e) {
314 				myTreePanel.collapseAll();
315 			}
316 		});
317 		collapseAllButton.setToolTipText("Collapse All");
318 		collapseAllButton.setIcon(new ImageIcon(Hl7V2MessageEditorPanel.class.getResource("/ca/uhn/hl7v2/testpanel/images/collapse_all.png")));
319 		mytoolBar_1.add(collapseAllButton);
320 
321 		expandAllButton = new JButton();
322 		expandAllButton.setBorderPainted(false);
323 		expandAllButton.addMouseListener(new HoverButtonMouseAdapter(expandAllButton));
324 		expandAllButton.addActionListener(new ActionListener() {
325 			public void actionPerformed(ActionEvent e) {
326 				myTreePanel.expandAll();
327 			}
328 		});
329 		expandAllButton.setToolTipText("Expand All");
330 		expandAllButton.setIcon(new ImageIcon(Hl7V2MessageEditorPanel.class.getResource("/ca/uhn/hl7v2/testpanel/images/expand_all.png")));
331 		mytoolBar_1.add(expandAllButton);
332 
333 		myhorizontalGlue = Box.createHorizontalGlue();
334 		mytoolBar_1.add(myhorizontalGlue);
335 
336 		mySpinner = new JButton("");
337 		mySpinner.setForeground(Color.DARK_GRAY);
338 		mySpinner.setHorizontalAlignment(SwingConstants.RIGHT);
339 		mySpinner.setMaximumSize(new Dimension(200, 15));
340 		mySpinner.setPreferredSize(new Dimension(200, 15));
341 		mySpinner.setMinimumSize(new Dimension(200, 15));
342 		mySpinner.setBorderPainted(false);
343 		mySpinner.setSize(new Dimension(16, 16));
344 		mytoolBar_1.add(mySpinner);
345 		myProfileComboboxModel = new ProfileComboModel();
346 
347 		myTablesComboModel = new TablesComboModel(myController);
348 
349 		mytoolBar = new JToolBar();
350 		mytoolBar.setFloatable(false);
351 		mytoolBar.setRollover(true);
352 		treeContainerPanel.add(mytoolBar, BorderLayout.NORTH);
353 
354 		myOutboundInterfaceCombo = new JComboBox();
355 		myOutboundInterfaceComboModel = new DefaultComboBoxModel();
356 
357 		mylabel_1 = new JLabel("Send");
358 		mytoolBar.add(mylabel_1);
359 		myOutboundInterfaceCombo.setModel(myOutboundInterfaceComboModel);
360 		myOutboundInterfaceCombo.setMaximumSize(new Dimension(200, 32767));
361 		mytoolBar.add(myOutboundInterfaceCombo);
362 
363 		mySendButton = new JButton("Send");
364 		mySendButton.addMouseListener(new HoverButtonMouseAdapter(mySendButton));
365 		mySendButton.setBorderPainted(false);
366 		mySendButton.addActionListener(new ActionListener() {
367 			public void actionPerformed(ActionEvent e) {
368 				// int selectedIndex =
369 				// myOutboundInterfaceComboModel.getIndexOf(myOutboundInterfaceComboModel.getSelectedItem());
370 				int selectedIndex = myOutboundInterfaceCombo.getSelectedIndex();
371 				OutboundConnection connection = myController.getOutboundConnectionList().getConnections().get(selectedIndex);
372 				activateSendingActivityTabForConnection(connection);
373 				myController.sendMessages(connection, myMessage, mySendingActivityTable.provideTransmissionCallback());
374 			}
375 		});
376 
377 		myhorizontalStrut_2 = Box.createHorizontalStrut(20);
378 		myhorizontalStrut_2.setPreferredSize(new Dimension(2, 0));
379 		myhorizontalStrut_2.setMinimumSize(new Dimension(2, 0));
380 		myhorizontalStrut_2.setMaximumSize(new Dimension(2, 32767));
381 		mytoolBar.add(myhorizontalStrut_2);
382 
383 		mySendOptionsButton = new JButton("Options");
384 		mySendOptionsButton.setBorderPainted(false);
385 		final HoverButtonMouseAdapter sendOptionsHoverAdaptor = new HoverButtonMouseAdapter(mySendOptionsButton);
386 		mySendOptionsButton.addMouseListener(sendOptionsHoverAdaptor);
387 		mySendOptionsButton.setIcon(new ImageIcon(Hl7V2MessageEditorPanel.class.getResource("/ca/uhn/hl7v2/testpanel/images/sendoptions.png")));
388 		mytoolBar.add(mySendOptionsButton);
389 		mySendOptionsButton.addActionListener(new ActionListener() {
390 
391 			@Override
392 			public void actionPerformed(ActionEvent theE) {
393 				if (mySendOptionsPopupDialog != null) {
394 					mySendOptionsPopupDialog.doHide();
395 					mySendOptionsPopupDialog = null;
396 					return;
397 				}
398 				mySendOptionsPopupDialog = new SendOptionsPopupDialog(Hl7V2MessageEditorPanel.this, myMessage, mySendOptionsButton, sendOptionsHoverAdaptor);
399 				Point los = mySendOptionsButton.getLocationOnScreen();
400 				mySendOptionsPopupDialog.setLocation(los.x, los.y + mySendOptionsButton.getHeight());
401 				mySendOptionsPopupDialog.setVisible(true);
402 			}
403 		});
404 
405 		mySendButton.setIcon(new ImageIcon(Hl7V2MessageEditorPanel.class.getResource("/ca/uhn/hl7v2/testpanel/images/button_execute.png")));
406 		mytoolBar.add(mySendButton);
407 
408 		myhorizontalStrut_1 = Box.createHorizontalStrut(20);
409 		mytoolBar.add(myhorizontalStrut_1);
410 
411 		mylabel_2 = new JLabel("Validate");
412 		mytoolBar.add(mylabel_2);
413 
414 		myProfileCombobox = new JComboBox();
415 		mytoolBar.add(myProfileCombobox);
416 		myProfileCombobox.setPreferredSize(new Dimension(200, 27));
417 		myProfileCombobox.setMinimumSize(new Dimension(200, 27));
418 		myProfileCombobox.addActionListener(new ActionListener() {
419 
420 			public void actionPerformed(ActionEvent e) {
421 				if (myHandlingProfileComboboxChange) {
422 					return;
423 				}
424 
425 				myHandlingProfileComboboxChange = true;
426 				try {
427 					if (myProfileCombobox.getSelectedIndex() == 0) {
428 						myMessage.setValidationContext(null);
429 					} else if (myProfileCombobox.getSelectedIndex() == 1) {
430 						myMessage.setValidationContext(new DefaultValidation());
431 					} else if (myProfileCombobox.getSelectedIndex() > 0) {
432 						ProfileGroup profile = myProfileComboboxModel.myProfileGroups.get(myProfileCombobox.getSelectedIndex());
433 						myMessage.setRuntimeProfile(profile);
434 
435 						// } else if (myProfileCombobox.getSelectedItem() ==
436 						// ProfileComboModel.APPLY_CONFORMANCE_PROFILE) {
437 						// IOkCancelCallback<Void> callback = new
438 						// IOkCancelCallback<Void>() {
439 						// public void ok(Void theArg) {
440 						// myProfileComboboxModel.update();
441 						// }
442 						//
443 						// public void cancel(Void theArg) {
444 						// myProfileCombobox.setSelectedIndex(0);
445 						// }
446 						// };
447 						// myController.chooseAndLoadConformanceProfileForMessage(myMessage,
448 						// callback);
449 					}
450 				} catch (ProfileException e2) {
451 					ourLog.error("Failed to load profile", e2);
452 				} finally {
453 					myHandlingProfileComboboxChange = false;
454 				}
455 			}
456 		});
457 		myProfileCombobox.setMaximumSize(new Dimension(300, 32767));
458 		myProfileCombobox.setModel(myProfileComboboxModel);
459 
460 		myhorizontalStrut_4 = Box.createHorizontalStrut(20);
461 		myhorizontalStrut_4.setPreferredSize(new Dimension(2, 0));
462 		myhorizontalStrut_4.setMinimumSize(new Dimension(2, 0));
463 		myhorizontalStrut_4.setMaximumSize(new Dimension(2, 32767));
464 		mytoolBar.add(myhorizontalStrut_4);
465 
466 		// mySendingPanel = new JPanel();
467 		// mySendingPanel.setBorder(null);
468 		// myTopTabBar.addTab("Sending", null, mySendingPanel, null);
469 		// mySendingPanel.setLayout(new BorderLayout(0, 0));
470 
471 		mySendingActivityTable = new ActivityTable();
472 		mySendingActivityTable.setController(myController);
473 		myTopTabBar.addTab("Sending", null, mySendingActivityTable, null);
474 
475 		// mySendingPanelScrollPanel = new JScrollPane();
476 		// mySendingPanelScrollPanel.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
477 		// mySendingPanelScrollPanel.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
478 		// mySendingPanelScrollPanel.setColumnHeaderView(mySendingActivityTable);
479 		//
480 		// mySendingPanel.add(mySendingPanelScrollPanel, BorderLayout.CENTER);
481 
482 		bottomPanel = new JPanel();
483 		bottomPanel.setPreferredSize(new Dimension(10, 20));
484 		bottomPanel.setMinimumSize(new Dimension(10, 20));
485 		add(bottomPanel, BorderLayout.SOUTH);
486 		GridBagLayout gbl_bottomPanel = new GridBagLayout();
487 		gbl_bottomPanel.columnWidths = new int[] { 98, 74, 0 };
488 		gbl_bottomPanel.rowHeights = new int[] { 16, 0 };
489 		gbl_bottomPanel.columnWeights = new double[] { 0.0, 1.0, Double.MIN_VALUE };
490 		gbl_bottomPanel.rowWeights = new double[] { 0.0, Double.MIN_VALUE };
491 		bottomPanel.setLayout(gbl_bottomPanel);
492 
493 		mylabel = new JLabel("Terser Path:");
494 		mylabel.setHorizontalTextPosition(SwingConstants.LEFT);
495 		mylabel.setHorizontalAlignment(SwingConstants.LEFT);
496 		GridBagConstraints gbc_label = new GridBagConstraints();
497 		gbc_label.fill = GridBagConstraints.VERTICAL;
498 		gbc_label.weighty = 1.0;
499 		gbc_label.anchor = GridBagConstraints.NORTHWEST;
500 		gbc_label.gridx = 0;
501 		gbc_label.gridy = 0;
502 		bottomPanel.add(mylabel, gbc_label);
503 
504 		myTerserPathTextField = new JLabel();
505 		myTerserPathTextField.setForeground(Color.BLUE);
506 		myTerserPathTextField.setFont(new Font("Lucida Console", Font.PLAIN, 13));
507 		myTerserPathTextField.setBorder(null);
508 		myTerserPathTextField.setBackground(SystemColor.control);
509 		myTerserPathTextField.addMouseListener(new MouseAdapter() {
510 			@Override
511 			public void mouseClicked(MouseEvent e) {
512 				if (StringUtils.isNotEmpty(myTerserPathTextField.getText())) {
513 					myTerserPathPopupMenu.show(myTerserPathTextField, 0, 0);
514 				}
515 			}
516 		});
517 
518 		GridBagConstraints gbc_TerserPathTextField = new GridBagConstraints();
519 		gbc_TerserPathTextField.weightx = 1.0;
520 		gbc_TerserPathTextField.fill = GridBagConstraints.HORIZONTAL;
521 		gbc_TerserPathTextField.gridx = 1;
522 		gbc_TerserPathTextField.gridy = 0;
523 		bottomPanel.add(myTerserPathTextField, gbc_TerserPathTextField);
524 
525 		initLocal();
526 
527 	}
528 
529 
530 	public void destroy() {
531 		myMessage.removePropertyChangeListener(Hl7V2MessageCollection.SOURCE_MESSAGE_PROPERTY, myMessageListeneer);
532 		myMessage.removePropertyChangeListener(Hl7V2MessageCollection.PROP_HIGHLITED_RANGE, myRangeListener);
533 		myMessage.removePropertyChangeListener(Hl7V2MessageCollection.SAVED_PROPERTY, myWindowTitleListener);
534 		myMessage.removePropertyChangeListener(Hl7V2MessageCollection.PROP_SAVE_FILENAME, myWindowTitleListener);
535 
536 		myTreePanel.destroy();
537 		myController.getOutboundConnectionList().addPropertyChangeListener(OutboundConnectionList.PROP_LIST, myOutboundConnectionsListener);
538 
539 		myTablesComboModel.destroy();
540 		unregisterProfileNamesListeners();
541 	}
542 
543 
544 	private void initLocal() {
545 
546 		myDocumentListener = new DocumentListener() {
547 
548 			public void changedUpdate(DocumentEvent theE) {
549 				ourLog.info("Document change: " + theE);
550 				handleChange(theE);
551 			}
552 
553 
554 			private void handleChange(DocumentEvent theE) {
555 				myDontRespondToSourceMessageChanges = true;
556 				try {
557 					long start = System.currentTimeMillis();
558 
559 					String newSource = myMessageEditor.getText();
560 					int changeStart = theE.getOffset();
561 					int changeEnd = changeStart + theE.getLength();
562 					myMessage.updateSourceMessage(newSource, changeStart, changeEnd);
563 
564 					ourLog.info("Handled document update in {} ms", System.currentTimeMillis() - start);
565 				} finally {
566 					myDontRespondToSourceMessageChanges = false;
567 				}
568 			}
569 
570 
571 			public void insertUpdate(DocumentEvent theE) {
572 				ourLog.info("Document insert: " + theE);
573 				handleChange(theE);
574 			}
575 
576 
577 			public void removeUpdate(DocumentEvent theE) {
578 				ourLog.info("Document removed: " + theE);
579 				handleChange(theE);
580 			}
581 		};
582 		myMessageEditor.getDocument().addDocumentListener(myDocumentListener);
583 
584 		myMessageEditor.addCaretListener(new CaretListener() {
585 
586 			public void caretUpdate(final CaretEvent theE) {
587 				removeMostHighlights();
588 				if (!myDisableCaretUpdateHandling) {
589 					myController.invokeInBackground(new Runnable() {
590 						@Override
591 						public void run() {
592 							myMessage.setHighlitedPathBasedOnRange(new Range(theE.getDot(), theE.getMark()));
593 							myTreePanel.repaint();
594 						}});
595 				}
596 			}
597 		});
598 
599 		updateOutboundConnectionsBox();
600 		myOutboundConnectionsListener = new PropertyChangeListener() {
601 
602 			public void propertyChange(PropertyChangeEvent theEvt) {
603 				updateOutboundConnectionsBox();
604 			}
605 		};
606 		myController.getOutboundConnectionList().addPropertyChangeListener(OutboundConnectionList.PROP_LIST, myOutboundConnectionsListener);
607 
608 		myOutboundInterfaceCombo.addActionListener(new ActionListener() {
609 			public void actionPerformed(ActionEvent theE) {
610 				if (!myOutboundInterfaceComboModelIsUpdating) {
611 					updateSendButton();
612 				}
613 			}
614 
615 		});
616 
617 		JMenuItem copyMenuItem = new JMenuItem("Copy to Clipboard");
618 		copyMenuItem.addActionListener(new ActionListener() {
619 			public void actionPerformed(ActionEvent theE) {
620 				String selection = myTerserPathTextField.getText();
621 				StringSelection data = new StringSelection(selection);
622 				Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
623 				clipboard.setContents(data, data);
624 			}
625 		});
626 		myTerserPathPopupMenu.add(copyMenuItem);
627 
628 		myProfilesListener = new PropertyChangeListener() {
629 			public void propertyChange(PropertyChangeEvent theEvt) {
630 				myProfileComboboxModel.update();
631 				registerProfileNamesListeners();
632 			}
633 		};
634 		myController.getProfileFileList().addPropertyChangeListener(ProfileFileList.PROP_FILES, myProfilesListener);
635 		registerProfileNamesListeners();
636 
637 		myProfilesNamesListener = new PropertyChangeListener() {
638 			public void propertyChange(PropertyChangeEvent theEvt) {
639 				myProfileComboboxModel.update();
640 			}
641 		};
642 
643 	}
644 
645 
646 	private void registerProfileNamesListeners() {
647 		unregisterProfileNamesListeners();
648 		for (ProfileGroup next : myController.getProfileFileList().getProfiles()) {
649 			next.addPropertyChangeListener(ProfileGroup.PROP_NAME, myProfilesNamesListener);
650 		}
651 	}
652 
653 
654 	private void unregisterProfileNamesListeners() {
655 		for (ProfileGroup next : myController.getProfileFileList().getProfiles()) {
656 			next.removePropertyChangeListener(ProfileGroup.PROP_NAME, myProfilesNamesListener);
657 		}
658 	}
659 
660 
661 	private void updateSendButton() {
662 		String selected = (String) myOutboundInterfaceCombo.getSelectedItem();
663 		if (selected == null || selected == NO_CONNECTIONS) {
664 			mySendButton.setEnabled(false);
665 			if (myMessage != null) {
666 				myMessage.setLastSendToInterfaceId(null);
667 			}
668 			return;
669 		}
670 
671 		if (selected == CREATE_NEW_CONNECTION) {
672 			IOkCancelCallback<OutboundConnection> handler = new IOkCancelCallback<OutboundConnection>() {
673 				public void ok(OutboundConnection theArg) {
674 					myMessage.setLastSendToInterfaceId(theArg.getId());
675 					myOutboundInterfaceCombo.setSelectedIndex(myOutboundInterfaceComboModel.getSize() - 2);
676 				}
677 
678 
679 				public void cancel(OutboundConnection theArg) {
680 					myOutboundInterfaceCombo.setSelectedIndex(0);
681 				}
682 			};
683 			myController.addOutboundConnectionToSendTo(handler);
684 			return;
685 		}
686 
687 		if (myMessage != null) {
688 			int selectedIndex = myOutboundInterfaceCombo.getSelectedIndex();
689 			OutboundConnection connection = myController.getOutboundConnectionList().getConnections().get(selectedIndex);
690 			myMessage.setLastSendToInterfaceId(connection.getId());
691 		}
692 
693 		mySendButton.setEnabled(true);
694 	}
695 
696 
697 	/**
698 	 * @param theMessage
699 	 *            the message to set
700 	 */
701 	public void setMessage(Hl7V2MessageCollection theMessage) {
702 		Validate.isTrue(myMessage == null);
703 
704 		myMessage = theMessage;
705 
706 		myShowCombo.setModel(new ShowComboModel());
707 
708 		// Prepopulate the "send to interface" combo to the last value it had
709 		if (StringUtils.isNotBlank(myMessage.getLastSendToInterfaceId())) {
710 			for (int i = 0; i < myOutboundInterfaceComboModelShadow.size(); i++) {
711 				if (myOutboundInterfaceComboModelShadow.get(i).getId().equals(myMessage.getLastSendToInterfaceId())) {
712 					myOutboundInterfaceCombo.setSelectedIndex(i);
713 					break;
714 				}
715 			}
716 		}
717 
718 		updateEncodingButtons();
719 		myRdbtnEr7.addActionListener(new ActionListener() {
720 			public void actionPerformed(ActionEvent theE) {
721 				removeHighlights();
722 				myMessage.setEncoding(Hl7V2EncodingTypeEnum.ER_7);
723 			}
724 		});
725 		myRdbtnXml.addActionListener(new ActionListener() {
726 			public void actionPerformed(ActionEvent theE) {
727 				removeHighlights();
728 				myMessage.setEncoding(Hl7V2EncodingTypeEnum.XML);
729 			}
730 		});
731 
732 		try {
733 			myDisableCaretUpdateHandling = true;
734 			updateMessageEditor();
735 		} finally {
736 			myDisableCaretUpdateHandling = false;
737 		}
738 
739 		myMessageListeneer = new PropertyChangeListener() {
740 			public void propertyChange(PropertyChangeEvent theEvt) {
741 				if (myDontRespondToSourceMessageChanges) {
742 					return;
743 				}
744 				try {
745 					myDisableCaretUpdateHandling = true;
746 					updateMessageEditor();
747 				} finally {
748 					myDisableCaretUpdateHandling = false;
749 				}
750 			}
751 		};
752 		myMessage.addPropertyChangeListener(Hl7V2MessageCollection.SOURCE_MESSAGE_PROPERTY, myMessageListeneer);
753 
754 		mySelectedPathListener = new PropertyChangeListener() {
755 
756 			public void propertyChange(PropertyChangeEvent theEvt) {
757 				String path = myMessage.getHighlitedPath();
758 				if (path == null) {
759 					path = "";
760 				}
761 
762 				int index = path.indexOf('/');
763 				if (index > -1) {
764 					path = path.substring(index);
765 				}
766 
767 				myTerserPathTextField.setText(path);
768 			}
769 		};
770 		myMessage.addPropertyChangeListener(Hl7V2MessageCollection.PROP_HIGHLITED_PATH, mySelectedPathListener);
771 
772 		myTreePanel.setMessage(myMessage);
773 
774 		myRangeListener = new PropertyChangeListener() {
775 
776 			public void propertyChange(PropertyChangeEvent theEvt) {
777 				if (theEvt.getNewValue() == null || !myFollowToggle.isSelected()) {
778 					return;
779 				}
780 				Range range = (Range) theEvt.getNewValue();
781 
782 				// myMessageScrollPane.getHorizontalScrollBar().setValue(0);
783 
784 				// myMessageEditor.select(range.getStart(), range.getEnd());
785 				// myMessageEditor.setCaretPosition(range.getStart());
786 
787 				myMessageEditor.setCaretPosition(range.getStart());
788 				myMessageEditor.moveCaretPosition(range.getEnd());
789 
790 				myMessageEditor.setCaretPosition(range.getEnd());
791 				myMessageEditor.moveCaretPosition(range.getStart());
792 
793 				// myMessageEditor.grabFocus();
794 				myMessageEditor.repaint();
795 
796 				String substring = myMessage.getSourceMessage().substring(range.getStart(), range.getEnd());
797 				ourLog.info("Selected range set to " + range + " which is " + substring);
798 			}
799 		};
800 		myMessage.addPropertyChangeListener(Hl7V2MessageCollection.PROP_HIGHLITED_RANGE, myRangeListener);
801 
802 		myProfileComboboxModel.update();
803 
804 		// Window Title
805 		myWindowTitleListener = new PropertyChangeListener() {
806 			public void propertyChange(PropertyChangeEvent theEvt) {
807 				updateWindowTitle();
808 			}
809 		};
810 		myMessage.addPropertyChangeListener(Hl7V2MessageCollection.SAVED_PROPERTY, myWindowTitleListener);
811 		myMessage.addPropertyChangeListener(Hl7V2MessageCollection.PROP_SAVE_FILENAME, myWindowTitleListener);
812 		updateWindowTitle();
813 
814 		EventQueue.invokeLater(new Runnable() {
815 
816 			public void run() {
817 				myMessageEditor.setCaretPosition(0);
818 				myMessageEditor.grabFocus();
819 			}
820 		});
821 
822 	}
823 
824 
825 	private void removeHighlights() {
826 		Highlighter hilite = myMessageEditor.getHighlighter();
827 		Highlighter.Highlight[] hilites = hilite.getHighlights();
828 		for (int i = 0; i < hilites.length; i++) {
829 			hilite.removeHighlight(hilites[i]);
830 		}
831 	}
832 
833 
834 	// Removes all but the 2 most recent highlights - the last tag pair
835 	// selected.
836 	private void removeMostHighlights() {
837 		Highlighter hilite = myMessageEditor.getHighlighter();
838 		Highlighter.Highlight[] hilites = hilite.getHighlights();
839 		for (int i = 0; i < hilites.length - 2; i++) {
840 			hilite.removeHighlight(hilites[i]);
841 		}
842 	}
843 
844 
845 	private void updateWindowTitle() {
846 		StringBuilder b = new StringBuilder();
847 
848 		if (myMessage.isSaved() == false) {
849 			b.append("Unsaved");
850 		}
851 
852 		if (b.length() > 0) {
853 			b.append(" - ");
854 		}
855 		if (StringUtils.isNotBlank(myMessage.getSaveFileName())) {
856 			b.append(myMessage.getSaveFileName());
857 		} else {
858 			b.append("New File");
859 		}
860 
861 		setWindowTitle(b.toString());
862 	}
863 
864 
865 	private void updateEncodingButtons() {
866 		switch (myMessage.getEncoding()) {
867 			case XML:
868 				myRdbtnXml.setSelected(true);
869 				myRdbtnEr7.setSelected(false);
870 				break;
871 			case ER_7:
872 				myRdbtnXml.setSelected(false);
873 				myRdbtnEr7.setSelected(true);
874 		}
875 	}
876 
877 	
878 
879 	private void updateMessageEditor() {
880 
881 		final JScrollBar vsb = myMessageScrollPane.getVerticalScrollBar();
882 		int initialVerticalValue = vsb.getValue();
883 
884 		myMessageEditor.getDocument().removeDocumentListener(myDocumentListener);
885 
886 		String sourceMessage = myMessage.getSourceMessage();
887 
888 		if (myMessage.getEncoding() == Hl7V2EncodingTypeEnum.XML) {
889 			myMessageEditor.setContentType("text/xml");
890 		} else {
891 			myMessageEditor.setContentType("text/er7");
892 			sourceMessage = sourceMessage.replace('\r', '\n');
893 		}
894 
895 		myMessageEditor.setText(sourceMessage);
896 
897 		myMessageEditor.getDocument().addDocumentListener(myDocumentListener);
898 
899 		final int verticalValue = Math.min(initialVerticalValue, vsb.getMaximum());
900 
901 		SwingUtilities.invokeLater(new Runnable() {
902 			public void run() {
903 				// myMessageEditor.setFont(Prefs.getHl7EditorFont());
904 				vsb.setValue(verticalValue);
905 			}
906 		});
907 
908 	}
909 
910 
911 	private void updateOutboundConnectionsBox() {
912 		int currentSelection = myOutboundInterfaceCombo.getSelectedIndex();
913 
914 		List<OutboundConnection> conn = myController.getOutboundConnectionList().getConnections();
915 
916 		myOutboundInterfaceComboModelIsUpdating = true;
917 
918 		myOutboundInterfaceComboModel.removeAllElements();
919 		myOutboundInterfaceComboModelShadow = new ArrayList<OutboundConnection>();
920 		if (conn.isEmpty()) {
921 			myOutboundInterfaceComboModel.addElement(NO_CONNECTIONS);
922 			myOutboundInterfaceComboModelShadow.add(null);
923 		} else {
924 			for (OutboundConnection next : conn) {
925 				myOutboundInterfaceComboModel.addElement(next.getName());
926 				myOutboundInterfaceComboModelShadow.add(next);
927 			}
928 		}
929 
930 		myOutboundInterfaceComboModel.addElement(CREATE_NEW_CONNECTION);
931 		myOutboundInterfaceComboModelShadow.add(null);
932 
933 		if (currentSelection != -1 && currentSelection < myOutboundInterfaceComboModel.getSize()) {
934 			myOutboundInterfaceCombo.setSelectedIndex(currentSelection);
935 		} else {
936 			myOutboundInterfaceCombo.setSelectedIndex(0);
937 		}
938 
939 		myOutboundInterfaceComboModelIsUpdating = false;
940 
941 		updateSendButton();
942 	}
943 
944 	private class ProfileComboModel extends DefaultComboBoxModel {
945 
946 		// private static final String APPLY_CONFORMANCE_PROFILE =
947 		// "Apply Conformance Profile...";
948 		private ArrayList<ProfileGroup> myProfileGroups;
949 
950 
951 		public void update() {
952 			myHandlingProfileComboboxChange = true;
953 			try {
954 				super.removeAllElements();
955 				myProfileGroups = new ArrayList<ProfileGroup>();
956 
957 				addElement("No Profile/Validation");
958 				myProfileGroups.add(null);
959 
960 				addElement("Default Datatype Validation (HAPI)");
961 				myProfileGroups.add(null);
962 
963 				// ProfileGroup profile = myMessage.getRuntimeProfile();
964 				// if (profile != null) {
965 				// String name = "Conf Profile (" +
966 				// profile.getMessage().getMsgType() + "^" +
967 				// profile.getMessage().getEventType() + ")";
968 				// String profName = myMessage.getRuntimeProfile().getName();
969 				// if (StringUtils.isNotBlank(profName)) {
970 				// name = name + ":" + profName;
971 				// }
972 				//
973 				// addElement(name);
974 				// }
975 
976 				// addElement(APPLY_CONFORMANCE_PROFILE);
977 
978 				for (ProfileGroup nextProfile : myController.getProfileFileList().getProfiles()) {
979 					String nextString = "Profile Group: " + nextProfile.getName();
980 					addElement(nextString);
981 					myProfileGroups.add(nextProfile);
982 					if (myMessage.getRuntimeProfile() == nextProfile) {
983 						setSelectedItem(nextString);
984 					}
985 				}
986 
987 				if (myMessage.getValidationContext() instanceof DefaultValidation) {
988 					setSelectedItem(getElementAt(1));
989 				} else if (getSelectedItem() == null) {
990 					setSelectedItem(getElementAt(0));
991 				}
992 			} finally {
993 				myHandlingProfileComboboxChange = false;
994 			}
995 		}
996 	}
997 
998 	public class ShowComboModel extends DefaultComboBoxModel implements ActionListener {
999 		private static final String ALL = "All";
1000 		private static final String ERRORS = "Errors";
1001 		private static final String POPULATED = "Populated";
1002 		private static final String SUPPORTED = "Supported";
1003 
1004 
1005 		public ShowComboModel() {
1006 			addElement(POPULATED);
1007 			addElement(ALL);
1008 			addElement(ERRORS);
1009 			addElement(SUPPORTED);
1010 
1011 			switch (myMessage.getEditorShowMode()) {
1012 				case ALL:
1013 					setSelectedItem(ALL);
1014 					break;
1015 				case ERROR:
1016 					setSelectedItem(ERRORS);
1017 					break;
1018 				case POPULATED:
1019 					setSelectedItem(POPULATED);
1020 					break;
1021 				case SUPPORTED:
1022 					setSelectedItem(SUPPORTED);
1023 					break;
1024 			}
1025 
1026 			myShowCombo.addActionListener(this);
1027 		}
1028 
1029 
1030 		public void actionPerformed(ActionEvent theE) {
1031 			String value = (String) myShowCombo.getSelectedItem();
1032 
1033 			if (value == ALL) {
1034 				myTreePanel.setEditorShowModeAndUpdateAccordingly(ShowEnum.ALL);
1035 			} else if (value == ERRORS) {
1036 				myTreePanel.setEditorShowModeAndUpdateAccordingly(ShowEnum.ERROR);
1037 			} else if (value == SUPPORTED) {
1038 				myTreePanel.setEditorShowModeAndUpdateAccordingly(ShowEnum.SUPPORTED);
1039 			} else {
1040 				myTreePanel.setEditorShowModeAndUpdateAccordingly(ShowEnum.POPULATED);
1041 			}
1042 
1043 		}
1044 	}
1045 
1046 
1047 	private void activateSendingActivityTabForConnection(OutboundConnection theConnection) {
1048 		mySendingActivityTable.setConnection(theConnection, false);
1049 		myTopTabBar.setSelectedComponent(mySendingActivityTable);
1050 	}
1051 
1052 	public Frame getWindow() {
1053 		return myController.getWindow();
1054 	}
1055 
1056 }