7 changed files with 531 additions and 513 deletions
@ -1,211 +1,192 @@ |
|||||||
/* |
/* |
||||||
* Copyright(c) 2001-2010, FineReport Inc, All Rights Reserved. |
* Copyright(c) 2001-2010, FineReport Inc, All Rights Reserved. |
||||||
*/ |
*/ |
||||||
package com.fr.design.gui.icombobox; |
package com.fr.design.gui.icombobox; |
||||||
|
|
||||||
import java.awt.Dimension; |
import com.fr.general.FRLogger; |
||||||
import java.util.ArrayList; |
import com.fr.general.Inter; |
||||||
import java.util.List; |
|
||||||
|
import javax.swing.DefaultComboBoxModel; |
||||||
import javax.swing.*; |
import javax.swing.SwingWorker; |
||||||
import javax.swing.event.DocumentEvent; |
import javax.swing.event.DocumentEvent; |
||||||
import javax.swing.event.DocumentListener; |
import javax.swing.event.DocumentListener; |
||||||
import javax.swing.event.PopupMenuEvent; |
import javax.swing.event.PopupMenuEvent; |
||||||
import javax.swing.event.PopupMenuListener; |
import javax.swing.event.PopupMenuListener; |
||||||
import javax.swing.plaf.basic.BasicComboPopup; |
import java.awt.Dimension; |
||||||
|
import java.util.concurrent.ExecutionException; |
||||||
import com.fr.general.Inter; |
|
||||||
|
/** |
||||||
/** |
* @author richer |
||||||
* @author richer |
* @version 2018年2月6日14点43分 by @yaoh.wu |
||||||
* @since 6.5.5 创建于2011-6-15 延迟加载的下拉框 |
* @since 6.5.5 创建于2011-6-15 延迟加载的下拉框 |
||||||
*/ |
*/ |
||||||
public abstract class LazyComboBox extends UIComboBox implements PopupMenuListener { |
public abstract class LazyComboBox extends UIComboBox implements PopupMenuListener { |
||||||
protected boolean loaded = false; |
|
||||||
private List<EventListener> ls = new ArrayList<EventListener>(); |
private static final int NUM = 80; |
||||||
private Object initialSelected = null; |
private static final String[] PENDING_CONTENT = new String[]{"", Inter.getLocText("Loading") + "..."}; |
||||||
private static final int NUM=80; |
|
||||||
|
/** |
||||||
public static final Object PENDING = new Object() { |
* 是否加载完成 |
||||||
|
*/ |
||||||
@Override |
protected boolean loaded = false; |
||||||
public String toString() { |
|
||||||
return Inter.getLocText("Loading") + "..."; |
/** |
||||||
} |
* 初始化选项 |
||||||
}; |
*/ |
||||||
|
private Object initialSelected = null; |
||||||
public LazyComboBox() { |
|
||||||
super(); |
|
||||||
this.setEditor(new FilterComboBoxEditor()); |
protected LazyComboBox() { |
||||||
addPopupMenuListener(this); |
super(); |
||||||
// updateUI();
|
this.setEditor(new FilterComboBoxEditor()); |
||||||
} |
addPopupMenuListener(this); |
||||||
|
} |
||||||
public void setLoaded(boolean loaded) { |
|
||||||
this.loaded = loaded; |
public void setLoaded(boolean loaded) { |
||||||
} |
this.loaded = loaded; |
||||||
|
} |
||||||
public abstract Object[] load(); |
|
||||||
|
/** |
||||||
public void setSelectedItem(Object anObject) { |
* 加载下拉框中的选项 |
||||||
initialSelected = anObject; |
* |
||||||
if (loaded) { |
* @return 下拉框中的选项 |
||||||
super.setSelectedItem(anObject); |
*/ |
||||||
} else { |
public abstract Object[] load(); |
||||||
|
|
||||||
setModel(new DefaultComboBoxModel(new Object[] { anObject })); |
@Override |
||||||
super.setSelectedItem(anObject); |
public void setSelectedItem(Object anObject) { |
||||||
} |
initialSelected = anObject; |
||||||
} |
if (loaded) { |
||||||
|
super.setSelectedItem(anObject); |
||||||
/** |
} else { |
||||||
* 通过调用该方法,在点击下拉框按钮之前就加载好数据,不需要出现loading了 |
this.setModel(new DefaultComboBoxModel<>(new Object[]{anObject})); |
||||||
*/ |
super.setSelectedItem(anObject); |
||||||
public void loadInstant() { |
} |
||||||
setLoaded(true); |
} |
||||||
loadList(); |
|
||||||
} |
@Override |
||||||
|
public void popupMenuWillBecomeVisible(PopupMenuEvent e) { |
||||||
@Override |
if (loaded) { |
||||||
public void popupMenuWillBecomeVisible(PopupMenuEvent e) { |
return; |
||||||
if (loaded) { |
} |
||||||
return; |
DefaultComboBoxModel<String> loadingModel = new DefaultComboBoxModel<>(PENDING_CONTENT); |
||||||
} |
this.setModel(loadingModel); |
||||||
DefaultComboBoxModel loadingModel = new DefaultComboBoxModel(new String[]{"", Inter.getLocText("Loading") + "..."}); |
new SwingWorker<Object[], Void>() { |
||||||
LazyComboBox.this.setModel(loadingModel); |
|
||||||
new SwingWorker<Void, Void>() { |
@Override |
||||||
|
protected Object[] doInBackground() { |
||||||
@Override |
return load(); |
||||||
protected Void doInBackground() throws Exception { |
} |
||||||
final Object selectedObj = getSelectedItem(); |
|
||||||
loadList(); |
@Override |
||||||
return null; |
public void done() { |
||||||
} |
try { |
||||||
|
LazyComboBox.this.loadList(get()); |
||||||
@Override |
} catch (InterruptedException | ExecutionException exception) { |
||||||
public void done() { |
FRLogger.getLogger().debug(exception.getMessage()); |
||||||
LazyComboBox.this.updateUI(); |
} |
||||||
LazyComboBox.this.fireEvent(); |
LazyComboBox.this.showPopup(); |
||||||
LazyComboBox.this.showPopup(); |
} |
||||||
} |
}.execute(); |
||||||
|
} |
||||||
}.execute(); |
|
||||||
|
|
||||||
|
/** |
||||||
} |
* 加载下拉列表 |
||||||
|
*/ |
||||||
/** |
public void loadList() { |
||||||
* 计算加载下拉列表 |
DefaultComboBoxModel<Object> model = new DefaultComboBoxModel<>(load()); |
||||||
*/ |
model.setSelectedItem(initialSelected); |
||||||
public void loadList() { |
this.setModel(model); |
||||||
DefaultComboBoxModel model = new DefaultComboBoxModel(load()); |
this.selectedItemReminder = initialSelected; |
||||||
model.setSelectedItem(initialSelected); |
loaded = true; |
||||||
LazyComboBox.this.setModel(model); |
} |
||||||
LazyComboBox.this.selectedItemReminder = initialSelected ; |
|
||||||
loaded = true; |
/** |
||||||
} |
* 加载下拉列表 |
||||||
|
* |
||||||
@Override |
* @param contents 下拉列表内容 |
||||||
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { |
*/ |
||||||
|
private void loadList(Object[] contents) { |
||||||
} |
DefaultComboBoxModel<Object> model = new DefaultComboBoxModel<>(contents); |
||||||
|
model.setSelectedItem(initialSelected); |
||||||
@Override |
this.setModel(model); |
||||||
public void popupMenuCanceled(PopupMenuEvent e) { |
this.selectedItemReminder = initialSelected; |
||||||
|
loaded = true; |
||||||
} |
} |
||||||
|
|
||||||
public void addClickListener(EventListener l) { |
@Override |
||||||
if (ls == null) { |
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { |
||||||
ls = new ArrayList<LazyComboBox.EventListener>(); |
|
||||||
} |
} |
||||||
ls.add(l); |
|
||||||
} |
@Override |
||||||
|
public void popupMenuCanceled(PopupMenuEvent e) { |
||||||
public void fireEvent() { |
|
||||||
for (int i = 0, n = ls.size(); i < n; i++) { |
} |
||||||
ls.get(i).fireEvent(); |
|
||||||
} |
@Override |
||||||
} |
public Dimension getPreferredSize() { |
||||||
|
Dimension dim = super.getPreferredSize(); |
||||||
@Override |
dim.width = NUM; |
||||||
public Dimension getPreferredSize() { |
return dim; |
||||||
Dimension dim = super.getPreferredSize(); |
} |
||||||
dim.width = NUM; |
|
||||||
return dim; |
class FilterComboBoxEditor extends UIComboBoxEditor implements DocumentListener { |
||||||
} |
private Object item; |
||||||
|
private volatile boolean filtering = false; |
||||||
private static class LazyPopMenu extends BasicComboPopup { |
private volatile boolean setting = false; |
||||||
|
|
||||||
public LazyPopMenu(final JComboBox combo) { |
public FilterComboBoxEditor() { |
||||||
super(combo); |
super(); |
||||||
LazyComboBox comboc = (LazyComboBox) combo; |
textField.getDocument().addDocumentListener(this); |
||||||
comboc.addClickListener(new EventListener() { |
} |
||||||
|
|
||||||
@Override |
@Override |
||||||
public void fireEvent() { |
public void setItem(Object item) { |
||||||
LazyPopMenu.this.show(); |
if (filtering) { |
||||||
combo.showPopup(); |
return; |
||||||
} |
} |
||||||
}); |
this.item = item; |
||||||
} |
this.setting = true; |
||||||
} |
textField.setSetting(true); |
||||||
|
String newText = (item == null) ? "" : item.toString(); |
||||||
private interface EventListener { |
textField.setText(newText); |
||||||
void fireEvent(); |
textField.setSetting(false); |
||||||
} |
this.setting = false; |
||||||
|
} |
||||||
class FilterComboBoxEditor extends UIComboBoxEditor implements DocumentListener { |
|
||||||
private Object item; |
@Override |
||||||
private volatile boolean filtering = false; |
public Object getItem() { |
||||||
private volatile boolean setting = false; |
return this.item; |
||||||
|
} |
||||||
public FilterComboBoxEditor() { |
|
||||||
super(); |
@Override |
||||||
textField.getDocument().addDocumentListener(this); |
public void insertUpdate(DocumentEvent e) { |
||||||
} |
handleChange(); |
||||||
|
} |
||||||
public void setItem(Object item) { |
|
||||||
if (filtering) { |
@Override |
||||||
return; |
public void removeUpdate(DocumentEvent e) { |
||||||
} |
handleChange(); |
||||||
this.item = item; |
} |
||||||
|
|
||||||
this.setting = true; |
@Override |
||||||
textField.setSetting(true); |
public void changedUpdate(DocumentEvent e) { |
||||||
String newText = (item == null) ? "" : item.toString(); |
handleChange(); |
||||||
textField.setText(newText); |
} |
||||||
textField.setSetting(false); |
|
||||||
this.setting = false; |
void handleChange() { |
||||||
} |
if (setting) { |
||||||
|
return; |
||||||
public Object getItem() { |
} |
||||||
return this.item; |
filtering = true; |
||||||
} |
String xx = textField.getText(); |
||||||
|
LazyComboBox.this.setSelectedItem(xx); |
||||||
public void insertUpdate(DocumentEvent e) { |
this.item = textField.getText(); |
||||||
handleChange(); |
|
||||||
} |
setPopupVisible(true); |
||||||
|
filtering = false; |
||||||
public void removeUpdate(DocumentEvent e) { |
} |
||||||
handleChange(); |
} |
||||||
} |
|
||||||
|
|
||||||
public void changedUpdate(DocumentEvent e) { |
|
||||||
handleChange(); |
|
||||||
} |
|
||||||
|
|
||||||
protected void handleChange() { |
|
||||||
if (setting) { |
|
||||||
return; |
|
||||||
} |
|
||||||
filtering = true; |
|
||||||
String xx = textField.getText(); |
|
||||||
LazyComboBox.this.setSelectedItem(xx); |
|
||||||
this.item = textField.getText(); |
|
||||||
|
|
||||||
setPopupVisible(true); |
|
||||||
filtering = false; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
} |
@ -1,214 +1,198 @@ |
|||||||
package com.fr.design.gui.icombobox; |
package com.fr.design.gui.icombobox; |
||||||
|
|
||||||
import com.fr.design.event.GlobalNameListener; |
import com.fr.design.event.GlobalNameListener; |
||||||
import com.fr.design.event.GlobalNameObserver; |
import com.fr.design.event.GlobalNameObserver; |
||||||
import com.fr.design.event.UIObserver; |
import com.fr.design.event.UIObserver; |
||||||
import com.fr.design.event.UIObserverListener; |
import com.fr.design.event.UIObserverListener; |
||||||
import com.fr.design.utils.gui.GUICoreUtils; |
|
||||||
|
import javax.swing.ComboBoxModel; |
||||||
import javax.swing.*; |
import javax.swing.JComboBox; |
||||||
import javax.swing.plaf.ComboBoxUI; |
import javax.swing.ListCellRenderer; |
||||||
import javax.swing.plaf.basic.ComboPopup; |
import javax.swing.plaf.ComboBoxUI; |
||||||
import java.awt.*; |
import javax.swing.plaf.basic.ComboPopup; |
||||||
import java.awt.event.FocusAdapter; |
import java.awt.Dimension; |
||||||
import java.awt.event.FocusEvent; |
import java.awt.event.FocusAdapter; |
||||||
import java.awt.event.ItemEvent; |
import java.awt.event.FocusEvent; |
||||||
import java.awt.event.ItemListener; |
import java.awt.event.ItemEvent; |
||||||
import java.util.Vector; |
import java.awt.event.ItemListener; |
||||||
|
import java.util.Vector; |
||||||
/** |
|
||||||
* august:非常beautiful的ComboBox,不支持编辑状态. 内容过长时,鼠标移动过去会有ToolTips,不会有横向滚动条 |
/** |
||||||
* 假如支持编辑,因为UIComboBox的TextField 的绘制 并不是靠Renderer来控制 , |
* august:非常beautiful的ComboBox,不支持编辑状态. 内容过长时,鼠标移动过去会有ToolTips,不会有横向滚动条 |
||||||
* 它会通过paintCurrentValueBackground()来绘制背景, |
* 假如支持编辑,因为UIComboBox的TextField 的绘制 并不是靠Renderer来控制 , |
||||||
* 然后通过paintCurrentValue(),去绘制UIComboBox里显示的值。所考虑情况比现在复杂多多多多多了,所以暂时不支持 |
* 它会通过paintCurrentValueBackground()来绘制背景, |
||||||
* 另外,项的内容最好不要有图标 |
* 然后通过paintCurrentValue(),去绘制UIComboBox里显示的值。所考虑情况比现在复杂多多多多多了,所以暂时不支持 |
||||||
* |
* 另外,项的内容最好不要有图标 |
||||||
* @author zhou |
* |
||||||
* @since 2012-5-9下午3:18:58 |
* @author zhou |
||||||
*/ |
* @since 2012-5-9下午3:18:58 |
||||||
public class UIComboBox extends JComboBox implements UIObserver, GlobalNameObserver { |
*/ |
||||||
|
public class UIComboBox extends JComboBox implements UIObserver, GlobalNameObserver { |
||||||
/** |
|
||||||
* |
/** |
||||||
*/ |
* |
||||||
private static final long serialVersionUID = 1L; |
*/ |
||||||
|
private static final long serialVersionUID = 1L; |
||||||
private static final int SIZE = 20; |
|
||||||
|
private static final int SIZE = 20; |
||||||
private static final int SIZE5 = 5; |
|
||||||
|
private static final int SIZE5 = 5; |
||||||
protected UIObserverListener uiObserverListener; |
|
||||||
|
protected UIObserverListener uiObserverListener; |
||||||
private String comboBoxName = ""; |
|
||||||
|
private String comboBoxName = ""; |
||||||
private GlobalNameListener globalNameListener = null; |
|
||||||
|
private GlobalNameListener globalNameListener = null; |
||||||
public UIComboBox() { |
|
||||||
super(); |
public UIComboBox() { |
||||||
init(); |
super(); |
||||||
} |
init(); |
||||||
|
} |
||||||
public UIComboBox(ComboBoxModel model) { |
|
||||||
super(model); |
public UIComboBox(ComboBoxModel model) { |
||||||
init(); |
super(model); |
||||||
} |
init(); |
||||||
|
} |
||||||
public UIComboBox(Object[] items) { |
|
||||||
super(items); |
public UIComboBox(Object[] items) { |
||||||
init(); |
super(items); |
||||||
} |
init(); |
||||||
|
} |
||||||
public UIComboBox(Vector<?> items) { |
|
||||||
super(items); |
public UIComboBox(Vector<?> items) { |
||||||
init(); |
super(items); |
||||||
} |
init(); |
||||||
|
} |
||||||
private void init() { |
|
||||||
setOpaque(false); |
private void init() { |
||||||
setUI(getUIComboBoxUI()); |
setOpaque(false); |
||||||
setRenderer(new UIComboBoxRenderer()); |
setUI(getUIComboBoxUI()); |
||||||
setEditor(new UIComboBoxEditor()); |
setRenderer(new UIComboBoxRenderer()); |
||||||
initListener(); |
setEditor(new UIComboBoxEditor()); |
||||||
} |
initListener(); |
||||||
|
} |
||||||
protected void initListener() { |
|
||||||
if (shouldResponseChangeListener()) { |
protected void initListener() { |
||||||
this.addFocusListener(new FocusAdapter() { |
if (shouldResponseChangeListener()) { |
||||||
@Override |
this.addFocusListener(new FocusAdapter() { |
||||||
public void focusGained(FocusEvent e) { |
@Override |
||||||
fireSetGlobalName(); |
public void focusGained(FocusEvent e) { |
||||||
} |
fireSetGlobalName(); |
||||||
}); |
} |
||||||
this.addItemListener(new ItemListener() { |
}); |
||||||
@Override |
this.addItemListener(new ItemListener() { |
||||||
public void itemStateChanged(ItemEvent e) { |
@Override |
||||||
if (uiObserverListener == null) { |
public void itemStateChanged(ItemEvent e) { |
||||||
return; |
if (uiObserverListener == null) { |
||||||
} |
return; |
||||||
fireSetGlobalName(); |
} |
||||||
if (e.getStateChange() == ItemEvent.SELECTED) { |
fireSetGlobalName(); |
||||||
uiObserverListener.doChange(); |
if (e.getStateChange() == ItemEvent.SELECTED) { |
||||||
} |
uiObserverListener.doChange(); |
||||||
} |
} |
||||||
}); |
} |
||||||
} |
}); |
||||||
} |
} |
||||||
|
} |
||||||
protected void fireSetGlobalName() { |
|
||||||
if (globalNameListener != null && shouldResponseNameListener()) { |
protected void fireSetGlobalName() { |
||||||
globalNameListener.setGlobalName(comboBoxName); |
if (globalNameListener != null && shouldResponseNameListener()) { |
||||||
} |
globalNameListener.setGlobalName(comboBoxName); |
||||||
} |
} |
||||||
|
} |
||||||
|
|
||||||
protected ComboBoxUI getUIComboBoxUI() { |
|
||||||
return new UIComboBoxUI(); |
protected ComboBoxUI getUIComboBoxUI() { |
||||||
} |
return new UIComboBoxUI(); |
||||||
|
} |
||||||
/** |
|
||||||
* 只允许设置为UIComboBoxRenderer,所以要继承UIComboBoxRenderer |
/** |
||||||
*/ |
* 只允许设置为UIComboBoxRenderer,所以要继承UIComboBoxRenderer |
||||||
@Override |
*/ |
||||||
public void setRenderer(ListCellRenderer aRenderer) { |
@Override |
||||||
if (aRenderer instanceof UIComboBoxRenderer) { |
public void setRenderer(ListCellRenderer aRenderer) { |
||||||
super.setRenderer(aRenderer); |
if (aRenderer instanceof UIComboBoxRenderer) { |
||||||
} else { |
super.setRenderer(aRenderer); |
||||||
//throw new IllegalArgumentException("Must be UIComboBoxRenderer");
|
} |
||||||
} |
} |
||||||
} |
|
||||||
|
protected ComboPopup createPopup() { |
||||||
protected ComboPopup createPopup() { |
return null; |
||||||
return null; |
} |
||||||
} |
|
||||||
|
@Override |
||||||
public void setGlobalName(String name) { |
public void setGlobalName(String name) { |
||||||
comboBoxName = name; |
comboBoxName = name; |
||||||
} |
} |
||||||
|
|
||||||
@Override |
@Override |
||||||
public Dimension getPreferredSize() { |
public Dimension getPreferredSize() { |
||||||
return new Dimension(super.getPreferredSize().width + SIZE5, SIZE);//加5的原因在于:render里,每一个项前面了空了一格,要多几像素
|
//加5的原因在于:render里,每一个项前面了空了一格,要多几像素
|
||||||
} |
return new Dimension(super.getPreferredSize().width + SIZE5, SIZE); |
||||||
|
} |
||||||
/** |
|
||||||
* 鼠标进入事件 |
/** |
||||||
*/ |
* 鼠标进入事件 |
||||||
public void mouseEnterEvent() { |
*/ |
||||||
|
public void mouseEnterEvent() { |
||||||
} |
|
||||||
|
} |
||||||
/** |
|
||||||
* 鼠标离开事件 |
/** |
||||||
*/ |
* 鼠标离开事件 |
||||||
public void mouseExitEvent() { |
*/ |
||||||
|
public void mouseExitEvent() { |
||||||
} |
|
||||||
|
} |
||||||
/** |
|
||||||
* |
/** |
||||||
*/ |
* |
||||||
public void updateUI() { |
*/ |
||||||
setUI(getUIComboBoxUI()); |
@Override |
||||||
} |
public void updateUI() { |
||||||
|
setUI(getUIComboBoxUI()); |
||||||
|
} |
||||||
/** |
|
||||||
* |
|
||||||
* @param listener 观察者监听事件 |
/** |
||||||
*/ |
* @param listener 观察者监听事件 |
||||||
public void registerChangeListener(UIObserverListener listener) { |
*/ |
||||||
uiObserverListener = listener; |
@Override |
||||||
} |
public void registerChangeListener(UIObserverListener listener) { |
||||||
|
uiObserverListener = listener; |
||||||
public void removeChangeListener(){ |
} |
||||||
uiObserverListener = null; |
|
||||||
} |
public void removeChangeListener() { |
||||||
|
uiObserverListener = null; |
||||||
public UIObserverListener getUiObserverListener(){ |
} |
||||||
return uiObserverListener; |
|
||||||
} |
public UIObserverListener getUiObserverListener() { |
||||||
|
return uiObserverListener; |
||||||
/** |
} |
||||||
* @return |
|
||||||
*/ |
/** |
||||||
public boolean shouldResponseChangeListener() { |
* @return 是否响应变更事件 |
||||||
return true; |
*/ |
||||||
} |
@Override |
||||||
|
public boolean shouldResponseChangeListener() { |
||||||
/** |
return true; |
||||||
* |
} |
||||||
* @param listener 观察者监听事件 |
|
||||||
*/ |
/** |
||||||
public void registerNameListener(GlobalNameListener listener) { |
* @param listener 观察者监听事件 |
||||||
globalNameListener = listener; |
*/ |
||||||
} |
@Override |
||||||
|
public void registerNameListener(GlobalNameListener listener) { |
||||||
/** |
globalNameListener = listener; |
||||||
* |
} |
||||||
* @return |
|
||||||
*/ |
/** |
||||||
public boolean shouldResponseNameListener() { |
* @return 是否响应名称事件 |
||||||
return true; |
*/ |
||||||
} |
@Override |
||||||
|
public boolean shouldResponseNameListener() { |
||||||
|
return true; |
||||||
/** |
} |
||||||
* @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); |
|
||||||
UIComboBox bb = new UIComboBox(new String[]{"", "jerry", "kunsnat", "richer"}); |
|
||||||
bb.setEditable(true); |
|
||||||
bb.setBounds(20, 20, bb.getPreferredSize().width, bb.getPreferredSize().height); |
|
||||||
content.add(bb); |
|
||||||
GUICoreUtils.centerWindow(jf); |
|
||||||
jf.setSize(400, 400); |
|
||||||
jf.setVisible(true); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
} |
Loading…
Reference in new issue