ScrollTextArea.java
     1: //========================================================================================
     2: //  ScrollTextArea.java
     3: //    en:Text area with scroll bars -- It is a modified JScrollPage that has a JTextArea as the view.
     4: //    ja:スクロールバー付きテキストエリア -- JTextAreaをビューに持つJScrollPaneです。
     5: //  Copyright (C) 2003-2017 Makoto Kamada
     6: //
     7: //  This file is part of the XEiJ (X68000 Emulator in Java).
     8: //  You can use, modify and redistribute the XEiJ if the conditions are met.
     9: //  Read the XEiJ License for more details.
    10: //  http://stdkmd.com/xeij/
    11: //========================================================================================
    12: 
    13: //----------------------------------------------------------------------------------------
    14: //  追加機能
    15: //    背景にグリッドを表示できる
    16: //    ハイライトエリアを表示できる
    17: //    ハイライトカーソルを表示できる
    18: //    アンダーラインカーソルを表示できる
    19: //----------------------------------------------------------------------------------------
    20: 
    21: package xeij;
    22: 
    23: import java.awt.*;  //BasicStroke,BorderLayout,BoxLayout,Color,Component,Container,Cursor,Desktop,Dimension,Font,Frame,Graphics,Graphics2D,GraphicsDevice,GraphicsEnvironment,GridLayout,Image,Insets,Paint,Point,Rectangle,RenderingHints,Robot,Shape,Stroke,TexturePaint,Toolkit
    24: import java.awt.event.*;  //ActionEvent,ActionListener,ComponentAdapter,ComponentEvent,ComponentListener,FocusAdapter,FocusEvent,FocusListener,InputEvent,KeyAdapter,KeyEvent,KeyListener,MouseAdapter,MouseEvent,MouseListener,MouseMotionAdapter,MouseWheelEvent,WindowAdapter,WindowEvent,WindowListener,WindowStateListener
    25: import java.awt.image.*;  //BufferedImage,DataBuffer,DataBufferByte,DataBufferInt,IndexColorModel
    26: import javax.swing.*;  //AbstractSpinnerModel,Box,ButtonGroup,DefaultListModel,ImageIcon,JApplet,JButton,JCheckBox,JCheckBoxMenuItem,JDialog,JFileChooser,JFrame,JLabel,JList,JMenu,JMenuBar,JMenuItem,JPanel,JRadioButton,JScrollPane,JSpinner,JTextArea,JTextField,JTextPane,JViewport,ScrollPaneConstants,SpinnerListModel,SpinnerNumberModel,SwingConstants,SwingUtilities,UIManager,UIDefaults,UnsupportedLookAndFeelException
    27: import javax.swing.event.*;  //CaretListener,ChangeEvent,ChangeListener,DocumentEvent,DocumentListener,ListSelectionListener
    28: import javax.swing.text.*;  //AbstractDocument,BadLocationException,DefaultCaret,Document,DocumentFilter,JTextComponent,ParagraphView,Style,StyleConstants,StyleContext,StyledDocument
    29: 
    30: public class ScrollTextArea extends JScrollPane {
    31: 
    32:   private boolean opaqueOn;  //true=背景を表示する
    33:   private boolean gridOn;  //true=グリッドを表示する
    34:   private boolean highlightCursorOn;  //true=ハイライトカーソルを表示する
    35:   private boolean underlineCursorOn;  //true=アンダーラインカーソルを表示する
    36: 
    37:   private Color foregroundColor;  //文字の色
    38:   private Color backgroundColor;  //背景の色
    39:   private Color rowGridColor;  //行グリッドの色
    40:   private Color columnGridColor;  //列グリッドの色
    41:   private Color tabColumnGridColor;  //タブ列グリッドの色
    42:   private Color highlightAreaColor;  //ハイライトエリアの色
    43:   private Color highlightCursorColor;  //ハイライトカーソルの色
    44:   private Color underlineCursorColor;  //アンダーラインカーソルの色
    45: 
    46:   private Paint gridPaint;  //グリッドのテクスチャペイント
    47:   private int highlightAreaStart;  //ハイライトエリアの開始位置
    48:   private int highlightAreaEnd;  //ハイライトエリアの終了位置。-1=ハイライトエリアなし
    49:   private Stroke underlineCursorStroke;  //アンダーラインカーソルのストローク
    50: 
    51:   private JTextArea textArea;
    52: 
    53:   private int fontWidth;
    54:   private int fontHeight;
    55:   private int marginTop;
    56:   private int marginLeft;
    57: 
    58:   private DefaultCaret caret;
    59: 
    60:   //コンストラクタ
    61:   public ScrollTextArea () {
    62: 
    63:     opaqueOn = true;
    64:     gridOn = true;
    65:     highlightCursorOn = false;
    66:     underlineCursorOn = false;
    67: 
    68:     foregroundColor      = new Color (LnF.LNF_RGB[14]);
    69:     backgroundColor      = new Color (LnF.LNF_RGB[0]);
    70:     rowGridColor         = new Color (LnF.LNF_RGB[2]);
    71:     columnGridColor      = new Color (LnF.LNF_RGB[1]);
    72:     tabColumnGridColor   = new Color (LnF.LNF_RGB[2]);
    73:     highlightAreaColor   = new Color (LnF.LNF_RGB[3]);
    74:     highlightCursorColor = new Color (LnF.LNF_RGB[4]);
    75:     underlineCursorColor = new Color (LnF.LNF_RGB[10]);
    76: 
    77:     gridPaint = backgroundColor;
    78:     highlightAreaStart = highlightAreaEnd = -1;
    79:     underlineCursorStroke = new BasicStroke (1.0F,
    80:                                              BasicStroke.CAP_SQUARE,
    81:                                              BasicStroke.JOIN_MITER,
    82:                                              10.0F,
    83:                                              new float[] { 0.0F, 2.0F },
    84:                                              0.0F);
    85: 
    86:     textArea = new JTextArea () {
    87:       //描画
    88:       @Override public void paintComponent (Graphics g) {
    89:         Graphics2D g2 = (Graphics2D) g;
    90:         int width = getWidth ();
    91:         int height = getHeight ();
    92:         //背景
    93:         if (opaqueOn) {
    94:           g2.setPaint (gridOn ? gridPaint : backgroundColor);
    95:           g2.fillRect (0, 0, width, height);
    96:         }
    97:         //ハイライトエリア
    98:         if (0 <= highlightAreaEnd) {
    99:           try {
   100:             Rectangle startRect = modelToView (highlightAreaStart);
   101:             Rectangle endRect = modelToView (highlightAreaEnd);
   102:             Insets margin = getMargin ();
   103:             g2.setPaint (highlightAreaColor);
   104:             g2.fillRect (margin.left, startRect.y,
   105:                          width - margin.left - margin.right, endRect.y - startRect.y + endRect.height);
   106:           } catch (BadLocationException ble) {
   107:           }
   108:         }
   109:         //ラインカーソル
   110:         if (highlightCursorOn || underlineCursorOn) {
   111:           try {
   112:             Rectangle caretRect = modelToView (getCaretPosition ());
   113:             Insets margin = getMargin ();
   114:             if (highlightCursorOn) {
   115:               g2.setPaint (highlightCursorColor);
   116:               g2.fillRect (margin.left, caretRect.y,
   117:                            width - margin.left - margin.right, caretRect.height);
   118:             }
   119:             if (underlineCursorOn) {
   120:               g2.setPaint (underlineCursorColor);
   121:               g2.setStroke (underlineCursorStroke);
   122:               g2.drawLine (margin.left, caretRect.y + caretRect.height - 1,
   123:                            width - margin.right - 1, caretRect.y + caretRect.height - 1);
   124:             }
   125:           } catch (BadLocationException ble) {
   126:           }
   127:         }
   128:         //テキスト
   129:         super.paintComponent (g);
   130:       }
   131:     };
   132:     textArea.setOpaque (false);  //背景色を塗らせない
   133:     super.setOpaque (false);
   134: 
   135:     textArea.setForeground (foregroundColor);
   136:     textArea.setSelectionColor (new Color (LnF.LNF_RGB[7]));  //選択領域の背景の色
   137:     //textArea.setSelectedTextColor (new Color (LnF.LNF_RGB[14]));  //選択領域のテキストの色
   138: 
   139:     Font font = textArea.getFont ();
   140:     fontWidth = font.getSize () + 1 >> 1;
   141:     fontHeight = textArea.getFontMetrics (font).getHeight ();
   142:     Insets margin = textArea.getMargin ();
   143:     marginTop = margin.top;
   144:     marginLeft = margin.left;
   145: 
   146:     createGrid ();
   147: 
   148:     caret = new DefaultCaret () {
   149:       @Override protected void damage (Rectangle r) {
   150:         if (r != null) {
   151:           if (highlightCursorOn || underlineCursorOn) {
   152:             x = 0;
   153:             y = r.y;
   154:             width = textArea.getWidth ();
   155:             height = r.height;
   156:             textArea.repaint ();
   157:           } else {
   158:             super.damage (r);
   159:           }
   160:         }
   161:       }
   162:     };
   163:     caret.setBlinkRate (500);
   164:     textArea.setCaret (caret);
   165: 
   166:     getViewport ().setView (textArea);
   167:   }
   168: 
   169:   //グリッドを作る
   170:   //  フォントの右下にグリッドを表示する
   171:   //  フォント(6,13),マージン(3,3,3,3)のとき
   172:   //  ..t.....c.....c.....c.....c.....c.....c.....c...
   173:   //  ................................................
   174:   //  r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.
   175:   //  ..t.....c.....c.....c.....c.....c.....c.....c...
   176:   //  ................................................
   177:   //  ..t.....c.....c.....c.....c.....c.....c.....c...
   178:   //  ................................................
   179:   //  ..t.....c.....c.....c.....c.....c.....c.....c...
   180:   //  ................................................
   181:   //  ..t.....c.....c.....c.....c.....c.....c.....c...
   182:   //  ................................................
   183:   //  ..t.....c.....c.....c.....c.....c.....c.....c...
   184:   //  ................................................
   185:   private void createGrid () {
   186:     int backgroundRGB = backgroundColor.getRGB ();
   187:     int rowGridRGB = rowGridColor.getRGB ();
   188:     int columnGridRGB = columnGridColor.getRGB ();
   189:     int tabColumnGridRGB = tabColumnGridColor.getRGB ();
   190:     int imageWidth = fontWidth * 8;
   191:     BufferedImage image = new BufferedImage (imageWidth, fontHeight, BufferedImage.TYPE_INT_RGB);
   192:     int[] bitmap = ((DataBufferInt) image.getRaster ().getDataBuffer ()).getData ();
   193:     for (int y = 0; y < fontHeight; y++) {
   194:       for (int x = 0; x < imageWidth; x++) {
   195:         bitmap[(marginLeft + x) % imageWidth + imageWidth * ((marginTop + y) % fontHeight)] =
   196:           (((y ^ fontHeight) & 1) == 0 ? backgroundRGB :
   197:            y == fontHeight - 1 ? ((x % fontWidth ^ fontWidth) & 1) != 0 ? rowGridRGB : backgroundRGB :
   198:            x == imageWidth - 1 ? tabColumnGridRGB : x % fontWidth == fontWidth - 1 ? columnGridRGB : backgroundRGB);
   199:       }
   200:     }
   201:     gridPaint = new TexturePaint (image, new Rectangle (0, 0, imageWidth, fontHeight));
   202:   }
   203: 
   204:   //サイズ
   205:   @Override public void setMaximumSize (Dimension size) {
   206:     super.setMaximumSize (size);
   207:     getViewport ().setMaximumSize (size);
   208:   }
   209:   @Override public void setMinimumSize (Dimension size) {
   210:     super.setMinimumSize (size);
   211:     getViewport ().setMinimumSize (size);
   212:   }
   213:   @Override public void setPreferredSize (Dimension size) {
   214:     super.setPreferredSize (size);
   215:     getViewport ().setPreferredSize (size);
   216:   }
   217: 
   218:   //マージン
   219:   public Insets getMargin () {
   220:     return textArea.getMargin ();
   221:   }
   222:   public void setMargin (Insets m) {
   223:     textArea.setMargin (m);
   224:     int t = m.top;
   225:     int l = m.left;
   226:     if (marginTop != t || marginLeft != l) {
   227:       marginTop = t;
   228:       marginLeft = l;
   229:       createGrid ();
   230:     }
   231:   }
   232: 
   233:   //背景
   234:   @Override public boolean isOpaque () {
   235:     return opaqueOn;
   236:   }
   237:   @Override public void setOpaque (boolean opaque) {
   238:     opaqueOn = opaque;
   239:   }
   240: 
   241:   //色
   242:   @Override public Color getForeground () {
   243:     return foregroundColor;
   244:   }
   245:   @Override public void setForeground (Color color) {
   246:     foregroundColor = color;
   247:     if (textArea != null) {  //スーパークラスのコンストラクタからの呼び出しではない
   248:       textArea.setForeground (color);
   249:     }
   250:   }
   251:   @Override public Color getBackground () {
   252:     return backgroundColor;
   253:   }
   254:   @Override public void setBackground (Color color) {
   255:     backgroundColor = color;
   256:     if (textArea != null) {  //スーパークラスのコンストラクタからの呼び出しではない
   257:       createGrid ();
   258:     }
   259:   }
   260: 
   261:   //フォント
   262:   @Override public Font getFont () {
   263:     if (textArea != null) {  //スーパークラスのコンストラクタからの呼び出しではない
   264:       return textArea.getFont ();
   265:     }
   266:     return super.getFont ();
   267:   }
   268:   @Override public void setFont (Font font) {
   269:     if (textArea != null) {  //スーパークラスのコンストラクタからの呼び出しではない
   270:       textArea.setFont (font);
   271:       int w = font.getSize () + 1 >> 1;
   272:       int h = textArea.getFontMetrics (font).getHeight ();
   273:       if (fontWidth != w || fontHeight != h) {
   274:         fontWidth = w;
   275:         fontHeight = h;
   276:         createGrid ();
   277:       }
   278:     }
   279:   }
   280: 
   281:   //イベント
   282:   public void addCaretListener (CaretListener listener) {
   283:     textArea.addCaretListener (listener);
   284:   }
   285:   public void removeCaretListener (CaretListener listener) {
   286:     textArea.removeCaretListener (listener);
   287:   }
   288:   public void addDocumentListener (DocumentListener listener) {
   289:     textArea.getDocument ().addDocumentListener (listener);
   290:   }
   291:   public void removeDocumentListener (DocumentListener listener) {
   292:     textArea.getDocument ().removeDocumentListener (listener);
   293:   }
   294:   @Override public void addKeyListener (KeyListener listener) {
   295:     textArea.addKeyListener (listener);
   296:   }
   297:   @Override public void removeKeyListener (KeyListener listener) {
   298:     textArea.removeKeyListener (listener);
   299:   }
   300:   @Override public void addMouseListener (MouseListener listener) {
   301:     textArea.addMouseListener (listener);
   302:   }
   303:   @Override public void removeMouseListener (MouseListener listener) {
   304:     textArea.removeMouseListener (listener);
   305:   }
   306:   @Override public void addFocusListener (FocusListener listener) {
   307:     textArea.addFocusListener (listener);
   308:   }
   309:   @Override public void removeFocusListener (FocusListener listener) {
   310:     textArea.removeFocusListener (listener);
   311:   }
   312: 
   313:   //折り返し
   314:   public boolean getLineWrap () {
   315:     return textArea.getLineWrap ();
   316:   }
   317:   public void setLineWrap (boolean wrap) {
   318:     textArea.setLineWrap (wrap);
   319:   }
   320: 
   321:   //キャレット
   322:   public Color getCaretColor () {
   323:     return textArea.getCaretColor ();
   324:   }
   325:   public void setCaretColor (Color color) {
   326:     textArea.setCaretColor (color);
   327:   }
   328:   public int getCaretPosition () {
   329:     return textArea.getCaretPosition ();
   330:   }
   331:   public void setCaretPosition (int pos) {
   332:     textArea.setCaretPosition (pos);
   333:   }
   334:   public boolean isCaretVisible () {
   335:     return caret.isVisible ();
   336:   }
   337:   public void setCaretVisible (boolean visible) {
   338:     caret.setVisible (visible);
   339:   }
   340: 
   341:   //テキスト
   342:   public JTextArea getTextArea () {
   343:     return textArea;
   344:   }
   345:   public String getText () {
   346:     return textArea.getText ();
   347:   }
   348:   public void setText (String text) {
   349:     highlightAreaEnd = -1;
   350:     textArea.setText (text);
   351:   }
   352: 
   353:   //編集
   354:   public void append (String text) {
   355:     highlightAreaEnd = -1;
   356:     textArea.append (text);
   357:   }
   358:   public void insert (String text, int pos) {
   359:     highlightAreaEnd = -1;
   360:     textArea.insert (text, pos);
   361:   }
   362:   public void replaceRange (String text, int start, int end) {
   363:     highlightAreaEnd = -1;
   364:     textArea.replaceRange (text, start, end);
   365:   }
   366: 
   367:   //選択
   368:   public void selectAll () {
   369:     textArea.selectAll ();
   370:   }
   371:   public String getSelectedText () {
   372:     return textArea.getSelectedText ();
   373:   }
   374:   public int getSelectionStart () {
   375:     return textArea.getSelectionStart ();
   376:   }
   377:   public int getSelectionEnd () {
   378:     return textArea.getSelectionEnd ();
   379:   }
   380: 
   381:   //モード
   382:   public boolean isEditable () {
   383:     return textArea.isEditable ();
   384:   }
   385:   public void setEditable (boolean editable) {
   386:     textArea.setEditable (editable);
   387:   }
   388: 
   389:   //グリッド
   390:   public boolean isGridOn () {
   391:     return gridOn;
   392:   }
   393:   public void setGridOn (boolean on) {
   394:     gridOn = on;
   395:   }
   396:   public Color getRowGridColor () {
   397:     return rowGridColor;
   398:   }
   399:   public void setRowGridColor (Color color) {
   400:     rowGridColor = color;
   401:     createGrid ();
   402:   }
   403:   public Color getColumnGridColor () {
   404:     return columnGridColor;
   405:   }
   406:   public void setColumnGridColor (Color color) {
   407:     columnGridColor = color;
   408:     createGrid ();
   409:   }
   410:   public Color getTabColumnGridColor () {
   411:     return tabColumnGridColor;
   412:   }
   413:   public void setTabColumnGridColor (Color color) {
   414:     tabColumnGridColor = color;
   415:     createGrid ();
   416:   }
   417: 
   418:   //ハイライトエリア
   419:   public void setHighlightArea (int start, int end) {
   420:     highlightAreaStart = start;
   421:     highlightAreaEnd = end;
   422:   }
   423: 
   424:   //ハイライトカーソル
   425:   public boolean isHighlightCursorOn () {
   426:     return highlightCursorOn;
   427:   }
   428:   public void setHighlightCursorOn (boolean on) {
   429:     highlightCursorOn = on;
   430:   }
   431:   public Color getHighlightCursorColor () {
   432:     return highlightCursorColor;
   433:   }
   434:   public void setHighlightCursorColor (Color color) {
   435:     highlightCursorColor = color;
   436:   }
   437: 
   438:   //アンダーラインカーソル
   439:   public boolean isUnderlineCursorOn () {
   440:     return underlineCursorOn;
   441:   }
   442:   public void setUnderlineCursorOn (boolean on) {
   443:     underlineCursorOn = on;
   444:   }
   445:   public Color getUnderlineCursorColor () {
   446:     return underlineCursorColor;
   447:   }
   448:   public void setUnderlineCursorColor (Color color) {
   449:     underlineCursorColor = color;
   450:   }
   451: 
   452: }  //class ScrollTextArea
   453: 
   454: 
   455: