package edu.mills.feeney.thesis.aimerger;
import java.nio.file.*;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import javax.imageio.ImageIO;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
/**
* A user interface enabling users to merge two App Inventor projects.
*
* @author feeney.kate@gmail.com (Kate Feeney)
* <p/>
* modified by Arezu Esmaili (arezuesmaili1@gmail.com) - July 2015
*/
public class AIMerger extends JFrame {
private static AIMerger instance;
public Container myCP;
private JPanel mainProjectDisplayP;
private JPanel mergeButtonP;
private JPanel secondProjectDisplayP;
private JLabel instructMainProjectL;
private JLabel instructMainProjectNotesL;
private JLabel instructSecondProjectL;
private JLabel instructSecondProjectNotesL;
private JLabel mainProjectTitleL;
private JLabel secondProjectTitleL;
private JLabel mainProjectAssetsL;
private JLabel mainProjectScreensInstrucL;
private JLabel mainProjectScreensL;
private JLabel mainProjectAssetsInstrucL;
private JLabel secondProjectAssetsL;
private JLabel secondProjectScreensInstrucL;
private JLabel secondProjectScreensL;
private JLabel secondProjectAssetsInstrucL;
private JLabel picLabel;
private JLabel revisionNote;
private JButton mainProjectBrowseB;
private JButton secondProjectBrowseB;
private JButton mergeB;
private JTextField mainProjectTF;
private JTextField secondProjectTF;
private static final Font HEADER_TWO_FONT = new Font("Dialog", Font.PLAIN, 18);
private static final Font HEADER_THREE_FONT = new Font("Dialog", Font.ITALIC, 12);
private static final double HEIGHT_PERCENT_OF_SCREEN = 0.8;
private static final double WIDTH_PERCENT_OF_SCREEN = 0.8;
private static final int BORDER_THICKNESS = 50;
private Dimension projectDisplayPanelSize;
private Point projectDisplayPanelLocation;
private JScrollPane mainProjectScreensP;
private JScrollPane mainProjectAssetsP;
private JScrollPane secondProjectScreensP;
private JScrollPane secondProjectAssetsP;
private CheckBoxList mainProjectScreensCBL;
private CheckBoxList mainProjectAssetsCBL;
private AIProject mainProject;
private CheckBoxList secondProjectScreensCBL;
private CheckBoxList secondProjectAssetsCBL;
private AIProject secondProject;
private String mergeProjectPath;
private static final Color GREY = new Color(200, 200, 200);
private String oldName = null;
private String newName = null;
private String browserPath = null;
/**
* Returns a file path with no file extension.
*
* @param a file path.
* @return a file path with no file extension.
*/
public String pathNoExt(String path) {
return path.substring(0, path.lastIndexOf('.'));
}
/**
* Returns a file's extension given its path.
*
* @param a file path.
* @return a file's extension.
*/
public String getExt(String path) {
return path.substring(path.lastIndexOf('.'));
}
/**
* Returns the file's name given its path.
*
* @param a file path.
* @return a file's name.
*/
public String fileName(String path) {
return path.substring(path.lastIndexOf('/') + 1);
}
// Action listener for the main project's browse button.
private class MainProjectBrowseBActionListener implements ActionListener {
// When the main project's Browse button is pressed, a file-browsing window
// opens and the file selected by the user appears in the main project text box.
@Override
public void actionPerformed(ActionEvent event) {
String path = getFileToOpen();
if (path != null) {
mainProjectTF.setText(path);
browserPath = path;
// Create AIProject for the main project.
mainProject = new AIProject(mainProjectTF.getText());
}
// Display main project.
if (mainProject.isValid()) {
mainProjectDisplayP.setVisible(true);
updateMainProjectView();
// Set the lower center panel to visible if the lower right panel is already visible.
mergeButtonP.setVisible(secondProjectDisplayP.isVisible());
} else {
mainProjectDisplayP.setVisible(false);
}
}
}
// Action listener for the second project's browse button.
private class SecondProjectBrowseBActionListener implements ActionListener {
// When the second project's Browse button is pressed, a file-browsing window
// opens and the file selected by the user appears in the second project text box.
@Override
public void actionPerformed(ActionEvent event) {
String path = getFileToOpen();
if (path != null) {
secondProjectTF.setText(path);
browserPath = path;
secondProject = new AIProject(secondProjectTF.getText());
}
if (secondProject.isValid()) {
secondProjectDisplayP.setVisible(true);
updateSecondProjectView();
// Set the lower center panel to visible if the lower left panel is already visible.
mergeButtonP.setVisible(mainProjectDisplayP.isVisible());
} else {
secondProjectDisplayP.setVisible(false);
}
}
}
// Action listener for the merge button.
private class MergeBActionListener implements ActionListener {
// Action performed when button is clicked.
@Override
public void actionPerformed(ActionEvent event) {
if (alertToDuplicates() && containsScreen1()) {
// List to hold files to be included in the new project from the main
// project.
List<String> filesFromMainProject = new ArrayList<String>();
// The properties file from the main project is always included in the new project.
filesFromMainProject.add(mainProject.getPropertiesFilePath());
// List to hold files to be included in the new project from the second project.
List<String> filesFromSecondProject = new ArrayList<String>();
// Temporary list to hold the name of the screens from the main project that have been
// checked to be included in the new project.
List<String> mainProjectCheckedScreens = mainProjectScreensCBL.getChecked();
// Add checked screens to the list of files to include from the main project.
if (!mainProjectCheckedScreens.isEmpty()) {
for (AIScreen aiScreen : mainProject.getScreensList()) {
if (mainProjectCheckedScreens.contains(aiScreen.getName())) {
String path = aiScreen.getPath();
filesFromMainProject.add(path);
filesFromMainProject.add(path.substring(0, path.lastIndexOf(".scm")).concat(".bky"));
}
}
}
// Temporary list to hold the name of the assets from the main project that have been
// checked to be included in the new project.
List<String> mainProjectCheckedAssets = mainProjectAssetsCBL.getChecked();
// Add checked assets to the list of files to include from the main project.
if (!mainProjectCheckedAssets.isEmpty()) {
for (AIAsset aiAsset : mainProject.getAssetsList()) {
if (mainProjectCheckedAssets.contains(aiAsset.getName())) {
filesFromMainProject.add(aiAsset.getPath());
}
}
}
// Temporary list to hold the name of the screens from the second project that have been
// checked to be included in the new project.
List<String> secondProjectCheckedScreens = secondProjectScreensCBL.getChecked();
// Add checked screens to the list of files to include from the second project.
if (!secondProjectCheckedScreens.isEmpty()) {
for (AIScreen aiScreen : secondProject.getScreensList()) {
if (secondProjectCheckedScreens.contains(aiScreen.getName())) {
String path = aiScreen.getPath();
filesFromSecondProject.add(path);
filesFromSecondProject.add(path.substring(0, path.lastIndexOf(".scm")).concat(".bky"));
}
}
}
// Temporary list to hold the name of the assets from the second project that have been
// checked to be included in the new project.
List<String> secondProjectCheckedAssets = secondProjectAssetsCBL.getChecked();
// Add checked assets to the list of files to include from the second project.
if (!secondProjectCheckedAssets.isEmpty()) {
for (AIAsset aiAsset : secondProject.getAssetsList()) {
if (secondProjectCheckedAssets.contains(aiAsset.getName())) {
filesFromSecondProject.add(aiAsset.getPath());
}
}
}
try {
mergeProjectPath = getFileToSaveTo();
ZipOutputStream outZip = new ZipOutputStream(new FileOutputStream(mergeProjectPath));
byte[] buf = new byte[4096];
ZipInputStream mainZipInput = new ZipInputStream(new BufferedInputStream(
new FileInputStream(mainProject.getProjectPath())));
// Write files from main project to new project.
ZipEntry curEntry;
while ((curEntry = mainZipInput.getNextEntry()) != null) {
if (filesFromMainProject.contains(curEntry.getName())) {
outZip.putNextEntry(curEntry);
int len;
while ((len = mainZipInput.read(buf)) > 0) {
outZip.write(buf, 0, len);
}
outZip.closeEntry();
mainZipInput.closeEntry();
}
}
mainZipInput.close();
if (!filesFromSecondProject.isEmpty()) {
ZipInputStream secondZipInput = new ZipInputStream(new BufferedInputStream(
new FileInputStream(secondProject.getProjectPath())));
// Write files from second project to new project.
while ((curEntry = secondZipInput.getNextEntry()) != null) {
if (filesFromSecondProject.contains(curEntry.getName())) {
if (oldName != null && curEntry.toString().contains(oldName)) {
curEntry = new ZipEntry(newName + getExt(curEntry.toString()));
}
outZip.putNextEntry(curEntry);
int len;
while ((len = secondZipInput.read(buf)) > 0) {
if (getExt(curEntry.toString()).equals(".scm") && curEntry.toString().equals(
newName + getExt(curEntry.toString()))) {
String s = new String(buf, 0, len);
String s2 = new String(s.replace("\"$Name\":\"Screen1\"", "\"$Name\":\"" +
fileName(newName) + "\""));
String s3 = new String(s2.replace("\"Title\":\"Screen1\"", "\"Title\":\"" +
fileName(newName) + "\""));
buf = s3.getBytes();
len = s3.length();
}
outZip.write(buf, 0, len);
}
outZip.closeEntry();
secondZipInput.closeEntry();
}
}
secondZipInput.close();
}
outZip.close();
offerNewMerge();
} catch (IOException e1) {
JOptionPane.showMessageDialog(myCP, "Invalid file name.", "File Name Error",
JOptionPane.ERROR_MESSAGE);
actionPerformed(event);
}
}
}
}
/*
* Resets UI to what appears when the application starts. If a project's path
* is passed then this is loaded as the main project.
*/
private void resetAIMerger(String aiProjectPath) {
oldName = null;
newName = null;
browserPath = null;
mainProjectScreensCBL.clearChecked();
mainProjectAssetsCBL.clearChecked();
secondProjectScreensCBL.clearChecked();
secondProjectAssetsCBL.clearChecked();
mainProjectTF.setText(null);
secondProjectTF.setText(null);
mainProjectDisplayP.setVisible(false);
secondProjectDisplayP.setVisible(false);
mergeButtonP.setVisible(false);
if (aiProjectPath != null) {
mainProjectTF.setText(aiProjectPath);
mainProject = new AIProject(aiProjectPath);
mainProjectDisplayP.setVisible(true);
updateMainProjectView();
}
}
/*
* Informs the user their merge was successful and asks if they would like to
* merge another project.
*/
private void offerNewMerge() {
int response = JOptionPane.showOptionDialog(myCP, "Projects Successfully Merged. "
+ "Would you like to merge more projects?", "Projects Merged", JOptionPane.YES_NO_OPTION,
JOptionPane.INFORMATION_MESSAGE, null, null, JOptionPane.YES_OPTION);
switch (response) {
default:
// This should never happen
throw new IllegalArgumentException("not an option");
case JOptionPane.CLOSED_OPTION:
case JOptionPane.NO_OPTION:
closeApplication();
break;
case JOptionPane.YES_OPTION:
offerToMergeToNewProject();
break;
}
}
/*
* Asks user if they want to merge another project to the recently merged project.
*/
private void offerToMergeToNewProject() {
int response = JOptionPane.showOptionDialog(myCP, "Would you like one of the projects to merge"
+ " to be the project you just created?", "Merge More Projects", JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE, null, null, JOptionPane.YES_OPTION);
switch (response) {
default:
// This should never happen
throw new IllegalArgumentException("not an option");
case JOptionPane.CLOSED_OPTION:
closeApplication();
break;
case JOptionPane.NO_OPTION:
resetAIMerger(null);
break;
case JOptionPane.YES_OPTION:
resetAIMerger(mergeProjectPath);
break;
}
}
/*
* Confirms the user wants to exit AIMerger.
*/
private void closeApplication() {
int response = JOptionPane.showOptionDialog(myCP, "Exit AIMerger?", "Exit",
JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, null,
JOptionPane.YES_OPTION);
switch (response) {
default:
// This should never happen
throw new IllegalArgumentException("not an option");
case JOptionPane.CLOSED_OPTION:
case JOptionPane.CANCEL_OPTION:
offerNewMerge();
break;
case JOptionPane.OK_OPTION:
System.exit(0);
break;
}
}
private String getFileToSaveTo() {
// Get new project's file path.
JFileChooser mergeProjectFS = new JFileChooser();
mergeProjectFS.setDialogType(JFileChooser.SAVE_DIALOG);
String projectPath = null;
int validPath = mergeProjectFS.showSaveDialog(myCP);
if (validPath != JFileChooser.ERROR_OPTION || validPath != JFileChooser.CANCEL_OPTION) {
// Make sure the file is an aia file.
File projectFile = mergeProjectFS.getSelectedFile();
projectPath = projectFile.getPath();
if (!projectPath.toLowerCase().endsWith(".aia")) {
projectPath = projectPath.concat(".aia");
projectFile = new File(projectPath);
}
// Confirm that the user wants to overwrite an existing project, but do not allow
// overwriting one of the two projects being merged.
if (projectFile.exists()) {
if (projectFile.getPath().equalsIgnoreCase(mainProject.getProjectPath())
|| projectFile.getPath().equalsIgnoreCase(secondProject.getProjectPath())) {
JOptionPane.showMessageDialog(myCP, "You can not overwrite one of the two "
+ "projects being merged. Select anther file name.", "File Name Error",
JOptionPane.ERROR_MESSAGE);
return getFileToSaveTo();
}
validPath = JOptionPane.showOptionDialog(myCP, "The file name you selected already "
+ "exists. Would you like to replace it?", "Replace Existing File",
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, null,
JOptionPane.NO_OPTION);
if (validPath == JOptionPane.CLOSED_OPTION || validPath == JOptionPane.NO_OPTION) {
return getFileToSaveTo();
}
}
// The project's name is the name of the aia file.
String projectName = projectPath.substring(projectPath.lastIndexOf(File.separator) + 1,
projectPath.lastIndexOf(".aia"));
// The projects name must start with a letter and can only contain letters,
// numbers and underscores.
if (!Character.isLetter(projectName.charAt(0)) || !projectName.matches("^[a-zA-Z0-9_]*$")) {
JOptionPane.showMessageDialog(myCP, "Project names must start with a letter and "
+ "can contain only letters, numbers, and underscores", "File Name Error",
JOptionPane.ERROR_MESSAGE);
return getFileToSaveTo();
}
}
return projectPath;
}
/*
* Returns true is all screens and assets to be merged have unique names, else returns false.
*/
private boolean alertToDuplicates() {
for (String screen : mainProjectScreensCBL.getChecked()) {
//Asks to rename Screen1 of second project if both Screen1s are checked.
if (secondProjectScreensCBL.getChecked().contains("Screen1") && screen.equals("Screen1") &&
newName == null) {
JOptionPane.showMessageDialog(myCP, "Your project will not correctly merge with two " +
"Screen1s checked." + "\n" + "Rename your second project's Screen1 to continue.",
"Rename Screen1", JOptionPane.WARNING_MESSAGE);
String path = "";
String screenName = JOptionPane.showInputDialog(secondProjectDisplayP, "Rename",
"Enter new name");
// The screen's name must start with a letter and can only contain letters,
// numbers and underscores.
while (!Character.isLetter(screenName.charAt(0)) || !screenName.matches("^[a-zA-Z0-9_]*$") ||
secondProjectScreensCBL.getChecked().contains(screenName) ||
mainProjectScreensCBL.getChecked().contains(screenName) ||
screenName.toLowerCase().equals("screen1")) {
JOptionPane.showMessageDialog(myCP, "Screen names must start with a letter and "
+ "can contain only letters, numbers, " + "\n" + "and underscores, and cannot be "
+ "the same as another checked screen.", "File Name Error",
JOptionPane.ERROR_MESSAGE);
screenName = JOptionPane.showInputDialog(myCP, "Rename", "Enter new name");
}
for (AIScreen aiScreen : secondProject.getScreensList()) {
if (aiScreen.getName().equals("Screen1")) {
path += aiScreen.getPath();
}
}
String newPath = path.substring(0, path.lastIndexOf('/') + 1) + screenName;
oldName = pathNoExt(path);
newName = newPath;
}
if (secondProjectScreensCBL.getChecked().contains(screen) && screen != "Check All" &&
screen != "Screen1") {
JOptionPane.showMessageDialog(myCP, "You cannot select two screens with the "
+ "same name. Please uncheck one of the screens and remerge.", "Duplicate Error",
JOptionPane.ERROR_MESSAGE);
return false;
}
}
for (String asset : mainProjectAssetsCBL.getChecked()) {
if (secondProjectAssetsCBL.getChecked().contains(asset) && asset != "Check All") {
JOptionPane.showMessageDialog(myCP, "You cannot select two assets with the "
+ "same name. Please uncheck one of the assets and remerge.", "Duplicate Error",
JOptionPane.ERROR_MESSAGE);
return false;
}
}
return true;
}
/*
* Returns true if at least one Screen1 is checked, otherwise returns false.
*/
private boolean containsScreen1() {
for (String screen : mainProjectScreensCBL.getChecked()) {
if (screen.equals("Screen1")) {
return true;
}
}
for (String screen : secondProjectScreensCBL.getChecked()) {
if (screen.equals("Screen1")) {
return true;
}
}
JOptionPane.showMessageDialog(myCP, "Your project must have a Screen1 to merge properly. "
+ "Please check a Screen1 and remerge.", "No Screen1 Error",
JOptionPane.ERROR_MESSAGE);
return false;
}
/*
* Launches a file chooser and returns the file chosen. The file returned will be the new merged
* project.
*/
private String getFileToOpen() {
JFileChooser projectFC = new JFileChooser(browserPath);
int validPath = projectFC.showOpenDialog(myCP);
if (validPath == JFileChooser.ERROR_OPTION || validPath == JFileChooser.CANCEL_OPTION) {
return null;
} else {
return projectFC.getSelectedFile().toString();
}
}
/*an
* Updates the lower left part of the screen to display the main project.
*/
private static void updateMainProjectView() {
getInstance().mainProjectTitleL.setText("Main Project: "
+ getInstance().mainProject.getProjectName());
getInstance().mainProjectScreensCBL.setListData(getScreenCheckBoxes(getInstance().mainProject,
true));
getInstance().mainProjectScreensCBL.clearChecked();
getInstance().mainProjectScreensCBL.getChecked().add("Screen1");
getInstance().mainProjectScreensP.setViewportView(getInstance().mainProjectScreensCBL);
getInstance().mainProjectAssetsCBL.setListData(getAssetCheckBoxes(getInstance().mainProject));
getInstance().mainProjectAssetsCBL.clearChecked();
getInstance().mainProjectAssetsP.setViewportView(getInstance().mainProjectAssetsCBL);
getInstance().mainProjectDisplayP.repaint();
}
/*
* Updates the lower right part of the screen to display the second project.
*/
private static void updateSecondProjectView() {
getInstance().secondProjectTitleL.setText("Second Project: "
+ getInstance().secondProject.getProjectName());
getInstance().secondProjectScreensCBL.setListData(getScreenCheckBoxes(
getInstance().secondProject, false));
getInstance().secondProjectScreensCBL.clearChecked();
getInstance().secondProjectScreensP.setViewportView(getInstance().secondProjectScreensCBL);
getInstance().secondProjectAssetsCBL
.setListData(getAssetCheckBoxes(getInstance().secondProject));
getInstance().secondProjectAssetsCBL.clearChecked();
getInstance().secondProjectAssetsP.setViewportView(getInstance().secondProjectAssetsCBL);
getInstance().secondProjectDisplayP.repaint();
}
/*
* Creates an array of JCheckBoxes, a JCheckBox for each asset in the project.
*/
private static JCheckBox[] getAssetCheckBoxes(AIProject project) {
List<AIAsset> tempAssetsList = project.getAssetsList();
JCheckBox[] assetCheckBoxLabels = new JCheckBox[1 + tempAssetsList.size()];
assetCheckBoxLabels[0] = new JCheckBox("Check All");
for (int i = 1; i <= tempAssetsList.size(); i++) {
assetCheckBoxLabels[i] = new JCheckBox(tempAssetsList.get(i - 1).getName());
}
return assetCheckBoxLabels;
}
/*
* Creates an array of JCheckBoxes, a JCheckBox for each screen in the
* project. If the project is the main project then "Screen1" is a checked
* JCheckBox.
*/
private static JCheckBox[] getScreenCheckBoxes(AIProject project, boolean isMainProject) {
List<AIScreen> tempScreensList = project.getScreensList();
JCheckBox[] screenCheckBoxLabels = new JCheckBox[1 + tempScreensList.size()];
screenCheckBoxLabels[0] = new JCheckBox("Check All");
for (int i = 1; i <= tempScreensList.size(); i++) {
String tempScreenName = tempScreensList.get(i - 1).getName();
if (tempScreenName.equals("Screen1") && isMainProject) {
screenCheckBoxLabels[i] = new JCheckBox(tempScreenName, true);
} else {
screenCheckBoxLabels[i] = new JCheckBox(tempScreenName);
}
}
return screenCheckBoxLabels;
}
public AIMerger() {
super("App Inventor Merger v1.2 -- for App Inventor 2");
// Set the size and location of the application's window based on the screen size.
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setSize((int) (screenSize.width * WIDTH_PERCENT_OF_SCREEN),
(int) (screenSize.height * HEIGHT_PERCENT_OF_SCREEN));
setLocation((int) ((screenSize.width - screenSize.width * WIDTH_PERCENT_OF_SCREEN) / 2),
(int) ((screenSize.height - screenSize.height * HEIGHT_PERCENT_OF_SCREEN) / 2));
// Create a reference to the content pane of the JFrame.
myCP = this.getContentPane();
myCP.setLayout(null);
myCP.setBackground(GREY);
// Main project title and instructions.
instructMainProjectL = new JLabel("Browse for and load your Main Project.");
instructMainProjectL.setFont(HEADER_TWO_FONT);
instructMainProjectL.setSize(1000, 24);
instructMainProjectL.setLocation(20, 20);
myCP.add(instructMainProjectL);
// Main project note.
instructMainProjectNotesL = new JLabel("The main project's Screen1 will be the "
+ "merged project's Screen1");
instructMainProjectNotesL.setFont(HEADER_THREE_FONT);
instructMainProjectNotesL.setSize(1000, 20);
instructMainProjectNotesL.setLocation(20, instructMainProjectL.getLocation().y + 20);
myCP.add(instructMainProjectNotesL);
// Text field to enter path to main project.
mainProjectTF = new JTextField(300);
mainProjectTF.setBackground(Color.WHITE);
mainProjectTF.setEditable(true);
mainProjectTF.setSize(300, 30);
mainProjectTF.setLocation(20, instructMainProjectNotesL.getLocation().y + 20);
myCP.add(mainProjectTF);
// Browse button for main project.
mainProjectBrowseB = new JButton("Browse");
mainProjectBrowseB.setSize(100, 30);
mainProjectBrowseB.setLocation(mainProjectTF.getLocation().x + mainProjectTF.getSize().width
+ 10, instructMainProjectNotesL.getLocation().y + 20);
mainProjectBrowseB.addActionListener(new MainProjectBrowseBActionListener());
myCP.add(mainProjectBrowseB);
// Browse button for the second project
secondProjectBrowseB = new JButton("Browse");
secondProjectBrowseB.setSize(100, 30);
secondProjectBrowseB.setLocation((int) ((screenSize.width * WIDTH_PERCENT_OF_SCREEN)
- (mainProjectBrowseB.getSize().width + 20)), mainProjectTF.getLocation().y);
secondProjectBrowseB.addActionListener(new SecondProjectBrowseBActionListener());
myCP.add(secondProjectBrowseB);
// Second project title.
instructSecondProjectL = new JLabel("Browse for and load your Second Project.");
instructSecondProjectL.setFont(HEADER_TWO_FONT);
instructSecondProjectL.setSize(1000, 24);
instructSecondProjectL.setLocation(secondProjectBrowseB.getLocation().x - (20 +
mainProjectTF.getSize().width), 20);
myCP.add(instructSecondProjectL);
// Second project note.
instructSecondProjectNotesL = new JLabel("The second project's Screen1 can be renamed " +
"to be merged");
instructSecondProjectNotesL.setFont(HEADER_THREE_FONT);
instructSecondProjectNotesL.setSize(1000, 20);
instructSecondProjectNotesL.setLocation(instructSecondProjectL.getLocation().x,
instructSecondProjectL.getLocation().y + 20);
myCP.add(instructSecondProjectNotesL);
// Text field to enter the second project's path.
secondProjectTF = new JTextField(300);
secondProjectTF.setBackground(Color.WHITE);
secondProjectTF.setEditable(true);
secondProjectTF.setSize(300, 30);
secondProjectTF.setLocation(instructSecondProjectL.getLocation().x,
instructSecondProjectL.getLocation().y + 40);
myCP.add(secondProjectTF);
// AI Merger icon
JLabel picLabel = new JLabel(new ImageIcon(getClass().getResource("img/logoclear.png")));
picLabel.setSize(329, 145);
picLabel.setLocation((int) (screenSize.width * WIDTH_PERCENT_OF_SCREEN) / 2 -
(picLabel.getSize().width / 2), mainProjectBrowseB.getLocation().y + 20);
myCP.add(picLabel);
// Location and size of the panel that displays the projects.
projectDisplayPanelLocation = new Point(20, secondProjectTF.getLocation().y + BORDER_THICKNESS);
projectDisplayPanelSize = new Dimension((int) (screenSize.width * WIDTH_PERCENT_OF_SCREEN) -
BORDER_THICKNESS, (int) (screenSize.height * HEIGHT_PERCENT_OF_SCREEN) -
projectDisplayPanelLocation.y - BORDER_THICKNESS - 25);
// Panel that holds the components of the main project.
mainProjectDisplayP = new JPanel();
mainProjectDisplayP.setVisible(false);
mainProjectDisplayP.setSize(projectDisplayPanelSize.width / 3, projectDisplayPanelSize.height);
mainProjectDisplayP.setLocation(20, projectDisplayPanelLocation.y);
mainProjectDisplayP.setLayout(new BoxLayout(mainProjectDisplayP, BoxLayout.Y_AXIS));
mainProjectDisplayP.setBorder(new EmptyBorder(5, 20, 20, 20));
myCP.add(mainProjectDisplayP);
// Main project's name.
mainProjectTitleL = new JLabel();
mainProjectTitleL.setFont(HEADER_TWO_FONT);
mainProjectDisplayP.add(mainProjectTitleL);
mainProjectDisplayP.add(Box.createRigidArea(new Dimension(0, 10)));
// Label for the list of screens for the main project
mainProjectScreensL = new JLabel("Screens");
mainProjectScreensL.setFont(HEADER_TWO_FONT);
mainProjectDisplayP.add(mainProjectScreensL);
// Instructions for the list of screens for the main project.
mainProjectScreensInstrucL = new JLabel("Check screens to merge into new project");
mainProjectScreensInstrucL.setFont(HEADER_THREE_FONT);
mainProjectDisplayP.add(mainProjectScreensInstrucL);
// List of checkboxes for the screens from the main project
mainProjectScreensCBL = new CheckBoxList();
mainProjectScreensP = new JScrollPane();
mainProjectDisplayP.add(mainProjectScreensP);
mainProjectDisplayP.add(Box.createRigidArea(new Dimension(0, 10)));
// Label for the list of assets for the main project
mainProjectAssetsL = new JLabel("Assets");
mainProjectAssetsL.setFont(HEADER_TWO_FONT);
mainProjectDisplayP.add(mainProjectAssetsL);
// Instructions for the list of assets for the main project.
mainProjectAssetsInstrucL = new JLabel("Check assets to merge into new project");
mainProjectAssetsInstrucL.setFont(HEADER_THREE_FONT);
mainProjectDisplayP.add(mainProjectAssetsInstrucL);
// List of checkboxes for the assets from the main project.
mainProjectAssetsCBL = new CheckBoxList();
mainProjectAssetsP = new JScrollPane();
mainProjectDisplayP.add(mainProjectAssetsP);
// Panel that holds the merge button and arrows.
mergeButtonP = new JPanel();
mergeButtonP.setVisible(false);
mergeButtonP.setSize(projectDisplayPanelSize.width / 3, projectDisplayPanelSize.height);
mergeButtonP.setLocation(projectDisplayPanelSize.width / 3 + 20, projectDisplayPanelLocation.y);
mergeButtonP.setBackground(GREY);
mergeButtonP.setLayout(null);
myCP.add(mergeButtonP);
// Merge button.
mergeB = new JButton("Merge");
mergeB.setSize(150, 100);
mergeB.setLocation(mergeButtonP.getWidth() / 2 - mergeB.getWidth() / 2,
mergeButtonP.getHeight() / 2 - mergeB.getHeight() / 2);
mergeB.addActionListener(new MergeBActionListener());
mergeButtonP.add(mergeB);
// Merging arrows image.
picLabel = new JLabel(new ImageIcon(getClass().getResource("img/arrows3.png")));
picLabel.setSize(329, 250);
picLabel.setLocation((mergeButtonP.getWidth() - picLabel.getWidth()) / 2,
(mergeButtonP.getHeight() - picLabel.getHeight()) / 2);
mergeButtonP.add(picLabel);
// Panel that holds the components of the second project.
secondProjectDisplayP = new JPanel();
secondProjectDisplayP.setVisible(false);
secondProjectDisplayP.setSize(projectDisplayPanelSize.width / 3,
projectDisplayPanelSize.height);
secondProjectDisplayP.setLocation(2 * projectDisplayPanelSize.width / 3 + 20,
projectDisplayPanelLocation.y);
secondProjectDisplayP.setLayout(new BoxLayout(secondProjectDisplayP, BoxLayout.Y_AXIS));
secondProjectDisplayP.setBorder(new EmptyBorder(5, 20, 20, 20));
myCP.add(secondProjectDisplayP);
// Second project's name.
secondProjectTitleL = new JLabel();
secondProjectTitleL.setFont(HEADER_TWO_FONT);
secondProjectDisplayP.add(secondProjectTitleL);
secondProjectDisplayP.add(Box.createRigidArea(new Dimension(0, 10)));
// Label for the list of screens for the second project
secondProjectScreensL = new JLabel("Screens");
secondProjectScreensL.setFont(HEADER_TWO_FONT);
secondProjectDisplayP.add(secondProjectScreensL);
// Instructions for the list of screens for the second project.
secondProjectScreensInstrucL = new JLabel("Check screens to merge into new project");
secondProjectScreensInstrucL.setFont(HEADER_THREE_FONT);
secondProjectDisplayP.add(secondProjectScreensInstrucL);
// List of checkboxes for the screens from the second project.
secondProjectScreensCBL = new CheckBoxList();
secondProjectScreensP = new JScrollPane();
secondProjectDisplayP.add(secondProjectScreensP);
secondProjectDisplayP.add(Box.createRigidArea(new Dimension(0, 10)));
// Label for the list of assets for the second project
secondProjectAssetsL = new JLabel("Assets");
secondProjectAssetsL.setFont(HEADER_TWO_FONT);
secondProjectDisplayP.add(secondProjectAssetsL);
// Instructions for the list of assets for the second project.
secondProjectAssetsInstrucL = new JLabel("Check assets to merge into new project");
secondProjectAssetsInstrucL.setFont(HEADER_THREE_FONT);
secondProjectDisplayP.add(secondProjectAssetsInstrucL);
// List of checkboxes for the assets from the second project.
secondProjectAssetsCBL = new CheckBoxList();
secondProjectAssetsP = new JScrollPane();
secondProjectDisplayP.add(secondProjectAssetsP);
// Label for the revision note
revisionNote = new JLabel("Last revision by Arezu "
+ "Esmaili (July 2015)");
revisionNote.setFont(new Font("Serif", Font.PLAIN, 12));
revisionNote.setSize(500, 30);
revisionNote.setLocation((int) ((screenSize.width * WIDTH_PERCENT_OF_SCREEN)
- 235), (int) (screenSize.height * HEIGHT_PERCENT_OF_SCREEN) - 75);
myCP.add(revisionNote);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static AIMerger getInstance() {
return instance;
}
public static void main(String[] args) {
instance = new AIMerger();
}
}