//fields of KTextArea
private Text m_text = new Text(); //文本
private int m_rpos = 0; //光标行位置 m_rpos∈0..getLines()-1,行只计数,不分区
private int m_cpos = 0; //光标列位置 m_cpos∈0..MAXCOLUMNS private boolean m_selected = false;
private int m_rsel_end;
private int m_csel_end; private int m_view_x = 0; //m_view_x∈[0,∽), view of x∈m_view_x..m_view_x+VIEW_WIDTH;
private int m_view_y = 0; //m_view_y∈[0,∽), view of y∈m_view_y..m_view_y+VIEW_HEIGHT; private java.awt.Scrollbar m_hScrollbar = new java.awt.Scrollbar(java.awt.Scrollbar.HORIZONTAL);
private java.awt.Scrollbar m_vScrollbar = new java.awt.Scrollbar(java.awt.Scrollbar.VERTICAL); private class TextCanvasComponent extends java.awt.Component
{
private java.awt.Image m_offImage = null;
public void invalidate()
{
super.invalidate();
m_offImage = null;
}
public void update(java.awt.Graphics g)
{
paint(g);
}
public void paint(java.awt.Graphics g)
{
if(m_offImage == null){
m_offImage = this.createImage(this.getWidth(),this.getHeight());
}
java.awt.Graphics goff = m_offImage.getGraphics();
goff.setColor(java.awt.Color.white);
goff.fillRect(0,0,this.getWidth()-1,this.getHeight()-1);
textCanvas_paint(goff);
g.drawImage(m_offImage,0,0,null);
goff.dispose();
}
}
private TextCanvasComponent m_textCanvas = new TextCanvasComponent();
public KTextArea()
{
this("");
}
public KTextArea(String text0)
{
this.setLayout(new java.awt.BorderLayout());
this.add(m_textCanvas,java.awt.BorderLayout.CENTER); m_textCanvas.addComponentListener(new java.awt.event.ComponentAdapter() {
public void componentResized(java.awt.event.ComponentEvent e)
{
adjustViewXY();
}
}); this.add(m_hScrollbar,java.awt.BorderLayout.SOUTH);
this.add(m_vScrollbar,java.awt.BorderLayout.EAST);
m_hScrollbar.addAdjustmentListener(new java.awt.event.AdjustmentListener(){
public void adjustmentValueChanged(java.awt.event.AdjustmentEvent e)
{
m_view_x = e.getValue();
m_textCanvas.repaint();
}
});
m_vScrollbar.addAdjustmentListener(new java.awt.event.AdjustmentListener(){
public void adjustmentValueChanged(java.awt.event.AdjustmentEvent e)
{
m_view_y = e.getValue();
m_textCanvas.repaint();
}
}); m_textCanvas.addMouseListener(new java.awt.event.MouseAdapter()
{
public void mousePressed(java.awt.event.MouseEvent e)
{
if((e.getModifiers()&java.awt.event.MouseEvent.BUTTON1_MASK) != 0){ //左鍵按下
// 物理视图<xp,yp> = 逻辑视图<xl=xp,yl=yp> = 内容视图<xc=xl+m_view_x,yc=yl+m_view_y>
// e.getX(),e.getY()为物理视图
int x = e.getX()+m_view_x;
int y = e.getY()+m_view_y;
this_mousePressed(x,y);
}
}
public void mouseEntered(java.awt.event.MouseEvent e)
{
setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.TEXT_CURSOR));
}
public void mouseExited(java.awt.event.MouseEvent e)
{
setCursor(java.awt.Cursor.getDefaultCursor());
}
});
m_textCanvas.addKeyListener(new java.awt.event.KeyAdapter()
{
public void keyTyped(java.awt.event.KeyEvent e)
{
char c = e.getKeyChar();
if(Character.isDefined(c) && Character.getType(c)!=Character.CONTROL){
this_keyTyped(c);
}
}
public void keyPressed(java.awt.event.KeyEvent e)
{
switch(e.getKeyCode()){
case java.awt.event.KeyEvent.VK_LEFT:
this_keyLeft();
break;
case java.awt.event.KeyEvent.VK_RIGHT:
this_keyRight();
break;
case java.awt.event.KeyEvent.VK_UP:
this_keyUp();
break;
case java.awt.event.KeyEvent.VK_DOWN:
this_keyDown();
break;
case java.awt.event.KeyEvent.VK_HOME:
if((e.getModifiers()&java.awt.event.KeyEvent.CTRL_MASK) == 0){
this_keyHome();
}else{
this_keyCtrlHome();
}
break;
case java.awt.event.KeyEvent.VK_END:
if((e.getModifiers()&java.awt.event.KeyEvent.CTRL_MASK) == 0){
this_keyEnd();
}else{
this_keyCtrlEnd();
}
break;
case java.awt.event.KeyEvent.VK_PAGE_UP:
this_keyPageUp();
break;
case java.awt.event.KeyEvent.VK_PAGE_DOWN:
this_keyPageDown();
break; //删除键
case java.awt.event.KeyEvent.VK_BACK_SPACE:
this_keyBackspace();
break;
case java.awt.event.KeyEvent.VK_DELETE:
if(e.getModifiers() != java.awt.event.KeyEvent.ALT_MASK){
this_keyDelete();
}else{
this_keyCtrlY();
}
break;
case java.awt.event.KeyEvent.VK_ENTER:
this_keyEnter();
break;
//修改字体
case java.awt.event.KeyEvent.VK_F2:
m_textCanvas.setFont(new java.awt.Font(m_textCanvas.getFont().getName(),m_textCanvas.getFont().getStyle(),m_textCanvas.getFont().getSize()>1 ? m_textCanvas.getFont().getSize()-1 : 1));
repaint();
break;
case java.awt.event.KeyEvent.VK_F3:
m_textCanvas.setFont(new java.awt.Font(m_textCanvas.getFont().getName(),m_textCanvas.getFont().getStyle(),m_textCanvas.getFont().getSize()+1));
repaint();
break;
}
}
});
//            java.util.StringTokenizer st = new java.util.StringTokenizer("\n");
//            while(st.hasMoreTokens()){
//                String line = st.nextToken();
//                System.out.println("line="+line);
//                m_text.appendLine(new StringBuffer(line));
//            }
//for test only
m_text.appendLine(new StringBuffer("R0欢迎使用轻量级组件编写的文本编辑器."));
m_text.appendLine(new StringBuffer("R1这个演示了修改光标的方法,你可以使用鼠标点击或键盘的光标键来修改光标."));
m_text.appendLine(new StringBuffer("R2这个演示了不可编辑的文本组件."));
m_text.appendLine(new StringBuffer("R3负责中心内会议的组织召开和文件起草等工作."));
m_text.appendLine(new StringBuffer("R4负责中心各种经济合同、人事合同管理."));
m_text.appendLine(new StringBuffer("R5负责中心印鉴的管理,严格按审批手续用印,并对使用情况予以监督。"));
m_text.appendLine(new StringBuffer("R6负责中心计算机系统维护、耗材使用、日常管理等方面工作。"));
m_text.appendLine(new StringBuffer("R7积极完成中心领导交办的其它临时性工作任务."));
}

解决方案 »

  1.   


    public void textCanvas_paint(java.awt.Graphics g)
    {
    java.awt.FontMetrics fm = g.getFontMetrics();
    int LH = fm.getHeight();
    int VIEWW = m_textCanvas.getWidth();
    int VIEWH = m_textCanvas.getHeight(); //视图区为<0..VIEWW-1, 0..VIEWH-1>
    String CRTAG = "↙"; //回车标记
    //set scrollbars
    //第i行在内容视图上的y坐标范围为:i*H..(i+1)*H-1
    //转化为逻辑视图上的y坐标范围为:i*H-m_view_x..(i+1)*H-1-m_view_y
    //逻辑视图区域y坐标范围为:0..VIEWH-1
    //所以第i行可视需要:i*H-m_view_y>=0 && (i+1)*H-1-m_view_y<=VIEWH-1
    //变换为i>=m_view_y/H && i<=(VIEWH+m_view_y)/H-1
    int maxWidthInView = 0;
    for(int i=m_view_y/LH; i<=Math.min(m_text.getLines()-1,(VIEWH+m_view_y)/LH-1); i++){ //可视行
    int iLineLen = fm.stringWidth(m_text.getLine(i).toString())+fm.stringWidth(CRTAG);
    if(maxWidthInView < iLineLen){
    maxWidthInView = iLineLen;
    }
    }
    m_hScrollbar.setValues(m_view_x,VIEWW,0,maxWidthInView-1);
    //max=m_view_y+Max{VIEWH,ch-m_view_y}
    m_vScrollbar.setValues(m_view_y,VIEWH,0,m_view_y+Math.max(VIEWH,m_text.getLines()*LH-m_view_y-1)); g.setColor(java.awt.Color.blue);
    g.drawRect(0,0,m_textCanvas.getWidth()-1,m_textCanvas.getHeight()-1);
    g.setClip(0,0,m_textCanvas.getWidth()-1,m_textCanvas.getHeight()-1);
    //draw text
    int MAXASCENT = fm.getMaxAscent();
    //仅画出在逻辑视图区可视的文本行
    //end_line_in_view = ┏(m_view_y+VIEWH)/LH┓-1
    for(int i=m_view_y/LH; i<Math.min(((m_view_y+VIEWH)%LH)==0 ? (m_view_y+VIEWH)/LH : (m_view_y+VIEWH)/LH+1,  m_text.getLines()); i++){ //多加上了一行
    String iLine = m_text.getLine(i).toString();
    g.setColor(java.awt.Color.black);
    g.drawString(iLine,0-m_view_x,i*LH-m_view_y+MAXASCENT); //物理视图<0,i*H>转换为逻辑视图<0-m_view_x, i*H-m_view_y>
    g.setColor(java.awt.Color.lightGray);
    g.drawString(CRTAG,0-m_view_x+fm.stringWidth(iLine),i*LH-m_view_y+MAXASCENT);
    } //draw caret
    g.setColor(java.awt.Color.red);
    int caretx = (m_text.getLines()>0 ? fm.stringWidth(m_text.getLine(m_rpos).substring(0,m_cpos)) : 0) - m_view_x; //内容视图转换为逻辑视图
    int carety = LH*m_rpos-m_view_y;
    g.drawLine(caretx,carety,caretx,carety+LH);
    }
    private void adjustViewXY() //调整m_view_x,m_view_y以确保m_cpos,m_rpos在视图内
    {
    if(m_text.getLines() > 0){
    //adjust m_view_x
    //第j列的内容视图坐标X范围为(【j-1】+【j】)/2 .. (【j】+【j+1】)/2
    //转换为逻辑视图坐标范围为(【j-1】+【j】)/2-m_view_x .. (【j】+【j+1】)/2-m_view_x
    //而逻辑视图区域为0 .. VIEWW-1
    java.awt.FontMetrics fm = m_textCanvas.getFontMetrics(m_textCanvas.getFont());
    int VIEWW = m_textCanvas.getWidth();
    int VIEWH = m_textCanvas.getHeight();
    String lineText = m_text.getLine(m_rpos).toString();
    int LINE_CHARS= lineText.length();
    if(LINE_CHARS > 0){
    if(m_cpos==0){
    if(m_view_x > 0){
    m_view_x = 0;
    }
    }else if(m_cpos==LINE_CHARS){
    if(m_view_x+VIEWW < fm.stringWidth(lineText)){
    m_view_x = fm.stringWidth(lineText)-VIEWW;
    }
    }else{
    if(m_view_x > (fm.stringWidth(lineText.substring(0,m_cpos-1))+fm.stringWidth(lineText.substring(0,m_cpos)))/2){
    m_view_x = (fm.stringWidth(lineText.substring(0,m_cpos-1))+fm.stringWidth(lineText.substring(0,m_cpos)))/2;
    }
    if(m_view_x < (fm.stringWidth(lineText.substring(0,m_cpos))+fm.stringWidth(lineText.substring(0,m_cpos+1)))/2-VIEWW+1){
    m_view_x = (fm.stringWidth(lineText.substring(0,m_cpos))+fm.stringWidth(lineText.substring(0,m_cpos+1)))/2-VIEWW+1;
    }
    }
    }
    //adjust m_view_y
    //第i行的内容坐标Y范围为i*H..(i+1)*H-1,转换为逻辑坐标为i*H-m_view_x..(i+1)*H-1-m_view_y
    //视图区域Y范围为0..VIEWH-1
    int H = fm.getHeight();
    if(m_view_y > m_rpos*H){
    m_view_y = m_rpos*H;
    }
    if(m_view_y<(m_rpos+1)*H-VIEWH){
    m_view_y = (m_rpos+1)*H-VIEWH;
    }
    }
    }
      

  2.   

    // <x,y>为内容视图坐标
    private void this_mousePressed(int x,int y)
    {
    if(m_text.getLines() > 0){
    m_rpos = find_rpos_of_y(y);
    m_cpos = find_cpos_of_x(x);
    adjustViewXY();
    repaint();
    }
    }
    //y为在内容视图上的坐标
    protected int find_rpos_of_y(int y)
    {
    //第i行的内容视图坐标范围为i*H .. i*H+(H-1)
    //则i*H/H=i, [i*H+(H-1)]/H=i...(H-1)
    //所以i*H div H=i,i*H+(H-1) div H=i
    java.awt.FontMetrics fm = m_textCanvas.getFontMetrics(m_textCanvas.getFont());
    int i = y/fm.getHeight(); //┗y/H┛
    i = Math.min(Math.max(i,0),m_text.getLines()-1);
    return i;
    }
    protected int find_cpos_of_x(int x) //assert m_text.getLines()>0
    {
    java.awt.FontMetrics fm = m_textCanvas.getFontMetrics(m_textCanvas.getFont());
    int cpos;
    String linetext = m_text.getLine(m_rpos).toString();
    int N= linetext.length();
    if(N > 0){
    if(x <= fm.stringWidth(linetext.substring(0,1))/2){
    cpos = 0;
    }else if(x >= (fm.stringWidth(linetext.substring(0,N-1))+fm.stringWidth(linetext.substring(0,N)))/2){
    cpos = N;
    }else{
    int low = 1, high = N-1;
    boolean suitable = false;
    int mid = 0;
    while( ! suitable){
    mid = (low+high)/2;
    if(x>=(fm.stringWidth(linetext.substring(0,mid-1))+fm.stringWidth(linetext.substring(0,mid)))/2 && x<(fm.stringWidth(linetext.substring(0,mid))+fm.stringWidth(linetext.substring(0,mid+1)))/2){
    suitable = true;
    }else if(x < (fm.stringWidth(linetext.substring(0,mid-1))+fm.stringWidth(linetext.substring(0,mid)))/2){
    high = mid-1;
    }else{
    low = mid+1;
    }
    }
    cpos = mid;
    }
    }else{ //N==0
    cpos =0;
    }
    return cpos;
    }
    private void this_keyLeft()
    {
    if(m_text.getLines() > 0){
    if(m_cpos>0){
    m_cpos--;
    }
    adjustViewXY();
    repaint();
    }
    }
    private void this_keyRight()
    {
    if(m_text.getLines() > 0){
    if(m_cpos<m_text.getLine(m_rpos).length()){
    m_cpos++;
    }
    adjustViewXY();
    repaint();
    }
    }
    private void this_keyUp()
    {
    if(m_text.getLines() > 0){
    if(m_rpos>0){
    m_rpos--;
    m_cpos = (m_cpos<=m_text.getLine(m_rpos).length() ? m_cpos : m_text.getLine(m_rpos).length());
    }
    adjustViewXY();
    repaint();
    }
    }
    private void this_keyDown()
    {
    if(m_text.getLines() > 0){
    if(m_rpos<m_text.getLines()-1){
    m_rpos++;
    m_cpos = (m_cpos<=m_text.getLine(m_rpos).length() ? m_cpos : m_text.getLine(m_rpos).length());
    }
    adjustViewXY();
    repaint();
    }
    }
    private void this_keyHome()
    {
    if(m_text.getLines() > 0){
    m_cpos = 0;
    adjustViewXY();
    repaint();
    }
    }
    private void this_keyEnd()
    {
    if(m_text.getLines() > 0){
    m_cpos = m_text.getLine(m_rpos).length();
    adjustViewXY();
    repaint();
    }
    }
    private void this_keyPageUp()
    {
    if(m_text.getLines() > 0){
    int VIEWH = m_textCanvas.getHeight();
    int H = m_textCanvas.getFontMetrics(m_textCanvas.getFont()).getHeight();
    int pageLines = VIEWH/H; //VIEWH div LH=pl,[VIEWH+(LH-1)] div LH=pl
    m_rpos = Math.max(m_rpos-pageLines,0);
    m_cpos = (m_cpos<=m_text.getLine(m_rpos).length() ? m_cpos : m_text.getLine(m_rpos).length());
    adjustViewXY();
    repaint();
    }
    }
    private void this_keyPageDown()
    {
    if(m_text.getLines() > 0){
    int VIEWH = m_textCanvas.getHeight();
    int H = m_textCanvas.getFontMetrics(m_textCanvas.getFont()).getHeight();
    int pageLines = VIEWH/H;
    m_rpos = Math.min(m_rpos+pageLines+1,m_text.getLines()-1);
    m_cpos = (m_cpos<=m_text.getLine(m_rpos).length() ? m_cpos : m_text.getLine(m_rpos).length());
    adjustViewXY();
    repaint();
    }
    }
    private void this_keyCtrlHome()
    {
    if(m_text.getLines() > 0){
    m_rpos = 0;
    m_cpos = (m_cpos<=m_text.getLine(m_rpos).length() ? m_cpos : m_text.getLine(m_rpos).length());
    adjustViewXY();
    repaint();
    }
    }
    private void this_keyCtrlEnd()
    {
    if(m_text.getLines() > 0){
    m_rpos = m_text.getLines()-1;
    m_cpos = (m_cpos<=m_text.getLine(m_rpos).length() ? m_cpos : m_text.getLine(m_rpos).length());
    adjustViewXY();
    repaint();
    }
    }
    private void this_keyTyped(char c)
    {
    if(m_text.getLines() == 0){
    m_text.appendLine(new StringBuffer());
    }
    m_text.getLine(m_rpos).insert(m_cpos,c);
    m_cpos++;
    adjustViewXY();
    repaint();
    }
    private void this_keyBackspace()
    {
    if(m_text.getLines() > 0){
    if(m_cpos > 0){
    m_text.getLine(m_rpos).deleteCharAt(m_cpos-1);
    m_cpos--;
    }else if(m_rpos > 0){
    StringBuffer line0 = m_text.getLine(m_rpos);
    StringBuffer lineprev = m_text.getLine(m_rpos-1);
    m_cpos = lineprev.length();
    lineprev.append(line0.toString());
    m_text.deleteLine(m_rpos);
    m_rpos--;
    }
    adjustViewXY();
    repaint();
    }
    }
    private void this_keyDelete()
    {
    if(m_text.getLines() > 0){
    if(m_cpos < m_text.getLine(m_rpos).length()){
    m_text.getLine(m_rpos).deleteCharAt(m_cpos);
    }else if(m_rpos < m_text.getLines()-1){
    StringBuffer line0 = m_text.getLine(m_rpos);
    StringBuffer linenext = m_text.getLine(m_rpos+1);
    line0.append(linenext.toString());
    m_text.deleteLine(m_rpos+1);
    }
    adjustViewXY();
    repaint();
    }
    }
    public void this_keyEnter()
    {
    if(m_text.getLines() == 0){
    m_text.appendLine(new StringBuffer());
    }
    StringBuffer line0 = m_text.getLine(m_rpos);
    StringBuffer line1 = new StringBuffer(line0.substring(m_cpos,line0.length()));
    line0.delete(m_cpos,line0.length());
    m_text.insertLineAfter(m_rpos,line1);
    m_rpos++;
    m_cpos = 0;
    adjustViewXY();
    repaint();
    }
    public void this_keyCtrlY() //Ctrl-Y,delete current line
    {
    if(m_text.getLines()>0){
    m_text.deleteLine(m_rpos);
    m_rpos = Math.max(Math.min(m_rpos,m_text.getLines()-1),0);
    m_cpos = 0;
    adjustViewXY();
    repaint();
    }
    }
    }
      

  3.   

    public class MyApp extends java.awt.Frame
    {
            public static void main(String[] args) throws Exception
            {
                    MyApp myApp1 = new MyApp();
            }
            private KTextArea m_textArea1 = new KTextArea("欢迎使用轻量级组件编写的文本编辑器。\n这个演示了修改光标的方法,");
            public MyApp() throws Exception
            {
                    addWindowListener(new java.awt.event.WindowAdapter(){
                            public void windowClosing(java.awt.event.WindowEvent e)
                            {
                                    dispose();
                            }
                            public void windowClosed(java.awt.event.WindowEvent e)
                            {
                                    System.exit(0);
                            }
                    });
                    setSize(320,240);
    // setResizable(false);
                    setLayout(new java.awt.GridLayout(0,1,5,10));
                    add(m_textArea1);
    // pack();
                    setLocation((java.awt.Toolkit.getDefaultToolkit().getScreenSize().width-this.getSize().width)/2,(java.awt.Toolkit.getDefaultToolkit().getScreenSize().height-this.getSize().height)/2);
                    setVisible(true);
            }
    }
      

  4.   

    上面是测试程序,这个组件的用法和java.awt.TextArea基本相同(也有一些不同点)
      

  5.   

    单行文本编辑器组件的源代码在:
    http://community.csdn.net/Expert/topic/3161/3161156.xml?temp=.1985742