?? swingdesigner.java
字號:
/*
* BeanDesigner.java
*
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package dyno.swing.designer.beans;
import dyno.swing.designer.beans.actions.ActionCategory;
import dyno.swing.designer.beans.actions.AlignmentAction;
import dyno.swing.designer.beans.actions.EditingAction;
import dyno.swing.designer.beans.actions.SameSizeAction;
import dyno.swing.designer.beans.actions.ViewAction;
import dyno.swing.designer.beans.events.AddingMouseListener;
import dyno.swing.designer.beans.events.DesignerEditAdapter;
import dyno.swing.designer.beans.events.DesignerEditListener;
import dyno.swing.designer.beans.events.DesignerEvent;
import dyno.swing.designer.beans.events.DesignerStateListener;
import dyno.swing.designer.beans.events.EditingMouseListener;
import dyno.swing.designer.beans.events.HotKeyProxy;
import dyno.swing.designer.beans.location.Location;
import dyno.swing.designer.beans.models.AddingModel;
import dyno.swing.designer.beans.models.SelectionModel;
import dyno.swing.designer.beans.models.StateModel;
import dyno.swing.designer.beans.toolkit.BeanInfoToggleButton;
import dyno.swing.designer.beans.events.EditListenerTable;
import dyno.swing.designer.beans.events.StateListenerTable;
import dyno.swing.designer.beans.toolkit.ComponentPalette;
import dyno.swing.designer.treeview.ComponentTree;
import dyno.swing.designer.treeview.ComponentTreeEvent;
import dyno.swing.designer.treeview.ComponentTreeListener;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JInternalFrame;
import javax.swing.JPanel;
import javax.swing.LookAndFeel;
import javax.swing.RootPaneContainer;
import javax.swing.Scrollable;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.TreePath;
/**
* 設計界面組件。該組件是界面設計工具的核心,主要負責的是被設計界面的顯示,界面設計操作狀態的
* 顯示,編輯狀態的顯示等等。
*
* @author William Chen
*/
public class SwingDesigner extends JComponent implements Constants, TreeSelectionListener, ComponentTreeListener, Scrollable, InvocationHandler {
/**
* 當前正在設計的組件樹的根節點。目前只支持JPanel作為根節點。可以很容易的修改使其支持其他
* 容器。被設計的組件其name屬性都不為空,其值為該組件的變量名稱。
*/
private Component rootComponent;
/**
* 設計界面的左上角的坐標。目的是留出空間顯示設計界面的邊框。
*/
private int leftOffset;
private int topOffset;
/**
* 當前設計界面狀態是否是添加模式。界面設計工具的狀態可以分編輯狀態和添加狀態。編輯狀態的
* 設計界面可以選擇組件、刪除、剪切、粘帖、對其、屬性編輯等等操作。添加狀態是指在選擇組件
* 之后設計界面所處于的狀態。添加狀態和編輯狀態下鼠標處理方式相差很大,所以分為此兩種狀態
*/
private boolean addingMode;
/**
* 下面的變量都是非序列化成員,不記錄設計狀態,只作為設計時臨時狀態使用。
*/
//編輯狀態時鼠標處理器
private transient EditingMouseListener editingMouseListener;
//界面設計器的鍵盤熱鍵處理器,主要處理編輯如復制、剪切、刪除、粘帖等熱鍵
private transient HotKeyProxy keyListener;
//添加組件狀態下的鼠標處理器
private transient AddingMouseListener addingMouseListener;
//編輯狀態下的model,存儲編輯狀態下的臨時狀態,比如拖拽區域、鼠標熱點等等
private transient StateModel stateModel;
//添加狀態下的model,存儲添加狀態下的臨時狀態,比如要添加的組件、當前鼠標位置等等
private transient AddingModel addingModel;
//當前負責額外渲染的painter,主要目的用來渲染添加組件的位置提示,它通常由外部類設置,在
//設計器渲染時被調用渲染這些位置提示。
private transient Painter painter;
private boolean invalidated = true;
//存儲被選擇組件和剪切板的model
private transient SelectionModel selectionModel;
private EditListenerTable edit;
private StateListenerTable state;
private VariableSpace space;
private Border outline_border;
private Action[] designer_actions;
private Border inner_border;
private ComponentPalette palette;
private LookAndFeel designLnf;
private LookAndFeel ideLnf;
public void setPalette(ComponentPalette palette) {
this.palette = palette;
}
public VariableSpace getVariableSpace() {
return space;
}
public Dimension getPreferredSize() {
Dimension size = rootComponent.getSize();
Insets insets = getOutlineInsets();
size.width = 2 * leftOffset + size.width + insets.left + insets.right;
size.height = 2 * topOffset + size.height + insets.top + insets.bottom;
return size;
}
/** Creates a new instance of BeanDesigner */
public SwingDesigner() {
setBackground(Color.white);
setDoubleBuffered(true);
space = new VariableSpace();
designLnf = UIManager.getLookAndFeel();
ideLnf = UIManager.getLookAndFeel();
//初始化
leftOffset = 20;
topOffset = 20;
//為了處理鍵盤事件,需要SwingDesigner能夠獲取焦點
setFocusable(true);
edit = new EditListenerTable();
state = new StateListenerTable();
//初始化
selectionModel = new SelectionModel(this);
stateModel = new StateModel(this);
//初始化界面設計工具的UI實例
updateUI();
//初始化缺省的設計組件
initRootComponent();
//初始化事件處理器
initializeListener();
}
public EditListenerTable getEditListenerTable() {
return edit;
}
public StateListenerTable getStateListenerTable() {
return state;
}
private void initActionListener(Action[] actions) {
for (Action action : actions) {
if (action != null) {
if (action instanceof DesignerEditListener) {
addDesignerEditListener((DesignerEditListener) action);
}
if (action instanceof DesignerStateListener) {
addDesignerStateListener((DesignerStateListener) action);
}
if (action instanceof ActionCategory) {
Action[] sub = ((ActionCategory) action).getSubActions();
initActionListener(sub);
}
}
}
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Container container = (Container) rootComponent;
Util.layoutContainer(container);
repaint();
return null;
}
/**
* 初始化事件處理器,初始狀態為編輯狀態,所以下初始化并添加編輯類的事件處理器
*/
private void initializeListener() {
//點擊
editingMouseListener = new EditingMouseListener(this);
//熱鍵
keyListener = new HotKeyProxy(this);
addMouseMotionListener(editingMouseListener);
addMouseListener(editingMouseListener);
addKeyListener(keyListener);
addInvocationHandler(this);
addDesignerEditListener(new DesignerEditAdapter() {
public void componentMoved(DesignerEvent evt) {
setInvalidated(true);
repaint();
}
public void componentResized(DesignerEvent evt) {
setInvalidated(true);
repaint();
}
@Override
public void componentCut(DesignerEvent evt) {
setInvalidated(true);
}
@Override
public void componentDeleted(DesignerEvent evt) {
setInvalidated(true);
}
@Override
public void componentPasted(DesignerEvent evt) {
setInvalidated(true);
}
public void componentAdded(DesignerEvent evt) {
setInvalidated(true);
}
public void componentEdited(DesignerEvent evt) {
setInvalidated(true);
Component comp = evt.getAffectedComponents().get(0);
Container par = comp.getParent();
if (par != null) {
LayoutManager layout = par.getLayout();
if (layout != null) {
Util.layoutContainer(par);
}
}
}
});
}
public void addInvocationHandler(InvocationHandler h) {
ClassLoader loader = getClass().getClassLoader();
Class[] interfaces = new Class[]{DesignerEditListener.class, DesignerStateListener.class};
Object proxyListener = Proxy.newProxyInstance(loader, interfaces, h);
addDesignerEditListener((DesignerEditListener) proxyListener);
addDesignerStateListener((DesignerStateListener) proxyListener);
}
/**
* 開始添加狀態,在用戶選擇面板上的組件時啟動
*
* @param beanInfo 當前選中組件的BeanInfo對象
*/
public void startAddingState(BeanInfo beanInfo) {
//刪除編輯狀態的事件處理器
removeMouseListener(editingMouseListener);
removeMouseMotionListener(editingMouseListener);
removeKeyListener(keyListener);
//根據所選擇的組件的BeanInfo生成相應的AddingModel
//AddingModel和StateModel不一樣,適合當前選擇的組件相關的
addingModel = new AddingModel(this, beanInfo);
addingMouseListener = new AddingMouseListener(this, beanInfo);
//添加事件
addMouseListener(addingMouseListener);
addMouseMotionListener(addingMouseListener);
//設置當前模式位添加模式
setAddingMode(true);
//觸發狀態添加模式事件
fireStartDesigning();
repaint();
}
/**
* 停止添加模式、返回編輯模式
*/
public void stopAddingState() {
//刪除添加模式下的鼠標處理器
removeMouseListener(addingMouseListener);
removeMouseMotionListener(addingMouseListener);
//恢復為空,UI類根據addingModel是否空決定是否停止渲染要添加的組件
addingModel = null;
addingMouseListener = null;
painter = null;
//添加編輯狀態下的鼠標事件處理器和鍵盤事件處理器
//由于他們是無狀態,是和組件無關的,因此不需要重新生成
addMouseMotionListener(editingMouseListener);
addMouseListener(editingMouseListener);
addKeyListener(keyListener);
//設置模式為編輯模式
setAddingMode(false);
//觸發停止添加模式的事件
fireStopDesigning();
repaint();
}
//設置其UI類為DesignerUI,負責渲染
@Override
public void updateUI() {
setUI(new DesignerUI());
}
/**
* 在拖拽區域選擇方式鼠標釋放時調用此函數來更新所選擇的組件
* @param e 當前鼠標事件,用來和起始點構成選擇框,計算被圈入的組件
*/
public void selectComponents(MouseEvent e) {
//調用stateModel的selectComponent更新被選擇的組件,stateModel定義了拖拽起始點
stateModel.selectComponents(e);
//清除stateModel為非拖拽狀態
stateModel.reset();
repaint();
}
/**
* 鼠標拖拽組件改變其位置和尺寸,釋放鼠標時調用
*/
public void releaseComponentsDragging(MouseEvent e) {
setInvalidated(true);
//恢復初始狀態,改變被拖拽組件的位置和尺寸
stateModel.releaseDragging(e);
//觸發拖拽事件
fireComponentDragged(stateModel.getHotspotComponents());
repaint();
}
/**
* 從root組件遞歸查找x,y所在的組件,注意是正在被設計的組件,因此其name屬性必須不為空
*/
private Component componentAt(int x, int y, Component root) {
if (!(root instanceof RootPaneContainer) && !root.isVisible()) {
return null;
}
x -= root.getX();
y -= root.getY();
if (root instanceof Container) {
Container rootContainer = (Container) root;
int count = rootContainer.getComponentCount();
if (count > 0) {
for (int i = 0; i < count; i++) {
Component child = rootContainer.getComponent(i);
//只有name不為空的組件才是搜索范圍,這兒遞歸下溯調用
Component dest = componentAt(x, y, child);
if (dest != null) {
return dest;
}
}
}
}
Rectangle rect = Util.computeVisibleRect(root);
if (Util.isDesigning(root) && (x >= rect.getX()) && (x <= (rect.getX() + rect.getWidth())) && (y >= rect.getY()) && (y <= (rect.getY() + rect.getHeight()))) {
//判斷是否處于交叉區域
return root;
}
return null;
}
/**
* 根據當前stateModel中所標識的鼠標位置狀態數據更新鼠標的形狀
*/
public void updateCursor() {
Location location = stateModel.getLocation();
//調用位置枚舉的多態方法getCursor獲取鼠標形狀
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -