PlanetSquires Forums

Support Forums => Other Software and Code => Topic started by: Robert Rioja on November 11, 2009, 09:43:52 AM

Title: screen resolution
Post by: Robert Rioja on November 11, 2009, 09:43:52 AM
Does anyone know how to determine screen resolution?  I want to display two dialogs next to each other so that one covers 30% of the screen, and the other covers the rest of the screen.
Thanks.
Title: Re: screen resolution
Post by: Paul Squires on November 11, 2009, 09:50:38 AM
This is code directly from the FF3 source code itself.


    ' Do a check to see what the display resolution is and
    ' what the number of colors are. Display a warning
    ' message if less tha 800x600 and 256 colors.

    nScreenWidth  = GetSystemMetrics(%SM_CXSCREEN )
    nScreenHeight = GetSystemMetrics(%SM_CYSCREEN)

    hDC    = GetDC( GetDesktopWindow )
    nColor = GetDeviceCaps(hDC, %BITSPIXEL)
    ReleaseDC GetDesktopWindow, hDC

    Select Case nColor
      Case 0 To 7:    nColorDepth = "less than 256 colors"
      Case 8:         nColorDepth = "256 colors"
      Case 16:        nColorDepth = "High Color (16 bit)"
      Case 32:        nColorDepth = "True Color (32 bit)"
    End Select

    If nScreenWidth < 800 And nScreenHeight < 600 Then
       nmsg1 = "The current display resolution is set at " & Format$(nScreenWidth) & _
               "x" & Format$(nScreenHeight) & "." & $CrLf & _
               $APPNAME & " works best on resolutions of at least 800x600."
    End If
    If nColor < 8 Then
       nmsg2 = "The current display color depth is set to " & nColorDepth & "." & $CrLf & _
               $APPNAME & " works best with color depths of at least 256 colors."
    End If
    If Len(nmsg1) Or Len(nmsg2) Then
       MsgBox nmsg1 & $CrLf & $CrLf & nmsg2, %MB_ICONINFORMATION, "Display Resolution Warning"
    End If

Title: Re: screen resolution
Post by: Haakon Birkeland on November 11, 2009, 09:58:01 AM
Darn, you beat me to it. 8o)
Title: Re: screen resolution
Post by: Robert Rioja on November 13, 2009, 12:49:16 PM
I just realized that PB has a Desktop Get Client command to do it.
Thanks anyway.

Title: Re: screen resolution
Post by: Haakon Birkeland on November 13, 2009, 12:56:11 PM
Beware that CLIENT only gets the desktop minus any space taken by "systray". Use DESKTOP GET SIZE to always retrive the complete desktop / screen resolution.
Title: Re: screen resolution
Post by: Paul Squires on November 13, 2009, 02:24:27 PM
PB may have a built in statement but by using the Win api we have access to so much more. Just by remembering to use GetSystemMetrics you can call up information on just about anything related to your system. It pays to learn bits and pieces of the api and to retain that knowledge rather than try to rely on PB statements or FF Functions.

Actually, a nice exercise would be to take PB's builtin statements and show the corresponding WinAPI statements. We'd probably be surprised at how easy it would be to use the winapi. You would also remove one level of indirection (bypassing the PB Function which probably calls the WinAPI anyway).


Value Meaning
SM_ARRANGE Flags specifying how the system arranged minimized windows. For more information about minimized windows, see the following Remarks section.
SM_CLEANBOOT Value that specifies how the system was started:0  Normal boot1  Fail-safe boot2  Fail-safe with network bootFail-safe boot (also called SafeBoot) bypasses the user's startup files.
SM_CMOUSEBUTTONS Number of buttons on mouse, or zero if no mouse is installed.
SM_CXBORDER,
SM_CYBORDER The width and height, in pixels, of a window border. This is equivalent to the SM_CXEDGE value for windows with the 3-D look.
SM_CXCURSOR,
SM_CYCURSOR Width and height, in pixels, of a cursor. These are the cursor dimensions supported by the current display driver. The system cannot create cursors of other sizes.
SM_CXDLGFRAME,
SM_CYDLGFRAME Same as SM_CXFIXEDFRAME and SM_CYFIXEDFRAME.
SM_CXDOUBLECLK,
SM_CYDOUBLECLK Width and height, in pixels, of the rectangle around the location of a first click in a double-click sequence. The second click must occur within this rectangle for the system to consider the two clicks a double-click. (The two clicks must also occur within a specified time.)
SM_CXDRAG,
SM_CYDRAG Width and height, in pixels, of a rectangle centered on a drag point to allow for limited movement of the mouse pointer before a drag operation begins. This allows the user to click and release the mouse button easily without unintentionally starting a drag operation.
SM_CXEDGE,
SM_CYEDGE Dimensions, in pixels, of a 3-D border. These are the 3-D counterparts of SM_CXBORDER and SM_CYBORDER.
SM_CXFIXEDFRAME,
SM_CYFIXEDFRAME Thickness, in pixels, of the frame around the perimeter of a window that has a caption but is not sizable. SM_CXFIXEDFRAME is the width of the horizontal border and SM_CYFIXEDFRAME is the height of the vertical border. Same as SM_CXDLGFRAME and SM_CYDLGFRAME.
SM_CXFRAME,
SM_CYFRAME Same as SM_CXSIZEFRAME and SM_CYSIZEFRAME.
SM_CXFULLSCREEN, SM_CYFULLSCREEN Width and height of the client area for a full-screen window. To get the coordinates of the portion of the screen not obscured by the tray, call the SystemParametersInfo function with the SPI_GETWORKAREA value.
SM_CXHSCROLL, SM_CYHSCROLL Width, in pixels, of the arrow bitmap on a horizontal scroll bar; and height, in pixels, of a horizontal scroll bar.
SM_CXHTHUMB Width, in pixels, of the thumb box in a horizontal scroll bar.
SM_CXICON,
SM_CYICON The default width and height, in pixels, of an icon. These values are typically 32x32, but can vary depending on the installed display hardware.The LoadIcon function can only load icons of these dimensions.
SM_CXICONSPACING, SM_CYICONSPACING Dimensions, in pixels, of a grid cell for items in large icon view. Each item fits into a rectangle of this size when arranged. These values are always greater than or equal to SM_CXICON and SM_CYICON.
SM_CXMAXIMIZED,
SM_CYMAXIMIZED Default dimensions, in pixels, of a maximized top-level window.
SM_CXMAXTRACK,
SM_CYMAXTRACK Default maximum dimensions, in pixels, of a window that has a caption and sizing borders. The user cannot drag the window frame to a size larger than these dimensions. A window can override these values by processing the WM_GETMINMAXINFO message.
SM_CXMENUCHECK,
SM_CYMENUCHECK Dimensions, in pixels, of the default menu check-mark bitmap.
SM_CXMENUSIZE,
SM_CYMENUSIZE Dimensions, in pixels, of menu bar buttons, such as multiple document (MIDI) child close.
SM_CXMIN,
SM_CYMIN Minimum width and height, in pixels, of a window.
SM_CXMINIMIZED,
SM_CYMINIMIZED Dimensions, in pixels, of a normal minimized window.
SM_CXMINSPACING
SM_CYMINSPACING Dimensions, in pixels, of a grid cell for minimized windows. Each minimized window fits into a rectangle this size when arranged. These values are always greater than or equal to SM_CXMINIMIZED and SM_CYMINIMIZED.
SM_CXMINTRACK, SM_CYMINTRACK Minimum tracking width and height, in pixels, of a window. The user cannot drag the window frame to a size smaller than these dimensions. A window can override these values by processing the WM_GETMINMAXINFO message.
SM_CXSCREEN,
SM_CYSCREEN Width and height, in pixels, of the screen.
SM_CXSIZE,
SM_CYSIZE Width and height, in pixels, of a button in a window's caption or title bar.
SM_CXSIZEFRAME,
SM_CYSIZEFRAME Thickness, in pixels, of the sizing border around the perimeter of a window that can be resized. SM_CXSIZEFRAME is the width of the horizontal border and SM_CYSIZEFRAME is the height of the vertical border. Same as SM_CXFRAME and SM_CYFRAME.
SM_CXSMICON,
SM_CYSMICON Recommended dimensions, in pixels, of a small icon. Small icons typically appear in window captions and in small icon view.
SM_CXSMSIZE
SM_CYSMSIZE Dimensions, in pixels, of small caption buttons.
SM_CXVSCROLL, SM_CYVSCROLL Width, in pixels, of a vertical scroll bar; and height, in pixels, of the arrow bitmap on a vertical scroll bar.
SM_CYCAPTION Height, in pixels, of normal caption area.
SM_CYKANJIWINDOW For double-byte character set versions of Windows, height, in pixels, of the Kanji window at the bottom of the screen.
SM_CYMENU Height, in pixels, of single-line menu bar.
SM_CYSMCAPTION Height, in pixels, of a small caption.
SM_CYVTHUMB Height , in pixels, of the thumb box in a vertical scroll bar.
SM_DBCSENABLED TRUE or nonzero if the double-byte character set (DBCS) version of USER.EXE is installed; FALSE, or zero otherwise.
SM_DEBUG TRUE or nonzero if the debugging version of USER.EXE is installed; FALSE, or zero, otherwise.
SM_MENUDROPALIGNMENT TRUE, or nonzero if drop-down menus are right-aligned relative to the corresponding menu-bar item; FALSE, or zero if they are left-aligned.
SM_MIDEASTENABLED TRUE if the system is enabled for Hebrew/Arabic languages.
SM_MOUSEPRESENT TRUE or nonzero if a mouse is installed; FALSE, or zero, otherwise.
SM_MOUSEWHEELPRESENT Windows NT only: TRUE or nonzero if a mouse with a wheel is installed; FALSE, or zero, otherwise.
SM_NETWORK The least significant bit is set if a network is present; otherwise, it is cleared. The other bits are reserved for future use.
SM_PENWINDOWS TRUE or nonzero if the Microsoft Windows for Pen computing extensions are installed; zero, or FALSE, otherwise.
SM_SECURE TRUE if security is present, FALSE otherwise.
SM_SHOWSOUNDS TRUE or nonzero if the user requires an application to present information visually in situations where it would otherwise present the information only in audible form; FALSE, or zero, otherwise.
SM_SLOWMACHINE TRUE if the computer has a low-end (slow) processor, FALSE otherwise.
SM_SWAPBUTTON TRUE or nonzero if the meanings of the left and right mouse buttons are swapped; FALSE, or zero, otherwise.


