Fichero
de cabecera: menu.h
/*
*
menu.h
*
*
Created on: 13/04/2011
*
Author: juanon
*/
#ifndef
MENU_H_
#define
MENU_H_
#define
KB_ENTER "\n" // codigos que generan una serie de teclas
especiales: KB_
#define
KB_ESC "\e"
#define
KB_UP "\e[A" // los codigos los podemos ver en un terminal
Alt+Ctrl+F1, y pulsar tecla , antes de hacer login y veremos como se
genera
#define
KB_DOWN "\e[B"
#define
KB_LEFT "\e[D"
#define
KB_RIGHT "\e[C"
#define
KB_REPAG "\e[5~"
#define
KB_AVPAG "\e[6~"
struct
menuLista {
//
NODO :estructuras
dinamicas:
inicialmente no tienen tamaño pero a medida que necesitamos se puede
ampliar.
char
*cad;
//dato
struct
menuLista *sig;//puntero
siguiente
struct
menuLista *ant;//puntero
anterior
//la
ventaja de utilizar NODO es que no necesitamos saber el nº de
elementos como nos pasaria en una tabla
}
info;
//
Comunes
void
escribirAnchoFijo(int
ancho,
char
*texto);
//
dado un ancho,
cortara el texto si es mayor o rellenara con espacios blancos si el
texto es menor que el ancho
//
menuSimple
int
menuSimpleHorizontal(int
col,
int
fil,
char
**opc,
int
colorLetra,
int
colorFondo,
int
opcInicial);
//
muestra :en
la columna incial,
fila,
tabla de las opciones,
color de letra,
color de fondo,
y una posible opcion inicial
int
menuSimpleVertical(int
col,
int
fil,
char
**opc,
int
colorLetra,
int
colorFondo,
int
opcInicial,int
conMargen);//
en este ademas le ponemos un margen alrededor del menu.
//
menuLista
struct
menuLista *menuListaLeerArchivo(char
*nombreFichero,
int
ancho);
//
coge el archivo y lo lee en el ancho que me interesa y te crea la
lista
int
menuListaVertical(int
col,
int
fil,
int
ancho,
int
alto,
struct
menuLista *inicio,
int
colorLetra,
int
colorFondo);
//
el que ejecuta el menu azul:
lista que lee para crear el menu
void
menuListaDestruir(struct
menuLista *);
//
destruye la lista:
toda memoria que reservemos cuando no la uso hay que borrarla
#endif
/* MENU_H_ */
Fichero
principal: menu.c
#include
<stdio.h>
#include
<stdlib.h>
#include
<conio.h>
#include
<string.h>
#include
"menu.h"
int
ej_menuSimple()
{
char
*opciones[]={"cero","uno","dos","tres","cuatro","cinco","seis","siete","ocho","otro
ocho","diez",""};
//
matriz con su valores,
la opcion 11
(la
ultima tiene que ser siempre "",
para saber que esa es la ultima)
int
resp1 =
0;
int
resp2 =
0;
noCursor();
//oculta
el cursor
resp1 =
menuSimpleHorizontal(1,1,opciones,WHITE,GREEN,2);
//ver
linea 48
lista de opciones definida en la 8,
y la opcion por defecto es la 2
(que
es la tercera opcion)
resp2 =
menuSimpleVertical(1,2,opciones,WHITE,RED,3,1);
//letra
blanca,
fondo rojo,
opcion por defecto 3
(que
es la 4
en la lista),
y el 1
indica un ancho de borde 1
siCursor();
textcolor(LIGHTGRAY);
gotoxy(1,1);
printf("Valor
devuelto por menú horizontal: %d\n",
resp1);
printf("Valor
devuelto por menú vertical: %d\n",
resp2);
return
0;
}
int
ej_menuLista()
{
int
resp =
0;
struct
menuLista *menu;
//
creo una variable llamada menu,
que es un puntero a una estrucutra menulista
menu =
NULL;
//
le doy valor NULL:
menu apunte a ningun sitio,
a nulo
if
(!(menu
=
menuListaLeerArchivo("ejemplo.txt",
25)))
{
//
ir leyendo el contenido del archivo y fabricar la lista
//
y asigno al puntero menu el inicio de la lista.
printf("\n
ERROR: No se pudo abrir el archivo especificado");
exit(1);
}
noCursor();
resp =
menuListaVertical(10,
5,
25,
12,
menu,
WHITE,
BLUE);
menuListaDestruir(menu);
siCursor();
textcolor(LIGHTGRAY);
gotoxy(1,
3);
printf("Valor
devuelto por menuLista: %d\n",
resp);
return
0;
}
int
main()
{
ej_menuSimple();
ej_menuLista();
return
0;
}
//
comentario de documentacion:
descripcion corta y terminada en ".",
la descripcion larga es del punto en adelante
/**
*
Muestra y ejecuta un menú simple vertical.
*
@param col Coordenada X de la esquina superior izquierda del menú
*
@param fil Coordenada Y de la esquina superior izquierda del menú
*
@param opc Array de opciones a mostrar, debe terminar en una opción
vacía
*
@param colorLetra Color usado para el texto del menú y para el fondo
de la opción resaltada
*
@param colorFondo Color usado para el fondo del menú y para el texto
de la opción resaltada
*
@param opcInicial Opción resaltada inicial
*
@param conMargen Usar margen adicional rodeando el menú
*
@return Nº de opción seleccionada o -1 si se pulsó ESC
*/
int
menuSimpleVertical(int
col,
int
fil,
char
**opc,
int
colorLetra,
int
colorFondo,
int
opcInicial,int
conMargen)
{
char
teclas[10]="";
char
buffer[25];
int
c =
0;
int
ancho =
0;
int
cantidadOpciones;
//
Calcula ancho necesario
for(c
=
0;
*opc[c];
c++)
{
//
recorre todos los valores del array opc,
mientras que el contenido de opc tenga algo recorre cuando encuentra
"",
termina
if(strlen(opc[c])
>
ancho)
{
ancho =
strlen(opc[c]);
}
}
//
Total de opciones
cantidadOpciones =
c -
1;
//
c lo ha ido contando en el for,
y le resto 1,
pues la ultima era ""
if(opcInicial
>
cantidadOpciones)
{
opcInicial =
cantidadOpciones;
}
//
Recuadro de margen
if(conMargen)
{
textcolor(colorLetra);
textbackground(colorFondo);
sprintf(buffer,"%%%ds",
ancho +
4);//
Crear una cadena de formato:
buffer="%%%ds",ancho+4
(si
ancho vale=12):
%%:
muestra %,d=nº
entero,s=es
una letra:
buffer="%16s"
for(c
=
0;c
<=
cantidadOpciones+2;
c++)
{
gotoxy(col,
fil +
c);
printf(buffer,"
");
//
crea un recuadro printf("%16s","
");
}
fil++;
col+=2;
}
//
Bucle principal,
termina al pulsar ESC o ENTER
while(strcmp(teclas,
KB_ESC)
!=
0
&&
strcmp(teclas,
KB_ENTER)
!=
0)
{
//
se ha plsado alguna tecla que no es escape ni enter
//
Muestra el menu
textcolor(colorLetra);
textbackground(colorFondo);
for(c
=
0;
*opc[c];
c++)
{
//
for
como antes hasta encontar opc=""
gotoxy(col,
fil +
c);
escribirAnchoFijo(ancho,
opc[c]);
//
me interesa que ilumine todo el ancho fijo del menu
}
//
Muestra la opcion resaltada
textcolor(colorFondo);
textbackground(colorLetra);
gotoxy(col,fil
+
opcInicial);
//
me voy donde esta iluminada
escribirAnchoFijo(ancho,
opc[opcInicial]);
//la
dibujo
fflush(stdout);//
Necesario ya que ninguna opción contiene '\n',
de esta manera hacemos forzosamente salga a pantalla. Vacia el buffer de pantalla y lo escribe todo.
//
Espera la pulsación de una tecla
IniciaTeclado();
//gestionar
la pulsacion de tecla sin entrar enter
c =
1;
teclas[0]
=
getch();
//
recupero la tecla que hay
while(kbhit())
{
teclas[c++]
=
getch();
//
rescata el resto de caracteres por si fuese una secuencia \e[C,
\e[A
}
teclas[c]
=
'\0';
//
fin de cadena
TerminaTeclado();
//
Procesa la tecla pulsada
if(strcmp(teclas,
KB_UP)
==
0)
{
//tecla
arriba,
opcInicial --;
//
subo
}
if(strcmp(teclas,
KB_DOWN)
==
0)
{
//
tecla abajo
opcInicial ++;
/bajo
}
//
Asegura que la opción está dentro de los límites
if(opcInicial
<
0)
{
//
me salgo por arriba
opcInicial =
cantidadOpciones;
//entro
por debajo,
no hace tope
}
if(opcInicial
>
cantidadOpciones)
{
//me
salgo por abajo
opcInicial =
0;
//
reentro por arriba
}
}
//salida
de teclas ESC o ENTER
//
Si se salió con ESC,
se devolverá -1
if(strcmp(teclas,
KB_ESC)
==
0)
{
opcInicial =
-1;
}
return
opcInicial;
//
DEVUELVE LA OPCION QUE ESTA ILUMINADA
}
//
DOCUMENTACION DEL MENU SIMPLE HORIZONTAL
/**
*
Muestra y ejecuta un menú simple horizontal.
*
@param col Coordenada X de la esquina superior izquierda del menú
*
@param fil Coordenada Y de la esquina superior izquierda del menú
*
@param opc Array de opciones a mostrar, debe terminar en una opción
vacía
*
@param colorLetra Color usado para el texto del menú y para el fondo
de la opción resaltada
*
@param colorFondo Color usado para el fondo del menú y para el texto
de la opción resaltada
*
@param opcInicial Opción resaltada inicial
*
@return Nº de opción seleccionada o -1 si se pulsó ESC
*/
int
menuSimpleHorizontal(int
col,
int
fil,
char
**opc,
int
colorLetra,
int
colorFondo,
int
opcInicial)
{
char
teclas[10]="";
int
c =
0;
int
ancho =
0;
int
cantidadOpciones;
//
Calcula ancho necesario
for(c
=
0;
*opc[c];
c++)
{
if(strlen(opc[c])
>
ancho)
{
ancho =
strlen(opc[c]);
}
}
//
Total de opciones
cantidadOpciones =
c -
1;
if(opcInicial
>
cantidadOpciones)
{
opcInicial =
cantidadOpciones;
}
//
Bucle principal,
termina al pulsar ESC o ENTER
while(strcmp(teclas,
KB_ESC)
!=
0
&&
strcmp(teclas,
KB_ENTER)
!=
0)
{
//
Muestra el menu
textcolor(colorLetra);
textbackground(colorFondo);
for(c
=
0;
*opc[c];
c++)
{
gotoxy(col
+
ancho *
c,
fil);
escribirAnchoFijo(ancho,
opc[c]);
}
//
Muestra la opcion resaltada
textcolor(colorFondo);
textbackground(colorLetra);
gotoxy(col
+
ancho *
opcInicial,fil);
escribirAnchoFijo(ancho,
opc[opcInicial]);
fflush(stdout); //
Necesario ya que ninguna opción contiene \n
//
Espera la pulsación de una tecla
IniciaTeclado();
c =
1;
teclas[0]
=
getch();
while(kbhit())
{
teclas[c++]
=
getch();
}
teclas[c]
=
'\0';
TerminaTeclado();
//
Procesa la tecla pulsada
if(strcmp(teclas,
KB_LEFT)
==
0)
{
opcInicial --;
}
if(strcmp(teclas,
KB_RIGHT)
==
0)
{
opcInicial ++;
}
//
Asegura que la opción está dentro de los límites
if(opcInicial
<
0)
{
opcInicial =
cantidadOpciones;
}
if(opcInicial
>
cantidadOpciones)
{
opcInicial =
0;
}
}
//
Si se salió con ESC,
se devolverá -1
if(strcmp(teclas,
KB_ESC)
==
0)
{
opcInicial =
-1;
}
return
opcInicial;
}
/**
*
Escribe una cadena de caracteres de ancho fijo, rellena de bancos o
corta la cadena según sea necesaario.
*
@param ancho
*
@param texto
*/
void
escribirAnchoFijo(int
ancho,
char
*texto)
{
int
c =
0;
int
largoTexto =
strlen(texto);
//ALMACENO
LA LONGITUD QUE TIENE EL TEXTO:
nota strlen cuenta á como 1
byte,
pero son mas
char
*salida;
if(!(salida
=
(char
*)
malloc (sizeof(char)
*
ancho +
1)))
{
//
RESERVO MEMORIA DE ANCHO+1
(que
es el /0),
NOTA:
sizeof(char)
printf("Memoria
insuficiente\n");
//
si falla la reserva de memoria
exit(1);
}
while(c
<
ancho)
{
if(c
<
largoTexto)
{
//
imprimo el caracter
*(salida
+
c)
=
texto[c];
//
if(texto[c]
==
-61)
//
antes una letra siempre ocupaba un byte,
pero actalmente al tener letras acentuadas,
pueden usarse varios bytes para codificala
ancho++;
//
si el primer bye es 61
sabemos que ocupa 2
byte,
y es una letra acentuada (áéíóuñ)
if(texto[c]
<
32
&&
texto[c]
>=
0)
{
//
caracteres especiales ASCII:
0-31,
que no son imprimibles ESC ENTER
*(salida
+
c)
=
'
';
//LO
SUSTITUYE POR UN ESPACIO EN BLANCO
}
}
else
{
//CUANDO
C es igual o mayor que la longitud largoTexto
*(salida
+
c)
=
'
';
//relleno
con espacios en blanco hasta llegar a ancho
}
c++;
}
*(salida
+
c)
=
'\0';
//
se ha metido \0,
fin de cadena
printf("%s",
salida);
//TODO
EN UN PRINTF:
VALOR +
ESPACIOS
free(salida);
//
libero memoria:
ya que salida no la vuelvo a necesitar.
}
/**
*
Crea una lista doblemente enlazada con el contenido de un fichero.
*
@param nombreFichero Fichero con el contenido del menú
*
@param ancho Ancho de cada línea
*
@return Puntero al primer nodo de la lista
*/
struct
menuLista *menuListaLeerArchivo(char
*nombreFichero,
int
ancho)
{
FILE *f;
struct
menuLista *aux,
*inicio,
*final;
//creado
estos 3
punteros
inicio =
NULL;
//
primer puntero marca a NULL,
nos va a servir donde empieza la secuencia de nodo
//puntero
final:
nos va a apuntar donde esta el ultimo nodo //
puntero auxliar,
creas el nodo
f =
fopen(nombreFichero,
"r");
if
(!f)
return
NULL;
//
no se ha podido abrir el archivo
else
{
while
(!feof(f))
{
//
miestras que no sea final de fichero
if
(!(aux
=
(struct
menuLista *)
malloc(sizeof(struct
menuLista))))
{
//
crea en un sitio de la memoria,
ha reservado el espacio para que quepa una estructura tipo menulista
printf("\n
Memoria insuficiente ");
exit(1);
}
if(!(aux->cad
=
(char
*)
malloc(ancho
+
1)))
{
//
reservamos la memoria para la cadena de caracteres
printf("\n
Memoria insuficiente ");
exit(1);
}
fgets(aux->cad,
ancho +
1,
f);
//
lee del fichero,
y almacenara en el cap de la estructura aux,
la primera linea que lee del fichero
if(feof(f))
{
//liberamos
la cadena y el auxiliar.
free(aux->cad);
//
1º libero la cadena (el
orden es importante)
free(aux);
//
2º libero el nodo,
break;
}
if
(inicio
==
NULL) //
si es la primera línea (no
tiene nungun nodo)
inicio =
aux;
//
engancho el nodo a la lista
else
{
final->sig
=
aux;
aux->ant
=
final;
//marca
la estructura anterior
}
final =
aux;
//
1º pasada como es el unico nodo que hay,
es el primero como el ultimo,
apunto al bloque completo
//
en la 2º pasada,
final marcara a la 2º estructura
}
//
volvemos a leer el fichero
inicio->ant
=
NULL;
//
primer elmento punntero inicial marca a null
final->sig
=
NULL;
//
ultimo elemento puntero final marca a null
}
fclose(f);
return
inicio;
//
devuelvo el puntero donde se inicia la estructura.
}
/**
*
Muestra y ejecuta un menú vertical basado en una lista doblemente
enlazada.
*
@param col Coordenada X de la esquina superior izquierda del menú
*
@param fil Coordenada Y de la esquina superior izquierda del menú
*
@param ancho Ancho de cada opción del menú
*
@param alto Altura del menu en líneas
*
@param inicio Puntero al primer nodo de la lista
*
@param colorLetra Color usado para el texto del menú y para el fondo
de la opción resaltada
*
@param colorFondo Color usado para el fondo del menú y para el texto
de la opción resaltada
*
@return Nº de opción seleccionada o -1 si se pulsó ESC
*/
int
menuListaVertical(int
col,
int
fil,
int
ancho,
int
alto,
struct
menuLista *inicio,
int
colorLetra,
int
colorFondo)
{
struct
menuLista *aux,
*encendido,
*priPanta,
*ultPanta;
register
int
i; //
contador para bucles
char
op[10]
=
"\0"; //
tecla pulsaada
int
redibujar =
1;
register
int
linea_actual =
0; //
linea actual
int
c =
0;
aux =
encendido =
priPanta =
ultPanta =
inicio; /*
iniciamos los valores para que apunte inicio */
//
Mueve "ultPanta"
"alto"
posiciones más adelante que inicio
for
(i
=
0;
i <
alto;
i++)
{
ultPanta =
ultPanta->sig;
if
(ultPanta->sig
==
NULL)
break;
}
ultPanta =
ultPanta->ant; //
retrocedemos para apuntar al puntero anterior
while
((strcmp(op,
KB_ESC))
!=
0
&&
(strcmp(op,KB_ENTER)
!=
0))
{
if
(redibujar)
{
textcolor(colorLetra);
textbackground(colorFondo);
aux =
priPanta;
for
(i
=
0;
i <
alto;
i++)
{
gotoxy(col,
fil +
i);
if(aux
==
encendido)
{
textcolor(colorFondo);
textbackground(colorLetra);
}
else
{
textcolor(colorLetra);
textbackground(colorFondo);
}
escribirAnchoFijo(ancho,
aux->cad);
aux =
aux->sig;
}
fflush(stdout);
}
IniciaTeclado();
c =
1;
op[0]
=
getch();
while(kbhit())
{
op[c++]
=
getch(); //
recupera todos los caracteres pendientes
}
op[c]
=
'\0';
TerminaTeclado();
//
Arriba
if
(strcmp(op,
KB_UP)
==
0)
{
if
(encendido
==
inicio)
{
redibujar =
0;
}
else
{
linea_actual--;
redibujar =
1;
if(encendido
==
priPanta)
{
priPanta =
priPanta->ant;
ultPanta =
ultPanta->ant;
encendido =
encendido->ant;
}
else
{
encendido =
encendido->ant;
}
}
}
//
Abajo
if
(strcmp(op,
KB_DOWN)
==
0)
{
if
(encendido->sig
==
NULL)
{
redibujar =
0;
}
else
{
linea_actual++;
redibujar =
1;
if(encendido
==
ultPanta)
{
priPanta =
priPanta->sig;
ultPanta =
ultPanta->sig;
encendido =
encendido->sig;
}
else
{
encendido =
encendido->sig;
}
}
}
//REPAG
if
(strcmp(op,
KB_REPAG)
==
0)
{
if
(encendido
==
inicio)
{
redibujar =
0;
}
else
{
redibujar =
1;
for
(i
=
0;
i <
alto;
i++)
{
if(encendido
==
inicio)
{
break;
}
if(priPanta
!=
inicio)
{
priPanta =
priPanta->ant;
ultPanta =
ultPanta->ant;
}
encendido =
encendido->ant;
linea_actual--;
}
}
}
//AVPAG
if
(strcmp(op,
KB_AVPAG)
==
0)
{
if
(encendido->sig
==
NULL)
{
redibujar =
0;
}
else
{
redibujar =
1;
for
(i
=
0;
i <
alto;
i++)
{
if(encendido->sig
==
NULL)
{
break;
}
if
(ultPanta->sig
!=
NULL)
{
priPanta =
priPanta->sig;
ultPanta =
ultPanta->sig;
}
encendido =
encendido->sig;
linea_actual++;
}
}
}
aux =
priPanta;
} //
termina while
if(strcmp(op,
KB_ESC)
==
0)
{
linea_actual =
-1;
}
return
linea_actual;
} //
fin de la función
/**
*
Libera la memoria asigana a la lista del menú
*
@param inicio Puntero al primer nodo de la lista
*/
void
menuListaDestruir(struct
menuLista *inicio)
{
struct
menuLista *aux,
*aux2;
for
(aux
=
inicio;
aux->sig
!=
NULL;
aux =
aux->sig)
{
aux2 =
aux->sig;
free(aux->cad);
free(aux);
aux =
aux2;
}
}
No hay comentarios:
Publicar un comentario