/***************************************************************************** Gestionnaire de terminal Unix: listbox.c (c) Pierre Adriaans 1994 *****************************************************************************/ #include "keyboard.h" #include "screen.h" #include "listbox.h" /***************************************************************************** InitListBoxItem() ------------------------------------------------------------------------------ Input: un pointeur de tete par adresse Process: liberation de l'eventuelle liste pointee par ce pointeur et remise a NULL du pointeur Output: / *****************************************************************************/ void InitListBoxItem(ListBoxItem_t **Liste) { ListBoxItem_t *Cour,*Temp; Cour = *Liste; while(Cour != (ListBoxItem_t *)NULL) { free(Cour->Str); Temp = Cour; Cour = Cour->Suivant; free(Temp); } *Liste = (ListBoxItem_t *)NULL; } /***************************************************************************** AddListBoxItem() ------------------------------------------------------------------------------ Input: - pointeur de tete d'une liste par adresse - string a inserer dans la liste Process: insertion en queue de l'item dans la liste Output: 0 *****************************************************************************/ int AddListBoxItem(ListBoxItem_t **Liste,char *Str) { ListBoxItem_t *Cour,*Prec, *Temp = (ListBoxItem_t *)NULL; Temp = (ListBoxItem_t *)malloc(sizeof(ListBoxItem_t)); if(Temp == (ListBoxItem_t *)NULL) raise(SIGME); Temp->Str = (char *)NULL; Temp->Str = (char *)malloc(strlen(Str) + 1); if(Temp->Str == (char *)NULL) raise(SIGME); strcpy(Temp->Str,Str); Temp->Suivant = (ListBoxItem_t *)NULL; if(*Liste == (ListBoxItem_t *)NULL) { *Liste = Temp; (*Liste)->Prec = (ListBoxItem_t *)NULL; } else { Cour = *Liste; Prec = (ListBoxItem_t *)NULL; while(Cour != (ListBoxItem_t *)NULL) { Prec = Cour; Cour = Cour->Suivant; } Prec->Suivant = Temp; Temp->Prec = Prec; } return(0); } /***************************************************************************** GetLBNbreItems() ------------------------------------------------------------------------------ Input: un pointeur de tete Process: renvoie le nombre d'entrees dans la liste *****************************************************************************/ int GetLBNbreItems(ListBoxItem_t *Liste) { int i; for(i=0;Liste != (ListBoxItem_t *)NULL;Liste = Liste->Suivant,i++); return(i); } /***************************************************************************** ListBox() ------------------------------------------------------------------------------ La Boite de liste. Voir le commentaire general. *****************************************************************************/ int ListBox(int Lig,int Col,int Height,int Length,char AttBox,char AttInv, char *Title,ListBoxItem_t *Liste,int Type,int Frame) { int ActLig,ActCol, /* Coord de la zone exploitable */ ActHeight,ActLength, i, /* Compteur */ SelIndex, /* Index de selection courant */ InvLig,InvCol, /* Coord de la zone inverse */ X,Y, /* Position du curseur avant appel */ Fini = 0, /* Fin d'execution */ rc, /* Code de retour */ NbreItem; /* Nbre de choix */ ListBoxItem_t *Current, /* Choix courant */ *End, /* Dernier choix */ *Temp; /* Pointeur de parcours */ /* Calculer le nombre d'entrees dans la liste */ NbreItem = GetLBNbreItems(Liste); /* Recuperer la position du curseur */ GetXY(&Y,&X); if(Type == LB_DOUBLE) { /* Ouvrir une fenetre sans cadre, puis une fenetre interne */ OpenWin(Lig,Col,Height,Length,AttBox,UNFRAMED,SHADED); Lig += 1; Col += 3; Height -=2; Length -=6; PaintFrame(Lig,Col,Height,Length,AttBox,Frame); SetActiveWindow(Lig+1,Col+2,Height-2,Length-4); } else /* Simple fenetre */ OpenWin(Lig,Col,Height,Length,AttBox,Frame,SHADED); /* Recuperer la fenetre active d'affichage */ GetActiveWindow(&ActLig,&ActCol,&ActHeight,&ActLength); /* Afficher le titre centre, tronque si besoin est */ if(strcmp(Title,"") != 0) { if(strlen(Title) > ActLength) Title[ActLength] = 0; WriteString(Title,AttBox,Lig, Col + 3 + (((Length-6) / 2) - (strlen(Title) / 2))); } /* Virer le cursuer du chemin */ GotoXY(Y,X); /* Initialiser le pointeur End sur le dernier item de la liste */ for(Current = Liste,End = (ListBoxItem_t *)NULL; Current != (ListBoxItem_t *)NULL; End = Current,Current = Current->Suivant); /* Verifier la longueur de toutes les entrees, et tronquer si besoin */ for(Current = Liste; Current != (ListBoxItem_t *)NULL; Current = Current->Suivant) if(strlen(Current->Str) > ActLength-2) (Current->Str)[ActLength-2] = 0; /* Afficher la portion superieure de la liste dans la fenetre */ for(i=ActLig,Current = Liste; i < (ActLig + ActHeight) && Current != (ListBoxItem_t *)NULL; i++,Current = Current->Suivant) WriteString(Current->Str,AttBox,i,ActCol+1); /* Par defaut, option 0 */ SelIndex = 0; /* Fixer les coordonnees de la premiere zone d'invertion video */ InvLig = ActLig; InvCol = ActCol; /* Inverser la premiere option */ PaintString(InvLig,InvCol,ActLength,AttInv); /* Pour le moment, l'option choisie est la premiere de la liste */ Current = Liste; while(!Fini) { switch(ReadKbd()) { case CR: CloseWin(); rc = SelIndex + 1; Fini = 1; break; case DELETE: case CTRL_C: case ESC: CloseWin(); rc = -1; Fini = 1; break; case K_UP: if(Current->Prec != (ListBoxItem_t *)NULL) { /* Si le choix courant a un predecesseur, y deplacer la barre avec scrolling bas si necessaire */ SelIndex--; PaintString(InvLig,InvCol,ActLength,AttBox); Current = Current->Prec; if(InvLig == ActLig) { ScrollDownWin(ActLig,ActCol,ActHeight,ActLength,1); WriteString(Current->Str,AttBox,ActLig,ActCol+1); } else InvLig--; PaintString(InvLig,InvCol,ActLength,AttInv); } else { /* Sinon, aller sur le dernier choix (wraparoud) */ if(InvLig + NbreItem - SelIndex <= ActLig + ActHeight) /* Le dernier item est deja dans la fenetre: simplement deplacer la barre */ PaintString(InvLig,InvCol,ActLength,AttBox); else { /* Le dernier item n'est pas dans la fenetre: effacer tout et les reafficher pour avoir le dernier item en derniere position de la fenetre */ ClrZone(ActLig,ActCol,ActHeight,ActLength,AttBox); /* Positionner Current sur le premier item a devoir etre reaffiche en remontant la liste a partir de la fin */ for(i = 0,Current = End; i < ActHeight - 1 && Current != (ListBoxItem_t *)NULL; i++,Current = Current->Prec); /* Affichage */ for(i = ActLig; i < (ActLig + ActHeight) && Current != (ListBoxItem_t *)NULL; i++,Current = Current->Suivant) WriteString(Current->Str,AttBox,i,ActCol+1); } Current = End; SelIndex = NbreItem - 1; if(NbreItem < ActHeight) InvLig = ActLig + SelIndex; else InvLig = ActLig + ActHeight - 1; PaintString(InvLig,InvCol,ActLength,AttInv); } break; case K_DOWN: /* Si le choix courant a un suivant, y deplacer la barre avec scrolling haut si necessaire */ if(Current->Suivant != (ListBoxItem_t *)NULL) { /* Si le choix courant a un suivant, y deplacer la barre avec scrolling haut si necessaire */ SelIndex++; PaintString(InvLig,InvCol,ActLength,AttBox); Current = Current->Suivant; if(InvLig == ActLig + ActHeight - 1) { ScrollUpWin(ActLig,ActCol,ActHeight,ActLength,1); WriteString(Current->Str,AttBox,ActLig+ActHeight-1,ActCol+1); } else InvLig++; PaintString(InvLig,InvCol,ActLength,AttInv); } else { if(SelIndex < ActHeight && InvLig - SelIndex >= ActLig) /* L'item 0 est tjs dans la fenetre: simplement deplacer la barre */ PaintString(InvLig,InvCol,ActLength,AttBox); else { /* L'item 0 n'est plus dans la fenetre: effacer tous les items et les reafficher a partir du 0, puis y placer la barre */ ClrZone(ActLig,ActCol,ActHeight,ActLength,AttBox); for(i = ActLig,Current = Liste; i < (ActLig + ActHeight) && Current != (ListBoxItem_t *)NULL; i++,Current = Current->Suivant) WriteString(Current->Str,AttBox,i,ActCol+1); } Current = Liste; SelIndex = 0; InvLig = ActLig; PaintString(InvLig,InvCol,ActLength,AttInv); } break; case K_HOME: case CTRL_B: /* Si on est pas deja sur la premiere option, s'y deplacer, avec mise a jour de la porion de liste affichee si necessaire */ if(SelIndex != 0) { if(SelIndex < ActHeight && InvLig - SelIndex >= ActLig) /* L'item 0 est tjs dans la fenetre: simplement deplacer la barre */ PaintString(InvLig,InvCol,ActLength,AttBox); else { /* L'item 0 n'est plus dans la fenetre: effacer tous les items et les reafficher a partir du 0, puis y placer la barre */ ClrZone(ActLig,ActCol,ActHeight,ActLength,AttBox); for(i = ActLig,Current = Liste; i < (ActLig + ActHeight) && Current != (ListBoxItem_t *)NULL; i++,Current = Current->Suivant) WriteString(Current->Str,AttBox,i,ActCol+1); } Current = Liste; SelIndex = 0; InvLig = ActLig; PaintString(InvLig,InvCol,ActLength,AttInv); } break; case K_END: case CTRL_E: /* Si on est pas sur la derniere option, s'y placer, avec mise a jour de la porion de liste affichee si necessaire */ if(SelIndex != NbreItem - 1) { if(InvLig + NbreItem - SelIndex <= ActLig + ActHeight) /* Le dernier item est deja dans la fenetre: simplement deplacer la barre */ PaintString(InvLig,InvCol,ActLength,AttBox); else { /* Le dernier item n'est pas dans la fenetre: effacer tout et les reafficher pour avoir le dernier item en derniere position de la fenetre */ ClrZone(ActLig,ActCol,ActHeight,ActLength,AttBox); /* Positionner Current sur le premier item a devoir etre reaffiche en remontant la liste a partir de la fin */ for(i = 0,Current = End; i < ActHeight - 1 && Current != (ListBoxItem_t *)NULL; i++,Current = Current->Prec); /* Affichage */ for(i = ActLig; i < (ActLig + ActHeight) && Current != (ListBoxItem_t *)NULL; i++,Current = Current->Suivant) WriteString(Current->Str,AttBox,i,ActCol+1); } Current = End; SelIndex = NbreItem - 1; if(NbreItem < ActHeight) InvLig = ActLig + SelIndex; else InvLig = ActLig + ActHeight - 1; PaintString(InvLig,InvCol,ActLength,AttInv); } break; case CTRL_U: case K_PGUP: /* Si on est au milieu ou a la fin de la fenetre, remonter sur la premiere option Si on est au sommet de la fenetre, rectifier l'affichage vers le haut d'une fenetre moins un element. Si il ne reste pas assez d'elements pour faire ca, reafficher depuis le debut et placer la barre sur le premier element */ if(SelIndex != 0) { PaintString(InvLig,InvCol,ActLength,AttBox); if(InvLig != ActLig) { /* La barre est au milieu de la fenetre: simplement la placer au sommet */ for(i=InvLig;i>ActLig;i--,Current = Current->Prec,SelIndex--); InvLig = ActLig; } else { /* La barre est deja au sommet: effacer tout et remonter dans la liste d'un nombre d'items egal a la hauteur de la fenetre. */ for(i = 0; i < ActHeight - 1 && Current != (ListBoxItem_t *)NULL; i++,Current = Current->Prec,SelIndex--); /* Si Current est a NULL, c'est que ce qui restait de la liste est plus petit qu'une hauteur de fenetre: on va donc reafficher depuis le debut */ if(Current == (ListBoxItem_t *)NULL) { Current = Liste; SelIndex = 0; } /* Affichage */ Temp = Current; ClrZone(ActLig,ActCol,ActHeight,ActLength,AttBox); for(i = ActLig; i < (ActLig + ActHeight) && Temp != (ListBoxItem_t *)NULL; i++,Temp = Temp->Suivant) WriteString(Temp->Str,AttBox,i,ActCol+1); InvLig = ActLig; } PaintString(InvLig,InvCol,ActLength,AttInv); } break; case CTRL_D: case K_PGDN: /* Idem PgUp, mais vers le bas */ if(SelIndex != NbreItem - 1) { PaintString(InvLig,InvCol,ActLength,AttBox); if(InvLig != ActLig + ActHeight - 1) { /* La barre est au milieu de la fenetre: simplement la placer a la fin */ if(NbreItem < ActHeight) { /* La liste est plus petite que la fenetre */ Current = End; InvLig = ActLig + NbreItem - 1; SelIndex = NbreItem - 1; } else { /* La liste est plus grande que la fenetre */ for(i=InvLig;i < ActLig + ActHeight - 1; i++,Current = Current->Suivant,SelIndex++); InvLig = ActLig + ActHeight - 1; } } else { /* La barre est deja a la fin: effacer tout et redescendre dans la liste d'un nombre d'items egal a la hauteur de la fenetre. - Temp memorisera le premier item a etre reaffiche, c-a-d celui qui sera au sommet de la fenetre - Current memorisera celui ou la barre en inverse se trouvera, dans ce cas, toujours sur la derniere ligne de la fenetre */ /* Donc, si il reste plus qu'une hauteur de fenetre dans la liste, lors d'un PGDN, l'item qui etait en inverse lorsque la barre est en fin de fenetre sera celui qui commencera les affichage a la fenetre suivante */ Temp = Current; /* Tester si il reste plus d'un fenetre a afficher */ for(i = 0; i < ActHeight - 1 && Current != (ListBoxItem_t *)NULL; i++,Current = Current->Suivant,SelIndex++); /* Si Current est a NULL, c'est que ce qui restait de la liste est plus petit qu'une hauteur de fenetre: on va donc reafficher une fenetre ayant le dernier item sur la derniere ligne */ if(Current == (ListBoxItem_t *)NULL) { /* Positionner Temp sur le premier item a devoir etre reaffiche en remontant la liste a partir de la fin. Current sera dans ce cas toujour le dernier item de la liste */ for(i = 0,Current = End,Temp = End,SelIndex = NbreItem - 1; i < ActHeight - 1 && Temp != (ListBoxItem_t *)NULL; i++,Temp = Temp->Prec); } /* Affichage */ ClrZone(ActLig,ActCol,ActHeight,ActLength,AttBox); for(i = ActLig; i < (ActLig + ActHeight) && Temp != (ListBoxItem_t *)NULL; i++,Temp = Temp->Suivant) WriteString(Temp->Str,AttBox,i,ActCol+1); InvLig = ActLig + ActHeight - 1; } PaintString(InvLig,InvCol,ActLength,AttInv); } break; } } return(rc); }