Tuesday, November 18, 2003

java swing hack for mdi interface windows menu

in multiple document interface (mdi) apps one expected feature is the windows menu which shows all the open windows and the user can goto any window by clicking on the menu sub item. the windows menu is thus totally dynamic. i have implemented the same functionality in java swing (the java gui api). but as you will see it is pretty dirty.!! it does the work but you guys think of some other ways to implement the same. basically we all come from diff languages so thats a problem.

there are two classes, Main and IntFrame. Main is the actual class which forms the frame and IntFrame is the internal frame which gets formed everytime you make a new (file>new) internal window in the app window.

the code is...


1.
package blog;

public class Main extends javax.swing.JFrame {

private javax.swing.JMenu file;
private javax.swing.JMenuItem newItem;
private javax.swing.JMenuItem closeItem;
private javax.swing.JDesktopPane desktop;
private javax.swing.JMenu windows;
private javax.swing.JMenuBar jMenuBar;
private javax.swing.JMenuItem exitItem;
private long winNo;
private java.util.ArrayList windowList;
private java.util.ArrayList menuItemList;

public Main() {
//initialise the variables for the window menu
winNo = 0;
windowList = new java.util.ArrayList();
menuItemList = new java.util.ArrayList();
//build the remaining components in the app, basically the menu
initComponents();
}

private void initComponents() {
desktop = new javax.swing.JDesktopPane();
jMenuBar = new javax.swing.JMenuBar();
file = new javax.swing.JMenu();
newItem = new javax.swing.JMenuItem();
closeItem = new javax.swing.JMenuItem();
exitItem = new javax.swing.JMenuItem();
windows = new javax.swing.JMenu();
setTitle("Window menu");
setName("frame");
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent evt) {
exitForm(evt);
}
});
getContentPane().add(desktop, java.awt.BorderLayout.CENTER);
file.setText("File");
newItem.setText("New");
newItem.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
newItemActionPerformed(evt);
}
});
file.add(newItem);
closeItem.setText("CloseAll");
closeItem.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
closeItemActionPerformed(evt);
}
});
file.add(closeItem);
exitItem.setText("Exit");
exitItem.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
exitItemActionPerformed(evt);
}
});
file.add(exitItem);
jMenuBar.add(file);
windows.setText("Window");
jMenuBar.add(windows);
setJMenuBar(jMenuBar);
pack();
}

private void closeItemActionPerformed(java.awt.event.ActionEvent evt) {
dispAllWindows();
}

private void newItemActionPerformed(java.awt.event.ActionEvent evt) {
blog.IntFrame intFrame = new blog.IntFrame(getThis());
javax.swing.JDesktopPane dtp = this.getdesktop();
dtp.add(intFrame);
intFrame.show();
}

private void exitItemActionPerformed(java.awt.event.ActionEvent evt) {
System.exit(0);
}

private void exitForm(java.awt.event.WindowEvent evt) {
System.exit(0);
}

private Main getThis(){
return this;
}

public javax.swing.JDesktopPane getdesktop(){
return this.desktop;
}

// create a dynamic window menu item
public void regWindow(javax.swing.JInternalFrame frm){

winNo++;
java.lang.String cls = frm.getClass().toString();
int n = cls.lastIndexOf('.');
java.lang.String title = new java.lang.String(cls.substring(n+1)+" #"+winNo);
frm.setTitle(title);
windowList.add(frm);
javax.swing.JMenuItem winItem = new javax.swing.JMenuItem();
menuItemList.add(winItem);
winItem.setText(title);
winItem.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
winItemActionPerformed(evt);
}
});
windows.add(winItem);
}

// dispose a particular frame
public void dispWindow(javax.swing.JInternalFrame frm){

int position = windowList.indexOf(frm);
windowList.remove(position);
javax.swing.JMenuItem temp = (javax.swing.JMenuItem)menuItemList.remove(position);
windows.remove(temp);
windowList.add(position,"");
menuItemList.add(position,"");
}

// close all the open frames
private void dispAllWindows(){

int x = windows.getMenuComponentCount();
x--;
for(;x>=0;x--){
javax.swing.JMenuItem tempMenuItem = windows.getItem(x);
int position = menuItemList.indexOf(tempMenuItem);
javax.swing.JInternalFrame tempIntFrm = (javax.swing.JInternalFrame)windowList.remove(position);
tempIntFrm.setVisible(false);
tempIntFrm.dispose();
windows.remove(tempMenuItem);
menuItemList.remove(position);
}
int size = windowList.size();
while(size>0){
windowList.remove(size-1);
menuItemList.remove(size-1);
size--;
}
winNo=0;
}


private void winItemActionPerformed(java.awt.event.ActionEvent evt){
javax.swing.JMenuItem tempMenuItem = (javax.swing.JMenuItem)evt.getSource();
java.lang.String tempWin = tempMenuItem.getText();
int x = tempWin.lastIndexOf('#');
java.lang.String tempNo = tempWin.substring(x+1);
int n;
try{
n = java.lang.Integer.parseInt(tempNo);
javax.swing.JInternalFrame tempFrm = (javax.swing.JInternalFrame)windowList.get(n-1);
tempFrm.toFront();
}catch(java.lang.NumberFormatException nfe){
System.out.println("in client:+nfe");
}
return;
}

// main method
public static void main(String args[]) {
new Main().show();
}

}

2.
package blog;
/*this class defines the internal frame opened by Main*/
public class IntFrame extends javax.swing.JInternalFrame {

private javax.swing.JButton close;
private blog.Main parentFrame;

public IntFrame(blog.Main parent) {
this.parentFrame = parent;
//call to the Main parent class method in constructor
parentFrame.regWindow(this);
initComponents();
}

private void initComponents() {
close = new javax.swing.JButton();
setResizable(true);
setPreferredSize(new java.awt.Dimension(200, 100));
close.setText("close");
close.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
closeActionPerformed(evt);
}
});
getContentPane().add(close, java.awt.BorderLayout.CENTER);
pack();
}

private void closeActionPerformed(java.awt.event.ActionEvent evt) {
//call to the Main parent class method before dispose

parentFrame.dispWindow(this);
setVisible(false);
this.dispose();
}

}


to run first save both files into a directory called blog. goto cd ..
in a terminal
$> javac blog/IntFrame.java blog/Main.java
to execute
$> java blog.Main

the important methods in Main are regWindow, dispWindow and dispAllWindows. whenever a new internal frame is opened it calls regWindow which basically adds a menu item to the windows menu. the same during dispWindow on close of the internal frame. dispAllWindows closes all windows. read the three methods. using the class name of IntFrame, a title is given and so is a counter added. this is also used in the new menu item pointer. two arraylists windowList and menuItemList are the heart of the system. windowList contains references to the IntFrame instances and menuItemList the references for the dynamically added menuItems. during dispose i just remove the corresponding elements in both the arraylists. but to maintain the positioning of the other windows placeholder Object instances are added at the positions of the remove elements. the winno is also used for positioning and setting the title.

one problem is that the maximum no of openable internal frames is the range of long. also when you click on a windows menu item i have not changed focus to the window but just bring it to the front. (amazingly could not find the setFocus() method )

by now yu must have noticed what a dirty solution it is !! any ideas??

No comments: