Curso Básico de Programación
en Visual Basic

 

Entrega Veinticinco: 18/Oct/98 (la entrega de plata)
por Guillermo "guille" Som

Si quieres linkar con las otras entregas, desde el índice lo puedes hacer

 

Si esto de los números de las entregas fuese como los años de un matrimonio, con esta cumpliríamos las bodas de plata, pero esto no es ningún matrimonio ni nada que se le parezca... o casi, ya que es casi como un compromiso entre tú, el lector, y yo, el profe, así que vamos a ver si seguimos llevándonos bien para no tener que divorciarnos ni nada por el estilo.
La mejor forma de llevarnos bien es que cada uno haga lo que tiene que hacer, tú estudiar y practicar y yo dedicarme a seguir cumpliendo entregas... que por otro lado son menos pesadas que cumplir años...

Hoy vamos a continuar con los eventos, esta será la última de la serie, que ya está bien.
En la próxima entrega haremos un pequeño repaso a cómo se deben declarar las variables y algunos otros consejos que espero que sigas a pies juntillas, o al menos los tenga casi siempre presentes... ya verás a que me refiero, pero ahora vamos a ver esos cuantos eventos que en algunos casos es conveniente saber utilizar.

 

Otros eventos.

Ya habrás comprobado que eventos hay para casi cualquier cosa, muchos de ellos no se suelen usar de forma habitual, y siquiera los habituales. La razón es porque no siempre es necesario interceptar todos los avisos que nos envían los controles. Según el tipo de control y en ciertas de estancias se usan unos u otros pero aún no he visto ninguna aplicación que necesitara manipular todo, qué digo todo, ni siquiera la mitad de ellos. Creo que sería una locura.

Hay que saber que algunos eventos son procesados por los controles o formularios cuando ese control o formulario está activo o recibe el foco, mientras esto no ocurra, el susodicho control estará en standby.

 

Evento GotFocus.

Cuando un control se convierte en activo y recibe el foco, produce el evento GotFocus, es decir que tiene el foco de atención del usuario; de igual forma, cuando deja de ser el control activo, hace sonar la alarma mediante el evento LostFocus.
El control en el que suele usarse estos eventos, de forma más habitual, es el TextBox.
Normalmente en el evento de GotFocus se suele seleccionar todo el texto que contenga, para hacerlo, como ya vimos con "profusión" en la entrega anterior, se suele usar un código como este:

Private Sub Text1_GotFocus()
    With Text1
    .SelStart = 0
    .SelLength = Len(.Text)
    End With
End Sub

Aunque ya deberías saberlo, te vuelvo a repetir lo que hacen esas líneas de código:

.SelStart = 0 Le indica al control que la posición de inicio del texto seleccionado empiece por la posición cero, es decir la primera letra.
.SelLength = Len(.Text) SelLength es la longitud del texto seleccionado y el valor que se le asigna es la longitud total del texto que haya en el control.

Por tanto lo que se hace es seleccionar todo el contenido del TextBox, las propiedades SelStart y SelLength también se podrían usar para saber cuál es el texto que hay seleccionado y poderlo usar en los casos de búsqueda y sustitución de palabras. Pero hay otra forma más rápida de saberlo, mediante la propiedad SelText del TextBox, con esta propiedad nos indica que texto es el que está seleccionado en ese momento.
Para almacenar en una variable el texto que actualmente está seleccionado, haremos esto:

'Guardar en sBuscar el texto seleccionado en Text1
sBuscar = Text1.SelText

Evento LostFocus.

En el evento LostFocus se suele comprobar si el contenido es el que se espera que haya. Realmente es un pequeño problema el tener que validar los datos en este evento, ya que cuando se producen, inmediatamente hay otro control que recibe el foco y si resulta que los datos contenidos en ese control no son los que esperábamos se intente volver a darle el foco a este control, con lo cual se produciría un evento LostFocus en el control que recibió el foco cuando el primero lo perdió y si en ese segundo control también hay una comprobación de datos y éstos son erróneos, tendremos tal cacao que el pobre Visual Basic se terminará quejándose.

Aunque el que se habrá quejado habrá sido tú, ya que me imagino que no te has enterado de lo que acabo de decir, pero no te voy a dar un ejemplo para que lo entiendas, en vez de eso, te voy a poner un ejercicio y así de camino practicas un poco.

Crea un proyecto nuevo, en el Form inserta dos TextBox y CommandButton, el botón se usará para salir del programa, en cada uno de los TextBox se comprobará que el contenido no esté vacio y no se permitirá salir del TextBox en cuestión si el contenido no es el que esperamos que sea, por ejemplo, podrías hacer que el primero sólo admita números y el segundo sólo letras. Aunque esto se podría hacer de otra forma, prefiero que lo hagas en el LostFocus, para que practiques.
Pero como te digo, si lo prefieres, puedes validar cualquier otra cosa, por ejemplo que tenga algo escrito o cualquier otra cosa que se te ocurra... yo no suelo validar casi nada en esos eventos ya que normalmente suelen dar más quebraderos de cabeza que ventajas. Pero de lo que se trata es que seas el que decida que es lo mejor o peor para tus programas, esto es extensible a cualquier otra cosa que yo u otra persona pueda decirte: evalúalo primero y después decide si es lo que realmente te hace falta.

Para que realmente funcionen esas comprobaciones, asignales una cadena vacia nada más cargarse el formulario y añade también código al botón de salir para que descargue el formulario, así podrás comprobar que hasta que no se cumplan las condiciones especificadas, no dejará de "pitarte" para que escribas lo que debes escribir.
Por supuesto, estas comprobaciones se harán en los eventos LostFocus de cada control y si el contenido no cumple las condiciones que esperamos, habrá que volver a darle el foco, usando el método SetFocus, además de un Beep para que sepamos que los datos contenidos no son válidos.

El código a usar sería algo como esto:

Private Sub Text1_LostFocus()
    '...
    'Si el contenido del control no es válido entonces
    Beep
    Text1.SetFocus    'Volvemos a darle el foco

End Sub

Pruébalo y verás cómo ahora entenderás lo que digo, y si no lo entiendes al menos habrás visto cómo el Visual Basic se queja.

Bueno, vale, te pongo el código que no permitirá que los TextBoxes estén vacios y si está al cambiar de control, se quedará en ese, pero como te he dicho, esto volverá loco al VB.

'------------------------------------------------------------------
'Pruebas para la entrega veinticinco del curso básico   (18/Oct/98)
'------------------------------------------------------------------
Option Explicit

Private Sub Form_Load()
    'Borramos el contenido de los TextBox
    Text1 = ""
    Text2 = ""

    'Hacemos que el Text1 sea el primero en recibir el foco
    Text1.TabIndex = 0
End Sub

Private Sub cmdSalir_Click()
    Unload Me
End Sub

Private Sub Text1_LostFocus()
    'No permitir que el TextBox esté vacio
    If Len(Trim$(Text1)) = 0 Then
        Beep
        Text1.SetFocus
    End If
End Sub

Private Sub Text2_LostFocus()
    'No permitir que el TextBox esté vacio
    If Len(Trim$(Text2)) = 0 Then
        Beep
        Text2.SetFocus
    End If
End Sub

Ejecuta el programa, el foco lo tendrá el primer cuadro de texto, sin escribir nada, pulsa la tecla TAB para cambiar al otro control, verá cómo el VB se vuelve turuta... Tendrás que pulsar Control+Break (Cttl+Inter) para pararlo...

En la versión 6 del VB se incluye un nuevo evento: Validate que será el que se use para "validar" el contenido de un TextBox, en lugar de hacerlo en el evento LostFocus. Sobre este evento ya veremos algo más adelante (en unas cuantas celebraciones más), aunque dentro de poco lo podrás ver en la sección sobre el VB6 que tengo en mis páginas, las cuales seguramente se sumarán en su momento a este curso... cuando les toque el turno.

 

Evento Change.

Otro evento relacionado con los controles que permiten introducir información es: Change. Este evento se disparará siempre que el contenido del control cambie, por tanto en un TextBox se producirá si lo que hay en la propiedad Text cambia.
Esto ya lo vimos en las primeras entregas, además de cómo no usar este evento, para que no diese un error de desbordamiento de la pila.

 

