retro text box array

Post your FreeBASIC tips and tricks here. Please don’t post your code without including an explanation.
BasicCoder2
Posts: 3566
Joined: Jan 01, 2009 7:03
Location: Australia

retro text box array

Postby BasicCoder2 » Jul 26, 2015 1:34

This uses a version of my retro text box code to demo its use in an array.
By retro I mean it is written the way I wrote code with QBASIC and only uses the standard font.
One day I might start encapsulating a control's software as a class :)

The text can be left/right or centered for each text box.
If the text in the selected text box is larger than the width of the text box its full contents are displayed on the bottom bar.

Code: Select all

screenres 640,480,32
color rgb(0,0,0),rgb(200,200,240):cls

dim shared as integer mx,my,mb

' GLOBAL VARIABLES USED BY TEXTBOX ROUTINES DrawTextBox and EditTextBox
dim shared as integer cursor

type TextBox
    as integer x
    as integer y
    as integer w
    as integer h
    as string  t    'text
    as integer a    'active=1
    as integer j    'justify left = 0 /center = 1/right = 2
    as integer posX 'retains info when text larger than width of text box
end type

cursor = 0   

sub drawTextBox(tb as textBox)
    dim as integer shiftX
    screenlock()
   
    '*******************  THIS DRAWS THE BOX ***************************
    'clear rectangle
    line (tb.x,tb.y)-(tb.x+tb.w+8,tb.y+tb.h),rgb(255,255,255),bf
    'draw border
    line (tb.x,tb.y)-(tb.x+tb.w+8,tb.y+tb.h),rgb(127,127,127),b
    line (tb.x+1,tb.y+1)-(tb.x+tb.w+7,tb.y+tb.h-1),rgb(127,127,127),b
    '*******************************************************************
   
    if tb.a = 1 then
        'keep cursor within the box
        if cursor > tb.posX+tb.w/8 then
            tb.posX = tb.posX + 1
        end if
   
        if cursor < tb.posX+1 then
            if tb.posX>0 then
                tb.posX = tb.posX - 1
            end if
        end if
    end if

    if len(tb.t)*8 > tb.w or tb.j = 0 then
        draw string (tb.x+4,tb.y+4),mid(tb.t,tb.posX+1,tb.w\8)   
        if tb.a = 1 then
            line (tb.x+cursor*8+4-tb.posX*8,tb.y+2)-(tb.x+cursor*8+5-tb.posX*8,tb.y + 14),rgb(10,10,10),bf  'draw cursor
        end if
    else
        if tb.j = 1 then shiftX = tb.w\2-len(tb.t)*8/2
        if tb.j = 2 then shiftX = tb.w-len(tb.t)*8
        draw string (tb.x+4+shiftX,tb.y+4),tb.t
        if tb.a = 1 then
            line (tb.x+cursor*8+4+shiftX,tb.y+2)-(tb.x+cursor*8+5+shiftX,tb.y + 14),rgb(10,10,10),bf  'draw cursor
        end if
    end if
   
    screenunlock()

end sub

function editTextBox(tb as TextBox) as integer

    dim as string key
    dim as integer ascKey

    cursor =(mx - tb.x)\8+tb.posX
    if cursor > len(tb.t) then
        cursor = len(tb.t)
    end if
    while mb=1
        getmouse mx,my,,mb
    wend

    tb.a = 1 'activate
    while inkey<>"":wend  'clear buffer
    drawTextBox(tb) 'show cursor
    do
        getmouse mx,my,,mb
        if mb=1 then
            if  mx>tb.x and mx<tb.x+tb.w and my>tb.y and my<tb.y+tb.h then
                cursor =(mx - tb.x)\8+tb.posX
                if cursor > len(tb.t) then
                    cursor = len(tb.t)
                end if
                while mb=1
                    getmouse mx,my,,mb
                wend
            end if
            drawTextBox(tb)  ' to show cursor at new position
        end if
       
        key = inkey
        if key<>"" then
            if len(key)>1 then
                ascKey = asc(right(key,1))
                if ascKey = 75 then  'CURSOR LEFT
                    if cursor > 0 then
                        cursor = cursor -1
                    end if
                end if
                if ascKey = 77 then  'CURSOR RIGHT
                    if cursor < len(tb.t) then
                        cursor = cursor + 1
                    end if
                end if
                if ascKey = 83 then 'DELETE
                    tb.t = left(tb.t,cursor) + right(tb.t,len(tb.t)-cursor-1)
                end if
            else
                ascKey = asc(key)
                if ascKey =8 then
                    if cursor > 0 then  'BACKSPACE
                        tb.t = left(tb.t,cursor-1) + mid(tb.t,cursor+1,len(tb.t)-cursor)
                        cursor = cursor - 1
                    end if
                else
                    if ascKey<>9 and ascKey<>27 and ascKey<>13 then  'TAB, ESC, ENTER
                        tb.t = left(tb.t,cursor) + key + right(tb.t,len(tb.t)-cursor)
                        cursor = cursor + 1
                    end if
                end if
            end if
            drawTextBox(tb)
        end if
        sleep 2
   
    loop until asc(key)=13 or ascKey=9 or ascKey=27 or mb=1 'ENTER, TAB, ESC

    tb.a = 0 'deactivate
    drawTextBox(tb)  'this draws contents without cursor
   
    return ascKey
   
