RiverLayout.java

00001 package edu.stanford.hci.r3.util.layout;
00002 
00003 import java.awt.*;
00004 import java.util.*;
00005 
00037 public class RiverLayout extends FlowLayout implements LayoutManager, java.io.Serializable {
00038 
00039         public static final String CENTER_ALIGN = "center";
00040 
00041         public static final String HFILL = "hfill";
00042 
00043         public static final String LEFT_ALIGN = "left";
00044 
00045         public static final String LINE_BREAK = "br";
00046 
00047         public static final String PARAGRAPH_BREAK = "p";
00048 
00049         public static final String RIGHT_ALIGN = "right";
00050 
00051         public static final String TAB_STOP = "tab";
00052 
00053         public static final String VCENTER = "vcenter";
00054 
00055         public static final String VFILL = "vfill";
00056 
00057         public static final String VTOP = "vtop";
00058 
00059         final Map<Component, String> constraints = new HashMap<Component, String>();
00060 
00061         Insets extraInsets;
00062 
00063         int hgap;
00064 
00065         final Insets totalInsets = new Insets(0, 0, 0, 0);// Dummy values. Set by getInsets()
00066 
00067         String valign = VCENTER;
00068 
00069         int vgap;
00070 
00071         public RiverLayout() {
00072                 this(10, 5);
00073         }
00074 
00075         public RiverLayout(int hgap, int vgap) {
00076                 this.hgap = hgap;
00077                 this.vgap = vgap;
00078                 setExtraInsets(new Insets(0, hgap, hgap, hgap));
00079         }
00080 
00087         public void addLayoutComponent(String name, Component comp) {
00088                 constraints.put(comp, name);
00089         }
00090 
00091         protected void adjustAlignment(Component m) {
00092                 if (hasConstraint(m, RiverLayout.LEFT_ALIGN))
00093                         setAlignment(FlowLayout.LEFT);
00094                 else if (hasConstraint(m, RiverLayout.RIGHT_ALIGN))
00095                         setAlignment(FlowLayout.RIGHT);
00096                 else if (hasConstraint(m, RiverLayout.CENTER_ALIGN))
00097                         setAlignment(FlowLayout.CENTER);
00098                 if (hasConstraint(m, RiverLayout.VTOP))
00099                         valign = VTOP;
00100                 else if (hasConstraint(m, RiverLayout.VCENTER))
00101                         valign = VCENTER;
00102 
00103         }
00104 
00108         protected Ruler calcTabs(Container target) {
00109                 Ruler ruler = new Ruler();
00110                 int nmembers = target.getComponentCount();
00111 
00112                 int x = 0;
00113                 int tabIndex = 0; // First tab stop
00114                 for (int i = 0; i < nmembers; i++) {
00115                         Component m = target.getComponent(i);
00116                         // if (m.isVisible()) {
00117                         if (isFirstInRow(m) || i == 0) {
00118                                 x = 0;
00119                                 tabIndex = 0;
00120                         } else
00121                                 x += hgap;
00122                         if (hasConstraint(m, TAB_STOP)) {
00123                                 ruler.setTab(tabIndex, x); // Will only increase
00124                                 x = ruler.getTab(tabIndex++); // Jump forward if neccesary
00125                         }
00126                         Dimension d = m.getPreferredSize();
00127                         x += d.width;
00128                 }
00129                 // }
00130                 return ruler;
00131         }
00132 
00133         public Insets getExtraInsets() {
00134                 return extraInsets;
00135         }
00136 
00140         public int getHgap() {
00141                 return hgap;
00142         }
00143 
00144         protected Insets getInsets(Container target) {
00145                 Insets insets = target.getInsets();
00146                 totalInsets.top = insets.top + extraInsets.top;
00147                 totalInsets.left = insets.left + extraInsets.left;
00148                 totalInsets.bottom = insets.bottom + extraInsets.bottom;
00149                 totalInsets.right = insets.right + extraInsets.right;
00150                 return totalInsets;
00151         }
00152 
00156         public int getVgap() {
00157                 return vgap;
00158         }
00159 
00160         boolean hasConstraint(Component comp, String test) {
00161                 String cons = (String) constraints.get(comp);
00162                 if (cons == null)
00163                         return false;
00164                 StringTokenizer tokens = new StringTokenizer(cons);
00165                 while (tokens.hasMoreTokens())
00166                         if (tokens.nextToken().equals(test))
00167                                 return true;
00168                 return false;
00169         }
00170 
00171         boolean hasHfill(Component comp) {
00172                 return hasConstraint(comp, RiverLayout.HFILL);
00173         }
00174 
00175         boolean hasVfill(Component comp) {
00176                 return hasConstraint(comp, RiverLayout.VFILL);
00177         }
00178 
00179         boolean isFirstInRow(Component comp) {
00180                 String cons = (String) constraints.get(comp);
00181                 return cons != null
00182                                 && (cons.indexOf(RiverLayout.LINE_BREAK) != -1 || cons
00183                                                 .indexOf(RiverLayout.PARAGRAPH_BREAK) != -1);
00184         }
00185 
00196         public void layoutContainer(Container target) {
00197                 setAlignment(FlowLayout.LEFT);
00198                 synchronized (target.getTreeLock()) {
00199                         Insets insets = getInsets(target);
00200                         int maxwidth = target.getWidth() - (insets.left + insets.right);
00201                         int maxheight = target.getHeight() - (insets.top + insets.bottom);
00202 
00203                         int nmembers = target.getComponentCount();
00204                         int x = 0, y = insets.top + vgap;
00205                         int rowh = 0, start = 0, moveDownStart = 0;
00206 
00207                         boolean ltr = target.getComponentOrientation().isLeftToRight();
00208                         Component toHfill = null;
00209                         Component toVfill = null;
00210                         Ruler ruler = calcTabs(target);
00211                         int tabIndex = 0;
00212 
00213                         for (int i = 0; i < nmembers; i++) {
00214                                 Component m = target.getComponent(i);
00215                                 // if (m.isVisible()) {
00216                                 Dimension d = m.getPreferredSize();
00217                                 m.setSize(d.width, d.height);
00218 
00219                                 if (isFirstInRow(m))
00220                                         tabIndex = 0;
00221                                 if (hasConstraint(m, TAB_STOP))
00222                                         x = ruler.getTab(tabIndex++);
00223                                 if (!isFirstInRow(m)) {
00224                                         if (i > 0 && !hasConstraint(m, TAB_STOP)) {
00225                                                 x += hgap;
00226                                         }
00227                                         x += d.width;
00228                                         rowh = Math.max(rowh, d.height);
00229                                 } else {
00230                                         if (toVfill != null && moveDownStart == 0) {
00231                                                 moveDownStart = i;
00232                                         }
00233                                         if (toHfill != null) {
00234                                                 toHfill.setSize(toHfill.getWidth() + maxwidth - x, toHfill.getHeight());
00235                                                 x = maxwidth;
00236                                         }
00237                                         moveComponents(target, insets.left, y, maxwidth - x, rowh, start, i, ltr, ruler);
00238                                         x = d.width;
00239                                         y += vgap + rowh;
00240                                         if (hasConstraint(m, PARAGRAPH_BREAK))
00241                                                 y += 2 * vgap;
00242                                         rowh = d.height;
00243                                         start = i;
00244                                         toHfill = null;
00245                                 }
00246                                 // }
00247                                 if (hasHfill(m)) {
00248                                         toHfill = m;
00249                                 }
00250                                 if (hasVfill(m)) {
00251                                         toVfill = m;
00252                                 }
00253                                 adjustAlignment(m);
00254                         }
00255 
00256                         if (toVfill != null && moveDownStart == 0) { // Don't move anything if hfill
00257                                 // component is last component
00258                                 moveDownStart = nmembers;
00259                         }
00260                         if (toHfill != null) { // last component
00261                                 toHfill.setSize(toHfill.getWidth() + maxwidth - x, toHfill.getHeight());
00262                                 x = maxwidth;
00263                         }
00264                         moveComponents(target, insets.left, y, maxwidth - x, rowh, start, nmembers, ltr, ruler);
00265                         int yslack = maxheight - (y + rowh);
00266                         if (yslack != 0 && toVfill != null) {
00267                                 toVfill.setSize(toVfill.getWidth(), yslack + toVfill.getHeight());
00268                                 relMove(target, 0, yslack, moveDownStart, nmembers);
00269                         }
00270                 }
00271         }
00272 
00284         public Dimension minimumLayoutSize(Container target) {
00285                 synchronized (target.getTreeLock()) {
00286                         Dimension dim = new Dimension(0, 0);
00287                         Dimension rowDim = new Dimension(0, 0);
00288                         int nmembers = target.getComponentCount();
00289                         boolean firstVisibleComponent = true;
00290                         int tabIndex = 0;
00291                         Ruler ruler = calcTabs(target);
00292 
00293                         for (int i = 0; i < nmembers; i++) {
00294                                 Component m = target.getComponent(i);
00295                                 // if (m.isVisible()) {
00296                                 if (isFirstInRow(m)) {
00297                                         tabIndex = 0;
00298                                         dim.width = Math.max(dim.width, rowDim.width);
00299                                         dim.height += rowDim.height + vgap;
00300                                         if (hasConstraint(m, PARAGRAPH_BREAK))
00301                                                 dim.height += 2 * vgap;
00302                                         rowDim = new Dimension(0, 0);
00303                                 }
00304                                 if (hasConstraint(m, TAB_STOP))
00305                                         rowDim.width = ruler.getTab(tabIndex++);
00306                                 Dimension d = m.getMinimumSize();
00307                                 rowDim.height = Math.max(rowDim.height, d.height);
00308                                 if (firstVisibleComponent) {
00309                                         firstVisibleComponent = false;
00310                                 } else {
00311                                         rowDim.width += hgap;
00312                                 }
00313                                 rowDim.width += d.width;
00314                                 // }
00315                         }
00316                         dim.width = Math.max(dim.width, rowDim.width);
00317                         dim.height += rowDim.height;
00318 
00319                         Insets insets = getInsets(target);
00320                         dim.width += insets.left + insets.right;// + hgap * 2;
00321                         dim.height += insets.top + insets.bottom;// + vgap * 2;
00322                         return dim;
00323                 }
00324         }
00325 
00344         protected void moveComponents(Container target, int x, int y, int width, int height,
00345                         int rowStart, int rowEnd, boolean ltr, Ruler ruler) {
00346                 synchronized (target.getTreeLock()) {
00347                         switch (getAlignment()) {
00348                         case FlowLayout.LEFT:
00349                                 x += ltr ? 0 : width;
00350                                 break;
00351                         case FlowLayout.CENTER:
00352                                 x += width / 2;
00353                                 break;
00354                         case FlowLayout.RIGHT:
00355                                 x += ltr ? width : 0;
00356                                 break;
00357                         case LEADING:
00358                                 break;
00359                         case TRAILING:
00360                                 x += width;
00361                                 break;
00362                         }
00363                         int tabIndex = 0;
00364                         for (int i = rowStart; i < rowEnd; i++) {
00365                                 Component m = target.getComponent(i);
00366                                 // if (m.isVisible()) {
00367                                 if (hasConstraint(m, TAB_STOP))
00368                                         x = getInsets(target).left + ruler.getTab(tabIndex++);
00369                                 int dy = (valign == VTOP) ? 0 : (height - m.getHeight()) / 2;
00370                                 if (ltr) {
00371                                         m.setLocation(x, y + dy);
00372                                 } else {
00373                                         m.setLocation(target.getWidth() - x - m.getWidth(), y + dy);
00374                                 }
00375                                 x += m.getWidth() + hgap;
00376                                 // }
00377                         }
00378                 }
00379         }
00380 
00392         public Dimension preferredLayoutSize(Container target) {
00393                 synchronized (target.getTreeLock()) {
00394                         Dimension dim = new Dimension(0, 0);
00395                         Dimension rowDim = new Dimension(0, 0);
00396                         int nmembers = target.getComponentCount();
00397                         boolean firstVisibleComponent = true;
00398                         int tabIndex = 0;
00399                         Ruler ruler = calcTabs(target);
00400 
00401                         for (int i = 0; i < nmembers; i++) {
00402                                 Component m = target.getComponent(i);
00403                                 // if (m.isVisible()) {
00404                                 if (isFirstInRow(m)) {
00405                                         tabIndex = 0;
00406                                         dim.width = Math.max(dim.width, rowDim.width);
00407                                         dim.height += rowDim.height + vgap;
00408                                         if (hasConstraint(m, PARAGRAPH_BREAK))
00409                                                 dim.height += 2 * vgap;
00410                                         rowDim = new Dimension(0, 0);
00411                                 }
00412                                 if (hasConstraint(m, TAB_STOP))
00413                                         rowDim.width = ruler.getTab(tabIndex++);
00414                                 Dimension d = m.getPreferredSize();
00415                                 rowDim.height = Math.max(rowDim.height, d.height);
00416                                 if (firstVisibleComponent) {
00417                                         firstVisibleComponent = false;
00418                                 } else {
00419                                         rowDim.width += hgap;
00420                                 }
00421                                 rowDim.width += d.width;
00422                                 // }
00423                         }
00424                         dim.width = Math.max(dim.width, rowDim.width);
00425                         dim.height += rowDim.height;
00426 
00427                         Insets insets = getInsets(target);
00428                         dim.width += insets.left + insets.right;// + hgap * 2;
00429                         dim.height += insets.top + insets.bottom;// + vgap * 2;
00430                         return dim;
00431                 }
00432         }
00433 
00434         protected void relMove(Container target, int dx, int dy, int rowStart, int rowEnd) {
00435                 synchronized (target.getTreeLock()) {
00436                         for (int i = rowStart; i < rowEnd; i++) {
00437                                 Component m = target.getComponent(i);
00438                                 // if (m.isVisible()) {
00439                                 m.setLocation(m.getX() + dx, m.getY() + dy);
00440                                 // }
00441                         }
00442 
00443                 }
00444         }
00445 
00453         public void removeLayoutComponent(Component comp) {
00454                 constraints.remove(comp);
00455         }
00456 
00457         public void setExtraInsets(Insets newExtraInsets) {
00458                 extraInsets = newExtraInsets;
00459         }
00460 
00464         public void setHgap(int hgap) {
00465                 this.hgap = hgap;
00466         }
00467 
00471         public void setVgap(int vgap) {
00472                 this.vgap = vgap;
00473         }
00474 
00475 }
00476 
00477 class Ruler {
00478         public static void main(String[] args) {
00479                 Ruler r = new Ruler();
00480                 r.setTab(0, 10);
00481                 r.setTab(1, 20);
00482                 r.setTab(2, 30);
00483                 System.out.println(r);
00484                 r.setTab(1, 25);
00485                 System.out.println(r);
00486                 System.out.println(r.getTab(0));
00487         }
00488 
00489         private final Vector<Integer> tabs = new Vector<Integer>();
00490 
00491         public int getTab(int num) {
00492                 return ((Integer) tabs.get(num)).intValue();
00493         }
00494 
00495         public void setTab(int num, int xpos) {
00496                 if (num >= tabs.size())
00497                         tabs.add(num, new Integer(xpos));
00498                 else {
00499                         // Transpose all tabs from this tab stop and onwards
00500                         int delta = xpos - getTab(num);
00501                         if (delta > 0) {
00502                                 for (int i = num; i < tabs.size(); i++) {
00503                                         tabs.set(i, new Integer(getTab(i) + delta));
00504                                 }
00505                         }
00506                 }
00507         }
00508 
00509         public String toString() {
00510                 StringBuffer ret = new StringBuffer(getClass().getName() + " {");
00511                 for (int i = 0; i < tabs.size(); i++) {
00512                         ret.append(tabs.get(i));
00513                         if (i < tabs.size() - 1)
00514                                 ret.append(',');
00515                 }
00516                 ret.append('}');
00517                 return ret.toString();
00518         }
00519 }

Generated on Sat Apr 14 18:21:38 2007 for R3 Paper Toolkit by  doxygen 1.4.7