1 package fr.ifremer.quadrige3.ui.swing;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 import com.google.common.collect.Lists;
27 import com.google.common.collect.Maps;
28 import com.google.common.collect.Sets;
29 import fr.ifremer.quadrige3.core.config.QuadrigeCoreConfiguration;
30 import fr.ifremer.quadrige3.core.dao.technical.Assert;
31 import fr.ifremer.quadrige3.core.dao.technical.decorator.Decorator;
32 import fr.ifremer.quadrige3.core.service.ClientServiceLocator;
33 import fr.ifremer.quadrige3.core.service.decorator.DecoratorService;
34 import fr.ifremer.quadrige3.ui.swing.action.AbstractChangeScreenAction;
35 import fr.ifremer.quadrige3.ui.swing.action.ActionUI;
36 import fr.ifremer.quadrige3.ui.swing.component.ActionComboBoxModel;
37 import fr.ifremer.quadrige3.ui.swing.component.ActionListCellRenderer;
38 import fr.ifremer.quadrige3.ui.swing.component.ColoredFilteredListCellRenderer;
39 import fr.ifremer.quadrige3.ui.swing.component.ExtendedDecoratorListCellRenderer;
40 import fr.ifremer.quadrige3.ui.swing.component.bean.ExtendedBeanDoubleList;
41 import fr.ifremer.quadrige3.ui.swing.component.date.*;
42 import fr.ifremer.quadrige3.ui.swing.component.number.NumberEditor;
43 import fr.ifremer.quadrige3.ui.swing.component.time.LocalTimeEditor;
44 import fr.ifremer.quadrige3.ui.swing.content.AbstractMainUIHandler;
45 import fr.ifremer.quadrige3.ui.swing.content.MainUI;
46 import fr.ifremer.quadrige3.ui.swing.model.AbstractBeanUIModel;
47 import fr.ifremer.quadrige3.ui.swing.plaf.HintSynthTextFieldUI;
48 import jaxx.runtime.swing.editor.bean.BeanDoubleList;
49 import jaxx.runtime.swing.editor.bean.BeanFilterableComboBox;
50 import jaxx.runtime.swing.model.JaxxFilterableListModel;
51 import jaxx.runtime.swing.renderer.FilteredDecoratorListCellRenderer;
52 import jaxx.runtime.validator.swing.SwingValidator;
53 import org.apache.commons.collections4.CollectionUtils;
54 import org.apache.commons.lang3.StringUtils;
55 import org.apache.commons.lang3.time.DateUtils;
56 import org.apache.commons.logging.Log;
57 import org.apache.commons.logging.LogFactory;
58 import org.jdesktop.jxlayer.JXLayer;
59 import org.jdesktop.swingx.JXDatePicker;
60 import org.jdesktop.swingx.JXPanel;
61 import org.nuiton.decorator.MultiJXPathDecorator;
62 import org.nuiton.jaxx.application.bean.JavaBeanObjectUtil;
63 import org.nuiton.jaxx.application.swing.AbstractApplicationUIHandler;
64 import org.nuiton.jaxx.application.swing.action.ApplicationActionUIHandler;
65 import org.nuiton.jaxx.application.swing.action.ApplicationUIAction;
66 import org.nuiton.jaxx.application.swing.util.Cancelable;
67 import org.nuiton.validator.bean.simple.SimpleBeanValidator;
68
69 import javax.swing.*;
70 import javax.swing.event.DocumentEvent;
71 import javax.swing.event.DocumentListener;
72 import javax.swing.event.ListDataEvent;
73 import javax.swing.event.ListDataListener;
74 import javax.swing.plaf.nimbus.NimbusLookAndFeel;
75 import javax.swing.text.AbstractDocument;
76 import javax.swing.text.JTextComponent;
77 import java.awt.*;
78 import java.awt.event.*;
79 import java.beans.PropertyChangeEvent;
80 import java.beans.PropertyChangeListener;
81 import java.io.File;
82 import java.io.Serializable;
83 import java.time.LocalDate;
84 import java.time.LocalTime;
85 import java.util.List;
86 import java.util.*;
87
88 import static org.nuiton.i18n.I18n.t;
89
90
91
92
93
94
95
96
97
98 public abstract class AbstractUIHandler<M, UI extends ApplicationUI<M, ?>>
99 extends AbstractApplicationUIHandler<M, UI>
100 implements UIMessageNotifier {
101
102
103
104
105 private static final Log LOG = LogFactory.getLog(AbstractUIHandler.class);
106
107 private final Map<AbstractBeanUIModel, PropertyChangeListener> modelModifyListenerMap = Maps.newHashMap();
108 private final Map<SimpleBeanValidator, PropertyChangeListener> validatorValidListenerMap = Maps.newHashMap();
109
110
111 private Map<JComponent, Container> previousBodyContentByFullScreenPanel = new HashMap<>();
112
113 private Map<JComponent, Container> previousParentByFullScreenPanel = new HashMap<>();
114
115
116
117
118
119
120
121 @SuppressWarnings("unchecked")
122 public void toggleFullScreen(JPanel panel, JToggleButton toggleButton) {
123
124 if (toggleButton.isSelected()) {
125
126 Assert.isTrue(!previousBodyContentByFullScreenPanel.containsKey(panel) && !previousParentByFullScreenPanel.containsKey(panel), "this panel is already full screen");
127 Assert.isTrue(SwingUtilities.isDescendingFrom(toggleButton, panel), "the toggle full screen button must be inside the panel to full screen");
128 Assert.notNull(panel.getParent(), "the panel to full screen has no parent");
129
130 previousParentByFullScreenPanel.put(panel, panel.getParent());
131 previousBodyContentByFullScreenPanel.put(panel, getContext().getMainUI().getBody().getContentContainer());
132
133 getContext().getMainUI().getBody().setContentContainer(panel);
134
135 } else {
136
137 Assert.isTrue(previousBodyContentByFullScreenPanel.containsKey(panel), "this full screen panel has no previous body content");
138 Assert.isTrue(previousParentByFullScreenPanel.containsKey(panel), "this full screen panel has no previous parent");
139
140 getContext().getMainUI().getBody().setContentContainer(previousBodyContentByFullScreenPanel.remove(panel));
141
142 Container previousParent = previousParentByFullScreenPanel.remove(panel);
143
144 if (previousParent instanceof JLayer) {
145 ((JLayer<JPanel>) previousParent).setView(panel);
146 } else if (previousParent instanceof JXLayer) {
147 ((JXLayer<JPanel>) previousParent).setView(panel);
148 } else {
149
150 previousParent.add(panel);
151 }
152
153 }
154
155 getContext().getMainUI().revalidate();
156 getContext().getMainUI().repaint();
157 }
158
159
160
161
162
163
164
165
166
167
168 public void changeScreenAction(Class<? extends AbstractChangeScreenAction> screenActionClass) {
169 ApplicationUIAction<? extends AbstractChangeScreenAction> screenAction;
170 screenAction = getContext().getActionFactory().createUIAction(getContext().getMainUI().getHandler(), screenActionClass);
171 getContext().getActionEngine().runAction(screenAction.getLogicAction());
172 }
173
174
175
176
177 @Override
178 public void showInformationMessage(String message) {
179 getContext().showInformationMessage(message);
180 }
181
182
183
184
185 public void showNotImplementedFunctionality() {
186 getContext().getDialogHelper().showMessageDialog(t("quadrige3.error.notImplemented"));
187 }
188
189
190
191
192 @Override
193 public ApplicationUIContext getContext() {
194 return (ApplicationUIContext) super.getContext();
195 }
196
197
198
199
200
201
202 public String getTitle() {
203 return t("quadrige3.screen." + getUI().getClass().getSimpleName() + ".title");
204 }
205
206
207
208
209
210
211 public QuadrigeCoreConfiguration getConfig() {
212 return getContext().getConfiguration();
213 }
214
215
216
217
218 @Override
219 public Component getTopestUI() {
220 return getContext().getActionUI();
221 }
222
223
224
225
226 @Override
227 public void onCloseUI() {
228
229 if (LOG.isDebugEnabled()) {
230 LOG.debug("closing: " + getUI());
231 }
232 }
233
234
235
236
237 public void clearValidators() {
238 MainUI main = getContext().getMainUI();
239 Assert.notNull(main, "No mainUI registered in application context");
240 AbstractMainUIHandler handler = (AbstractMainUIHandler) main.getHandler();
241 handler.clearValidators();
242 }
243
244
245
246
247 @Override
248 protected JComponent getComponentToFocus() {
249 return null;
250 }
251
252
253
254
255 @Override
256 public SwingValidator<M> getValidator() {
257 return null;
258 }
259
260
261
262
263 public void afterViewInit() {
264
265 if (getUI() instanceof Container) {
266 propagateAfterViewInit((Container) getUI());
267 }
268 }
269
270 private void propagateAfterViewInit(Container container) {
271 for (Component component : container.getComponents()) {
272 if (component instanceof ApplicationUI) {
273 ((ApplicationUI) component).getHandler().afterViewInit();
274 } else if (component instanceof Container) {
275 propagateAfterViewInit((Container) component);
276 }
277 }
278 }
279
280
281
282
283 public void forceRevalidateModel() {
284
285 if (getValidator() != null) {
286 getValidator().doValidate();
287 }
288 if (getModel() instanceof AbstractBeanUIModel) {
289 ((AbstractBeanUIModel) getModel()).firePropertyChanged(AbstractBeanUIModel.PROPERTY_VALID, null, null);
290 }
291 }
292
293
294
295
296 @Override
297 public <O> Decorator<O> getDecorator(Class<O> type, String name) {
298 DecoratorService decoratorService = ClientServiceLocator.instance().getDecoratorService();
299
300 Assert.notNull(type);
301
302 return decoratorService.getDecoratorByType(type, name);
303 }
304
305
306
307
308 @Override
309 public String decorate(Serializable object, String context) {
310 return super.decorate(object, context);
311 }
312
313
314
315
316 @Override
317 public String decorate(Serializable object) {
318 return super.decorate(object);
319 }
320
321
322
323
324
325
326 protected void listenModelModify(AbstractBeanUIModel model) {
327 Assert.isTrue(getModel() != model, "model can't listen to itself");
328
329 stopListenModelModify(model);
330
331 PropertyChangeListener listener = evt -> {
332 Boolean modify = (Boolean) evt.getNewValue();
333 if (modify != null) {
334 ((AbstractBeanUIModel) getModel()).setModify(modify);
335 }
336 };
337
338 model.addPropertyChangeListener(AbstractBeanUIModel.PROPERTY_MODIFY, listener);
339 modelModifyListenerMap.put(model, listener);
340 }
341
342
343
344
345
346
347 protected void stopListenModelModify(AbstractBeanUIModel model) {
348 PropertyChangeListener listener = modelModifyListenerMap.get(model);
349 if (listener != null) {
350 model.removePropertyChangeListener(AbstractBeanUIModel.PROPERTY_MODIFY, listener);
351 modelModifyListenerMap.remove(model);
352 }
353 }
354
355
356
357
358
359
360 protected void listenModelValid(AbstractBeanUIModel model) {
361 model.addPropertyChangeListener(AbstractBeanUIModel.PROPERTY_VALID, evt -> {
362 Boolean valid = (Boolean) evt.getNewValue();
363 if (valid != null) {
364 ((AbstractBeanUIModel) getModel()).setValid(valid);
365 }
366 });
367 }
368
369
370
371
372
373
374
375 public void setEnabled(Container container, boolean enabled) {
376 Component[] components = container.getComponents();
377 for (Component component : components) {
378 component.setEnabled(enabled);
379 if (component instanceof Container) {
380 setEnabled((Container) component, enabled);
381 }
382 }
383 }
384
385
386
387
388 @Override
389 public void setDate(ActionEvent event, String property) {
390 Date value = null;
391 if (event.getSource() instanceof JXDatePicker) {
392 value = ((JXDatePicker) event.getSource()).getDate();
393 } else if (event.getSource() instanceof JDatePanel) {
394 value = ((JDatePanel) event.getSource()).getDate();
395 } else if (event.getSource() instanceof JDatePicker) {
396 value = ((JDatePicker) event.getSource()).getDate();
397 }
398
399 Date date = (Date) JavaBeanObjectUtil.getProperty(getModel(), property);
400 if (value != null && date != null) {
401 Calendar cal = DateUtils.toCalendar(date);
402 value = DateUtils.setHours(value, cal.get(Calendar.HOUR_OF_DAY));
403 value = DateUtils.setMinutes(value, cal.get(Calendar.MINUTE));
404 value = DateUtils.setSeconds(value, cal.get(Calendar.SECOND));
405 }
406 JavaBeanObjectUtil.setProperty(getModel(), property, value);
407 }
408
409 public void setLocalDate(ActionEvent event, String property) {
410 LocalDate value = null;
411 if (event.getSource() instanceof JLocalDatePanel) {
412 value = ((JLocalDatePanel) event.getSource()).getLocalDate();
413 } else if (event.getSource() instanceof JLocalDatePicker) {
414 value = ((JLocalDatePicker) event.getSource()).getLocalDate();
415 }
416 JavaBeanObjectUtil.setProperty(getModel(), property, value);
417 }
418
419 public void setLocalTime(ActionEvent event, String property) {
420 LocalTime value = null;
421 if (event.getSource() instanceof LocalTimeEditor) {
422 value = ((LocalTimeEditor) event.getSource()).getLocalTime();
423 }
424
425 JavaBeanObjectUtil.setProperty(this.getModel(), property, value);
426 }
427
428 public void setTimeInSecond(ActionEvent event, String property) {
429 Integer value = null;
430 if (event.getSource() instanceof LocalTimeEditor) {
431 value = ((LocalTimeEditor) event.getSource()).getTimeInSecond();
432 }
433
434 JavaBeanObjectUtil.setProperty(this.getModel(), property, value);
435 }
436
437
438
439
440
441
442
443 @Override
444 public void setText(KeyEvent event, String property) {
445 JTextComponent field = (JTextComponent) event.getSource();
446 String value = field.getText();
447 if (value != null && value.startsWith(" ")) {
448
449 value = value.trim();
450 field.setText(value);
451 }
452 JavaBeanObjectUtil.setProperty(getModel(), property, value);
453 event.consume();
454 }
455
456
457
458
459
460
461
462
463 @Override
464 protected void initUIComponent(Object component) {
465 super.initUIComponent(component);
466
467 if (component instanceof NumberEditor) {
468 initNumberEditor((NumberEditor) component);
469
470 } else if (component instanceof JDatePicker) {
471 initDatePicker((JDatePicker) component);
472
473 } else if (component instanceof JLocalDatePicker) {
474 initLocalDatePicker((JLocalDatePicker) component);
475 }
476 }
477
478 private void initLocalDatePicker(JLocalDatePicker datePicker) {
479
480 String datePattern = getContext().getDateFormat();
481 datePicker.setDateFormat(datePattern, ApplicationUIUtil.getDefault2DigitYearStart());
482
483
484 datePicker.setColor(AbstractComponentColor.Key.FG_MONTH_SELECTOR, Color.black);
485 datePicker.setColor(AbstractComponentColor.Key.BG_MONTH_SELECTOR, UIManager.getColor("background"));
486 datePicker.setColor(AbstractComponentColor.Key.FG_GRID_HEADER, UIManager.getColor("thematicLabelColor"));
487 datePicker.setColor(AbstractComponentColor.Key.BG_GRID_SELECTED, UIManager.getColor("nimbusSelectionBackground"));
488 datePicker.setColor(AbstractComponentColor.Key.BG_GRID_TODAY_SELECTED, UIManager.getColor("nimbusSelectionBackground"));
489
490 datePicker.setToolTipText(t("jaxx.application.common.datefield.tip", datePattern));
491 datePicker.setTextEditable(true);
492 datePicker.setShowYearButtons(true);
493
494 if (isAutoSelectOnFocus(datePicker)) {
495 addAutoSelectOnFocus(datePicker.getEditor());
496 }
497
498 }
499
500 private void initDatePicker(JDatePicker datePicker) {
501
502 String datePattern = getContext().getDateFormat();
503 datePicker.setDateFormat(datePattern, ApplicationUIUtil.getDefault2DigitYearStart());
504
505
506 datePicker.setColor(AbstractComponentColor.Key.FG_MONTH_SELECTOR, Color.black);
507 datePicker.setColor(AbstractComponentColor.Key.BG_MONTH_SELECTOR, UIManager.getColor("background"));
508 datePicker.setColor(AbstractComponentColor.Key.FG_GRID_HEADER, UIManager.getColor("thematicLabelColor"));
509 datePicker.setColor(AbstractComponentColor.Key.BG_GRID_SELECTED, UIManager.getColor("nimbusSelectionBackground"));
510 datePicker.setColor(AbstractComponentColor.Key.BG_GRID_TODAY_SELECTED, UIManager.getColor("nimbusSelectionBackground"));
511
512 datePicker.setToolTipText(t("jaxx.application.common.datefield.tip", datePattern));
513 datePicker.setTextEditable(true);
514 datePicker.setShowYearButtons(true);
515
516 if (isAutoSelectOnFocus(datePicker)) {
517 addAutoSelectOnFocus(datePicker.getEditor());
518 }
519
520 }
521
522 private void initNumberEditor(NumberEditor numberEditor) {
523
524
525 numberEditor.init();
526
527
528 numberEditor.getNumber0().setText(t("numbereditor.0"));
529 }
530
531
532
533
534 @Override
535 protected void initTextField(JTextField jTextField) {
536 super.initTextField(jTextField);
537
538 String hint = (String) jTextField.getClientProperty("hint");
539 if (hint != null && UIManager.getLookAndFeel() instanceof NimbusLookAndFeel) {
540 jTextField.setUI(new HintSynthTextFieldUI(hint, false));
541 }
542 }
543
544
545
546
547 @Override
548 protected void initButton(AbstractButton abstractButton) {
549 super.initButton(abstractButton);
550
551
552 abstractButton.getInputMap().put(KeyStroke.getKeyStroke("pressed ENTER"), "pressed");
553 abstractButton.getInputMap().put(KeyStroke.getKeyStroke("released ENTER"), "released");
554
555
556 Class actionName = (Class) abstractButton.getClientProperty("applicationWorkerAction");
557 if (actionName != null) {
558 if (abstractButton.getAction() == null) {
559 @SuppressWarnings("unchecked") Action action = getContext().getActionFactory().createNonBlockingUIAction(this, actionName, abstractButton);
560 abstractButton.setAction(action);
561 } else {
562 if (LOG.isErrorEnabled()) {
563 LOG.error(String.format("button '%s' has already an action. the action '%s' will be ignored", abstractButton.getName(), actionName.getName()));
564 }
565 }
566 }
567 }
568
569
570
571
572 @Override
573 protected <E> void initBeanList(BeanDoubleList<E> list, List<E> data, List<E> selectedData) {
574 initBeanList(list, data, selectedData, -1);
575 }
576
577
578
579
580 @Override
581 @Deprecated
582 protected <E> void initBeanList(BeanDoubleList<E> list, List<E> data, List<E> selectedData, org.nuiton.decorator.Decorator<E> selectedDecorator) {
583 if (selectedDecorator instanceof Decorator) {
584 initBeanList(list, data, selectedData, (Decorator<E>) selectedDecorator);
585 }
586 LOG.warn("Use of deprecated method : fr.ifremer.quadrige3.ui.swing.AbstractUIHandler.initBeanList(jaxx.runtime.swing.editor.bean.BeanDoubleList<E>, java.util.List<E>, java.util.List<E>, org.nuiton.decorator.Decorator<E>)");
587 super.initBeanList(list, data, selectedData, selectedDecorator);
588 }
589
590 protected <E> void initBeanList(BeanDoubleList<E> list, List<E> data, List<E> selectedData, Decorator<E> selectedDecorator) {
591 initBeanList(list, data, selectedData, -1);
592 }
593
594
595
596
597
598
599
600
601
602
603 @SuppressWarnings("unchecked")
604 protected <E> void initBeanList(final BeanDoubleList<E> list, List<E> data, List<E> selectedData, int preferredWidth) {
605
606 Assert.notNull(list, "No list!");
607
608 Class<E> beanType = list.getBeanType();
609 Assert.notNull(beanType, "No beanType on the double list!");
610
611 String decoratorContext = null;
612 if (list instanceof ExtendedBeanDoubleList) {
613 decoratorContext = ((ExtendedBeanDoubleList) list).getDecoratorContext();
614 }
615
616 Decorator<E> decorator = getDecorator(beanType, decoratorContext);
617
618 if (CollectionUtils.isNotEmpty(data)) {
619 if (decorator != null) {
620 data.sort(decorator.getCurrentComparator());
621 if (selectedData != null) {
622 selectedData.sort(decorator.getCurrentComparator());
623 }
624 }
625 }
626
627 if (LOG.isDebugEnabled()) {
628 LOG.debug("entity list [" + beanType.getName() + "] : " + (data == null ? 0 : data.size()));
629 }
630
631 list.setI18nPrefix(getContext().getI18nPrefix());
632
633
634
635
636
637 list.init(decorator, null, data, selectedData);
638
639 if (LOG.isDebugEnabled()) {
640 LOG.debug("Jlist [" + beanType.getName() + "] : " + list.getUniverseList().getModel().getSize());
641 }
642
643 if (preferredWidth > -1) {
644 fixBeanListWidth(list, preferredWidth);
645 }
646
647
648 final FilteredDecoratorListCellRenderer universeRenderer = newFilteredListCellRenderer(list.getHandler().getDecorator());
649 list.getUniverseList().setCellRenderer(universeRenderer);
650 list.getSelectedList().setCellRenderer(newListCellRender(list.getHandler().getDecorator()));
651
652
653
654 final JTextField filterField = list.getFilterField();
655 AbstractDocument document = (AbstractDocument) list.getFilterField().getDocument();
656 DocumentListener[] dls = document.getDocumentListeners();
657 if (dls != null) {
658 for (DocumentListener dl : dls) {
659 if (dl.getClass().getName().contains("BeanDoubleListHandler")) {
660 document.removeDocumentListener(dl);
661 break;
662 }
663 }
664 }
665 final JaxxFilterableListModel<E> filterModel = (JaxxFilterableListModel<E>) list.getModel().getUniverseModel();
666 document.addDocumentListener(new DocumentListener() {
667
668 @Override
669 public void insertUpdate(DocumentEvent e) {
670 String text = filterField.getText();
671 universeRenderer.setFilterText(text);
672 filterModel.setFilterText(text);
673 }
674
675 @Override
676 public void removeUpdate(DocumentEvent e) {
677 String text = filterField.getText();
678 universeRenderer.setFilterText(text);
679 filterModel.setFilterText(text);
680 }
681
682 @Override
683 public void changedUpdate(DocumentEvent e) {
684 String text = filterField.getText();
685 universeRenderer.setFilterText(text);
686 filterModel.setFilterText(text);
687 }
688 });
689
690 filterModel.addListDataListener(new ListDataListener() {
691 @Override
692 public void intervalAdded(ListDataEvent e) {
693 }
694
695 @Override
696 public void intervalRemoved(ListDataEvent e) {
697 }
698
699 @Override
700 public void contentsChanged(ListDataEvent e) {
701
702 boolean empty = e.getIndex0() == 0 && e.getIndex1() == 0;
703 list.getAddButton().setEnabled(!empty && !list.getUniverseList().isSelectionEmpty());
704 if (empty) {
705 list.getUniverseList().clearSelection();
706 }
707 }
708 });
709
710
711 list.getSelectedList().addMouseListener(new MouseAdapter() {
712
713 @Override
714 public void mousePressed(MouseEvent e) {
715 boolean rightClick = SwingUtilities.isRightMouseButton(e);
716 if (rightClick) {
717 JList list = (JList) e.getSource();
718 int index = list.locationToIndex(e.getPoint());
719 list.setSelectedIndex(index);
720 }
721 }
722 });
723
724
725 list.getUniverseList().setFocusTraversalKeysEnabled(true);
726 list.getSelectedList().setFocusTraversalKeysEnabled(true);
727 }
728
729 protected <O> FilteredDecoratorListCellRenderer newFilteredListCellRenderer(MultiJXPathDecorator<O> decorator) {
730 Assert.notNull(decorator);
731 return new ColoredFilteredListCellRenderer(decorator,
732 UIManager.getColor("nimbusSelectionBackground"), UIManager.getColor("nimbusSelectionBackground"));
733 }
734
735 @Override
736 @Deprecated
737 protected <O> ListCellRenderer newListCellRender(org.nuiton.decorator.Decorator<O> decorator) {
738 if (decorator instanceof Decorator) {
739 return newListCellRender((Decorator<O>) decorator);
740 }
741 LOG.warn("use of deprecated method : fr.ifremer.quadrige3.ui.swing.AbstractUIHandler.newListCellRender(org.nuiton.decorator.Decorator<O>)");
742 return super.newListCellRender(decorator);
743 }
744
745 protected <O> ListCellRenderer newListCellRender(MultiJXPathDecorator<O> decorator) {
746 Assert.notNull(decorator);
747 return new ExtendedDecoratorListCellRenderer(decorator, UIManager.getColor("nimbusSelectionBackground"), true);
748 }
749
750 private <E> void fixBeanListWidth(BeanDoubleList<E> list, int preferredWidth) {
751
752 JScrollPane scrollPane = (JScrollPane) list.get$objectMap().get("$JScrollPane0");
753 if (scrollPane == null) {
754 if (LOG.isWarnEnabled()) {
755 LOG.warn("the component '$JScrollPane0' has not been found in the BeanDoubleList");
756 }
757 } else {
758 scrollPane.setPreferredSize(new Dimension(preferredWidth, (int) scrollPane.getPreferredSize().getHeight()));
759 }
760 scrollPane = (JScrollPane) list.get$objectMap().get("$JScrollPane1");
761 if (scrollPane == null) {
762 if (LOG.isWarnEnabled()) {
763 LOG.warn("the component '$JScrollPane1' has not been found in the BeanDoubleList");
764 }
765 } else {
766 scrollPane.setPreferredSize(new Dimension(preferredWidth, (int) scrollPane.getPreferredSize().getHeight()));
767 }
768
769 }
770
771
772
773
774 @Override
775 protected <E> void initBeanFilterableComboBox(
776 final BeanFilterableComboBox<E> comboBox,
777 final List<E> data,
778 final E selectedData,
779 final String decoratorContext) {
780
781 initBeanFilterableComboBox(comboBox, data, selectedData, decoratorContext, null);
782 }
783
784 @SuppressWarnings("unchecked")
785 protected <E> void initBeanFilterableComboBox(
786 final BeanFilterableComboBox<E> comboBox,
787 final List<E> data,
788 final E selectedData,
789 final String decoratorContext,
790 final String toolTipDecoratorContext) {
791
792 Assert.notNull(comboBox, "No comboBox!");
793
794 final Class<E> beanType = comboBox.getBeanType();
795
796 Assert.notNull(beanType, "No beanType on the combobox!");
797
798 Decorator<E> decorator = getDecorator(beanType, decoratorContext);
799
800 List<E> dataTemp = data;
801 if (dataTemp == null) {
802 dataTemp = Lists.newArrayList();
803 }
804 if (LOG.isDebugEnabled()) {
805 LOG.debug("entity comboBox list [" + beanType.getName() + "] : " + dataTemp.size());
806 }
807
808 comboBox.setI18nPrefix(getContext().getI18nPrefix());
809
810
811 comboBox.init(decorator, dataTemp);
812
813
814 final MultiJXPathDecorator<E> finalDecorator = comboBox.getHandler().getDecorator();
815
816
817
818
819 comboBox.getCombobox().setRenderer(newComboBoxListCellRenderer(finalDecorator));
820
821
822 final MultiJXPathDecorator<E> toolTipDecorator = toolTipDecoratorContext != null
823 ? getDecorator(beanType, toolTipDecoratorContext)
824 : finalDecorator;
825
826
827 comboBox.addPropertyChangeListener(BeanFilterableComboBox.PROPERTY_SELECTED_ITEM, evt -> {
828 E newValue = (E) evt.getNewValue();
829 String tooltip = beanType.isInstance(newValue) ? toolTipDecorator.toString(newValue) : null;
830 comboBox.getCombobox().setToolTipText(tooltip);
831 });
832
833 comboBox.setSelectedItem(selectedData);
834
835 if (LOG.isDebugEnabled()) {
836 LOG.debug("combo [" + beanType.getName() + "] : " + comboBox.getData().size());
837 }
838 }
839
840 protected <E> ListCellRenderer newComboBoxListCellRenderer(MultiJXPathDecorator<E> decorator) {
841 Assert.notNull(decorator);
842 return new ExtendedDecoratorListCellRenderer(decorator, UIManager.getColor("nimbusSelectionBackground"), false);
843 }
844
845
846
847
848 @Override
849 protected void initLabel(JLabel jLabel) {
850 super.initLabel(jLabel);
851
852
853 if (jLabel.getLabelFor() instanceof JComponent) {
854 JComponent c = (JComponent) jLabel.getLabelFor();
855 if (c.getClientProperty("validatorLabel") == null) {
856 c.putClientProperty("validatorLabel", jLabel.getText());
857 }
858 }
859 }
860
861
862
863
864
865
866 protected void initActionComboBox(final JComboBox<?> combo) {
867 Assert.notNull(combo, "no combobox to init");
868 Assert.notNull(combo.getModel(), "the combobox has no model");
869 Assert.isInstanceOf(ActionComboBoxModel.class, combo.getModel(), "the combobox model must be a ActionListModel");
870
871 combo.setEditable(false);
872 combo.setRenderer(new ActionListCellRenderer(combo));
873
874
875 combo.addActionListener(this::actionComboBoxAction);
876 combo.addMouseListener(new MouseAdapter() {
877
878 @Override
879 public void mouseClicked(MouseEvent e) {
880 JComboBox<?> combo = (JComboBox<?>) e.getSource();
881 if (combo.isEnabled()) {
882 actionComboBoxAction(e);
883 }
884 }
885
886 @Override
887 public void mouseEntered(MouseEvent e) {
888 JComboBox<?> combo = (JComboBox<?>) e.getSource();
889 if (combo.isEnabled()) {
890 combo.showPopup();
891 }
892 }
893 });
894 }
895
896
897
898
899
900
901
902
903 public DefaultComboBoxModel newActionComboBoxModel(Object item, Object... items) {
904 AbstractButton button = (AbstractButton) item;
905 AbstractButton[] otherButtons = Arrays.copyOf(items, items.length, AbstractButton[].class);
906 return new ActionComboBoxModel(button, otherButtons);
907 }
908
909
910
911
912
913
914 protected void actionComboBoxAction(EventObject event) {
915 JComboBox<?> existingCombo = (JComboBox<?>) event.getSource();
916 JButton selectedButton = (JButton) existingCombo.getSelectedItem();
917
918 if (existingCombo.getModel() instanceof ActionComboBoxModel) {
919 ((ActionComboBoxModel) existingCombo.getModel()).reset();
920 } else {
921 existingCombo.setSelectedIndex(0);
922 }
923
924
925 existingCombo.hidePopup();
926 if (selectedButton != null && selectedButton.isEnabled()) {
927 if (selectedButton.getAction() != null) {
928 getContext().getActionEngine().runAction(selectedButton);
929 } else {
930 selectedButton.doClick();
931 }
932 }
933 }
934
935
936
937
938
939
940
941
942
943
944 @SuppressWarnings("unchecked")
945 protected void registerValidators(SwingValidator... validators) {
946 MainUI main = getContext().getMainUI();
947 Assert.notNull(main, "No mainUI registered in application context");
948 AbstractMainUIHandler handler = (AbstractMainUIHandler) main.getHandler();
949 handler.clearValidators();
950 for (SwingValidator validator : validators) {
951 handler.registerValidator(validator);
952 }
953 }
954
955
956
957
958
959
960
961 protected void listenValidatorValid(SimpleBeanValidator validator, final AbstractBeanUIModel model) {
962
963 stopListenValidatorValid(validator);
964 Assert.notNull(model);
965
966 PropertyChangeListener listener = evt -> {
967 if (LOG.isDebugEnabled()) {
968 LOG.debug("method listenValidatorValid " + "Model [" + model + "] pass to valid state [" + evt.getNewValue() + "]");
969 }
970 model.setValid((Boolean) evt.getNewValue());
971 };
972 validator.addPropertyChangeListener(SimpleBeanValidator.VALID_PROPERTY, listener);
973 validatorValidListenerMap.put(validator, listener);
974 }
975
976
977
978
979
980
981 protected void stopListenValidatorValid(SimpleBeanValidator validator) {
982 PropertyChangeListener listener = validatorValidListenerMap.get(validator);
983 if (listener != null) {
984 validator.removePropertyChangeListener(SimpleBeanValidator.VALID_PROPERTY, listener);
985 validatorValidListenerMap.remove(validator);
986 }
987 }
988
989
990
991
992
993
994
995 protected void listenValidationTableHasNoFatalError(final SimpleBeanValidator validator,
996 final AbstractBeanUIModel model) {
997 getContext().getMainUI().getValidatorMessageWidget().addTableModelListener(e -> {
998 boolean valid = !validator.hasFatalErrors();
999 if (LOG.isDebugEnabled()) {
1000 LOG.debug("method listenValidationTableHasNoFatalError " + "Model [" + model
1001 + "] pass to valid state [" + valid + "]");
1002 }
1003 model.setValid(valid);
1004 });
1005 }
1006
1007
1008
1009
1010
1011
1012 protected void listModelIsModify(AbstractBeanUIModel model) {
1013 model.addPropertyChangeListener(new PropertyChangeListener() {
1014
1015 final Set<String> excludeProperties = getPropertiesToIgnore();
1016
1017 @Override
1018 public void propertyChange(PropertyChangeEvent evt) {
1019 if (!excludeProperties.contains(evt.getPropertyName())) {
1020 ((AbstractBeanUIModel) evt.getSource()).setModify(true);
1021 }
1022 }
1023 });
1024 }
1025
1026
1027
1028
1029
1030
1031 protected Set<String> getPropertiesToIgnore() {
1032 return Sets.newHashSet(
1033 AbstractBeanUIModel.PROPERTY_MODIFY,
1034 AbstractBeanUIModel.PROPERTY_VALID);
1035 }
1036
1037
1038
1039
1040
1041
1042
1043
1044 public boolean askBefore(String title, String message) {
1045 return getContext().getDialogHelper().showConfirmDialog(message, title, JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION;
1046 }
1047
1048
1049
1050
1051
1052
1053
1054 public boolean askOverwriteFile(File file) {
1055 if (!file.exists()) {
1056
1057 return true;
1058 }
1059
1060
1061 return getContext().getDialogHelper().showConfirmDialog(
1062 t("quadrige3.action.common.askOverwriteFile.message", file),
1063 t("quadrige3.action.common.askOverwriteFile.title"), DialogHelper.OVERWRITE_FILE_CANCEL_OPTION) == JOptionPane.OK_OPTION;
1064
1065 }
1066
1067
1068
1069
1070 @Override
1071 public int askSaveBeforeLeaving(String message) {
1072 return getContext().getDialogHelper().showConfirmDialog(message, t("quadrige3.action.common.askSaveBeforeLeaving.title"), DialogHelper.SAVE_NO_SAVE_CANCEL_OPTION);
1073 }
1074
1075
1076
1077
1078 @Override
1079 public boolean askCancelEditBeforeLeaving(String message) {
1080 return getContext().getDialogHelper().showConfirmDialog(
1081 message, t("quadrige3.action.common.askCancelEditBeforeLeaving.title"), DialogHelper.NO_SAVE_CANCEL_OPTION) == JOptionPane.OK_OPTION;
1082 }
1083
1084
1085
1086
1087
1088
1089
1090
1091 public boolean askBeforeDelete(String title, String message) {
1092 return getContext().getDialogHelper().showConfirmDialog(message, title, DialogHelper.DELETE_CANCEL_OPTION) == JOptionPane.OK_OPTION;
1093 }
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103 public boolean askBeforeDeleteMany(String title, String message, List<String> list) {
1104 return getContext().getDialogHelper().showConfirmDialog(message, ApplicationUIUtil.getHtmlString(list), null, title, DialogHelper.DELETE_CANCEL_OPTION) == JOptionPane.OK_OPTION;
1105 }
1106
1107
1108
1109
1110
1111
1112
1113 public int askBeforeChangeTab(String message) {
1114 return getContext().getDialogHelper().showConfirmDialog(message, t("quadrige3.action.common.askBeforeChangeTab.title"), DialogHelper.SAVE_NO_SAVE_CANCEL_OPTION);
1115 }
1116
1117
1118
1119
1120
1121
1122
1123
1124 public boolean askBeforeReset(String title, String message) {
1125 return getContext().getDialogHelper().showConfirmDialog(message, title, DialogHelper.RESET_CANCEL_OPTION) == JOptionPane.OK_OPTION;
1126 }
1127
1128
1129
1130
1131
1132
1133
1134 public boolean askDeleteInvalidBeforeLeaving(String message) {
1135 return getContext().getDialogHelper().showConfirmDialog(
1136 message, t("quadrige3.action.common.askDeleteInvalidBeforeLeaving.title"), DialogHelper.DELETE_INVALID_CANCEL_OPTION) == JOptionPane.OK_OPTION;
1137 }
1138
1139
1140
1141
1142 @Override
1143 protected void initScrollPane(JScrollPane scrollPane) {
1144
1145
1146 Boolean onlyVerticalScrollable = (Boolean) scrollPane.getClientProperty("onlyVerticalScrollable");
1147 if (onlyVerticalScrollable != null && onlyVerticalScrollable) {
1148 scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
1149 scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
1150
1151
1152 Component component = scrollPane.getViewport().getView();
1153 JXPanel innerPanel = new JXPanel(new BorderLayout());
1154 innerPanel.add(component);
1155 scrollPane.setViewportView(innerPanel);
1156 innerPanel.setScrollableTracksViewportHeight(false);
1157 innerPanel.setScrollableTracksViewportWidth(true);
1158 }
1159
1160
1161 if (scrollPane.getViewport().getComponentCount() > 0) {
1162 boolean isTable = false;
1163 for (Component component : scrollPane.getViewport().getComponents()) {
1164 if (component instanceof JTable) {
1165 isTable = true;
1166 break;
1167 }
1168 }
1169 if (!isTable) {
1170 scrollPane.getVerticalScrollBar().setUnitIncrement(20);
1171 }
1172 }
1173 }
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184 public void openDialog(JDialog dialog) {
1185 openDialog(dialog, null);
1186 }
1187
1188
1189
1190
1191
1192
1193
1194 public void openDialog(JDialog dialog, Dimension size) {
1195 openDialog(dialog, size, false, null);
1196 }
1197
1198
1199
1200
1201
1202
1203 public void openDialogForceOnTop(JDialog dialog) {
1204 openDialogForceOnTop(dialog, null);
1205 }
1206
1207
1208
1209
1210
1211
1212
1213 public void openDialogForceOnTop(JDialog dialog, Dimension size) {
1214 openDialog(dialog, size, true, null);
1215 }
1216
1217 public void openDialog(JDialog dialog, Dimension size, boolean forceOnTop, Component centerComponent) {
1218
1219
1220 ActionUI oldApplicationActionUI = getContext().getActionUI();
1221
1222 try {
1223
1224 final ActionUI dialogActionUI = new ActionUI(dialog, true) {
1225 @Override
1226 protected ApplicationActionUIHandler createHandler() {
1227
1228 ApplicationUIUtil.setApplicationContext(this, getContext());
1229 return super.createHandler();
1230 }
1231 };
1232 getContext().setActionUI(dialogActionUI);
1233
1234
1235 initDialog(dialog, null);
1236
1237 if (CollectionUtils.isEmpty(dialog.getIconImages())) {
1238 dialog.setIconImage(getContext().getMainUI().getIconImage());
1239 }
1240
1241 if (size != null) {
1242 dialog.setSize(size);
1243 } else {
1244 dialog.pack();
1245 }
1246
1247
1248 dialog.setLocationRelativeTo(centerComponent != null ? centerComponent : getContext().getMainUI());
1249
1250
1251 dialog.setModalityType(forceOnTop
1252 ? Dialog.ModalityType.TOOLKIT_MODAL
1253 : Dialog.ModalityType.APPLICATION_MODAL);
1254 dialog.setVisible(true);
1255
1256 } finally {
1257
1258
1259 getContext().setActionUI(oldApplicationActionUI);
1260 }
1261
1262 }
1263
1264 @Override
1265 public void openDialog(org.nuiton.jaxx.application.swing.ApplicationUI dialogContent, String title, Dimension dim) {
1266 openDialog(dialogContent, title, dim, null);
1267 }
1268
1269 public void openDialog(org.nuiton.jaxx.application.swing.ApplicationUI dialogContent, String title, Dimension dim, Component centerComponent) {
1270
1271 Component topestUI = getTopestUI();
1272
1273 JDialog dialog;
1274 if (topestUI instanceof Frame) {
1275 dialog = new JDialog((Frame) topestUI, title, true);
1276 } else {
1277 dialog = new JDialog((Dialog) topestUI, title, true);
1278 }
1279
1280
1281 initDialog(dialog, dialogContent.getHandler());
1282
1283 dialog.setIconImage(getContext().getMainUI().getIconImage());
1284 Component dialogComponent = (Component) dialogContent;
1285 dialogComponent.setVisible(true);
1286 dialog.add(dialogComponent);
1287 dialog.setResizable(true);
1288 dialog.setName("dialog_" + dialogComponent.getName());
1289
1290 if (dim != null) {
1291 dialog.setSize(dim);
1292 } else {
1293 dialog.pack();
1294 }
1295
1296
1297 dialog.setLocationRelativeTo(centerComponent != null ? centerComponent : getContext().getMainUI());
1298
1299 dialog.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
1300 dialog.setVisible(true);
1301 }
1302
1303 public JFrame createFrame(Component dialogContent, String title, Image iconImage, Rectangle dim) {
1304
1305 JFrame frame = new JFrame();
1306 frame.setTitle(getContext().getMainUI().getTitle() + (StringUtils.isNotEmpty(title) ? " - " + title : ""));
1307 if (iconImage != null) {
1308 frame.setIconImage(iconImage);
1309 } else {
1310 frame.setIconImage(getContext().getMainUI().getIconImage());
1311 }
1312
1313 Container dialogComponent;
1314 if (dialogContent instanceof Container) {
1315 dialogComponent = (Container) dialogContent;
1316 } else {
1317 dialogComponent = new JPanel(new BorderLayout());
1318 dialogComponent.add(dialogContent);
1319 }
1320
1321
1322 if (dialogContent instanceof ApplicationUI) {
1323 initDialog(frame, ((ApplicationUI) dialogContent).getHandler());
1324 }
1325 dialogComponent.setVisible(true);
1326 frame.setContentPane(dialogComponent);
1327 frame.setName("frame_" + dialogComponent.getName());
1328
1329 if (dim != null) {
1330 frame.setBounds(dim);
1331 } else {
1332 frame.pack();
1333 }
1334
1335 frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
1336 return frame;
1337 }
1338
1339 public JFrame openFrame(Component dialogContent, String title, Image iconImage, Rectangle dim) {
1340
1341 JFrame frame = createFrame(dialogContent, title, iconImage, dim);
1342 SwingUtilities.invokeLater(() -> frame.setVisible(true));
1343 return frame;
1344 }
1345
1346 private void initDialog(Window dialog, AbstractApplicationUIHandler dialogHandler) {
1347
1348 final AbstractApplicationUIHandler handler;
1349 if (dialogHandler == null) {
1350 handler = dialog instanceof ApplicationUI
1351 ? ((ApplicationUI) dialog).getHandler()
1352 : null;
1353 } else {
1354 handler = dialogHandler;
1355 }
1356
1357 if (handler instanceof Cancelable) {
1358
1359
1360 final Action cancelAction = new AbstractAction() {
1361
1362 @Override
1363 public void actionPerformed(ActionEvent e) {
1364 ((Cancelable) handler).cancel();
1365 }
1366 };
1367
1368 JRootPane rootPane = ((RootPaneContainer) dialog).getRootPane();
1369 KeyStroke shortcutClosePopup = getContext().getConfiguration().getShortcutClosePopup();
1370 rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(shortcutClosePopup, "close");
1371 rootPane.getActionMap().put("close", cancelAction);
1372
1373 dialog.addWindowListener(new WindowAdapter() {
1374 @Override
1375 public void windowClosing(WindowEvent e) {
1376 cancelAction.actionPerformed(null);
1377 }
1378 });
1379 }
1380
1381 dialog.addWindowListener(new WindowAdapter() {
1382
1383 @Override
1384 public void windowClosed(WindowEvent e) {
1385 Optional.ofNullable(handler).ifPresent(AbstractApplicationUIHandler::closeDialog);
1386 }
1387 });
1388
1389 }
1390
1391
1392
1393
1394
1395
1396 @Override
1397 public void closeDialog() {
1398
1399 ApplicationUIUtil.destroy(getUI());
1400
1401 if (getUI() instanceof JDialog) {
1402 if (LOG.isDebugEnabled()) {
1403 LOG.debug("Destroy dialog " + getUI());
1404 }
1405 ((JDialog) getUI()).dispose();
1406 } else {
1407 JDialog parent = getParentContainer(JDialog.class);
1408 if (parent != null) {
1409 if (LOG.isDebugEnabled()) {
1410 LOG.debug("Destroy dialog " + parent + " of " + getUI());
1411 }
1412 parent.dispose();
1413 }
1414 }
1415 }
1416
1417 }