    001    /*
    002     * SideKickActions.java
    003     * :tabSize=8:indentSize=8:noTabs=false:
    004     * :folding=explicit:collapseFolds=1:
    005     *
    006     * Copyright (C) 2003, 2005 Slava Pestov
    007     *
    008     * This program is free software; you can redistribute it and/or
    009     * modify it under the terms of the GNU General Public License
    010     * as published by the Free Software Foundation; either version 2
    011     * of the License, or any later version.
    012     *
    013     * This program is distributed in the hope that it will be useful,
    014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
    015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    016     * GNU General Public License for more details.
    017     *
    018     * You should have received a copy of the GNU General Public License
    019     * along with this program; if not, write to the Free Software
    020     * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    021     */
    022
    023    package sidekick;
    024
    025    //{{{ Import statements
    026    import javax.swing.tree.*;
    027    import javax.swing.SwingUtilities;
    028    import javax.swing.Timer;
    029    import java.awt.event.*;
    030    import org.gjt.sp.jedit.textarea.*;
    031    import org.gjt.sp.jedit.*;
    032    //}}}
    033
    034    public class SideKickActions
    035    {
    036            //{{{ keyComplete() method
    037            public static void keyComplete(View view)
    038            {
    039                    if(timer != null)
    040                            timer.stop();
    041
    042                    if(!completeInstant)
    043                            return;
    044
    045                    complete(view,COMPLETE_INSTANT_KEY);
    046            } //}}}
    047
    048            //{{{ keyCompleteWithDelay() method
    049            public static void keyCompleteWithDelay(final View view)
    050            {
    051                    if(!completeDelay)
    052                            return;
    053
    054                    if(timer != null)
    055                            timer.stop();
    056
    057                    final JEditTextArea textArea = view.getTextArea();
    058
    059                    caretWhenCompleteKeyPressed = textArea.getCaretPosition();
    060
    061                    if(timer == null)
    062                    {
    063                            timer = new Timer(0,new ActionListener()
    064                            {
    065                                    public void actionPerformed(ActionEvent evt)
    066                                    {
    067                                            if(caretWhenCompleteKeyPressed == textArea.getCaretPosition())
    068                                                    complete(view,COMPLETE_DELAY_KEY);
    069                                    }
    070                            });
    071
    072                            timer.setInitialDelay(delay);
    073                            timer.setRepeats(false);
    074                    }
    075
    076                    timer.start();
    077            } //}}}
    078
    079            //{{{ complete() method
    080            public static final int COMPLETE_COMMAND = 0;
    081            public static final int COMPLETE_DELAY_KEY = 1;
    082            public static final int COMPLETE_INSTANT_KEY = 2;
    083
    084            public static void complete(View view, int mode)
    085            {
    086                    EditPane editPane = view.getEditPane();
    087                    Buffer buffer = editPane.getBuffer();
    088                    JEditTextArea textArea = editPane.getTextArea();
    089
    090                    SideKickParser parser = SideKickPlugin
    091                            .getParserForBuffer(buffer);
    092                    SideKickParsedData data = SideKickParsedData
    093                            .getParsedData(view);
    094
    095                    SideKickCompletion complete = null;
    096
    097                    if(buffer.isEditable()
    098                            && data != null && parser != null
    099                            && parser.supportsCompletion())
    100                    {
    101                            complete = parser.complete(editPane,
    102                                    textArea.getCaretPosition());
    103                    }
    104
    105                    if(complete == null || complete.size() == 0)
    106                    {
    107                            if(mode == COMPLETE_INSTANT_KEY
    108                                    || mode == COMPLETE_DELAY_KEY)
    109                            {
    110                                    // don't bother user with beep if eg
    111                                    // they press < in XML mode
    112                                    return;
    113                            }
    114                            else
    115                            {
    116                                    view.getToolkit().beep();
    117                                    return;
    118                            }
    119                    }
    120                    else if(complete.size() == 1)
    121                    {
    122                            // if user invokes complete explicitly, insert the
    123                            // completion immediately.
    124                            //
    125                            // if the user eg enters </ in XML mode, there will
    126                            // only be one completion and / is an instant complete
    127                            // key, so we insert it
    128                            if(mode == COMPLETE_COMMAND
    129                                    || mode == COMPLETE_INSTANT_KEY)
    130                            {
    131                                    complete.insert(0);
    132                                    return;
    133                            }
    134                    }
    135
    136                    // show the popup if
    137                    // - complete has one element and user invoked with delay key
    138                    // - or complete has multiple elements
    139                    // and popup is not already shown because of explicit invocation
    140                    // of the complete action during the trigger delay
    141                    if(popup != null)
    142                            return;
    143
    144                    popup = new SideKickCompletionPopup(view,parser,
    145                            textArea.getCaretPosition(), complete)
    146                    {
    147                            /** forget reference to this popup when it is disposed */
    148                            public void dispose()
    149                            {
    150                                    super.dispose();
    151                                    popup = null;
    152                            }
    153                    };
    154            } //}}}
    155
    156            //{{{ selectAsset() method
    157            public static void selectAsset(View view)
    158            {
    159                    SideKickParsedData data = SideKickParsedData.getParsedData(view);
    160                    if(data == null)
    161                    {
    162                            view.getToolkit().beep();
    163                            return;
    164                    }
    165
    166                    JEditTextArea textArea = view.getTextArea();
    167
    168                    IAsset asset = data.getAssetAtOffset(
    169                            textArea.getCaretPosition());
    170
    171                    if(asset == null || asset.getEnd() == null)
    172                    {
    173                            view.getToolkit().beep();
    174                            return;
    175                    }
    176
    177                    textArea.setCaretPosition(asset.getEnd().getOffset());
    178                    textArea.addToSelection(
    179                            new Selection.Range(
    180                                    asset.getStart().getOffset(),
    181                                    asset.getEnd().getOffset()));
    182            } //}}}
    183
    184            //{{{ narrowToAsset() method
    185            public static void narrowToAsset(View view)
    186            {
    187                    SideKickParsedData data = SideKickParsedData.getParsedData(view);
    188                    if(data == null)
    189                    {
    190                            view.getToolkit().beep();
    191                            return;
    192                    }
    193
    194                    JEditTextArea textArea = view.getTextArea();
    195
    196                    TreePath path = data.getTreePathForPosition(textArea.getCaretPosition());
    197                    if(path == null)
    198                    {
    199                            view.getToolkit().beep();
    200                            return;
    201                    }
    202                    IAsset asset = (IAsset)((DefaultMutableTreeNode)path
    203                            .getLastPathComponent()).getUserObject();
    204
    205                    if(asset == null || asset.getEnd() == null)
    206                    {
    207                            view.getToolkit().beep();
    208                            return;
    209                    }
    210
    211                    textArea.getDisplayManager().narrow(
    212                            textArea.getLineOfOffset(asset.getStart().getOffset()),
    213                            textArea.getLineOfOffset(asset.getStart().getOffset()));
    214            } //}}}
    215
    216            //{{{ goToPrevAsset() method
    217            public static void goToPrevAsset(View view)
    218            {
    219                    SideKickParsedData data = SideKickParsedData.getParsedData(view);
    220                    if(data == null)
    221                    {
    222                            view.getToolkit().beep();
    223                            return;
    224                    }
    225
    226                    JEditTextArea textArea = view.getTextArea();
    227
    228                    int caret = textArea.getCaretPosition();
    229                    TreePath path = data.getTreePathForPosition(caret);
    230                    if(path == null)
    231                    {
    232                            view.getToolkit().beep();
    233                            return;
    234                    }
    235
    236                    DefaultMutableTreeNode node = (DefaultMutableTreeNode)
    237                            path.getLastPathComponent();
    238
    239                    // see if caret is at the end of a child of the current asset
    240                    for(int i = 0; i < node.getChildCount(); i++)
    241                    {
    242                            IAsset asset = (IAsset)((DefaultMutableTreeNode)node.getChildAt(i))
    243                                    .getUserObject();
    244                            if(asset.getEnd() != null && caret == asset.getEnd().getOffset())
    245                            {
    246                                    textArea.setCaretPosition(asset.getStart().getOffset());
    247                                    return;
    248                            }
    249                    }
    250
    251                    IAsset asset = ((IAsset)node.getUserObject());
    252                    if(caret == asset.getStart().getOffset())
    253                    {
    254                            DefaultMutableTreeNode parent = (DefaultMutableTreeNode)
    255                                    node.getParent();
    256                            for(int i = 0; i < parent.getChildCount(); i++)
    257                            {
    258                                    if(node == parent.getChildAt(i))
    259                                    {
    260                                            if(i == 0)
    261                                            {
    262                                                    if(parent.getUserObject() instanceof IAsset)
    263                                                    {
    264                                                            textArea.setCaretPosition(
    265                                                                    ((IAsset)parent.getUserObject())
    266                                                                    .getStart().getOffset());
    267                                                    }
    268                                            }
    269                                            else
    270                                            {
    271                                                    IAsset prevAsset = (IAsset)((DefaultMutableTreeNode)
    272                                                            parent.getChildAt(i - 1)).getUserObject();
    273                                                    if(prevAsset.getEnd() != null)
    274                                                            textArea.setCaretPosition(prevAsset.getEnd().getOffset());
    275                                            }
    276                                            return;
    277                                    }
    278                            }
    279                    }
    280                    else
    281                            textArea.setCaretPosition(asset.getStart().getOffset());
    282            } //}}}
    283
    284            //{{{ goToNextAsset() method
    285            public static void goToNextAsset(View view)
    286            {
    287                    SideKickParsedData data = SideKickParsedData.getParsedData(view);
    288                    if(data == null)
    289                    {
    290                            view.getToolkit().beep();
    291                            return;
    292                    }
    293
    294                    JEditTextArea textArea = view.getTextArea();
    295
    296                    int caret = textArea.getCaretPosition();
    297                    TreePath path = data.getTreePathForPosition(caret);
    298                    if(path == null)
    299                    {
    300                            view.getToolkit().beep();
    301                            return;
    302                    }
    303
    304                    DefaultMutableTreeNode node = (DefaultMutableTreeNode)
    305                            path.getLastPathComponent();
    306
    307                    // see if caret is at the end of a child of the current asset
    308                    for(int i = 0; i < node.getChildCount(); i++)
    309                    {
    310                            IAsset asset = (IAsset)((DefaultMutableTreeNode)node.getChildAt(i))
    311                                    .getUserObject();
    312                            if(caret == asset.getEnd().getOffset())
    313                            {
    314                                    if(i != node.getChildCount() - 1)
    315                                    {
    316                                            IAsset nextAsset = (IAsset)((DefaultMutableTreeNode)
    317                                                    node.getChildAt(i + 1)).getUserObject();
    318                                            textArea.setCaretPosition(nextAsset.getStart().getOffset());
    319                                            return;
    320                                    }
    321                                    else
    322                                            break;
    323                            }
    324                    }
    325
    326                    textArea.setCaretPosition(((IAsset)node.getUserObject()).getEnd().getOffset());
    327            } //}}}
    328
    329            //{{{ propertiesChanged() method
    330            public static void propertiesChanged()
    331            {
    332                    completeDelay = jEdit.getBooleanProperty("sidekick.complete-delay.toggle");
    333                    completeInstant = jEdit.getBooleanProperty("sidekick.complete-instant.toggle");
    334                    delay = jEdit.getIntegerProperty("sidekick.complete-delay",500);
    335                    if(timer != null)
    336                            timer.setInitialDelay(delay);
    337            } //}}}
    338
    339            //{{{ Private members
    340            private static boolean completeDelay;
    341            private static boolean completeInstant;
    342            private static int delay;
    343            private static int caretWhenCompleteKeyPressed;
    344            private static Timer timer;
    345            private static SideKickCompletionPopup popup;
    346            //}}}
    347    }