00001 package edu.stanford.hci.r3.util.layout;
00002
00003
00004
00005
00006
00007
00008
00009
00010 import java.awt.*;
00011 import java.util.*;
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054 public class StackedLayout implements LayoutManager {
00055 static final int ABS = 0x08;
00056
00057 static final int BACK = 0x02;
00058
00059
00060 static final int CENTER = 0x00;
00061
00062 static final int FILL = 0x04;
00063
00064 static final int FLUSH = 0x10;
00065
00066 static final int FRONT = 0x01;
00067
00069 public static final int HORIZONTAL = 0;
00070
00071 static final int POSMASK = 0x03;
00072
00073 static final int SIZEMASK = 0x0C;
00074
00076 public static final int VERTICAL = 1;
00077
00078 final Map<Component, int[]> codeTable = new Hashtable<Component, int[]>();
00079
00080 int defaultCode[] = { CENTER, CENTER, 0, 0 };
00081
00082 int margin = 2;
00083
00084 int orientation = HORIZONTAL;
00085
00086
00087
00088
00089
00091 public StackedLayout() {
00092 }
00093
00095 public StackedLayout(int orientation) {
00096 this.orientation = orientation;
00097 }
00098
00102 public StackedLayout(int orientation, int margin) {
00103 this.orientation = orientation;
00104 this.margin = margin;
00105 }
00106
00107
00108
00109
00110
00113 public void addLayoutComponent(String tag, Component comp) {
00114 tag = tag.toUpperCase().trim();
00115 int hcode = CENTER, vcode = CENTER, harg = 0, varg = 0;
00116 int i, l = tag.length(), n;
00117 for (i = 0; i < l;) {
00118 if (tag.startsWith("CENTER", i)) {
00119 i += 6;
00120 } else if (tag.startsWith("LEFT", i)) {
00121 i += 4;
00122 hcode |= FRONT;
00123 } else if (tag.startsWith("TOP", i)) {
00124 i += 3;
00125 vcode |= FRONT;
00126 } else if (tag.startsWith("RIGHT", i)) {
00127 i += 5;
00128 hcode |= BACK;
00129 } else if (tag.startsWith("BOTTOM", i)) {
00130 i += 6;
00131 vcode |= BACK;
00132 } else if (tag.startsWith("WIDE", i)) {
00133 i += 4;
00134 hcode |= FILL;
00135 if (tag.startsWith("*", i)) {
00136 i++;
00137 n = countDigits(tag, i);
00138 harg = parseArg(tag, i, n);
00139 i += n;
00140 } else
00141 harg = 1;
00142 } else if (tag.startsWith("TALL", i)) {
00143 i += 4;
00144 vcode |= FILL;
00145 if (tag.startsWith("*", i)) {
00146 i++;
00147 n = countDigits(tag, i);
00148 varg = parseArg(tag, i, n);
00149 i += n;
00150 } else
00151 varg = 1;
00152 } else if (tag.startsWith("FILL", i)) {
00153 i += 4;
00154 hcode |= FILL;
00155 vcode |= FILL;
00156 if (tag.startsWith("*", i)) {
00157 i++;
00158 n = countDigits(tag, i);
00159 harg = varg = parseArg(tag, i, n);
00160 i += n;
00161 } else
00162 harg = varg = 1;
00163 } else if (tag.startsWith("WIDTH", i)) {
00164 i += 5;
00165 hcode |= ABS;
00166 if (tag.startsWith("=", i)) {
00167 i++;
00168 n = countDigits(tag, i);
00169 harg = parseArg(tag, i, n);
00170 i += n;
00171 } else {
00172 harg = -1;
00173 break;
00174 }
00175 } else if (tag.startsWith("HEIGHT", i)) {
00176 i += 6;
00177 vcode |= ABS;
00178 if (tag.startsWith("=", i)) {
00179 i++;
00180 n = countDigits(tag, i);
00181 varg = parseArg(tag, i, n);
00182 i += n;
00183 } else {
00184 varg = -1;
00185 break;
00186 }
00187 } else if (tag.startsWith("FLUSH", i)) {
00188 i += 5;
00189 hcode |= FLUSH;
00190 vcode |= FLUSH;
00191 } else {
00192 harg = -1;
00193 break;
00194 }
00195 for (; (i < l) && Character.isWhitespace(tag.charAt(i)); i++)
00196 ;
00197 }
00198 if ((harg == -1) || (varg == -1))
00199 System.out.println("StackLayout: can't understand \"" + tag + "\"");
00200 else {
00201 int codes[] = { hcode, vcode, harg, varg };
00202 codeTable.put(comp, codes);
00203 }
00204 }
00205
00206 Dimension computeLayoutSize(Container parent, boolean preferred) {
00207 Insets in = parent.getInsets();
00208 int inW = in.left + in.right, inH = in.top + in.bottom;
00209 int n = parent.getComponentCount();
00210 if (orientation == HORIZONTAL) {
00211 int maxH = 0, totW = 0, m;
00212 for (int i = 0; i < n; i++) {
00213 Component comp = parent.getComponent(i);
00214 if (comp.isVisible()) {
00215 int code = getCode(comp)[orientation];
00216 m = ((code & FLUSH) == 0 ? margin : 0);
00217 Dimension d = (preferred && ((code & SIZEMASK) == FILL) ? comp
00218 .getPreferredSize() : comp.getMinimumSize());
00219 maxH = Math.max(maxH, d.height + 2 * m);
00220 totW += d.width + 2 * m;
00221 }
00222 }
00223 return new Dimension(totW + inW, maxH + inH);
00224 } else {
00225 int maxW = 0, totH = 0, m;
00226 for (int i = 0; i < n; i++) {
00227 Component comp = parent.getComponent(i);
00228 if (comp.isVisible()) {
00229 int code = getCode(comp)[orientation];
00230 m = ((code & FLUSH) == 0 ? margin : 0);
00231 Dimension d = (preferred && ((code & SIZEMASK) == FILL) ? comp
00232 .getPreferredSize() : comp.getMinimumSize());
00233 maxW = Math.max(maxW, d.width + 2 * m);
00234 totH += d.height + 2 * m;
00235 }
00236 }
00237 return new Dimension(maxW + inW, totH + inH);
00238 }
00239 }
00240
00241 int countDigits(String tag, int i) {
00242 int j, l = tag.length();
00243 for (j = i; (j < l) && Character.isDigit(tag.charAt(j)); j++)
00244 ;
00245 return j - i;
00246 }
00247
00248 int[] getCode(Component comp) {
00249 int code[] = (int[]) codeTable.get(comp);
00250 return (code == null ? defaultCode : code);
00251 }
00252
00254 public void layoutContainer(Container parent) {
00255 int along = orientation, across = (orientation + 1) % 2;
00256 int n = parent.getComponentCount();
00257 Insets in = parent.getInsets();
00258 Dimension sz = parent.getSize();
00259 int W = sz.width - in.left - in.right, H = sz.height - in.top - in.bottom;
00260 int L = (orientation == HORIZONTAL ? W : H),
00261 D = (orientation == HORIZONTAL ? H : W);
00262
00263
00264
00265 int nFills = 0, nRubber = 0;
00266 int sum = 0, prev;
00267 prev = FRONT;
00268 int codes[][] = new int[n][];
00269 int sizes[][] = new int[n][2];
00270 for (int i = 0; i < n; i++) {
00271 Component comp = parent.getComponent(i);
00272 if (comp.isVisible()) {
00273 Dimension d = comp.getMinimumSize();
00274 int code[] = getCode(comp);
00275 int size[] = sizes[i];
00276 codes[i] = code;
00277 size[0] = d.width;
00278 size[1] = d.height;
00279 int l = size[along], c = code[along];
00280 switch (c & SIZEMASK) {
00281 case FILL:
00282 nFills += code[along + 2];
00283 break;
00284 case ABS:
00285 sum += code[along + 2];
00286 break;
00287 default:
00288 sum += l;
00289 break;
00290 }
00291 switch (c & POSMASK) {
00292 case CENTER:
00293 nRubber++;
00294 break;
00295 case BACK:
00296 if (prev != BACK)
00297 nRubber++;
00298 break;
00299 }
00300 if ((c & FLUSH) == 0)
00301 sum += 2 * margin;
00302 prev = (c & POSMASK);
00303 }
00304 }
00305 if (prev == CENTER)
00306 nRubber++;
00307
00308
00309 int rubber = ((nFills != 0) || (nRubber == 0) ? 0 : Math.max(0, (L - sum) / nRubber)), fill = (nFills == 0 ? 0
00310 : Math.max(0, (L - sum) / nFills));
00311
00312
00313 int r = (orientation == HORIZONTAL ? in.left : in.top),
00314 s0 = (orientation == HORIZONTAL ? in.top : in.left),
00315 s, l, d, m;
00316 prev = FRONT;
00317 for (int i = 0; i < n; i++) {
00318 int code[] = codes[i];
00319 int size[] = sizes[i];
00320 if (code != null) {
00321 int c = code[along], ca = code[across];
00322 m = ((c & FLUSH) == 0 ? margin : 0);
00323 r += m;
00324 s = s0 + m;
00325 l = size[along];
00326 d = size[across];
00327 switch (c & SIZEMASK) {
00328 case FILL:
00329 if (fill > 0)
00330 l = fill * code[along + 2];
00331 break;
00332 case ABS:
00333 l = code[along + 2];
00334 break;
00335 }
00336 switch (c & POSMASK) {
00337 case CENTER:
00338 r += rubber;
00339 break;
00340 case BACK:
00341 if (prev != BACK)
00342 r += rubber;
00343 break;
00344 }
00345 prev = (c & POSMASK);
00346 switch (ca & SIZEMASK) {
00347 case FILL:
00348 d = D - 2 * m;
00349 break;
00350 case ABS:
00351 d = code[across + 2];
00352 break;
00353 }
00354 switch (ca & POSMASK) {
00355 case BACK:
00356 s += D - d;
00357 break;
00358 case CENTER:
00359 s += (D - d) / 2;
00360 break;
00361 }
00362 Component comp = parent.getComponent(i);
00363 if (orientation == HORIZONTAL)
00364 comp.setBounds(r, s, l, d);
00365 else
00366 comp.setBounds(s, r, d, l);
00367 r += l + m;
00368 }
00369 }
00370 }
00371
00373 public Dimension minimumLayoutSize(Container parent) {
00374 return computeLayoutSize(parent, false);
00375 }
00376
00377 int parseArg(String tag, int i, int n) {
00378 int num = -1;
00379 try {
00380 num = Integer.parseInt(tag.substring(i, i + n));
00381 } catch (Exception e) {
00382 }
00383 return num;
00384 }
00385
00387 public Dimension preferredLayoutSize(Container parent) {
00388 return computeLayoutSize(parent, true);
00389 }
00390
00392 public void removeLayoutComponent(Component comp) {
00393 codeTable.remove(comp);
00394 }
00395
00396 boolean stretches(Component comp) {
00397 int c[] = getCode(comp);
00398 return c[orientation] == FILL;
00399 }
00400
00401 }