/*
 * This file is part of the Poliqarp suite.
 * It was taken from the Quarterback Java Classes Collection,
 * available at http://www.cantamen.de.
 */

// $Id: TableCellSizeAdjustor.java,v 1.2 2006/03/01 12:35:46 nathell Exp $
// This file is distributed under the terms of the GNU LGPL.
//   It can be found at http://www.fsf.org/copyleft/lesser.html
// (C) Dirk Hillbrecht 1999-2000, chitec/Dirk Hillbrecht 2000,2001,2002.

package ipipan.poliqarp.gui;

import java.awt.Component;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.event.*;
import javax.swing.table.*;

/** Adjusts width and height of table colums according to their content.
    TableCellSizeAdjustor listens for changes in data or column arrangement
    of a table and readjusts the table's column widths, so that for each column, the
    widest content of all rows fits into the table. Furthermore, it adjusts the cell heights (globally)
    so that the largest content fits.<p>
    TableCellSizeAdjustor doesn't make any assumptions about the content of the table, apart from the model
    object itself not changing. Otherwise, it just uses
    the normal interfaces and methods. TableCellSizeAdjustor turns the table in AUTO_RESIZE_OFF mode
    as the resizing is now managed completely by the adjustor.<p>
    TableCellSizeAdjustor is bound to its table by using the static method <tt>adjustorForTable(JTable)</tt>:

    <pre>
    ...
    JTable jt=new JTable();
    TableCellSizeAdjustor tcsa=TableCellSizeAdjustor.adjustorForTable(jt);
    ...
    </pre>

    After changes of the table's model or column model (with Table.setModel() or Table.setColumnModel())
    the TableCellSizeAdjustor must be informed through its method <tt>rebindListeners()</tt>:

    <pre>
    ...
    jt.setModel(newmodel);
    tcsa.rebindListeners();
    ...
    </pre>

    Implementation notes:

    <ul><li>TCSA works always in two steps: First, events on table changes are received and handled. This result
    either in a adjustment instruction (which rows in which column have to be recalculated) or in a
    recalculate-everything-flag. These instructions are stored internally and in a second step, they are
    processed and the table column widths and height are recalibrated.

    <li>Both steps are uncoupled: The event handlers result in calling toggleAdjustment() which puts a Runnable
    object on the AWT/Swing event queue. That Runnable executes runAdjustment() which performs the calibration task.

    <li>Even though threaded uncoupling is implemented, both parts are never executed in parallel. Instead, everything
    happens in the AWT/Swing event handling thread and therefore sequentially. For this reason, no monitors or
    locks are needed on the internal storage objects (<tt>Vector ajis</tt> and <tt>boolean recalccompletely</tt>).

    <li>Multiple events can occur until actual adjustment is performed. This happens when the events are
    passed before the adjustment Runnable is on top of the AWT event queue. In this case, adjustment instructions
    are combined as possible and of course, no new adjustment Runnables are put on the event queue.

    <li>TableCellSizeAdjustor tries to calculate as less cell sizes as possible. Complete adjustment of a table is
    only performed after structural changes. Complete adjustment of a column is only performed after removing
    the widest or highest element or shrinking it to a certain extent (see setReadjustFraction()).

    <li>Note that this class implements its Listener interfaces
    directly, not using inner classes. This special case is one of the very rare ones
    where I accept not using inner classes for event listener implementation.<p>
    Note that the current implementation of this object may be somehow inefficient or slow on larger tables.
    </ul>

    @author Dirk Hillbrecht 1999-2000, chitec/Dirk Hillbrecht 2000,2001,2002.
    Distributed under the terms of the {@link <a href="http://www.fsf.org/copyleft/lesser.html">GNU LGPL</a>}.
    @version $Id: TableCellSizeAdjustor.java,v 1.2 2006/03/01 12:35:46 nathell Exp $

*/
public final class TableCellSizeAdjustor implements TableModelListener,TableColumnModelListener {

