All Downloads are FREE. Search and download functionalities are using the official Maven repository.

at.spardat.xma.page.DialogPage Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2003, 2007 s IT Solutions AT Spardat GmbH .
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     s IT Solutions AT Spardat GmbH - initial API and implementation
 *******************************************************************************/

package at.spardat.xma.page;

import org.eclipse.swt.widgets.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.*;

import java.util.*;

import at.spardat.xma.boot.component.IDialog;
import at.spardat.xma.component.*;
import at.spardat.xma.mdl.UIDelegateClient;
import at.spardat.xma.mdl.list.ListUIDelegateClient;
import at.spardat.xma.mdl.simple.SimpleUIDelegateClient;

import org.eclipse.swt.*;

/**
 * This Class is the base class of all dialog Pages.
 * It implements the main livecycle protocoll for the dialog
 * and the handling of the corresponding SWT-Shell.
 *
 * @author s2877
 *
 */
public abstract class DialogPage extends PageClient implements IDialogPage {

    /**
     * The SWT-shell for the dialog.
     */
    Shell shell;

    /**
     * SWT-Style for the Shell.
     */
    int style;


    /** optional parent shell */
    Shell parentShell;

    /**
     * Flag indicating if the Dialog was finished with a save operation.
* true means ok
* false means the Dialog is still running or was canceld.
*/ boolean exitStatus; /** * The StatusBar of the Dialog. */ protected StatusBar statusBar; /** The SWT-Composite serving as parent for the StatusBar*/ protected Composite statusBarComposite; /** * The Widget which currently has the focus. */ private Control focusControl; /** * An ordered list of validation errors. Keys are Widget-objects (the * widgets that are in error), values are Strings describing the error. * The entries are ordered by the tab order of the widgets. */ private SortedMap validationErrors_ = new TreeMap(new TabOrderComparator()); /** * An ordered list of warnings. Keys are Widget-objects, * values are Strings describing the error. * The entries are ordered by the tab order of the widgets. */ private SortedMap warnings; /** * An ordered list of infos. Keys are Widget-objects, * values are Strings describing the error. * The entries are ordered by the tab order of the widgets. */ private SortedMap infos; // /** // * An ordered list of errors set by application code. Keys are Widget-objects (the // * widgets that are in error), values are Strings describing the error. // * The entries are ordered by the tab order of the widgets. // */ // private SortedMap appErrors_ = new TreeMap(new TabOrderComparator()); /** * Flag indicating that stateChandedBase() has to be called once more. */ private boolean rerunStateChanched=false; /** * Initializes a DialogPage inside a given Component. * * @param component the Component containing the DialogPage. * @param style The SWT-Style for the Shell of the DialogPage. * @param stateless indicating if this page is stateless on the server. * @throws IllegalArgumentException if component is null. */ public DialogPage(ComponentClient component, boolean stateless, int style) { super(component,stateless); if (component == null) throw new IllegalArgumentException("Component must not be null"); this.style=style; } /** * Initializes a DialogPage inside a given Component. * * @param component the Component containing the DialogPage. * @param parentShell the Shell which shall be the parent of the Shell of the DialogPage. * @param style The SWT-Style for the Shell of the DialogPage. * @param stateless indicating if this page is stateless on the server. * @throws IllegalArgumentException if component is null. */ public DialogPage(ComponentClient component, Shell parentShell, boolean stateless, int style) { super(component,stateless); if (component == null) throw new IllegalArgumentException("Component must not be null"); this.parentShell=parentShell; this.style=style; } /** * Initializes a DialogPage inside the same Component as the parent PageClient. * * @param parent the PageClient calling this DialogPage. * @param stateless indicating if this page is stateless on the server. * @param style The SWT-Style for the Shell of the DialogPage. */ public DialogPage(PageClient parent, boolean stateless, int style) { super(parent,stateless); this.style=style; } /** * Get the DialogPage of this Page. * * @return this. */ public IDialog getDialog() { if(dialog!=null) return dialog; else return this; } /** * Get the SWT-Shell of the DialogPage. For DialogPages the corresponding * SWT-Composite of the DialogPage is a Shell and will be returned. * * @return the SWT-Shell containing the widgets of this DialogPage. */ public Shell getShell() { if(dialog!=null) return dialog.getShell(); else return shell; } // /** // * Gets the StatusBar of this DialogPage. // * // * @return the SatusBar of the Dialog // */ // public StatusBar getStatusBar() { // return statusBar; // } /** * Sets the focus to the given control. * @param control the Control to set the focus to. */ public void setFocus(final Control control) { boolean success = control.setFocus(); if(!success) { getShell().getDisplay().asyncExec(new Runnable() { public void run() { if (!control.isDisposed()) control.setFocus(); } }); } } /** * Gets the Control on the DialogPage or a Subpage of it, which currently * has the focus. * @return the Control with the focus. */ public Control getFocusControl() { return focusControl; } /** * Sets the Control on the DialogPage of a Subpage of it, which currently * has the focus. The Control must allready have the focus. No SWT-Method will * be called. This method is reserverd for the runtime to keep track of the focus. * Do not call it directly! * @param control the Control with the focus. */ public void setFocusControl(Control control) { focusControl=control; } public Composite createComposite(Composite parentComp) { // if(dialog==null) { // shell = new Shell((Shell)parentComp,style); // composite = shell; // } else { composite = new Composite(parentComp, SWT.NONE); // } composite.setData(this); return composite; } /** * Creates the Widgets of the PageClient and all Subpages by calling * {@link #createWidgets()} on the PageClient and all Subpages. */ public void initGUI() { if(!hasModels()) { createModels(); } if(dialog==null) { if(shell==null||shell.isDisposed()) { if(parentShell!=null) { shell = new Shell(parentShell, style); } else if(parent!=null) { shell = new Shell(parent.getDialog().getShell(), style); } else { shell = new Shell(getComponent().getDisplay(), style); } } shell.setData(this); FormLayout layout = new FormLayout(); shell.setLayout(layout); DialogEventAdapter adapter = new DialogEventAdapter(this); shell.addShellListener(adapter); shell.addDisposeListener(adapter); // shell.addKeyListener(new KeyListener() { // public void keyPressed(KeyEvent e) { // if(((e.stateMask & (SWT.CONTROL|SWT.SHIFT))!=0) && e.keyCode == 'D') { // printData(); // } // } // public void keyReleased(KeyEvent e) {} // }); FormData data; if ( doCreateStatusBar() ) { statusBarComposite = new Composite(shell, SWT.NONE); statusBarComposite.setLayout(new FormLayout()); data = new FormData(); data.left = new FormAttachment(0, 0); data.right = new FormAttachment(100, 0); data.bottom = new FormAttachment(100, 0); statusBarComposite.setLayoutData(data); statusBar = new StatusBar(statusBarComposite, SWT.NONE); data = new FormData(); data.left = new FormAttachment(0, 0); data.right = new FormAttachment(100, 0); data.top = new FormAttachment(0, 0); data.bottom = new FormAttachment(100, 0); statusBar.setLayoutData(data); } composite = new Composite(shell,SWT.NONE); data = new FormData(); data.left = new FormAttachment(0, 0); data.right = new FormAttachment(100, 0); data.top = new FormAttachment(0, 0); data.bottom = doCreateStatusBar() ? new FormAttachment(statusBarComposite,0,SWT.DEFAULT) : new FormAttachment(100, 0); composite.setLayoutData(data); } super.initGUI(); attachUI(); } /** * Toggles if the status bar will be displayed or not. * @return false to hide the status bar */ protected boolean doCreateStatusBar() { return true; } /** * Method called to start the DialogPage. * The Dialog will be shown and executed. * For modal dialogs invoke() blocks until the Dialog is closed. * For nonmodal dialogs invoke() returns imediately. * * @return true if a nonmodal dialog is started successfully or * a modal dialog is closed via the ok-, save- or a similar button. * false if a modal dialog is canceld. */ public boolean invoke() { initGUI(); enterBase(); stateChangedBase(); updateErrorStatus(focusControl); shell.open(); setEventsEnabled(true); if((shell.getStyle() & (SWT.PRIMARY_MODAL|SWT.APPLICATION_MODAL|SWT.SYSTEM_MODAL)) != 0) { eventLoop(); return exitStatus; } return true; } /** * Execute the eventLoop and dispach all Events. Every modal SWT-Shell has its * own eventLoop which is implemented here. */ private void eventLoop() { Display display = shell.getDisplay(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } /** * Notify the PageClient and all Subpages of a possible Change in the PageModels * by calling {@link #determineState()} and {@link #stateChanged()} on the PageClient * and all Subpages. My be repeated by calling {@link #setRerunStateChanched()}. */ public void stateChangedBase() { do { rerunStateChanched=false; super.stateChangedBase(); } while(rerunStateChanched); } /** * Requests another call to {@link #determineState()} and {@link #stateChanged()} if called within one of * these methods. It has no effect if called outside these methods. * @since 1.7.0 */ public void setRerunStateChanched() { rerunStateChanched=true; } /** * Eventhandler called by SWT every time, the Shell is tried to be closed. * This Method calls {@link #close()} which my decide if the Shell will actually be closed. * It the Shell is not closed, {@link #stateChanged()} is called. * * @param event the SWT-Event that happended. */ void shellClosed(ShellEvent event) { try{ setEventsEnabled(false); try { event.doit = close(); } catch (Exception exc) { showException(exc); } if(event.doit) { exitStatus=false; leaveBase(); removeWidgetsBase(); notifyClose(); } else { stateChangedBase(); } } finally { if(getWidgets()!=null) setEventsEnabled(true); } } /** * Closes the dialog with exitStatus true. * */ public void closeOK() { if(dialog!=null) dialog.closeOK(); else { exitStatus=true; leaveBase(); removeWidgetsBase(); getShell().dispose(); } notifyClose(); } /** * Closes the dialog with exitStatus false. * */ public void closeCancel() { if(dialog!=null) dialog.closeCancel(); else { exitStatus=false; leaveBase(); removeWidgetsBase(); getShell().dispose(); } notifyClose(); } /** * Sends the closeEvent of the component, if this page is the main page * of an embeddable component. Automatically called by shellClosed(), closeOK(), closeCancel() */ private void notifyClose() { ComponentClient comp = getComponent(); if(comp instanceof EmbeddableComponent) { if(((EmbeddableComponent)comp).getMainPage()==this) { comp.notifyClosed(); } } } /** * Eventhandler called by SWT when the Shell is disposed. * * @param event the SWT-Event that happended. */ void widgetDisposed(DisposeEvent event) { // removeWidgetsBase(); moved to close,closeOK,closeCancel } /** * Notify the PageClient and all Subpages, that the Widgets are disposed * by calling {@link #removeWidgets()} on all Subpages and the PageClient. */ public void removeWidgetsBase() { super.removeWidgetsBase(); statusBar=null; statusBarComposite=null; focusControl=null; } /** * This method will be called every time the Shell is tried to be closed. * It is intended to be implemented by the application programmer if he * wants to react on this event. The application programmer my decide if * the shell will actually be closed. * * @return true means it is ok to close the Shell * false means do not close the Shell */ protected boolean close() { return true; } /** * Indicates if the dialog is shown to the user. Use this method to * determine if a modeless dialog is open or closed. * * @return true if the dialog is visible to the user. */ public boolean isVisible() { return shell!=null && shell.isVisible(); } /** * Returns the exitStatus of the DialogPage. */ public boolean getExitstatus() { return exitStatus; } /** * Returns the internationalized title of this dialog. * The title is read form the properties file. */ public String getDialogTitle() { Locale locale = getComponent().getSession().getContext().getLocale(); ResourceBundle messages = ResourceBundle.getBundle(getComponent().getClass().getName(),locale); String className = getClass().getName(); className=className.substring(className.lastIndexOf('.')+1); return messages.getString(className); } /** * Returns the number of Errors in this dialog including its subpages. * This is the count of widgets with errors. */ public int getErrorCount () { if(dialog!=null) return dialog.getErrorCount(); else return validationErrors_.size(); } /** * Removes a validation error for a particular widget. This method * is for internal use only. Calling it causes undefined behaviour! * * @param widget the widget where to clear the error state. */ public void clearValidationErrorImpl (Widget widget) { if(dialog!=null) ((IDialogPage)dialog).clearValidationErrorImpl(widget); else errorsRemove(widget,0); } /** * Sets a validation error for a particular widget. This method * is for internal use only. Calling it causes undefined behaviour! * * @param widget the widget where the error occured * @param errorText indicates the error */ public void setValidationErrorImpl (Widget widget,String errorText) { if(dialog!=null) ((IDialogPage)dialog).setValidationErrorImpl(widget,errorText); else errorsPut(widget,errorText,0); } /** * Determines if a validation error currently exists for a given widget. * Note that validation is disabled automatically for readonly widgets, so * readonly widgets never have validation errors. * @param widget to search for validation errors * @return true if the validator reported an error for the given widget. false otherwise. */ public boolean hasValidationError(Widget widget) { if(dialog!=null) { return ((IDialogPage)dialog).hasValidationError(widget); } else { String[] list = (String[]) validationErrors_.get(widget); return list!=null&&list[0]!=null; } } /** * Returns some error text. If there is an error * for the provided control, this is returned. Otherwise the * error for the widget next in tab order is returned. If one control * contains a validation error and an error set via {@link #setError(Widget, String)}, * the validation error is returned. * * @param control the UI control (SWT widget) whose error is most wanted. * @return null, if there are no validation errors. Otherwise, a * String is returned following the algorithm described * above. */ private String getError (Widget control) { if (validationErrors_.size() == 0) return null; String[] list; SortedMap tail = validationErrors_.tailMap(control); if(tail.size()>0) { list = (String[])tail.values().iterator().next(); } else { list = (String[])validationErrors_.values().iterator().next(); } if(list[0]!=null) return list[0]; else return list[1]; } /** * Updates the error message in the StatusBar. If there are more then one * error, the error belonging to the widget which currently owns the focus * is displayed. If there is no error on the PageClient, the error message is cleared. * * @param current the Widget which currently owns the focus. */ public void updateErrorStatus(Widget current) { if ( statusBar == null ) return; String error = getError(current); if (error != null) { statusBar.setError(error); } else { String warning = getWarning(current); if(warning!=null) { statusBar.setWarning(warning); } else { String info = getInfo(current); if(info!=null) { statusBar.setInfo(info); } else { statusBar.clear(); } } } statusBar.message.setToolTipText(getStatusMessages(current)); } /** * Stores an error * @param widget the widget where the error occured * @param error the errortext to display * @param type 0=validationError, 1=appError */ private void errorsPut(Widget widget,String error,int type) { String[] list = (String[]) validationErrors_.get(widget); if(list==null) { list=new String[2]; validationErrors_.put(widget,list); } list[type]=error; if(widget!=null) { Object delegate = widget.getData(); if(delegate instanceof UIDelegateClient) ((UIDelegateClient)delegate).setErrorColor(true); } } /** * Removes an error * @param widget the widget where the error occured * @param type 0=validationError, 1=appError */ private void errorsRemove(Widget widget, int type) { String[] list = (String[]) validationErrors_.get(widget); if(list!=null) { list[type]=null; if(list[0]==null&&list[1]==null) { validationErrors_.remove(widget); if(widget!=null) { Object delegate = widget.getData(); if(delegate instanceof UIDelegateClient) ((UIDelegateClient)delegate).setErrorColor(false); } } } else { // color must be set even if error state does not change if(widget!=null && !widget.isDisposed()) { // maybe the widget changed from editable to not editable Object delegate = widget.getData(); if(delegate instanceof UIDelegateClient) ((UIDelegateClient)delegate).setErrorColor(false); } } } /** * Removes the error for the given widget. * * @param widget the widget where to clear the error state. */ public void clearError (Widget widget) { if(dialog!=null) dialog.clearError(widget); else errorsRemove(widget,1); } /** * Sets an error for the given widget. The error is shown in the status line. * Only one error can be shown at a time. The precedence rules are:
* 1) the validation error of the widget owning the focus
* 2) the error of the widget owning the focus set with this method
* 3) the error of the next widget in the tab order containing an error
* 4) the error set for the null-widget.
* * @param widget the widget where the error occured. Null is allowd if the error can not * be associated with a widget. * @param errorText indicates the error */ public void setError (Widget widget,String errorText) { if(dialog!=null) dialog.setError(widget,errorText); else errorsPut(widget,errorText,1); } /** * Resets all widgets containing values with validation errors to their * last known valid value from the model. */ public void overrideErrorsFromModel() { ArrayList errors = new ArrayList(validationErrors_.keySet()); for(Iterator it=errors.iterator();it.hasNext();) { Widget widget = (Widget)it.next(); Object delegate = widget.getData(); if(delegate instanceof SimpleUIDelegateClient) { ((SimpleUIDelegateClient)delegate).resetWidgetFromModel(); } else if(delegate instanceof ListUIDelegateClient) { ((ListUIDelegateClient)delegate).resetWidgetFromModel(); } } } /** * Sets an warning for the given widget. The warning is shown in the status line, * if currently no error has to be shown. * Only one error or warning can be shown at a time. The precedence rules are:
* 1) any error of any widget (see {@link #setError(Widget, String)}
* 2) the warning of the widget owning the focus
* 3) the warning of the next widget in the tab order containing a warning
* 4) the warning set for the null-widget.
* * @param widget the widget the warning is associated with. Null is allowed. * @param warningText indicates the warning */ public void setWarning(Widget widget,String warningText) { if(dialog!=null) { ((IDialogPage)dialog).setWarning(widget,warningText); } else { if(warnings==null) { warnings = new TreeMap(new TabOrderComparator()); } warnings.put(widget, warningText); } } /** * Removes the warning for the given widget. * * @param widget the widget where to clear the warning. */ public void clearWarning(Widget widget) { if(dialog!=null) { ((IDialogPage)dialog).clearWarning(widget); } else if(warnings!=null) { warnings.remove(widget); } } /** * Returns a warning text. If there is a warning associated * to the provided control, this is returned. Otherwise the * warning for the widget next in tab order is returned. * * @param control the UI control (SWT widget) whose warning is most wanted. * @return null, if there are no warnings. Otherwise, a * String is returned following the algorithm described * above. */ private String getWarning (Widget control) { if (warnings==null || warnings.size() == 0) return null; SortedMap tail = warnings.tailMap(control); if(tail.size()>0) { return (String)tail.values().iterator().next(); } else { return (String)warnings.values().iterator().next(); } } /** * Sets an info for the given widget. The info is shown in the status line, * if currently no error or warning has to be shown. * Only one error, warning or info can be shown at a time. The precedence rules are:
* 1) any error or warning of any widget (see {@link #setWarning(Widget, String)}
* 2) the info of the widget owning the focus
* 3) the info of the next widget in the tab order containing a warning
* 4) the info set for the null-widget.
* * @param widget the widget the info is associated with. Null is allowed. * @param infoText indicates the info */ public void setInfo(Widget widget,String infoText) { if(dialog!=null) { ((IDialogPage)dialog).setInfo(widget,infoText); } else { if(infos==null) { infos = new TreeMap(new TabOrderComparator()); } infos.put(widget, infoText); } } /** * Removes the info for the given widget. * * @param widget the widget where to clear the info. */ public void clearInfo(Widget widget) { if(dialog!=null) { ((IDialogPage)dialog).clearInfo(widget); } else if(infos!=null) { infos.remove(widget); } } /** * Returns an info text. If there is a info associated * to the provided control, this is returned. Otherwise the * info for the widget next in tab order is returned. * * @param control the UI control (SWT widget) whose info is most wanted. * @return null, if there are no infos. Otherwise, a * String is returned following the algorithm described * above. */ private String getInfo (Widget control) { if (infos==null || infos.size() == 0) return null; SortedMap tail = infos.tailMap(control); if(tail.size()>0) { return (String)tail.values().iterator().next(); } else { return (String)infos.values().iterator().next(); } } /** * Returns all validation errors, errors, warnings and infos of * the dialog seperated by newlines. The order is the same as * the precedence order for the status bar. * @param current the control witch currently has the focus. */ private String getStatusMessages(Widget current) { StringBuffer messages = new StringBuffer(); // errors SortedMap tail = validationErrors_.tailMap(current); for(Iterator it=tail.values().iterator();it.hasNext();) { String[] errors = (String[]) it.next(); if(errors[0]!=null) messages.append(errors[0]+"\n"); if(errors[1]!=null) messages.append(errors[1]+"\n"); } SortedMap head = validationErrors_.headMap(current); for(Iterator it=head.values().iterator();it.hasNext();) { String[] errors = (String[]) it.next(); if(errors[0]!=null) messages.append(errors[0]+"\n"); if(errors[1]!=null) messages.append(errors[1]+"\n"); } // warnings if(warnings!=null) { tail = warnings.tailMap(current); for(Iterator it=tail.values().iterator();it.hasNext();) { messages.append(it.next()+"\n"); } head = warnings.headMap(current); for(Iterator it=head.values().iterator();it.hasNext();) { messages.append(it.next()+"\n"); } } // infos if(infos!=null) { tail = infos.tailMap(current); for(Iterator it=tail.values().iterator();it.hasNext();) { messages.append(it.next()+"\n"); } head = infos.headMap(current); for(Iterator it=head.values().iterator();it.hasNext();) { messages.append(it.next()+"\n"); } } // remove last "\n". if(messages.length()>0) { messages.delete(messages.length()-1, messages.length()); } return messages.toString(); } /** * Centers this DialogPabe on the screen. If more than one monitor is used, * the dialog is centered on the primary monitor. */ public void centerShell() { centerShell(getShell()); } /** * Centers the given shell on the screen. If more than one monitor is used, * the shell is centered on the primary monitor. */ public static void centerShell(Shell shell) { centerShell(null,shell); } /** * Centers the given shell on the parent shell. * * If the parent shell is null, the shell is centered on the display. * * If more than one monitor is used, the shell is centered on the * primary monitor. */ public static void centerShell(Shell parentShell, Shell shell) { Rectangle parentRect; Rectangle splashRect = shell.getBounds(); if (parentShell == null) { parentRect = shell.getDisplay().getPrimaryMonitor().getBounds(); } else { parentRect = parentShell.getBounds(); } int x = parentRect.x + (parentRect.width - splashRect.width) / 2; int y = parentRect.y + (parentRect.height - splashRect.height) / 2; shell.setLocation(x, y); } /** * Comparator class used to order SWT-Components by their * tab order in this dialog. * @author s2877 */ class TabOrderComparator implements Comparator { /** * Determines wich of the both controls comes first in the tab * order of this dialog. * @param o1 the first object to be compared. * @param o2 the second object to be compared. * @return -1 if o1 comes first in tab order, 1 if o2 comes * first in tab order, 0 if o1 and o2 are identical. * if both controls are not defined in the tab order, * they are sorted by their respective hashValues. * @throws ClassCastException if the arguments are not SWT-Controls. */ public int compare(Object o1, Object o2) { if(o1==o2) return 0; if(o1==null) return 1; if(o2==null) return -1; Widget first = firstTab(composite,(Widget)o1,(Widget)o2); if(first==o1) return -1; if(first==o2) return 1; // no defined tab-order -> sort by hashCode int h1=o1.hashCode(); int h2=o2.hashCode(); if(h1h2) return 1; // identical hashCode -> consider equal return 0; } /** * Returns that control given in the parameters which is found first in * the tab order of the given composite or one of its subcomposits. * @param comp the composite where to search for the controls * @param c1, c2 the controls to deside which comes first in tab order * @return the control of the parameters which is found first in the tab order * or null if none of them is found. */ private Widget firstTab(Composite comp,Widget c1, Widget c2) { Control[] conts = comp.getTabList(); for(int i=0;i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy