mercredi 31 août 2011

DynamicForm brief

Un formulaire de type DynamicForm gère une collection d’objets de type FormItem qui seront rendus en tant qu’éléments de saisie utilisateur.
L’objet DynamicForm gère tous les aspects du formulaire :
  • La disposition
  • Les valeurs
  • La validation.
  • La liaison de données.
Les principaux FormItem sont :
CanvasItem, CheckboxItem, ColorPickerItem, DateItem, FloatItem, HiddenItem, IntegerItem, RadioGroupItem, SelectItem, SpacerItem, StaticTextItem, TextAreaItem, TextItem

Et pour tous les autres cas CanvasItem est très pratique car il permet "d'intégrer " toute sorte de Widget n'héritant pas de FormItem. on peut ainsi personnaliser un formulaire en mettant une zone avec plusieurs boutons dans une seule colonne par exemple ou mettre un ListGrid etc.. (voir aussi ici)

 
Une DynamicForm  est composée de colonnes dans lesquelles sont disposés les différents contrôles de saisie utilisateurs dérivant tous de FormItem. Par défaut il y a deux colonnes, la plupart des contrôles occupent deux colonnes, une pour le titre et une pour la zone de saisie. Mais rien n'empêche de disposer d'un nombre quelquonque de colonnes(pair ou impair).
On peut disposer les FormItems comme on le désire en utilisant setStartRow, setEndRow et des objets de type SpacerItem ou RowSpacerItem pour créer des « trous » d'une cellule ou d'une rangée complète dans la grille.
En ce qui concerne le style, les FormItems peuvent être personnalisés séparément  pour leur titre ou pour la « boite de saisie ».
Le titre par défaut effectue automatiquement un retour à la ligne, propriété que l’on peut modifier à l’aide de  setWrapTitle (false supprime le retour automatique).
Quand à la boite de saisie on peut utiliser soit  setTextBoxStyle et pour les DateItem  en mode "représentation texte" définir un TextItem stylé comme on le désire et l'attribuer à ce DateItem par
setTextFieldProperties(TextItem textfieldproperties).
La saisie peut être rendue obligatoire (apparition d’une signalisation lors de la validation) par setRequired (le message à afficher est indiquer avec la méthode setRequiredMessage).
Lors de la création de formulaire en mode lecture on peut inhiber la saisie  d’un champ en utilisant setDisabled(true). Dans un tel cas si l’on désire que l’apparence du champ ne soit pas modifiée il faut utiliser setShowDisabled(false) et la valeur du champ ne sera plus grisée et donc plus lisible.
Une sorte de fieldSet peut être inséré à l'aide des méthodes setIsGroup et setGroupTitle

 A DynamicForm  is a set of columns where your different form input elements are arranged. By default there are two column, most of the formItem use two columns (one for the title and the other for the input element) but you can set any number of column and an input control can span several column.By using setStartRow and setEndRow you can setup the controls as you want. "holes" can be introduced by adding SpacerItem (a cell "hole") or RowSpacerItem(a row "hole") too.
For the cases where you don't find what you are looking for in the FormItems, CanvasItem is really practise when you want to use a Control(widget) which doesn't extend FormItem whith it you can add anything you want in your form as a ListGrid for example. (you can have a look here )
The title of the formItem wrap automatically by default and we can use setWrapTitle(false) to have a one line only title.
Some sort of fieldSet can be put around the form by using the methods setIsGroup and setGroupTitle.  

mardi 30 août 2011

List2List transfer from one list to another / first steps

    public static void testListeAListe(){
        String[] laListeSource = new String[5];
        laListeSource[0] = "Pharmacie";
        laListeSource[1] = "Médecine";
        laListeSource[2] = "Osthéopathie";
        laListeSource[3] = "Dentiste";
        laListeSource[4] = "Prothèse";
        ListeAListe lesListes = new ListeAListe(laListeSource,null,"Rubriques","Panier",300);
        lesListes.draw();
    }







public class ListeAListe extends HLayout {
    private final ListGrid source;
    private final ListGrid destination;
    private RecordList sourceList;
    private RecordList destinationList;
    private String titreSource;
    private String titreDestination;
    private ListeAListe moimeme;
    private DataSource sourceDS;
    private DataSource destinationDS;

