Introduction
As you all know, Java is the world's popular & powerful programming language which is platform independent. There are many editors developed using Java that we use like Eclipse, NetBeans, Intelli-J, JEdit, etc. These editors are the same as notepad but with added and developed extended features.
In this article, we will cover only the main functions such as open, save, close, run, etc. but will not see to create syntax highlighting in Java.
Well, it is easy to create syntax highlighting in Java using StyledDocument
. You can easily find the source code to create syntax highlighting in Java on the internet. We will also change the icon of tabs when document text is changed or saved, like Notepad++ provides. First, design your Notepad with Menus, Menu Items, ToolBars, etc.
Download the source code to view the complete code of advanced tabbed notepad and also created jar application. I am using JTextPane
object instead of JTextArea
. It is easy to perform New & Open operations. But to perform save or edit operations, first you need to get the current textpane
control from the JTabbedPane
after performing New or Open operations for use/perform other operations.
Let's Start
- Create the object of
JList
, JTabbedPane
, JSplit
. - Add
JList
object & JTabbedPane
object to JSplit
. - Add
JSplit
object to the Container
of the Frame
.
Using the Code
Getting JTextPane Control from Current Tab(Selected Tab) in JTabbedPane
- Get the selected index from the added
JTabbedPane
. - Cast the component of selected index of
JTabbedPane
with JScrollPane
(if JScrollPane
added). - Get the
Viewport
from the casted JScrollPane
. - Finally, cast the component of
index 0
with JTextPane
.
Here's the code to get added JTextPane
component from the added current Tab.
int sel = _tabbedPane.getSelectedIndex();
JScrollPane jscroll=(JScrollPane)_tabbedPane.getComponentAt(sel);
JViewport jview=jscroll.getViewport();
JTextPane textpane=(JTextPane)jview.getComponent(0);
It's four lines of code. But you can do this only in one/two lines like this:
int sel = _tabbedPane.getSelectedIndex();
JTextPane textPane =
(JTextPane)(((JScrollPane)_tabbedPane.getComponentAt(sel)).getViewport()).getComponent(0);
Now perform all actions on textPane
object.
Ok, now let's see the functions that are important. Add ActionListener
to that menu item according to the actions you want.
1) New
This function is easy to implement. Just adding new Tab & textPane
to JTabbedPane
. Here i am also checking if dark theme is enabled or not, if it is then set background color to components. Here's the code.
public void File_New_Action()
{
JTextPane _textPane=new JTextPane();
_textPane.setFont(new Font("Calibri",Font.PLAIN,14));
if(isDarkTheme){
_textPane.setBackground(new Color(10, 10, 20));
_textPane.setForeground(new Color(250, 250, 250));
}
JScrollPane jsp=new JScrollPane(_textPane);
_textPane.addKeyListener(new KeyTypedAction());
_textPane.getDocument().addUndoableEditListener(_undoManager);
_tabbedPane.addTab("Document "+count+" ",jsp);
_textPane.addCaretListener(new CaretAction());
_textPane.addMouseListener(new TextPane_MouseAction());
int index=_tabbedPane.getTabCount()-1;
_tabbedPane.setSelectedIndex(index);
_tabbedPane.setIconAt(index, new ImageIcon(this.getClass().getResource("resources/save.png")));
listModel.addElement("Document "+count+" ");
_list.setSelectedIndex(index);
setTitle("Tabbed Notepad in Java - [ Document "+count+" ]");
filenameLabel.setText("Document "+count);
count++;
}
2) Open
This function is the same as New function, only just needs to read files. Here, I am using FileDialog
by setting setMultipleMode
to true
for getting multiple files. Adding tab to tabbedpane as selected file name, changing title of frame, etc. Once object of textPane
is created, then add KeyListener
& UndoableListener
to it. Here's the code for open function.
public void File_Open_Action()
{
FileDialog fd = new FileDialog(new JFrame(), "Select File",FileDialog.LOAD);
fd.setMultipleMode(true);
fd.show();
if (fd.getFiles()!=null)
{
File[] files=fd.getFiles();
for(File item : files)
{
String filename = item.toString();
String file=filename;
if(filename.contains("\\")){
file = filename.substring(filename.lastIndexOf("\\") + 1);
}
else if(filename.contains("/")){
file = filename.substring(filename.lastIndexOf("/") + 1);
}
int count=_tabbedPane.getTabCount();
JTextPane _textPane=new JTextPane();
_textPane.setFont(new Font("Calibri",Font.PLAIN,14));
if (isDarkTheme) {
_textPane.setBackground(new Color(10, 10, 20));
_textPane.setForeground(new Color(250, 250, 250));
}
JScrollPane jsp=new JScrollPane(_textPane);
_textPane.addKeyListener(new KeyTypedAction());
_textPane.getDocument().addUndoableEditListener(_undoManager);
_textPane.addCaretListener(new CaretAction());
_textPane.addMouseListener(new TextPane_MouseAction());
_tabbedPane.addTab(file,jsp);
_tabbedPane.setSelectedIndex(count);
_tabbedPane.setIconAt(count, new ImageIcon(this.getClass().getResource("resources/save.png")));
listModel.addElement(file);
_list.setSelectedIndex(count);
setTitle("Tabbed Notepad in Java - [ "+file+" ]");
filenameLabel.setText(filename);
filesHoldListModel.addElement(filename);
BufferedReader d;
StringBuffer sb = new StringBuffer();
try
{
d = new BufferedReader(new FileReader(filename));
String line;
while((line=d.readLine())!=null)
sb.append(line + "\n");
_textPane.setText(sb.toString());
d.close();
}
catch(FileNotFoundException fe)
{
System.out.println("File not Found");
}
catch(IOException ioe){}
_textPane.requestFocus();
}
}
}
3) Save All
Well, it is easy to implement Save As & Save function. Here, we will see save all function. It is easy to implement this function. Use for
loop from 0
to the maximimum index of tabbedpane & set that to the selected index of tabbedpane. Get the filename from added filenameLabel
& save that file by getting the current textpane from current tab.
To identify the full filename path, I have created the list filesHoldList
and set the full filename path to the filenameLabel
which is added to the statusStrip
for saving the document. Here's the code.
public void File_SaveAll_Action()
{
if (_tabbedPane.getTabCount() > 0)
{
int maxindex = _tabbedPane.getTabCount() - 1;
for (int i = 0; i <= maxindex; i++)
{
_tabbedPane.setSelectedIndex(i);
String filename = filenameLabel.getText();
int sel = _tabbedPane.getSelectedIndex();
JTextPane textPane = (JTextPane) (((JScrollPane) _tabbedPane.getComponentAt(sel)).getViewport()).getComponent(0);
if (filename.contains("\\")||filename.contains("/"))
{
File f = new File(filename);
if (f.exists())
{
try
{
DataOutputStream d = new DataOutputStream(new FileOutputStream(filename));
String line = textPane.getText();
d.writeBytes(line);
d.close();
String tabtext = _tabbedPane.getTitleAt(sel);
if (tabtext.contains("*")) {
tabtext = tabtext.replace("*", "");
_tabbedPane.setTitleAt(sel, tabtext);
setTitle("Tabbed Notepad in Java - [ " + tabtext + " ]");
_tabbedPane.setIconAt(sel, new ImageIcon(this.getClass().getResource("resources/save.png")));
}
}
catch (Exception ex)
{
System.out.println("File not found");
}
textPane.requestFocus();
}
}
}
}
}
4) Close
This function is nothing but to remove selected tab from tabbedpane. But before removing tab, you must display a dialog box to save modified changes before close. If none of the documents is modified, then remove that tab, otherwise show dialog for save. Download the source code to view CloseAll
function code. Here's the code.
public void File_Close_Action()
{
if (_tabbedPane.getTabCount() > 0)
{
int sel = _tabbedPane.getSelectedIndex();
String tabtext = _tabbedPane.getTitleAt(sel);
if (tabtext.contains("*"))
{
int n = JOptionPane.showConfirmDialog(null, "Do you want to save " + tabtext + " before close ?",
"Save or Not", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
tabtext.replace("*", "");
if (n == 0)
{
String filename = filenameLabel.getText();
JTextPane textPane = (JTextPane) (((JScrollPane) _tabbedPane.getComponentAt(sel)).getViewport()).getComponent(0);
if (filename.contains("\\")||filename.contains("/"))
{
File_Save_Action();
_tabbedPane.remove(sel);
listModel.removeAllElements();
for (int i = 0; i < _tabbedPane.getTabCount(); i++)
{
String item = _tabbedPane.getTitleAt(i);
if (item.contains("*"))
{
item = item.replace("*", "").trim();
}
listModel.addElement(item);
}
_list.setSelectedIndex(_tabbedPane.getTabCount()-1);
rowLabel.setText("Row :");
colLabel.setText("Col :");
if(_tabbedPane.getTabCount()==0)
{
setTitle("Tabbed Notepad in Java");
filenameLabel.setText("");
rowLabel.setText("Row :");
colLabel.setText("Col :");
}
}
else if (filename.contains("Document "))
{
File_SaveAs_Action();
_tabbedPane.remove(sel);
listModel.removeAllElements();
for (int i = 0; i < _tabbedPane.getTabCount(); i++)
{
String item = _tabbedPane.getTitleAt(i);
if (item.contains("*"))
{
item = item.replace("*", "").trim();
}
listModel.addElement(item);
}
_list.setSelectedIndex(_tabbedPane.getTabCount() - 1);
rowLabel.setText("Row :");
colLabel.setText("Col :");
if (_tabbedPane.getTabCount() == 0)
{
setTitle("Tabbed Notepad in Java");
filenameLabel.setText("");
rowLabel.setText("Row :");
colLabel.setText("Col :");
}
}
}
if (n == 1)
{
_tabbedPane.remove(sel);
listModel.removeAllElements();
for (int i = 0; i < _tabbedPane.getTabCount(); i++)
{
String item = _tabbedPane.getTitleAt(i);
if (item.contains("*"))
{
item = item.replace("*", "").trim();
}
listModel.addElement(item);
}
_list.setSelectedIndex(_tabbedPane.getTabCount() - 1);
rowLabel.setText("Row :");
colLabel.setText("Col :");
if (_tabbedPane.getTabCount() == 0)
{
setTitle("Tabbed Notepad in Java");
filenameLabel.setText("");
rowLabel.setText("Row :");
colLabel.setText("Col :");
}
}
}
else
{
_tabbedPane.remove(sel);
listModel.removeAllElements();
for (int i = 0; i < _tabbedPane.getTabCount(); i++)
{
String item = _tabbedPane.getTitleAt(i);
if (item.contains("*"))
{
item = item.replace("*", "").trim();
}
listModel.addElement(item);
}
_list.setSelectedIndex(_tabbedPane.getTabCount() - 1);
rowLabel.setText("Row :");
colLabel.setText("Col :");
if (_tabbedPane.getTabCount() == 0)
{
setTitle("Tabbed Notepad in Java");
filenameLabel.setText("");
rowLabel.setText("Row :");
colLabel.setText("Col :");
}
}
}
else
{
setTitle("Tabbed Notepad in Java");
filenameLabel.setText("");
rowLabel.setText("Row :");
colLabel.setText("Col :");
}
}
It is easy to implement Edit operations in Java like Cut, Copy, Paste, Undo, Redo, etc.
Java does not provide the pre created font dialog. You have to create your own font dialog box. Here's my created font dialog box code that sets the font to the current textpane in current selected tab in tabbedpane.
Here's the code:
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.io.IOException;
import javax.swing.*;
import javax.swing.event.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public final class FontAction extends JDialog implements ListSelectionListener,ActionListener
{
String[] fontNames=GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
String[] fontStyles={
" Plain ",
" Bold ",
" Italic ",
" Plain+Bold ",
" Plain+Italic ",
" Bold+Italic ",
" Plain+Bold+Italic "
};
List lst=new List();
JList fontsList;
JList fontStyleList;
JList fontSizeList;
JPanel jp1,jp2;
DefaultListModel model;
JLabel displayLabel;
JButton ok,cancel;
JTextPane textPane;
static boolean isDarkTheme = false;
public FontAction(JTextPane tx)
{
textPane=tx;
Container cp=getContentPane();
isDarkTheme = getNodeTextContent("lookAndFeel").equals("GlobalDark");
fontsList=new JList(fontNames);
fontStyleList=new JList(fontStyles);
fontsList.setFont(new Font("Calibri",Font.PLAIN,14));
fontStyleList.setFont(new Font("Calibri",Font.PLAIN,14));
model=new DefaultListModel();
fontSizeList = new JList(model);
fontSizeList.setFont(new Font("Calibri", Font.PLAIN, 14));
for(int i=1;i<=160;i++)
{
model.addElement(" "+i+" ");
}
fontsList.setSelectedIndex(8);
fontStyleList.setSelectedIndex(0);
fontSizeList.setSelectedIndex(21);
fontsList.addListSelectionListener(this);
fontStyleList.addListSelectionListener(this);
fontSizeList.addListSelectionListener(this);
jp1=new JPanel();
jp2=new JPanel();
JPanel jp3=new JPanel();
jp3.add(new JScrollPane(fontsList));
JPanel jp4=new JPanel();
jp4.setLayout(new GridLayout(0,2));
jp4.add(new JScrollPane(fontStyleList));
jp4.add(new JScrollPane(fontSizeList));
jp1.add(jp3,BorderLayout.WEST);
jp1.add(jp4,BorderLayout.EAST);
displayLabel=new JLabel("Java Programming",JLabel.CENTER);
displayLabel.setFont(new Font("Arial",Font.PLAIN,21));
jp1.add(displayLabel);
ok=new JButton(" OK ");
cancel=new JButton(" Cancel ");
if (isDarkTheme) {
fontsList.setBackground(new Color(40, 40, 40));
fontStyleList.setBackground(new Color(40, 40, 40));
fontSizeList.setBackground(new Color(40, 40, 40));
displayLabel.setForeground(new Color(240, 240, 240));
}
ok.addActionListener(this);
cancel.addActionListener(this);
jp2.add(ok);
jp2.add(cancel);
cp.add(jp1,BorderLayout.CENTER);
cp.add(jp2,BorderLayout.SOUTH);
}
@Override
public void valueChanged(ListSelectionEvent evt)
{
String fontname=fontsList.getSelectedValue().toString();
String fontstyle=fontStyleList.getSelectedValue().toString().trim();
int fontsize=Integer.parseInt(fontSizeList.getSelectedValue().toString().trim());
switch(fontstyle)
{
case "Plain":
displayLabel.setFont(new Font(fontname, Font.PLAIN, fontsize));
break;
case "Bold":
displayLabel.setFont(new Font(fontname, Font.BOLD, fontsize));
break;
case "Italic":
displayLabel.setFont(new Font(fontname, Font.ITALIC, fontsize));
break;
case "Plain+Bold":
displayLabel.setFont(new Font(fontname, Font.PLAIN + Font.BOLD, fontsize));
break;
case "Plain+Italic":
displayLabel.setFont(new Font(fontname, Font.PLAIN + Font.ITALIC, fontsize));
break;
case "Bold+Italic":
displayLabel.setFont(new Font(fontname, Font.BOLD + Font.ITALIC, fontsize));
break;
case "Plain+Bold+Italic":
displayLabel.setFont(new Font(fontname, Font.PLAIN + Font.BOLD + Font.ITALIC, fontsize));
break;
}
}
@Override
public void actionPerformed(ActionEvent evt)
{
Object source=evt.getSource();
if(source==ok)
{
String fontname = fontsList.getSelectedValue().toString();
String fontstyle = fontStyleList.getSelectedValue().toString().trim();
int fontsize = Integer.parseInt(fontSizeList.getSelectedValue().toString().trim());
switch (fontstyle)
{
case "Plain":
textPane.setFont(new Font(fontname, Font.PLAIN, fontsize));
break;
case "Bold":
textPane.setFont(new Font(fontname, Font.BOLD, fontsize));
break;
case "Italic":
textPane.setFont(new Font(fontname, Font.ITALIC, fontsize));
break;
case "Plain+Bold":
textPane.setFont(new Font(fontname, Font.PLAIN + Font.BOLD, fontsize));
break;
case "Plain+Italic":
textPane.setFont(new Font(fontname, Font.PLAIN + Font.ITALIC, fontsize));
break;
case "Bold+Italic":
textPane.setFont(new Font(fontname, Font.BOLD + Font.ITALIC, fontsize));
break;
case "Plain+Bold+Italic":
textPane.setFont(new Font(fontname, Font.PLAIN + Font.BOLD + Font.ITALIC, fontsize));
break;
}
this.dispose();
}
else if(source==cancel)
{
this.dispose();
}
}
public String getNodeTextContent(String nodetag) {
String content = "";
try {
File fXmlFile = new File("files/viewsfile.xml");
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(fXmlFile);
doc.getDocumentElement().normalize();
NodeList nList = doc.getElementsByTagName("views");
for (int temp = 0; temp < nList.getLength(); temp++) {
Node nNode = nList.item(temp);
Element eElement = (Element) nNode;
content = eElement.getElementsByTagName(nodetag).item(0).getTextContent();
}
} catch (ParserConfigurationException | SAXException | IOException | DOMException e) {}
return content;
}
}
Selecting Tab by Clicking on Document Selector
As you know, every advanced editor has this function, where you can click on the document name, then that name of document tab is activated like Solution Explorer in Visual Studio. It is easy to implement this function by just creating a class that implements the interface ListSelectionListener
and define the valueChanged()
function. And add addListSelectionListener()
to the document selector list with implemented ListSelectionListener
class object. Get each tab title from tabbedpane, compare that value with selected item from the list & set that tab to selected. Here's the code:
class SelectTabFromListItem implements ListSelectionListener
{
public void valueChanged(ListSelectionEvent evt)
{
if(_list.getSelectedValue()!=null)
{
String list_item=_list.getSelectedValue().toString().trim();
if(_tabbedPane.getTabCount() >0)
{
int tabcount=_tabbedPane.getTabCount();
for(int i=0;i<tabcount;i++)
{
String title=_tabbedPane.getTitleAt(i).trim();
if (title.contains("*"))
{
title = title.replace("*", "").trim();
}
if(title.equals(list_item))
{
_tabbedPane.setSelectedIndex(i);
setTitle("Tabbed Notepad in Java -
[ "+_tabbedPane.getTitleAt(_tabbedPane.getSelectedIndex())+" ]");
}
}
}
}
}
}
Changing Title of frame, text of filenameLabel when Tab Changed
It is easy to implement this function by just creating a class that implement the interface ChangeListener
and define the stateChanged()
function. And add addChangeListener()
to the tabbedpane
with implemented ChangeListener
class object. Here's the code:
class TabChanged implements ChangeListener
{
public void stateChanged(ChangeEvent evt)
{
if(_tabbedPane.getTabCount()>0)
{
Object[] files=filesHoldListModel.toArray();
String tabtext=_tabbedPane.getTitleAt(_tabbedPane.getSelectedIndex()).trim();
if(tabtext.contains("*"))
{
tabtext=tabtext.replace("*", "");
}
for(Object filename : files)
{
String file=filename.toString().substring
(filename.toString().lastIndexOf("\\")+1);
if(file.equals(tabtext))
{
filenameLabel.setText(filename.toString());
setTitle("Tabbed Notepad in Java -
[ "+_tabbedPane.getTitleAt(_tabbedPane.getSelectedIndex())+" ]");
}
}
if(tabtext.contains("Document "))
{
filenameLabel.setText(tabtext);
setTitle("Tabbed Notepad in Java -
[ "+_tabbedPane.getTitleAt(_tabbedPane.getSelectedIndex())+" ]");
}
}
}
}
To set dark theme we will set setDefaultLookAndFeelDecorated()
to true
and will call method MetalLookAndFeel.setCurrentTheme()
. we will use javax.swing.plaf.ColorUIResource
class. See following link for many infomation about ColorUIResource.
http://docs.oracle.com/javase/7/docs/api/javax/swing/plaf/class-use/ColorUIResource.html
I have created a class JavaGlobalDarkTheme
in which i defined all these methods and calling them in setGlobalDarkLookAndFeel()
method.
public static void setGlobalDarkLookAndFeel()
{
MetalLookAndFeel.setCurrentTheme(new JavaGlobalDarkTheme().darkTheme);
try {
UIManager.setLookAndFeel(new MetalLookAndFeel());
} catch (Exception ev) {
}
JFrame.setDefaultLookAndFeelDecorated(true);
TabbedNotepad tb = new TabbedNotepad();
tb.setExtendedState(JFrame.MAXIMIZED_BOTH);
BufferedImage image = null;
try {
image = ImageIO.read(tb.getClass().getResource("resources/myicon.png"));
} catch (IOException e) {
}
tb.setIconImage(image);
tb.setSize(800, 600);
tb.setLocation(100, 50);
tb.setVisible(true);
}
}