Как стать автором
Обновить

Простой редактор текста при помощи java на swing

Уровень сложностиСредний
Время на прочтение8 мин
Количество просмотров1.2K

Всем привет. Для того чтобы писать код нужен удобный инструмент, кто-то пользуется IDE, кто-то редакторами текста Emacs, VIM. Зачастую, чтобы программисту было удобно, разработчику надо соблюсти много нюансов, тоесть в одном случае есть ускорение разработки, но решение тяжеловесное, в другом случае, разработчик, чтобы пользоваться редактором начинает писать код, чтобы писать код. Конечно это крайности, и проще поставить IDE/VSCode/etc... , но что если можно найти эту золотую середину!?

Неоднократно автор пытался делать текстовый редактор, но что-то было всё не то, пока не вспомнил, что есть старый добрый SWING, и java by design иногда круто и интересно.

В этой статье хочу показать как получилось минимальными усилиями сделать то, что на С или С++ потребует больших знаний без QT/GTK/etc...

Начнем!

Пользуюсь системой FreeBSD 14.2 Release

 % uname -rv
14.2-RELEASE FreeBSD 14.2-RELEASE releng/14.2-n269506-c8918d6c7412 GENERIC

Как поставить java (на всякий случай)

% pkg install openjdk17-17.0.13+11.1 openjdk17-jre-17.0.13+11.1
коммеентарий-строка#в /etc/fstab надо дополнить следующее и перезагрузиться или примонтировать чего не достаточно
fdesc	/dev/fd		fdescfs		rw	0	0
proc	/proc	procfs	rw	0	0

На всякий случай как скомпилировать программу

 % cat > main.java
class Main{
public static void main(String[] args){
	System.out.println("Hello World");
}
}
^C
 % javac main.java
 % java main.java
Hello World
 % 
 % cat > MANIFEST.MF
Manifest-Version: 1.0
Main-Class: Main
^C
 % jar -cvfm app.jar MANIFEST.MF *.class;
added manifest
adding: Main.class(in = 413) (out= 285)(deflated 30%)
 % java -jar ./app.jar
Hello World
 % 

Создадим скрипт на сборку

 % cat > compile.sh
javac Main.java; jar -cvfm app.jar MANIFEST.MF *.class; java -jar ./app.jar  
^C
 % cat compile.sh 
javac Main.java; jar -cvfm app.jar MANIFEST.MF *.class; java -jar ./app.jar
 % chmod +x compile.sh

Приступим к подходу шаблона над текстовым редактором

В ресурсах указано чем я пользовался java-swing-create-a-simple-text-editor.

давайте рассмотрим псевдокод того к чему надо приблизиться,

{
  Scroll nScrolls;
  MenubarWithItemsOrCli barTop,barBottom;
  Frame frame;
  TextArea text,numbers,cli;//stringsWithMetadata
  Process p;
  Prompt prompt;
  Command command;
}=> superClass
{
  load barTop(MenuAndItems);
  load Group(Scroll(numbers,text));
  load barBottom(Scroll(cli));
  connectAllLoadingToFrame(barTop,GroupWithOneScroll,barBottom);
  setCursorInValidPostoTextArea();
  setSettingsForFrame;
  Show();
}=> мы имеем строку куда вводятся символы и число строк
{}//Events
{}//ProcessIfNeedProvide
{
  call Editor;
}

давайте проинициализируем суперКласс

import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import java.io.*;
import java.awt.event.*;
import javax.swing.plaf.metal.*;
import javax.swing.text.*;
import java.io.IOException;

class Editor extends JFrame implements ActionListener {
    // Text component
    private JTextArea t;                                     //code
    private JTextArea t1;                                    //lineNumbers
    private JTextArea t2;                                    //mini cli
    // Frame
    private JFrame f;                                        //frame-likewWindow
    private StringBuilder LineN;                             //string
    private String prompter=" \n % ";                        //prompt
    private String commander;                                //command after prompt
    private int LineNN=1;                                    //number of lines on doc
    // Constructor
    public Editor() 
    {
        // Create a frame
    	JFrame.setDefaultLookAndFeelDecorated(true);
        f = new JFrame("editor");
        try {
            // Set metal look and feel
            UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
            // Set theme to ocean
            MetalLookAndFeel.setCurrentTheme(new OceanTheme());//set theme read in doc
        }
        catch (Exception e) {
        }
    	//config jtextarea for numberLines
        t1 = new JTextArea("1\n",2,1);//start jtextarea with a1,a2,a3 - text , row, collumns
    	t1.setEditable(false);//disable edit
    	//config jtextarea for textSpace
        t = new JTextArea("111",21,20);
    	t.setTabSize(1);//1 tab
    	t.setCaretPosition(t.getDocument().getLength());//set cursor position in doc
    	t.addKeyListener(new CounterLines());//connect listen keyboard
	

	// Create a menubar
        JMenuBar mb = new JMenuBar();//menubar top
        // Create amenu for menu
        JMenu m1 = new JMenu("File");//menu for file
        // Create menu items
        JMenuItem mi1 = new JMenuItem("New");//item for file
        JMenuItem mi2 = new JMenuItem("Open");//item for file
        JMenuItem mi3 = new JMenuItem("Save");//item for file
        JMenuItem mi9 = new JMenuItem("Print");//item for file
        // Add action listener
        mi1.addActionListener(this);//connect event
        mi2.addActionListener(this);
        mi3.addActionListener(this);
        mi9.addActionListener(this);
        m1.add(mi1);//connect to File
        m1.add(mi2);
        m1.add(mi3);
        m1.add(mi9);
        // Create amenu for menu
        JMenu m2 = new JMenu("Edit");//menu for Edit
        // Create menu items
        JMenuItem mi4 = new JMenuItem("cut");//item for edit
        JMenuItem mi5 = new JMenuItem("copy");//item for edit
        JMenuItem mi6 = new JMenuItem("paste");//item for edit
        // Add action listener
        mi4.addActionListener(this);//connect event
        mi5.addActionListener(this);
        mi6.addActionListener(this);
        m2.add(mi4);//connect to Edit
        m2.add(mi5);
        m2.add(mi6);
        JMenu mc = new JMenu("Close");//menu button for close instant

    	mc.addMouseListener(new ExitAction());//connect event

        mb.add(m1);//connect all menu to menubar-top
        mb.add(m2);
        mb.add(mc);

    	f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//exit on button close X

    	JMenuBar mbConsole = new JMenuBar();//create menubar-bottom for cli
    	t2 = new JTextArea(prompter,10,20);//create jtextarea with a1 a2 a3
    	t2.addKeyListener(new CmrPrompter());//connect keyevent
    	mbConsole.add(t2);//connect jtextarea-cli to bottom menubar

    	//collors for all jtextareas t=code t1=numbers t2=cli
    	Color c = new Color(0,0,0,255);
    	Color cf = new Color(200,200,200,255);
    	t.setBackground(c);
    	t.setForeground(cf);
    	Color c1 = new Color(50,50,50,255);
    	Color cf1 = new Color(200,200,200,255);
    	t1.setBackground(c1);
    	t1.setForeground(cf1);
    	Color c2 = new Color(60,60,60,255);
    	Color cf2 = new Color(200,200,200,255);
    	t2.setBackground(c2);
    	t2.setForeground(cf2);
	
        JPanel panel = new JPanel();//create panel
    	panel.setLayout(new BorderLayout());//create template for complians

	
    	panel.add(t1, BorderLayout.WEST);//left because numbers
        panel.add(t, BorderLayout.CENTER);//right extend to center 
    	
        f.add(mb,BorderLayout.NORTH);//menubar to up
    	f.add(new JScrollPane(mbConsole),BorderLayout.SOUTH);//set scroll to cli and set bottom position
	
    	f.add(new JScrollPane(panel));//connect all panel to frame
	
        f.setSize(1200, 900);//set size wxh
    	centreWindow(f);//set center position
        f.show();//show()

    	t.requestFocus();//fix some situation with current pos and doc
    	t.setCaretPosition(t.getText().length());//fix some situation with cursor
    }
  //используем performs
   // If a button is pressed
    public void actionPerformed(ActionEvent e)//actions for items
    {
        String s = e.getActionCommand();
 
        if (s.equals("cut")) {
            t.cut();
        }
        else if (s.equals("copy")) {
            t.copy();
        }
        else if (s.equals("paste")) {
            t.paste();
        }
        else if (s.equals("Save")) {
            // Create an object of JFileChooser class
            JFileChooser j = new JFileChooser("f:");
 
            // Invoke the showsSaveDialog function to show the save dialog
            int r = j.showSaveDialog(null);
 
            if (r == JFileChooser.APPROVE_OPTION) {
 
                // Set the label to the path of the selected directory
                File fi = new File(j.getSelectedFile().getAbsolutePath());
 
                try {
                    // Create a file writer
                    FileWriter wr = new FileWriter(fi, false);
 
                    // Create buffered writer to write
                    BufferedWriter w = new BufferedWriter(wr);
 
                    // Write
                    w.write(t.getText());
 
                    w.flush();
                    w.close();
                }
                catch (Exception evt) {
                    JOptionPane.showMessageDialog(f, evt.getMessage());
                }
            }
            // If the user cancelled the operation
            else
                JOptionPane.showMessageDialog(f, "the user cancelled the operation");
        }
        else if (s.equals("Print")) {
            try {
                // print the file
                t.print();
            }
            catch (Exception evt) {
                JOptionPane.showMessageDialog(f, evt.getMessage());
            }
        }
        else if (s.equals("Open")) {
            // Create an object of JFileChooser class
            JFileChooser j = new JFileChooser("f:");
 
            // Invoke the showsOpenDialog function to show the save dialog
            int r = j.showOpenDialog(null);
 
            // If the user selects a file
            if (r == JFileChooser.APPROVE_OPTION) {
                // Set the label to the path of the selected directory
                File fi = new File(j.getSelectedFile().getAbsolutePath());
 
                try {
                    // String
                    String s1 = "", sl = "";
 
                    // File reader
                    FileReader fr = new FileReader(fi);
 
                    // Buffered reader
                    BufferedReader br = new BufferedReader(fr);
 
                    // Initialize sl
                    sl = br.readLine();
 
                    // Take the input from the file
                    while ((s1 = br.readLine()) != null) {
                        sl = sl + "\n" + s1;
            			LineNN+=1;//set linenumbers while if line
            			t1.append(""+LineNN);
            			t1.append(" \n");
                    }
 
                    // Set the text
                    t.setText(sl);
                }
                catch (Exception evt) {
                    JOptionPane.showMessageDialog(f, evt.getMessage());
                }
            }
            // If the user cancelled the operation
            else
                JOptionPane.showMessageDialog(f, "the user cancelled the operation");
        }
        else if (s.equals("New")) {
            t.setText("");
        }
        else if (s.equals("Close")) {
        }
	repaint();//
	revalidate();
    }
  //выход по Close
      //exit after clicked on Close Menu!=item
    class ExitAction extends MouseInputAdapter {
    	public void mouseClicked(MouseEvent mouseEvent) {
	    System.exit(0);
	}
    }
  //подсчет строк и применение в буффере счетчика строк
      class CounterLines extends KeyAdapter {//listen keyboard event for enter if enter add \n +i to numberLines
      	public void keyPressed(KeyEvent e) {
    	    if(e.getKeyCode() == KeyEvent.VK_ENTER){
        		LineNN+=1;
        		t1.append(""+LineNN);
        		t1.append(" \n");
    	    }
    	}
    }
  //установим окно в центр
      public static void centreWindow(Window frame) {//set center Window
    	Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize();//get size
    	int x = (int) ((dimension.getWidth() - frame.getWidth()) / 2);//
    	int y = (int) ((dimension.getHeight() - frame.getHeight()) / 2);
    	frame.setLocation(x, y);
    }
  //поток где проводится команда
      class CmrPrompter extends KeyAdapter{//cmd\n - \n emulate enter
    	public void keyPressed(KeyEvent e){
    	    if(e.getKeyCode() == KeyEvent.VK_ENTER){
        		//Runtime runtime = Runtime.getRuntime();//default process

        		//get token from doc https://godbolt.org/z/9e5W3zf81
        		String s1 = t2.getText();
        		String parts[] = s1.split("\n");
        		int sz = parts.length;
        		String part1 = parts[sz-1];
        		String parts2[] = part1.split("% ");
        		int sz1 = parts2.length;
        		//System.out.println(parts2[sz1-1]);//commentary dont touching
    		commander=parts2[sz1-1];//set command token
		try {//try
    		    Process pR = Runtime.getRuntime().exec(commander);//call program
    		    int exitCode = pR.waitFor();
    		    PrintWriter cmdLineIn = new PrintWriter(pR.getOutputStream());
    		    BufferedReader cmdLineOut = new BufferedReader(new InputStreamReader(pR.getInputStream()));
    		    BufferedReader cmdLineErr = new BufferedReader(new InputStreamReader(pR.getErrorStream()));
    		    String s = null;
    		    //System.out.println(exitCode);
    		    if(exitCode==0){
        			while((s=cmdLineOut.readLine())!=null){//read line by line
        			    t2.append("\n");
        			    t2.append(s);
        			}
    		    }
    		    if(exitCode==1){
        			while((s=cmdLineErr.readLine())!=null){//read line by line
        			    t2.append("\n");
        			    t2.append(s);
        			}	
    		    }
      		    pR.destroy();
		}
		catch (Exception e1){
		    e1.printStackTrace();
		}
		commander= "";//free command string
		
		t2.append("\n");//set to the end//combo div-s
		t2.append(" % ");//set to the end//combo div-s
		
		t2.setText(t2.getText().substring(0,t2.getText().lastIndexOf("\r\n")));//set cursor after prompt 
		System.gc();//just
	    }
    }
  }
      // Main class
    public static void main(String[] args)
    {
        Editor e = new Editor();//jump to editor
	
    }
}
  

Этот код решает на стадии инициализации группирование обьектов, а именно - выставление двух елементов - буффер числа строк и текстовое поле пользователя в 1 скролл, выставление верхнего бара, применение в нижнем баре текстовое поле для использования в нём проведение пользовательских комманд.

В стадии использования как редактор он пока простенький, на первых подходах свою задачу выполняет.

если скомпилировать этот код и запустить

% ./compile.sh 
Note: Editor.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
added manifest
adding: Editor.class(in = 6437) (out= 3606)(deflated 43%)
adding: Editor$1.class(in = 767) (out= 521)(deflated 32%)
adding: Editor$CmrPrompter.class(in = 1966) (out= 1124)(deflated 42%)
adding: Editor$CounterLines.class(in = 1129) (out= 658)(deflated 41%)
adding: Editor$ExitAction.class(in = 489) (out= 347)(deflated 29%)
adding: Editor$SMenuListener.class(in = 626) (out= 386)(deflated 38%)
adding: Editor$TextAreaOutputStream.class(in = 951) (out= 566)(deflated 40%)
adding: Editor$TextAreaOutputStream$1.class(in = 781) (out= 461)(deflated 40%)
adding: SMenuListener.class(in = 606) (out= 356)(deflated 41%)
запустится редактор
запустится редактор
Скрытый текст
возможный пример
возможный пример
то как удалось реализовать всё вместе - почти финал
то как удалось реализовать всё вместе - почти финал

Важный момент автор заметил при проведении процесса выпадение без остановки

Exception in thread "AWT-EventQueue-0" java.lang.StringIndexOutOfBoundsException: begin 0, end -1, length 73

это происходит в строке, где я решаю очистить строку от последнего елемента чтобы после проведения комманды курсор был как в терминале на своём месте, я пока пропустил это предупреждение.

t2.setText(t2.getText().substring(0,t2.getText().lastIndexOf("\r\n"))); //set cursor after prompt 

Теперь мы знаем как сделать простой редактор. А вам приходилось делать текстовый редактор? Поделитесь об этом в комментарии

Ресурсы:

JTextArea - разбор java-swing-create-a-simple-text-editor

BorderLayout

Runtime - java-runtime-getruntime-method

swing-jtextfield

abstract, Class, subclasses

ProcessBuilder

bash-get-exit-code-of-command

Теги:
Хабы:
Всего голосов 9: ↑1 и ↓8-7
Комментарии19

Публикации

Работа

Java разработчик
205 вакансий

Ближайшие события