Developpez.com - 4D
X

Choisissez d'abord la catégorieensuite la rubrique :


Code source de l'éditeur d'Etats Rapides - Partie 1 (4D 2004)

Date de publication : Juillet 2004

Par Olivier DESCHANELS (Responsable Programme 4D S.A.)
 

Les états rapides ont été révisés pour la version 2003 avec notamment un assistant permettant de créer plus aisément et rapidement les états désirés.Cet assistant est entièrement réalisé grâce à 4ème Dimension, vous pouvez donc réaliser par vous-même un assistant similaire. Cependant comme cet assistant nécessite un certain travail nous avons décidé de vous donner dans la présente note technique le code source. Vous pourrez donc l'intégrer dans vos bases et y apporter les modifications souhaitées.

I. Introduction
II. Le dialogue principal
III. La liste des tables et des champs
IV. Afficher la liste des tables et des champs
V. Afficher la liste des champs liés
Utilisation des listes des tables et des champs
VI. Utiliser les paramètres avancés
VII. Traduction de l’interface
VIII. Conclusion
IX. Base exemple


I. Introduction

Les états rapides ont été révisés pour la version 2003 avec notamment un assistant permettant de créer plus aisément et rapidement les états désirés.
Cet assistant est entièrement réalisé grâce à 4ème Dimension, vous pouvez donc réaliser par vous-même un assistant similaire. Cependant comme cet assistant nécessite un certain travail nous avons décidé de vous donner dans la présente note technique le code source. Vous pourrez donc l'intégrer dans vos bases et y apporter les modifications souhaitées. Ce code source est en 4D 2004.

Dans cette note technique, nous allons détailler l'architecture générale de l'assistant des états rapides en insistant sur certains points particuliers nous semblant plus ardus à comprendre. Néanmoins nous recommandons au lecteur de garder en référence la documentation sur les commandes liées aux états rapides ; pour le néophyte dans le domaine, une lecture préalable est fortement recommandée.

La base fournie en annexe de cette note technique est principalement constituée d'un dialogue et d'une série de méthodes. Parmi ces méthodes, celle qui est nommée __Open_dialog permet de lancer l'assistant.
Toutes les autres méthodes sont préfixées par NQR_  afin de préparer l'éventuelle mise en composant du code.


II. Le dialogue principal

Ce dialogue principal nommé "NQR_Dialog" (dans la table [Interface]) est constitué de 15 pages plus une page zéro.

Le dialogue contient à la fois l'assistant et le mode manuel obtenu dès l'appel aux états rapides.
Le mode manuel est codé en page 1 du dialogue. L'assistant occupe les pages 2 à 15.
La page zéro est pour sa part présente en fond de chaque page.
Une méthode formulaire complète le dialogue. Cette méthode formulaire est construite très classiquement autour d'un Au cas ou réagissant à quatre événements : Sur chargement, Sur libération, Sur redimensionnement, Sur appel zone du plug in.

Lors de l'ouverture du dialogue, l'événement Sur chargement permettra d'initialiser l'ensemble des variables nécessaires. Symétriquement l'événement Sur libération permettra, lors de la fermeture du dialogue, de décharger de la mémoire les objets construits au cours de la navigation dans les pages.
Lors du redimensionnement du formulaire, certaines pages nécessitent un re-calcul des objets ; c'est l'objet de l'événement Sur redimensionnement.
L'événement Sur appel zone de plugin est déclenché suite à certaines actions dans l'éditeur d'états rapide ; nous en reparlerons.



La page zéro est principalement constituée de :

  • la zone de plug-in nommée "nqr_area"

  • la zone de navigation de l'assistant constituée d'images et de boutons.

Pictures 0452x0292



La zone de plug in est de type "Report" c'est-à-dire l'éditeur d'états rapides. Cette zone représentera graphiquement le rapport en cours de construction et permettra de le modifier via une barre de menus et des barres d'outils.

Regardons à présent la page une du dialogue :

Pictures 0458x0294



Cette page semble assez confuse au premier abord. En effet nous y voyons en même temps le bouton permettant l'accès à l'assistant et le bouton de retour au mode manuel. Regardons la même page 1, en masquant la page zéro grâce à l'option "page 0" du menu contextuel :

Pictures 0453x0364



Pictures 0454x0292



