Browse Source

Merge pull request #1193 in BA/design from ~HZZZ/design:REPORT-3410 to release/9.0

* commit '14867392c5cfb84c271646d4f5c0e8654b862532':
  SonarQube
  SonarQube
  UIButtonGroup,CustomStylePane border
  TextArea边框
  文字tab去掉边框
  Spinner的边框问题
  format
  密码输入框边框颜色修改
master
superman 7 years ago
parent
commit
01644cb7d3
  1. 9
      designer/src/com/fr/design/mainframe/cell/settingpane/style/CustomStylePane.java
  2. 1
      designer_base/src/com/fr/design/gui/UILookAndFeel.java
  3. 3
      designer_base/src/com/fr/design/gui/borders/UITextFieldBorder.java
  4. 61
      designer_base/src/com/fr/design/gui/ibutton/UIButtonGroup.java
  5. 89
      designer_base/src/com/fr/design/gui/ibutton/UITabGroup.java
  6. 47
      designer_base/src/com/fr/design/gui/ipasswordfield/UIPassWordField.java
  7. 512
      designer_base/src/com/fr/design/gui/ispinner/UISpinner.java
  8. 22
      designer_base/src/com/fr/design/gui/itextarea/UITextArea.java
  9. 83
      designer_base/src/com/fr/design/gui/itextarea/UITextAreaUI.java
  10. 403
      designer_base/src/com/fr/design/gui/itextfield/UINumberField.java

9
designer/src/com/fr/design/mainframe/cell/settingpane/style/CustomStylePane.java

@ -6,15 +6,12 @@ import com.fr.base.Style;
import com.fr.design.actions.utils.ReportActionUtils;
import com.fr.design.dialog.BasicPane;
import com.fr.design.dialog.MultiTabPane;
import com.fr.design.gui.ibutton.FiveButtonLayout;
import com.fr.design.gui.style.*;
import com.fr.design.mainframe.ElementCasePane;
import com.fr.design.style.BorderUtils;
import com.fr.design.utils.gui.GUICoreUtils;
import com.fr.general.Inter;
import com.fr.stable.Constants;
import javax.swing.*;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.util.ArrayList;
@ -41,7 +38,7 @@ public class CustomStylePane extends MultiTabPane<Style> {
super();
tabPane.setOneLineTab(true);
tabPane.setDrawLine(false);
tabPane.setLayout(new FiveButtonLayout(1));
tabPane.setLayout(new GridLayout(1, 3, 0, 0));
}
public static void main(String[] args) {
@ -164,8 +161,6 @@ public class CustomStylePane extends MultiTabPane<Style> {
*
*/
public void updateBean(Style ob) {
return;
}
}

1
designer_base/src/com/fr/design/gui/UILookAndFeel.java

@ -171,6 +171,7 @@ public class UILookAndFeel extends MetalLookAndFeel {
table.put("FormattedTextField.border", new UITextFieldBorder());
table.put("TextField.border", new UITextFieldBorder());
table.put("PasswordField.border", new UITextFieldBorder());
table.put("TextArea.border", new UITextFieldBorder());
}

3
designer_base/src/com/fr/design/gui/borders/UITextFieldBorder.java

@ -4,6 +4,7 @@
package com.fr.design.gui.borders;
import com.fr.design.constants.UIConstants;
import com.fr.design.utils.DrawRoutines;
import com.fr.design.utils.ThemeUtils;
@ -54,7 +55,7 @@ public class UITextFieldBorder extends AbstractBorder implements UIResource {
g, ThemeUtils.TEXT_BORDER_DISABLED_COLOR, x, y, w, h);
} else {
DrawRoutines.drawBorder(
g, ThemeUtils.TEXT_BORDER_COLOR, x, y, w, h);
g, UIConstants.POP_DIALOG_BORDER, x, y, w, h);
}
}
}

61
designer_base/src/com/fr/design/gui/ibutton/UIButtonGroup.java

@ -7,6 +7,7 @@ import com.fr.design.event.GlobalNameObserver;
import com.fr.design.utils.gui.GUICoreUtils;
import com.fr.stable.ArrayUtils;
import com.fr.stable.Constants;
import com.fr.stable.StringUtils;
import javax.swing.*;
import javax.swing.border.Border;
@ -28,7 +29,7 @@ public class UIButtonGroup<T> extends JPanel implements GlobalNameObserver {
protected int selectedIndex = -1;
private List<T> objectList;// 起到一个render的作用
private GlobalNameListener globalNameListener = null;
private String buttonGroupName = "";
private String buttonGroupName = StringUtils.EMPTY;
private boolean isToolBarComponent = false;
private boolean isClick;
@ -179,34 +180,6 @@ public class UIButtonGroup<T> extends JPanel implements GlobalNameObserver {
g2d.setClip(oldClip);
}
/**
* 重载Border画法
*
* @param g
*/
@Override
protected void paintBorder(Graphics g) {
if (isToolBarComponent) {
return;
}
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(UIConstants.SHADOW_GREY);
int width = 0;
for (int i = 0; i < labelButtonList.size() - 1; i++) {
width += labelButtonList.get(i).getWidth() + 1;
int height = labelButtonList.get(i).getHeight();
g.drawLine(width, 0, width, height);
}
width += labelButtonList.get(labelButtonList.size() - 1).getWidth() + 1;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.drawRoundRect(0, 0, width, getHeight() - 1, UIConstants.BUTTON_GROUP_ARC, UIConstants.BUTTON_GROUP_ARC);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
}
/**
* setSelectedItem
*
@ -369,21 +342,21 @@ public class UIButtonGroup<T> extends JPanel implements GlobalNameObserver {
* @param args
*/
public static void main(String... args) {
JFrame jf = new JFrame("test");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel content = (JPanel) jf.getContentPane();
content.setLayout(new BorderLayout());
Icon[] a1 = {BaseUtils.readIcon("/com/fr/design/images/m_format/cellstyle/h_left_normal.png"), BaseUtils.readIcon("/com/fr/design/images/m_format/cellstyle/h_center_normal.png"),
BaseUtils.readIcon("/com/fr/design/images/m_format/cellstyle/h_right_normal.png")};
Integer[] a2 = new Integer[]{Constants.LEFT, Constants.CENTER, Constants.RIGHT};
UIButtonGroup<Integer> bb = new UIButtonGroup<Integer>(a1, a2);
bb.setBounds(20, 20, bb.getPreferredSize().width, bb.getPreferredSize().height);
bb.setSelectedIndex(0);
bb.setEnabled(false);
content.add(bb);
GUICoreUtils.centerWindow(jf);
jf.setSize(400, 400);
jf.setVisible(true);
// JFrame jf = new JFrame("test");
// jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// JPanel content = (JPanel) jf.getContentPane();
// content.setLayout(new BorderLayout());
// Icon[] a1 = {BaseUtils.readIcon("/com/fr/design/images/m_format/cellstyle/h_left_normal.png"), BaseUtils.readIcon("/com/fr/design/images/m_format/cellstyle/h_center_normal.png"),
// BaseUtils.readIcon("/com/fr/design/images/m_format/cellstyle/h_right_normal.png")};
// Integer[] a2 = new Integer[]{Constants.LEFT, Constants.CENTER, Constants.RIGHT};
// UIButtonGroup<Integer> bb = new UIButtonGroup<Integer>(a1, a2);
// bb.setBounds(20, 20, bb.getPreferredSize().width, bb.getPreferredSize().height);
// bb.setSelectedIndex(0);
// bb.setEnabled(false);
// content.add(bb);
// GUICoreUtils.centerWindow(jf);
// jf.setSize(400, 400);
// jf.setVisible(true);
}

89
designer_base/src/com/fr/design/gui/ibutton/UITabGroup.java

@ -1,12 +1,8 @@
package com.fr.design.gui.ibutton;
import java.awt.*;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.*;
import javax.swing.border.Border;
import com.fr.design.constants.UIConstants;
import java.awt.*;
public class UITabGroup extends UIButtonGroup<Integer> {
private boolean isOneLineTab = false;
@ -22,7 +18,7 @@ public class UITabGroup extends UIButtonGroup<Integer> {
* @param index 序号
*/
public void tabChanged(int index) {
return;
}
public UITabGroup(Icon[] iconArray) {
@ -46,83 +42,6 @@ public class UITabGroup extends UIButtonGroup<Integer> {
}
}
@Override
protected void paintBorder(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(UIConstants.LINE_COLOR);
if (!isTwoLine()) {
int width = isDrawLine ? ORIGINAL_WIDTH : 0;
for (int i = 0; i < labelButtonList.size() - 1; i++) {
width += labelButtonList.get(i).getWidth() + 1;
int height = labelButtonList.get(i).getHeight();
g.drawLine(width, 0, width, height);
}
width += labelButtonList.get(labelButtonList.size() - 1).getWidth() + 1;
if (isDrawLine) {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.drawRoundRect(ORIGINAL_WIDTH, 0, width - ORIGINAL_WIDTH, getHeight() - 1, UIConstants.ARC, UIConstants.ARC);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
} else {
g2d.drawRect(0, 0, width, getHeight() - 1);
}
} else if (labelButtonList.size() % 2 != 0) {
paintOddNumberButtons(g, g2d);
} else {
int width = ORIGINAL_WIDTH;
int number = labelButtonList.size() / 2;
for (int i = 0; i < number - 1; i++) {
width += labelButtonList.get(i).getWidth() + 1;
int height = labelButtonList.get(i).getHeight() * 2 + 1;
g.drawLine(width, 0, width, height);
}
width += labelButtonList.get(number - 1).getWidth() + 1;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.drawRoundRect(ORIGINAL_WIDTH, 0, width - ORIGINAL_WIDTH, getHeight() - 1, UIConstants.ARC, UIConstants.ARC);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
}
if (isDrawLine) {
g2d.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2);
}
}
private void paintOddNumberButtons(Graphics g, Graphics2D g2d) {
int width = ORIGINAL_WIDTH;
int buttonHeight = labelButtonList.get(0).getHeight();
int upButtonCount = labelButtonList.size() / 2 + 1;
for (int i = 0; i < upButtonCount - 1; i++) {
width += labelButtonList.get(i).getWidth() + 1;
int height = labelButtonList.get(i).getHeight() + 1;
g.drawLine(width, 0, width, height);
}
width += labelButtonList.get(upButtonCount - 1).getWidth() + 1;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.drawRoundRect(ORIGINAL_WIDTH, 0, width - ORIGINAL_WIDTH, buttonHeight + 1, UIConstants.ARC, UIConstants.ARC);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
int line2X = labelButtonList.get(upButtonCount).getX();
int line2Y = labelButtonList.get(upButtonCount).getY();
width = line2X;
width += labelButtonList.get(upButtonCount).getWidth();
int height = labelButtonList.get(upButtonCount).getHeight() + 1;
g.drawLine(width, line2Y, width, line2Y + height);
for (int i = upButtonCount + 1; i < labelButtonList.size() - 1; i++) {
width += labelButtonList.get(i).getWidth() + 1;
height = labelButtonList.get(i).getHeight() + 1;
g.drawLine(width, line2Y, width, line2Y + height);
}
width += labelButtonList.get(upButtonCount).getWidth() + 1;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.drawRoundRect(line2X - 1, line2Y, width - line2X + 1, buttonHeight, UIConstants.ARC, UIConstants.ARC);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
}
protected boolean isTwoLine() {
return labelButtonList.size() >= BUTTON_NUMBER && !isOneLineTab;
}
public void setOneLineTab(boolean isOneLineTab) {
this.isOneLineTab = isOneLineTab;
}
@ -138,7 +57,7 @@ public class UITabGroup extends UIButtonGroup<Integer> {
@Override
protected Border getGroupBorder() {
if (!isDrawLine) {
return BorderFactory.createEmptyBorder(1, 1, 1, 1);
return BorderFactory.createEmptyBorder(0, 0, 0, 0);
}
return BorderFactory.createEmptyBorder(1, GAP, 1, GAP);
}

47
designer_base/src/com/fr/design/gui/ipasswordfield/UIPassWordField.java

@ -1,8 +1,13 @@
package com.fr.design.gui.ipasswordfield;
import com.fr.design.constants.UIConstants;
import javax.swing.*;
import javax.swing.text.Document;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
/**
* Created with IntelliJ IDEA.
@ -12,24 +17,56 @@ import javax.swing.text.Document;
* To change this template use File | Settings | File Templates.
*/
public class UIPassWordField extends JPasswordField {
private boolean isRollOver;
public UIPassWordField () {
public UIPassWordField() {
super();
addRollOverListener();
}
public UIPassWordField (String text) {
public UIPassWordField(String text) {
super(text);
addRollOverListener();
}
public UIPassWordField (int columns) {
public UIPassWordField(int columns) {
super(columns);
addRollOverListener();
}
public UIPassWordField (String text, int columns) {
public UIPassWordField(String text, int columns) {
super(text, columns);
addRollOverListener();
}
public UIPassWordField (Document doc, String txt, int columns) {
public UIPassWordField(Document doc, String txt, int columns) {
super(doc, txt, columns);
addRollOverListener();
}
private void addRollOverListener() {
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
isRollOver = true;
UIPassWordField.this.repaint();
}
@Override
public void mouseExited(MouseEvent e) {
isRollOver = false;
UIPassWordField.this.repaint();
}
});
}
@Override
protected void paintBorder(Graphics g) {
if (isRollOver && this.isEnabled()) {
g.setColor(UIConstants.TEXT_FILED_BORDER_SELECTED);
g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
} else {
super.paintBorder(g);
}
}
}

512
designer_base/src/com/fr/design/gui/ispinner/UISpinner.java

@ -1,17 +1,5 @@
package com.fr.design.gui.ispinner;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.RoundRectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.JTextComponent;
import com.fr.design.constants.UIConstants;
import com.fr.design.event.GlobalNameListener;
import com.fr.design.event.GlobalNameObserver;
@ -19,185 +7,195 @@ import com.fr.design.event.UIObserver;
import com.fr.design.event.UIObserverListener;
import com.fr.design.gui.ibutton.UIButton;
import com.fr.design.gui.itextfield.UINumberField;
import com.fr.design.gui.itextfield.UITextField;
import com.fr.design.gui.itextfield.UITextFieldUI;
import com.fr.stable.Constants;
import com.fr.design.utils.gui.GUICoreUtils;
import com.fr.design.utils.gui.GUIPaintUtils;
import com.fr.stable.Constants;
import com.fr.stable.StringUtils;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.*;
import java.awt.event.*;
public class UISpinner extends JPanel implements UIObserver, GlobalNameObserver {
protected double value;
private static final int SIZE = 20;
private static final int LEN = 13;
private UINumberField textField;
private UIButton preButton;
private UIButton nextButton;
private double minValue;
private double maxValue;
private double dierta;
private String spinnerName = "";
private UIObserverListener uiObserverListener;
private GlobalNameListener globalNameListener = null;
public UISpinner(double minValue, double maxValue, double dierta) {
this.minValue = minValue;
this.maxValue = maxValue;
this.dierta = dierta;
initComponents();
iniListener();
}
public UISpinner(double minValue, double maxValue, double dierta, double defaultValue) {
this(minValue, maxValue, dierta);
textField.setValue(defaultValue);
}
private void iniListener() {
if (shouldResponseChangeListener()) {
this.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
if (uiObserverListener == null) {
return;
}
uiObserverListener.doChange();
}
});
}
}
/**
* 给组件分别加上FocusListener
* @param focusListener 监听事件
*/
public void addUISpinnerFocusListenner(FocusListener focusListener) {
this.addFocusListener(focusListener);
this.textField.addFocusListener(focusListener);
this.preButton.addFocusListener(focusListener);
this.nextButton.addFocusListener(focusListener);
}
public double getValue() {
return value;
}
public void setGlobalName(String name) {
spinnerName = name;
}
public UINumberField getTextField() {
return textField;
}
public void setValue(double value) {
if (globalNameListener != null && shouldResponseNameListener()) {
globalNameListener.setGlobalName(spinnerName);
}
value = value < minValue ? minValue : value;
value = value > maxValue ? maxValue : value;
if (value == this.value) {
return;
}
this.value = value;
textField.getDocument().removeDocumentListener(docListener);
textField.setValue(value);
textField.getDocument().addDocumentListener(docListener);
fireStateChanged();
}
public void setTextFieldValue(double value) {
if (globalNameListener != null && shouldResponseNameListener()) {
globalNameListener.setGlobalName(spinnerName);
}
value = value < minValue ? minValue : value;
value = value > maxValue ? maxValue : value;
if (value == this.value) {
return;
}
this.value = value;
fireStateChanged();
}
public void setEnabled(boolean flag) {
super.setEnabled(flag);
this.textField.setEnabled(flag);
this.preButton.setEnabled(flag);
this.nextButton.setEnabled(flag);
}
@Override
public Dimension getPreferredSize() {
Dimension dim = super.getPreferredSize();
dim.height = SIZE;
return dim;
}
/**
*增加 a <code>ChangeListener</code> to the listener list.
* @param l 监听事件
*/
public void addChangeListener(ChangeListener l) {
this.listenerList.add(ChangeListener.class, l);
}
/**
*移除 a <code>ChangeListener</code> from the listener list.
* @param l 监听事件
*/
public void removeChangeListener(ChangeListener l) {
this.listenerList.remove(ChangeListener.class, l);
}
// august: Process the listeners last to first
protected void fireStateChanged() {
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ChangeListener.class) {
((ChangeListener) listeners[i + 1]).stateChanged(new ChangeEvent(this));
}
}
}
private void initComponents() {
textField = initNumberField();
textField.setMaxValue(maxValue);
textField.setMinValue(minValue);
setValue(value);
textField.setUI(new SpinnerTextFieldUI(textField));
preButton = new UIButton(UIConstants.ARROW_UP_ICON){
protected double value;
private static final int SIZE = 20;
private static final int LEN = 13;
private static final int WIDTH = 13;
private static final int HEIGHT = 10;
private UINumberField textField;
private UIButton preButton;
private UIButton nextButton;
private double minValue;
private double maxValue;
private double dierta;
private String spinnerName = StringUtils.EMPTY;
private UIObserverListener uiObserverListener;
private GlobalNameListener globalNameListener = null;
public UISpinner(double minValue, double maxValue, double dierta) {
this.minValue = minValue;
this.maxValue = maxValue;
this.dierta = dierta;
initComponents();
iniListener();
}
public UISpinner(double minValue, double maxValue, double dierta, double defaultValue) {
this(minValue, maxValue, dierta);
textField.setValue(defaultValue);
}
private void iniListener() {
if (shouldResponseChangeListener()) {
this.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
if (uiObserverListener == null) {
return;
}
uiObserverListener.doChange();
}
});
}
}
/**
* 给组件分别加上FocusListener
*
* @param focusListener 监听事件
*/
public void addUISpinnerFocusListenner(FocusListener focusListener) {
this.addFocusListener(focusListener);
this.textField.addFocusListener(focusListener);
this.preButton.addFocusListener(focusListener);
this.nextButton.addFocusListener(focusListener);
}
public double getValue() {
return value;
}
public void setGlobalName(String name) {
spinnerName = name;
}
public UINumberField getTextField() {
return textField;
}
public void setValue(double value) {
if (globalNameListener != null && shouldResponseNameListener()) {
globalNameListener.setGlobalName(spinnerName);
}
value = value < minValue ? minValue : value;
value = value > maxValue ? maxValue : value;
if (value == this.value) {
return;
}
this.value = value;
textField.getDocument().removeDocumentListener(docListener);
textField.setValue(value);
textField.getDocument().addDocumentListener(docListener);
fireStateChanged();
}
public void setTextFieldValue(double value) {
if (globalNameListener != null && shouldResponseNameListener()) {
globalNameListener.setGlobalName(spinnerName);
}
value = value < minValue ? minValue : value;
value = value > maxValue ? maxValue : value;
if (value == this.value) {
return;
}
this.value = value;
fireStateChanged();
}
public void setEnabled(boolean flag) {
super.setEnabled(flag);
this.textField.setEnabled(flag);
this.preButton.setEnabled(flag);
this.nextButton.setEnabled(flag);
}
@Override
public Dimension getPreferredSize() {
Dimension dim = super.getPreferredSize();
dim.height = SIZE;
return dim;
}
/**
* 增加 a <code>ChangeListener</code> to the listener list.
*
* @param l 监听事件
*/
public void addChangeListener(ChangeListener l) {
this.listenerList.add(ChangeListener.class, l);
}
/**
* 移除 a <code>ChangeListener</code> from the listener list.
*
* @param l 监听事件
*/
public void removeChangeListener(ChangeListener l) {
this.listenerList.remove(ChangeListener.class, l);
}
// august: Process the listeners last to first
protected void fireStateChanged() {
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ChangeListener.class) {
((ChangeListener) listeners[i + 1]).stateChanged(new ChangeEvent(this));
}
}
}
private void initComponents() {
textField = initNumberField();
textField.setMaxValue(maxValue);
textField.setMinValue(minValue);
setValue(value);
preButton = new UIButton(UIConstants.ARROW_UP_ICON) {
public boolean shouldResponseChangeListener() {
return false;
}
};
preButton.setRoundBorder(true, Constants.LEFT);
nextButton = new UIButton(UIConstants.ARROW_DOWN_ICON) {
preButton.setRoundBorder(true, Constants.LEFT);
nextButton = new UIButton(UIConstants.ARROW_DOWN_ICON) {
public boolean shouldResponseChangeListener() {
return false;
}
};
nextButton.setRoundBorder(true, Constants.LEFT);
setLayout(new BorderLayout());
add(textField, BorderLayout.CENTER);
JPanel arrowPane = new JPanel();
arrowPane.setPreferredSize(new Dimension(LEN, SIZE));
arrowPane.setLayout(new GridLayout(2, 1));
preButton.setBounds(0, 1, 13, 10);
nextButton.setBounds(0, 10, 13, 10);
arrowPane.add(preButton);
arrowPane.add(nextButton);
add(arrowPane, BorderLayout.EAST);
nextButton.setRoundBorder(true, Constants.LEFT);
setLayout(new BorderLayout());
add(textField, BorderLayout.CENTER);
JPanel arrowPane = new JPanel();
arrowPane.setPreferredSize(new Dimension(LEN, SIZE));
arrowPane.setLayout(new GridLayout(2, 1));
preButton.setBounds(0, 1, WIDTH, HEIGHT);
nextButton.setBounds(0, HEIGHT, WIDTH, HEIGHT);
arrowPane.add(preButton);
arrowPane.add(nextButton);
add(arrowPane, BorderLayout.EAST);
componentInitListeners();
}
}
private void componentInitListeners(){
private void componentInitListeners() {
preButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
@ -219,7 +217,7 @@ public class UISpinner extends JPanel implements UIObserver, GlobalNameObserver
}
}
});
textField.getDocument().removeDocumentListener(docListener);
textField.getDocument().removeDocumentListener(docListener);
textField.getDocument().addDocumentListener(docListener);
textField.addFocusListener(new FocusAdapter() {
@Override
@ -229,123 +227,85 @@ public class UISpinner extends JPanel implements UIObserver, GlobalNameObserver
});
}
protected UINumberField initNumberField() {
return new UINumberField(2) {
public boolean shouldResponseChangeListener() {
return false;
}
};
}
private DocumentListener docListener = new DocumentListener() {
@Override
public void removeUpdate(DocumentEvent e) {
setTextFieldValue(textField.getValue());
}
@Override
public void insertUpdate(DocumentEvent e) {
setTextFieldValue(textField.getValue());
}
@Override
public void changedUpdate(DocumentEvent e) {
setTextFieldValue(textField.getValue());
}
};
/**
* 给组件登记一个观察者监听事件
* @param listener 观察者监听事件
*/
public void registerChangeListener(UIObserverListener listener) {
uiObserverListener = listener;
}
protected UINumberField initNumberField() {
return new UINumberField(2) {
public boolean shouldResponseChangeListener() {
return false;
}
};
}
private DocumentListener docListener = new DocumentListener() {
@Override
public void removeUpdate(DocumentEvent e) {
setTextFieldValue(textField.getValue());
}
@Override
public void insertUpdate(DocumentEvent e) {
setTextFieldValue(textField.getValue());
}
@Override
public void changedUpdate(DocumentEvent e) {
setTextFieldValue(textField.getValue());
}
};
/**
* 给组件登记一个观察者监听事件
*
* @param listener 观察者监听事件
*/
public void registerChangeListener(UIObserverListener listener) {
uiObserverListener = listener;
}
/**
* 组件是否需要响应添加的观察者事件
*
* @return 如果需要响应观察者事件则返回true否则返回false
*/
public boolean shouldResponseChangeListener() {
return true;
}
public boolean shouldResponseChangeListener() {
return true;
}
/**
* 给组件登记一个全局名字观察者监听事件
*
* @param listener 观察者监听事件
*/
public void registerNameListener(GlobalNameListener listener) {
globalNameListener = listener;
}
public void registerNameListener(GlobalNameListener listener) {
globalNameListener = listener;
}
/**
* 组件是否需要响应添加的观察者事件
*
* @return 如果需要响应观察者事件则返回true否则返回false
*/
public boolean shouldResponseNameListener() {
return true;
}
private class SpinnerTextFieldUI extends UITextFieldUI {
public SpinnerTextFieldUI(UITextField textField) {
super(textField);
}
@Override
public void paintBorder(Graphics2D g2d, int width, int height,
boolean isRound, int rectDirection) {
// do nothing
}
protected void paintBackground(Graphics g) {
JTextComponent editor = getComponent();
int width = editor.getWidth();
int height = editor.getHeight();
Shape oldClip = g.getClip();
Shape roundShape = new RoundRectangle2D.Double(0, 0, width, height, UIConstants.ARC, UIConstants.ARC);
Graphics2D g2d = (Graphics2D) g;
g2d.clearRect(0, 0, width, height);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.clip(roundShape);
g2d.setColor(Color.WHITE);
g2d.fillRoundRect(1, 1, width - 2, height - 2, UIConstants.ARC, UIConstants.ARC);
if (isRollOver && isEnabled()) {
Shape shape = new RoundRectangle2D.Double(1, 1, width - 3, height - 3, UIConstants.ARC, UIConstants.ARC);
GUIPaintUtils.paintBorderShadow(g2d, 3, shape, UIConstants.HOVER_BLUE, Color.WHITE);
} else {
g2d.setColor(UIConstants.LINE_COLOR);
g2d.drawRoundRect(1, 1, width - 2, height - 2, UIConstants.ARC, UIConstants.ARC);
g2d.clearRect(width - 2, 0, 2, height);
g2d.setClip(oldClip);
g2d.drawLine(width - 2, 1, width, 1);
g2d.drawLine(width - 2, height - 1, width, height - 1);
}
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
}
}
public boolean shouldResponseNameListener() {
return true;
}
/**
* 程序入口 测试
* @param args 参数
* 程序入口 测试
*
* @param args 参数
*/
public static void main(String... args) {
LayoutManager layoutManager = null;
JFrame jf = new JFrame("test");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel content = (JPanel) jf.getContentPane();
content.setLayout(layoutManager);
UISpinner bb = new UISpinner(0, 9, 1);
bb.setValue(4);
bb.setBounds(20, 20, bb.getPreferredSize().width, bb.getPreferredSize().height);
content.add(bb);
GUICoreUtils.centerWindow(jf);
jf.setSize(400, 400);
jf.setVisible(true);
}
public static void main(String... args) {
// LayoutManager layoutManager = null;
// JFrame jf = new JFrame("test");
// jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// JPanel content = (JPanel) jf.getContentPane();
// content.setLayout(layoutManager);
//
// UISpinner bb = new UISpinner(0, 9, 1);
// bb.setValue(4);
// bb.setBounds(20, 20, bb.getPreferredSize().width, bb.getPreferredSize().height);
// content.add(bb);
// GUICoreUtils.centerWindow(jf);
// jf.setSize(400, 400);
// jf.setVisible(true);
}
}