end function


'=====================================================================
'===============    USE OF TEXTBOX EXAMPLE    ========================
'=====================================================================

'global variables
dim shared as integer event     'flag button down on a text box
dim shared as integer retKey    'exit char returned from editTextBox
dim shared as integer col,row   'column and row of current textbox being edited

dim shared as textBox tb1(5,20)  'create a 3 x 20 textbox array
'initialize 5 x 20 array of text boxes
for j as integer = 0 to 19
    for i as integer = 0 to 4
        tb1(i,j).x = i*100+50         
        tb1(i,j).y = j*18+50
        tb1(i,j).w = 11*8   'wide enough for 10 characters 8 pixels wide
        tb1(i,j).h = 16
        tb1(i,j).t = ""
        tb1(i,j).a = 0     
        tb1(i,j).j = 0
    next i
next j


sub update()
    screenlock
    cls
    for j as integer = 0 to 19
        for i as integer = 0 to 4
            drawTextBox(tb1(i,j))
        next i
    next j
    draw string (50,10),"USE MOUSE KEY TO SELECT A TEXT BOX"
    draw string (50,20),"HIT ESC KEY TO EXIT PROGRAM"
   
    'display content of current textbox
    line (0,460)-(639,479),rgb(100,100,200),bf
    draw string (20,479-12),tb1(col,row).t

    screenunlock
end sub

'     --- MAIN ---

update()  'show the text boxes to start with

do
    getmouse mx,my,,mb

    'test if left mouse button down event over a text box
    event = -1
    for j as integer = 0 to 19
        for i as integer = 0 to 4
            if mb=1 and mx>tb1(i,j).x and mx<tb1(i,j).x+tb1(i,j).w and my>tb1(i,j).y and my<tb1(i,j).y+tb1(i,j).h then
                col = i
                row = j
                event = 1
            end if
        next i
    next j
   
    'mouse down on text box event?
    if event = 1 then
        update()
        retKey = editTextBox(tb1(col,row))
        update()
    end if

    sleep 2
   
loop until multikey(&H01)
 
Last edited by BasicCoder2 on Aug 09, 2015 2:21, edited 1 time in total.
sancho2
Posts: 547
Joined: May 17, 2015 6:41

Re: retro text box array

Postby sancho2 » Jul 27, 2015 5:25

Thanks for the code demo, BasicCoder2.
I had been preparing to code exactly this. I logged on today and saw your code and I tried it out. I didn't fully read your comments however and I went ahead and built a class of it. Now I see that you might be doing that as well.

My version is incomplete. Two things I am going to add are a flexible border, and replacement of graphics under the textbox after the user is done with it. I did add a simple textbox erasing routine (TextBoxCLS) that clears the textbox from screen.
I also added a collection class to deal with the array of textboxes. It is incomplete but enough to demonstrate the textbox class.

Here is my version of your retro textbox as a class.

Code: Select all

' ---------------------------------------------------------------------------------------
' TextBox as a Class.
'    This is BasicCoder2's retro textbox implemented as a class.
' ---------------------------------------------------------------------------------------
ScreenRes 640,480,32
color rgb(0,0,0),rgb(200,200,240):cls

dim shared as integer mx,my,mb

type TextBox
   Public:
      As integer x
      As integer y
      As integer w
      As integer h
      As string  t    'text
      As integer a    'active=1
      As integer j    'justify left = 0 /center = 1/right = 2
      As integer posX 'retains info when text larger than width of text box
      Declare Constructor (x As Integer, y As Integer, w As Integer, h As Integer, _
               t As String, a As Integer, j As Integer)
      Declare Sub DrawTextBox(cursor As Integer = 0)
      Declare Function EditTextBox() As Integer
      Declare Sub EraseTextBox()
      Declare Function HitTest(x As Integer, y As Integer) As Integer
      Declare Sub TextBoxCLS()
end Type

Type TextBoxCollection
   Public:
      activeTB As TextBox Ptr
      count As Integer
      txb(Any) As TextBox Ptr
      Declare function AddTextBox(tbp As TextBox) As Integer
      Declare Function HitTest(x As Integer, y As Integer) As Integer
      Declare Sub ShowAll()
      Declare Destructor
End Type
Constructor TextBox(x As Integer, y As Integer, w As Integer, h As Integer, _
              t As String, a As Integer, j As Integer)
   '
   this.x = x
   this.y = y
   this.w = w
   this.h = h
   this.t = t
   this.a = a
   this.j = j
End Constructor
              
Function TextBox.EditTextBox() As Integer
   ' This function is mostly left the way BasicCoder2 posted it.
   ' The only real changes made were to bind it to an instance
   ' Instead of using global variable cursor, I use it procedure (method) level
   ' and pass it to the DrawTextBox() method
        dim as string key
        dim as Integer ascKey, cursor

        cursor =(mx - this.x)\8+this.posX
        if cursor > len(this.t) then
            cursor = len(this.t)
        end if
        while mb=1
            GetMouse mx,my,,mb ' [edit] the mx my and mb variables should be changed to local vars
        wend

        this.a = 1 'activate
        while inkey<>"":wend  'clear buffer
        this.drawTextBox(cursor) 'show cursor (code edited for class instance)
        do
            getmouse mx,my,,mb
            if mb=1 then
                if  mx>this.x and mx<this.x+this.w and my>this.y and my<this.y+this.h then
                    cursor =(mx - this.x)\8+this.posX
                    if cursor > len(this.t) then
                        cursor = len(this.t)
                    end if
                    while mb=1
                        getmouse mx,my,,mb
                    wend
                end if
                this.drawTextBox(cursor)  ' to show cursor at new position (code edited for class instance)
            end if
           
            key = inkey
            if key<>"" then
                if len(key)>1 then
                    ascKey = asc(right(key,1))
                    if ascKey = 75 then  'CURSOR LEFT
                        if cursor > 0 then
                            cursor = cursor -1
                        end if
                    end if
                    if ascKey = 77 then  'CURSOR RIGHT
                        if cursor < len(this.t) then
                            cursor = cursor + 1
                        end if
                    end if
                    if ascKey = 83 then 'DELETE
                        this.t = left(this.t,cursor) + right(this.t,len(this.t)-cursor-1)
                    end if
                else
                    ascKey = asc(key)
                    if ascKey =8 then
                        if cursor > 0 then  'BACKSPACE
                            this.t = left(this.t,cursor-1) + mid(this.t,cursor+1,len(this.t)-cursor)
                            cursor = cursor - 1
                        end if
                    else
                        if ascKey<>9 and ascKey<>27 and ascKey<>13 then  'TAB, ESC, ENTER
                            this.t = left(this.t,cursor) + key + right(this.t,len(this.t)-cursor)
                            cursor = cursor + 1
                        end if
                    end if
                end if
                this.drawTextBox(cursor)   ' code edited for class instance
            end if
            sleep 2
       
        loop until asc(key)=13 or ascKey=9 or ascKey=27 or mb=1 'ENTER, TAB, ESC

        this.a = 0 'deactivate
        this.drawTextBox()  'this draws contents without cursor (code edited for class instance)
       
        return ascKey
       
End Function
Sub TextBox.DrawTextBox(cursor As Integer = 0)
   ' BasicCoder2 original procedure with only variables changed to reflect class instance
   ' There is a parameter added as well
        dim As integer shiftX
        ScreenLock()
       
        '*******************  THIS DRAWS THE BOX ***************************
        'clear rectangle
        line (this.x,this.y)-(this.x+this.w+8,this.y+this.h),rgb(255,255,255),bf
        'draw border
        line (this.x,this.y)-(this.x+this.w+8,this.y+this.h),rgb(127,127,127),b
        line (this.x+1,this.y+1)-(this.x+this.w+7,this.y+this.h-1),rgb(127,127,127),b
        '*******************************************************************
        if this.a = 1 then
            'keep cursor within the box
            if cursor > this.posX+this.w/8 then
                this.posX = this.posX + 1
            end if
       
            if cursor < this.posX+1 then
                if this.posX>0 then
                    this.posX = this.posX - 1
                end if
            end if
        end if

        if len(this.t)*8 > this.w or this.j = 0 then
            draw string (this.x+4,this.y+4),mid(this.t,this.posX+1,this.w\8)   
            if this.a = 1 then
                line (this.x+cursor*8+4-this.posX*8,this.y+2)-(this.x+cursor*8+5-this.posX*8,this.y + 14),rgb(10,10,10),bf  'draw cursor
            end if
        else
            if this.j = 1 then shiftX = this.w\2-len(this.t)*8/2
            if this.j = 2 then shiftX = this.w-len(this.t)*8
            draw string (this.x+4+shiftX,this.y+4),this.t
            if this.a = 1 then
                line (this.x+cursor*8+4+shiftX,this.y+2)-(this.x+cursor*8+5+shiftX,this.y + 14),rgb(10,10,10),bf  'draw cursor
            end if
        end if
        ScreenUnLock()
        sleep 2
   
End Sub
Sub TextBox.TextBoxCLS()
   ' This sub reduces the view to the dimensions of the text box then cls
   ' It then returns the view to the entire screen
   View (this.x, this.y)- (this.x + this.w + 8, this.y + this.h -1)
   Cls
   View
End Sub
Function TextBox.HitTest(x As Integer, y As Integer) As Integer
   ' return 1 if x,y is within the bounds of this textbox
   If x < this.x OrElse x > this.x + this.w - 1 Then
      Return 0
   EndIf
   If y < this.y OrElse y > this.y + this.h - 1 Then
      Return 0
   EndIf

   Return 1

End Function
Sub TextBoxCollection.ShowAll()
   ' Draw all the textboxes in the collection to the screen
   Dim n As Integer
   For n = 1 To this.count
      this.txb(n)->DrawTextBox()
   Next
End Sub
Function TextBoxCollection.HitTest(x As Integer, y As Integer) As Integer
   ' If xy is within a collected textbox, return the index of that textbox
   ' Otherwise return 0
   Dim As Integer n, ret
   
   ret = 0
   For n = 1 To this.count
      If this.txb(n)->HitTest(x, y) > 0 Then
         ret = n
         Exit For
      EndIf
   Next
   
   Return ret
End Function
Function TextBoxCollection.AddTextBox(tbp As TextBox) As Integer
   ' Add a textbox to the collection
   ' return the number of textboxes
   Dim As Integer n
   
   n = this.count + 1

   If n > UBound(this.txb) Then
      ReDim Preserve this.txb(1 To n)
   EndIf
   
   this.txb(n) = Callocate(SizeOf(TextBox))

   *this.txb(n) = tbp
   this.count += 1 
   
   Return this.count
End Function
Destructor TextBoxCollection
   ' FB errors on deallocate if there is no valid pointer.
   ' During testing and debugging I had lots of problems
   ' with this routine so I enclosed it in ab #if block.
   ' Its probably unnecessary.
   Dim As Integer n

   For n = 1 To this.count
      #If TypeOf(this.txb(n)) = TypeOf(TextBox Ptr)
         DeAllocate(this.txb(n))
      #EndIf
   Next
   
   this.count = 0
   
End Destructor