La page est à présent beaucoup plus claire ! Nous retrouvons dans cette page l'ensemble des boutons proposés par le mode manuel. La zone blanche en haut sera réservée à l'éditeur d'états rapides (la zone de plug-in). Comme la taille de la zone de plug-in diffère entre le mode manuel et l'assistant, il est nécessaire de la retailler lors d'un passage d'un mode à l'autre. Ainsi le code suivant permet d'adapter les dimensions lors du chargement du dialogue (le dialogue s'ouvre en mode manuel) :

      `le dialogue s'ouvre en mode manuel ; on adapte la hauteur en conséquence
   LIRE RECT OBJET(nqr_area;$g;$h;$d;$b)
   DEPLACER OBJET(nqr_area;$g;$h;$d;213;*)

Ces lignes se trouvent dans la partie Sur chargement de la méthode formulaire.
De la même façon la méthode objet associée au bouton d'appel de l'assistant (bouton avec le chapeau et la baguette de magicien) se charge de changer la taille de la zone :

      `nous entrons dans l'assistant ; on y adapte la taille de la zone de plugin
   LIRE RECT OBJET(nqr_area;$g;$h;$d;$b)
   DEPLACER OBJET(nqr_area;$g;$h;$d;$b-110;*)

Ce code est la première étape de l'initialisation de l'assistant et se trouve dans la méthode NQR_MP_Etape_Init.

Enfin lors du retour en mode manuel le code ci-dessous est appelé :

      `nous revenons au mode manuel ; la taille de la zone de plugin est adaptée
   LIRE RECT OBJET(nqr_area;$g;$h;$d;$b)
   DEPLACER OBJET(nqr_area;$g;$h;$d;$b+110;*)

Ce code se trouve dans la méthode objet du bouton de la page zéro permettant le retour au mode manuel et symbolisé par une équerre, un crayon et une gomme.

Pour que ce changement dynamique de taille s'opère correctement, il est primordial que la zone de plug-in dessinée en page zéro soit placée au dessus de tous les autres objets de la page. En effet la zone va recouvrir les autres objets et ainsi les masquer comme cela est désiré.


III. La liste des tables et des champs

Nous avons planté le décor du dialogue. Etudions à présent la liste des tables et des champs qui sont les principaux "acteurs" dans un état rapide.

L'initialisation s'effectue au sein de la méthode nommée NQR_Init_Struct_Description.

Cette méthode commence par la déclaration et le chargement de la liste des tables :

      `la liste des tables

   TABLEAU TEXTE(_nqr_table;0)
   TABLEAU ENTIER LONG(_nqr_table_id;0)

   LIRE TITRES TABLES(_nqr_table;_nqr_table_id)



L'utilisation de la commande LIRE TITRES TABLES implique que seules les tables désignées via la structure virtuelle sont proposées pour l'état rapide. Pour plus de détail sur les structures virtuelles voir la documentation de la commande FIXER TITRES TABLES.
Si le développeur ne définit pas de structure virtuelle, alors la liste des tables sera constituée des tables de la structure dont l'attribut "invisible" est décoché ; autrement dit les tables invisibles en structure le seront aussi dans le dialogue des états rapides.
Le tableau _nqr_table contiendra les noms des tables tandis que le tableau  _nqr_table_id contiendra le numéro d'ordre de création des tables dans la structure.

La méthode continue avec l'initialisation des tableaux pour les champs :

      `la liste des champs par table

   $nb_table:=Nombre de tables
   TABLEAU TEXTE(_nqr_champ;$nb_table;0)

   TABLEAU ENTIER LONG(_nqr_champ_id;$nb_table;0)



Ces tableaux sont à deux dimensions. La première dimension contiendra le numéro de la table ; la seconde sera le nom du champ pour  _nqr_champ et son numéro pour  _nqr_champ_id.

L'initialisation se termine par les tableaux de définition des relations entre les tables :

      `la liste des liens

   TABLEAU ENTIER(_nqr_table_depart;0)
   TABLEAU ENTIER(_nqr_champ_depart;0)
   TABLEAU ENTIER(_nqr_table_arrivee;0)
   TABLEAU ENTIER(_nqr_champ_arrivee;0)
   TABLEAU BOOLEEN(_nqr_allerauto;0)

   TABLEAU BOOLEEN(_nqr_retourauto;0)



Il est maintenant temps d'alimenter les tableaux définis avec les valeurs adéquates :

      `boucle sur la liste des tables VISIBLES pour charger les champs et les relations

   Boucle ($Table;1;Taille tableau(_nqr_table_id);1)

         `recuperer le numéro de la table en cours d'étude
      $id_table:=_nqr_table_id{$Table}

         `lire la liste des champs et la placer dans les tableaux prévu à cet effet
      LIRE TITRES CHAMPS(Table($id_table)->;$_champ;$_id)
      COPIER TABLEAU($_champ;_nqr_champ{$id_table})
      COPIER TABLEAU($_id;_nqr_champ_id{$id_table})

         `recherche des relations partant de la table en cours d'étude
      Boucle ($Champ;1;Nombre de champs($id_table);1)

            `lecture de la nature du lien
         LIRE PROPRIETES LIEN($id_table;$Champ;$table_dest;$champ_dest;$descriminant;$allerauto;$retourauto)

            `si un lien existe
         Si ($champ_dest#0) & ($table_dest#0)
               `lire les propriétés du lien dans le contexte courrant du process
            LIRE LIEN CHAMP(Champ($id_table;$Champ)->;$aller2;$retour2;*)
            Au cas ou
               : ($aller2=2)
                  $allerauto:=Faux
               : ($aller2=3)
                  $allerauto:=Vrai
               Sinon   `code défensif (inutile en principe)
                  $allerauto:=Faux
            Fin de cas
            Au cas ou
               : ($retour2=2)
                  $retourauto:=Faux
               : ($retour2=3)
                  $retourauto:=Vrai
               Sinon   `code défensif (inutile en principe)
                  $allerauto:=Faux
            Fin de cas

               `ajout des valeurs dans les tableaux prévus
            AJOUTER A TABLEAU(_nqr_table_depart;$id_table)
            AJOUTER A TABLEAU(_nqr_champ_depart;$Champ)
            AJOUTER A TABLEAU(_nqr_table_arrivee;$table_dest)
            AJOUTER A TABLEAU(_nqr_champ_arrivee;$champ_dest)
            AJOUTER A TABLEAU(_nqr_allerauto;$allerauto)
            AJOUTER A TABLEAU(_nqr_retourauto;$retourauto)
         Fin de si

      Fin de boucle

   Fin de boucle



De même que précédemment, la structure virtuelle est récupérée via la commande LIRE TITRES CHAMPS.

4ème Dimension 2004 apporte un contrôle beaucoup plus fin des liens avec notamment la possibilité de rendre automatique un lien précis et non plus l'ensemble des liens de la base. Avant d'utiliser le dialogue d'états rapides, un développeur peut décider d'activer tel ou tel lien. Pour en tenir compte nous utilisons la commande LIRE LIEN CHAMP qui permet de connaître précisément l'état du lien. L'utilisation de l'étoile en dernier paramètre précise que nous souhaitons connaître l'état courant au sein du process. Dans ce cas la commande ne retourne que 2 valeurs : 2 ou 3 qui signifient respectivement que le lien est manuel ou automatique. Notez que le code prévoit une autre valeur et gère ce cas par une valeur par défaut. C'est normalement impossible, mais comme la commande est récente nous préférons faire du code défensif. En effet nous ne sommes pas à l'abri d'un bogue ou d'une incompréhension de notre part.

Il ne reste plus qu'à trier les noms des tables :

      `tri des tables par ordre alphabetique

   TRIER TABLEAU(_nqr_table;_nqr_table_id)


Cette initialisation est réalisée une fois pour toute au chargement du dialogue.


IV. Afficher la liste des tables et des champs

Il existe trois façon d'afficher les tables et les champs :

  • liste des champs de la table courante ;

  • liste des champs de la table courante et des tables liées à la table courante ;

  • liste des champs de toutes les tables ;
    (il existe une quatrième option, nous en reparlerons plus tard).


L'option retenue est définie via un popup menu (nommé _nqr_filter). Ces options sont définie via la lecture d'une ressources STR# :

      `definir les options d'affichage (popup menu)

   LISTE DE CHAINES VERS TABLEAU(14905;_nqr_filter)
   _nqr_filter:=1
   nqr_cb_1:=1
   nqr_cb_2:=0
   nqr_cb_3:=0
   nqr_cb_4:=0
   nqr_cb_5:=0



Le choix par défaut est le premier. Nous en profitons également pour définir une série de variables correspondant aux options possibles.
Dans les trois cas principaux, une liste hiérarchique est utilisée pour afficher la liste des champs : nqr_nqr_lh_champ.
Cette liste est alimentée par la méthode NQR_MP_Get_Fields.
Cette méthode commence par supprimer le contenu de la liste en vue de sa reconstruction complète. C'est en effet dans ce cas plus simple de tout refaire que de chercher à mettre à jour !

      `suppression des anciennes listes en vue de leur reconstruction complête.

   Si (Liste existante(nqr_nqr_lh_champ))
      SUPPRIMER LISTE(nqr_nqr_lh_champ;*)
   Fin de si
   Si (Liste existante(nqr_nqr_lh_champ2))
      SUPPRIMER LISTE(nqr_nqr_lh_champ2;*)
   Fin de si
   Si (Liste existante(nqr_nqr_lh_champ3))
      SUPPRIMER LISTE(nqr_nqr_lh_champ3;*)
   Fin de si



Comme vous pouvez le constater, nous traitons en fait 3 listes. Ceci est dû au fait que la liste des champs apparaît dans trois pages (page mode manuel, assistant liste, assistant tableau croisé) et qu'un objet liste hiérarchique ne peut être présent que sur une seule page d'un formulaire. Pour passer cette limitation nous allons cloner la liste nqr_nqr_lh_champ après sa construction en deux autres liste nqr_nqr_lh_champ2 et nqr_nqr_lh_champ3 :

      `définition des listes clonées

   nqr_nqr_lh_champ2:=Copier liste(nqr_nqr_lh_champ)
   nqr_nqr_lh_champ3:=Copier liste(nqr_nqr_lh_champ)



La liste est définie par les lignes suivantes :

      `definition de la liste

   nqr_nqr_lh_champ:=Nouvelle liste
   CHANGER PROPRIETES LISTE(nqr_nqr_lh_champ;0;0;18;1)



Ensuite le code est un grand Au cas ou gérant les 4 cas possibles. Etudions le premier cas : liste des champs de la table courante.
La table courante est définie par la variable _nqr_table (l'index du tableau de même nom). Notez qu'un bout de code défensif permet de s'assurer que la table courante est bien définie.

Voici le code qui construit la liste des champs :

   : (nqr_cb_1=1)   `liste des champs de la table courante

      `recuperer le numéro structurel de la table courante
   $Table:=_nqr_table_id{_nqr_table}

      `boucle sur les champs de la table courante
   Boucle ($champ_l;1;Taille tableau(_nqr_champ{$Table});1)
         `lire le numero structurel du champ
      $Champ:=_nqr_champ_id{$Table}{$champ_l}
         `Lecture du type du champ
      $Type:=Type(Champ($Table;$Champ)->)

         `recherche de la position du champ dans le tableau des identifiants
      $trouve_table:=Chercher dans tableau(_nqr_table_id;$Table)
      Si ($trouve_table>0)
         $trouve_champ:=Chercher dans tableau(_nqr_champ_id{$Table};$Champ)
      Sinon   `code défensif en cas de pb
         $trouve_champ:=-1
      Fin de si

      Au cas ou
         : ($trouve_table<0)   `pb avec la table ; ne rien faire
         : ($trouve_champ<0)   `pb avec le champ ; ne rien faire
         : ($Type=Est un BLOB )   `c'est un blob ; ne rien faire car un blob ne peut être dans un etat rapide
         : ($Type=Est une sous table )   `c'est une sous table ; construire la liste des sous champs

               `lecture de la liste des sous champs
            LIRE TITRES CHAMPS(Champ($Table;$Champ)->;$_souschamps;$_souschamps_id)   `non supporté,
               ` mais fonctionnel
            $souschamp_liste:=Nouvelle liste
            Boucle ($souschamp;1;Taille tableau($_souschamps);1)
                  `contruire la référence du sous champ

               $ref_souschamp:=($_souschamps_id{$souschamp} << 24)+($Table << 16)+$Champ
               AJOUTER A LISTE($souschamp_liste;$_souschamps{$souschamp};$ref_souschamp)
               CHANGER PROPRIETES ELEMENT($souschamp_liste;$ref_souschamp;
                  Faux;Normal ;1150+Type(Champ($Table;$Champ;$_souschamps_id{$souschamp})->))
            Fin de boucle

               `construire la référence du champ
            $ref_champ:=($Table << 16)+$Champ
               `ajout du champ à la liste
            AJOUTER A LISTE(nqr_nqr_lh_champ;_nqr_champ{$Table}{$trouve_champ};
               $ref_champ;$souschamp_liste;Vrai)
            CHANGER PROPRIETES ELEMENT(nqr_nqr_lh_champ;$ref_champ;
               Faux;Normal ;1150+Type(Champ($Table;$Champ)->))
         Sinon   `c'est un champ valide ; afficher celui-ci dans la liste
               `construire la référence du champ
            $ref_champ:=($Table << 16)+$Champ
               `le champ est-il indexé
            LIRE PROPRIETES CHAMP($Table;$Champ;$Type;$Longueur;$index)
            Si ($index)
               $style:=Gras
            Sinon
               $style:=Normal
            Fin de si
               `ajout du champ à liste
            AJOUTER A LISTE(nqr_nqr_lh_champ;_nqr_champ{$Table}{$trouve_champ};$ref_champ)
            CHANGER PROPRIETES ELEMENT(nqr_nqr_lh_champ;$ref_champ;
               Faux;$style;1150+Type(Champ($Table;$Champ)->))

      Fin de cas

   Fin de boucle



Ce code est relativement simple à comprendre. Notons cependant certains détails.
En premier lieu nous pouvons noter que les commandes LIRE TITRES CHAMPS et Champ fonctionnent également pour les sous-champs.

warning Attention : ceci n'est pas officiellement documenté. En effet si cela fonctionne, ce n'est pas supporté par 4D SA. Vous pouvez donc l'utiliser mais en connaissance de cause.
Chaque élément au sein d'une liste hiérarchique doit avoir une référence unique associée. Cette référence est obligatoirement un entier long. Dans notre code nous avons choisi de construire cette référence en mixant le numéro de table et le numéro du champ. Ainsi nous pourrons retrouver ces informations par la suite en lisant la référence d'un élément sélectionné de la liste hiérarchique. Comment réaliser le mixage ? En partant d'une constatation simple : un entier long se code sur 32 bits. Le nombre de tables maximum est 255 : cela se code donc sur moins de 8 bits. Le nombre de champs maximum est 511 qui se code lui sur moins de 16 bits. Il y a donc de la place pour coder les deux nombres dans un entier long. Nous utilisons pour cela les opérateurs sur les bits :

   $ref_champ:=($Table << 16)+$Champ



Le numéro de table est décalé de 16 bits sur la gauche afin de laisser les 16 premiers bits libres pour le codage du numéro de champ.
Nous réalisons le même type de codage lorsqu'un sous-champ se présente :

   $ref_souschamp:=($_souschamps_id{$souschamp} << 24)+($Table << 16)+$Champ



Notons que le nombre de sous-champs est ici limité structurellement à 255 ce qui est largement suffisant !
Notons également que nous ne gérons pas les niveaux multiples de sous-tables qui ne sont plus supportés par 4D.

Etudions à présent le deuxième cas ou nous affichons la liste des champs de la table courante et des tables liées. Voici la partie du code qui gère ce cas :

   : (nqr_cb_2=1)   `liste des champs de la table courante et des tables liées à la table courante

         `recuperer le numéro structurel de la table courante

      $Table:=_nqr_table_id{_nqr_table}

         `boucle sur les champs de la table courante
      Boucle ($champ_l;1;Taille tableau(_nqr_champ{$Table});1)
            `lire le numero structurel du champ
         $Champ:=_nqr_champ_id{$Table}{$champ_l}
            `Lecture du type du champ
         $Type:=Type(Champ($Table;$Champ)->)

            `recherche de la position du champ dans le tableau des identifiants
         $trouve_table:=Chercher dans tableau(_nqr_table_id;$Table)
         Si ($trouve_table>0)
            $trouve_champ:=Chercher dans tableau(_nqr_champ_id{$Table};$Champ)
         Sinon   `code défensif en cas de pb
            $trouve_champ:=-1
         Fin de si
         Au cas ou
            : ($trouve_table<0)   `pb avec la table ; ne rien faire
            : ($trouve_champ<0)   `pb avec le champ ; ne rien faire
            : ($Type=Est un BLOB )   `c'est un blob ; ne rien faire car un blob ne peut être dans un etat rapide
            : ($Type=Est une sous table )   `c'est une sous table ; construire la liste des sous champs
                  `lecture de la liste des sous champs

               LIRE TITRES CHAMPS(Champ($Table;$Champ)->;$_souschamps;$_souschamps_id)
               $souschamp_liste:=Nouvelle liste
               Boucle ($souschamp;1;Taille tableau($_souschamps);1)
                     `contruire la référence du sous champ
                  $ref_souschamp:=($_souschamps_id{$souschamp} << 24)+($Table << 16)+$Champ
                  AJOUTER A LISTE($souschamp_liste;$_souschamps{$souschamp};$ref_souschamp)
                  CHANGER PROPRIETES ELEMENT($souschamp_liste;$ref_souschamp;
                     Faux;Normal ;1150+Type(Champ($Table;$Champ;$_souschamps_id{$souschamp})->))
               Fin de boucle
                  `construire la référence du champ
               $ref_champ:=($Table << 16)+$Champ
               AJOUTER A LISTE(nqr_nqr_lh_champ;_nqr_champ{$Table}{$trouve_champ};
               $ref_champ;$souschamp_liste;Vrai)
               CHANGER PROPRIETES ELEMENT(nqr_nqr_lh_champ;$ref_champ;
                  Faux;Normal ;1150+Type(Champ($Table;$Champ)->))

            Sinon   `c'est un champ valide;afficher celui-ci dans la liste
                  `construire la référence du champ

               $ref_champ:=($Table << 16)+$Champ
               LIRE PROPRIETES CHAMP($Table;$Champ;$Type;$Longueur;$index)
               Si ($index)
                  $style:=Gras
               Sinon
                  $style:=Normal
               Fin de si

               $sous_liste:=0   `il n'y a pas encore de sous liste

                  `boucle sur les relations mémorisées

               Boucle ($tt;1;Taille tableau(_nqr_table_depart);1)
                     `etude du lien aller
                  Si (_nqr_table_depart{$tt}=$Table) & (_nqr_champ_depart{$tt}=$Champ)   `c'est le bon champ de départ
                     Si (_nqr_allerauto{$tt})   `le lien est automatique (le calcul du rapport sera possible)
                           `recherche de la table d'arrivée
                        $trouve_table:=Chercher dans tableau(_nqr_table_id;_nqr_table_arrivee{$tt})
                        Si ($trouve_table>0)
                              `si la liste n'existe pas encore, la créer
                           Si ($sous_liste=0)
                              $sous_liste:=Nouvelle liste
                           Fin de si
                              `la référence choisie est - le numéro de la relation
                           $ref_champ2:=-$tt
                              `ajout de la table liée à la sous liste
                           AJOUTER A LISTE($sous_liste;"["+_nqr_table{$trouve_table}+"]";$ref_champ2)
                           CHANGER PROPRIETES ELEMENT($sous_liste;$ref_champ2;Faux;Gras ;14930)
                        Fin de si
                     Fin de si
                  Fin de si
                     `etude du lien retour
                  Si (_nqr_table_arrivee{$tt}=$Table) & (_nqr_champ_arrivee{$tt}=$Champ)   `c'est le bon champ d'arrivée
                     Si (_nqr_retourauto{$tt}) & (nqr_hier=1)   `le lien est automatique (le calcul du rapport sera possible)
                           `et les liens retours sont autorisés
                           `recherche de la table de départ

                        $trouve_table:=Chercher dans tableau(_nqr_table_id;_nqr_table_depart{$tt})
                        Si ($trouve_table>0)
                              `si la liste n'existe pas encore, la créer
                           Si ($sous_liste=0)
                              $sous_liste:=Nouvelle liste
                           Fin de si
                              `la référence choisie est - le numéro de la relation décalée de 16 bits
                           $ref_champ2:=-($tt << 16)
                              `ajout de la table liée à la sous liste
                           AJOUTER A LISTE($sous_liste;"["+_nqr_table{$trouve_table}+"]";$ref_champ2)
                           CHANGER PROPRIETES ELEMENT($sous_liste;$ref_champ2;Faux;Gras ;14931)
                        Fin de si
                     Fin de si
                  Fin de si
               Fin de boucle
                  `ajout du champ à la liste avec la sous liste des relations éventuelle
               AJOUTER A LISTE(nqr_nqr_lh_champ;_nqr_champ{$Table}{$trouve_champ};$ref_champ;$sous_liste;Vrai)
               CHANGER PROPRIETES ELEMENT(nqr_nqr_lh_champ;$ref_champ;
                  Faux;$style;1150+Type(Champ($Table;$Champ)->))
         Fin de cas
      Fin de boucle



Ce code est similaire sur de nombreux points a celui étudié précédemment. Dans le présent cas, si le champ est ni un blob ni un sous-champ, nous faisons une boucle sur les relations pour détecter les éventuelles relations partant ou arrivant sur ce champ. Si une relation existe la table liée est affichée dans une sous liste associée à l'élément.

Ici nous utilisons des références négatives pour référencer les tables ainsi ajoutées au sein de la liste hiérarchique. De plus s'il s'agit d'un lien retour alors le numéro de table est décalé de 16 bits.
Comme vous le savez certainement, les liens se propagent dans 4D tant que le sens des liens de change pas. Ainsi il peut y avoir des tables liées en cascade. Or le code précédent ne gère qu'un niveau de tables liées. Il est donc nécessaire de compléter le code. Pour cela nous allons faire une boucle sur tous les éléments de la liste en y ajoutant au fur et à mesure les cascades de tables possibles :

      `mise en place des liaisons en cascade

   $element:=0   `on commence au début !
      `analyse de tous les éléments de la liste obtenue
   Tant que ($element<Nombre elements(nqr_nqr_lh_champ))
      $element:=$element+1   `passer à l'element suivant
         `lire les information sur l'élément et notamment la sous liste éventuellement associée

      INFORMATION ELEMENT(nqr_nqr_lh_champ;$element;$ref_element;$text;$sous_liste)
      Si ($ref_element<0)   `si la référence est négative alors il peut y avoir une cascade
         Si (((-$ref_element) >> 16)>0)
               `c'est un lien retour
               `lire la table d'arrivée

            $table_arrivee:=_nqr_table_depart{((-$ref_element) >> 16)}
               `boucle sur toutes les relations
            Boucle ($ttt;1;Taille tableau(_nqr_table_arrivee))
               Si (_nqr_table_arrivee{$ttt}=$table_arrivee)   `c'est la bonne table d'arrivée
                  Si (_nqr_retourauto{$ttt}) & (nqr_hier=1)   `le lien est automatique (le calcul du rapport sera possible)
                        `et les liens retours sont autorisés
                        `recherche de la table de départ

                     $trouve_table:=Chercher dans tableau(_nqr_table_id;_nqr_table_depart{$ttt})
                     Si ($trouve_table>0)
                           `si la liste n'existe pas encore, la créer
                        Si ($sous_liste=0)
                           $sous_liste:=Nouvelle liste
                        Fin de si
                        $ref_champ3:=-($ttt << 16)
                        AJOUTER A LISTE($sous_liste;"["+_nqr_table{$trouve_table}+"]";$ref_champ3)
                        CHANGER PROPRIETES ELEMENT($sous_liste;$ref_champ3;Faux;Gras ;14931)
                     Fin de si
                  Fin de si
               Fin de si
            Fin de boucle
               `modifier l'élément pour lui ajouter l'éventuelle nouvelle sous liste
            CHANGER ELEMENT(nqr_nqr_lh_champ;$ref_element;$text;$ref_element;$sous_liste;Vrai)
         Sinon
               `c'est un lien aller
               `lire la table de départ

            $table_depart:=_nqr_table_arrivee{-$ref_element}
               `boucle sur toutes les relations
            Boucle ($ttt;1;Taille tableau(_nqr_table_depart))
               Si (_nqr_table_depart{$ttt}=$table_depart)   `c'est la bonne table de départ
                  Si (_nqr_allerauto{$ttt})   `le lien est automatique (le calcul du rapport sera possible)
                        `recherche de la table d'arrivée

                     $trouve_table:=Chercher dans tableau(_nqr_table_id;_nqr_table_arrivee{$ttt})
                     Si ($trouve_table>0)   `si la liste n'existe pas encore, la créer
                        Si ($sous_liste=0)
                           $sous_liste:=Nouvelle liste
                        Fin de si
                        $ref_champ3:=-$ttt
                        AJOUTER A LISTE($sous_liste;"["+_nqr_table{$trouve_table}+"]";$ref_champ3)
                        CHANGER PROPRIETES ELEMENT($sous_liste;$ref_champ3;Faux;Gras ;14930)
                     Fin de si
                  Fin de si
               Fin de si
            Fin de boucle
               ` modifier l'élément pour lui ajouter l'éventuelle nouvelle sous liste
            CHANGER ELEMENT(nqr_nqr_lh_champ;$ref_element;$text;$ref_element;$sous_liste;Vrai)
         Fin de si
      Fin de si
   Fin tant que



Notons ici l'astuce principale qui consiste à tirer parti du fait que lors de l'ajout d'un élément dans une liste celui-ci se trouve alors sous l'élément parent (celui en cours d'étude). Ainsi lors du pas suivant de la boucle ce nouvel élément sera de suite étudié pour voir si lui-même ne génère pas une nouvelle cascade.
L'utilisation de la commande CHANGER ELEMENT permet de modifier un élément déjà présent dans la liste pour lui ajouter une sous liste éventuelle dans notre cas.

Le dernier cas (liste de toutes les tables) est très similaire au niveau de la logique, et une étude est à présent inutile si vous avez compris les études précédentes !


V. Afficher la liste des champs liés

Nous venons de voir que les tables liées s'affichent au sein de la liste des champs. Or ce sont des champs qui vont alimenter l'état rapide. Pour cela vous avez sûrement remarqué qu'un clic sur une telle table fait apparaître une seconde liste. Cette seconde liste contient une liste des champs de la table liée. Cette liste est construite par la méthode NQR_MP_View_Fields. Etudions celle-ci.

La première partie de la méthode initialise les coordonnées et les noms des différentes listes rentrant en jeu en fonction des pages :

      `choix des coordonnées en fonction de la page

   $continuer:=Vrai
   Au cas ou
      : (Page formulaire courante=1)
         $liste_champ:=nqr_nqr_lh_champ
         $liste_detail:=nqr_nqr_lh_champ_Detail
         $h1:=162
         $h2:=84
         $h3:=90
         $suffixe:=""

      : (Page formulaire courante=3)
         $liste_champ:=nqr_nqr_lh_champ2
         $liste_detail:=nqr_nqr_lh_champ_Detail2
         $h1:=158
         $h2:=64
         $h3:=70
         $suffixe:="2"

      : (Page formulaire courante=7)
         $liste_champ:=nqr_nqr_lh_champ3
         $liste_detail:=nqr_nqr_lh_champ_Detail3
         $h1:=205
         $h2:=110
         $h3:=116
         $suffixe:="3"

      Sinon   `code défensif
         $continuer:=Faux

   Fin de cas



Notez qu'ici les coordonnées sont des hauteurs écrites "en dur", ce qui sous-entend que si vous désirez modifier géographiquement les listes il faudra faire évoluer en conséquence les chiffres.
Ensuite, en fonction de l'élément sélectionné dans la liste principale, le code suivant est exécuté :

   $item:=Elements selectionnes($liste_champ)

   INFORMATION ELEMENT($liste_champ;$item;$ref_champ;$text)

   Au cas cas
      : ($ref_champ>=0)   `c'est un champ ou un sous champ
            `lire la position de la liste prinicpale

         LIRE RECT OBJET(*;("nqr_lh_champ"+$suffixe);$g;$h;$d;$b)
            `la liste principale prend l'ensemble de la place
         DEPLACER OBJET(*;("nqr_lh_champ"+$suffixe);$g;$h;$d;$h+$h1;*)
            `la liste secondaire est rejetée hors de la zone visible
         DEPLACER OBJET(*;("nqr_lh_champ_detail"+$suffixe);10000;10000;10000;10000;*)
         SELECTIONNER ELEMENTS PAR POSITION($liste_champ;$item)

      : ($ref_champ<0)   `c'est une table
            `lire la position de la liste prinicpale

         LIRE RECT OBJET(*;("nqr_lh_champ"+$suffixe);$g;$h;$d;$b)
            `adapter la position à la moitié de la place
         DEPLACER OBJET(*;("nqr_lh_champ"+$suffixe);$g;$h;$d;$h+$h2;*)
            `l'autrre moitié est pour la liste secondaire
         DEPLACER OBJET(*;("nqr_lh_champ_detail"+$suffixe);$g;$h+$h3;$d;$h+$h1;*)

         SELECTIONNER ELEMENTS PAR REFERENCE($liste_champ;$ref_champ)



Le principe est de déplacer les objets "listes" en fonction du contexte. Notons que les coordonnées horizontales sont déduites de la position précédente de la liste principale ce qui évite d'avoir à trop coder "en dur" les coordonnées.

Le code ensuite exécuté permet de construire la liste des champs liés suivants des principes et des notations similaires aux codes précédents.

Notons enfin les dernières lignes de la méthode :

      `mise à jour des informations dans les listes

   Au cas ou
      : (Page formulaire courante=1)
         nqr_lh_champ_Detail:=$liste_detail
         ALLER A CHAMP(nqr_lh_champ_Detail)
         REDESSINER LISTE(nqr_lh_champ)
         Si (Liste existante(nqr_lh_champ_detail))
            CHANGER PROPRIETES LISTE(nqr_lh_champ_detail;0;0;18;1)
            REDESSINER LISTE(nqr_lh_champ_detail)
         Fin de si

      : (Page formulaire courante=3)
         nqr_lh_champ_Detail2:=$liste_detail
         ALLER A CHAMP(nqr_lh_champ_Detail2)
         REDESSINER LISTE(nqr_lh_champ2)
         Si (Liste existante(nqr_lh_champ_detail2))
            CHANGER PROPRIETES LISTE(nqr_lh_champ_detail2;0;0;18;1)
            REDESSINER LISTE(nqr_lh_champ_detail2)
         Fin de si

      : (Page formulaire courante=7)
         nqr_lh_champ_Detail3:=$liste_detail
         ALLER A CHAMP(nqr_lh_champ_Detail3)
         REDESSINER LISTE(nqr_lh_champ3)
         Si (Liste existante(nqr_lh_champ_detail3))
            CHANGER PROPRIETES LISTE(nqr_lh_champ_detail3;0;0;18;1)
            REDESSINER LISTE(nqr_lh_champ_detail3)
         Fin de si
   Fin de cas



Pourquoi faire un code détaillé ici alors que tout le reste de la méthode est générique ? Cette anomalie est liée a l'utilisation de la commande REDESSINER LISTE qui doit utiliser explicitement le nom de la liste à mettre à jour. En effet, le code suivant n'aurait pas fonctionné :

   REDESSINER LISTE($liste_detail)   ` NE FONCTIONNE PAS !


Utilisation des listes des tables et des champs

Maintenant que les listes de champs sont prêtes à l'emploi regardons comment elles sont utilisées.
Pour insérer un champ dans un rapport nous pouvons utiliser soit le glisser-déposer, soit le double-clic sur un champ, soit les boutons dédiés à cette action.

Pour la gestion du glisser-déposer regardons la méthode objet associée à la zone de plug-in "nqr_area".

Cette méthode objet gère les deux événements de base du glisser-déposer (Sur glisser et Sur déposer) dans une structure Au cas ou. Cette méthode objet gère un paramètre de retour $0 pour accepter ou non l'action. Ainsi si lors du glissé, nous estimons que l'objet proposé au traitement ne convient pas nous refusons l'action en forçant $0 à -1. Dans le cas contraire, nous passons la valeur de $0 à zéro.

Pour déterminer la nature de l'objet proposé au traitement, nous analysons la source via les commandes PROPRIETES GLISSER DEPOSER et RESOUDRE POINTEUR. Nous obtenons ainsi le nom de l'objet proposé. Ensuite au sein de celui-ci nous regardons l'élément sélectionné et nous décodons sa référence pour retrouver les numéros structurels de table, de champ et de sous-champ. Ce décodage est effectué grâce aux lignes suivantes :

   $Table:=($ref_champ & 0x00FFFFFF) >> 16   `decodage de la table
   $champ:=$ref_champ & 0xFFFF   `decodage du champ
   $souschamp:=$ref_champ >> 24   `decodage du sous champ



Dans le traitement de l'événement Sur déposer, nous traitons en deux cas distincts le rapport en liste et les tableaux croisés. La différence est principalement au niveau du nombre de colonnes qui est fixe (3) pour les tableaux croisés et variable pour les rapports en liste.
Nous testons aussi le type du champ glissé pour refuser les sous-tables qui ne peuvent être des colonnes d'un état.
Dans le cas d'un rapport en liste, le code suivant permet de supprimer les tris sur les colonnes transformée lors du glissé déposé en colonne de type texte, ou image (et sous table par sécurité bien que cela soit a priori inutile) :

   Si ($Type=Est un texte ) | ($Type=Est une image ) | ($Type=Est une sous table )   ` ces types ne sont pas triables ; il faut enlever les tris
      QR LIRE TRIS(nqr_area;_nqr_col;_nqr_order)
      $trouve:=Chercher dans tableau(_nqr_col;nqr_current_col)
      Si ($trouve>0)
         SUPPRIMER LIGNES(_nqr_col;$trouve)
         SUPPRIMER LIGNES(_nqr_order;$trouve)
         QR FIXER TRIS(nqr_area;_nqr_col;_nqr_order)
      Fin de si
   Fin de si



Dans le cas d'un double-clic sur la liste des champs les commandes QR INSERER COLONNE et QR FIXER INFO COLONNE entrent en jeu. En effet dans le cadre d'un rapport en liste, on ajoute la colonne à la fin du rapport alors que dans le cas d'un tableau croisé le champ est affecté a une des 3 colonnes prédéterminées de l'état. Le code ci-dessous de la méthode objet nqr_lh_champ permet de déterminer la colonne ciblée :

      `dans quelle colonne positionner le champ ?

   Au cas ou
      : (nqr_tc_colonne="")  `la colonne 1 est libre
         $colonne:=1
      : (nqr_tc_ligne="")  `la colonne 2 (les lignes) est libre
         $colonne:=2
      : (nqr_tc_cellule="")`la colonne 3 (les cellules) est libre
         $colonne:=3
      Sinon
         $colonne:=3

   Fin de cas



Si nous utilisons le bouton permettant d'insérer un champ en tant que critère de tri, la difficulté réside dans la création si nécessaire de la colonne associée. C'est l'objet de la méthode objet du bouton nqr_bOne1.

Dans cette méthode, nous retrouvons les éléments de contrôle déjà évoqué plus haut. Pour savoir si le champ est déjà affecté à une colonne nous utilisons le code suivant :

      `la colonne pour le critère de tri existe-t-elle déjà ?

   $present:=Faux   `initialisation de la réponse
   $table_name:=Nom de la table($Table)
   $champ_name:=Nom du champ($Table;$Champ)
      `boucle sur l'ensemble des colonnes
   Boucle ($i;1;QR Nombre de colonnes(nqr_area);1)
         `lecture des infos pour récuperer le contenu de la colonne
      QR LIRE INFO COLONNE(nqr_area;$i;$title;$field;$hide;$size;$repeat;$format)
         `comparaison du contenu au champ qui va servir de critère de tri
      Au cas ou
         : ($field#("["+$table_name+"]"+$champ_name)
            `ce n'est pas le bon champ ; ne rien faire
         Sinon
            $present:=Vrai   `c'est le bon champ
            $colonne:=$i   `arrêter la boucle
      Fin de cas

   Fin de boucle



Dans ce cas, nous sommes obligés de passer par une boucle et une comparaison avec le nom du champ et de la table afin de trouver la réponse.


VI. Utiliser les paramètres avancés

Afin de contrôler le contexte d'utilisation, tout comme dans la commande QR ETAT, vous disposez de quatres variables permettant de définir :

  • si l'assistant est accessible,

  • si le choix de la table et le dialogue de recherche sont affichés,

  • si les liens retour sont autorisés,

  • la table sélectionnée par défaut.


Les variables correspondantes sont respectivement :

  • nqr_wizard (0=masqué ; 1=affiché)

  • nqr_recherche (0=masqué ; 1=affiché)

  • nqr_hier (0=non autorisé ; 1=autorisé)

  • nqr_table_defaut (numéro de la table par défaut ; 0 si pas de table définie : le choix est alors automatiquement réalisé par le dialogue).


Pour mettre en œuvre ces choix, il suffit d'affecter les variables avant l'appel du dialogue comme dans le code suivant :

   nqr_wizard:=1   `afficher l'assistant

   nqr_recherche:=1   `afficher la table et les recherches
   nqr_hier:=1   `autoriser les liens retours
   $ref:=Creer fenetre formulaire([Interface];"NQR_Dialog")
   DIALOGUE([Interface];"NQR_Dialog")

   FERMER FENETRE


VII. Traduction de l’interface

Comme vous l'avez déjà peut-être remarqué, le dialogue utilise un certain nombre de ressources dont les identifiants commencent à la valeur 14900.
Vous pouvez les utiliser, mais si vous devez les modifier, nous vous conseillons vivement de créer votre propre fichier de ressources et d'utiliser alors une plage de numéro au-delà de 15000 comme le stipule les règles d'usage en la matière.


VIII. Conclusion

Nous sommes à présent au terme de cette première partie. Vous avez normalement en main tous les outils pour adapter, améliorer ou simplement utiliser le mode manuel de l'éditeur d'états rapides.

Dans la seconde partie, nous aborderons en profondeur l'assistant de création, soit les pages 2 à 15 du dialogue. Il y a donc encore de la lecture en perspective !


IX. Base exemple

Téléchargez la base exemple :

base exemple

__________________________________________________
Copyright © 1985-2009 4D SA - Tous droits réservés
Tous les efforts ont été faits pour que le contenu de cette note technique présente le maximum de fiabilité possible.
Néanmoins, les différents éléments composant cette note technique, et le cas échéant, le code, sont fournis sans garantie d'aucune sorte. L'auteur et 4D S.A. déclinent donc toute responsabilité quant à l'utilisation qui pourrait être faite de ces éléments, tant à l'égard de leurs utilisateurs que des tiers.
Les informations contenues dans ce document peuvent faire l'objet de modifications sans préavis et ne sauraient en aucune manière engager 4D SA. La fourniture du logiciel décrit dans ce document est régie par un octroi de licence dont les termes sont précisés par ailleurs dans la licence électronique figurant sur le support du Logiciel et de la Documentation afférente. Le logiciel et sa documentation ne peuvent être utilisés, copiés ou reproduits sur quelque support que ce soit et de quelque manière que ce soit, que conformément aux termes de cette licence.
Aucune partie de ce document ne peut être reproduite ou recopiée de quelque manière que ce soit, électronique ou mécanique, y compris par photocopie, enregistrement, archivage ou tout autre procédé de stockage, de traitement et de récupération d'informations, pour d'autres buts que l'usage personnel de l'acheteur, et ce exclusivement aux conditions contractuelles, sans la permission explicite de 4D SA.
4D, 4D Calc, 4D Draw, 4D Write, 4D Insider, 4ème Dimension ®, 4D Server, 4D Compiler ainsi que les logos 4e Dimension, sont des marques enregistrées de 4D SA.
Windows,Windows NT,Win 32s et Microsoft sont des marques enregistrées de Microsoft Corporation.
Apple, Macintosh, Power Macintosh, LaserWriter, ImageWriter, QuickTime sont des marques enregistrées ou des noms commerciaux de Apple Computer,Inc.
Mac2Win Software Copyright © 1990-2002 est un produit de Altura Software,Inc.
4D Write contient des éléments de "MacLink Plus file translation", un produit de DataViz, Inc,55 Corporate drive,Trumbull,CT,USA.
XTND Copyright 1992-2002 © 4D SA. Tous droits réservés.
XTND Technology Copyright 1989-2002 © Claris Corporation.. Tous droits réservés ACROBAT © Copyright 1987-2002, Secret Commercial Adobe Systems Inc.Tous droits réservés. ACROBAT est une marque enregistrée d'Adobe Systems Inc.
Tous les autres noms de produits ou appellations sont des marques déposées ou des noms commerciaux appartenant à leurs propriétaires respectifs.
__________________________________________________
 



Valid XHTML 1.1!Valid CSS!

Ce document est issu de http://www.developpez.com et reste la propriété exclusive de son auteur. La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.
Contacter le responsable de la rubrique 4D