22
designer_base/src/com/fr/design/gui/itextarea/UITextArea.java

@ -4,6 +4,7 @@ import com.fr.common.inputevent.InputEventBaseOnOS;
import com.fr.design.event.UIObserver;
import com.fr.design.event.UIObserverListener;
import com.fr.design.utils.gui.GUICoreUtils;
import com.fr.stable.Constants;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
@ -99,10 +100,9 @@ public class UITextArea extends JTextArea implements UIObserver {
@Override
protected void paintBorder(Graphics g) {
getUI().paintBorder((Graphics2D) g, getWidth(), getHeight(), true, Constants.NULL);
}
@Override
/**
*
@ -122,14 +122,14 @@ public class UITextArea extends JTextArea implements UIObserver {
* @param args
*/
public static void main(String... args) {
JFrame jf = new JFrame("test");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel content = (JPanel) jf.getContentPane();
content.setLayout(new BorderLayout());
UITextArea bb = new UITextArea("123455weoijweio reiwj kewl jfejkfljds kl jfldk jfk jdskfjkdsfklj dkl jfsdjf");
content.add(bb, BorderLayout.CENTER);
GUICoreUtils.centerWindow(jf);
jf.setSize(400, 400);
jf.setVisible(true);
// JFrame jf = new JFrame("test");
// jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// JPanel content = (JPanel) jf.getContentPane();
// content.setLayout(new BorderLayout());
// UITextArea bb = new UITextArea("123455weoijweio reiwj kewl jfejkfljds kl jfldk jfk jdskfjkdsfklj dkl jfsdjf");
// content.add(bb, BorderLayout.CENTER);
// GUICoreUtils.centerWindow(jf);
// jf.setSize(400, 400);
// jf.setVisible(true);
}
}

83
designer_base/src/com/fr/design/gui/itextarea/UITextAreaUI.java

@ -1,56 +1,43 @@
package com.fr.design.gui.itextarea;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.RoundRectangle2D;
import javax.swing.JComponent;
import javax.swing.plaf.basic.BasicTextAreaUI;
import javax.swing.text.JTextComponent;
import com.fr.design.constants.UIConstants;
import com.fr.design.utils.gui.GUIPaintUtils;
public class UITextAreaUI extends BasicTextAreaUI {
protected boolean isRollOver;
private JComponent textField;
public UITextAreaUI(final JComponent textField) {
super();
this.textField = textField;
this.textField.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
isRollOver = true;
textField.repaint();
}
@Override
public void mouseExited(MouseEvent e) {
isRollOver = false;
textField.repaint();
}
});
}
import javax.swing.*;
import javax.swing.plaf.basic.BasicTextAreaUI;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
@Override
protected void paintBackground(Graphics g) {
JTextComponent editor = getComponent();
int width = editor.getWidth();
int height = editor.getHeight();
Graphics2D g2d = (Graphics2D)g;
g2d.clearRect(0, 0, width, height);
if(isRollOver && textField.isEnabled() && ((UITextArea)textField).isEditable()) {
Shape shape = new RoundRectangle2D.Double(1, 1, width - 3, height - 3, UIConstants.ARC, UIConstants.ARC);
GUIPaintUtils.paintBorderShadow(g2d, 3, shape, UIConstants.HOVER_BLUE, Color.WHITE);
} else {
g2d.setColor(UIConstants.LINE_COLOR);
g2d.drawRoundRect(1, 1, width - 2, height - 2, UIConstants.ARC, UIConstants.ARC);
}
}
public class UITextAreaUI extends BasicTextAreaUI {
protected boolean isRollOver;
private JComponent textField;
public UITextAreaUI(final JComponent textField) {
super();
this.textField = textField;
this.textField.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
isRollOver = true;
textField.repaint();
}
@Override
public void mouseExited(MouseEvent e) {
isRollOver = false;
textField.repaint();
}
});
}
public void paintBorder(Graphics2D g2d, int width, int height, boolean isRound, int rectDirection) {
if (isRollOver && textField.isEnabled()) {
g2d.setColor(UIConstants.TEXT_FILED_BORDER_SELECTED);
g2d.drawRect(0, 0, width - 1, height - 1);
} else {
GUIPaintUtils.drawBorder(g2d, 0, 0, width, height, isRound, rectDirection);
}
}
}

