Suivez-nous sur X
|
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
ALL
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
A propos d'Obligement
|
|
David Brunet
|
|
|
|
Programmation : Compléments d'interfaces
(Article écrit par Damien Guichard - décembre 2004)
|
|
Dernière étape
Dans l'article précédent, on a survolé la création d'interfaces graphiques pilotées par échange de messages.
Dans ce dernier article, on aborde quelques compléments de confort indispensables comme les raccourcis clavier,
les AppWindows et les ports ARexx.
Raccourcis clavier
Gadtools gère tout seul les raccourcis clavier pour les menus.
Les raccourcis clavier pour les gadgets ne sont pas gérés par Gadtools mais sont plutôt simulés par le programmeur.
Vous ajoutez IDCMP_VANILLAKEY dans les WA_IDCMP de votre fenêtre et vous êtes notifié chaque fois qu'une touche est pressée.
Alors imsg.code contient le caractère clavier et à votre charge d'effectuer la fonction qui lui correspond
dans votre programme.
Pour aider l'utilisateur à (re)connaître les raccourcis, spécifiez GT_UNDERSCORE,"_" dans la liste d'attributs
de CreateGadgetA(). Et placez un "_" dans le libellé de votre gadget, de sorte que "_quitter" incite l'utilisateur
à presser "q" pour quitter.
L'exemple du prochain chapitre illustre la programmation des raccourcis clavier.
AppWindows
Pour créer une AppWindow :
- Vous devez ouvrir/fermer la workbench.library.
- On transforme une fenêtre déjà ouverte en appwindow via AddAppWindowA().
- Et alors on peut recevoir des appmessages.
- appmsg.type=AMTYPE_APPWINDOW signifie un événement appwindow.
- appmsg.numargs est le nombre d'icônes lachées dans la fenêtre.
- appmsg.arglist sont les icônes, comme avec un WB Startup message.
- Avant de fermer la fenêtre appelez RemoveAppWindow().
Cet exemple affiche toutes les icônes lâchées dans la ListView, utilisez le menu ou le raccourci clavier
pour quitter, "new" efface la liste, l'action "save" est bidon :
OPT OSVERSION=37
MODULE 'intuition/intuition','intuition/screens','intuition/gadgetclass'
MODULE 'gadtools','libraries/gadtools'
MODULE 'wb','workbench/startup','workbench/workbench'
MODULE 'utility/tagitem','graphics/rastport'
MODULE 'exec/nodes','exec/lists'
MODULE '*gt_window'
CONST ERR_NOWB=ERR_NOWINDOW+1
DEF win:PTR TO window
DEF appwin=NIL
DEF listview=NIL
DEF labels:PTR TO mlh
DEF path[80]:STRING
PROC main() HANDLE
DEF menu
workbenchbase:=OpenLibrary('workbench.library',37)
IF workbenchbase=NIL THEN Raise(ERR_NOWB)
menu:=[NM_TITLE,0,'Project',0,0,0,0,
NM_ITEM,0,'New','N',0,0,0,
NM_ITEM,0,'Load...','L',0,0,0,
NM_ITEM,0,'Save','S',0,0,0,
NM_ITEM,0,NM_BARLABEL,0,0,0,0,
NM_ITEM,0,'Quit','Q',0,0,0,
0,0,0,0,0,0,0]:newmenu
gt_openwindow({windef},menu,{winhandler})
EXCEPT DO
IF workbenchbase THEN CloseLibrary(workbenchbase)
ENDPROC
PROC newlist()
DEF list:PTR TO mlh
NEW list
list.head:=list+4
list.tailpred:=list
ENDPROC list
PROC insert(list,name)
AddTail(list,NEW [0,0,0,0,name]:ln)
ENDPROC
PROC windef(scr:PTR TO screen,gad,visual,glist)
DEF offx,offy
offx:=scr.wborleft
offy:=scr.wbortop+scr.rastport.txheight+1
labels:=newlist()
gad:=listview:=CreateGadgetA(LISTVIEW_KIND,gad,
[offx+20,offy+14,255,130,NIL,scr.font,0,0,visual,0]:newgadget,
[GTLV_SCROLLWIDTH,20,GTLV_LABELS,labels,GTLV_SHOWSELECTED,NIL,TAG_DONE])
gad:=CreateGadgetA(BUTTON_KIND,gad,
[offx+20,offy+144,70,18,'_New',scr.font,"N",0,visual,0]:newgadget,
[GT_UNDERSCORE,"_",TAG_DONE])
gad:=CreateGadgetA(BUTTON_KIND,gad,
[offx+112,offy+144,70,18,'_Save',scr.font,"S",0,visual,0]:newgadget,
[GT_UNDERSCORE,"_",TAG_DONE])
gad:=CreateGadgetA(BUTTON_KIND,gad,
[offx+205,offy+144,70,18,'_Quit',scr.font,"Q",0,visual,0]:newgadget,
[GT_UNDERSCORE,"_",TAG_DONE])
IF gad=NIL THEN Raise(ERR_NOGADGET)
win:=OpenWindowTagList(NIL,
[WA_TITLE, 'AppWin Demo',
WA_FLAGS, WFLG_DEPTHGADGET+WFLG_CLOSEGADGET+WFLG_DRAGBAR+
WFLG_SIMPLE_REFRESH+WFLG_ACTIVATE,
WA_IDCMP, IDCMP_CLOSEWINDOW+IDCMP_REFRESHWINDOW+IDCMP_VANILLAKEY+
IDCMP_GADGETDOWN+IDCMP_GADGETUP+IDCMP_MOUSEMOVE+IDCMP_MENUPICK,
WA_WIDTH, 300,
WA_HEIGHT, offy+176,
WA_ZOOM, [0,offy,130,offy]:INT,
WA_GADGETS, glist,
WA_NEWLOOKMENUS, TRUE,
WA_AUTOADJUST, TRUE,
TAG_DONE])
IF win
appwin:=AddAppWindowA(0,0,win,win.userport,NIL)
ENDIF
RETURN win
ENDPROC
PROC itemnum(n)
ENDPROC Shr(n,5) AND $3F
PROC winhandler(msg:PTR TO appmessage,class,code,gad:PTR TO gadget) HANDLE
IF msg.type=AMTYPE_APPWINDOW
expand_list(msg)
Gt_ReplyIMsg(msg)
ELSE
Gt_ReplyIMsg(msg)
SELECT class
CASE IDCMP_CLOSEWINDOW
Raise(ERR_OK)
CASE IDCMP_REFRESHWINDOW
Gt_BeginRefresh(win)
Gt_EndRefresh(win,TRUE)
CASE IDCMP_GADGETUP
IF gad.gadgetid="N"
clear_list()
ELSEIF gad.gadgetid="Q"
Raise(ERR_OK)
ENDIF
CASE IDCMP_MENUPICK
IF itemnum(code)=0 THEN clear_list()
IF itemnum(code)=4 THEN Raise(ERR_OK)
CASE IDCMP_VANILLAKEY
IF code="n"
clear_list()
ELSEIF code="q"
Raise(ERR_OK)
ENDIF
ENDSELECT
ENDIF
EXCEPT
IF appwin THEN RemoveAppWindow(appwin)
Raise(ERR_OK)
ENDPROC
PROC clear_list()
labels.head:=labels+4
labels.tailpred:=labels
Gt_SetGadgetAttrsA(listview,win,NIL,[GTLV_LABELS,labels,0])
ENDPROC
PROC expand_list(msg:PTR TO appmessage)
DEF i,name
FOR i:=0 TO msg.numargs-1
NameFromLock(msg.arglist[i].lock,path,80)
AddPart(path,msg.arglist[i].name,80)
name:=String(StrLen(path))
StrCopy(name,path)
insert(labels,name)
ENDFOR
Gt_SetGadgetAttrsA(listview,win,NIL,[GTLV_LABELS,labels,0])
ENDPROC
|
Les AppIcons fonctionnent de façon très similaire aux AppWindows.
Serveurs ARexx
Un serveur ARexx augmente sensiblement l'interopérabilité de votre programme en exportant toutes ses fonctionnalités,
ce qui permet de scripter leur utilisation et de coopérer avec une multitude d'autres programmes serveurs ARexx.
L'interaction avec ARexx se fait par la rexxsyslib.library, pour cela vous créez et nommez un port de message, ce port
de message deviendra votre adresse de serveur ARexx. Pour créer ce port de message vous utilisez les fonctions
CreateMsgPort() et DeleteMsgPort() de la exec.library, mais vous pouvez tout aussi bien réutiliser votre port de
fenêtre. Ensuite, il suffit de définir la priorité du port de message, puis de le rendre public à l'aide de la
fonction AddPort(), à la fin il faudra le retirer à l'aide de la fonction RemPort().
Si vous réutilisez votre port de fenêtre, la fonction IsRexxMsg() vous permettra de distinguer un message ARexx
d'un simple message intuition.
Une fois le message rxmsg identifié comme étant un message ARexx :
- rxmsg.args[0] est la ligne de commande.
- Interprétez cette commande.
- Définissez le code d'erreur rxmsg.result1 (comme une commande DOS).
- Pour retourner une valeur placez la chaîne dans rxmsg.result2.
- Finalement répondez avec ReplyMsg(rxmsg).
Ces quelques commandes s'inspirent de la graphics.library pour offrir un outil de dessin scriptable par ARexx :
QUIT
MOVE x y
DRAW x y
RECTFILL xmin ymin xmax ymax
ELLIPSE cx cy a b
SETPEN pen
TEXT string
Ce petite source implémente le serveur ARexx pour ces commandes :
OPT OSVERSION=37
MODULE 'exec/ports','exec/nodes','intuition/intuition'
MODULE 'rexxsyslib','rexx/storage'
ENUM ERR_OK,ERR_NOREXXSYSLIB,ERR_NOWINDOW
CONST SIGNAL_BREAK=$1000
DEF win:PTR TO window
DEF rastport
DEF xa,ya,xb,yb:LONG
PROC main() HANDLE
rexxsysbase:=OpenLibrary('rexxsyslib.library',36)
IF rexxsysbase=NIL THEN Raise(ERR_NOREXXSYSLIB)
win:=OpenWindowTagList(NIL,
[WA_TITLE, 'AREXX Paint Demo',
WA_FLAGS, WFLG_DEPTHGADGET+WFLG_CLOSEGADGET+WFLG_DRAGBAR+
WFLG_NOCAREREFRESH+WFLG_ACTIVATE,
WA_IDCMP, IDCMP_CLOSEWINDOW,
WA_WIDTH, 320,
WA_HEIGHT, 256,
WA_AUTOADJUST, TRUE,
NIL])
IF win=NIL THEN Raise(ERR_NOWINDOW)
win.userport::ln.name:='APaint.1'
win.userport::ln.pri:=5
AddPort(win.userport)
rastport:=win.rport
SetAPen(rastport,1)
handle()
EXCEPT DO
SELECT exception
CASE ERR_NOREXXSYSLIB
PrintF('Could not open rexxsyslib.library v36+ !\n')
CASE ERR_NOWINDOW
PrintF('Could not open window!\n')
ENDSELECT
IF win THEN CloseWindow(win)
IF rexxsysbase THEN CloseLibrary(rexxsysbase)
ENDPROC
PROC handle() HANDLE
DEF signals,winsig,iclass,imsg:PTR TO intuimessage
DEF cmdline,rxmsg:PTR TO rexxmsg
winsig:=Shl(1,win.userport.sigbit)
LOOP
signals:=Wait(winsig+SIGNAL_BREAK)
IF signals AND winsig
WHILE rxmsg:=imsg:=GetMsg(win.userport)
IF IsRexxMsg(imsg)
cmdline:=TrimStr(rxmsg.args[0])
IF StrCmp(cmdline,'QUIT')
rxmsg.result1:=0
ReplyMsg(rxmsg)
Raise(0)
ELSEIF StrCmp(cmdline,'MOVE ',STRLEN)
IF read_args(rxmsg,2)
Move(rastport,xa,ya)
ENDIF
ELSEIF StrCmp(cmdline,'DRAW ',STRLEN)
IF read_args(rxmsg,2)
Draw(rastport,xa,ya)
ENDIF
ELSEIF StrCmp(cmdline,'RECTFILL ',STRLEN)
IF read_args(rxmsg,4)
IF (xb>=xa) AND (yb>=ya)
RectFill(rastport,xa,ya,xb,yb)
ELSE
rxmsg.result1:=10
ENDIF
ENDIF
ELSEIF StrCmp(cmdline,'ELLIPSE ',STRLEN)
IF read_args(rxmsg,4)
DrawEllipse(rastport,xa,ya,xb,yb)
ENDIF
ELSEIF StrCmp(cmdline,'SETPEN ',STRLEN)
IF read_args(rxmsg,1)
SetAPen(rastport,xa)
ENDIF
ELSEIF StrCmp(cmdline,'TEXT ',STRLEN)
cmdline:=TrimStr(cmdline+STRLEN)
Text(rastport,cmdline,StrLen(cmdline))
rxmsg.result1:=0
ENDIF
ReplyMsg(rxmsg)
ELSE
iclass:=imsg.class
ReplyMsg(imsg)
SELECT iclass
CASE IDCMP_CLOSEWINDOW
Raise(0)
ENDSELECT
ENDIF
ENDWHILE
ENDIF
IF signals AND SIGNAL_BREAK
Raise(0)
ENDIF
ENDLOOP
EXCEPT
WHILE imsg:=GetMsg(win.userport)
ReplyMsg(imsg)
ENDWHILE
ENDPROC
PROC read_args(rxmsg:PTR TO rexxmsg,num)
DEF str,ch
str:=TrimStr(rxmsg.args[0])
WHILE str[]<>" " DO INC str
xa,ch:=Val(str); IF ch THEN DEC num; str:=str+ch
ya,ch:=Val(str); IF ch THEN DEC num; str:=str+ch
xb,ch:=Val(str); IF ch THEN DEC num; str:=str+ch
yb,ch:=Val(str); IF ch THEN DEC num; str:=str+ch
IF num THEN rxmsg.result1:=10 ELSE rxmsg.result1:=0
ENDPROC num=0
|
Le Shell permet d'utiliser ces commandes de façon interactive :
RUN >NIL: APaint
RX "ADDRESS 'APaint.1' ELLIPSE 160 128 100 60"
RX "ADDRESS 'APaint.1' QUIT"
|
Une chose que ce petit programme ne gère pas ce sont les exécutions multiples d'un même programme serveur.
Dans ce cas vous voulez soit partager un même serveur soit avoir de multiples serveurs, un pour chaque tâche.
Pour avoir de multiples serveurs il suffirait de les nommer Apaint.1, APaint.2, APaint.3 et ainsi de suite.
Voyez la rexxsyslib.library ainsi que rexx/storage.h pour plus d'informations sur les serveurs ARexx.
Les Gadget-Toolkits
Ce tutoriel ne couvre pas les gadget-toolkits.
Cependant, voici où trouver quelques ressources et documentations pour le programmeur Amiga-E :
MUI :
de.aminet.net/aminet/dev/mui/mui38dev.lha.
de.aminet.net/aminet/dev/mui/mui38dev-E.lha.
Feelin :
www.feelin.fr.
Pour aller plus loin
Un tutoriel n'est ni exhaustif ni une référence. Un programme AmigaOS réaliste est fait de mille autres
choses, non abordées ici, comme les écrans, les datatypes, les memory-pools, les processus, les périphériques logiques (devices),
les fichiers IFF et bien d'autres encore. Savoir programmer c'est donc aussi savoir trouver de la
documentation, et des exemples, le plus souvent en anglais. Voici quelques liens pour démarrer.
La documentation indispensable c'est les Autodocs de Commodore, et c'est encore mieux dans le format
AmigaGuide : de.aminet.net/pub/aminet/dev/misc/AmigaOS_guides.lha.
Les non moins indispensables AmigaOS Includes :
obligement.free.fr/files/ndk_amigaos39.lzx.
Le compilateur EC v3.3a de Wouter van Oortmerssen (version complète) :
www.aminet.net/dev/e/amigae33a.lha et home.swipnet.se/blubbe/ECX.
La liste de diffusion Amiga-E :
www.freelists.org/list/positron.
|