Arrastrar y Soltar controles y otros "objetos".

Pero no creas que esto terminar aquí ya que te voy a explicar algo sobre cómo arrastrar y soltar, ya que ésta es una de las peculiaridades del entorno Windows y es una de las cosillas que deberías de tener siempre en tus aplicaciones. Aunque el ejemplo y la explicación que te voy a dar es para el Visual Basic 5, si quieres ver cómo se hace con la versión anterior te recomiendo que te leas los trucos de mis páginas.
También te voy a dar un ejemplo de cómo hacer que se puedan mover los controles de tamaño y o posición dentro del formulario, en tiempo de ejecución, claro. No es perfecto, pero medio funciona, el problema que tiene es que hay que codificar en muchos eventos, pero para ello he creado unos procedimientos para hacer menos tedioso el tema...
La "versión" que tenía antes de esta "utilidad", no permitía que se soltara un control encima de otro, sólo permitía que se soltasen en el formulario, osea, si en la nueva posición había otro control, no se dejaba en ese sitio...
Pero como te digo, ahora va mucho mejor... después de el evento OLEDragDrop veremos el código y un poco de explicación.

 

Evento OLEDragDrop.

Veamos primero cómo hacer que nuestra aplicación permita que se suelte un fichero y cuando se haga se muestre en un TextBox, por tanto no pruebes con ficheros que no sean de texto.
Para poder hacer esto, hay que asignar las siguientes propiedades del Form y del TextBox que mostrará el texto:
OLEDropMode = 1 ' Manual
El TextBox debería ser Multiline para que se muestre bien el fichero soltado.

Para asignar a un TextBox el primer fichero de los que se sueltan en un formulario o cualquier control que tenga la propiedad OLEDropMode igual a uno, vamos a usar un procedimiento genérico, se supone que será para ficheros de texto, incluso documentos RTF si el control en cuestión es un RecichTextBox.

Private Sub AsignarFichero(unTextBox As Control)
    'Asignar al TextBox indicado el contenido del fichero soltado
    Dim sFic As String
    Dim nFic As Long
    Dim sText As String

    'Asignar sólo el primer fichero
    sFic = Data.Files(1)

    'Abrirlo y leer el contenido
    nFic = FreeFile
    Open sFic For Input As nFic
    sText = Input$(LOF(nFic), nFic)
    Close nFic

    'Asignarlo al control indicado
    unTextBox = sText
End Sub

Para usar este procedimiento, lo llamaremos desde el evento OLEDragDrop del formulario y del control en cuestión:

Private Sub Form_OLEDragDrop(Data As DataObject, Effect As Long, _
            Button As Integer, Shift As Integer, X As Single, Y As Single)
    AsignarFichero Text3
End Sub

Private Sub Text3_OLEDragDrop(Data As DataObject, Effect As Long, _
            Button As Integer, Shift As Integer, X As Single, Y As Single)
    AsignarFichero Text3
End Sub

Ahora ya puedes "soltar" un fichero de texto en el formulario o el TextBox y se mostrará el contenido en ese TextBox.

 

Evento DragDrop y propiedad Drag

Ejemplo de cómo redimensionar y mover controles en tiempo de ejecución.

Ahora vamos a ver cómo mover controles por el formulario y soltarlos en la posición deseada. También vamos a ver cómo, mediante el uso de una llamada al API de Windows, podemos hacer que un control sea "redimensionable", la única pega para hacer que sea redimensionable, o si lo prefieres, la única condición, es que el control sea una "ventana", no te confundas, los controles en Visual Basic son controles, pero el Windows trata a algunos de ellos como si fuesen ventanas, por ejemplo el control TextBox es para Windows una ventana, sin embargo el control Label no lo es. Normalmente se puede saber si un control es "ventanero" si tiene la propiedad hWnd, ya que los que no son realmente una ventana, no tienen esa propiedad.

Veamos el código y las declaraciones para hacer que todo esto pueda funcionar.

Primero te explico que en cada control hay que hacer lo siguiente en los eventos MouseDown y DragDrop, salvo en el Form, como veremos después, ya que en el Form sólo se detectará la acción de soltar el control:

Private Sub NombreDelControl_DragDrop(Source As Control, X As Single, Y As Single)
    EndDragDrop NombreDelControl, Source, X, Y
End Sub

Private Sub NombreDelControl_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    IniciarDrag NombreDelControl, Button, X, Y
End Sub

En los controles que no queramos permitir que se suelte, hay que "cancelar" la operación de Drag & Drop, por ejemplo, si tenemos un botón y no queremos que se suelte encima, habría que hacer algo como esto:

Private Sub cmdSalir_DragDrop(Source As Control, X As Single, Y As Single)
    'Cancelar la acción de soltado
    CancelarDrag Source
End Sub

El procedimiento de calcular la nueva posición se encarga de hacer las comprobaciones pertinente para posicionar adecuadamente el control soltado; si el control que se mueve, está contenido a su vez en otro control, no se permite que se mueva fuera de su contenedor. Esta forma de actuar puedes cambiarla si quieres, aunque deberías tener en cuenta que el contenedor debe ser el control de destino o el formulario en caso de que se suelte sobre el form, también podrías hacer que al soltarse un control en otro, el que recibe el control fuese el contenedor... todo es cuestión de que hagas pruebas y lo adecues a tus gustos.

Una cosa que debes tener en cuenta si quieres mover los TextBox, es que no te permitirá seleccionar el texto usando el ratón, ya que al pulsarse con el ratón se llama al procedimiento que "avisa" que se está haciendo el Drag&Drop y al dejarlo "invisible", pierde el foco, aunque esto se soluciona en el procedimiento que calcula la nueva posición, podrías querer que no se pueda mover, ese es el caso del Text4 del ejemplo que pongo a continuación.

Ahora veamos el código de este ejemplo,
el código lo puedes conseguir pulsando en este link: codigo25.zip (6.62 KB)

 

'----------------------------------------------
'Prueba para redimensionar Pictures
' y mover controles                 (23/Sep/96)
'
'Revisado/mejorado: el 18/Oct/98
'
'©Guillermo 'guille' Som, 1996-98
'----------------------------------------------
Option Explicit

Dim frmName As String

Dim DY As Single
Dim DX As Single

Dim NumColumnas As Integer
Dim NumFilas As Integer
Dim bIniciando As Boolean

'Declaraciones del API para 32 bits
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
    (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
    (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function SetWindowPos Lib "user32" _
    (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, _
    ByVal X As Long, ByVal Y As Long, ByVal cX As Long, ByVal cY As Long, _
    ByVal wFlags As Long) As Long

Const GWL_STYLE = (-16)
Const WS_THICKFRAME = &H40000
'
Const SWP_DRAWFRAME = &H20
Const SWP_NOMOVE = &H2
Const SWP_NOSIZE = &H1
Const SWP_NOZORDER = &H4

Private Sub CmdSalir_Click()
    Unload Me
End Sub

Private Sub cmdSalir_DragDrop(Source As Control, X As Single, Y As Single)
    'Cancelar la acción de soltado
    CancelarDrag Source
End Sub

Private Sub Form_DragDrop(Source As Control, X As Single, Y As Single)
    'Cuando se suelta en el form, no especificar el destino
    EndDragDrop Source, X, Y
End Sub

Private Sub Form_Load()
    bIniciando = True

    'Asignamos el nombre del formulario, ya que lo necesitaremos
    'para saber si se mueve encima del form o de otro control
    frmName = Me.Name

    'Hacer estos controles redimensionables,
    'usando el API
    CambiarEstilo PicColum(0)
    CambiarEstilo Text2
    CambiarEstilo Picture1
    CambiarEstilo Frame1
    CambiarEstilo Picture3
    CambiarEstilo Picture4
    CambiarEstilo Text4

    Text3 = "Left= " & Text3.Left & ", Top= " & Text3.Top

    'Para crear filas/columnas en un Control
    NumFilas = 2
    Label1(0) = "Cabecera de la colunma"
    Load Text1(1)
    With Text1(1)
        Set .Container = PicColum(0)
        .Visible = True
        .Top = Text1(0).Top + Text1(0).Height
    End With
    Load Label2(1)
    With Label2(1)
        .Visible = True
        .Top = Label2(0).Top + Label2(0).Height
        .Caption = "Fila 2"
    End With
    NumColumnas = 1

    bIniciando = False
End Sub

Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    Text2.Text = "X= " & Str$(X) & ", Y= " & Str$(Y)
End Sub

Private Sub Frame1_DragDrop(Source As Control, X As Single, Y As Single)
    EndDragDrop Source, X, Y, Frame1
End Sub

Private Sub Frame1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    IniciarDrag Frame1, Button, X, Y
End Sub

Private Sub Label1_DragDrop(Index As Integer, Source As Control, X As Single, Y As Single)
    EndDragDrop Source, X, Y, Label1(Index)
End Sub

Private Sub Label2_DragDrop(Index As Integer, Source As Control, X As Single, Y As Single)
    EndDragDrop Source, X, Y, Label2(Index)
End Sub

Private Sub PicColum_DragDrop(Index As Integer, Source As Control, X As Single, Y As Single)
    EndDragDrop Source, X, Y, PicColum(Index)
End Sub

Private Sub PicColum_Resize(Index As Integer)
    Dim k As Integer
    Dim i As Integer

    If bIniciando Then Exit Sub

    'ajustar el ancho del Label y los texts
    Label1(Index).Width = PicColum(Index).Width
    For i = 0 To NumFilas - 1
        k = i * NumColumnas + Index
        Text1(k).Width = PicColum(Index).Width
    Next
    PicColum(0).Left = Label2(0).Width
    For i = 0 To NumColumnas - 1
        If i > 0 Then
            PicColum(i).Left = PicColum(i - 1).Left + PicColum(i - 1).Width
        End If
        PicColum(i).Top = 0
    Next
End Sub

Private Sub Picture1_DragDrop(Source As Control, X As Single, Y As Single)
    EndDragDrop Source, X, Y, Picture1
End Sub

Private Sub Picture1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    IniciarDrag Picture1, Button, X, Y
End Sub

Private Sub Picture2_DragDrop(Source As Control, X As Single, Y As Single)
    EndDragDrop Source, X, Y, Picture2
End Sub

Private Sub Picture2_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    IniciarDrag Picture2, Button, X, Y
End Sub

Private Sub Picture3_DragDrop(Source As Control, X As Single, Y As Single)
    EndDragDrop Source, X, Y, Picture3
End Sub

Private Sub Picture3_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    IniciarDrag Picture3, Button, X, Y
End Sub

Private Sub Picture4_DragDrop(Source As Control, X As Single, Y As Single)
    EndDragDrop Source, X, Y, Picture4
End Sub

Private Sub Picture4_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    IniciarDrag Picture4, Button, X, Y
End Sub

Private Sub Text1_DragDrop(Index As Integer, Source As Control, X As Single, Y As Single)
    EndDragDrop Source, X, Y, Text1(Index)
End Sub

Private Sub Text2_DragDrop(Source As Control, X As Single, Y As Single)
    EndDragDrop Source, X, Y, Text2
End Sub

Private Sub Text2_DragOver(Source As Control, X As Single, Y As Single, State As Integer)
    'Si no se quiere que pase por encima otro control
    'CancelarDrag Source
End Sub

Private Sub Text2_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    IniciarDrag Text2, Button, X, Y
End Sub

Private Sub Text3_DragDrop(Source As Control, X As Single, Y As Single)
    EndDragDrop Source, X, Y, Text3
End Sub

Private Sub Text3_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    IniciarDrag Text3, Button, X, Y
End Sub

Private Sub Text4_DragDrop(Source As Control, X As Single, Y As Single)
    EndDragDrop Source, X, Y, Text4
End Sub

Private Sub Text4_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    'Si se quiere poder seleccionar con el ratón
    'quitar la siguiente llamada, aunque entonces no se permitirá
    'mover el control
    'IniciarDrag Text4, Button, X, Y
End Sub

Private Sub CambiarEstilo(queControl As Control)
    Dim Style As Long

    On Local Error Resume Next

    Style = GetWindowLong(queControl.hwnd, GWL_STYLE)
    If Err Then
        Err = 0
        MsgBox "El control " & queControl.Name & " no permite que se redimensione", vbInformation
        Exit Sub
    End If
    Style = Style Or WS_THICKFRAME
    Style = SetWindowLong(queControl.hwnd, GWL_STYLE, Style)
    Style = SetWindowPos(queControl.hwnd, _
        Me.hwnd, 0, 0, 0, 0, SWP_NOZORDER Or _
        SWP_NOSIZE Or SWP_NOMOVE Or SWP_DRAWFRAME)

End Sub

Private Sub CancelarDrag(Source As Control)
    Source.Visible = True
    Source.Drag vbCancel
End Sub

Private Sub EndDragDrop(Source As Control, X As Single, Y As Single, Optional Dest As Object)
    Dim posX As Long
    Dim posY As Long

    If Dest Is Nothing Then
        'Si no se especifica el control de origen,
        'por ejemplo cuando se suelta en el formulario
        posX = -30
        posY = -30
        'Si el nombre del contenedor del Source no es el nombre
        'del destino, no moverlo.
        'Esto ocurrirá cuando se mueve un control contenido en otro
        'y el destino no es el control que lo contiene
        If Source.Container.Name <> frmName Then
            CancelarDrag Source
            Exit Sub
        End If
    Else
        'Si el control destino es el mismo que el contenedor del Source
        'Esto ocurrirá cuando el control que se mueve se suelta
        'en el control en que está contenido
        If Dest.Name = Source.Container.Name Then
            posX = -60
            posY = -60
        Else
            'Si el nombre del contenedor del Source no es el nombre
            'del destino, no moverlo.
            'Esto ocurrirá cuando se mueve un control contenido en otro
            'y el destino no es el control que lo contiene
            If Source.Container.Name <> frmName Then
                CancelarDrag Source
                Exit Sub
            End If

            With Dest
                'El nombre del formulario se asignará
                'a la variable frmName
                If .Container.Name = frmName Then
                    'Esto ocurrirá cuando se suelte un control
                    'en otro que esté contenido en el formulario,
                    'no contenido en otro control
                    posX = .Left
                    posY = .Top
                Else
                    'Esto ocurrirá cuando se suelte en un control
                    'que está contenido en otro control
                    posX = .Container.Left + .Left + 60
                    posY = .Container.Top + .Top + 60
                End If
            End With
        End If
    End If

    'Posicionar el control soltado
    With Source
        .Visible = True
        .Move posX + X - DX, posY + Y - DY
        .Drag vbEndDrag
        .ZOrder
    End With
    'Si el Source es un textbox, darle el foco
    If TypeOf Source Is TextBox Then
        Source.SetFocus
    End If
    'Si se van a usar RichTextBox, hacer la comparación correspondiente
End Sub

Private Sub IniciarDrag(Source As Control, Button As Integer, X As Single, Y As Single)
    If Button = vbLeftButton Then
        DX = X
        DY = Y
        Source.Drag vbBeginDrag
        'Cambiar a no visible, ya que si no,
        'el form no detectaría que se ha soltado,
        'si el puntero del ratón no sale del control.
        Source.Visible = False
        Source.Drag
    End If
End Sub

Hasta aquí ha llegado esta "entrega de plata", si quieres hacer un comentario, guárdatelo para otra ocasión... je, je.

Parte de esta entrega la tenía escrita desde el 15 de julio de 1998, en total un folio manuscrito por las dos caras y pasado al Word usando el ViaVoice de IBM que aunque no me entiende, (seguramente porque yo no "dicto" bien), algo es algo y en cuanto le enseñe a entenderme seguramente las entregas llegarán con mayor rapidez. Aunque es algo "lentillo" y la verdad es que el código no hay forma de hacérselo entender, pero seguiré probando a ver si logro que algún día sepa de que le estoy hablando... al menos me rio un poco de lo que escribe cuando no me entiende...

Hasta la próxima que será... imagínatelo...

Nos vemos.
Guillermo

Nerja, 18 de Octubre de 1998


 
entrega anterior ir al índice siguiente entrega

Ir al índice principal del Guille