Title: Re: screen resolution
Post by: Patrice Terrier on November 14, 2009, 06:29:17 PM
You should also take care of multi-monitor, and be aware that x, y screen could be negative, thus do not use LOWORD/HIWORD to split the lParam, but LO(INTEGER, and HI(INTEGER ...

...
Title: Re: screen resolution
Post by: Haakon Birkeland on November 14, 2009, 06:52:45 PM
Any flag or function available for checking if a multi-monitor setup is active? I know some applications have a tendency to center dialogs in the middle of the two screen areas, which is rather annoying, while others seem to act as if there would only be one monitor.

Some code I have been playing around with lately reports the resolution of one of the monitors only (1280x1024), but the code tracking the cursor position goes beyond the 1280px and into the secondary monitor until the pixel count is as expected â€" doubled.

And by negative x and Y calues, are you thinking of the cursor position in regards to the second monitor placement adjacent to the primary one? Or am I totally off here ...?
Title: Re: screen resolution
Post by: Patrice Terrier on November 15, 2009, 05:06:29 AM
Yes, if the second monitor is on the left of the main one, then the X coordinate is negative.


''// 4.11
''-----------------------------------------------------------------------------------
'' BEGIN ROUTINES FOR WORKING WITH MULTIPLE MONITORS
''-----------------------------------------------------------------------------------
TYPE MONITORINFO
    cbSize AS DWORD
    rcMonitor AS RECT
    rcWork AS RECT
    dwFlags AS DWORD
END TYPE
'
'%MONITOR_DEFAULTTONULL    = &H00000000
'%MONITOR_DEFAULTTOPRIMARY = &H00000001
%MONITOR_DEFAULTTONEAREST = &H00000002
DECLARE FUNCTION MonitorFromWindow LIB "USER32.DLL" ALIAS "MonitorFromWindow" (BYVAL hwnd AS DWORD, BYVAL dwFlags AS DWORD) AS DWORD
'DECLARE FUNCTION EnumDisplayMonitors LIB "USER32.DLL" ALIAS "EnumDisplayMonitors" (BYVAL hdc AS DWORD, lprcClip AS RECT, lpfnEnum AS DWORD, BYVAL dwData AS LONG) AS LONG
'DECLARE FUNCTION HeapAlloc LIB "KERNEL32.DLL" ALIAS "HeapAlloc" (BYVAL hHeap AS DWORD, BYVAL dwFlags AS DWORD, BYVAL dwBytes AS DWORD) AS DWORD
'DECLARE FUNCTION HeapFree LIB "KERNEL32.DLL" ALIAS "HeapFree" (BYVAL hHeap AS DWORD, BYVAL dwFlags AS DWORD, BYVAL lpMem AS DWORD) AS LONG
'DECLARE FUNCTION GetProcessHeap LIB "KERNEL32.DLL" ALIAS "GetProcessHeap" () AS LONG
'DECLARE FUNCTION SystemParametersInfo LIB "USER32.DLL" ALIAS "SystemParametersInfoA" (BYVAL uAction AS DWORD, BYVAL uParam AS DWORD, lpvParam AS ANY, BYVAL fuWinIni AS DWORD) AS LONG
DECLARE FUNCTION GetMonitorInfo LIB "USER32.DLL" ALIAS "GetMonitorInfoA" (BYVAL hMonitor AS DWORD, lpmi AS MONITORINFO) AS LONG
'%MONITOR_AREA       = &H0000??          ' use monitor entire area
'%MONITOR_WORKAREA   = &H0002??          ' use monitor work area
'
'%MONITOR_SCREEN     = &H0008??          ' screen coordinates
'%MONITOR_VIRTUAL    = &H0010??          ' virtual coordinates
'
'TYPE MONITORDATA
'  hMonitor    AS DWORD
'  trcWork     AS RECT
'  trcMonitor  AS RECT
'  fPrimary    AS LONG
'END TYPE
'
'TYPE MONITORS
'  cMonitors   AS LONG
'  xMap        AS INTEGER
'  yMap        AS INTEGER
'  ptmd        AS MONITORDATA PTR
'END TYPE
'
'GLOBAL gtmonitors             AS MONITORS
'
'FUNCTION CopyData (BYVAL lpSrc AS LONG, BYVAL lpDest  AS LONG, BYVAL cblCopy AS LONG) AS LONG
'    ! cld                             ; read/write forward
'    ! mov ecx, cblCopy                ; put count of bytes to copy in counter
'    ! mov esi, lpSrc                  ; put source address in source index
'    ! mov edi, lpDest                 ; put destination address in destination index
'    ! rep movsb                       ; repeat move byte from esi to edi until ecx is zero
'END FUNCTION
'
'' Dual-monitor system example.
'' (The number of monitors is immaterial)
'' scr  = screen coordinates
'' virt = virtual coordinates
''
''  scr(-1280,0) virt(0,0)    scr(0,0) virt(1280,0)    scr(1920,0) virt(3200,0)
''            |                        |                        |
''            +------------------------+------------------------+
''            |  /|\                   |  /|\                   |
''            |   |                    |   |                    |
''            |   |                    |   |                    |
''            |<---------1280--------->|<---------1920--------->|
''            |   |                    |   |                    |
''            |   |                    |   |                    |
''            | 1024                   | 1200    primary        |
''            |   |                    |   |                    |
''            |   |                    |   |                    |
''            |   |                    |   |                    |
''            +------------------------|   |                    |
''            |                        |  \|/                   |
''            +------------------------+------------------------+
''                                                  scr(1920,1200) virt(3200,1200)
'
'
' PROCEDURE: HeapSortMonitors
' PURPOSE:   Sorts a monitor table in ascending order using HeapSort.
'            The entries are sorted on the x or y coordinate of the
'            upper left corner of the trcWork member.
'            The table MUST be zero-based.
'SUB HeapSortMonitors ( _
'  BYVAL ptmd  AS MONITORDATA PTR, _
'  BYVAL N     AS LONG, _
'  BYVAL fVert AS LONG _
'  )
'
'  LOCAL tmd   AS MONITORDATA
'  LOCAL I     AS LONG
'  LOCAL J     AS LONG
'  LOCAL L     AS LONG
'  LOCAL IR    AS LONG
'
'  IF N <= 1 THEN EXIT SUB
'
'  ' Skip heaps with size 1
'  L  = N \ 2
'  IR = N - 1
'
'  DO
'    IF L > 0 THEN
'      DECR L
'      CopyData VARPTR(@ptmd[L]), VARPTR(tmd), SIZEOF(tmd)
'    ELSE
'      CopyData VARPTR(@ptmd[IR]), VARPTR(tmd), SIZEOF(tmd)
'      CopyData VARPTR(@ptmd[0]), VARPTR(@ptmd[IR]), SIZEOF(tmd)
'      DECR IR
'      IF IR = 0 THEN
'        CopyData VARPTR(tmd), VARPTR(@ptmd[0]), SIZEOF(tmd)
'        EXIT SUB
'      END IF
'    END IF
'    I = L
'    J = L + L + 1
'
'    WHILE J <= IR
'      IF J < IR THEN
'        IF fVert THEN
'          IF @ptmd[J].trcWork.nTop < @ptmd[J + 1].trcWork.nTop THEN
'            INCR J
'          END IF
'        ELSE
'          IF @ptmd[J].trcWork.nLeft < @ptmd[J + 1].trcWork.nLeft THEN
'            INCR J
'          END IF
'        END IF
'      END IF
'      IF fVert THEN
'        IF tmd.trcWork.nTop < @ptmd[J].trcWork.nTop THEN
'          CopyData VARPTR(@ptmd[J]), VARPTR(@ptmd[i]), SIZEOF(tmd)
'          I = J
'          J = J + J + 1
'        ELSE
'          J = IR + 1
'        END IF
'      ELSE
'        IF tmd.trcWork.nLeft < @ptmd[J].trcWork.nLeft THEN
'          CopyData VARPTR(@ptmd[J]), VARPTR(@ptmd[i]), SIZEOF(tmd)
'          I = J
'          J = J + J + 1
'        ELSE
'          J = IR + 1
'        END IF
'      END IF
'    WEND
'
'    CopyData VARPTR(tmd), VARPTR(@ptmd[i]), SIZEOF(tmd)
'  LOOP
'
'END SUB
'
'FUNCTION MonitorCountEnumProc(BYVAL hMonitor AS DWORD, BYVAL hDC AS DWORD, trc AS RECT, BYVAL lParam AS LONG) AS LONG
'    LOCAL plCount AS LONG PTR
'    plCount = lParam
'    INCR @plCount
'    FUNCTION = -1
'END FUNCTION
'
'FUNCTION zGetDisplayMonitorCount() AS LONG
'    LOCAL cMonitors AS LONG
'    CALL EnumDisplayMonitors(0, BYVAL 0, BYVAL CODEPTR(MonitorCountEnumProc), VARPTR(cMonitors))
'    FUNCTION = cMonitors
'END FUNCTION
'
'FUNCTION MonitorEnumProc(BYVAL hMonitor AS DWORD, BYVAL hDC AS DWORD, trc AS RECT, BYVAL lParam AS LONG) AS LONG
'    LOCAL tmi       AS MONITORINFO
'    LOCAL nMonitor  AS LONG
'    tmi.cbSize = SIZEOF(tmi)
'    IF GetMonitorInfo(hMonitor, tmi) THEN
'       nMonitor = gtmonitors.cMonitors
'       gtmonitors.@ptmd[nMonitor].hMonitor   = hMonitor
'       gtmonitors.@ptmd[nMonitor].trcWork    = tmi.rcWork
'       gtmonitors.@ptmd[nMonitor].trcMonitor = tmi.rcMonitor
'       gtmonitors.@ptmd[nMonitor].fPrimary   = (tmi.dwFlags AND &H00000001) ' // %MONITORINFOF_PRIMARY = &H00000001
'       INCR gtmonitors.cMonitors
'       FUNCTION = -1
'    END IF
'END FUNCTION
'
'FUNCTION zGetDisplayMonitors() AS DWORD
'
'    LOCAL cMonitors     AS LONG
'    LOCAL nMonitor      AS LONG
'    LOCAL fNotSupported AS LONG
'    LOCAL lRet          AS LONG
'
'    IF gtmonitors.ptmd THEN
'       CALL HeapFree(GetProcessHeap(), 0, gtmonitors.ptmd)
'    END IF
'    cMonitors = zGetDisplayMonitorCount()
'
'    IF cMonitors = 0 THEN
'       fNotSupported = -1
'       cMonitors = 1
'    END IF
'
'    gtmonitors.ptmd = HeapAlloc(GetProcessHeap(), &H00000008, cMonitors * SIZEOF(gtmonitors.@ptmd))
'    IF gtmonitors.ptmd THEN
'       IF fNotSupported THEN
'          gtmonitors.cMonitors = 1
'          gtmonitors.@ptmd[0].hMonitor           = 0
'          gtmonitors.@ptmd[0].trcMonitor.nLeft   = 0
'          gtmonitors.@ptmd[0].trcMonitor.nTop    = 0
'          gtmonitors.@ptmd[0].trcMonitor.nRight  = GetSystemMetrics(0)
'          gtmonitors.@ptmd[0].trcMonitor.nBottom = GetSystemMetrics(1)
'          lRet = SystemParametersInfo(48, SIZEOF(gtmonitors.@ptmd[0].trcWork), BYVAL VARPTR(gtmonitors.@ptmd[0].trcWork), 0)
'          IF lRet = 0 THEN
'             gtmonitors.@ptmd[0].trcWork = gtmonitors.@ptmd[0].trcMonitor
'          END IF
'          gtmonitors.@ptmd[0].fPrimary = -1
'       ELSE
'          gtmonitors.cMonitors = 0
'          CALL EnumDisplayMonitors(0, BYVAL 0, BYVAL CODEPTR(MonitorEnumProc), 0)
'       END IF
'       nMonitor = 0
'
'       WHILE nMonitor < gtmonitors.cMonitors
'          IF gtmonitors.@ptmd[nMonitor].fPrimary THEN
'             ' To convert
'             ' xScreen  = xVirtual - xMap
'             ' xVirtual = xScreen + xMap
'             gtmonitors.xMap = gtmonitors.@ptmd[nMonitor].trcWork.nLeft
'             gtmonitors.yMap = gtmonitors.@ptmd[nMonitor].trcWork.nTop
'             EXIT LOOP
'          END IF
'          INCR nMonitor
'       WEND
'
'       FUNCTION = gtmonitors.ptmd
'
'    END IF
'
'END FUNCTION
'
'FUNCTION zGetMonitorRect (BYVAL hMonitor AS DWORD, BYVAL dwFlags AS DWORD, trc AS RECT) AS LONG
'
'    LOCAL lRet, nMonitor AS LONG
'
'    nMonitor = 0
'
'    WHILE nMonitor < gtmonitors.cMonitors
'      IF gtmonitors.@ptmd[nMonitor].hMonitor = hMonitor THEN
'         ' To convert
'         ' xScreen  = xVirtual - xMap
'         ' xVirtual = xScreen + xMap
'         IF (dwFlags AND %MONITOR_WORKAREA) THEN
'            trc.nLeft   = gtmonitors.@ptmd[nMonitor].trcWork.nLeft
'            trc.nTop    = gtmonitors.@ptmd[nMonitor].trcWork.nTop
'            trc.nRight  = gtmonitors.@ptmd[nMonitor].trcWork.nRight
'            trc.nBottom = gtmonitors.@ptmd[nMonitor].trcWork.nBottom
'         ELSE
'            trc.nLeft   = gtmonitors.@ptmd[nMonitor].trcMonitor.nLeft
'            trc.nTop    = gtmonitors.@ptmd[nMonitor].trcMonitor.nTop
'            trc.nRight  = gtmonitors.@ptmd[nMonitor].trcMonitor.nRight
'            trc.nBottom = gtmonitors.@ptmd[nMonitor].trcMonitor.nBottom
'         END IF
'         IF (dwFlags AND %MONITOR_SCREEN) THEN
'            trc.nLeft   = trc.nLeft   - gtmonitors.xMap
'            trc.nTop    = trc.nTop    - gtmonitors.yMap
'            trc.nRight  = trc.nRight  - gtmonitors.xMap
'            trc.nBottom = trc.nBottom - gtmonitors.yMap
'         END IF
'         lRet = -1
'         EXIT LOOP
'      END IF
'      INCR nMonitor
'    WEND
'
'    FUNCTION = lRet
'
'END FUNCTION
''-----------------------------------------------------------------------------------
'' END ROUTINES FOR WORKING WITH MULTIPLE MONITORS
''-----------------------------------------------------------------------------------

Title: Re: screen resolution
Post by: Haakon Birkeland on November 15, 2009, 02:16:38 PM
Thanks. I've had a little look so far, but found some of this a bit complicated so I just saved the rest in a text-file for when I would need it.