Changeset 866


Ignore:
Timestamp:
02/08/14 15:08:51 (10 years ago)
Author:
davidb
Message:

Code to allow interactive widgets to have their respective corners anchored both left and right, top and bottom, so will naturally resize when windows size changed

Location:
trunk/src/org/expeditee/items/widgets
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/expeditee/items/widgets/InteractiveWidget.java

    r805 r866  
    1313import java.awt.event.KeyListener;
    1414import java.awt.event.MouseEvent;
     15import java.io.PrintWriter;
     16import java.io.StringWriter;
    1517import java.lang.reflect.Constructor;
    1618import java.util.ArrayList;
     
    2022import java.util.LinkedList;
    2123import java.util.List;
     24import java.util.StringTokenizer;
    2225
    2326import javax.swing.JComponent;
    2427import javax.swing.SwingUtilities;
    2528
     29import org.apache.commons.cli.CommandLine;
     30import org.apache.commons.cli.CommandLineParser;
     31import org.apache.commons.cli.GnuParser;
     32import org.apache.commons.cli.HelpFormatter;
     33import org.apache.commons.cli.Option;
     34import org.apache.commons.cli.OptionBuilder;
     35import org.apache.commons.cli.Options;
     36import org.apache.commons.cli.ParseException;
    2637import org.expeditee.actions.Actions;
    2738import org.expeditee.gui.Browser;
     
    7283        protected Text _textRepresentation;
    7384
     85        protected Float _anchorLeft;
     86        protected Float _anchorRight;
     87        protected Float _anchorTop;
     88        protected Float _anchorBottom;
     89       
    7490        protected final static Color FREESPACE_BACKCOLOR = new Color(100, 100, 100);
    7591
     
    135151
    136152                String text = source.getText();
    137                 if (text == null)
     153                if (text == null) {
    138154                        throw new IllegalArgumentException("source does not have any text");
     155                }
    139156
    140157                text = text.trim();
    141158
    142                 // Check starts with the widget tag and seporator
     159                // Check starts with the widget tag and separator
    143160                if (!text.startsWith(TAG + ":"))
    144                         throw new IllegalArgumentException("Source text must begin with \""
    145                                         + TAG + ":\"");
    146 
     161                        throw new IllegalArgumentException("Source text must begin with \"" + TAG + ":\"");
     162
     163                // skip over the '@iw:' preamble
    147164                text = text.substring(TAG.length() + 1).trim();
    148165
    149166                int index = 0;
    150167                if (text.length() > 0) {
    151                         index = text.indexOf(':'); // used for
    152                         // signifying start
    153                         // of arguments
    154                 }
    155 
    156                 if (index == 0)
    157                         throw new IllegalArgumentException("Source text must begin with \""
    158                                         + TAG + "\"");
    159 
    160                 String[] tokens = (index == -1) ? text.split("\\s+") : text.substring(
    161                                 0, index).split(" ");
    162 
     168                    // Having removed @iw:, then next ':' is used for signifying start of arguments
     169                    index = text.indexOf(':');
     170                }
     171
     172                if (index == 0) {
     173                    throw new IllegalArgumentException("Source text must begin with \"" + TAG + "\"");
     174                }
     175
     176                //
     177                // Step 1:
     178                //   For an X-rayable text item in the form:
     179                //     @iw: <class name> [options] width height : <rest...>
     180                //
     181                //   Parse the 'core' part of the X-rayable text item
     182                //   i.e, the text between the first ':' and the second ':'
     183                // 
     184                //
     185               
     186                String tokens_str = (index == -1) ? text : text.substring(0, index);
     187
     188                String[] tokens = parseArgsApache(tokens_str);
     189
     190                // make anything starting with a '-' lowercase
     191                for (int i=0; i<tokens.length; i++) {
     192                        if (tokens[i].startsWith("-")) {
     193                                tokens[i] = tokens[i].toLowerCase();
     194                        }
     195                }
     196               
     197                // create the command line parser
     198                CommandLineParser parser = new GnuParser();
     199
     200                // create the Options
     201                Options options = new Options();
     202                options.addOption( "al", "anchorleft",   true, "Anchor the vertical left-hand edge of the interactive widget to the value provided " );
     203                options.addOption( "ar", "anchorright",  true, "Anchor the vertical right-hand edge of the interactive widget to the value provided " );
     204                options.addOption( "at", "anchortop",    true, "Anchor the vertical top edge of the interactive widget to the value provided " );
     205                options.addOption( "ab", "anchorbottom", true, "Anchor the vertical bottom edge of the interactive widget to the value provided " );
     206               
     207                /*
     208                 * Alternative way to do the above, but with more control over how the values are set up and can be used
     209                 *
     210                @SuppressWarnings("static-access")
     211                Option anchor_left_opt = OptionBuilder.withLongOpt( "anchorleft" )
     212                                .withDescription( "use <x-pos> as left anchor for the vertical lefthand edge of the interactive widget" )
     213                                .hasArg()
     214                                .withArgName("<x-pos>")
     215                                .create("al");
     216         
     217                @SuppressWarnings("static-access")
     218                Option anchor_right_opt = OptionBuilder.withLongOpt( "anchorright" )
     219                                .withDescription( "use <x-pos> as right anchor for the vertical righthand edge of the interactive widget" )
     220                                .hasArg()
     221                                .withArgName("<x-pos>")
     222                                .create("ar");
     223               
     224               
     225                @SuppressWarnings("static-access")
     226                Option anchor_top_opt = OptionBuilder.withLongOpt( "anchortop" )
     227                                .withDescription( "use <y-pos> as top anchor for the horizontal top end of the interactive widget" )
     228                                .hasArg()
     229                                .withArgName("<x-pos>")
     230                                .create("at");
     231         
     232                @SuppressWarnings("static-access")
     233                Option anchor_bottom_opt = OptionBuilder.withLongOpt( "anchorbottom" )
     234                                .withDescription( "use <y-pos> as bottom anchor for the horizontal bottom edge of the interactive widget" )
     235                                .hasArg()
     236                                .withArgName("<x-pos>")
     237                                .create("ab");
     238               
     239                options.addOption(anchor_left_opt);
     240                options.addOption(anchor_right_opt);
     241                options.addOption(anchor_top_opt);
     242                options.addOption(anchor_bottom_opt);
     243                */
     244
     245                CommandLine core_line;
     246                try {
     247                    // parse the command line arguments
     248                    core_line = parser.parse( options, tokens );
     249                   
     250                    // Update tokens to be version with the options removed
     251                    tokens = core_line.getArgs();
     252                   
     253                }
     254                catch( ParseException exp ) {
     255                    System.err.println( "Unexpected exception:" + exp.getMessage() );
     256                    core_line = null;
     257                   
     258                }
     259               
     260                HelpFormatter formatter = new HelpFormatter();
     261                StringWriter usage_str_writer = new StringWriter();
     262            PrintWriter printWriter = new PrintWriter(usage_str_writer);
     263           
     264            String widget_name = tokens[0];
     265            final String widget_prefix = "org.expeditee.items.widgets";
     266            if (widget_name.startsWith(widget_prefix)) {
     267                widget_name = widget_name.substring(widget_prefix.length());
     268            }
     269           
     270                formatter.printHelp(printWriter, 80, TAG + ":" + widget_name + " [options] width height", null, options, 4, 0, null);
     271                System.out.println(usage_str_writer.toString());
     272               
    163273                float width = -1, height = -1;
    164274
    165                 if (tokens.length < 1)
    166                         throw new IllegalArgumentException(
    167                                         "Missing widget class name in source text");
     275                if (tokens.length < 1) {
     276                        throw new IllegalArgumentException("Missing widget class name in source text");
     277                }
    168278
    169279                try {
     
    200310                                        + " does not exist or is not an InteractiveWidget");
    201311
     312               
     313                //
     314                // Step 2:
     315                //   For an X-rayable text item in the form:
     316                //     @iw: <class name> [options] width height : <rest...>
     317                //
     318                //   Parse the <rest ...> part
     319               
    202320                // Extract out the parameters - if any
    203321                String[] args = null;
    204322                if (index > 0) { // index of the first ":"
    205                         args = (text.length() == (index + 1)) ? null : parseArgs(text
    206                                         .substring(index + 1));
     323                        if (text.length()>(index+1)) {
     324                                String args_str = text.substring(index + 1);
     325                               
     326                                 args = parseArgsApache(args_str);
     327               
     328                        }       
    207329                }
    208330
     
    222344                }
    223345
    224                 // Use default dimensions if not provided (or provided as negitive
     346                // Step 3:
     347                //    Set up the size and position of the widget
     348
     349                // Use default dimensions if not provided (or provided as negative
    225350                // values)
    226                 if (width <= 0)
     351                if (width <= 0) {
    227352                        width = inst.getWidth();
    228                 if (height <= 0)
     353                }
     354                if (height <= 0) {
    229355                        height = inst.getHeight();
     356                }
    230357
    231358                inst.setSize(width, height);
    232 
     359               
     360                // Apply any anchor values supplied in the core part of the @iw item
     361                Float anchor_left   = null;
     362                Float anchor_right  = null;
     363                Float anchor_top    = null;
     364                Float anchor_bottom = null;
     365               
     366            if(core_line.hasOption( "anchorleft" ) ) {
     367                String al_str = core_line.getOptionValue( "anchorleft" );
     368               
     369                anchor_left = Float.parseFloat(al_str);
     370            }
     371           
     372            if(core_line.hasOption( "anchorright" ) ) {
     373                String ar_str = core_line.getOptionValue( "anchorright" );
     374               
     375                anchor_right = Float.parseFloat(ar_str);
     376            }
     377           
     378            if(core_line.hasOption( "anchortop" ) ) {
     379                String at_str = core_line.getOptionValue( "anchortop" );
     380               
     381                anchor_top = Float.parseFloat(at_str);
     382            }
     383           
     384            if(core_line.hasOption( "anchorbottom" ) ) {
     385                String ab_str = core_line.getOptionValue( "anchorbottom" );
     386               
     387                anchor_bottom = Float.parseFloat(ab_str);
     388            }
     389           
     390            inst.setAnchorCorners(anchor_left,anchor_right,anchor_top,anchor_bottom);
     391           
     392       
     393               
    233394                return inst;
    234395        }
     
    319480
    320481                        } else if (c == '\"') {
    321                                 // If double qouted
     482                                // If double quoted
    322483                                if (args.length() >= (i + 2) && args.charAt(i + 1) == '\"') {
    323484
     
    376537                return sb.length() > 0 ? sb.toString() : null;
    377538        }
     539
     540
     541
     542
     543    /**
     544     * Similar to parseArgs() above. 
     545     * Code based on routine used in Apache Ant:
     546     *   org.apache.tools.ant.types.Commandline::translateCommandline()
     547     * @param toProcess the command line to process.
     548     * @return the command line broken into strings.
     549     * An empty or null toProcess parameter results in a zero sized array.
     550     */
     551    public static String[] parseArgsApache(String toProcess) {
     552        if (toProcess == null || toProcess.length() == 0) {
     553            //no command? no string
     554            return new String[0];
     555        }
     556        // parse with a simple finite state machine
     557
     558        final int normal = 0;
     559        final int inQuote = 1;
     560        final int inDoubleQuote = 2;
     561        int state = normal;
     562        final StringTokenizer tok = new StringTokenizer(toProcess, "\"\' ", true);
     563        final ArrayList<String> result = new ArrayList<String>();
     564        final StringBuilder current = new StringBuilder();
     565        boolean lastTokenHasBeenQuoted = false;
     566
     567        while (tok.hasMoreTokens()) {
     568            String nextTok = tok.nextToken();
     569            switch (state) {
     570            case inQuote:
     571                if ("\'".equals(nextTok)) {
     572                    lastTokenHasBeenQuoted = true;
     573                    state = normal;
     574                } else {
     575                    current.append(nextTok);
     576                }
     577                break;
     578            case inDoubleQuote:
     579                if ("\"".equals(nextTok)) {
     580                    lastTokenHasBeenQuoted = true;
     581                    state = normal;
     582                } else {
     583                    current.append(nextTok);
     584                }
     585                break;
     586            default:
     587                if ("\'".equals(nextTok)) {
     588                    state = inQuote;
     589                } else if ("\"".equals(nextTok)) {
     590                    state = inDoubleQuote;
     591                } else if (" ".equals(nextTok)) {
     592                    if (lastTokenHasBeenQuoted || current.length() != 0) {
     593                        result.add(current.toString());
     594                        current.setLength(0);
     595                    }
     596                } else {
     597                    current.append(nextTok);
     598                }
     599                lastTokenHasBeenQuoted = false;
     600                break;
     601            }
     602        }
     603        if (lastTokenHasBeenQuoted || current.length() != 0) {
     604            result.add(current.toString());
     605        }
     606        if (state == inQuote || state == inDoubleQuote) {
     607            System.err.println("Error: Unbalanced quotes -- failed to parse '" + toProcess + "'");
     608            return null;
     609        }
     610
     611        return result.toArray(new String[result.size()]);
     612    }
     613
     614
     615
    378616
    379617        /**
     
    456694                _textRepresentation = source;
    457695
    458                 setSizeRestrictions(minWidth, maxWidth, minHeight, maxHeight); // throws
    459                 // IllegalArgumentException's
     696                setSizeRestrictions(minWidth, maxWidth, minHeight, maxHeight); // throws IllegalArgumentException's
    460697
    461698                int x = source.getX();
     
    608845        /**
    609846         * @return The current representation for this widget. The representation
    610          *         stores link infomation, data etc... It is used for saving and
     847         *         stores link information, data etc... It is used for saving and
    611848         *         loading of the widget. Never null.
    612849         *
     
    617854
    618855        /**
    619          * @return The expeditee anotation string.
     856         * @return The Expeditee annotation string.
    620857         */
    621858        protected String getAnnotationString() {
     
    628865                sb.append(getClass().getName());
    629866
    630                 // Append size information if needed (not an attibute of text items)
     867                if (_anchorLeft != null) {
     868                    sb.append(" -anchorLeft " + Math.round(_anchorLeft));
     869                }
     870                if (_anchorRight != null) {
     871                    sb.append(" -anchorRight " + Math.round(_anchorRight));
     872                }
     873
     874                if (_anchorTop != null) {
     875                    sb.append(" -anchorTop " + Math.round(_anchorTop));
     876                }
     877
     878                if (_anchorBottom != null) {
     879                    sb.append(" -anchorBottom " + Math.round(_anchorBottom));
     880                }
     881
     882                // Append size information if needed (not an attribute of text items)
    631883                if (!isFixedSize()) {
    632884                        sb.append(' ');
     
    647899
    648900        /**
    649          * Sets both the new size as well as the new min/max widdet/height
    650          * restricitons.
     901         * Sets both the new size as well as the new min/max widget/height
     902         * restrictions.
    651903         *
    652904         * @param minWidth
     
    700952        public void setSize(float width, float height) {
    701953
    702                 // Clamp
    703                 if (width < _minWidth && _minWidth >= 0)
     954                // If 'width' and 'height' exceed the min/max values for width/height
     955                // => clamp to the relevant min/max value
     956                if (width < _minWidth && _minWidth >= 0) {
    704957                        width = _minWidth;
    705                 else if (width > _maxWidth && _maxWidth >= 0)
     958                }
     959                else if (width > _maxWidth && _maxWidth >= 0) {
    706960                        width = _maxWidth;
    707 
    708                 if (height < _minHeight && _minHeight >= 0)
     961                }
     962               
     963                if (height < _minHeight && _minHeight >= 0) {
    709964                        height = _minHeight;
    710                 else if (height > _maxHeight && _maxHeight >= 0)
     965                }
     966                else if (height > _maxHeight && _maxHeight >= 0) {
    711967                        height = _maxHeight;
    712 
     968                }
     969               
     970                // Remember current isFloating() values
    713971                boolean vfloating[] = new boolean[] { _d1.isFloating(),
    714972                                _d2.isFloating(), _d3.isFloating(), _d4.isFloating() };
     
    719977                _d4.setFloating(true);
    720978
    721                 float x = _d1.getX() + width;
    722                 float y = _d1.getY() + height;
    723 
    724                 _d2.setX(x);
    725                 _d3.setX(x);
    726                 _d3.setY(y);
    727                 _d4.setY(y);
    728 
     979                float xr = _d1.getX() + width;
     980                float yb = _d1.getY() + height;
     981
     982                _d2.setX(xr);
     983                _d3.setX(xr);
     984                _d3.setY(yb);
     985                _d4.setY(yb);
     986
     987                // Restore isFloating() values
    729988                _d1.setFloating(vfloating[0]);
    730989                _d2.setFloating(vfloating[1]);
     
    735994        }
    736995
     996    public void setAnchorCorners(Float left, Float right, Float top, Float bottom)
     997    {
     998        setAnchorLeft(left);
     999        setAnchorRight(right);
     1000        setAnchorTop(top);
     1001        setAnchorBottom(bottom);
     1002    }
     1003   
     1004
    7371005        public void setPosition(int x, int y) {
    7381006                if (x == getX() && y == getY())
    7391007                        return;
    7401008
     1009                // Remember current isFloating() values
    7411010                boolean vfloating[] = new boolean[] { _d1.isFloating(),
    7421011                                _d2.isFloating(), _d3.isFloating(), _d4.isFloating() };
     
    7571026                _d4.setPosition(x, y + height);
    7581027
     1028                // Restore isFloating() values
    7591029                _d1.setFloating(vfloating[0]);
    7601030                _d2.setFloating(vfloating[1]);
     
    7711041
    7721042        /**
    773          * to be called from corners only
     1043         * Updates position of given WidgetCorner to the given (x,y),
     1044         *   and updates related values (connected corners, width and height)
    7741045         *
    7751046         * @param src
     
    17191990        }
    17201991
     1992        public void setAnchorLeft(Float anchor) {
     1993                _anchorLeft = anchor;
     1994                // Anchor left-edge corners (dots) as well
     1995                _d1.setAnchorCornerX(anchor,null);
     1996                _d4.setAnchorCornerX(anchor,null);
     1997
     1998                if (anchor != null) {
     1999                    setPositions(_d1, anchor, _d1.getY());
     2000                    onResized();
     2001                }
     2002               
     2003                // Move X-rayable items as well
     2004                getSource().setAnchorLeft(anchor);
     2005        }
     2006
     2007        public void setAnchorRight(Float anchor) {
     2008                _anchorRight = anchor;
     2009                // Anchor right-edge corners (dots) as well
     2010                _d2.setAnchorCornerX(null,anchor); // right
     2011                _d3.setAnchorCornerX(null,anchor); // right
     2012
     2013                if (anchor != null) {
     2014                        setPositions(_d2, FrameGraphics.getMaxFrameSize().width - anchor, _d2.getY());
     2015                        onResized();
     2016                }
     2017               
     2018                if (_anchorLeft == null) {
     2019                        // Prefer having the X-rayable item at anchorLeft position (if defined) over moving to anchorRight
     2020                        getSource().setAnchorRight(anchor);
     2021                }
     2022        }
     2023       
    17212024        public void setAnchorTop(Float anchor) {
    1722                 setPosition(getX(),Math.round(anchor));
     2025                _anchorTop = anchor;
     2026                // Anchor top-edge corners (dots) as well
     2027                _d1.setAnchorCornerY(anchor,null);
     2028                _d2.setAnchorCornerY(anchor,null);
     2029               
     2030                if (anchor != null) {
     2031                    setPositions(_d2, _d2.getX(), anchor);
     2032                    onResized();
     2033                }
     2034       
     2035                // Move X-rayable items as well
    17232036                getSource().setAnchorTop(anchor);
    17242037        }
    17252038
    17262039        public void setAnchorBottom(Float anchor) {
    1727                 setPosition(getX(),
    1728                             Math.round(FrameGraphics.getMaxFrameSize().height - anchor - getHeight()));
    1729                 getSource().setAnchorBottom(anchor);
    1730         }
    1731 
    1732         public void setAnchorLeft(Float anchor) {
    1733                 setPosition(Math.round(anchor),
    1734                             getY());
    1735                 getSource().setAnchorLeft(anchor);
    1736         }
    1737 
    1738         public void setAnchorRight(Float anchor) {
    1739                 setPosition(Math.round(FrameGraphics.getMaxFrameSize().width - anchor - getWidth()),
    1740                             getY());
    1741                 getSource().setAnchorRight(anchor);
    1742         }
     2040                _anchorBottom = anchor;
     2041                // Anchor bottom-edge corners (dots) as well
     2042                _d3.setAnchorCornerY(null,anchor);
     2043                _d4.setAnchorCornerY(null,anchor);
     2044               
     2045                if (anchor != null) {
     2046                    setPositions(_d3, _d3.getX(), FrameGraphics.getMaxFrameSize().height  - anchor);
     2047                    onResized();
     2048                }
     2049               
     2050                if (_anchorTop == null) {
     2051                    // Prefer having the X-rayable item at anchorTop position (if defined) over moving to anchorBottom
     2052                    getSource().setAnchorBottom(anchor);
     2053                }
     2054        }
     2055
    17432056
    17442057        public boolean isAnchored() {
    1745                 return getSource().isAnchored();
     2058                        return (isAnchoredX()) || (isAnchoredY());
     2059                       
     2060                //return getSource().isAnchored();
    17462061        }
    17472062
    17482063        public boolean isAnchoredX() {
    1749                 return getSource().isAnchoredX();
     2064                return (_anchorLeft != null) || (_anchorRight != null);
     2065                //return getSource().isAnchoredX();
    17502066        }
    17512067
    17522068        public boolean isAnchoredY() {
    1753                 return getSource().isAnchoredY();
     2069                return (_anchorTop != null) || (_anchorBottom != null);
     2070                //return getSource().isAnchoredY();
    17542071        }
    17552072       
  • trunk/src/org/expeditee/items/widgets/WidgetCorner.java

    r805 r866  
    3636                                super.setPosition(x, y);
    3737                        }
    38                 } else
     38                }
     39                else {
    3940                        super.setPosition(x, y);
     41                }
    4042                invalidateFill();
    4143        }
     
    4850                                super.setXY(x, y);
    4951                        }
    50                 } else
     52                }
     53                else {
    5154                        super.setXY(x, y);
     55                }
    5256                invalidateFill();
    5357        }
     
    164168        }
    165169
     170        public void setAnchorCornerX(Float anchorLeft, Float anchorRight) {
     171                _anchorLeft = anchorLeft;
     172                _anchorRight = anchorRight;
     173        }
     174       
     175        public void setAnchorCornerY(Float anchorTop, Float anchorBottom) {
     176                _anchorTop = anchorTop;
     177                _anchorBottom= anchorBottom;
     178        }
     179       
     180        @Override
     181        public void setAnchorLeft(Float anchor) {
     182                _widgetSource.setAnchorLeft(anchor);
     183                _anchorLeft = anchor;
     184                _anchorRight = null;
     185        }
     186
     187        @Override
     188        public void setAnchorRight(Float anchor) {
     189                _widgetSource.setAnchorRight(anchor);
     190                _anchorLeft = null;
     191                _anchorRight = anchor;
     192        }
     193       
    166194        @Override
    167195        public void setAnchorTop(Float anchor) {
    168196                _widgetSource.setAnchorTop(anchor);
     197                _anchorTop = anchor;
     198                _anchorBottom = null;
    169199        }
    170200
     
    172202        public void setAnchorBottom(Float anchor) {
    173203                _widgetSource.setAnchorBottom(anchor);
    174         }
    175 
    176         @Override
    177         public void setAnchorLeft(Float anchor) {
    178                 _widgetSource.setAnchorLeft(anchor);
    179         }
    180 
    181         @Override
    182         public void setAnchorRight(Float anchor) {
    183                 _widgetSource.setAnchorRight(anchor);
    184         }
    185 
     204                _anchorTop = null;
     205                _anchorBottom = anchor;
     206        }
     207
     208        /*
    186209        @Override
    187210        public Float getAnchorTop() {
     
    203226                return _widgetSource.getSource().getAnchorRight();
    204227        }
    205 
     228*/
     229       
    206230        @Override
    207231        public void setLink(String link) {
Note: See TracChangeset for help on using the changeset viewer.