2222import beast .core .Input ;
2323import beast .core .parameter .RealParameter ;
2424import beast .evolution .tree .SCMigrationModel ;
25- import java .awt .Color ;
26- import java .awt .Component ;
27- import java .awt .Dimension ;
28- import java .awt .GridBagConstraints ;
29- import java .awt .GridBagLayout ;
30- import java .awt .Insets ;
25+ import beast .evolution .tree .StructuredCoalescentMultiTypeTree ;
26+ import com .sun .org .apache .xml .internal .security .Init ;
27+ import multitypetree .distributions .StructuredCoalescentTreeDensity ;
28+
29+ import java .awt .*;
3130import java .awt .event .ItemEvent ;
32- import javax . swing . JCheckBox ;
33- import javax . swing . JLabel ;
34- import javax . swing . JPanel ;
35- import javax . swing . JSpinner ;
36- import javax .swing .JTable ;
37- import javax .swing .ListSelectionModel ;
38- import javax .swing .SpinnerNumberModel ;
31+ import java . awt . font . TextLayout ;
32+ import java . awt . image . BufferedImage ;
33+ import java . util . ArrayList ;
34+ import java . util . List ;
35+ import javax .swing .* ;
36+ import javax .swing .border . Border ;
37+ import javax .swing .border . CompoundBorder ;
3938import javax .swing .border .EtchedBorder ;
4039import javax .swing .event .ChangeEvent ;
4140import javax .swing .event .TableModelEvent ;
4241import javax .swing .table .DefaultTableCellRenderer ;
4342import javax .swing .table .DefaultTableModel ;
4443import javax .swing .table .TableCellRenderer ;
44+ import javax .swing .table .TableColumn ;
4545
4646/**
4747 * A BEAUti input editor for MigrationModels.
@@ -58,6 +58,8 @@ public class MigrationModelInputEditor extends InputEditor.Base {
5858
5959 boolean dimChangeInProgress = false ;
6060
61+ List <String > rowNames = new ArrayList <>();
62+
6163 public MigrationModelInputEditor (BeautiDoc doc ) {
6264 super (doc );
6365 }
@@ -87,7 +89,7 @@ public void init(Input<?> input, BEASTInterface beastObject, int itemNr,
8789 rateMatrixModel = new DefaultTableModel () {
8890 @ Override
8991 public boolean isCellEditable (int row , int column ) {
90- return row != column ;
92+ return row != column && column != migModel . getNTypes () ;
9193 }
9294 };
9395 popSizeEstCheckBox = new JCheckBox ("estimate" );
@@ -107,6 +109,7 @@ public boolean isCellEditable(int row, int column) {
107109 c .weightx = 0.0 ;
108110 c .anchor = GridBagConstraints .LINE_END ;
109111 panel .add (new JLabel ("Number of demes: " ), c );
112+
110113 JSpinner dimSpinner = new JSpinner (nTypesModel );
111114 dimSpinner .setMaximumSize (new Dimension (100 , Short .MAX_VALUE ));
112115 c .gridx = 1 ;
@@ -121,7 +124,22 @@ public boolean isCellEditable(int row, int column) {
121124 c .weightx = 0.0 ;
122125 c .anchor = GridBagConstraints .LINE_END ;
123126 panel .add (new JLabel ("Population sizes: " ), c );
124- JTable popSizeTable = new JTable (popSizeModel );
127+
128+ JTable popSizeTable = new JTable (popSizeModel ) {
129+ @ Override
130+ public TableCellRenderer getCellRenderer (int row , int column ) {
131+ return new DefaultTableCellRenderer () {
132+ @ Override
133+ public Component getTableCellRendererComponent (
134+ JTable table , Object value , boolean isSelected ,
135+ boolean hasFocus , int row , int column ) {
136+ setHorizontalAlignment (SwingConstants .CENTER );
137+ return super .getTableCellRendererComponent (
138+ table , value , isSelected , hasFocus , row , column );
139+ }
140+ };
141+ }
142+ };
125143 popSizeTable .setShowVerticalLines (true );
126144 popSizeTable .setCellSelectionEnabled (true );
127145 popSizeTable .setSelectionMode (ListSelectionModel .SINGLE_SELECTION );
@@ -133,6 +151,7 @@ public boolean isCellEditable(int row, int column) {
133151 c .anchor = GridBagConstraints .LINE_START ;
134152 panel .add (popSizeTable , c );
135153 popSizeEstCheckBox .setSelected (((RealParameter )migModel .popSizesInput .get ()).isEstimatedInput .get ());
154+
136155 c .gridx = 2 ;
137156 c .gridy = 1 ;
138157 c .anchor = GridBagConstraints .LINE_END ;
@@ -149,40 +168,92 @@ public boolean isCellEditable(int row, int column) {
149168 JTable rateMatrixTable = new JTable (rateMatrixModel ) {
150169 @ Override
151170 public TableCellRenderer getCellRenderer (int row , int column ) {
152- if (row != column )
153- return super .getCellRenderer (row , column );
154- else
155- return new DefaultTableCellRenderer () {
156- @ Override
157- public Component getTableCellRendererComponent (
158- JTable table , Object value , boolean isSelected ,
159- boolean hasFocus , int row , int column ) {
160- JLabel label = new JLabel ();
161- label .setOpaque (true );
162- label .setBackground (Color .GRAY );
163- return label ;
164- }
165- };
171+
172+ return new DefaultTableCellRenderer () {
173+ @ Override
174+ public Component getTableCellRendererComponent (
175+ JTable table , Object value , boolean isSelected ,
176+ boolean hasFocus , int row , int column ) {
177+
178+
179+
180+ if (row == column ) {
181+ JLabel label = new JLabel ();
182+ label .setOpaque (true );
183+ label .setBackground (Color .GRAY );
184+
185+ return label ;
186+
187+ } else {
188+
189+ Component c = super .getTableCellRendererComponent (
190+ table , value , isSelected , hasFocus , row , column );
191+
192+ JComponent jc = (JComponent )c ;
193+ if (column == migModel .getNTypes ()) {
194+ c .setBackground (panel .getBackground ());
195+ c .setForeground (Color .gray );
196+ setHorizontalAlignment (SwingConstants .LEFT );
197+ } else {
198+ int l = 1 , r = 1 , t = 1 , b =1 ;
199+ if (column >0 )
200+ l = 0 ;
201+ if (row >0 )
202+ t = 0 ;
203+
204+ setBorder (BorderFactory .createMatteBorder (t , l , b , r , Color .GRAY ));
205+ setHorizontalAlignment (SwingConstants .CENTER );
206+ }
207+ return c ;
208+ }
209+ }
210+ };
166211 }
167212 };
168- rateMatrixTable .setShowGrid (true );
213+ rateMatrixTable .setShowGrid (false );
214+ rateMatrixTable .setIntercellSpacing (new Dimension (0 ,0 ));
169215 rateMatrixTable .setCellSelectionEnabled (true );
170216 rateMatrixTable .setSelectionMode (ListSelectionModel .SINGLE_SELECTION );
171- rateMatrixTable .setMaximumSize (new Dimension (100 , Short .MAX_VALUE ));
217+ rateMatrixTable .setAutoResizeMode (JTable .AUTO_RESIZE_OFF );
218+ TableColumn col = rateMatrixTable .getColumnModel ().getColumn (migModel .getNTypes ());
219+
220+
221+ FontMetrics metrics = new Canvas ().getFontMetrics (getFont ());
222+ int maxWidth = 0 ;
223+ for (String rowName : rowNames )
224+ maxWidth = Math .max (maxWidth , metrics .stringWidth (rowName + "M" ));
225+
226+ col .setPreferredWidth (maxWidth );
227+ // rateMatrixTable.setMaximumSize(new Dimension(100, Short.MAX_VALUE));
172228
173229 c .gridx = 1 ;
174230 c .gridy = 2 ;
175231 c .anchor = GridBagConstraints .LINE_START ;
176232 c .weightx = 1.0 ;
177233 panel .add (rateMatrixTable , c );
178- rateMatrixEstCheckBox .setSelected (((RealParameter )migModel .rateMatrixInput .get ()).isEstimatedInput .get ());
179234
235+ rateMatrixEstCheckBox .setSelected (((RealParameter )migModel .rateMatrixInput .get ()).isEstimatedInput .get ());
180236 c .gridx = 2 ;
181237 c .gridy = 2 ;
182238 c .anchor = GridBagConstraints .LINE_END ;
183239 c .weightx = 1.0 ;
184240 panel .add (rateMatrixEstCheckBox , c );
185241
242+ c .gridx = 1 ;
243+ c .gridy = 3 ;
244+ c .anchor = GridBagConstraints .LINE_START ;
245+ c .weightx = 1.0 ;
246+ panel .add (new JLabel ("Rows: sources, columns: sinks (backwards in time)" ), c );
247+
248+ c .gridx = 1 ;
249+ c .gridy = 4 ;
250+ c .anchor = GridBagConstraints .LINE_START ;
251+ c .weightx = 1.0 ;
252+ JLabel multilineLabel = new JLabel ();
253+ multilineLabel .setText ("<html><body>Correspondence between row/col indices<br>"
254+ + "and deme names shown to right of matrix.</body></html>" );
255+ panel .add (multilineLabel , c );
256+
186257 add (panel );
187258
188259
@@ -247,7 +318,23 @@ public void loadFromMigrationModel() {
247318 popSizeModel .setRowCount (1 );
248319 popSizeModel .setColumnCount (migModel .getNTypes ());
249320 rateMatrixModel .setRowCount (migModel .getNTypes ());
250- rateMatrixModel .setColumnCount (migModel .getNTypes ());
321+ rateMatrixModel .setColumnCount (migModel .getNTypes ()+1 );
322+
323+ List <String > traitNames = null ;
324+ if (m_beastObject instanceof StructuredCoalescentTreeDensity ) {
325+ StructuredCoalescentTreeDensity scDensity = (StructuredCoalescentTreeDensity )m_beastObject ;
326+ StructuredCoalescentMultiTypeTree scTree = (StructuredCoalescentMultiTypeTree )(scDensity .mtTreeInput .get ());
327+ traitNames = InitMigrationModelConnector .uniqueTraitsInData (scTree );
328+ nTypesModel .setMinimum (Math .max (traitNames .size (),2 ));
329+ };
330+
331+ rowNames .clear ();
332+ for (int i = 0 ; i < migModel .getNTypes (); i ++) {
333+ if (traitNames != null && i <traitNames .size ())
334+ rowNames .add (" " + traitNames .get (i ) + " (" + String .valueOf (i ) + ") " );
335+ else
336+ rowNames .add (" (" + String .valueOf (i ) + ") " );
337+ }
251338
252339 for (int i =0 ; i <migModel .getNTypes (); i ++) {
253340 popSizeModel .setValueAt (migModel .getPopSize (i ), 0 , i );
@@ -256,6 +343,8 @@ public void loadFromMigrationModel() {
256343 continue ;
257344 rateMatrixModel .setValueAt (migModel .getBackwardRate (i , j ), i , j );
258345 }
346+
347+ rateMatrixModel .setValueAt (rowNames .get (i ), i , migModel .getNTypes ());
259348 }
260349
261350 popSizeEstCheckBox .setSelected (((RealParameter )migModel .popSizesInput .get ()).isEstimatedInput .get ());
@@ -281,7 +370,7 @@ public void saveToMigrationModel() {
281370 StringBuilder sbRateMatrix = new StringBuilder ();
282371 boolean first = true ;
283372 for (int i =0 ; i <rateMatrixModel .getRowCount (); i ++) {
284- for (int j =0 ; j <rateMatrixModel .getColumnCount (); j ++) {
373+ for (int j =0 ; j <rateMatrixModel .getColumnCount ()- 1 ; j ++) {
285374 if (i == j )
286375 continue ;
287376
0 commit comments