Project

General

Profile

Download (26.2 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2017 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9
package eu.etaxonomy.vaadin.mvp;
10

    
11
import java.util.ArrayList;
12
import java.util.Arrays;
13
import java.util.HashSet;
14
import java.util.List;
15
import java.util.Map;
16
import java.util.Set;
17
import java.util.Stack;
18

    
19
import org.apache.log4j.Logger;
20
import org.vaadin.spring.events.EventScope;
21

    
22
import com.vaadin.data.Validator.InvalidValueException;
23
import com.vaadin.data.fieldgroup.BeanFieldGroup;
24
import com.vaadin.data.fieldgroup.FieldGroup.CommitEvent;
25
import com.vaadin.data.fieldgroup.FieldGroup.CommitException;
26
import com.vaadin.data.fieldgroup.FieldGroup.CommitHandler;
27
import com.vaadin.data.fieldgroup.FieldGroup.FieldGroupInvalidValueException;
28
import com.vaadin.server.AbstractErrorMessage.ContentMode;
29
import com.vaadin.server.ErrorMessage.ErrorLevel;
30
import com.vaadin.server.FontAwesome;
31
import com.vaadin.server.UserError;
32
import com.vaadin.shared.ui.MarginInfo;
33
import com.vaadin.ui.AbstractComponentContainer;
34
import com.vaadin.ui.AbstractField;
35
import com.vaadin.ui.AbstractLayout;
36
import com.vaadin.ui.AbstractOrderedLayout;
37
import com.vaadin.ui.Alignment;
38
import com.vaadin.ui.Button;
39
import com.vaadin.ui.CheckBox;
40
import com.vaadin.ui.Component;
41
import com.vaadin.ui.CssLayout;
42
import com.vaadin.ui.Field;
43
import com.vaadin.ui.GridLayout;
44
import com.vaadin.ui.GridLayout.OutOfBoundsException;
45
import com.vaadin.ui.GridLayout.OverlapsException;
46
import com.vaadin.ui.HorizontalLayout;
47
import com.vaadin.ui.Label;
48
import com.vaadin.ui.Layout;
49
import com.vaadin.ui.Layout.MarginHandler;
50
import com.vaadin.ui.Notification;
51
import com.vaadin.ui.Notification.Type;
52
import com.vaadin.ui.PopupDateField;
53
import com.vaadin.ui.TextField;
54
import com.vaadin.ui.VerticalLayout;
55
import com.vaadin.ui.themes.ValoTheme;
56

    
57
import eu.etaxonomy.cdm.database.PermissionDeniedException;
58
import eu.etaxonomy.cdm.vaadin.component.TextFieldNFix;
59
import eu.etaxonomy.cdm.vaadin.event.AbstractEditorAction;
60
import eu.etaxonomy.cdm.vaadin.event.AbstractEditorAction.EditorActionContext;
61
import eu.etaxonomy.vaadin.component.NestedFieldGroup;
62
import eu.etaxonomy.vaadin.component.SwitchableTextField;
63
import eu.etaxonomy.vaadin.mvp.event.EditorDeleteEvent;
64
import eu.etaxonomy.vaadin.mvp.event.EditorPreSaveEvent;
65
import eu.etaxonomy.vaadin.mvp.event.EditorSaveEvent;
66
import eu.etaxonomy.vaadin.ui.view.DoneWithPopupEvent;
67
import eu.etaxonomy.vaadin.ui.view.DoneWithPopupEvent.Reason;
68

    
69
/**
70
 *
71
 * Optional with a delete button which can be enabled with {@link #withDeleteButton(boolean)}
72
 *
73
 * @author a.kohlbecker
74
 * @since Apr 5, 2017
75
 *
76
 */
77
public abstract class AbstractPopupEditor<DTO extends Object, P extends AbstractEditorPresenter<DTO, ? extends ApplicationView>>
78
    extends AbstractPopupView<P> {
79

    
80
    public static final Logger logger = Logger.getLogger(AbstractPopupEditor.class);
81

    
82
    private BeanFieldGroup<DTO> fieldGroup;
83

    
84
    private VerticalLayout mainLayout;
85

    
86
    private Layout fieldLayout;
87

    
88
    private HorizontalLayout buttonLayout;
89

    
90
    private Button save;
91

    
92
    private Button cancel;
93

    
94
    private Button delete;
95

    
96
    private CssLayout toolBar = new CssLayout();
97

    
98
    private CssLayout toolBarButtonGroup = new CssLayout();
99

    
100
    private Label statusMessageLabel = new Label();
101

    
102
    Set<String> statusMessages = new HashSet<>();
103

    
104
    private GridLayout _gridLayoutCache;
105

    
106
    private boolean isBeanLoaded;
107

    
108
    private Stack<EditorActionContext> context = new Stack<EditorActionContext>();
109

    
110
    private boolean isContextUpdated;
111

    
112
    private boolean isAdvancedMode = false;
113

    
114
    private List<Component> advancedModeComponents = new ArrayList<>();
115

    
116
    private Button advancedModeButton;
117

    
118
    public AbstractPopupEditor(Layout layout, Class<DTO> dtoType) {
119

    
120
        mainLayout = new VerticalLayout();
121
        // IMPORTANT: mainLayout must be set to full size otherwise the
122
        // popup window may have problems with automatic resizing of its
123
        // content.
124
        mainLayout.setSizeFull();
125

    
126
        setCompositionRoot(mainLayout);
127

    
128
        fieldGroup = new BeanFieldGroup<>(dtoType);
129
        fieldGroup.addCommitHandler(new SaveHandler());
130

    
131
        toolBar.addStyleName(ValoTheme.WINDOW_TOP_TOOLBAR);
132
        toolBar.setWidth(100, Unit.PERCENTAGE);
133
        toolBarButtonGroup.addStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP);
134
        toolBarButtonGroup.setWidthUndefined();
135
        toolBar.addComponent(toolBarButtonGroup);
136
        toolBar.setVisible(false);
137

    
138
        fieldLayout = layout;
139
        fieldLayout.setWidthUndefined();
140
        if(fieldLayout instanceof AbstractOrderedLayout){
141
            ((AbstractOrderedLayout)fieldLayout).setSpacing(true);
142
        }
143
        if(MarginHandler.class.isAssignableFrom(fieldLayout.getClass())){
144
            ((MarginHandler)fieldLayout).setMargin(new MarginInfo(false, true, true, true));
145
        }
146

    
147
        buttonLayout = new HorizontalLayout();
148
        buttonLayout.setStyleName(ValoTheme.WINDOW_BOTTOM_TOOLBAR);
149
        buttonLayout.setWidth(100, Unit.PERCENTAGE);
150
        buttonLayout.setSpacing(true);
151

    
152
        save = new Button("Save", FontAwesome.SAVE);
153
        save.setStyleName(ValoTheme.BUTTON_PRIMARY);
154
        save.addClickListener(e -> save());
155

    
156
        cancel = new Button("Cancel", FontAwesome.REMOVE);
157
        cancel.addClickListener(e -> cancel());
158

    
159
        delete = new Button("Delete", FontAwesome.TRASH);
160
        delete.setStyleName(ValoTheme.BUTTON_DANGER);
161
        delete.addClickListener(e -> delete());
162
        delete.setVisible(false);
163

    
164
        buttonLayout.addComponents(delete, save, cancel);
165
        // delete is initially invisible, let save take all space
166
        buttonLayout.setExpandRatio(save, 1);
167
        buttonLayout.setComponentAlignment(delete, Alignment.TOP_RIGHT);
168
        buttonLayout.setComponentAlignment(save, Alignment.TOP_RIGHT);
169
        buttonLayout.setComponentAlignment(cancel, Alignment.TOP_RIGHT);
170

    
171
        statusMessageLabel.setWidthUndefined();
172

    
173
        mainLayout.addComponents(toolBar, fieldLayout, statusMessageLabel, buttonLayout);
174
        mainLayout.setComponentAlignment(statusMessageLabel, Alignment.BOTTOM_RIGHT);
175
        mainLayout.setComponentAlignment(toolBar, Alignment.TOP_RIGHT);
176

    
177
        updateToolBarVisibility();
178
    }
179

    
180
    protected VerticalLayout getMainLayout() {
181
        return mainLayout;
182
    }
183

    
184
    protected Layout getFieldLayout() {
185
        return fieldLayout;
186
    }
187

    
188
    /**
189
     * @return
190
     */
191
    private GridLayout gridLayout() {
192
        if(_gridLayoutCache == null){
193
            if(fieldLayout instanceof GridLayout){
194
                _gridLayoutCache = (GridLayout)fieldLayout;
195
            } else {
196
                throw new RuntimeException("The fieldlayout of this editor is not a GridLayout");
197
            }
198
        }
199
        return _gridLayoutCache;
200
    }
201

    
202
    @Override
203
    public void setReadOnly(boolean readOnly) {
204
        super.setReadOnly(readOnly);
205
        save.setVisible(!readOnly);
206
        delete.setVisible(!readOnly);
207
        cancel.setCaption(readOnly ? "Close" : "Cancel");
208
        recursiveReadonly(readOnly, (AbstractComponentContainer)getFieldLayout());
209
    }
210

    
211
    /**
212
     * @param readOnly
213
     * @param layout
214
     */
215
    protected void recursiveReadonly(boolean readOnly, AbstractComponentContainer layout) {
216
        for(Component c : layout){
217
            c.setReadOnly(readOnly);
218
            if(c instanceof AbstractComponentContainer){
219
                recursiveReadonly(readOnly, (AbstractComponentContainer)c);
220
            }
221
        }
222
    }
223

    
224
    /**
225
     * @return
226
     * @return
227
     */
228
    protected AbstractLayout getToolBar() {
229
        return toolBar;
230
    }
231

    
232
    /**
233
     * @return
234
     * @return
235
     */
236
    protected void toolBarAdd(Component c) {
237
        toolBar.addComponent(c, toolBar.getComponentIndex(toolBarButtonGroup) - 1);
238
        updateToolBarVisibility();
239
    }
240

    
241
    /**
242
     * @return
243
     * @return
244
     */
245
    protected void toolBarButtonGroupAdd(Component c) {
246
        toolBarButtonGroup.addComponent(c);
247
        updateToolBarVisibility();
248
    }
249

    
250
    /**
251
     * @return
252
     * @return
253
     */
254
    protected void toolBarButtonGroupRemove(Component c) {
255
        toolBarButtonGroup.removeComponent(c);
256
        updateToolBarVisibility();
257
    }
258

    
259
    /**
260
     *
261
     */
262
    private void updateToolBarVisibility() {
263
        boolean showToolbar = toolBarButtonGroup.getComponentCount() + toolBar.getComponentCount() > 1;
264
        toolBar.setVisible(toolBarButtonGroup.getComponentCount() + toolBar.getComponentCount() > 1);
265
        if(!showToolbar){
266
            mainLayout.setMargin(new MarginInfo(true, false, false, false));
267
        } else {
268
            mainLayout.setMargin(false);
269
        }
270

    
271
    }
272

    
273
    /**
274
     * The top tool-bar is initially invisible.
275
     *
276
     * @param visible
277
     */
278
    protected void setToolBarVisible(boolean visible){
279
        toolBar.setVisible(true);
280
    }
281

    
282
    /**
283
     * @return the isAdvancedMode
284
     */
285
    public boolean isAdvancedMode() {
286
        return isAdvancedMode;
287
    }
288

    
289
    /**
290
     * @param isAdvancedMode the isAdvancedMode to set
291
     */
292
    public void setAdvancedMode(boolean isAdvancedMode) {
293
        this.isAdvancedMode = isAdvancedMode;
294
        advancedModeComponents.forEach(c -> c.setVisible(isAdvancedMode));
295
    }
296

    
297
    public void setAdvancedModeEnabled(boolean activate){
298
        if(activate && advancedModeButton == null){
299
            advancedModeButton = new Button(FontAwesome.WRENCH); // FontAwesome.FLASK
300
            advancedModeButton.setIconAlternateText("Advanced mode");
301
            advancedModeButton.addStyleName(ValoTheme.BUTTON_TINY);
302
            toolBarButtonGroupAdd(advancedModeButton);
303
            advancedModeButton.addClickListener(e -> {
304
                setAdvancedMode(!isAdvancedMode);
305
                }
306
            );
307

    
308
        } else if(advancedModeButton != null) {
309
            toolBarButtonGroupRemove(advancedModeButton);
310
            advancedModeButton = null;
311
        }
312
    }
313

    
314
    public void registerAdvancedModeComponents(Component ... c){
315
        advancedModeComponents.addAll(Arrays.asList(c));
316
    }
317

    
318

    
319
    // ------------------------ event handler ------------------------ //
320

    
321
    private class SaveHandler implements CommitHandler {
322

    
323
        private static final long serialVersionUID = 2047223089707080659L;
324

    
325
        @Override
326
        public void preCommit(CommitEvent commitEvent) throws CommitException {
327
            logger.debug("preCommit(), publishing EditorPreSaveEvent");
328
            // notify the presenter to start a transaction
329
            viewEventBus.publish(this, new EditorPreSaveEvent<DTO>(AbstractPopupEditor.this, getBean()));
330
        }
331

    
332
        @Override
333
        public void postCommit(CommitEvent commitEvent) throws CommitException {
334
            try {
335
                if(logger.isTraceEnabled()){
336
                    logger.trace("postCommit() publishing EditorSaveEvent for " + getBean().toString());
337
                }
338
                // notify the presenter to persist the bean and to commit the transaction
339
                viewEventBus.publish(this, new EditorSaveEvent<DTO>(AbstractPopupEditor.this, getBean()));
340
                if(logger.isTraceEnabled()){
341
                    logger.trace("postCommit() publishing DoneWithPopupEvent");
342
                }
343
                // notify the NavigationManagerBean to close the window and to dispose the view
344
                viewEventBus.publish(EventScope.UI, this, new DoneWithPopupEvent(AbstractPopupEditor.this, Reason.SAVE));
345
            } catch (Exception e) {
346
                logger.error(e);
347
                throw new CommitException("Failed to store data to backend", e);
348
            }
349
        }
350
    }
351

    
352
    protected void addCommitHandler(CommitHandler commitHandler) {
353
        fieldGroup.addCommitHandler(commitHandler);
354
    }
355

    
356

    
357
    /**
358
     * Cancel editing and discard all modifications.
359
     */
360
    @Override
361
    public void cancel() {
362
        fieldGroup.discard();
363
        viewEventBus.publish(EventScope.UI, this, new DoneWithPopupEvent(this, Reason.CANCEL));
364
    }
365

    
366
    /**
367
     * @return
368
     */
369
    private void delete() {
370
        viewEventBus.publish(this, new EditorDeleteEvent<DTO>(this, fieldGroup.getItemDataSource().getBean()));
371
        viewEventBus.publish(EventScope.UI, this, new DoneWithPopupEvent(this, Reason.DELETE));
372
    }
373

    
374
    /**
375
     * Save the changes made in the editor.
376
     */
377
    private void save() {
378
        try {
379
            fieldGroup.commit();
380
        } catch (CommitException e) {
381
            fieldGroup.getFields().forEach(f -> ((AbstractField<?>)f).setValidationVisible(true));
382
            if(e.getCause() != null && e.getCause() instanceof FieldGroupInvalidValueException){
383
                FieldGroupInvalidValueException invalidValueException = (FieldGroupInvalidValueException)e.getCause();
384
                updateFieldNotifications(invalidValueException.getInvalidFields());
385
                Notification.show("The entered data in " + invalidValueException.getInvalidFields().size() + " fields is incomplete or invalid.");
386
            } else if(e.getCause() != null && e.getCause().getCause() != null && e.getCause().getCause() instanceof PermissionDeniedException){
387
                PermissionDeniedException permissionDeniedException = (PermissionDeniedException)e.getCause().getCause();
388
                Notification.show("Permission denied", permissionDeniedException.getMessage(), Type.ERROR_MESSAGE);
389
            } else {
390
//                Logger.getLogger(this.getClass()).error("Error saving", e);
391
//                Notification.show("Error saving", Type.ERROR_MESSAGE);
392
                throw new RuntimeException("Error saving", e);
393
            }
394
        }
395
    }
396

    
397
    /**
398
     * @param invalidFields
399
     */
400
    private void updateFieldNotifications(Map<Field<?>, InvalidValueException> invalidFields) {
401
        for(Field<?> f : invalidFields.keySet()){
402
            if(f instanceof AbstractField){
403
                String message = invalidFields.get(f).getHtmlMessage();
404
                ((AbstractField)f).setComponentError(new UserError(message, ContentMode.HTML, ErrorLevel.ERROR));
405
            }
406
        }
407

    
408
    }
409

    
410
    // ------------------------ field adding methods ------------------------ //
411

    
412

    
413
    protected TextField addTextField(String caption, String propertyId) {
414
        return addField(new TextFieldNFix(caption), propertyId);
415
    }
416

    
417
    protected TextField addTextField(String caption, String propertyId, int column1, int row1,
418
            int column2, int row2)
419
            throws OverlapsException, OutOfBoundsException {
420
        return addField(new TextFieldNFix(caption), propertyId, column1, row1, column2, row2);
421
    }
422

    
423
    protected TextField addTextField(String caption, String propertyId, int column, int row)
424
            throws OverlapsException, OutOfBoundsException {
425
        return addField(new TextFieldNFix(caption), propertyId, column, row);
426
    }
427

    
428
    protected SwitchableTextField addSwitchableTextField(String caption, String textPropertyId, String switchPropertyId, int column1, int row1,
429
            int column2, int row2)
430
            throws OverlapsException, OutOfBoundsException {
431

    
432
        SwitchableTextField field = new SwitchableTextField(caption);
433
        field.bindTo(fieldGroup, textPropertyId, switchPropertyId);
434
        addComponent(field, column1, row1, column2, row2);
435
        return field;
436
    }
437

    
438
    protected SwitchableTextField addSwitchableTextField(String caption, String textPropertyId, String switchPropertyId, int column, int row)
439
            throws OverlapsException, OutOfBoundsException {
440

    
441
        SwitchableTextField field = new SwitchableTextField(caption);
442
        field.bindTo(fieldGroup, textPropertyId, switchPropertyId);
443
        addComponent(field, column, row);
444
        return field;
445
    }
446

    
447
    protected PopupDateField addDateField(String caption, String propertyId) {
448
        return addField(new PopupDateField(caption), propertyId);
449
    }
450

    
451
    protected CheckBox addCheckBox(String caption, String propertyId) {
452
        return addField(new CheckBox(caption), propertyId);
453
    }
454

    
455
    protected CheckBox addCheckBox(String caption, String propertyId, int column, int row){
456
        return addField(new CheckBox(caption), propertyId, column, row);
457
    }
458

    
459
    protected <T extends Field> T addField(T field, String propertyId) {
460
        fieldGroup.bind(field, propertyId);
461
        if(NestedFieldGroup.class.isAssignableFrom(field.getClass())){
462
            ((NestedFieldGroup)field).registerParentFieldGroup(fieldGroup);
463
        }
464
        addComponent(field);
465
        return field;
466
    }
467

    
468
    /**
469
     * Can only be used if the <code>fieldlayout</code> is a GridLayout.
470
     *
471
     * @param field
472
     *            the field to be added, not <code>null</code>.
473
     * @param propertyId
474
     * @param column
475
     *            the column index, starting from 0.
476
     * @param row
477
     *            the row index, starting from 0.
478
     * @throws OverlapsException
479
     *             if the new component overlaps with any of the components
480
     *             already in the grid.
481
     * @throws OutOfBoundsException
482
     *             if the cell is outside the grid area.
483
     */
484
    protected <T extends Field> T addField(T field, String propertyId, int column, int row)
485
            throws OverlapsException, OutOfBoundsException {
486
        fieldGroup.bind(field, propertyId);
487
        if(NestedFieldGroup.class.isAssignableFrom(field.getClass())){
488
            ((NestedFieldGroup)field).registerParentFieldGroup(fieldGroup);
489
        }
490
        addComponent(field, column, row);
491
        return field;
492
    }
493

    
494
    /**
495
     * Can only be used if the <code>fieldlayout</code> is a GridLayout.
496
     *
497
     * @param field
498
     * @param propertyId
499
     * @param column1
500
     * @param row1
501
     * @param column2
502
     * @param row2
503
     * @return
504
     * @throws OverlapsException
505
     * @throws OutOfBoundsException
506
     */
507
    protected <T extends Field> T addField(T field, String propertyId, int column1, int row1,
508
            int column2, int row2)
509
            throws OverlapsException, OutOfBoundsException {
510
        if(propertyId != null){
511
            fieldGroup.bind(field, propertyId);
512
            if(NestedFieldGroup.class.isAssignableFrom(field.getClass())){
513
                ((NestedFieldGroup)field).registerParentFieldGroup(fieldGroup);
514
            }
515
        }
516
        addComponent(field, column1, row1, column2, row2);
517
        return field;
518
    }
519

    
520
    protected Field<?> getField(Object propertyId){
521
        return fieldGroup.getField(propertyId);
522
    }
523

    
524
    protected void addComponent(Component component) {
525
        fieldLayout.addComponent(component);
526
        applyDefaultComponentStyles(component);
527
    }
528

    
529
    protected void bindField(Field field, String propertyId){
530
        fieldGroup.bind(field, propertyId);
531
    }
532

    
533
    /**
534
     * @param component
535
     */
536
    public void applyDefaultComponentStyles(Component component) {
537
        component.addStyleName(getDefaultComponentStyles());
538
    }
539

    
540
    protected abstract String getDefaultComponentStyles();
541

    
542
    /**
543
     * Can only be used if the <code>fieldlayout</code> is a GridLayout.
544
     * <p>
545
     * Adds the component to the grid in cells column1,row1 (NortWest corner of
546
     * the area.) End coordinates (SouthEast corner of the area) are the same as
547
     * column1,row1. The coordinates are zero-based. Component width and height
548
     * is 1.
549
     *
550
     * @param component
551
     *            the component to be added, not <code>null</code>.
552
     * @param column
553
     *            the column index, starting from 0.
554
     * @param row
555
     *            the row index, starting from 0.
556
     * @throws OverlapsException
557
     *             if the new component overlaps with any of the components
558
     *             already in the grid.
559
     * @throws OutOfBoundsException
560
     *             if the cell is outside the grid area.
561
     */
562
    public void addComponent(Component component, int column, int row)
563
            throws OverlapsException, OutOfBoundsException {
564
        applyDefaultComponentStyles(component);
565
        gridLayout().addComponent(component, column, row, column, row);
566
    }
567

    
568
    /**
569
     * Can only be used if the <code>fieldlayout</code> is a GridLayout.
570
     * <p>
571
     * Adds a component to the grid in the specified area. The area is defined
572
     * by specifying the upper left corner (column1, row1) and the lower right
573
     * corner (column2, row2) of the area. The coordinates are zero-based.
574
     * </p>
575
     *
576
     * <p>
577
     * If the area overlaps with any of the existing components already present
578
     * in the grid, the operation will fail and an {@link OverlapsException} is
579
     * thrown.
580
     * </p>
581
     *
582
     * @param component
583
     *            the component to be added, not <code>null</code>.
584
     * @param column1
585
     *            the column of the upper left corner of the area <code>c</code>
586
     *            is supposed to occupy. The leftmost column has index 0.
587
     * @param row1
588
     *            the row of the upper left corner of the area <code>c</code> is
589
     *            supposed to occupy. The topmost row has index 0.
590
     * @param column2
591
     *            the column of the lower right corner of the area
592
     *            <code>c</code> is supposed to occupy.
593
     * @param row2
594
     *            the row of the lower right corner of the area <code>c</code>
595
     *            is supposed to occupy.
596
     * @throws OverlapsException
597
     *             if the new component overlaps with any of the components
598
     *             already in the grid.
599
     * @throws OutOfBoundsException
600
     *             if the cells are outside the grid area.
601
     */
602
    public void addComponent(Component component, int column1, int row1,
603
            int column2, int row2)
604
            throws OverlapsException, OutOfBoundsException {
605
        applyDefaultComponentStyles(component);
606
        gridLayout().addComponent(component, column1, row1, column2, row2);
607
    }
608

    
609
    public void setSaveButtonEnabled(boolean enabled){
610
        save.setEnabled(enabled);
611
    }
612

    
613
    public void withDeleteButton(boolean withDelete){
614

    
615
        if(withDelete){
616
            buttonLayout.setExpandRatio(save, 0);
617
            buttonLayout.setExpandRatio(delete, 1);
618
        } else {
619
            buttonLayout.setExpandRatio(save, 1);
620
            buttonLayout.setExpandRatio(delete, 0);
621
        }
622
        delete.setVisible(withDelete);
623
    }
624

    
625
    public boolean addStatusMessage(String message){
626
        boolean returnVal = statusMessages.add(message);
627
        updateStatusLabel();
628
        return returnVal;
629
    }
630

    
631
    public boolean removeStatusMessage(String message){
632
        boolean returnVal = statusMessages.remove(message);
633
        updateStatusLabel();
634
        return returnVal;
635
    }
636

    
637
    /**
638
     *
639
     */
640
    private void updateStatusLabel() {
641
        String text = "";
642
        for(String s : statusMessages){
643
            text += s + " ";
644
        }
645
        statusMessageLabel.setValue(text);
646
        statusMessageLabel.setVisible(!text.isEmpty());
647
        statusMessageLabel.addStyleName(ValoTheme.LABEL_COLORED);
648
    }
649

    
650
    // ------------------------ data binding ------------------------ //
651

    
652
    protected void bindDesign(Component component) {
653
        fieldLayout.removeAllComponents();
654
        fieldGroup.bindMemberFields(component);
655
        fieldLayout.addComponent(component);
656
    }
657

    
658

    
659
    public final void loadInEditor(Object identifier) {
660

    
661
        DTO beanToEdit = getPresenter().loadBeanById(identifier);
662
        fieldGroup.setItemDataSource(beanToEdit);
663
        afterItemDataSourceSet();
664
        getPresenter().adaptToUserPermission(beanToEdit);
665
        isBeanLoaded = true;
666
    }
667

    
668
    /**
669
     * Passes the beanInstantiator to the presenter method {@link AbstractEditorPresenter#setBeanInstantiator(BeanInstantiator)}
670
     *
671
     * @param beanInstantiator
672
     */
673
    public final void setBeanInstantiator(BeanInstantiator<DTO> beanInstantiator) {
674
        getPresenter().setBeanInstantiator(beanInstantiator);
675
    }
676

    
677
    /**
678
     * Returns the bean contained in the itemDatasource of the fieldGroup.
679
     *
680
     * @return
681
     */
682
    public DTO getBean() {
683
        if(fieldGroup.getItemDataSource() != null){
684
            return fieldGroup.getItemDataSource().getBean();
685

    
686
        }
687
        return null;
688
    }
689

    
690
    /**
691
     * @return true once the bean has been loaded indicating that all fields have
692
     *   been setup configured so that the editor is ready for use.
693
     */
694
    public boolean isBeanLoaded() {
695
        return isBeanLoaded;
696
    }
697

    
698
    /**
699
     * This method should only be used by the presenter of this view
700
     *
701
     * @param bean
702
     */
703
    protected void updateItemDataSource(DTO bean) {
704
        fieldGroup.getItemDataSource().setBean(bean);
705
    }
706

    
707
    /**
708
     * This method is called after setting the item data source whereby the
709
     * {@link FieldGroup#configureField(Field<?> field)} method will be called.
710
     * In this method all fields are set to default states defined for the fieldGroup.
711
     * <p>
712
     * You can now implement this method if you need to modify the state or value of individual fields.
713
     */
714
    protected void afterItemDataSourceSet() {
715
    }
716

    
717
    // ------------------------ issue related temporary solutions --------------------- //
718
    /**
719
     * Publicly accessible equivalent to getPreseneter(), needed for
720
     * managing the presenter listeners.
721
     * <p>
722
     * TODO: refactor the presenter listeners management to get rid of this method
723
     *
724
     * @return
725
     * @deprecated marked deprecated to emphasize on the special character of this method
726
     *    which should only be used internally see #6673
727
     */
728
    @Deprecated
729
    public P presenter() {
730
        return getPresenter();
731
    }
732

    
733
    /**
734
     * Returns the context of editor actions for this editor.
735
     * The context submitted with {@link #setParentContext(Stack)} will be updated
736
     * to represent the current context.
737
     *
738
     * @return the context
739
     */
740
    public Stack<EditorActionContext> getEditorActionContext() {
741
        if(!isContextUpdated){
742
            if(getBean() == null){
743
                throw new RuntimeException("getContext() is only possible after the bean is loaded");
744
            }
745
            context.push(new AbstractEditorAction.EditorActionContext(getBean(), this));
746
            isContextUpdated = true;
747
        }
748
        return context;
749
    }
750

    
751
    /**
752
     * Set the context of editor actions parent to this editor
753
     *
754
     * @param context the context to set
755
     */
756
    public void setParentEditorActionContext(Stack<EditorActionContext> context) {
757
        if(context != null){
758
            this.context.addAll(context);
759
        }
760
    }
761

    
762
}
(6-6/12)