package org.korsakow.ide.ui.controller; import java.awt.Color; import java.awt.Dimension; import java.awt.Point; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DropTargetListener; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Collection; import javax.swing.JColorChooser; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JViewport; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.dsrg.soenea.domain.MapperException; import org.korsakow.domain.interf.IImage; import org.korsakow.domain.interf.IProject; import org.korsakow.domain.mapper.input.ProjectInputMapper; import org.korsakow.ide.Application; import org.korsakow.ide.dnd.DropTargetDispatcher; import org.korsakow.ide.resources.widget.DefaultTableWidgetPropertiesEditor; import org.korsakow.ide.resources.widget.WidgetModel; import org.korsakow.ide.resources.widget.WidgetPropertiesEditor; import org.korsakow.ide.resources.widget.propertyhandler.ColorPropertyHandler; import org.korsakow.ide.ui.ResourceEditor; import org.korsakow.ide.ui.controller.action.MoveBackwardsAction; import org.korsakow.ide.ui.controller.action.MoveForwardsAction; import org.korsakow.ide.ui.controller.action.MoveToBackAction; import org.korsakow.ide.ui.controller.action.MoveToFrontAction; import org.korsakow.ide.ui.controller.dnd.ImageDropTarget; import org.korsakow.ide.ui.controller.dnd.SnuDropTarget; import org.korsakow.ide.ui.controller.dnd.TextDropTarget; import org.korsakow.ide.ui.controller.dnd.VideoDropTarget; import org.korsakow.ide.ui.controller.dnd.WidgetTypeDropTarget; import org.korsakow.ide.ui.dnd.DataFlavors; import org.korsakow.ide.ui.interfacebuilder.InterfaceBuilderMainPanel; import org.korsakow.ide.ui.interfacebuilder.WidgetCanvas; import org.korsakow.ide.ui.interfacebuilder.WidgetCanvasModel; import org.korsakow.ide.ui.interfacebuilder.WidgetCanvasModelAdapter; import org.korsakow.ide.util.UIUtil; public class InterfaceResourceEditorController { private final ResourceEditor editor; private final WidgetCanvasController widgetCanvasController; private final InterfaceBuilderMainPanel interfaceBuilderPanel; private final WidgetInfoPanelUpdater widgetInfoPanelUpdater = new WidgetInfoPanelUpdater(); public InterfaceResourceEditorController(ResourceEditor editor, Long resourceId) { IProject project; try { project = ProjectInputMapper.find(); } catch (MapperException e) { Application.getInstance().showUnhandledErrorDialog(e); throw new IllegalArgumentException(e); } interfaceBuilderPanel = (InterfaceBuilderMainPanel)editor.getResourceView(); this.editor = editor; interfaceBuilderPanel.getInfoPanel().setEnabled(false); interfaceBuilderPanel.getInfoPanel().addXChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { onInfoXChange(); } }); interfaceBuilderPanel.getInfoPanel().addYChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { onInfoYChange(); } }); interfaceBuilderPanel.getInfoPanel().addWidthChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { onInfoWidthChange(); } }); interfaceBuilderPanel.getInfoPanel().addHeightChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { onInfoHeightChange(); } }); ChangeListener gridInfoListener = new ChangeListener() { public void stateChanged(ChangeEvent event) { getCanvasModel().setGridSize(interfaceBuilderPanel.getGridInfoPanel().getGridWidthValue(), interfaceBuilderPanel.getGridInfoPanel().getGridHeightValue()); } }; interfaceBuilderPanel.getGridInfoPanel().addWidthChangeListener(gridInfoListener); interfaceBuilderPanel.getGridInfoPanel().addHeightChangeListener(gridInfoListener); interfaceBuilderPanel.getArrangePanel().addMoveBackActionListener(new MoveToBackAction(interfaceBuilderPanel)); interfaceBuilderPanel.getArrangePanel().addMoveBackwardsActionListener(new MoveBackwardsAction(interfaceBuilderPanel)); interfaceBuilderPanel.getArrangePanel().addMoveForwardsActionListener(new MoveForwardsAction(interfaceBuilderPanel)); interfaceBuilderPanel.getArrangePanel().addMoveFrontActionListener(new MoveToFrontAction(interfaceBuilderPanel)); interfaceBuilderPanel.addShowBackgroundListener(new ActionListener() { public void actionPerformed(ActionEvent event) { getCanvas().setShowBackground(interfaceBuilderPanel.getShowBackground()); } }); widgetCanvasController = new WidgetCanvasController(interfaceBuilderPanel.getCanvas()); interfaceBuilderPanel.getCanvas().getSelectionModel().addListener(new WidgetCanvasModelAdapter() { @Override public void selectionChanged(Collection<WidgetModel> oldSelection, Collection<WidgetModel> newSelection) { onCanvasSelectionChange(oldSelection, newSelection); } }); interfaceBuilderPanel.addBackgroundImageActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { IImage backgroundImage = interfaceBuilderPanel.getBackgroundImage(); if (backgroundImage == null) { IProject project; try { project = ProjectInputMapper.find(); } catch (MapperException e) { Application.getInstance().showUnhandledErrorDialog(e); return; } backgroundImage = project.getBackgroundImage(); } getCanvas().setBackgroundImage(backgroundImage); } }); interfaceBuilderPanel.addClearBackgroundColorActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { IProject project; try { project = ProjectInputMapper.find(); } catch (MapperException e) { Application.getInstance().showUnhandledErrorDialog(e); return; } getCanvas().setBackgroundColor(project.getBackgroundColor()); interfaceBuilderPanel.setBackgroundColorModel(null); } }); interfaceBuilderPanel.addBackgroundColorActionListener(new BackgroundColorListener(interfaceBuilderPanel)); final DropTargetDispatcher dropDispatcher = new DropTargetDispatcher(); interfaceBuilderPanel.getCanvas().setDropTarget(dropDispatcher); dropDispatcher.addDropTargetListenerNoThrow(new DropTargetListener() { public void dragEnter(DropTargetDragEvent dtde) { InterfaceResourceEditorController.this.editor.toFront(); } public void dragExit(DropTargetEvent dte) { InterfaceResourceEditorController.this.editor.toFront(); } public void dragOver(DropTargetDragEvent dtde) { InterfaceResourceEditorController.this.editor.toFront(); } public void drop(DropTargetDropEvent dtde) { InterfaceResourceEditorController.this.editor.toFront(); } public void dropActionChanged(DropTargetDragEvent dtde) { InterfaceResourceEditorController.this.editor.toFront(); } }); dropDispatcher.addFlavorHandler(DataFlavors.TextFlavor, new TextDropTarget(interfaceBuilderPanel.getCanvas())); dropDispatcher.addFlavorHandler(DataFlavors.VideoFlavor, new VideoDropTarget(interfaceBuilderPanel.getCanvas())); dropDispatcher.addFlavorHandler(DataFlavors.ImageFlavor, new ImageDropTarget(interfaceBuilderPanel.getCanvas())); dropDispatcher.addFlavorHandler(DataFlavors.WidgetType, new WidgetTypeDropTarget(interfaceBuilderPanel)); dropDispatcher.addFlavorHandler(DataFlavors.SnuFlavor, new SnuDropTarget(interfaceBuilderPanel.getCanvas())); final int movieWidth = project.getMovieWidth(); final int movieHeight = project.getMovieHeight(); final int stageWidth = Math.max(800, movieWidth*2); final int stageHeight = Math.max(600, movieHeight*2); getCanvasModel().setMovieSize(movieWidth, movieHeight); getCanvasModel().setStageSize(stageWidth, stageHeight); interfaceBuilderPanel.revalidate(); interfaceBuilderPanel.repaint(); UIUtil.runUITaskLater(new Runnable() { public void run() { final JViewport viewport = interfaceBuilderPanel.getCanvasScroll().getViewport(); viewport.setViewPosition(new Point(Math.abs(viewport.getExtentSize().width - viewport.getViewSize().width)/2, Math.abs(viewport.getExtentSize().height - viewport.getViewSize().height)/2)); } }, 100); // ensures the work-area (stage) grows to fit the whole window // and that the whole area is always scrollable even if the window gets smaller interfaceBuilderPanel.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { int width = 800; int height = 600; width = Math.max(width, movieWidth*2); height = Math.max(height, movieHeight*2); width = Math.max(width, interfaceBuilderPanel.getCanvasScroll().getViewport().getVisibleRect().width); height = Math.max(height, interfaceBuilderPanel.getCanvasScroll().getViewport().getVisibleRect().height); getCanvasModel().setStageSize(width, height); getCanvas().revalidate(); } }); } public void initialState() { widgetCanvasController.initialState(); } public void onResourceSave() { } private WidgetCanvasModel getCanvasModel() { return interfaceBuilderPanel.getCanvas().getModel(); } private WidgetCanvas getCanvas() { return interfaceBuilderPanel.getCanvas(); } public void onClose() { editor.dispose(); } private void onCanvasSelectionChange(Collection<WidgetModel> oldSelection, Collection<WidgetModel> newSelection) { for (WidgetModel widget : oldSelection) { widget.disposeWidgetEditor(); widget.removePropertyChangeListener(widgetInfoPanelUpdater); } if (newSelection.size() == 1) { WidgetModel widget = newSelection.iterator().next(); interfaceBuilderPanel.getInfoPanel().setEnabled(true); interfaceBuilderPanel.getInfoPanel().setXValue(widget.getX() - getCanvasModel().getMovieOffsetX()); interfaceBuilderPanel.getInfoPanel().setYValue(widget.getY() - getCanvasModel().getMovieOffsetY()); interfaceBuilderPanel.getInfoPanel().setWidthValue(widget.getWidth()); interfaceBuilderPanel.getInfoPanel().setHeightValue(widget.getHeight()); widget.addPropertyChangeListener(widgetInfoPanelUpdater); WidgetPropertiesEditor widgetEditor = widget.getWidgetEditor(); JComponent editorComponent = widgetEditor.getWidgetPropertiesEditorComponent(getCanvasModel()); editorComponent.setPreferredSize(new Dimension(Short.MAX_VALUE, 200)); interfaceBuilderPanel.setPropertiesEditor(editorComponent); } else if (newSelection.size() > 1) { interfaceBuilderPanel.getInfoPanel().setEnabled(false); DefaultTableWidgetPropertiesEditor widgetEditor = new DefaultTableWidgetPropertiesEditor(newSelection); JComponent editorComponent = widgetEditor.getWidgetPropertiesEditorComponent(getCanvasModel()); editorComponent.setPreferredSize(new Dimension(Short.MAX_VALUE, 200)); interfaceBuilderPanel.setPropertiesEditor(editorComponent); } else { interfaceBuilderPanel.getInfoPanel().setEnabled(false); interfaceBuilderPanel.setPropertiesEditor(null); } } private void onInfoHeightChange() { int value = interfaceBuilderPanel.getInfoPanel().getHeightValue(); Collection<WidgetModel> selection = interfaceBuilderPanel.getCanvas().getSelectionModel().getSelectedWidgets(); for (WidgetModel widget : selection) { widget.setHeight(value); widget.getComponent().revalidate(); } } private void onInfoWidthChange() { int value = interfaceBuilderPanel.getInfoPanel().getWidthValue(); Collection<WidgetModel> selection = interfaceBuilderPanel.getCanvas().getSelectionModel().getSelectedWidgets(); for (WidgetModel widget : selection) { widget.setWidth(value); widget.getComponent().revalidate(); } } private void onInfoYChange() { int value = interfaceBuilderPanel.getInfoPanel().getYValue(); Collection<WidgetModel> selection = interfaceBuilderPanel.getCanvas().getSelectionModel().getSelectedWidgets(); for (WidgetModel widget : selection) { widget.setY(value + getCanvasModel().getMovieOffsetY()); widget.getComponent().revalidate(); } } private void onInfoXChange() { int value = interfaceBuilderPanel.getInfoPanel().getXValue(); Collection<WidgetModel> selection = interfaceBuilderPanel.getCanvas().getSelectionModel().getSelectedWidgets(); for (WidgetModel widget : selection) { widget.setX(value + getCanvasModel().getMovieOffsetX()); widget.getComponent().revalidate(); } } private class WidgetInfoPanelUpdater implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent event) { final WidgetModel widget = (WidgetModel)event.getSource(); Collection<WidgetModel> selected = interfaceBuilderPanel.getCanvas().getSelectionModel().getSelectedWidgets(); if (!(selected.size()==1 && selected.contains(widget))) return; // sadly, this runnable-later is due to the fact the in awt-component's property-change notify implementation, // the actual change is effected AFTER dispatching.... // hopefully this run-later wont have any side-effects... UIUtil.runUITaskLater(new Runnable() { public void run() { interfaceBuilderPanel.getInfoPanel().setXValue(widget.getX() - getCanvasModel().getMovieOffsetX()); interfaceBuilderPanel.getInfoPanel().setYValue(widget.getY() - getCanvasModel().getMovieOffsetY()); interfaceBuilderPanel.getInfoPanel().setWidthValue(widget.getWidth()); interfaceBuilderPanel.getInfoPanel().setHeightValue(widget.getHeight()); } }); } } private static class BackgroundColorListener implements ActionListener { private final InterfaceBuilderMainPanel interfaceBuilderPanel; public BackgroundColorListener(InterfaceBuilderMainPanel interfaceBuilderPanel) { this.interfaceBuilderPanel = interfaceBuilderPanel; } public void actionPerformed(ActionEvent event) { Color initialColor = interfaceBuilderPanel.getBackgroundColor(); if (initialColor == null) initialColor = Color.black; final JColorChooser chooser = ColorPropertyHandler.createColorChooser(initialColor); ActionListener okListener = new ActionListener() { public void actionPerformed(ActionEvent event2) { IProject project; try { project = ProjectInputMapper.find(); } catch (MapperException e) { Application.getInstance().showUnhandledErrorDialog(e); return; } Color backgroundColor = chooser.getColor(); if (backgroundColor == null) { backgroundColor = project.getBackgroundColor(); } interfaceBuilderPanel.getCanvas().setBackgroundColor(backgroundColor); interfaceBuilderPanel.setBackgroundColorModel(backgroundColor); } }; ActionListener cancelListener = new ActionListener() { public void actionPerformed(ActionEvent e) { } }; final JDialog dialog = JColorChooser.createDialog(interfaceBuilderPanel, "Color", true, chooser, okListener, cancelListener); dialog.setVisible(true); } } }