Icônification et Drag & Drop : ça va être chaud !

Préambule

Pas de listing dans cet article... Vous trouverez tout ce qu'il faut dans le source LST avec le ressource correspondant dans le dossier BONUX. Ce programme, si vous le compilez, est un des modules pour Joe : le générateur de tags <IMG>. A vous donc d'analyser le code et d'apprendre. Cet article se bornera donc à décrire les principes de l'icônification et du Drag&Drop.

NB : concernant le copyright de JOE_IMG (c'est son nom :), vous avez le droit seulement de l'analyser et de faire des copier/coller de procédures pour vos propres programmes, voire créer un générateur d'autre tag, que le pack Joe Good's Tricks ne contient pas.
Vous n'avez aucun donc droit pour modifier, recompiler et de proposer ce programme à la place de l'original. Si vous avez des améliorations à suggérer, passez-moi un e-mail, on essayera de s'arranger :-)

La mise en icône

Très facile à faire puisqu'il faut surveiller certains messages dans l'EVNT_MULTI de la boucle principale et appeler les fonctions WIND_GET et WIND_SET avec les paramètres adéquats. A vous ensuite avec une variable booléenne d'afficher le contenu de la fenêtre (car on a toujours une fenêtre, même si elle a la taille d'une icône).

Constante | Opcode | Message
WM_ICONIFY | 34 | L'utilisateur a cliqué sur le bouton icônification et le GEM vous prévient qu'il faut modifier l'aspect de la fenêtre avec WIND_SET. Vous récupérez avec le message GEM, dans le buffer des messages :
    INT{msg_buf%+6} (ou MENU(3)) : handle de la fenêtre
    INT{msg_buf%+8} : sa position en x
    INT{msg_buf%+10} : sa position en y
    INT{msg_buf%+12} : sa largeur
    INT{msg_buf%+14} : sa hauteur
Ce sont les coordonnées externe de la fenêtre, il faut donc à partir de maintenant noter l'état icônifié dans une variable booléenne, et faire un WIND_SET(26) (au lieu du paramètre 5) avec les nouvelles coordonnéées. Attention ici, car il existe des petits programmes permettant de placer à loisir les fenêtre icônifiées, comme Alice 1.1. Il faut donc, après le WIND_SET(26), un WIND_GET(4) pour avoir les vraies coordonnées internes, celles fournies par le message GEM (et traitées par WIND_CALC) pouvant être erronées.
WM_UNICONIFY | 35 | On a double-cliqué sur la fenêtre icônifiée, il faut donc la remettre à son état "normal". Les coordonnées fournies par le message GEM (mêmes offsets et types que pour WM_ICONIFY) sont ceux de la feneêtre et on remet celle-ci à son état initial avec WIND_SET(27) (au lieu de 5). Si vous ne voulez pas utiliser les coordonnées du message GEM, utilisez WIN_SET(28) avec les vôtres.
WM_ALLICONIFY | 36 | L'utilisateur veut icônifier TOUTES les fenêtres de l'application et a appuyé sur Control en même temps de l'item d'icônification de la fenêtre. Personnellement, je n'ai jamais utilisé cette fonctionnalité. Mais d'après ce que j'ai compris, on obtient dans le buffer des messages les coordonnées d'une unique fenêtre (même offsets et types que plus haut). En plus des fonctions à appeler et code à écrire, identiques à WM_ICONIFY, il faut une variable booléenne en plus, celle indiquant que tous est mis dans une unique fenêtre, afin de différencier l'affichage dans la fenêtre icônifée et surtout lors de la désicônification par le message WM_UNICONIFY : utilisez plutôt un tableau et sauvez vous-même les coordonnées de vos fenêtres avant le WIND_SET(26) de l'icônification et utilisez WIND_SET(28) lors de la désicônification.

Pas besoin de redessiner l'intérieur des fenêtres, le GEM remarque que vous avez modifié sa taille et vous envoye un message de WM_REDRAW. C'est donc au niveau des routines de redessin qu'il faut faire intervenir vos variables booléennes et dessiner ce qu'il convient : icône ou affichage normal.