    public ListeAListe(String[] sourceRecords, String[] destinationRecords,
        String titreSource, String titreDestination, int hauteur) {
        super();
        this.moimeme = this;
        sourceDS = new DataSource();
        DataSourceField valeurSource = new DataSourceField("valeur", FieldType.TEXT); 
        valeurSource.setPrimaryKey(true);
        valeurSource.setTitle(titreSource);
        sourceDS.setFields(valeurSource);
        sourceDS.setClientOnly(true);
        destinationDS = new DataSource();
        DataSourceField valeurDestination = new DataSourceField("valeur", FieldType.TEXT); 
        valeurDestination.setPrimaryKey(true);
        valeurDestination.setTitle(titreDestination);
        destinationDS.setFields(valeurDestination);
        destinationDS.setClientOnly(true);
        this.setWidth("500px");
        this.setHeight(hauteur);
        source = new ListGrid();       
        source.setWidth("35%");
        sourceList = new RecordList();
        for (String s: sourceRecords)
        {
            ListGridRecord r = new ListGridRecord();
            r.setAttribute("valeur",s);
            sourceList.add(r);
        }

        sourceDS.setTestData(sourceList.toArray());
        source.setDataSource(sourceDS);
        source.setAutoFetchData(true);
        source.setEmptyMessage("Liste vide");

        destination = new ListGrid();
        destination.setWidth("35%");
        destinationList = new RecordList();
        destinationDS.setTestData(destinationList.toArray());
        destination.setDataSource(destinationDS);
        destination.setAutoFetchData(true);
        destination.setEmptyMessage("Liste vide");


        VStack transferStack = new VStack(3); 
        transferStack.setWidth("30%"); 
        transferStack.setAlign(VerticalAlignment.CENTER);

        TransferImgButton right = new TransferImgButton(TransferImgButton.RIGHT);
        right.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                ListGridRecord selection = source.getSelectedRecord();
                if(selection != null){
                    if(!recordExistant(selection,destinationList)) {
                        sourceDS.removeData(selection);
                        destinationDS.addData(selection);    
                        destination.selectRecord(selection, false);
                    }
                }

            }
        });

        TransferImgButton left = new TransferImgButton(TransferImgButton.LEFT);
        left.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                ListGridRecord selection = destination.getSelectedRecord();
                if(selection != null){
                    if(!recordExistant(selection,sourceList)) {
                        destinationDS.removeData(selection);
                        sourceDS.addData(selection);   
                        source.selectRecord(selection, false);
                    }
                }

            }
        });

        TransferImgButton rightAll = new TransferImgButton(TransferImgButton.RIGHT_ALL);
        rightAll.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                source.selectAllRecords();
                ListGridRecord[] records = source.getSelection();
                for(Record record : records){
                    sourceDS.removeData(record);
                    destinationDS.addData(record); 
                    destination.selectRecord(record, false);
                }

            }
        });

        TransferImgButton leftAll = new TransferImgButton(TransferImgButton.LEFT_ALL);
        leftAll.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                destination.selectAllRecords();
                ListGridRecord[] records = destination.getSelection();
                for(Record record : records){
                    destinationDS.removeData(record);
                    sourceDS.addData(record); 
                    source.selectRecord(record, false);
                }

            }
        });

        transferStack.addMember(right);
        transferStack.addMember(left);
        transferStack.addMember(rightAll);
        transferStack.addMember(leftAll);

        this.addMember(source);
        this.addMember(transferStack);
        this.addMember(destination); 

    }

    protected boolean recordExistant(ListGridRecord selectedRecord, RecordList liste) {
        if(liste.contains(selectedRecord)){
            return true;
        }else{
            return false;
        }
    }
}

vendredi 26 août 2011

TreeGrid Empty message

Un truc un peu vicieux:
J'avais utilisé setEmptyMessage pour indiquer que les données étaient en cours de chargement (pas de datasource, juste un appel RPC avec une boucle dans le onSuccess de la fonction de callBack): pas de problème l'affichage se faisait .
Mais j'ai du ensuite montrer la racine mais surtout afficher quelquechose d'autre que le folder, donc j'ai créer un TreeNode avec le titre qui allait bien et un icone adequat........... mais la plus de empty message car il y a dès le début ce TreeNode (enfin c'est ce que je pense).
Donc utilisation d'une fenêtre "Patientez"  sur laquelle j'invoque show() au départ  de ma requete et hide() une fois que le TreeGrid a reçu ses données du Tree. J'ai utilisé le eventBus et ma fenêtre est instanciée à l'ouverture de l'application pour être utilisée quand j'en ai besoin.

