/*
* FrontlineSMS <http://www.frontlinesms.com>
* Copyright 2007, 2008 kiwanja
*
* This file is part of FrontlineSMS.
*
* FrontlineSMS is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* FrontlineSMS is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FrontlineSMS. If not, see <http://www.gnu.org/licenses/>.
*/
package net.frontlinesms.plugins.forms.ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragSource;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.border.TitledBorder;
import net.frontlinesms.plugins.forms.ui.components.PreviewComponent;
import net.frontlinesms.plugins.forms.ui.components.VisualForm;
import net.frontlinesms.ui.FrontlineUI;
import net.frontlinesms.ui.SimpleConstraints;
import net.frontlinesms.ui.SimpleLayout;
import net.frontlinesms.ui.i18n.InternationalisationUtils;
/**
* This class represents the Preview.
*
* @author Carlos Eduardo Genz
* <li> kadu(at)masabi(dot)com
*/
public class PreviewPanel extends JPanel implements MouseListener, KeyListener {
private static final long serialVersionUID = 1L;
private VisualForm form = null;
private static final int GAP = 5; // GAP between the components
private DragListener dragListener;
private DragSource dragSource;
private int dragY = - 1; // Coordinate to draw a line (helping user to drop component)
private int WIDTH = 235;
public PreviewPanel(DragListener dragListener, DragSource dragSource) {
setLayout(new SimpleLayout());
// We have to set the correct font for some languages
TitledBorder titledBorder = new TitledBorder(InternationalisationUtils.getI18NString(FormsThinletTabController.COMMON_PREVIEW));
titledBorder.setTitleFont(FrontlineUI.currentResourceBundle.getFont());
setBorder(titledBorder);
this.addMouseListener(this);
this.addKeyListener(this);
this.dragListener = dragListener;
this.dragSource = dragSource;
}
/**
* Show the supplied form on the preview.
*
* @param form
*/
public void showForm(VisualForm form) {
this.form = form;
deselectAll();
refresh();
FormsUiController.getInstance().showProperties();
}
/**
* Adds the supplied component to the preview finding the right position
* according to the supplied coordinates.
*
* @param comp The component to be added.
* @param x
* @param y
*/
public void addComponent(PreviewComponent comp, int x, int y) {
int index = getIndex(y);
if (index == -1 || index >= form.getComponents().size()) {
// We are adding to the end.
form.getComponents().add(comp);
} else {
// We are adding to a specified position.
form.getComponents().add(index, comp);
}
refresh();
}
/**
* This method finds the closest component to the supplied coordinate
* and returns its index.
*
* @param y
* @return
*/
private int getIndex(int y) {
int index = 0;
for (PreviewComponent c : form.getComponents()) {
Rectangle b = c.getDrawComponent().getBounds();
if (y > b.y && y < b.y + b.height + GAP) {
// We find the component
if (y < b.y + ( (b.height + GAP)/ 2) ) {
// User dropped the component in the upper part of this component.
index = form.getComponents().indexOf(c);
} else {
// User dropped the component in the lower part of this component.
index = form.getComponents().indexOf(c) + 1;
}
break;
}
}
// If we get index = 0 and the list is not empty, we verify if the user
// dropped the component in the end of the list, so we'll have to add
// the component to the end of the panel.
if (index == 0 && !form.getComponents().isEmpty()) {
Rectangle b = form.getComponents().get(form.getComponents().size() - 1).getDrawComponent().getBounds();
index = y > b.y + b.height ? form.getComponents().size() : index;
}
return index;
}
/**
* Returns the selected component.
*
* @return
*/
public PreviewComponent getSelectedItem() {
int index = getSelectedIndex();
return index == -1 ? null : form.getComponents().get(index);
}
/**
* Returns the selected component index.
*
* @return
*/
private int getSelectedIndex() {
for (int i = 0; i < form.getComponents().size(); i++) {
PreviewComponent c = form.getComponents().get(i);
if (c.isSelected()) {
return i;
}
}
return -1;
}
/**
* Moves the supplied component finding the right position
* according to the supplied coordinates.
*
* @param comp
* @param x
* @param y
*/
public void moveComponent(Component comp, int x, int y) {
int index = getIndex(y); // Index to be moved.
PreviewComponent toRemove = findComponent(comp);
int exIndex = form.getComponents().indexOf(toRemove); // Index before being moved.
if (index != exIndex) {
form.getComponents().remove(toRemove);
// If the user are moving down, we need to decrease the future index by one,
// because we have just removed the component.
if (index > exIndex) index--;
if (index != -1 && index <= form.getComponents().size()) {
form.getComponents().add(index, toRemove);
}
}
refresh();
}
/**
* Searches and returns the preview component that contains the supplied component.
*
* @param comp
* @return
*/
private PreviewComponent findComponent(Component comp) {
for (PreviewComponent c : form.getComponents()) {
if (c.getDrawComponent().equals(comp)) {
return c;
}
}
return null;
}
/**
* Removes the selected component from preview.
*
* @param c
*/
public PreviewComponent removeComponent(Component c) {
PreviewComponent cc = findComponent(c);
form.getComponents().remove(cc);
refresh();
return cc;
}
/**
* Refreshes the preview.
*/
public void refresh() {
this.removeAll();
if (form != null) {
int x = 9;
int y = 20;
for (PreviewComponent c : form.getComponents()) {
c.updateDrawComponent();
int width = this.getBounds().width == 0 ? WIDTH : this.getBounds().width;
this.add(c.getDrawComponent(), new SimpleConstraints(x, y, width - 18, c.getComponent().getHeight()));
addListenerRecursevely(c.getDrawComponent());
y+= c.getComponent().getHeight() + GAP;
}
}
this.validate();
this.repaint();
dragY = -1;
}
/**
* Adds mouse/key/drag listeners to the supplied component and its children.
*
* @param c
*/
private void addListenerRecursevely(Container c) {
c.addMouseListener(this);
c.addKeyListener(this);
dragSource.createDefaultDragGestureRecognizer(c, DnDConstants.ACTION_COPY, dragListener);
for (Component c1: c.getComponents()) {
if (c1 instanceof Container) {
addListenerRecursevely((Container) c1);
}
}
}
/**
* Remove the selection.
*/
private void deselectAll() {
if (form != null) {
for (PreviewComponent c : form.getComponents()) {
c.setSelected(false);
c.getDrawComponent().validate();
c.getDrawComponent().repaint();
}
}
}
public void paint(Graphics g) {
super.paint(g);
for (PreviewComponent c : form.getComponents()) {
if (c.isSelected()) {
// Draw selection
g.setColor(new Color(0x316ac5));
Rectangle b = c.getDrawComponent().getBounds();
g.drawRect(this.getBounds().x + 3, b.y - 1, this.getBounds().width - 8, b.height + 1);
break;
}
}
if (dragY != -1) {
// Draw line to help users to drop components.
g.setColor(Color.BLACK);
g.drawLine(this.getBounds().x + 3, dragY, this.getBounds().width - 3, dragY);
g.drawLine(this.getBounds().x + 3, dragY - 4, this.getBounds().x + 3, dragY + 4);
g.drawLine(this.getBounds().width - 3, dragY - 4, this.getBounds().width - 3, dragY + 4);
}
}
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
// If it's a left-click, we change the selection.
deselectAll();
Object source = e.getSource();
if (source instanceof PreviewPanel) {
// User clicked on the panel, so we check coordinates.
for (PreviewComponent c : form.getComponents()) {
Rectangle b = c.getDrawComponent().getBounds();
if (e.getY() > b.y && e.getY() <= b.y + b.height + GAP) {
c.setSelected(true);
break;
}
}
} else {
// User clicked in a component, so we just need to find it.
Component comp = (Component) source;
findComponent(getContainerParent(comp)).setSelected(true);
}
// We need to repaint to show the selection and show properties for this component.
this.validate();
this.repaint();
this.requestFocus();
FormsUiController.getInstance().showProperties();
}
}
/**
* Searches the preview panel in the component.
*
* @param obj
* @return
*/
private static Component getContainerParent(Component obj) {
if (obj instanceof JPanel || obj instanceof JScrollPane)
return obj;
return getContainerParent(obj.getParent());
}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void keyPressed(KeyEvent e) {}
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP) {
moveSelectionUp();
} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
moveSelecionDown();
} else if (e.getKeyCode() == KeyEvent.VK_DELETE) {
removeSelected();
}
}
/**
* Removes the selected component after the user pressing 'delete'.
*/
private void removeSelected() {
form.getComponents().remove(getSelectedItem());
refresh();
FormsUiController.getInstance().showProperties();
}
/**
* Moves the selection down.
*/
private void moveSelecionDown() {
int index = getSelectedIndex();
if (index != -1 && index != form.getComponents().size() - 1) {
deselectAll();
index++;
form.getComponents().get(index).setSelected(true);
this.validate();
this.repaint();
FormsUiController.getInstance().showProperties();
}
}
/**
* Moves the selection up.
*/
private void moveSelectionUp() {
int index = getSelectedIndex();
if (index > 0) {
deselectAll();
index--;
form.getComponents().get(index).setSelected(true);
this.validate();
this.repaint();
FormsUiController.getInstance().showProperties();
}
}
public void keyTyped(KeyEvent e) {}
public void setDragY(int dragY) {
this.dragY = dragY;
}
public VisualForm getForm() {
return form;
}
}