package edu.mills.feeney.thesis.aimerger;
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)
*/
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 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 JButton mainProjectBrowseB;
private JButton mainProjectLoadB;
private JButton secondProjectBrowseB;
private JButton secondProjectLoadB;
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 ANDROID_GREEN = new Color(166, 199, 58);
// 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);
}
}
}
// 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);
}
}
}
// Action listener for the main project's load button.
private class MainProjectLoadBActionListener implements ActionListener {
// Action performed when button is clicked.
@Override
public void actionPerformed(ActionEvent event) {
// 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 load button.
private class SecondProjectLoadBActionListener implements ActionListener {
// Action performed when button is clicked.
@Override
public void actionPerformed(ActionEvent event) {
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()) {
// 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(".blk"));
}
}
}
// 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(".blk"));
}
}
}
// 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[1024];
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())) {
outZip.putNextEntry(curEntry);
int len;
while ((len = secondZipInput.read(buf)) > 0) {
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) {
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.WARNING_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 a zip file.
File projectFile = mergeProjectFS.getSelectedFile();
projectPath = projectFile.getPath();
if (!projectPath.toLowerCase().endsWith(".zip")) {
projectPath = projectPath.concat(".zip");
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.");
return getFileToSaveTo();
}
validPath = JOptionPane.showOptionDialog(myCP, "The file name you selected already "
+ "exists. Would you like to replace it?", "Replace", JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE, null, null, JOptionPane.NO_OPTION);
if (validPath == JOptionPane.CLOSED_OPTION || validPath == JOptionPane.NO_OPTION) {
return getFileToSaveTo();
}
}
// The projects name is the name of the zip file.
String projectName = projectPath.substring(projectPath.lastIndexOf(File.separator) + 1,
projectPath.lastIndexOf(".zip"));
// 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()) {
if (secondProjectScreensCBL.getChecked().contains(screen)) {
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)) {
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;
}
/*
* 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();
int validPath = projectFC.showOpenDialog(myCP);
if (validPath == JFileChooser.ERROR_OPTION || validPath == JFileChooser.CANCEL_OPTION) {
return null;
} else {
return projectFC.getSelectedFile().toString();
}
}
/*
* 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[tempAssetsList.size()];
for (int i = 0; i < tempAssetsList.size(); i++) {
assetCheckBoxLabels[i] = new JCheckBox(tempAssetsList.get(i).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[tempScreensList.size()];
for (int i = 0; i < tempScreensList.size(); i++) {
String tempScreenName = tempScreensList.get(i).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.1");
// 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(ANDROID_GREEN);
// 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, mainProjectTF.getLocation().y);
mainProjectBrowseB.addActionListener(new MainProjectBrowseBActionListener());
myCP.add(mainProjectBrowseB);
// Load button for main project
mainProjectLoadB = new JButton("Load");
mainProjectLoadB.setSize(100, 30);
mainProjectLoadB.setLocation(mainProjectBrowseB.getLocation().x
+ mainProjectBrowseB.getSize().width + 10, mainProjectTF.getLocation().y);
mainProjectLoadB.addActionListener(new MainProjectLoadBActionListener());
myCP.add(mainProjectLoadB);
// Second project title.
instructSecondProjectL = new JLabel("Browse for and load your Second Project.");
instructSecondProjectL.setFont(HEADER_TWO_FONT);
instructSecondProjectL.setSize(1000, 24);
instructSecondProjectL.setLocation(20, mainProjectTF.getLocation().y
+ mainProjectTF.getSize().height + 20);
myCP.add(instructSecondProjectL);
// 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(20, instructSecondProjectL.getLocation().y + 25);
myCP.add(secondProjectTF);
// Browse button for the second project
secondProjectBrowseB = new JButton("Browse");
secondProjectBrowseB.setSize(100, 30);
secondProjectBrowseB.setLocation(secondProjectTF.getLocation().x
+ secondProjectTF.getSize().width + 10, secondProjectTF.getLocation().y);
secondProjectBrowseB.addActionListener(new SecondProjectBrowseBActionListener());
myCP.add(secondProjectBrowseB);
// Load button for the second project.
secondProjectLoadB = new JButton("Load");
secondProjectLoadB.setSize(100, 30);
secondProjectLoadB.setLocation(
secondProjectBrowseB.getLocation().x + secondProjectBrowseB.getSize().width + 10,
secondProjectTF.getLocation().y);
secondProjectLoadB.addActionListener(new SecondProjectLoadBActionListener());
myCP.add(secondProjectLoadB);
// AI Merger icon
JLabel picLabel = new JLabel(new ImageIcon(getClass().getResource("img/logoclear.png")));
picLabel.setSize(400, 145);
picLabel.setLocation(575, 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);
// 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(ANDROID_GREEN);
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(332, 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);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static AIMerger getInstance() {
return instance;
}
public static void main(String[] args) {
instance = new AIMerger();
}
}