   /** Constant for creation method: adjust only the width of the cells. */
   public final static int ADJUST_WIDTH=1;

   /** Constant for creation method: adjust only the height of the cells. */
   public final static int ADJUST_HEIGHT=2;

   /** Constant for creation method: adjust width and height of the cells.
       This is the default.
   */
   public final static int ADJUST_BOTH=3;

   /** internal constant: recalculate column sizes with respect to all cells */
   private final static int RESIZECOMPLETELY=0;

   /** internal constant: take maximum height of currently handled column into consideration to be new maximum height */
   private final static int SETHEIGHT=1;

   /** internal constant: set width of currently handled column */
   private final static int SETWIDTH=2;

   /** internal constant: use width and height of last computation */
   private final static int SETBOTH=3;

   /** internal constant: no new maximum values found in last computation */
   private final static int DONOTHING=4;

   /** The JTable this Adjustor is working on. */
   private JTable jt;

   /** The TableModel of the JTable. */
   private TableModel tm;

   /** The TableColumnModel of the JTable. */
   private TableColumnModel tcm;

   /** Internal flag: Adjust width? */
   private boolean adjustwidth;

   /** Internal flag: Adjust height? */
   private boolean adjustheight;

   /** Internal flag: Do the adjustment? */
   private boolean toggleadjust;

   /** Internal flag: Adjust by CellEditor size? */
   private boolean adjustbyeditor;

   /** Flag: adjustor enabled? */
   private boolean enabled;
   /** Internal flag: Are there any postponed adjustments due to disabling? */
   private boolean postponeadjustment;

   /** maximum needed width for columns */
   private int[] maxwidth;
   /** row index of widest entry in column */
   private int[] maxwidthidx;
   /** maximum needed height for columns */
   private int[] maxheight;
   /** row index of heighest entry in column */
   private int[] maxheightidx;
   /** the table's columns */
   private TableColumn[] tablecolumn;

   /** maximum handled row index so far (for optimisation of inserts at the end) */
   private int maxrow;
   /** maximum height for all rows */
   private int allmaxheight;
   /** column count */
   private int columncount;
   /** flag: Everything must be calculated */
   private boolean recalccompletely;

   /** Vector of adjustment instructions */
   private List ajis;

   /** fraction of change for maximum value to shrink before column size is recalculated completely */
   private int divident,divisor;

   /** Compatibility code for Java 1.3 */
   private TableCellRenderer headerrenderer;

   /** Wrapper for instructions on what to recalculate.
       Adjustment can be constained to a part of the table. E.g. after insertion of some rows, only
       those rows have to be looked at.
   */
   class AdjustmentInstruction {
      public int firstrow,lastrow,column;

      public AdjustmentInstruction(int fx,int lx,int cx) {
         firstrow=fx;
         lastrow=lx;
         column=cx;
      }

      /** Two instructions are equal if all internal values are equal. */
      public boolean equals(AdjustmentInstruction aji) {
         return (firstrow==aji.firstrow && lastrow==aji.lastrow && column==aji.column);
      }

      /** Combine (mathmatical: unite) this and the other AdjustmentInstruction.
      @return true, if combination was possible, false otherwise */
      public boolean combine(AdjustmentInstruction aji) {
         if (aji.column==column) {
            // aji ist subset of this
            if (aji.firstrow>=firstrow && aji.lastrow<=lastrow)
               return true;
            // aji is superset of this
            if (aji.firstrow<=firstrow && aji.lastrow>=lastrow) {
               firstrow=aji.firstrow;
               lastrow=aji.lastrow;
               return true;
            }
            // aji covers end of this or starts directly after it
            if (aji.firstrow<=lastrow+1 && aji.lastrow>=lastrow) {
               lastrow=aji.lastrow;
               return true;
            }
            // aji covers beginning of this or ends directly before it
            if (aji.firstrow<=firstrow && aji.lastrow>=firstrow-1) {
               firstrow=aji.firstrow;
               return true;
            }
         }
         return false;
      }

      /** debug purposes only */
      public String toString() {
         return "<AdjustmentInstruction: 1st row: "+firstrow+", last row: "+lastrow+", column: "+(column==TableModelEvent.ALL_COLUMNS?"ALL":Integer.toString(column))+">";
      }

   }

   /** Creates new Adjustor for width and height, puts it to table, returns it.
       Size adjustment is done on the base of the renderer objects.
    */
   public static TableCellSizeAdjustor adjustorForTable(JTable jt) {
      return new TableCellSizeAdjustor(jt,ADJUST_BOTH,false);
   }

   /** Creates new Adjustor with given adjustments, puts it to table, returns it.
       Size adjustment is done on the base of the renderer objects.
    */
   public static TableCellSizeAdjustor adjustorForTable(JTable jt,int mode) {
      return new TableCellSizeAdjustor(jt,mode,false);
   }

   /** Creates new Adjustor for width and height, puts it to table, returns it.
       Size adjustment is done either on the base of the renderer objects (<tt>bex=false</tt>)
       or on the base of the editor objects (<tt>bex=true</tt>).
    */
   public static TableCellSizeAdjustor adjustorForTable(JTable jt,boolean bex) {
      return new TableCellSizeAdjustor(jt,ADJUST_BOTH,bex);
   }

   /** Creates new Adjustor with given adjustments, puts it to table, returns it.
       Size adjustment is done either on the base of the renderer objects (<tt>bex=false</tt>)
       or on the base of the editor objects (<tt>bex=true</tt>).
    */
   public static TableCellSizeAdjustor adjustorForTable(JTable jt,int mode,boolean bex) {
      return new TableCellSizeAdjustor(jt,mode,bex);
   }