403
designer_base/src/com/fr/design/gui/itextfield/UINumberField.java

@ -16,92 +16,95 @@ import java.awt.*;
* Number Field.
*/
public class UINumberField extends UITextField {
public static final double ERROR_VALUE = Double.MAX_VALUE + 4.44; // peter:错误的值.
/**
* 整数部分的长度
*/
private int maxIntegerLength = 24;
/**
* 小数部分的长度
*/
public static final double ERROR_VALUE = Double.MAX_VALUE + 4.44; // peter:错误的值.
public static final int MAX_INTEGERLENGTH = 24;
public static final int MAX_INTEGERLENGTH_32 = 32;
public static final int MAX_DECIMALLENGTH = 16;
/**
* 整数部分的长度
*/
private int maxIntegerLength = MAX_INTEGERLENGTH;
/**
* 小数部分的长度
*/
private static final int DEFAULTMAXDECIMALLENTH = 16;
private static final int TESTMAXVALUE = 100;
private static final int TESTMINVALUE = -10;
private int maxDecimalLength = 16;
private double minValue = -Double.MAX_VALUE;
private double maxValue = Double.MAX_VALUE;
private boolean isContentChanged = false;
public UINumberField() {
this(32, 16);
}
public UINumberField(int columns) {
this();
setColumns(columns);
}
public UINumberField(int maxIntegerLength, int maxDecimalLength) {
this(maxIntegerLength, maxDecimalLength, -Double.MAX_VALUE, Double.MAX_VALUE);
}
public UINumberField(int maxIntegerLength, int maxDecimalLength, double minValue, double maxValue) {
this.maxIntegerLength = maxIntegerLength;
this.maxDecimalLength = maxDecimalLength;
this.minValue = minValue;
this.maxValue = maxValue;
setFieldDocument();
}
public void setFieldDocument(){
private static final int TESTMAXVALUE = 100;
private static final int TESTMINVALUE = -10;
private int maxDecimalLength = MAX_DECIMALLENGTH;
private double minValue = -Double.MAX_VALUE;
private double maxValue = Double.MAX_VALUE;
private boolean isContentChanged = false;
public UINumberField() {
this(MAX_INTEGERLENGTH_32, MAX_DECIMALLENGTH);
}
public UINumberField(int columns) {
this();
setColumns(columns);
}
public UINumberField(int maxIntegerLength, int maxDecimalLength) {
this(maxIntegerLength, maxDecimalLength, -Double.MAX_VALUE, Double.MAX_VALUE);
}
public UINumberField(int maxIntegerLength, int maxDecimalLength, double minValue, double maxValue) {
this.maxIntegerLength = maxIntegerLength;
this.maxDecimalLength = maxDecimalLength;
this.minValue = minValue;
this.maxValue = maxValue;
setFieldDocument();
}
public void setFieldDocument() {
setDocument(new NumberDocument());
initListener();
}
public int getMaxIntegerLength() {
return maxIntegerLength;
}
public void setMaxIntegerLength(int maxIntegerLength) {
this.maxIntegerLength = maxIntegerLength;
}
public int getMaxDecimalLength() {
return maxDecimalLength;
}
public void setMaxDecimalLength(int maxDecimalLength) {
this.maxDecimalLength = maxDecimalLength;
}
public double getMinValue() {
return minValue;
}
public void setMinValue(double minValue) {
this.minValue = minValue;
}
public double getMaxValue() {
return maxValue;
}
public void setMaxValue(double maxValue) {
this.maxValue = maxValue;
}
/**
* Set the value.
*/
public void setValue(double value) {
this.setText(Utils.doubleToString(value));
}
/**
* Return the value.
*/
public double getValue() throws NumberFormatException {
public int getMaxIntegerLength() {
return maxIntegerLength;
}
public void setMaxIntegerLength(int maxIntegerLength) {
this.maxIntegerLength = maxIntegerLength;
}
public int getMaxDecimalLength() {
return maxDecimalLength;
}
public void setMaxDecimalLength(int maxDecimalLength) {
this.maxDecimalLength = maxDecimalLength;
}
public double getMinValue() {
return minValue;
}
public void setMinValue(double minValue) {
this.minValue = minValue;
}
public double getMaxValue() {
return maxValue;
}
public void setMaxValue(double maxValue) {
this.maxValue = maxValue;
}
/**
* Set the value.
*/
public void setValue(double value) {
this.setText(Utils.doubleToString(value));
}
/**
* Return the value.
*/
public double getValue() throws NumberFormatException {
try {
if (this.getText().length() == 0) {
return 0;
@ -113,134 +116,132 @@ public class UINumberField extends UITextField {
}
}
/**
* Retusn text value.
*/
public String getTextValue() {
return this.getText();
}
/**
* Set property integer.
*
* @param integer
* New value of property integer.
*/
public void setInteger(boolean integer) {
if (integer) {
this.maxDecimalLength = 0;
} else {
this.maxDecimalLength = DEFAULTMAXDECIMALLENTH;
}
}
/**
* Check whether the content changed.
*/
public boolean isContentChanged() {
return isContentChanged;
}
public void setisContentChanged(boolean isContentChanged){
/**
* Retusn text value.
*/
public String getTextValue() {
return this.getText();
}
/**
* Set property integer.
*
* @param integer New value of property integer.
*/
public void setInteger(boolean integer) {
if (integer) {
this.maxDecimalLength = 0;
} else {
this.maxDecimalLength = DEFAULTMAXDECIMALLENTH;
}
}
/**
* Check whether the content changed.
*/
public boolean isContentChanged() {
return isContentChanged;
}
public void setisContentChanged(boolean isContentChanged) {
this.isContentChanged = isContentChanged;
}
@Override
public Dimension getPreferredSize() {
return new Dimension(super.getPreferredSize().width, 20);
}
class NumberDocument extends PlainDocument {
public NumberDocument() {
}
@Override
public Dimension getPreferredSize() {
return new Dimension(super.getPreferredSize().width, 20);
}
class NumberDocument extends PlainDocument {
public boolean checkString(int offset, String s, String str) {
return (ComparatorUtils.equals(s,"F")
|| ComparatorUtils.equals(s,"f")
|| ComparatorUtils.equals(s,"D")
|| ComparatorUtils.equals(s,"d")
||(ComparatorUtils.equals(str.trim(),"0") && !ComparatorUtils.equals(s.substring(0, 1),".") && offset != 0)// 第一位是0时,第二位只能为小数点
||(ComparatorUtils.equals(s,".") && maxDecimalLength == 0));
return (ComparatorUtils.equals(s, "F")
|| ComparatorUtils.equals(s, "f")
|| ComparatorUtils.equals(s, "D")
|| ComparatorUtils.equals(s, "d")
|| (ComparatorUtils.equals(str.trim(), "0") && !ComparatorUtils.equals(s.substring(0, 1), ".") && offset != 0)// 第一位是0时,第二位只能为小数点
|| (ComparatorUtils.equals(s, ".") && maxDecimalLength == 0));
}
public void insertString(int offset, String s, AttributeSet a) throws BadLocationException {
String str = getText(0, getLength());
// 不能为f,F,d,D
if (checkString(offset,s, str) ) {
Toolkit.getDefaultToolkit().beep();
return;
}
String strNew = str.substring(0, offset) + s + str.substring(offset, getLength());
if(notChange(strNew)) {
return;
}
setisContentChanged(true);
super.insertString(offset, s, a);
}
// kunsnat: 这种限制输入 有个不好的地方, 比如删除时: 10.1 最大值限定100, 那么就删除中间的小数点之后变为101, 超出了100.
// 但是直接限制不能删除中间类似小数点, 那么也可能遇到: 最小值10 , 从100变化到其中的19, 就很难..
private boolean notChange(String strNew) {
boolean noChange = false;
strNew = strNew.replaceFirst("-", StringUtils.EMPTY); // 控制能输入负数
boolean isMinus = strNew.startsWith("-");
String strIntPart;
String strDecPart = StringUtils.EMPTY;
int decPos = strNew.indexOf(CoreConstants.DOT);
if (decPos > -1) {
strIntPart = strNew.substring(0, decPos);
strDecPart = strNew.substring(decPos + 1);
} else {
strIntPart = strNew;
}
if (isOverMaxOrMinValue(strIntPart, strDecPart, strNew)) {
Toolkit.getDefaultToolkit().beep();
noChange = true;
}
try {
if (!ComparatorUtils.equals(strNew, StringUtils.EMPTY) && !ComparatorUtils.equals(strNew, "-")) {// 控制能输入负数
double d = Double.parseDouble(strNew) * (isMinus ? -1 : 1);
if (d < minValue || d > maxValue) {
throw new Exception();
}
}
} catch (Exception e) {
Toolkit.getDefaultToolkit().beep();
noChange = true;
}
return noChange;
}
private boolean isOverMaxOrMinValue(String strIntPart, String strDecPart, String strNew) {
return strIntPart.length() > maxIntegerLength
|| strDecPart.length() > maxDecimalLength
|| (strNew.length() > 1 && ComparatorUtils.equals(strNew.substring(0, 1), "0") && !ComparatorUtils.equals(strNew.substring(1, 2),"."));
}
}
public void insertString(int offset, String s, AttributeSet a) throws BadLocationException {
String str = getText(0, getLength());
// 不能为f,F,d,D
if (checkString(offset, s, str)) {
Toolkit.getDefaultToolkit().beep();
return;
}
String strNew = str.substring(0, offset) + s + str.substring(offset, getLength());
if (notChange(strNew)) {
return;
}
setisContentChanged(true);
super.insertString(offset, s, a);
}
// kunsnat: 这种限制输入 有个不好的地方, 比如删除时: 10.1 最大值限定100, 那么就删除中间的小数点之后变为101, 超出了100.
// 但是直接限制不能删除中间类似小数点, 那么也可能遇到: 最小值10 , 从100变化到其中的19, 就很难..
private boolean notChange(String strNew) {
boolean noChange = false;
strNew = strNew.replaceFirst("-", StringUtils.EMPTY); // 控制能输入负数
boolean isMinus = strNew.startsWith("-");
String strIntPart;
String strDecPart = StringUtils.EMPTY;
int decPos = strNew.indexOf(CoreConstants.DOT);
if (decPos > -1) {
strIntPart = strNew.substring(0, decPos);
strDecPart = strNew.substring(decPos + 1);
} else {
strIntPart = strNew;
}
if (isOverMaxOrMinValue(strIntPart, strDecPart, strNew)) {
Toolkit.getDefaultToolkit().beep();
noChange = true;
}
try {
if (!ComparatorUtils.equals(strNew, StringUtils.EMPTY) && !ComparatorUtils.equals(strNew, "-")) {// 控制能输入负数
double d = Double.parseDouble(strNew) * (isMinus ? -1 : 1);
if (d < minValue || d > maxValue) {
throw new Exception();
}
}
} catch (Exception e) {
Toolkit.getDefaultToolkit().beep();
noChange = true;
}
return noChange;
}
private boolean isOverMaxOrMinValue(String strIntPart, String strDecPart, String strNew) {
boolean checkLength = strIntPart.length() > maxIntegerLength
|| strDecPart.length() > maxDecimalLength;
return checkLength || (strNew.length() > 1 && ComparatorUtils.equals(strNew.substring(0, 1), "0") && !ComparatorUtils.equals(strNew.substring(1, 2), "."));
}
}
/**
* 测试程序
*/
public static void main(String[] args) {
JFrame frame = new JFrame("");
frame.setSize(400, 320);
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
frame.setLocation((d.width - frame.getSize().width) / 2, (d.height - frame.getSize().height) / 2);
UINumberField tt = new UINumberField();
tt.setMinValue(0.0);
tt.setMaxValue(100.0);
frame.getContentPane().setLayout(new GridLayout(10, 2));
frame.getContentPane().add(new UILabel("New JNumberField()"));
frame.getContentPane().add(tt);
frame.getContentPane().add(new UILabel("New JNumberField(2)"));
frame.getContentPane().add(new UILabel("New JNumberField(8,2)"));
frame.getContentPane().add(new UINumberField(8, 2));
frame.getContentPane().add(new UILabel("New JNumberField(5,2,-10,100)"));
frame.getContentPane().add(new UINumberField(5, 2, TESTMINVALUE, TESTMAXVALUE));
frame.setVisible(true);
}
public static void main(String[] args) {
// JFrame frame = new JFrame("");
// frame.setSize(400, 320);
// Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
// frame.setLocation((d.width - frame.getSize().width) / 2, (d.height - frame.getSize().height) / 2);
// UINumberField tt = new UINumberField();
// tt.setMinValue(0.0);
// tt.setMaxValue(100.0);
// frame.getContentPane().setLayout(new GridLayout(10, 2));
// frame.getContentPane().add(new UILabel("New JNumberField()"));
// frame.getContentPane().add(tt);
// frame.getContentPane().add(new UILabel("New JNumberField(2)"));
// frame.getContentPane().add(new UILabel("New JNumberField(8,2)"));
// frame.getContentPane().add(new UINumberField(8, 2));
// frame.getContentPane().add(new UILabel("New JNumberField(5,2,-10,100)"));
// frame.getContentPane().add(new UINumberField(5, 2, TESTMINVALUE, TESTMAXVALUE));
// frame.setVisible(true);
}
}
Loading…
Cancel
Save