/*
* Copyright 2005-2010 Ignis Software Tools Ltd. All rights reserved.
*/
package jsystem.treeui.properties;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Vector;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.ListSelectionModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import jsystem.framework.DataType;
import jsystem.framework.FrameworkOptions;
import jsystem.framework.JSystemProperties;
import jsystem.guiMapping.JsystemMapping;
import jsystem.treeui.images.ImageCenter;
import jsystem.treeui.interfaces.JsystemPropertiesChangeListener;
import jsystem.treeui.properties.GUIFrameworkOptions.Group;
import jsystem.utils.StringUtils;
import jsystem.utils.SwingUtils;
/**
* The main dialog that preset and allow the user to edit the JSystem properties.
* @author Dror Voulichman
*/
public class JSystemPropertiesDialog implements ActionListener, MouseListener, KeyListener{
static JSystemPropertiesDialog jsystemPropertiesDialog = null;
private Group group;
private DataType dataType;
private String[] resurve, groupNames;
private String description, stringName, longDescription, example;
private Object defaultValue;
private boolean reloadRunnerRequire, exposeToDialog, saveDefaultValueToFile;
private static List<JsystemPropertiesChangeListener> changeListeners = new ArrayList<JsystemPropertiesChangeListener>();
JDialog myDialog = null;
ArrayList<Vector<JSystemProperty>> groupProperties = null;
Vector<JTable> tables = null;
JTabbedPane mainTabbedPane = null;
JScrollPane extentionPanel;
JTextArea textArea;
int currentTabIndex;
final static Color MAIN_COLOR = new Color(0xf6, 0xf6, 0xf6);
/**
*
* @return the same instance of the class, and in that way ensure that only one object can be instantiate from this class
* @throws Exception
*/
public static JSystemPropertiesDialog getInstance() throws Exception {
if (null == jsystemPropertiesDialog) {
jsystemPropertiesDialog = new JSystemPropertiesDialog();
}
return jsystemPropertiesDialog;
}
/**
* add a listener to call when relevant event occured.
* for example, a save action.
* @param listener
*/
public static void addListener(JsystemPropertiesChangeListener listener){
changeListeners.add(listener);
}
/**
* A listener for TAB change.
* We use this listener for the following purposes:
* 1 - o validate the last edited property - If the user entered an invalid
* value to the last edited property on the previous tab, and try to move to
* another tab, then we force him to go back to the last edited value.
* 2 - When a new tab is selected, and no row is selected, do not display any
* long description, until the user have pressed a specific line.
*/
ChangeListener changeListener = new ChangeListener() {
@Override
public void stateChanged(ChangeEvent changeEvent) {
JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent.getSource();
int newTabIndex = sourceTabbedPane.getSelectedIndex();
if ( (newTabIndex != currentTabIndex) && (stopLastCellEditing() == true) ) {
currentTabIndex = newTabIndex;
textArea.setText("");
}
}
};
/**
* initContents() method read all JSystem properties information from two Enums:
* FrameworkOptions and GUIFrameworkOptions, and fill up the properties into vectors.
*/
public void initContents() {
String value;
int numberOfGroups = Group.values().length;
groupNames = new String[numberOfGroups];
groupProperties = new ArrayList<Vector<JSystemProperty>>(numberOfGroups);
JSystemProperties pProperties = JSystemProperties.getInstance();
pProperties.rereadPropertiesFile();
// init group names (= Tab names)
for (Group currentGroup: Group.values()) {
groupNames[currentGroup.getIndex()] = new String(currentGroup.getValue());
groupProperties.add(currentGroup.getIndex(), new Vector<JSystemProperty>());
}
// Init all properties from the file
for (FrameworkOptions frameworkOptions: FrameworkOptions.values()) {
stringName = frameworkOptions.getString();
if (stringName == null) {
stringName = "";
}
description = frameworkOptions.getDescription();
if (description == null) {
description = "";
}
dataType = frameworkOptions.getDataType();
defaultValue = frameworkOptions.getDefaultValue();
reloadRunnerRequire = frameworkOptions.isReloadRunnerRequire();
resurve = frameworkOptions.getReserve();
saveDefaultValueToFile = frameworkOptions.isSaveDefaultValueToFile();
try {
GUIFrameworkOptions guiFrameworkOption = GUIFrameworkOptions.valueOf(frameworkOptions.name());
exposeToDialog = guiFrameworkOption.isExposeToDialog();
group = guiFrameworkOption.getGroup();
longDescription = guiFrameworkOption.getLongDescription();
if (longDescription == null) {
longDescription = "";
}
example = guiFrameworkOption.getExample();
if (example == null) {
example = "";
}
} catch (Exception e) {
group = Group.ADVANCED;
longDescription = "";
example = "";
exposeToDialog = false;
}
if (exposeToDialog) {
value = pProperties.getPreference(frameworkOptions);
if ( StringUtils.isEmpty(value) ){
if ( StringUtils.isEmpty(defaultValue.toString()) == false ) {
value = defaultValue.toString();
}
}
JSystemProperty property = new JSystemProperty(stringName, group, description, longDescription, example, dataType, defaultValue, reloadRunnerRequire, value, resurve, saveDefaultValueToFile);
groupProperties.get(group.getIndex()).add(property);
}
}
}
/**
*
* @param group - The name of the TAB to be construct.
* @param properties - A Vector holding all the properties to be preset in the current Tab.
* @return - A Panel with a JTable that holds all the properties.
*/
private JPanel constructTablePanel(int group, Vector<JSystemProperty> properties) {
// Construct the table
JSystemPropertiesTableModel model = new JSystemPropertiesTableModel(properties);
JTable table = new JTable(model);
table.getTableHeader().setReorderingAllowed(false);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.setBackground(MAIN_COLOR);
table.getColumn("Property Name").setHeaderRenderer(new JSystemOPropertiesTableHeaderRenderer());
table.getColumn("Property Name").setCellRenderer(new JSystemPropertiesTableRenderer());
table.getColumn("Description").setHeaderRenderer(new JSystemOPropertiesTableHeaderRenderer());
table.getColumn("Description").setCellRenderer(new JSystemPropertiesTableRenderer());
table.getColumn("Value").setHeaderRenderer(new JSystemOPropertiesTableHeaderRenderer());
table.getColumn("Value").setCellRenderer(new JSystemPropertiesTableRenderer());
table.setDefaultEditor(DataType.class, new JSystemPropertiesTableEditor(properties));
table.addMouseListener(this);
table.addKeyListener(this);
table.setRowHeight(24);
tables.add(table);
// Construct the JScrollPanel with the table inside it
JScrollPane scrollPane = new JScrollPane();
scrollPane.getViewport().setBackground(MAIN_COLOR);
scrollPane.setViewportView(table);
// Put everything together
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.add(scrollPane, BorderLayout.CENTER);
return panel;
}
/**
* @return - JTabbedpane that holds all the properties tables.
* @throws Exception
*/
private JTabbedPane constructTabbedPanel() throws Exception {
JTabbedPane tabbedPane = SwingUtils.getJTabbedPaneWithBgImage(
ImageCenter.getInstance().getImage(ImageCenter.ICON_TABBES_TOOLBAR_BG),
ImageCenter.getInstance().getImage(ImageCenter.ICON_TABBES_TOOLBAR_BG));
tabbedPane.setBackground(MAIN_COLOR);
tabbedPane.setName(JsystemMapping.getInstance().getJSystemPropertiesTabPanelName());
tabbedPane.setToolTipText(JsystemMapping.getInstance().getJSystemPropertiesTabPanelName());
tabbedPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10));
tabbedPane.addChangeListener(changeListener);
for (int group = 0; group < Group.values().length; group++) {
JPanel panel = constructTablePanel(group, groupProperties.get(group));
tabbedPane.addTab(groupNames[group], panel);
}
return (tabbedPane);
}
/**
* @param dialog - A JDialog to be resize
* @throws Exception
*/
private void dialogResize(JDialog dialog) throws Exception {
dialog.setModalityType(ModalityType.APPLICATION_MODAL);
dialog.setTitle(JsystemMapping.getInstance().getJSystemPropertiesMenuItem());
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
dialog.setLocation(screenSize.width / 4, screenSize.height / 5);
Dimension dialogSize = new Dimension((int) (screenSize.width / 1.5), (int) (screenSize.height / 1.5));
dialog.setPreferredSize(dialogSize);
}
/**
* @return - A panel with all the desired buttons
* @throws Exception
*/
private JPanel constructButtonsPanel() throws Exception {
String[] buttonNames = new String[] {
JsystemMapping.getInstance().getJSystemPropertiesSaveButtonName(),
JsystemMapping.getInstance().getJSystemPropertiesSystemDefaultButtonName(),
JsystemMapping.getInstance().getJSystemPropertiesCancleButtonName() };
JPanel buttonsPanel = new JPanel();
buttonsPanel.setBackground(MAIN_COLOR);
for (int index = 0; index < buttonNames.length; index++) {
JButton button = new JButton(buttonNames[index]);
button.addActionListener(this);
buttonsPanel.add(button);
}
return (buttonsPanel);
}
/**
* load up the dialog GUI
* @throws Exception
*/
public void dialogShow() throws Exception {
Image image = ImageCenter.getInstance().getAwtImage(ImageCenter.ICON_JSYSTEM);
JSplitPane innerSplitPane;
if (myDialog == null) {
textArea = new JTextArea();
textArea.setBackground(MAIN_COLOR);
textArea.setMargin(new Insets(10, 10, 10, 10));
textArea.setEditable(false);
textArea.setWrapStyleWord(true);
tables = new Vector<JTable>();
myDialog = new JDialog();
myDialog.setResizable(false);
myDialog.setIconImage(image);
initContents();
dialogResize(myDialog);
myDialog.setTitle(JsystemMapping.getInstance().getJSystemPropertiesMenuItem());
mainTabbedPane = constructTabbedPanel();
extentionPanel = SwingUtils.getJScrollPaneWithWaterMark(ImageCenter.getInstance().getAwtImage(
ImageCenter.ICON_TEST_TREE_BG), textArea);
extentionPanel.setBorder(BorderFactory.createLineBorder(Color.gray, 3));
innerSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, mainTabbedPane, extentionPanel);
innerSplitPane.setDividerLocation(250);
myDialog.add(innerSplitPane, BorderLayout.CENTER);
myDialog.add(constructButtonsPanel(), BorderLayout.SOUTH);
myDialog.pack();
}
myDialog.setVisible(true);
}
/**
* Close the dialog GUI
* @throws Exception
*/
private void dialogClose() throws Exception {
myDialog.setVisible(false);
myDialog.dispose();
tables.clear();
myDialog = null;
tables = null;
}
/**
* Save JSystem properties from the dialog into a file, and refresh the GUI
* @throws Exception
*/
private void actionSave() throws Exception {
Vector<String> listOfPropertiesThatRequierRestsrt = new Vector<String>();
JSystemProperties pJSysPropetires = JSystemProperties.getInstance();
JTable groupTable;
JSystemProperty currentProperty;
// Run over all the Tabs (Each tab contain a table with some properties
for (int group = 0; group < Group.values().length; group++) {
groupTable = tables.get(group);
// For each property in the current tab, save any property that contain a value (not null, and not empty) and that was changed
for (int propIndex = 0; propIndex < groupProperties.get(group).size(); propIndex++) {
currentProperty = groupProperties.get(group).get(propIndex);
String oldValue = currentProperty.getValue();
Object objectNewValue = groupTable.getValueAt(propIndex, JSystemPropertiesTableModel.VALUE_COLUMN);
String newValue = "";
if (objectNewValue != null) {
newValue = objectNewValue.toString();
}
if ( newValue.equalsIgnoreCase(oldValue) ) {
continue;
}
currentProperty.setDirty(true);
currentProperty.setValue(newValue);
if ( StringUtils.isEmpty(newValue) ) {
// The value of the current property was deleted, and need to remove this property from the file
pJSysPropetires.removePreference(currentProperty.getStringName());
} else {
pJSysPropetires.setPreference(currentProperty.getStringName(), newValue);
if (currentProperty.isReloadRunnerRequire()) {
listOfPropertiesThatRequierRestsrt.add(currentProperty.getStringName());
}
}
}
}
JSystemProperties.getInstance().flushCacheToFile();
// before closing the dialog, check if Runner should be reloaded, and pop up a compatible message to the user if needed.
if (listOfPropertiesThatRequierRestsrt.size() > 0) {
StringBuffer message = new StringBuffer("The following properties will only be effected after runner restart:");
for (int i = 0; i < listOfPropertiesThatRequierRestsrt.size(); i++) {
message.append("\n " + listOfPropertiesThatRequierRestsrt.get(i));
}
message.append("\n Would you like to restart runner now?");
int ans = JOptionPane.showConfirmDialog(
myDialog,
message.toString(),
JsystemMapping.getInstance().getJSystemPropertiesConfirmRestartDialogTitle(),
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
if (ans == JOptionPane.YES_OPTION) {
System.exit(6); // Reload the Runner
}
} else {
pJSysPropetires.rereadPropertiesFile();
}
dialogClose();
}
/**
* Restore System defaults.
* This operation close the dialog, delete jsystem.properties file, and reload the runner.
* During Runner loading, if jsystem.properties file can not be found, the application create this file with default values.
* @throws Exception
*/
private void actionSystemDefault() throws Exception {
int ans = JOptionPane.showConfirmDialog(
myDialog,
"In order to restore system defaults, runner must be restarted. \n " +
"continue with restart? \n " +
"(No will not change current properties)",
JsystemMapping.getInstance().getJSystemPropertiesRestoreDefaultsDiallogTitle(),
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
if (ans == JOptionPane.YES_OPTION) {
// restart the runner
JSystemProperties pPropetires = JSystemProperties.getInstance();
// 1) save needed data
Properties p = pPropetires.getPreferences();
// 2) close jsystem.properties
// 3) create a new Properties reference
pPropetires.clearAndResetJsystemPropertiesFile();
// 4) restore required data from properties
// TESTS_SOURCE_FOLDER
FrameworkOptions[] options = new FrameworkOptions[] {
FrameworkOptions.TESTS_CLASS_FOLDER,
FrameworkOptions.TESTS_SOURCE_FOLDER,
FrameworkOptions.USED_SUT_FILE,
FrameworkOptions.REPEAT_ENABLE};
for (FrameworkOptions option :options) {
System.out.println("Trying to save " + option.toString() + " into " + pPropetires.toString());
String value = p.getProperty(option.toString());
if (value!=null){
pPropetires.setPreference(option.toString(), value);
}
}
System.exit(6);
}
}
/**
* Close the dialog as a respond for pressing cancle button
* @throws Exception
*/
private void actionClose() throws Exception {
for (int counter = 0; counter < Group.values().length; counter++) {
groupNames[counter] = null;
groupProperties.get(counter).clear();
}
groupProperties.clear();
groupNames = null;
groupProperties = null;
dialogClose();
}
/**
* Button clicked event handler.
*/
public void actionPerformed(ActionEvent e) {
boolean dataIsvalid = stopLastCellEditing();
try {
if (e.getActionCommand() == JsystemMapping.getInstance().getJSystemPropertiesSaveButtonName()) {
if (dataIsvalid) {
actionSave();
fireChangeListeners();
}
} else if (e.getActionCommand() == JsystemMapping.getInstance().getJSystemPropertiesSystemDefaultButtonName()) {
actionSystemDefault();
} else if (e.getActionCommand() == JsystemMapping.getInstance().getJSystemPropertiesCancleButtonName()) {
actionClose();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
private void fireChangeListeners(){
for(JsystemPropertiesChangeListener listener : changeListeners){
listener.jsystemPropertiesChanged();
}
}
/**
* This method come to deal with a bug of JTable.
* stopCellEditing() is not called for the last edited cell, unless you press <Enter>
* We use stopLastCellEditing() method only in the context of user input validation.
* If the user entered an invalid value to one of the properties, and pressed save,
* in case his last input is invalid we force him to go back to the last edited value.
*/
private boolean stopLastCellEditing() {
boolean dataIsValid = true;
if (mainTabbedPane != null) {
// at the beginning of initialization tables has only one table, and from some reasons currentTabIndex > tables.size() and cause for exception
if (currentTabIndex < tables.size()) {
JTable lastEditedTable = tables.get(currentTabIndex);
int lastEditedcolumn = lastEditedTable.getSelectedColumn();
int lastEditedRow = lastEditedTable.getSelectedRow();
if ( (lastEditedcolumn == 2) && (lastEditedRow > -1) && (lastEditedTable != null)) {
dataIsValid = lastEditedTable.getCellEditor(lastEditedRow, lastEditedcolumn).stopCellEditing();
}
if (! dataIsValid) {
mainTabbedPane.setSelectedIndex(currentTabIndex);
}
}
}
return dataIsValid;
}
/**
* @param args
*/
public static void main(String[] args) {
try {
JSystemPropertiesDialog jsystemPropertiesDialog = JSystemPropertiesDialog.getInstance();
jsystemPropertiesDialog.dialogShow();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
/**
* Once the user pressed another line in the table, this method presents the
* compatible property long description, and default value in the extension panel
*/
@Override
public void mousePressed(MouseEvent e) {
updateDescription();
}
private void updateDescription(){
JTable lastEditedTable = tables.get(currentTabIndex);
int lastEditedRow = lastEditedTable.getSelectedRow();
String spaceLine = new String("\n");
if (lastEditedRow >= 0) {
JSystemProperty property = (groupProperties.get(currentTabIndex)).get(lastEditedRow);
String longDescription = property.getLongDescription();
String example = property.getExample();
String defaultValue = property.getDefaultVlaue();
StringBuffer extendedInfo = new StringBuffer();
if ( ! StringUtils.isEmpty(longDescription) ){
extendedInfo.append("Description:" + spaceLine + longDescription + spaceLine);
}
if ( ! StringUtils.isEmpty(example) ) {
extendedInfo.append(spaceLine + "Example:" + spaceLine + example + spaceLine);
}
if ( ! StringUtils.isEmpty(defaultValue) ) {
extendedInfo.append(spaceLine + "Default Value = " + defaultValue);
}
textArea.setText(extendedInfo.toString());
}
}
@Override
public void mouseReleased(MouseEvent e) {
updateDescription();
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == 38 || keyCode == 40){ // up or down arrows
updateDescription();
}
}
@Override
public void keyTyped(KeyEvent e) {
}
}