1248 lines
41 KiB
Java
1248 lines
41 KiB
Java
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
/*
|
|
* 'View' for interactive viewing of source code
|
|
*/
|
|
|
|
// when who what
|
|
// 06/27/97 jband added this header to my code
|
|
//
|
|
|
|
package com.netscape.jsdebugging.ifcui;
|
|
|
|
import java.util.Observable;
|
|
import java.util.Observer;
|
|
import netscape.application.*;
|
|
import netscape.util.*;
|
|
import com.netscape.jsdebugging.ifcui.palomar.util.*;
|
|
import java.io.StringBufferInputStream;
|
|
import java.io.DataInputStream;
|
|
import com.netscape.jsdebugging.ifcui.palomar.widget.layout.*;
|
|
import com.netscape.jsdebugging.api.*;
|
|
|
|
// XXX deal with window closing -- remove as Observer
|
|
|
|
public class SourceView extends InternalWindow
|
|
implements Observer
|
|
{
|
|
|
|
public SourceView( Rect rect,
|
|
Emperor emperor,
|
|
SourceTextItem item )
|
|
{
|
|
super(rect);
|
|
|
|
_item = item;
|
|
|
|
_emperor = emperor;
|
|
_controlTyrant = emperor.getControlTyrant();
|
|
_sourceTyrant = emperor.getSourceTyrant();
|
|
_breakpointTyrant = emperor.getBreakpointTyrant();
|
|
_sourceViewManager = emperor.getSourceViewManager();
|
|
_stackTyrant = emperor.getStackTyrant();
|
|
|
|
if(AS.S)ER.T(null!=_controlTyrant,"emperor init order problem", this);
|
|
if(AS.S)ER.T(null!=_sourceTyrant,"emperor init order problem", this);
|
|
if(AS.S)ER.T(null!=_breakpointTyrant,"emperor init order problem", this);
|
|
if(AS.S)ER.T(null!=_sourceViewManager,"emperor init order problem", this);
|
|
if(AS.S)ER.T(null!=_stackTyrant,"emperor init order problem", this);
|
|
|
|
_sourceLineVectorModel =
|
|
new SourceLineVectorModel(this, _controlTyrant, _stackTyrant,
|
|
_sourceTyrant, item);
|
|
_listview = new SourceTextListView(this);
|
|
|
|
Rect rectSG = new Rect(rect);
|
|
rectSG.moveBy(-rect.x, -rect.y);
|
|
ScrollGroup sg = new ScrollGroup(rectSG);
|
|
sg.setHorizScrollBarDisplay( ScrollGroup.ALWAYS_DISPLAY );
|
|
sg.setVertScrollBarDisplay( ScrollGroup.ALWAYS_DISPLAY );
|
|
sg.setContentView( _listview );
|
|
sg.setAutoResizeSubviews(true);
|
|
sg.contentView().setLayoutManager( new MarginLayout() );
|
|
sg.setBackgroundColor(_emperor.getBackgroundColor());
|
|
|
|
setCloseable( true );
|
|
setResizable( true );
|
|
// setTitle( "Source for: " + item.getURL() );
|
|
setTitle( item.getURL() );
|
|
addSubview(sg);
|
|
setAutoResizeSubviews(true);
|
|
contentView().setLayoutManager( new MarginLayout() );
|
|
|
|
_listview.setAllowsEmptySelection(true);
|
|
_listview.setAllowsMultipleSelection(false);
|
|
|
|
_listview.setBackgroundColor(_emperor.getBackgroundColor());
|
|
|
|
_sourceTyrant.addObserver(this);
|
|
_controlTyrant.addObserver(this);
|
|
_breakpointTyrant.addObserver(this);
|
|
|
|
_refresh(true);
|
|
|
|
layoutView(1,1);
|
|
layoutView(-1,-1);
|
|
|
|
show();
|
|
}
|
|
|
|
public void ensureSourceLineVisible( int line )
|
|
{
|
|
int userline = _sourceTyrant.systemLine2UserLine(_item,line);
|
|
if(userline >= 1 && userline <= _listview.count())
|
|
_listview.scrollItemAtToVisible(userline-1);
|
|
}
|
|
|
|
// implement observer interface
|
|
public void update(Observable o, Object arg)
|
|
{
|
|
if( o == _sourceTyrant )
|
|
{
|
|
SourceTyrantUpdate update = (SourceTyrantUpdate) arg;
|
|
switch( update.type )
|
|
{
|
|
case SourceTyrantUpdate.VECTOR_CHANGED:
|
|
_refresh(false);
|
|
updateMarks();
|
|
break;
|
|
case SourceTyrantUpdate.ITEM_CHANGED:
|
|
if( update.item == _item )
|
|
{
|
|
_refresh(false);
|
|
updateMarks();
|
|
}
|
|
break;
|
|
case SourceTyrantUpdate.ADJUSTMENTS_CHANGED:
|
|
updateMarks();
|
|
break;
|
|
default:
|
|
break; // ignore any other types
|
|
}
|
|
}
|
|
else if ( o == _breakpointTyrant )
|
|
{
|
|
BreakpointTyrantUpdate update = (BreakpointTyrantUpdate) arg;
|
|
switch( update.type )
|
|
{
|
|
case BreakpointTyrantUpdate.ADD_ONE:
|
|
case BreakpointTyrantUpdate.REMOVE_ONE:
|
|
case BreakpointTyrantUpdate.MODIFIED_ONE:
|
|
if( update.bp.getURL().equals(_item.getURL()) )
|
|
updateMarks();
|
|
// _refreshMarksForSingleLine(update.bp.getLine());
|
|
break;
|
|
case BreakpointTyrantUpdate.REMOVE_ALL_FOR_URL:
|
|
if( ! (update.bp.getURL().equals(_item.getURL())) )
|
|
break;
|
|
// else... FALL THROUGH
|
|
case BreakpointTyrantUpdate.REMOVE_ALL:
|
|
case BreakpointTyrantUpdate.REFRESH_ALL:
|
|
updateMarks();
|
|
break;
|
|
default:
|
|
break; // ignore any other types
|
|
}
|
|
}
|
|
else if ( o == _controlTyrant )
|
|
{
|
|
// update all dirty views on stop
|
|
if( ControlTyrant.STOPPED == _controlTyrant.getState() )
|
|
_refresh(false);
|
|
updateMarks();
|
|
}
|
|
|
|
}
|
|
|
|
public void forceRefresh()
|
|
{
|
|
_refresh(true);
|
|
}
|
|
|
|
public void updateMarks()
|
|
{
|
|
_sourceLineVectorModel.updateLineItemVector();
|
|
_listview.drawMarks();
|
|
}
|
|
|
|
/*
|
|
private synchronized void _refreshSingleLine( int line, Breakpoint bp )
|
|
{
|
|
if( null == _sourceLineVectorModel || null == _listview )
|
|
return;
|
|
_sourceLineVectorModel.updateSingleLineItem(line);
|
|
// XXX This will change with 'selective text'
|
|
_listview.drawItemAt(line-1);
|
|
}
|
|
private synchronized void _refreshMarksForSingleLine(int line)
|
|
{
|
|
if( null == _sourceLineVectorModel || null == _listview )
|
|
return;
|
|
_sourceLineVectorModel.updateSingleLineItem(line);
|
|
// XXX This will change with 'selective text'
|
|
_listview.drawMarksOfItemAt(line-1);
|
|
}
|
|
*/
|
|
|
|
private synchronized void _refresh(boolean force)
|
|
{
|
|
if( _item.getDirty() || force )
|
|
{
|
|
_emperor.setWaitCursor(true);
|
|
|
|
// System.out.println(" _refreshing: " + _item.getURL() +
|
|
// " _item.getDirty() returned: " +
|
|
// (_item.getDirty() ? "true" : "false") +
|
|
// "_item.getAlterCount() returned: " +
|
|
// _item.getAlterCount() +
|
|
// " force == " +
|
|
// (force ? "true" : "false") );
|
|
// refill all
|
|
_listview.removeAllItems();
|
|
_sourceLineVectorModel.rebuildLineItemVector();
|
|
setSelection(null,0);
|
|
|
|
Vector vec = _sourceLineVectorModel.getLineItemVector();
|
|
// System.out.println( "vec size = " + vec.size() );
|
|
|
|
Font linefont = _emperor.getFixedFont();
|
|
|
|
boolean showNumbers = _sourceViewManager.getShowLineNumbers();
|
|
SourceTextItemDrawer drawer = new SourceTextItemDrawer();
|
|
|
|
_listview.setItemDrawer(drawer);
|
|
|
|
int maxwidth = 0;
|
|
int itemHeight = 0;
|
|
for (int i = 0; i < vec.size(); i++)
|
|
{
|
|
SourceLineItemModel lineitem = (SourceLineItemModel) vec.elementAt(i);
|
|
String text = lineitem.text;
|
|
|
|
SourceTextListItem newlistitem = new SourceTextListItem(drawer,text.length());
|
|
newlistitem.setTitle( text );
|
|
newlistitem.setData( lineitem );
|
|
newlistitem.setFont( linefont );
|
|
newlistitem.setSelectedColor(_emperor.getSelectionColor());
|
|
|
|
_listview.addItem( newlistitem );
|
|
|
|
if( 0 == i )
|
|
{
|
|
itemHeight = _listview.rectForItem(newlistitem).height;
|
|
drawer.init( linefont, showNumbers, vec.size(), itemHeight );
|
|
}
|
|
maxwidth = Math.max( maxwidth, newlistitem.minWidth() );
|
|
}
|
|
|
|
_listview.setBounds(0,0,maxwidth,0);
|
|
_listview.sizeToMinSize();
|
|
|
|
layoutView(0,0);
|
|
_listview.draw();
|
|
|
|
_item.setDirty(false);
|
|
|
|
JSSourceLocation loc = _stackTyrant.getCurrentLocation();
|
|
if( null != loc && loc.getURL().equals(getURL()) )
|
|
ensureSourceLineVisible( loc.getLine() );
|
|
|
|
_emperor.setWaitCursor(false);
|
|
}
|
|
}
|
|
|
|
public String getSelectedText() {return _selectedText;}
|
|
public int getSelectedLineNumber() {return _selectedLineNumber;}
|
|
public void setSelection(String s, int lineNumber)
|
|
{
|
|
if( _selectedLineNumber == lineNumber && s != null && s.equals(_selectedText) )
|
|
return;
|
|
_selectedText = s;
|
|
_selectedLineNumber = lineNumber;
|
|
_sourceViewManager.selectionInSourceViewChanged(this,s,lineNumber);
|
|
}
|
|
|
|
public SourceViewManager getSourceViewManager() {return _sourceViewManager;}
|
|
public BreakpointTyrant getBreakpointTyrant() {return _breakpointTyrant;}
|
|
public Emperor getEmperor() {return _emperor;}
|
|
public SourceTyrant getSourceTyrant() {return _sourceTyrant;}
|
|
|
|
public SourceTextItem getSourceItem() {return _item;}
|
|
public String getURL() {return _item.getURL();}
|
|
|
|
private Emperor _emperor;
|
|
private ControlTyrant _controlTyrant;
|
|
private SourceTyrant _sourceTyrant;
|
|
private StackTyrant _stackTyrant;
|
|
private BreakpointTyrant _breakpointTyrant;
|
|
private SourceViewManager _sourceViewManager;
|
|
private SourceLineVectorModel _sourceLineVectorModel;
|
|
private SourceTextItem _item;
|
|
private SourceTextListView _listview;
|
|
private String _selectedText = null;
|
|
private int _selectedLineNumber = 0;
|
|
|
|
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
final class SourceTextListView extends SmartItemListView
|
|
{
|
|
public SourceTextListView( SourceView sourceView )
|
|
{
|
|
super();
|
|
_sourceView = sourceView;
|
|
}
|
|
|
|
|
|
public boolean mouseDown(MouseEvent me)
|
|
{
|
|
_dragOrigin = -1;
|
|
|
|
// middle click
|
|
if( me.isAltKeyDown() && _isInMarksZone(me) )
|
|
{
|
|
SourceTyrant st = getSourceView().getSourceTyrant();
|
|
SourceTextItem sti = _sourceView.getSourceItem();
|
|
st.clearAllAdjustmentsForItem(sti);
|
|
return false;
|
|
}
|
|
|
|
// right click
|
|
if( me.isMetaKeyDown() && _isInMarksZone(me) )
|
|
{
|
|
SourceTyrant sourceTyrant = getSourceView().getSourceTyrant();
|
|
|
|
_dragLast = _dragOrigin = _itemNumberAtPoint(me);
|
|
SourceTextItem sti = _sourceView.getSourceItem();
|
|
int sysline = sourceTyrant.userLine2SystemLine(sti,_dragOrigin);
|
|
_dragAdjLineOrigin = sysline;
|
|
// System.out.println( "original line = " + _dragOrigin );
|
|
// System.out.println( "sys line = " + sysline );
|
|
// System.out.println( "\n" );
|
|
return true;
|
|
}
|
|
return super.mouseDown(me);
|
|
}
|
|
|
|
|
|
public void mouseDragged(MouseEvent me)
|
|
{
|
|
if( -1 != _dragOrigin )
|
|
{
|
|
int curIndex = _itemNumberAtPoint(me);
|
|
if( _dragLast == curIndex )
|
|
return;
|
|
_extendAdjLine(me);
|
|
}
|
|
else
|
|
super.mouseDragged(me);
|
|
}
|
|
public void mouseUp(MouseEvent me)
|
|
{
|
|
if( -1 != _dragOrigin )
|
|
{
|
|
_extendAdjLine(me);
|
|
_dragAdjLineOrigin = -1;
|
|
}
|
|
else
|
|
super.mouseUp(me);
|
|
}
|
|
|
|
private final boolean _isInMarksZone(MouseEvent me)
|
|
{
|
|
return me.x < _drawer.marksWidth();
|
|
}
|
|
private final int _itemNumberAtPoint(MouseEvent me)
|
|
{
|
|
return 1 + indexOfItem(itemForPoint(me.x,me.y));
|
|
}
|
|
|
|
private void _extendAdjLine(MouseEvent me)
|
|
{
|
|
int curIndex = _itemNumberAtPoint(me);
|
|
if( _dragLast == curIndex )
|
|
return;
|
|
int offset = curIndex - _dragLast;
|
|
_dragLast = curIndex;
|
|
|
|
SourceTyrant st = getSourceView().getSourceTyrant();
|
|
SourceTextItem sti = _sourceView.getSourceItem();
|
|
int old_offset = st.getAdjustment(sti,_dragAdjLineOrigin);
|
|
st.makeAdjustment(sti, _dragAdjLineOrigin, old_offset + offset);
|
|
}
|
|
|
|
protected void ancestorWillRemoveFromViewHierarchy(View removedView)
|
|
{
|
|
if( removedView == _sourceView )
|
|
{
|
|
_sourceView.getSourceViewManager().viewBeingRemoved(_sourceView);
|
|
}
|
|
|
|
super.ancestorWillRemoveFromViewHierarchy(removedView);
|
|
}
|
|
|
|
public void setItemDrawer(SourceTextItemDrawer drawer) {_drawer = drawer;}
|
|
|
|
public void drawMarks()
|
|
{
|
|
if(AS.S)ER.T(null!=_drawer,"SourceTextListView has null drawer",this);
|
|
if( null == _drawer || null == _drawer.marksRect())
|
|
{
|
|
draw();
|
|
return;
|
|
}
|
|
// calc marks rect and call draw()
|
|
Rect r = new Rect( 0,0, _drawer.marksRect().width, height() );
|
|
// System.out.println( "draw marks rect: " + r );
|
|
draw(r);
|
|
}
|
|
|
|
public void drawMarksOfItemAt(int i)
|
|
{
|
|
if(AS.S)ER.T(null!=_drawer,"SourceTextListView has null drawer",this);
|
|
if( null == _drawer )
|
|
{
|
|
drawItemAt(i);
|
|
return;
|
|
}
|
|
// calc marks rect for item and call draw()
|
|
Rect rItem = rectForItemAt(i);
|
|
Rect r = new Rect( rItem.x,
|
|
rItem.y,
|
|
_drawer.marksRect().width,
|
|
rItem.height );
|
|
// System.out.println( "draw marks rect for single item: " + r );
|
|
draw(r);
|
|
}
|
|
|
|
public SourceTextListItem getItemWithSel() {return _itemWithSel;}
|
|
public int getSelStart() {return _selStart;}
|
|
public int getSelEnd() {return _selEnd;}
|
|
public void setItemWithSel(SourceTextListItem i) {_itemWithSel=i;}
|
|
public void setSelRange(int start, int end) {_selStart=start;_selEnd=end;}
|
|
|
|
public void selChangeCompleted()
|
|
{
|
|
int lineNumber = 0;
|
|
_selString = null;
|
|
|
|
if( null != _itemWithSel )
|
|
{
|
|
_selString = _itemWithSel.stringInRange(_selStart,_selEnd);
|
|
lineNumber = indexOfItem(_itemWithSel) + 1;
|
|
}
|
|
_sourceView.setSelection(_selString, lineNumber );
|
|
}
|
|
|
|
public SourceView getSourceView() {return _sourceView;}
|
|
|
|
private SourceView _sourceView;
|
|
private SourceTextItemDrawer _drawer;
|
|
private SourceTextListItem _itemWithSel;
|
|
private int _selStart;
|
|
private int _selEnd;
|
|
private String _selString;
|
|
private int _dragOrigin = -1;
|
|
private int _dragLast;
|
|
private int _dragAdjLineOrigin;
|
|
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
final class SourceTextItemDrawer
|
|
{
|
|
public SourceTextItemDrawer() {}
|
|
|
|
public void init( Font font, boolean showLineNumbers,
|
|
int lineCount, int itemHeight )
|
|
{
|
|
_font = font;
|
|
_showLineNumbers = showLineNumbers;
|
|
|
|
_lineNumberColumnCount = String.valueOf(lineCount).length();
|
|
// _height = font.fontMetrics().stringHeight();
|
|
_height = itemHeight;
|
|
_charWidth = font.fontMetrics().charWidth('0');
|
|
|
|
_rectBP = new Rect( 0, 0,
|
|
_height, _height);
|
|
Rect rectExec = new Rect( _rectBP.x + _rectBP.width, 0,
|
|
_height/2, _height);
|
|
Rect rectNum = new Rect( rectExec.x + rectExec.width, 0,
|
|
_showLineNumbers ? _charWidth*(_lineNumberColumnCount+1) : 0, _height);
|
|
|
|
_width = rectNum.x + rectNum.width;
|
|
|
|
_ptLineNumbers = new Point(rectNum.x, rectNum.y);
|
|
|
|
int margin = _height / 4;
|
|
|
|
// size before shrink
|
|
_rectExecPointBackground = new Rect( rectExec );
|
|
_rectBPBackground = new Rect( _rectBP );
|
|
_rectMarks = Rect.rectFromUnion(rectExec, _rectBP);
|
|
if( null == _rectMarks )
|
|
_rectMarks = new Rect();
|
|
|
|
_rectBP.growBy( -margin, - margin );
|
|
rectExec.growBy( -(margin/2), -margin );
|
|
|
|
// _rectBP.moveBy( 0, -margin/2 );
|
|
// rectExec.moveBy( 0, -margin/4 );
|
|
|
|
_polyExecPoint = new Polygon();
|
|
_polyExecPoint.addPoint( rectExec.x, rectExec.y );
|
|
_polyExecPoint.addPoint( rectExec.x, rectExec.y + rectExec.height);
|
|
_polyExecPoint.addPoint( rectExec.x+rectExec.width, rectExec.midY());
|
|
_polyExecPoint.addPoint( rectExec.x, rectExec.y );
|
|
|
|
_ptText = new Point( _width, 0 );
|
|
_stringClipper = new DrawStringClipper( _charWidth );
|
|
}
|
|
|
|
public void draw( Graphics g,
|
|
Rect boundsRect,
|
|
SourceTextListItem item,
|
|
int lineNumber,
|
|
boolean hasBreakpoint,
|
|
boolean hasConditionalBreakpoint,
|
|
int scriptType,
|
|
boolean isExecPoint,
|
|
String adjustmentChar )
|
|
{
|
|
SourceLineItemModel itemModel = (SourceLineItemModel) item.data();
|
|
|
|
Rect r = new Rect();
|
|
Rect clipRect = g.clipRect();
|
|
|
|
// draw the main text
|
|
|
|
String text = item.title();
|
|
if( null != text && 0 != text.length() )
|
|
{
|
|
r.setBounds( boundsRect.x + _ptText.x,
|
|
boundsRect.y + _ptText.y,
|
|
boundsRect.width - _ptText.x,
|
|
boundsRect.height - _ptText.y );
|
|
|
|
// System.out.println("r is "+r+" clip is "+clipRect);
|
|
if( r.intersects( clipRect ) )
|
|
{
|
|
/* no sync - we trust that we are drawn on only one thread */
|
|
if( _stringClipper.doClip(text, r, clipRect) > 0 )
|
|
item.drawStringInRect(g, _stringClipper.getString(), _font,
|
|
_stringClipper.getRect(),
|
|
Graphics.LEFT_JUSTIFIED);
|
|
}
|
|
}
|
|
|
|
if( hasBreakpoint )
|
|
{
|
|
r.setBounds( boundsRect.x + _rectBP.x,
|
|
boundsRect.y + _rectBP.y,
|
|
_rectBP.width,
|
|
_rectBP.height );
|
|
|
|
if( r.intersects( clipRect ) )
|
|
{
|
|
if( hasConditionalBreakpoint )
|
|
g.setColor( Color.orange );
|
|
else
|
|
g.setColor( Color.red );
|
|
g.fillOval( r );
|
|
}
|
|
}
|
|
|
|
if( null != adjustmentChar )
|
|
{
|
|
r.setBounds( boundsRect.x + _rectBPBackground.x,
|
|
boundsRect.y + _rectBPBackground.y,
|
|
_rectBPBackground.width,
|
|
_rectBPBackground.height );
|
|
|
|
if( r.intersects( clipRect ) )
|
|
{
|
|
g.setColor( Color.black );
|
|
g.setFont(_font);
|
|
g.drawStringInRect(adjustmentChar,r,Graphics.LEFT_JUSTIFIED);
|
|
}
|
|
}
|
|
|
|
if( scriptType != SourceLineItemModel.NO_SCRIPT )
|
|
{
|
|
r.setBounds( boundsRect.x + _rectExecPointBackground.x,
|
|
boundsRect.y + _rectExecPointBackground.y,
|
|
_rectExecPointBackground.width,
|
|
_rectExecPointBackground.height );
|
|
|
|
if( r.intersects( clipRect ) )
|
|
{
|
|
if( scriptType == SourceLineItemModel.FUNCTION_BODY )
|
|
g.setColor( Color.orange );
|
|
else
|
|
g.setColor( Color.yellow );
|
|
|
|
g.fillRect( r );
|
|
}
|
|
}
|
|
|
|
if( isExecPoint )
|
|
{
|
|
int count = 4;
|
|
int[] x = new int[count];
|
|
int[] y = new int[count];
|
|
|
|
for( int i = 0; i < count; i++ )
|
|
{
|
|
x[i] = _polyExecPoint.xPoints[i] + boundsRect.x;
|
|
y[i] = _polyExecPoint.yPoints[i] + boundsRect.y;
|
|
}
|
|
|
|
g.setColor( Color.blue );
|
|
g.fillPolygon( x,y,count);
|
|
|
|
}
|
|
if( _showLineNumbers )
|
|
{
|
|
r.setBounds( boundsRect.x + _ptLineNumbers.x,
|
|
boundsRect.y + _ptLineNumbers.y,
|
|
boundsRect.width - _ptLineNumbers.x,
|
|
boundsRect.height - _ptLineNumbers.y );
|
|
|
|
if( r.intersects( clipRect ) )
|
|
{
|
|
g.setColor( Color.darkGray );
|
|
g.setFont(_font);
|
|
g.drawStringInRect(lineNumberString(lineNumber),r,Graphics.LEFT_JUSTIFIED);
|
|
}
|
|
}
|
|
}
|
|
|
|
public int width() {return _width;}
|
|
public int height() {return _height;}
|
|
public int charWidth() {return _charWidth;}
|
|
public Rect breakpointRect() {return _rectBPBackground;}
|
|
public Rect marksRect() {return _rectMarks;}
|
|
public Point textOrigin() {return _ptText;}
|
|
public int marksWidth() {return _rectMarks.width;}
|
|
|
|
private String lineNumberString( int number )
|
|
{
|
|
StringBuffer buf = new StringBuffer();
|
|
String s = String.valueOf(number);
|
|
int len = s.length();
|
|
if(AS.S)ER.T(len<=_lineNumberColumnCount,"lineNumberColumnCount screwed up",this);
|
|
|
|
for( int i = 0; i < _lineNumberColumnCount-len; i++ )
|
|
buf.append('0');
|
|
buf.append(s);
|
|
buf.append(':');
|
|
|
|
return buf.toString();
|
|
}
|
|
|
|
private int _width;
|
|
private int _height;
|
|
|
|
private int _lineNumberColumnCount;
|
|
private Font _font;
|
|
|
|
private Rect _rectBP;
|
|
private Rect _rectBPBackground;
|
|
private Polygon _polyExecPoint;
|
|
private Rect _rectExecPointBackground;
|
|
private Rect _rectMarks;
|
|
private Point _ptLineNumbers;
|
|
private Point _ptText;
|
|
private boolean _showLineNumbers;
|
|
private int _charWidth;
|
|
private DrawStringClipper _stringClipper;
|
|
|
|
|
|
}
|
|
|
|
final class SourceTextListItem extends SmartListItem
|
|
{
|
|
public SourceTextListItem(SourceTextItemDrawer drawer, int charCount)
|
|
{
|
|
super();
|
|
_drawer = drawer;
|
|
_charCount = charCount;
|
|
}
|
|
|
|
public boolean mouseDown(MouseEvent me)
|
|
{
|
|
/*
|
|
System.out.println(".......................");
|
|
System.out.println("mousedown x=" + me.x + " y=" + me.y);
|
|
System.out.println(myRect());
|
|
System.out.println(".......................");
|
|
*/
|
|
|
|
_dragOrigin = -1;
|
|
|
|
// middle click
|
|
if( me.isAltKeyDown() )
|
|
return false;
|
|
|
|
// right click
|
|
if( me.isMetaKeyDown() )
|
|
{
|
|
// evaluate selected text
|
|
SourceTextListView v = (SourceTextListView) listView();
|
|
if( v.getItemWithSel() != this )
|
|
return false;
|
|
|
|
Rect rectText = textRect();
|
|
if( ! rectText.contains(me.x,me.y) )
|
|
return false;
|
|
|
|
Emperor emperor = v.getSourceView().getEmperor();
|
|
CommandTyrant ct = emperor.getCommandTyrant();
|
|
Menu menu = new Menu(true);
|
|
|
|
if( ControlTyrant.STOPPED == emperor.getControlTyrant().getState() )
|
|
{
|
|
menu.addItem("Eval", ct.cmdString(CommandTyrant.EVAL_SEL_STRING), ct);
|
|
menu.addItem("Inspect...", ct.cmdString(CommandTyrant.INSPECT_SEL_STRING), ct);
|
|
menu.addItem("Copy to Watch", ct.cmdString(CommandTyrant.COPY_TO_WATCH), ct);
|
|
menu.addSeparator();
|
|
}
|
|
|
|
menu.addItem("Copy", ct.cmdString(CommandTyrant.COPY), ct);
|
|
menu.addSeparator();
|
|
|
|
CmdState cs = ct.findCmdState(CommandTyrant.TOGGLE_BREAKPOINT);
|
|
if( null != cs && cs.enabled )
|
|
{
|
|
if( cs.checked )
|
|
{
|
|
menu.addItem("Clear Breakpoint", ct.cmdString(CommandTyrant.TOGGLE_BREAKPOINT), ct);
|
|
menu.addItem("Edit Breakpoint...", ct.cmdString(CommandTyrant.EDIT_BREAKPOINT), ct);
|
|
}
|
|
else
|
|
menu.addItem("Set Breakpoint", ct.cmdString(CommandTyrant.TOGGLE_BREAKPOINT), ct);
|
|
}
|
|
|
|
// String cmd = ct.cmdString( CommandTyrant.EVAL_STRING );
|
|
// String str = stringInRange(v.getSelStart(), v.getSelEnd());
|
|
// Application.application().performCommandLater( ct, cmd, str );
|
|
|
|
ListView lv = listView();
|
|
RootView rv = lv.rootView();
|
|
MouseEvent tme = lv.convertEventToView(rv, me);
|
|
MouseMenuView mmv = new MouseMenuView(menu);
|
|
mmv.show(rv, tme);
|
|
|
|
return false;
|
|
}
|
|
|
|
Rect rectBP = breakpointRect();
|
|
if( rectBP.contains(me.x,me.y) )
|
|
{
|
|
// toggle breakpoint
|
|
SourceLineItemModel model = (SourceLineItemModel) data();
|
|
SourceView sv = ((SourceTextListView)listView()).getSourceView();
|
|
BreakpointTyrant bpt = sv.getBreakpointTyrant();
|
|
SourceTyrant sourceTyrant = sv.getSourceTyrant();
|
|
SourceTextItem sti = sv.getSourceItem();
|
|
|
|
int sysline = sourceTyrant.userLine2SystemLine(sti,model.lineNumber);
|
|
|
|
Location loc = new Location( sv.getURL(), sysline );
|
|
|
|
Breakpoint bp = bpt.findBreakpoint(loc);
|
|
if( null == bp )
|
|
bpt.addBreakpoint(loc);
|
|
else
|
|
bpt.removeBreakpoint(bp);
|
|
|
|
// redraw done on notification...
|
|
}
|
|
else
|
|
{
|
|
// start select (or select word)
|
|
Rect rectText = textRect();
|
|
if( rectText.contains(me.x,me.y) )
|
|
{
|
|
int start, end;
|
|
SourceTextListView v = (SourceTextListView) listView();
|
|
start = end = charIndexAtPointInRect( me.x, rectText );
|
|
|
|
SourceTextListItem oldItem = v.getItemWithSel();
|
|
v.setItemWithSel(this);
|
|
|
|
if( me.clickCount() > 1 )
|
|
{
|
|
Range r = wordAtIndex(start);
|
|
if( null != r )
|
|
{
|
|
start = r.index;
|
|
end = start + r.length-1;
|
|
}
|
|
_dragOrigin = -1;
|
|
}
|
|
else
|
|
_dragOrigin = start;
|
|
|
|
v.setSelRange(start, end);
|
|
|
|
if( null != oldItem )
|
|
v.drawItemAt( v.indexOfItem(oldItem) );
|
|
v.draw( rectOfRangeInRect(start, end, rectText) );
|
|
|
|
if( _dragOrigin == -1 ) // i.e. was double click
|
|
v.selChangeCompleted();
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public void mouseDragged(MouseEvent me)
|
|
{
|
|
SourceTextListView v = (SourceTextListView) listView();
|
|
if( -1 != _dragOrigin && v.getItemWithSel() == this )
|
|
_extendSelection(me, v);
|
|
}
|
|
|
|
public void mouseUp(MouseEvent me)
|
|
{
|
|
SourceTextListView v = (SourceTextListView) listView();
|
|
if( -1 != _dragOrigin && v.getItemWithSel() == this )
|
|
{
|
|
_extendSelection(me, v);
|
|
v.selChangeCompleted();
|
|
_dragOrigin = -1;
|
|
}
|
|
}
|
|
|
|
private void _extendSelection(MouseEvent me, SourceTextListView v)
|
|
{
|
|
Rect rectText = textRect();
|
|
if( rectText.contains(me.x,me.y) )
|
|
{
|
|
int cur = charIndexAtPointInRect( me.x, rectText );
|
|
int oldStart = v.getSelStart();
|
|
int oldEnd = v.getSelEnd();
|
|
|
|
int start;
|
|
int end;
|
|
|
|
if( cur > _dragOrigin )
|
|
{
|
|
start = _dragOrigin;
|
|
end = cur;
|
|
}
|
|
else
|
|
{
|
|
start = cur;
|
|
end = _dragOrigin;
|
|
}
|
|
if( start == oldStart && end == oldEnd )
|
|
return;
|
|
|
|
v.setSelRange(start, end);
|
|
|
|
// redraw only changed parts (not worried about overlap)...
|
|
int x1, x2;
|
|
x1 = Math.min(oldStart, start);
|
|
x2 = Math.max(oldStart, start);
|
|
if( x1 != x2 )
|
|
v.draw( rectOfRangeInRect(x1, x2, rectText) );
|
|
x1 = Math.min(oldEnd, end);
|
|
x2 = Math.max(oldEnd, end);
|
|
if( x1 != x2 )
|
|
v.draw( rectOfRangeInRect(x1, x2, rectText) );
|
|
}
|
|
}
|
|
|
|
public int minWidth() {return super.minWidth() + _drawer.width();}
|
|
|
|
|
|
public Range wordAtIndex(int index)
|
|
{
|
|
String text = ((SourceLineItemModel)data()).text;
|
|
int len = text.length();
|
|
int start = -1;
|
|
int end;
|
|
int i;
|
|
|
|
// search for first char...
|
|
for( i = 0; i <= index; i++ )
|
|
{
|
|
char c = text.charAt(i);
|
|
if( -1 == start )
|
|
{
|
|
if( Character.isJavaIdentifierStart(c) )
|
|
start = i;
|
|
}
|
|
else
|
|
{
|
|
if( ! Character.isJavaIdentifierPart(c) )
|
|
start = -1;
|
|
}
|
|
}
|
|
if( -1 == start )
|
|
return null;
|
|
|
|
for(; i < len; i++ )
|
|
{
|
|
char c = text.charAt(i);
|
|
if( ! Character.isJavaIdentifierPart(c) )
|
|
break;
|
|
}
|
|
|
|
return new Range(start, i-start);
|
|
}
|
|
|
|
public String stringInRange(int start, int end)
|
|
{
|
|
return ((SourceLineItemModel)data()).text.substring(start,end+1);
|
|
}
|
|
|
|
|
|
|
|
protected void drawBackground(Graphics g, Rect boundsRect)
|
|
{
|
|
SourceTextListView v = (SourceTextListView) listView();
|
|
|
|
if( v.getItemWithSel() == this )
|
|
{
|
|
Rect r = rectOfRangeInRect(v.getSelStart(),v.getSelEnd(),textRect());
|
|
r.intersectWith(boundsRect);
|
|
if( ! r.isEmpty() )
|
|
{
|
|
g.setColor(selectedColor());
|
|
g.fillRect(r);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void drawInRect(Graphics g, Rect boundsRect)
|
|
{
|
|
drawBackground(g, boundsRect);
|
|
|
|
SourceLineItemModel model = (SourceLineItemModel) data();
|
|
_drawer.draw( g, boundsRect, this,
|
|
model.lineNumber,
|
|
model.bp != null,
|
|
model.bp != null?model.bp.getBreakCondition()!=null:false,
|
|
model.type,
|
|
model.executing,
|
|
model.adjustmentChar );
|
|
}
|
|
|
|
// make this public to be called by our drawer helper
|
|
public void drawStringInRect( Graphics g, String title,
|
|
Font titleFont, Rect textBounds,
|
|
int justification)
|
|
{
|
|
// XXX temp test...
|
|
// if(AS.DEBUG) {
|
|
// int len = title.length();
|
|
// String sub;
|
|
// if(0 == len)
|
|
// sub = "BLANK";
|
|
// else
|
|
// sub = title.substring(0, Math.min(60,len));
|
|
// System.out.println("drawing text: \""+sub+"\" at "+textBounds);
|
|
// }
|
|
super.drawStringInRect(g,title,titleFont,textBounds,justification);
|
|
}
|
|
|
|
public Rect breakpointRect()
|
|
{
|
|
// Rect r = new Rect( _drawer.breakpointRect() );
|
|
Rect r = new Rect( _drawer.marksRect() );
|
|
Rect myRect = myRect();
|
|
r.moveBy(myRect.x, myRect.y);
|
|
return r;
|
|
}
|
|
|
|
public Rect textRect()
|
|
{
|
|
Rect r = new Rect( _drawer.textOrigin().x,
|
|
_drawer.textOrigin().y,
|
|
_drawer.charWidth() * _charCount,
|
|
_drawer.height() );
|
|
Rect myRect = myRect();
|
|
r.moveBy(myRect.x, myRect.y);
|
|
return r;
|
|
}
|
|
|
|
// ASSUMES that point is in rect -- caller beware!
|
|
protected int charIndexAtPointInRect( int x, Rect r )
|
|
{
|
|
return (x - r.x) / _drawer.charWidth();
|
|
}
|
|
|
|
protected Rect rectOfRangeInRect( int start, int end, Rect r )
|
|
{
|
|
int cw = _drawer.charWidth();
|
|
return new Rect((start*cw)+r.x, r.y, (1+end-start)*cw, r.height);
|
|
}
|
|
|
|
private int _dragOrigin = -1;
|
|
private int _charCount;
|
|
private SourceTextItemDrawer _drawer;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
final class SourceLineVectorModel
|
|
{
|
|
public SourceLineVectorModel( SourceView sourceView,
|
|
ControlTyrant controlTyrant,
|
|
StackTyrant stackTyrant,
|
|
SourceTyrant sourceTyrant,
|
|
SourceTextItem sourceTextItem )
|
|
{
|
|
_sourceView = sourceView;
|
|
_controlTyrant = controlTyrant;
|
|
_stackTyrant = stackTyrant;
|
|
_sourceTextItem = sourceTextItem;
|
|
_sourceTyrant = sourceTyrant;
|
|
_sourceLineItemVector = new Vector();
|
|
}
|
|
|
|
public synchronized void rebuildLineItemVector()
|
|
{
|
|
Vector vec = new Vector();
|
|
try
|
|
{
|
|
DataInputStream s = new DataInputStream(
|
|
new StringBufferInputStream(
|
|
getSourceTextItem().getText() ));
|
|
if( null != s )
|
|
{
|
|
String linetext;
|
|
int lineNumber = 1;
|
|
while( null != (linetext = s.readLine()) )
|
|
{
|
|
SourceLineItemModel item = new SourceLineItemModel();
|
|
|
|
item.lineNumber = lineNumber;
|
|
item.text = Util.expandTabs(linetext,8);
|
|
item.type = SourceLineItemModel.NO_SCRIPT;
|
|
item.bp = null; // XXX
|
|
item.executing = false;
|
|
item.adjustmentChar = null;
|
|
|
|
vec.addElement( item );
|
|
lineNumber++ ;
|
|
}
|
|
s.close();
|
|
}
|
|
}
|
|
catch( Exception e ) {}
|
|
|
|
_sourceLineItemVector = vec;
|
|
updateLineItemVector();
|
|
}
|
|
|
|
public synchronized void updateLineItemVector()
|
|
{
|
|
/*
|
|
// XXX script stuff must change...
|
|
ScriptProvider sp = JSDScriptProvider.getScriptProvider();
|
|
if( null == sp )
|
|
return;
|
|
*/
|
|
|
|
int i;
|
|
int count;
|
|
|
|
// clear all line
|
|
|
|
int maxLineItemIndex = _sourceLineItemVector.size() - 1;
|
|
|
|
count = _sourceLineItemVector.size();
|
|
if( 0 == count )
|
|
return;
|
|
for( i = 0; i < count; i++ )
|
|
{
|
|
SourceLineItemModel item =
|
|
(SourceLineItemModel) _sourceLineItemVector.elementAt(i);
|
|
item.type = SourceLineItemModel.NO_SCRIPT;
|
|
item.bp = null;
|
|
item.executing = false;
|
|
item.adjustmentChar = null;
|
|
}
|
|
|
|
// walk through the scripts and mark lines with scripts
|
|
|
|
String url = _sourceTextItem.getURL();
|
|
BreakpointTyrant bpt = _sourceView.getBreakpointTyrant();
|
|
|
|
Vector vecScripts = bpt.getScriptsVectorForURL(url);
|
|
count = vecScripts.size();
|
|
for( i = 0; i < count; i++ )
|
|
{
|
|
Script script = (Script) vecScripts.elementAt(i);
|
|
|
|
if( ! script.isValid() )
|
|
continue;
|
|
|
|
boolean fun = null != script.getFunction();
|
|
|
|
ScriptSection[] sections = script.getSections();
|
|
for( int n = 0; n < sections.length; n++ )
|
|
{
|
|
int base = sections[n].getBaseLineNumber();
|
|
int extent = sections[n].getLineExtent();
|
|
|
|
for( int k = base; k < (base+extent); k++ )
|
|
{
|
|
if( k > maxLineItemIndex+1 )
|
|
continue;
|
|
int j = _sourceTyrant.systemLine2UserLine(_sourceTextItem,k);
|
|
if( j > maxLineItemIndex+1 )
|
|
continue;
|
|
SourceLineItemModel item =
|
|
(SourceLineItemModel) _sourceLineItemVector.elementAt(j-1);
|
|
|
|
// don't overwrite function with top level script
|
|
|
|
if( fun )
|
|
item.type = SourceLineItemModel.FUNCTION_BODY;
|
|
else if( item.type != SourceLineItemModel.FUNCTION_BODY )
|
|
item.type = SourceLineItemModel.TOP_LEVEL_SCRIPT;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// set breakpoints
|
|
Vector vecBP = bpt.getBreakpointsForURL( _sourceTextItem.getURL() );
|
|
count = vecBP.size();
|
|
for( i = 0; i < count; i++ )
|
|
{
|
|
Breakpoint bp = (Breakpoint)vecBP.elementAt(i);
|
|
int line = bp.getLine();
|
|
line = _sourceTyrant.systemLine2UserLine(_sourceTextItem,line);
|
|
|
|
if( line > maxLineItemIndex+1 )
|
|
continue;
|
|
|
|
SourceLineItemModel item = (SourceLineItemModel)
|
|
_sourceLineItemVector.elementAt(line-1);
|
|
item.bp = bp;
|
|
}
|
|
|
|
// set executing lines
|
|
|
|
if( ControlTyrant.STOPPED == _controlTyrant.getState() )
|
|
{
|
|
JSSourceLocation loc = _stackTyrant.getCurrentLocation();
|
|
if( null != loc && loc.getURL().equals(url) )
|
|
{
|
|
int index = loc.getLine();
|
|
index = _sourceTyrant.systemLine2UserLine(_sourceTextItem,index);
|
|
|
|
if( index > maxLineItemIndex+1 )
|
|
index = maxLineItemIndex+1;
|
|
if( index < 1 )
|
|
index = 1;
|
|
|
|
SourceLineItemModel item = (SourceLineItemModel)
|
|
_sourceLineItemVector.elementAt(index-1);
|
|
item.executing = true;
|
|
}
|
|
}
|
|
|
|
// set adjustment chars
|
|
|
|
int[] adjArray = _sourceTyrant.getUserAdjustedLineArray(_sourceTextItem);
|
|
if( null != adjArray )
|
|
{
|
|
String adjChar;
|
|
for( int k = 0; k < adjArray.length; k++ )
|
|
{
|
|
int line = adjArray[k];
|
|
if( line < 0 )
|
|
{
|
|
line *= -1;
|
|
adjChar = "-";
|
|
}
|
|
else
|
|
adjChar = "+";
|
|
SourceLineItemModel item = (SourceLineItemModel)
|
|
_sourceLineItemVector.elementAt(line-1);
|
|
item.adjustmentChar = adjChar;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// This is out of date. If anyone wants to reserect it then they
|
|
// had better update this to do the right things!
|
|
/*
|
|
public synchronized void updateSingleLineItem( int lineNumber )
|
|
{
|
|
BreakpointTyrant bpt = _sourceView.getBreakpointTyrant();
|
|
Breakpoint bp = bpt.findBreakpoint(
|
|
new Location(_sourceTextItem.getURL(),lineNumber));
|
|
|
|
int maxLineItemIndex = _sourceLineItemVector.size() - 1;
|
|
if( lineNumber > maxLineItemIndex+1 )
|
|
lineNumber = maxLineItemIndex+1;
|
|
|
|
SourceLineItemModel item = (SourceLineItemModel)
|
|
_sourceLineItemVector.elementAt(lineNumber-1);
|
|
item.bp = bp;
|
|
}
|
|
*/
|
|
|
|
public Vector getLineItemVector() {return _sourceLineItemVector;}
|
|
public SourceTextItem getSourceTextItem() {return _sourceTextItem;}
|
|
|
|
private SourceView _sourceView;
|
|
private SourceTyrant _sourceTyrant;
|
|
private ControlTyrant _controlTyrant;
|
|
private StackTyrant _stackTyrant;
|
|
private SourceTextItem _sourceTextItem; // has url, text, and fullness status
|
|
private Vector _sourceLineItemVector;
|
|
}
|
|
|
|
|
|
|