'The rest of code here is the main block. This is modified to reflect that
' TextBox is now a class
    '=====================================================================
    '===============    USE OF TEXTBOX EXAMPLE    ========================
    '=====================================================================

    Dim Shared As TextBoxCollection myTxts
   Dim n As Integer
   Dim tb As TextBox Ptr
    
    ' I kept this loop using rows and columns so I don't have to figure out spacing
    for j As integer = 0 to 19
        for i as integer = 0 to 4
           n += 1
           tb = New TextBox(i * 100 + 50, j * 18 + 50, 11 * 8, 16, "Text" & Str(n),0,0)
           myTxts.AddTextBox(*tb)
           Delete tb
        Next
    Next


   sub update()
      ' It doesn't seem necessary to redraw the textboxes. In testing
      ' I couldn't break the display. So I removed the redrawing
      ' of the textboxes.
      ' We could remove Update entirely. I left it here to display edited
      ' text from textbox the way BasicCoder2 had it.
      
      ScreenLock

      'myTxts.activeTB->DrawTextBox()      ' I don't think this is needed

      Draw string (50,10),"USE MOUSE KEY TO SELECT A TEXT BOX"
      Draw string (50,20),"HIT ESC KEY TO EXIT PROGRAM"
      '
      'display content of current textbox
      Line (0,460)-(639,479),rgb(100,100,200),bf
      Draw string (20,479-12),myTxts.activeTB->t
      '
      ScreenUnLock
   end sub


    '     --- MAIN ---
   myTxts.ShowAll()
   
   ' we need an active textbox or update fails
   myTxts.activeTB = myTxts.txb(1)
   
   update() 

   Dim tbn As Integer

    do
        getmouse mx,my,,mb

        If mb = 1 Then
           ' if the left mouse button is down then we check in the
           ' textbox collection to see if the mouse is over a
           ' member
           tbn = myTxts.HitTest(mx, my)
           If tbn > 0 Then
              myTxts.activeTB = myTxts.txb(tbn)
              myTxts.activeTB->EditTextBox()
              update()      
           EndIf
           
        EndIf
       
        sleep 2
       
    loop until multikey(&H01)

' this is only here to show how the TextBoxCLS() method works
myTxts.txb(3)->TextBoxCLS()
Sleep
myTxts.Destructor()
Sleep

[edit]
I just want to sneak a comment in here that the method TextBox.EditTextBos() should use local variables for GetMouse() as opposed to what I have.
Last edited by sancho2 on Jul 29, 2015 1:55, edited 1 time in total.
BasicCoder2
Posts: 3566
Joined: Jan 01, 2009 7:03
Location: Australia

Re: retro text box array

Postby BasicCoder2 » Jul 27, 2015 6:24

Interesting to see a class version although I don't really know enough to follow the code.
Unfortunately it looks like a I can't run it until I update my FreeBasic compiler.
All I get is a big list of compiler errors...
fbc: FreeBASIC Compiler - Version 0.24.0 (07-30-2012) for win32

I worry about updating as then maybe my current code will fail to compile.
grindstone
Posts: 737
Joined: May 05, 2015 5:35
Location: Germany

Re: retro text box array

Postby grindstone » Jul 27, 2015 8:27

BasicCoder2 wrote:I worry about updating as then maybe my current code will fail to compile.
Why don't you install multiple versions of the compiler. If one version fails, just try another. The only thing you have to do then is changing the compiler path at "Options > Path Options > Compiler Path".

Regards
grindstone
Tourist Trap
Posts: 2880
Joined: Jun 02, 2015 16:24

Re: retro text box array

Postby Tourist Trap » Jul 27, 2015 15:18

BasicCoder2 wrote:All I get is a big list of compiler errors...
fbc: FreeBASIC Compiler - Version 0.24.0 (07-30-2012) for win32
I worry about updating as then maybe my current code will fail to compile.

BasicCoder2 please please get the last update, it works fine. You won't be able to start any object programing below 0.90 fbc version.

However, as Grindstone has pointed out you can have many versions living side by side. If you don't want to bother changing compiler path in the menu of your IDE, just do like me, have many copies of your IDE and get each one dedicated to one version. I have even set each time a different background color, say ligth green for fbc 0.24, ligth blue for 1.03 and so on, which proves to be very convenient.

sancho2 wrote:My version is incomplete.

Nice reshape anyway.
dodicat
Posts: 6557
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: retro text box array

Postby dodicat » Jul 27, 2015 16:21

