1+ package cbit .plot .gui ;
2+
3+ import cbit .vcell .parser .ExpressionException ;
4+ import cbit .vcell .solver .ode .ODESolverResultSet ;
5+ import cbit .vcell .solver .ode .gui .ClusterSpecificationPanel ;
6+ import cbit .vcell .util .ColumnDescription ;
7+ import org .apache .logging .log4j .LogManager ;
8+ import org .apache .logging .log4j .Logger ;
9+ import org .vcell .util .gui .NonEditableDefaultTableModel ;
10+ import org .vcell .util .gui .ScrollTable ;
11+ import org .vcell .util .gui .SpecialtyTableRenderer ;
12+
13+ import javax .swing .*;
14+ import javax .swing .event .ChangeEvent ;
15+ import javax .swing .event .ChangeListener ;
16+ import javax .swing .table .DefaultTableCellRenderer ;
17+ import javax .swing .table .TableCellRenderer ;
18+ import javax .swing .table .TableColumn ;
19+ import java .awt .*;
20+ import java .awt .event .ActionEvent ;
21+ import java .awt .event .ActionListener ;
22+ import java .awt .event .MouseEvent ;
23+ import java .awt .event .MouseListener ;
24+ import java .beans .PropertyChangeEvent ;
25+ import java .beans .PropertyChangeListener ;
26+
27+ public class ClusterDataPanel extends JPanel {
28+
29+ private static final Logger LG = LogManager .getLogger (ClusterDataPanel .class );
30+
31+ private ScrollTable scrollPaneTable ;
32+ private NonEditableDefaultTableModel nonEditableDefaultTableModel = null ;
33+
34+ private final IvjEventHandler ivjEventHandler = new IvjEventHandler ();
35+
36+ class IvjEventHandler implements ActionListener , MouseListener , PropertyChangeListener , ChangeListener {
37+
38+ @ Override
39+ public void mouseClicked (MouseEvent e ) {
40+ if (e .getSource () == getScrollPaneTable ()) {
41+ int row = getScrollPaneTable ().rowAtPoint (e .getPoint ());
42+ int col = getScrollPaneTable ().columnAtPoint (e .getPoint ());
43+ System .out .println ("ClusterDataPanel: clicked row=" + row + " col=" + col );
44+ }
45+ }
46+
47+ @ Override public void mousePressed (MouseEvent e ) {}
48+ @ Override public void mouseReleased (MouseEvent e ) {}
49+ @ Override public void mouseEntered (MouseEvent e ) {}
50+ @ Override public void mouseExited (MouseEvent e ) {}
51+
52+ @ Override
53+ public void actionPerformed (ActionEvent e ) {
54+ // reserved for future buttons or context menu actions
55+ }
56+
57+ @ Override
58+ public void propertyChange (PropertyChangeEvent evt ) {
59+ // reserved for future dynamic formatting
60+ }
61+
62+ @ Override
63+ public void stateChanged (ChangeEvent e ) {
64+ // reserved for future slider/spinner interactions
65+ }
66+ }
67+
68+ private class ClusterHeaderRenderer extends DefaultTableCellRenderer {
69+
70+ private final TableCellRenderer base ;
71+ public ClusterHeaderRenderer (TableCellRenderer base ) {
72+ this .base = base ;
73+ }
74+
75+ @ Override
76+ public Component getTableCellRendererComponent (JTable table , Object value , boolean isSelected ,
77+ boolean hasFocus , int row , int column ) {
78+ // First let ScrollTable’s renderer do its work
79+ Component c = base .getTableCellRendererComponent (table , value , isSelected , hasFocus , row , column );
80+ if (!(c instanceof JLabel )) {
81+ return c ; // safety
82+ }
83+ JLabel lbl = (JLabel ) c ;
84+ String name = value == null ? "" : value .toString ();
85+
86+ // Read mode from table metadata
87+ ClusterSpecificationPanel .DisplayMode mode = (ClusterSpecificationPanel .DisplayMode )
88+ ((JComponent )table ).getClientProperty ("ClusterDisplayMode" );
89+
90+ // First-time creation: no mode yet
91+ if (mode == null ) {
92+ lbl .setToolTipText (null );
93+ return lbl ; // leave ScrollTable’s default header styling intact
94+ }
95+
96+ String unit = "" ;
97+ String tooltip = "" ;
98+ if (column == 0 ) {
99+ unit = "seconds" ;
100+ tooltip = "Simulation time in seconds" ;
101+ } else {
102+ switch (mode ) {
103+ case COUNTS :
104+ unit = "molecules" ;
105+ tooltip = "Number of clusters of size " + name + " molecules" ;
106+ break ;
107+ case MEAN :
108+ unit = "value" ;
109+ tooltip = "Cluster mean statistic for " + name ;
110+ break ;
111+ case OVERALL :
112+ unit = "value" ;
113+ tooltip = "Cluster overall statistic for " + name ;
114+ break ;
115+ }
116+ }
117+ lbl .setText ("<html>" + name + "<font color=\" #8B0000\" > [" + unit + "]</font></html>" );
118+ lbl .setToolTipText (tooltip );
119+ return lbl ;
120+ }
121+ }
122+
123+ public ClusterDataPanel () {
124+ super ();
125+ initialize ();
126+ }
127+ private void initialize () {
128+ try {
129+ setName ("ClusterDataPanel" );
130+ setLayout (new java .awt .BorderLayout ());
131+ setSize (541 , 348 );
132+ add (getScrollPaneTable ().getEnclosingScrollPane (), BorderLayout .CENTER );
133+ JLabel lblNewLabel = new JLabel ("<html>To <b>Copy</b> table data or <b>Export</b> as HDF5, select rows/cells and use the right mouse button menu.</html>" );
134+ add (lblNewLabel , BorderLayout .SOUTH );
135+ initConnections ();
136+ // controlKeys();
137+ } catch (Throwable exc ) {
138+ handleException (exc );
139+ }
140+ }
141+ private void initConnections () throws java .lang .Exception {
142+ this .addPropertyChangeListener (ivjEventHandler );
143+ getScrollPaneTable ().addMouseListener (ivjEventHandler );
144+ getScrollPaneTable ().setModel (getNonEditableDefaultTableModel ());
145+ getScrollPaneTable ().createDefaultColumnsFromModel ();
146+ TableCellRenderer baseHeaderRenderer = getScrollPaneTable ().getTableHeader ().getDefaultRenderer ();
147+ getScrollPaneTable ().getTableHeader ().setDefaultRenderer (new ClusterHeaderRenderer (baseHeaderRenderer ));
148+
149+ }
150+ private void handleException (java .lang .Throwable exception ) {
151+ System .out .println ("--------- UNCAUGHT EXCEPTION ---------" );
152+ exception .printStackTrace (System .out );
153+ }
154+
155+ // -----------------------------------------------------------
156+ private ScrollTable getScrollPaneTable () {
157+ if (scrollPaneTable == null ) {
158+ try {
159+ scrollPaneTable = new ScrollTable ();
160+ scrollPaneTable .setName ("ScrollPaneTable" );
161+ scrollPaneTable .setCellSelectionEnabled (true );
162+ scrollPaneTable .setBounds (0 , 0 , 200 , 200 );
163+ } catch (java .lang .Throwable ivjExc ) {
164+ handleException (ivjExc );
165+ }
166+ }
167+ return scrollPaneTable ;
168+ }
169+
170+ private NonEditableDefaultTableModel getNonEditableDefaultTableModel () {
171+ if (nonEditableDefaultTableModel == null ) {
172+ try {
173+ nonEditableDefaultTableModel = new NonEditableDefaultTableModel ();
174+ } catch (java .lang .Throwable ivjExc ) {
175+ handleException (ivjExc );
176+ }
177+ }
178+ return nonEditableDefaultTableModel ;
179+ }
180+
181+ public void setSpecialityRenderer (SpecialtyTableRenderer str ) {
182+ // TODO: write some appropriate renderer when we decide what to show in the tooltip
183+ // use RendererViewerDoubleWithTooltip for inspiration
184+ // getScrollPaneTable().setSpecialityRenderer(str);
185+ }
186+
187+
188+ public void updateData (ClusterSpecificationPanel .ClusterSelection sel ) throws ExpressionException {
189+ if (sel == null ) {
190+ getScrollPaneTable ().putClientProperty ("ClusterDisplayMode" , null );
191+ } else {
192+ getScrollPaneTable ().putClientProperty ("ClusterDisplayMode" , sel .mode );
193+ }
194+ if (sel == null || sel .resultSet == null || sel .columns == null || sel .columns .isEmpty ()) {
195+ getNonEditableDefaultTableModel ().setDataVector (new Object [][]{}, new Object []{"No data" });
196+ getScrollPaneTable ().createDefaultColumnsFromModel ();
197+ revalidate ();
198+ repaint ();
199+ return ;
200+ }
201+ ODESolverResultSet srs = sel .resultSet ;
202+ java .util .List <ColumnDescription > columns = sel .columns ;
203+
204+ int timeIndex = srs .findColumn ("t" );
205+ double [] times = srs .extractColumn (timeIndex );
206+ int rowCount = times .length ;
207+
208+ // column names
209+ String [] columnNames = new String [1 + columns .size ()];
210+ columnNames [0 ] = "t" ;
211+ for (int i = 0 ; i < columns .size (); i ++) {
212+ columnNames [i + 1 ] = columns .get (i ).getName ();
213+ }
214+
215+ // data
216+ Object [][] data = new Object [rowCount ][columnNames .length ];
217+ for (int r = 0 ; r < rowCount ; r ++) {
218+ int c = 0 ;
219+ data [r ][c ++] = times [r ];
220+ for (ColumnDescription cd : columns ) {
221+ int idx = srs .findColumn (cd .getName ());
222+ double [] y = srs .extractColumn (idx );
223+ data [r ][c ++] = y [r ];
224+ }
225+ }
226+
227+ // update existing model
228+ getNonEditableDefaultTableModel ().setDataVector (data , columnNames );
229+
230+ // refresh table columns
231+ getScrollPaneTable ().createDefaultColumnsFromModel ();
232+ autoSizeTableColumns (getScrollPaneTable ());
233+
234+ revalidate ();
235+ repaint ();
236+ }
237+
238+ private void autoSizeTableColumns (JTable table ) {
239+ final int margin = 12 ;
240+
241+ for (int col = 0 ; col < table .getColumnCount (); col ++) {
242+ TableColumn column = table .getColumnModel ().getColumn (col );
243+
244+ int maxWidth = 0 ;
245+
246+ // header
247+ TableCellRenderer headerRenderer = table .getTableHeader ().getDefaultRenderer ();
248+ Component headerComp = headerRenderer .getTableCellRendererComponent (
249+ table , column .getHeaderValue (), false , false , 0 , col );
250+ maxWidth = Math .max (maxWidth , headerComp .getPreferredSize ().width );
251+
252+ // cells
253+ for (int row = 0 ; row < table .getRowCount (); row ++) {
254+ TableCellRenderer cellRenderer = table .getCellRenderer (row , col );
255+ Component comp = table .prepareRenderer (cellRenderer , row , col );
256+ maxWidth = Math .max (maxWidth , comp .getPreferredSize ().width );
257+ }
258+
259+ column .setPreferredWidth (maxWidth + margin );
260+ }
261+ }
262+ }
0 commit comments