A nasty thing:
 I was using setEmptyMessage to prompt the user when loading data (no dataSource, only RPC with a loop in the onSucess of the callBack method. No problem the mesage was there.
Later I've beeen asked to add a root with a name and icon. So iI create a TreeNode and set the root of the Tree. But now no more message (I still have to investigate this...)
Anyway I add a "wait please" window with an image and a variable message. This window is instanciated at the beginning of the app. I used eventBus event to hide it or show when I need to ask the user to be patient.


mercredi 24 août 2011

Eclipse: Problèmes de développement - Development troubles

Ayant des soucis depuis le début avec ma configuration Eclipse en mode no-server avec Tomcat 7 (difficultés lors du premier run au redémarrage du système) j'ai découvert bien tard (voir le lien ci dessous) l'ongler Overview (double clic sur le serveur dans la fenêtre Server). Dans la rubrique Server Locations j'ai modifié la sélection pour Use Tomcat Installation et dans Server Options coché Publish module context to separate XML files. Et depuis je n'ai plus de problème.....

lien qui m'a déniaisé:
http://stackoverflow.com/questions/5492955/trying-to-get-tomcat-7-working-with-eclipse-3-6-1


From the beginning I got trouble at the first run of my app in Dev mode with -noserver option with Tomcat7 server. I discover with this link the Server config tab of Eclipse. So I modify to give to Eclipse the control on Tomcat in place of running it in worspace/metadata/plugin...... and since No problems


vendredi 5 août 2011

EventBus code

If something need to be clarified(and I supposed it should be) don't hesistate to write comments

(Update on 2012 October the 2nd)
MainPanel which implements the interface ActionChangedEventHandler to handle all the business events which are related to new action taken by the user.:
public class MainPanel extends VLayout implements ActionChangedEventHandler {

 ------
   

    public MainPanel(){
       
  //My MainPanel "subscribe" to the business event ACTION_CHANGED
 MainPanel.eventBus.addHandler(ActionChangedEvent.TYPE, this);     

 .........
ActionChanged event handler implementation in MainPanel: Everytime this business event will be triggered, my MainPanel will excute this bit of code.
I have several "subevent categories" defined by the property action of the event and which are defeined in the enum BusEventsAction .
@Override
    public void onActionChanged(ActionChangedEvent event) {
        switch(event.getAction()){
        case CATALOG:
            ContentPanelType1 type1= new ContentPanelType1 ();
            chargePanel(type1);
            break;
        case CATEGORY:
           ContentPanelType2 type2= new ContentPanelType2 ();
            chargePanel(type2);
            break;
        case VARIABLES:
........

            break;

ContentPanel:
public class ContentPanelType1 extends MyAppContentPanel implements Type1EventEventHandler
public ContentPanelType1 (){
MainPanel.eventBus.addHandler(Type1Event.TYPE, this);            initializeComponent()
    }

Business event handler (in this case the interface define one method called processRequest):
@Override
    public void processRequest(Type1Event event) {
        SC.say("Coucou");
       
    }

ActionChanged Event Handler interface:
public interface ActionChangedEventHandler  extends  EventHandler {
    void onActionChanged(ActionChangedEvent event);

}
ActionChanged  Event class:
public class ActionChangedEvent extends  GwtEvent<ActionChangedEventHandler> {
    public static final Type<ActionChangedEventHandler> TYPE = new Type<ActionChangedEventHandler>();
   
    private BusEventsAction action;

    public BusEventsAction getAction() {
        return action;
    }

    public void setAction(BusEventsAction action) {
        this.action = action;
    }

    @Override
    public Type getAssociatedType() {       
        return TYPE;
    }

   
    @Override
    protected void dispatch(ActionChangedEventHandler handler) {
        handler.onActionChanged(this);
       
    }

}

Bus events   Enum
public enum BusEvents { 
  ACTION_CHANGED(0),
  CATEGORY_CHANGED(1),
  VARIABLE_CHANGED(2),
  BASES_CHANGED(3),
  USERS_CHANGED(4),
  NEWS_CHANGED(5),
.....
 final private int  value;
   BusEvents( int value) {        
        this.value = value;
    }
   
    public int getValue(){
        return value;
    }
}
......
Bus events actions  Enum
public enum BusEventsAction { 
  CATALOGUE_VARIABLES(0),
  CATALOGUE_CATEGORIES(1),
  USERS(2),
      
.....
   

Business event Triggering:
Suppose I have a button which need to bring the variables catalog panel as the content I will do the following:
btnCatalog.addClickHandler(new ClickHandler() {
@Override
  public void onClick(ClickEvent event) {
//Instantiate an event
    ActionChangedEvent e = new ActionChangedEvent();
//set the sub event type
    e.setAction(BusEventsAction. CATALOGUE_VARIABLES);
//get the EventBus instance and fire the event    
    MainControl.getEventBus().fireEvent(e);
 }
});

Any panel implementing the ActionCHangeEventHandler will  process this event in its onActionChanged(ActionChangedEvent event) method and decide depending of the type of action if it will react to the event or not.

EventBus

If something need to be clarified(and I supposed it should be) don't hesistate to write comments

(mise à jour le 02/10/2012)
Architecture de mes Ihm:
Après passage dans une phase d'authentification on a accès au panel Principal: MainPanel.
Celui-ci contient une zone de contenu ContentPanelHolder qui recoit des panels ayant comme classe mère une classe abstraite MyAppContentPanel.
 Chacun de ces panels de contenu possède un jeu de boutons de commandes donnant accès aux fonctionnalités correspondantes aux droits de l'utilisateur.
Chaque bouton de commande va générer un événement métier de type ActionChangedEvent, cette classe d'événement possède un accesseur permettant de préciser la fonctionalités désirée ce qui correspondra donc à différents panels à charger dans le ContentPanelHolder.
public void setAction(BusEventsAction action) {
        this.action = action;
    }   
    @Override
    public Type getAssociatedType() {       
        return TYPE;
    } 

Bien sur les ContentPanels sont eux intéressés à recevoir les événements concernant la mise à jour des données .
L'EventBus permet de transformer les événements "utilisateurs" en "événements métier", de les déclencher et d'éviter de coupler la source des événements aux "abonnés"  qui vont les traiter.  On délègue la distribution à l'EventBus. Lorsque l'on a besoin de rajouter dans un composants (le plus souvent un panel) un comportement déclenché par tel ou tel événement, il suffit de faire implémenter l'interface du gestionnaire correspondant (handler) et d'inscrire le handler auprès de l'EventBus et d'écrire le code à exécuter en réaction à l'événement.
(update on 2012 October the 2nd )
UI Architecture:
After passing through an authentification stage the MainPanel is displayed.
This one cinlude a content place  ContentPanelHolder which is loaded with panels extending an abstract class MyAppContentPanel.
Each of this content panels has a set of command buttons corresponding to the actions authorized by the user's right.
Each button will trigger a business event of  ActionChangedEvent, this event class get  accessors to set and obtain the action required which in turn correspond to different panels to load in the ContentPanelHolder.
public void setAction(BusEventsAction action) {
        this.action = action;
    }   
    @Override
    public Type getAssociatedType() {       
        return TYPE;
    }

Besides the ContentPanels will subscribe to data related business events...
L'EventBus allow to transform the "user" events into "business events", to trigger them and to avoid to couple the events source with the "listenners" which will process them.  We delegate the dispatching to the EventBus. When we need to add in a component (mainly panels) a behavior triggered by an event we just have to do the necessary in the panel to implemnts the handler interface, declare the handler to the EventBus and write the code to execute in response to the event.

lundi 1 août 2011

Passage en 100% SmartGwt : Une zone Bouton

Suite à l'impossibilité de gérer le mélange des Composants Smart avec ceux de Gwt, toute l'application est modifiée pour n'utiliser que des composants SmartGwt.

1ère modification une zone cliquable (sorte de maxi bouton ) composé d'une image et d'un texte
public class MyButton extends Canvas {
public MyButton(String image,String libelle){
        super();
        this.setHeight(128);
        this.setWidth(128);
        this.setStyleName("plBoutonAction");
        VLayout v = new VLayout();
        HLayout h = new HLayout();        
        h.setAlign(Alignment.CENTER);
        h.setCursor(Cursor.HAND);
        v.setWidth(128);
        v.setHeight(128);
        v.setAlign(Alignment.CENTER);
                    
        
        img.setID("img_du_Bouton");
        img.setWidth(48);
        img.setHeight(48);
        img.setSrc(image);
        img.setCursor(Cursor.HAND);
        

        label.setHeight(48);        
        label.setAlign(Alignment.CENTER);
        label.setContents(libelle);
        label.setCursor(Cursor.HAND);
        
        h.addMember(img);
        v.addMember(h);
        v.addMember(label);        
        this.addChild(v);
        //Css style pour changer la bordure lors du Hover
        this.setStyleName("plBoutonAction");
        
    }

 Classe css:
.plBoutonAction {
    border: 1px solid #F1F1F1;
}

.plBoutonAction:hover {
    border: 1px solid #898989;
}

  
    J'ai essayé d'utiliser une classe plPointer qui ferait cursor: pointer; mais les styles des composants inclus ne semblent pas être hérités.
    Donc finalement j'utilise la méthode setCursor sur chaque sous-composant.
Cette classe possède plusieurs constructeurs avec différents paramètres width,height, container, rang du bouton dans un panneau de n boutons etc...


Further to many troubles due to a mix of Gwt/Smartgwt, I switch the application to 100% Smartgwt (except for upload).
I had some sort of Button zone which include an image and a label with a grey border appearing with the mouse hover..
I got trouble using a Css class which would do: cursor: pointer.
Style don't seem to be inherited by the member of a layout.
So at the end I use the setCursor method on each sub component.
This class get several constructors with different parameters such as width, height, container panel, button rank in a xx buttons panel etc...