Yea, OK Basiccoder2, nice job.
---
I keep a special copy of fb24 on my desktop, just so I can check quickly if Basiccoder is able to run it.
(Sometimes I forget, but not often)
I also mainly use screen 19 because I believe that basiccoder2's laptop is set to this (maybe silghtly bigger, but not much)

But this is Win XP, I believe that Win *8 and the likes have some folders private and it is more difficult to save compiler copies or fbide copies on the desktop.
Otherwise why on Earth would Basiccoder2 stay with FB24??
BasicCoder2
Posts: 3566
Joined: Jan 01, 2009 7:03
Location: Australia

Re: retro text box array

Postby BasicCoder2 » Jul 27, 2015 20:48

@dodicat,
Wow you go to all that trouble just so I can run one of your program snippets!
Actually I never use Screen or the old QBASIC palette numbers instead I like Screenres and 32 bit color with rgb(r,g,b).

The full screen resolution of the current laptop is 1366 x 768
The biggest window I would use is 1340 x 620 otherwise the bottom part vanishes beneath the task bar.

The reason I stay with FB24 is because my programs run fine and I fear I will mess things up if I update. Although I understand the basics of using a class in c++ it is unlikely I will ever bother learning to do likewise with the FreeBASIC's class implementation which is all an update would offer.

I also have a Window8 laptop with a touch screen and had thought of installing the latest FreeBASIC on that machine to run other people's FreeBASIC code. It is my fear that more than one version of FreeBASIC on the same computer will clash. I start FBIDE from the task bar so which version will fire up? There are many details about file paths and so on I have never really got my head around so I play it safe.

The bottom line is I am an old time QBASIC, Assembler, C self taught hobbyist programmer who essentially writes very simple program snippets not actual working functional full blown useful programs. I run all my FreeBasic program snippets from the FBIDE as I did with the QBASIC IDE, usually as a 640 x 480 window.

With regards to the second laptop I have no internet protection apart from window's defender is that sufficient? I tried to install Kaspersky but they insisted on having my phone number.

.
MrSwiss
Posts: 3542
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: retro text box array

Postby MrSwiss » Jul 27, 2015 22:58

BasicCoder2 wrote:The reason I stay with FB24 is because my programs run fine and I fear I will mess things up if I update. Although I understand the basics of using a class in c++ it is unlikely I will ever bother learning to do likewise with the FreeBASIC's class implementation which is all an update would offer.

I also have a Window8 laptop with a touch screen and had thought of installing the latest FreeBASIC on that machine to run other people's FreeBASIC code. It is my fear that more than one version of FreeBASIC on the same computer will clash. I start FBIDE from the task bar so which version will fire up? There are many details about file paths and so on I have never really got my head around so I play it safe.

There is absolutely no problem on having "incountable" numbers of FBC's on the same machine:
I have: 0.24.0, 0.90.1, 1.02.1, 1.03.0 (all 32bit, WIN) and
1.02.1, 1.03.0 (both 64bit, WIN) and
1.04.0 Dev. Ver. (32/64bit WIN), more probably later ...
FBEdit 1x directed at the current production compiler 32bit.
But compiling on the CLI with various other Versions ... no problem at all.
You just have to have a structure which is easy to follow like:
    (drive):\FreeBASIC\FB0240\
    (drive):\FreeBASIC\FB0901\
    (drive):\FreeBASIC\FB1021\32bit
    (drive):\FreeBASIC\FB1021\64bit
    (drive):\FreeBASIC\FB1030\32bit
    (drive):\FreeBASIC\FB1030\64bit
    (drive):\FreeBASIC\FB-DEV\32bit
    (drive):\FreeBASIC\FB-DEV\64bit
Since the version of the DEV version changes with every release, NO ver. info there.
dodicat
Posts: 6557
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: retro text box array

Postby dodicat » Jul 27, 2015 23:14

Of course you need to download the .zip file, not a self installing .exe.

I use Winrar to expand.
I put the .zip into the folder I choose, right click the .zip and use the extract here option.
MrSwiss
Posts: 3542
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: retro text box array

Postby MrSwiss » Jul 27, 2015 23:41

dodicat wrote:Of course you need to download the .zip file, not a self installing .exe.

A very good addition, forgot to mention that.

I usually download the .7z Version and expand with 7Zip ...

Return to “Tips and Tricks”

Who is online

Users browsing this forum: No registered users and 5 guests