De même il est important, pour la compatibilité avec les serveur d'icônification comme Alice, très courants Outre-Rhin, de ne pas tenir compte des coordonnées fournies avec les messages 34 et 36 pour connaître les coordonnées pour l'affichage, mais faire son propre WIND_GET(4). Ces petits utilitaires ne sont (pour l'instant du moins) pas capables d'intercepter les coordonnées fenêtres données dans les messages GEM et de les corriger.

On drague ou on droppe ?

Attention ici : les OPEN et autres fonctions typiques GFA sont interdites. Pour plus de sécurité on emploie les bons vieux et légaux GEMDOS pour les opérations disques. Bien que l'on emploie pas de disque dur, ni disquette pour les transferts de données d'une application à l'autre, on utilise lecteur virtuel U:\ , employez donc les appels légaux, le GFA n'étant pas toujours très net ;-) .
MagiC, MultiTOS ou tout autre AES supérieur ou égal à 3.99 est nécessaire. Il faut donc tester la version de l'AES. A ce propos, il y a un bug dans mes programmes, je crois que je teste le TOS plutôt que l'AES... De toute façon, la présence du lecteur U:\ indique que l'OS en question est moderne (Souvenez-vous qu'on ne peut aller au dela du lecteur P pour les monoTOS, même celui du Falcon).