   /** "Real" constructor of the class. Private by design.
       Table is set into AUTO_RESIZE_OFF mode, models are received, listeners installed.
   */
   private TableCellSizeAdjustor(JTable jtx,int mode,boolean bex) {
      jt=jtx;
      jt.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);  // We are doing the job now!
      tm=jt.getModel();
      tcm=jt.getColumnModel();
      tm.addTableModelListener(this);
      tcm.addColumnModelListener(this);
      adjustwidth=(mode==ADJUST_WIDTH || mode==ADJUST_BOTH);
      adjustheight=(mode==ADJUST_HEIGHT || mode==ADJUST_BOTH);
      toggleadjust=true;
      adjustbyeditor=bex;
      ajis=new ArrayList();
      divident=3;
      divisor=4;
      recalccompletely=true;
      maxrow=0;
      enabled=true;
      postponeadjustment=false;
      headerrenderer=new JTableHeader().getDefaultRenderer();
   }

   // ***********************
   // Part I: general methods
   // ***********************

   /** Rebinds the listeners to the table's models.
       Must be called after any replacement of the table's TableModel or TableColumnModel.
       TableCellSizeAdjustor must be listener to events of these model objects. */
   public void reBindListeners() {
      TableModel newtm=jt.getModel();
      TableColumnModel newtcm=jt.getColumnModel();
      if (newtm!=tm) {
         tm.removeTableModelListener(this);
         tm=newtm;
         tm.addTableModelListener(this);
         recalccompletely=true;
      }
      if (newtcm!=tcm) {
         tcm.removeColumnModelListener(this);
         tcm=newtcm;
         tcm.addColumnModelListener(this);
         recalccompletely=true;
      }
   }

   /** change the "complete readjustment" fraction.
       If the widest (or highest) cell in a column shrinks, one has to think about shrinking the
       whole column. As only the maximum width of each column is stored, recalculation is difficult in this case.
       In fact, there is no other way than calculating the maximum width of <em>each</em> cell in the column.
       As this step might be somehow time-consuming, it is not preformed everytime the widest column shrinks
       but only if it shrinks below a certain threshold level. That level is defined to be the oldest maximum width
       multiplied with the fraction passed to this method. So, after calling <tt>setReadjustFactor(2,3)</tt>
       complete recalculation of a column would only occur if the widest (or highest) element shrinks below
       two thirds of its old width (or height).<p>
       The default value for the fraction is three fourth (3/4).
   */
   public void setReadjustFraction(int dividentx,int divisorx) {
      divident=dividentx;
      divisor=divisorx;
   }

   // *********************************************
   // Part II: Methods for the event handling phase
   // *********************************************

   /** From TableModelListener.
       We adjust cells every time something is inserted or updated.
   */
   public void tableChanged(TableModelEvent e) {
//    String ts="";
//    switch (e.getType()) {
//      case e.INSERT: ts="INSERT"; break;
//      case e.UPDATE: ts="UPDATE"; break;
//      case e.DELETE: ts="DELETE"; break;
//    }
//    System.out.println("TCSA.tC, Event: "+e.toString()+", Col: "+e.getColumn()+(e.getColumn()==e.ALL_COLUMNS?" (all)":"")
//    +", 1st Row: "+e.getFirstRow()+(e.getFirstRow()==e.HEADER_ROW?" (header) ":"")
//    +", last row: "+e.getLastRow()+(e.getLastRow()==e.HEADER_ROW?" (header) ":"")
//    +", Type: "+e.getType()+" ("+ts+")");
      if (!recalccompletely) {
         switch (e.getType()) {
            case TableModelEvent.UPDATE:
               if ((e.getColumn()==TableModelEvent.ALL_COLUMNS &&
                     ((e.getFirstRow()==TableModelEvent.HEADER_ROW && e.getLastRow()==TableModelEvent.HEADER_ROW)
                      || (e.getFirstRow()==0 && e.getLastRow()==Integer.MAX_VALUE)))
                     // This means "table structure changed"
                     || (tm.getRowCount()!=maxrow)) {
                  // This means "updated all data"
                  recalccompletely=true;
                  ajis.clear();
               }
               else
                  insertAJI(new AdjustmentInstruction(e.getFirstRow(),Math.min(maxrow,e.getLastRow()),e.getColumn()));
               break;
            case TableModelEvent.INSERT:
               maxrow+=e.getLastRow()-e.getFirstRow();
               if (e.getFirstRow()<maxrow)
                  shiftIndices(e.getFirstRow(),e.getLastRow()-e.getFirstRow());
               insertAJI(new AdjustmentInstruction(e.getFirstRow()+1,e.getLastRow(),e.getColumn()));
               break;
            case TableModelEvent.DELETE:
               maxrow-=e.getLastRow()-e.getFirstRow();
               shiftIndices(e.getFirstRow(),e.getFirstRow()-e.getLastRow());
               break;
         }
      }
      toggleAdjustment();
   }

   /** From TableColumnModelListener.
       Tells listeners that a column was added to the model. We try to readjust in this case. */
   public void columnAdded(TableColumnModelEvent e) {
      //System.out.println("TCSA.cA, Event: "+e.toString()+", from: "+e.getFromIndex()+", to: "+e.getToIndex());
      recalccompletely=true;
      toggleAdjustment();
   }

   /** From TableColumnModelListener.
       Tells listeners that a column was removed from the model. We try to readjust in this case. */
   public void columnRemoved(TableColumnModelEvent e) {
      //System.out.println("TCSA.cR, Event: "+e.toString()+", from: "+e.getFromIndex()+", to: "+e.getToIndex());
      recalccompletely=true;
      toggleAdjustment();
   }

   /** From TableColumnModelListener.
       Tells listeners that a column was repositioned. We do nothing in this case. */
   public void columnMoved(TableColumnModelEvent e) {
      //System.out.println("TCSA.cM, Event: "+e.toString()+", from: "+e.getFromIndex()+", to: "+e.getToIndex());
   }

   /** From TableColumnModelListener.
       Tells listeners that a column was moved due to a margin change. We do nothing in this case. */
   public void columnMarginChanged(ChangeEvent e) {
      //System.out.println("TCSA.cMC, Event: "+e.toString());
   }

   /** From TableColumnModelListener.
       Tells listeners that the selection model of the TableColumnModel changed. We do nothing in this case. */
   public void columnSelectionChanged(ListSelectionEvent e) {
      //System.out.println("TCSA.cSC, Event: "+e.toString());
   }

   /** Inserts a call for runAdjustment() into the GUI Event Queue.
       This method is the final step of each event handling (and can also be called directly).
       It places a Runnable in the AWT event queue which performs the actual table adjustment.
       The placement is only done if there has not yet been placed such Runnable which has not been
       executed yet.<p>
       Note that should call this method only from within the AWT event handling thread itself and that
       such a call should normally not be needed anyhow as the table and its model should generate
       appropriate events on changes that affect the TableCellSizeAdjustor.
   */
   public void toggleAdjustment() {
      //System.out.println("TCSA.toggleAdjustment() called in Thread: "+Thread.currentThread().getName());
      if (toggleadjust) {
         toggleadjust=false;
         if (enabled) {
            SwingUtilities.invokeLater(new Runnable() {
               public void run() {
                  runAdjustment();
               }
            });
         }
         else
            postponeadjustment=true;
      }
   }

   /** Enable and disable adjustor.
       Disabled adjustors do not do any adjustments, but they remember any adjustments that became needed during
       the disabling period. If any adjustments are needed, an adjustment is done immideately after re-enabling. */
   public void setEnabled(boolean ex) {
      if (ex==enabled)
         return;
      enabled=ex;
      if (enabled) {
         if (postponeadjustment) {
            SwingUtilities.invokeLater(new Runnable() {
               public void run() {
                  runAdjustment();
               }
            });
         }
      }
      else
         postponeadjustment=false;
   }

   /** Return enabling state. */
   public boolean isEnabled() {
      return enabled;
   }

   /** Helper method to shift indices on insertion or deletion of rows. */
   private void shiftIndices(int begin,int offset) {
      for (int i=0;i<columncount;i++) {
         if (maxwidthidx[i]>begin) {
            if (offset<0 && maxwidthidx[i]<begin-offset) {
               maxwidthidx[i]=-1;
               maxwidth[i]=-1;
               insertAJI(new AdjustmentInstruction(0,maxrow,i));
               continue;
            }
            maxwidthidx[i]+=offset;
         }
      }
   }

   /** Insert a new AdjustmentInstruction into the vector.
       This method tries to combine several instructions. */
   private void insertAJI(AdjustmentInstruction aji) {
      //System.out.println("inserting adjustment instruction: "+aji);
      for (int i=0;i<ajis.size();i++)
         if (((AdjustmentInstruction)ajis.get(i)).combine(aji))
            return;
      ajis.add(aji);
   }

   // ******************************************
   // Part III: Methods for the adjustment phase
   // ******************************************

   /** Main method of adjustment performance.
       This method is called from the AWT event handling thread to perform the adjustment.
       First, it decides whether a complete recalculation of everything has to take place.
       If not, it steps through the adjustment instructions and performs the appropriate steps. */
   private void runAdjustment() {
      //System.out.println("*START* runAdjustment, completely: "+recalccompletely);
      //System.out.println("TCSA.runAdjustment() is running");
      if (recalccompletely) {
         //System.out.println("recalc completely branch");
         initArrays();
         for (int i=0;i<columncount;i++)
            finishSingleAdjustment(RESIZECOMPLETELY,i);
      }
      else {
         //System.out.println("partially recalculating branch reached");
         for (int i=0;i<ajis.size();i++) {
            AdjustmentInstruction aji=(AdjustmentInstruction)ajis.get(i);
            //System.out.println("adjusting row "+aji.firstrow+" to row "+aji.lastrow+" in column "+aji.column);
            if (aji.column==TableModelEvent.ALL_COLUMNS)
               for (int j=0;j<columncount;j++)
                  finishSingleAdjustment(adjustColumnPart(aji.firstrow,aji.lastrow,j),j);
            else
               finishSingleAdjustment(adjustColumnPart(aji.firstrow,aji.lastrow,aji.column),aji.column);
         }
      }

      // These seem to be needed for com.sun.tsc.JTreeTable and do not seem to hurt otherwise
      JTableHeader jth=jt.getTableHeader();
      jt.invalidate();
      jth.invalidate();
      jt.repaint();
      jth.repaint();

      toggleadjust=true;
      recalccompletely=false;
      ajis.clear();

      //System.out.println("* END * runAdjustment");
   }

   /** Performs the real adjustment for one column after calculations have been done.
       Note that this method might get the instruction "RESIZECOMPLETELY" in which
       case it performs a complete column adjustment itself. */
   private void finishSingleAdjustment(int instr,int column) {
      //System.out.println("*     * finishSingleAdjustment started, instr: "+instr+", column: "+column);
      if (instr==RESIZECOMPLETELY)
         instr=adjustCompleteColumn(column);
      if (adjustwidth && (instr==SETWIDTH || instr==SETBOTH)) {
         try {
            //System.out.println("*     * finishSingleAdjustment: setting width to "+(maxwidth[column]+6));
            tablecolumn[column].setWidth(maxwidth[column]+6);
            tablecolumn[column].setPreferredWidth(maxwidth[column]+6);
         }
         catch (NullPointerException npe1) {
            //System.out.println("*     * finishSingleAdjustment: NPE reached");
            //npe1.printStackTrace();
         }
      }
      if (adjustheight && (instr==SETHEIGHT || instr==SETBOTH) && collectHeights()==SETHEIGHT)
         jt.setRowHeight(allmaxheight);
   }

   /** Collect height values for all columns.
       Cell height is adjusted for the complete table. Therefore, the height is computed
       as the maximum of the required column heights. */
   private int collectHeights() {
      int omh=allmaxheight;
      for (int i=0;i<columncount;i++)
         allmaxheight=Math.max(allmaxheight,maxheight[i]);
      return (omh<allmaxheight?SETHEIGHT:DONOTHING);
   }

   /** Init internal storages.
       This step has to be performed after structural changes of the table, most notably
       insertion or deletion of <em>columns</em>. Note that the TableCellSizeAdjustor readjusts
       the whole table in this case and does not perform any optimisations. */
   private void initArrays() {
      columncount=tm.getColumnCount();
      //System.out.println("TCSA.initArrays() called");
      maxwidth=new int[columncount];
      maxheight=new int[columncount];
      maxwidthidx=new int[columncount];
      maxheightidx=new int[columncount];
      tablecolumn=new TableColumn[columncount];
      for (int col=0;col<columncount;col++) {
         try {
            tablecolumn[col]=jt.getColumnModel().getColumn(col);
         } catch (IndexOutOfBoundsException ioobe) {}
      }
      allmaxheight=4;
   }

   /** Adjusts a complete column including header */
   private int adjustCompleteColumn(int column) {
      maxwidth[column]=4;
      maxheight[column]=4;
      maxwidthidx[column]=-10;
      maxheightidx[column]=-10;
      adjustColumnPart(-1,jt.getRowCount()-1,column);
      return SETBOTH;
   }

   /** Adjust the part of a column.
       The "heart" of the whole object. Actual computation of cell sizes is done in this very routine.
       To do this, this method passes every value of the wanted part of the column
       to the appropriate TableCellRenderer or TableCellEditor and stores the highest preferredSize().width
       and .height it receives.<p>

       Note that this methods works on the internal fields of the object rather than passing any
       parameters forth and back. It does however return a handling instruction informing about any
       changes that have occured.
   */
   private int adjustColumnPart(int firstrow,int lastrow,int column) {
      //System.out.println("*     * adjustColumnPart started, first: "+firstrow+", last: "+lastrow+", column: "+column);
      try {
         boolean headerwidth=false;
         if (firstrow==-1) {
            TableCellRenderer tcr=tablecolumn[column].getHeaderRenderer();
            if (tcr==null && headerrenderer!=null) {
               tcr=headerrenderer;
               tablecolumn[column].setHeaderRenderer(tcr);
            }
            if (tcr!=null) {
               Component c=tcr.getTableCellRendererComponent(jt,tm.getColumnName(column),false,false,-1,column);
               int newwidth=(c==null?0:c.getPreferredSize().width);
               if (newwidth>maxwidth[column]) {
                  maxwidth[column]=newwidth;
                  headerwidth=true;
               }
            }
            if (lastrow==-1)
               return (headerwidth?SETWIDTH:DONOTHING);
            firstrow=0;
         }
         int mw=maxwidth[column];
         int mh=maxheight[column];
         int oldmw=mw;
         int oldmh=mh;
         int mwi=maxwidthidx[column];
         int mhi=maxheightidx[column];
         //System.out.println("adjusting column "+column+", row "+firstrow+"-"+lastrow+", mwi: "+mwi+", mhi: "+mhi);
         TableCellRenderer tcr=null;
         TableCellEditor tce=null;
         for (int row=firstrow;row<=lastrow;row++) {
            try {
               if (adjustbyeditor)
                  tce=jt.getCellEditor(row,column);
               else
                  tcr=jt.getCellRenderer(row,column);
               Object o=tm.getValueAt(row,column);
               Dimension d=(adjustbyeditor?tce.getTableCellEditorComponent(jt,o,false,row,column):tcr.getTableCellRendererComponent(jt,o,false,false,row,column)).getPreferredSize();
               if (row==mwi && (d.width*divisor<mw*divident)) {
                  //System.out.println("*     * adjustColumnPart: returning RESIZECOMPLETELY (a)");
                  return RESIZECOMPLETELY;
               }
               if (row==mhi && (d.height*divisor<mh*divident)) {
                  //System.out.println("*     * adjustColumnPart: returning RESIZECOMPLETELY (b)");
                  return RESIZECOMPLETELY;
               }
               if (d.width>mw) {
                  mw=d.width;
                  mwi=row;
               }
               if (d.height>mh) {
                  mh=d.height;
                  mhi=row;
               }
            }
            catch (NullPointerException npe) {
               //System.out.println("*     * adjustColumnPart: inner NPE reached, row: "+row);
               //npe.printStackTrace();
            }
            catch (IllegalArgumentException iae) {
               //System.out.println("*     * adjustColumnPart: IAE reached, row: "+row+", IAE: "+iae.toString());
               //iae.printStackTrace();
            }
            catch (IndexOutOfBoundsException ioobe) {
               //System.out.println("*     * adjustColumnPart: IOOBE reached, row: "+row+", IOOBE: "+ioobe.toString());
               //ioobe.printStackTrace();
            }
         }
         boolean aw=mw!=oldmw || headerwidth;
         boolean ah=mh!=oldmh;
         if (aw) {
            maxwidth[column]=mw;
            maxwidthidx[column]=mwi;
         }
         if (ah) {
            maxheight[column]=mh;
            maxheightidx[column]=mhi;
         }
         //System.out.println("*     * adjustColumnPart: returning "+(aw?(ah?"SETBOTH":"SETWIDTH"):(ah?"SETHEIGHT":"DONOTHING"))+", maxwidth: "+maxwidth[column]);
         return (aw?(ah?SETBOTH:SETWIDTH):(ah?SETHEIGHT:DONOTHING));
      }
      catch (NullPointerException npe) {
         //System.out.println("*     * adjustColumnPart: outer NPE reached, doing nothing");
         //npe.printStackTrace();
         return DONOTHING;
      }
   }

}

// end of file