Vu le peu de performance de ma mémoire (ça fait longtemps que j'ai pas revu mes routines de D&D, celles de Joe datent de la version 1.1 !), j'ai repris la trame du Compendium et explicité certains passages.

RECEVOIR en Drag&Drop :

1. attendre le message AP_DRAGDROP (63) pour l'EVNT_MULTI de la boucle principale et récupérer certaines valeurs de ce buffer :

       INT{msg_buf%+6} (ou MENU(3)) : handle fenêtre sur laquelle on a déposé quelque chose
       INT{msg_buf%+8} : position X de la souris lors du dépôt
       INT{msg_buf%+10} : position en Y
       INT{msg_buf%+12} : état du clavier (touches Control, Alt, Shift)
       INT{msg_buf%+14} (offset 7) : extension du fichier à ouvrir

   Par un MKI$ avec la valeur de l'offset 7, on obtient un fichier qu'il faut ouvrir :
   `U:\PIPE\DRAGDROP.xx` où xx est ce `MKI$(INT{msg_buf%+14})`.

2. Lorsque ce fichier est ouvert en lecture/écriture avec un GEMDOS, écrivez toujours avec un GEMDOS un mot nul (DD_OK ou 0 sur un WORD). Si vous êtes fâché que que vous voulez rien recevoir, écrivez 1 sur un WORD (DD_NAK).

3. Puis construisez dans un tampon mémoire de 32 octets, contenant 8 extensions de fichiers de 4 octets chacunes : exemple avec ".TXT", ".DOC", etc. Cette liste correspond aux fichiers que votre application PEUT gérer. Pour des images bitmaps, vu le nombre important de formats, on emploie le format .IMG , pour l'ascii, c'est .TXT ... vous pouvez créer vos propres formats, mais n'oubliez pas que le Drag&Drop est l'échange de données interapplications, donc la compatibilité de fichiers doit être maximum ;-)
   Attention, ARGS (donc sans point) est considéré comme l'échange d'un nom de fichier(s) (ou d'autres arguments). PATH correspond à l'échange d'un chemin et possède un protocole qui s'éloigne de la méthode décrite ici, que malheureusement pour moi, je n'ai pas réussi à comprendre.
   N'oubliez pas qu'on travaille comme en C. Si vous n'avez pas assez d'extensions à donner, remplissez le reste avec des null-bytes. Ecrivez ensuite ce tampon dans le fichier DRAGDROP.xx .

4. Lisez ensuite un mot (WORD ou INT) sur ce fichier, cette valeur est la taille de l'entête que vous devez lire ensuite. Donc si c'est 0, on abandonne et si vous lisez la valeur 16, lisez ensuite 16 octets. Dans cette entête est contenu : 4 octets correspondant au format de fichier de votre liste que l'application extérieure a reconnue et est prête à vous envoyer, ensuite un LONG (4 octets) correspondant à la taille des données qui vont être transmises, puis une chaîne de texte terminé par un null-byte (une chaîne en C, quoi :) explicitant ce qui va être échangée (si on ne veut pas expliciter, un null-byte suffit), et enfin le nom d'un fichier qui accompagne les données (null-byte si cela ne correspond pas à un fichier).
   Donc les deux derniers éléments sont facultatifs. On peut s'en passer, mais il faut, si vous envoyez des données en Drag&Drop, les prendre en compte pour la compatibilité.

5. On analyse donc les éléments de cet entête, et on écrit un WORD sur le fichier d'échange, selon les valeurs :

   Constante | Valeur | Explication
   DD_OK | 0 | On est prêt à recevoir des données. Après l'écriture de DD_OK dans le fichier, on va pouvoir lire par GEMDOS, toujours sur ce même fichier, les données, la taille de celles-ci étant fournie dans l'entête plus haut. Attention, ici, vous lisez des données, mais il faut les stocker dans une zone mémoire que VOUS devez allouer VOUS-MEMES.
   DD_NAK | 1 | On est des petits branleurs et on abandonne. Ne pas oublier de fermer le fichier avec un GEMDOS.
   DD_EXT | 2 | Le format de fichier ne nous convient pas, et, après l'écriture du mot DD_EXT par notre application, on attend un autre mot (taille d'une nouvelle l'entête), suivi de la nouvelle entête elle-même. Et c'est reparti comme en 14 !
   DD_LEN | 3 | Notre application ne peut recevoir autant de données. On attend donc une autre taille d'entête puis son entête.
   Si votre application est un bureau GEM, les valeurs suivantes sont employées
   DD_TRASH | 4 | C'est en fait sur votre poubelle qu'on a déposé l'objet du Drag&Drop. On ne fait rien et on ferme le fichier, car c'est à l'application étrangère de détruire ce qu'il a déposé sur la poubelle (ça peut être un morceau de texte, pas nécessairement un fichier entier)
   DD_PRINTER | 5 | Même chose : mais pour l'imprimante. On ferme tout et c'est l'application qui a engendré le Drag&Drop d'imprimer elle-même ce qui a été (non)-échangé.
   DD_CLIPBOARD | 6 | Idem, mais pour le presse-papier, l'application étrangère doit elle-même sauver dans le clipboard (comme un bête Control+V).

6. Quoiqu'il se soit passé, on ferme à la fin U:\PIPE\DRADROP.?? .

   Attention les arguments ARGS sont échangés comme des arguments d'une ligne de commande.

ENVOYER en Drag&Drop

On considère d'abord que vous avez déjà sélectionné ce que vous aller envoyer à une autre application. En général cela se fait à la souris en employant les fonction GRAF_? . Essayer de faire cela élégamment, car il faut que vos Drags et vos Drops soient ergonomiques si vous voulez qu'on les emploie.
Donc vous avez avec la souris fait un glisser-déposer sur une fenêtre quelconque. Si c'est une des vôtres, c'est à vous de gérer cela, si cette fenêtre n'est pas la vôtre, vous pouvez alors faire un Drag&Drop.
Utilisez WIND_FIND et WIND_GET(20 WM_OWNER) pour connaître l'ap_id& de l'application dans laquelle vous avez déposer vos trésors. Le Drag&Drop peut alors commencer.

1. Utilisez la fonction GEMDOS Psignal() (spécial MiNT et comprise par MagiC) qui permet d'ignorer les messages Drag&Drop de réception possibles qui peuvent nous être envoyés (signaux SIGPIPE(13), mais ne me demandez rien, je suis un bleu comme vous dans cette fonction. J'ai trimé pour que cela marche, mais je n'ai par contre pas compris grand chose).

2. Créez le fichier U:\PIPE\DRADROP.xx où xx est deux caractères ASCII qu'on pourra convertir en WORD. Il faut faire des test avant de créer le fichier pour voir s'il n'existe pas déjà. En général on prend d'abord "AA", puis "AB" sinon, etc. Si la création échoue, on essaye une autre extension. Ce fichier doit être caché, selon le Compendium.

3. utilisez APPL_WRITE vers l'application dont vous connaissez l'ap_id& (avec ce qu'on a dit plus haut avec WM_OWNER. Le buffer de 16 octets doit être remplie de cette manière :

   Offset (en octets) | Contenu (en mot, INT)
   +0 | AP_DRAGDROP (63)
   +2 | votre ap_id&
   +4 | 0
   +6 | Handle de la fenêtre où a été déposé les données à échanger
   +8 | Position en X de la souris à ce moment
   +10 | Position en Y de la souris
   +12 | Etat des touches Control, Alternate et Shift à ce moment (utilisation de GRAF_MKSTATE)
   +14 | le xx, extension du fichier de Drag&Drop, converti en WORD grâce à CVI("xx")

4. Utiliser ensuite la fonction GEMDOS Fselect() pour attendre une possible réponse (délai de 3 à 4 secondes selon le Compendium).

5. Si on a eut une réponse avant l'échéance, on lit un mot dans le fichier. Si ce WORD est nul (DD_OK) on continue, si ce dernier est à 1 (DD_NAK), on abandonne.

6. Si on a lu 0, on lit ensuite 32 octets. Ce petit tampon correspond aux formats de fichiers que l'autre application peut recevoir. si on trouve .TXT , alors on pourra lui envoyer du texte au format ASCII. Idem pour .IMG ou .GEM , etc. En général, 8 types de fichiers suffisent. Si notre application ne veux pas envoyer à ces 8 formats, on peut continuer en renvoyant plus tard le type de format voulu dans l'entête qui va suivre. A l'autre application de s'accorder. Mais en général, les formats échangés sont de type .TXT et ARGS, le reste du tampon étant rempli par des zéros.

7. On construit ensuite un entête, avec tout d'abord le format voulu (4 octets, exemple .TXT), puis sur un LONG (4 octets) la longueur des données à envoyer, puis une chaîne C décrivant le type de données échangées (ou un null-byte) (exmple : Texte ASCII), et enfin un nom de fichier facultatif (ou un null-byte obligatoirement sinon).
   On regarde combien cela fait en taille (n'oubliez pas de compter les null-bytes avec) et on écrit cette taille en WORD dans le fichier de Drag&Drop, puis tout de suite après l'entête elle-même.

8. On a ensuite une réponse de l'application réceptrice en lisant un WORD dans le fichier. Ce sont exactement les codes DD_OK, DD_TRASH et cie plus haut ! Mais ici faites attention : on est dans l'autre position, celle de l'envoyeur.

   Dans le cas favorable (DD_OK lu), on écrit dans le fichier les données à envoyer, puis on ferme et (je suppose) on rétablie la fonction Psignal à son état initial.

Ce n'est qu'un au revoir !

Compliqué, le Drag&Drop ? Pas vraiment. Les principales difficultés viennent en fait des fonctions que, nous, pauvres GFA-lamerz, n'employons jamais (Psignal et Fselect), mais aussi du fait de coder dans la même application les fonctions pour envoyer ET recevoir. Je mélangeais tout à un moment donné !
Mais une fois codé et compris, on peu oublier : les routines de Drag&Drop s'intégreront parfaitement dans votre bibliothèque de fonctions-maisons.

Un dernier point que j'ai oublié de souligner : tout se fait grâce à l'OS, pas besoin de mémoire tampon non protégée pour échanger des données. Le protocole est donc sécurisé : toutes les allocs mémoires peuvent être en mémoire protégée. Le GEM et ses enhanced clones comme MagiC, au contraire de certains OS commerciaux et billesques, sont faits dès le départ pour évoluer, étant bâtis sur des bases solides.

Bref, le TOS et le GEM en particulier, c'est pas un OS de mauviettes.

Rajah Lone
écrit le 4 Juin 99