Help Search program?

Discussions about the Liberty BASIC language, with particular reference to LB Booster
sarmednafi
Posts: 36
Joined: Fri Apr 06, 2018 6:27 am

Help Search program?

Post by sarmednafi »

The help search program wrote by Stefan work fine under LB. But Under LBB need some modification to display the list item we clicking on the screen. Any hints or king will be welcome.

Thanks to all
Sarmed
guest
Site Admin
Posts: 192
Joined: Tue Apr 03, 2018 1:34 pm

Re: Help Search program?

Post by guest »

The program relies on an undocumented 'feature' of LB 4, which is why it does not work in LBB. The particular feature it is relying on is that, apparently, in LB 4 the listbox string array can include NUL characters: chr$(0). What seems to happen is that the text before the NUL is visible (it can be seen in the listbox) whilst the text after the NUL is invisible; however the entire string, including the NUL, is returned by the selection? command!

This feature is quite surprising and completely undocumented. I have no idea how Stefan discovered it, but obviously by using an undocumented feature he was taking a risk, and it does not work in LBB. To make the program work you would need to find an alternative, documented, way of storing the 'hidden' text.

Richard.
guest
Site Admin
Posts: 192
Joined: Tue Apr 03, 2018 1:34 pm

Re: Help Search program?

Post by guest »

The silly thing is that it's quite easy to do it 'properly'. Just create a second array containing the 'hidden' text and then use the selectindex? command instead of the selection? command! I've tried making that change and as far as I can see, on a quick test, the program works:

Code: Select all

    ' LB Help Search Add-on.bas
    ' Author: Stefan Pendl
    ' Date: 24.04.11
    '
    ' Copyright 2011 by Stefan Pendl
    '
    ' This code is free for personal use.
    ' You may not republish this code in its current form.
    ' You may modify this code for your personal use.
    ' You may publish a modified version of this code under these conditions:
    '    1. You have made major changes to the code.
    '    2. You give credit to the original author
    '
    ' The ATL control code is based on code at http://lbpe.wikispaces.com/ATL+Tutorial
    '
    '
    ' Credits:
    '   Thanks to Alyce, Janet, Chris, Rod, Jack for inspiring me.
    '   Thanks to the LB community for giving me the opportunity to learn and exchange code.
    '
    '
    ' History:
    '
    ' v1.0.0  ... April 24th, 2011 ... Initial release
    ' v1.0.1  ... April 25th, 2011 ... moved index initialization after opening the window
    ' v1.0.2  ...       -"-        ... added static text control, if ATL failed to initialize
    ' v1.0.3  ... April 26th, 2011 ... added selection of "exact phrase", "all words" or "any word"
    ' v1.0.4  ...       -"-        ... added function to build search pattern, source restructured
    ' v1.0.5  ...       -"-        ... added selection of "index only", "page contents" or "source code"
    ' v1.0.6  ... April 28th, 2011 ... added custom resize handler, since native one is only for type window
    ' v1.0.7  ... April 30th, 2011 ... added Index button to display initial page and index of all pages
    '                                  added Usage button to display general usage of this application
    '                                  resizer and ATL control are now DPI aware
    ' v1.0.8  ...       -"-        ... current settings and window layout are saved on close
    ' v1.0.9  ...   May  1st, 2011 ... making sure the ini file destination folder exists
    '                                  save window layout without scaling
    ' v1.0.10 ...   May  2nd, 2011 ... now using a text box instead of the static text control to allow scroll bars
    '                                  added more comments in preparation of the final release
    ' v1.0.11 ...   May  7th, 2011 ... added credits
    '                                  update list box for each found page during the search
    ' v1.0.12 ...   May  8th, 2011 ... reverted back to Quick Start page as initial page
    ' v1.0.13 ...       -"-        ... added hint about ATL context menu for printing
    ' v1.0.14 ...   May 15th, 2011 ... reverted back to index page as initial page
    '
    ' Todo:
    '   # nothing left to do ;-)

    ' ################################
    ' ################################
    ' ##                            ##
    ' ## section for initialization ##
    ' ##                            ##
    ' ################################
    ' ################################

    ' check for valid LB version
    if val(Version$) < 4.04 then
        notice "Wrong LB Version!"; chr$(13);_
            "This add-on is only valid for LB v4.04 and above!"; chr$(13);_
            "Exiting ..."
        end
    end if

[InitVars]
    ' initialize variables
    LbInstallDir$    = GetFolder$(GetModuleFileName$())
    HelpFileRoot$    = DefaultDir$; "\lb4help\LibertyBASIC_4_web\html\"
    HelpFileIndex$   = DefaultDir$; "\lb4help\LibertyBASIC_4.html"
    HelpFilePattern$ = "*.htm"
    InitialTitle$    = "Index"
    InitialPage$     = HelpFileIndex$

    ' variables for the ini file
    AppDataFolder$   = GetEnvironmentVariable$("APPDATA")
    LbAppDataFolder$ = GetPathTail$(LbInstallDir$)
    IniFileName$     = "LB Help Search Add-on.ini"

    ResizerDelay = 250

    dim FilesInfo$(1,1), PageIndex$(1,2), FoundPages$(1), FileName$(1)

    ' initialize ATL control
    Open "atl" For DLL As #atl

    CallDLL #atl, "AtlAxWinInit", ATLinitialized As long

    ' ######################################
    ' ######################################
    ' ##                                  ##
    ' ## section for user interface setup ##
    ' ##                                  ##
    ' ######################################
    ' ######################################

[GUI]
    ' setup GUI
    nomainwin
    WindowWidth  = 800
    WindowHeight = 600
    UpperLeftX   = DisplayWidth - WindowWidth
    UpperLeftY   = 1

    ' minimum window size
    MinWindowWidth  = int(500 * GetScreenScaleForDialog())
    MinWindowHeight = int(400 * GetScreenScaleForDialog())

    ' default size for ATL/text box control
    DefaultAtlPosX   = 210
    DefaultAtlPosY   =  10
    DefaultAtlWidth  = 575
    DefaultAtlHeight = 550

    ' default settings of the radio buttons
    AsPhrase$ = "set"
    AllWords$ = "reset"
    AnyWord$  = "reset"

    IndexOnly$    = "set"
    PageContents$ = "reset"
    SourceCode$   = "reset"

    ' get the saved layout and settings from the ini file
    gosub [ReadDefaults]

    ' define the GUI
    WindowTitle$ = "LB Help Search"

    groupbox    #m.search,   "Search Text",                       5,   5, 200,  95
    textbox     #m.phrase,                                       15,  20, 180,  25
    radiobutton #m.AsPhrase, "As Phrase", [nothing], [nothing],  15,  50,  90,  20
    radiobutton #m.AllWords, "All Words", [nothing], [nothing], 105,  50,  90,  20
    radiobutton #m.AnyWord,  "Any Word",  [nothing], [nothing],  15,  70,  90,  20

    groupbox    #m.source,       "Search Source",                         5, 105, 200,  65
    radiobutton #m.IndexOnly,    "Index Only",    [nothing], [nothing],  15, 120,  90,  20
    radiobutton #m.PageContents, "Page Contents", [nothing], [nothing], 105, 120,  90,  20
    radiobutton #m.SourceCode,   "Source Code",   [nothing], [nothing],  15, 140,  90,  20

    button      #m.default,  "Search",    [DoSearch], ul,         5, 175,  60,  25
    button      #m.index,    "Index",     [Index],    ul,        75, 175,  60,  25
    button      #m.usage,    "Usage",     [Usage],    ul,       145, 175,  60,  25

    groupbox    #m.result,   "Search Results",                    5, 205, 200, 355
    stylebits   #m.pages,    _WS_HSCROLL, 0, 0, 0
    listbox     #m.pages,    FoundPages$(), [DisplayPage],       15, 222, 180, 325

    ' create the ATL or a text box control
    if ATLinitialized then
        AtlPosX   = int(DefaultAtlPosX   * GetScreenScaleForDialog())
        AtlPosY   = int(DefaultAtlPosY   * GetScreenScaleForDialog())
        AtlWidth  = int(DefaultAtlWidth  * GetScreenScaleForDialog())
        AtlHeight = int(DefaultAtlHeight * GetScreenScaleForDialog())
    else
        AtlPosX   = DefaultAtlPosX
        AtlPosY   = DefaultAtlPosY
        AtlWidth  = DefaultAtlWidth
        AtlHeight = DefaultAtlHeight

        ' the text box will be read-only and include scroll bars
        stylebits #m.info, _ES_READONLY or _WS_VSCROLL or _WS_HSCROLL, _ES_AUTOHSCROLL or _ES_AUTOVSCROLL, 0, 0
        textbox   #m.info, AtlPosX, AtlPosY, AtlWidth, AtlHeight
    end if

    ' create a dilog with a sizing frame and a minimize box
    stylebits #m, _WS_THICKFRAME or _WS_MINIMIZEBOX, 0, 0, 0
    open WindowTitle$; " - "; InitialTitle$ for dialog as #m
    #m "trapclose [quit]"

    cursor hourglass

    if ATLinitialized then
        Message$ = "MSHTML:<html><head></head><body><center><h1>Initializing index!";_
                   "<br/>Please wait ...</h1></center></body></html>"
        hATL = DisplayATL("#m", Message$, hATL, AtlPosX, AtlPosY, AtlWidth, AtlHeight)
    else
        #m.info "!font 20 bold"
        #m.info "Initializing index!"; chr$(13); "Please wait ..."
    end if

    ' create page index
    gosub [CreateIndex]

    ' preset controls
    #m.phrase " Enter Search Text "
    #m.pages  "singleclickselect"

    #m.AsPhrase AsPhrase$
    #m.AllWords AllWords$
    #m.AnyWord  AnyWord$

    #m.IndexOnly    IndexOnly$
    #m.PageContents PageContents$
    #m.SourceCode   SourceCode$

    #m.pages   "reload"
    #m.default "!setfocus"
    #m.phrase  "!setfocus"

    ' select entire text in the search box
    Handle = hwnd(#m.phrase)

    calldll #user32, "SendMessageA",_
        Handle     as ulong,_
        _EM_SETSEL as ulong,_
        0          as long,_
        -1         as ulong,_
        result     as long

    cursor normal

    OldWidth  = -1
    OldHeight = -1

    timer ResizerDelay, [resizer]

    ' invoke initial resizing
    goto [resizer]

    ' ################################
    ' ################################
    ' ##                            ##
    ' ## section for event handlers ##
    ' ##                            ##
    ' ################################
    ' ################################

[nothing]
    ' dummy event handler for the radio buttons
    wait

[Index]
    ' event handler for the index button
    OverridePhrase = 1

[DoSearch]
    ' event handler for the search and index buttons
    cursor hourglass
    timer 0

    ' clear the list box array
    redim FoundPages$(FoundFiles)
    redim FileName$(FoundFiles)

    #m.phrase "!contents? Phrase$"

    if Phrase$ = "" or OverridePhrase = 1 then
        for i = 1 to FoundFiles
            FoundPages$(i) = PageIndex$(i,1)
            FileName$(i) = PageIndex$(i,2)
        next
    else
        #m.AsPhrase "value? AsPhrase$"
        #m.AllWords "value? AllWords$"
        #m.AnyWord  "value? AnyWord$"

        #m.IndexOnly    "value? IndexOnly$"
        #m.PageContents "value? PageContents$"
        #m.SourceCode   "value? SourceCode$"

        ' build the dynamic search condition
        select case
            case AnyWord$ = "set"
            SearchPattern$ = BuildSearchPattern$(Phrase$, "contents$", "OR", 0)

            case AllWords$ = "set"
            SearchPattern$ = BuildSearchPattern$(Phrase$, "contents$", "AND", 0)

            case else
            SearchPattern$ = "instr(upper$(contents$), upper$(Phrase$)) > 0"
        end select

        ' cycle through the pages
        j = 1
        for i = 1 to FoundFiles
            select case
                case SourceCode$ = "set"
                contents$ = CollectSourceCode$(PageIndex$(i,2))

                case PageContents$ = "set"
                open PageIndex$(i,2) for input as #f
                contents$ = input$(#f, lof(#f))
                close #f

                case else
                contents$ = PageIndex$(i,1)
            end select

            if eval(SearchPattern$) then
                FoundPages$(j) = PageIndex$(i,1)
                FileName$(j) = PageIndex$(i,2)
                j = j + 1

                #m.pages   "reload"
            end if
        next
    end if

    #m.pages   "reload"
    #m.pages   "selectindex 0"
    #m.phrase  "!setfocus"
    #m.default "!setfocus"

    cursor normal
    timer ResizerDelay, [resizer]

    ' wait here if the search button was hit, continue for the index button
    if OverridePhrase = 0 then wait

[DisplayPage]
    ' event handler for the selected list box item and the index button
    if OverridePhrase = 1 then
        Page$ = InitialTitle$; chr$(0); InitialPage$
    else
        #m.pages "selectionindex? index"
        Page$ = FoundPages$(index); chr$(0); FileName$(index)
    end if

    FileName$ = word$(Page$, 2, chr$(0))

    if ATLinitialized then
        hATL = DisplayATL("#m", FileName$, hATL, AtlPosX, AtlPosY, AtlWidth, AtlHeight)
    else
        #m.info "!font 20 bold"
        #m.info "Displaying Pages in your Browser!"

        run "rundll32.exe url.dll,FileProtocolHandler "; chr$(34); FileName$; chr$(34)
    end if

    call SetWindowText "#m", WindowTitle$; " - "; word$(Page$, 1, chr$(0)); chr$(0)

    OverridePhrase = 0
    wait

[resizer]
    ' resize handler for the dialog window
    ' get window size
    call GetWindowRect "#m", UpperLeftX, UpperLeftY, WindowWidth, WindowHeight

    ' check if we match the minimum size
    if WindowWidth  < MinWindowWidth  then WindowWidth  = MinWindowWidth
    if WindowHeight < MinWindowHeight then WindowHeight = MinWindowHeight

    ' get desktop size
    if GetDesktopRect(DesktopPosX, DesktopPosY, DesktopWidth, DesktopHeight) = 1 then
        ' check if we are out of bounds
        if (UpperLeftX + WindowWidth) > (DesktopPosX + DesktopWidth) then
            UpperLeftX = DesktopPosX + DesktopWidth - WindowWidth
        end if
        if UpperLeftX < DesktopPosX then UpperLeftX = DesktopPosX

        if (UpperLeftY + WindowHeight) > (DesktopPosY + DesktopHeight) then
            UpperLeftY = DesktopPosY + DesktopHeight - WindowHeight
        end if
        if UpperLeftY < DesktopPosY then UpperLeftY = DesktopPosY

        ' check if we exceed the desktop size
        if WindowWidth  > DesktopWidth  then WindowWidth  = DesktopWidth
        if WindowHeight > DesktopHeight then WindowHeight = DesktopHeight
    end if

    call SetWindowPos hwnd(#m), UpperLeftX, UpperLeftY, WindowWidth, WindowHeight

    ' get client area size
    if GetClientRect("#m", NewWidth, NewHeight) = 0 then wait

    ' check if size has changed
    if NewWidth = OldWidth and NewHeight = OldHeight then wait

    ' resize controls
    call SetWindowPos hwnd(#m.pages),  -1, -1, int(180 * GetScreenScaleForDialog()),_
                                      NewHeight - int((222 + 20) * GetScreenScaleForDialog())

    call SetWindowPos hwnd(#m.result), -1, -1, int(200 * GetScreenScaleForDialog()),_
                                      NewHeight - int((205 + 10) * GetScreenScaleForDialog())

    if ATLinitialized then
        hBrowse = hATL

        AtlWidth  = NewWidth  - AtlPosX - int(10 * GetScreenScaleForDialog())
    else
        hBrowse = hwnd(#m.info)
        #m.info "!contents? hBrowseCaption$"

        AtlWidth  = NewWidth  - int((AtlPosX + 10) * GetScreenScaleForDialog())
    end if

    AtlHeight = NewHeight - AtlPosY - int(10 * GetScreenScaleForDialog())

    call SetWindowPos hBrowse, -1, -1, AtlWidth, AtlHeight

    ' refresh text box contents
    if not(ATLinitialized) then
        #m.info ""
        #m.info hBrowseCaption$
    end if

    ' remember current size to avoid running the entire handler, if nothing changed
    OldWidth  = NewWidth
    OldHeight = NewHeight
    wait

[Usage]
    ' event handler for the usage button
    if UsageMessage$ = "" then
        restore [UsageMessage]

        ' use an infinite loop to read the usage message
        while 1
            read String$

            ' break the loop, if we have reached the end of the message definition
            if String$ = "@END" then exit while

            ' filter strings valid only for the ATL control
            if left$(String$, 4) = "ATL:" then
                if ATLinitialized then
                    UsageMessage$ = UsageMessage$ + mid$(String$, 5)
                else
                    String$ = ""
                end if
            else
                ' replace "\n" by the new-line characters or tag
                if instr(String$, "\n") > 0 then
                    if ATLinitialized then
                        ' filter strings valid only for the static text control
                        if left$(String$, 7) = "STATIC:" then
                            String$ = ""
                        else
                            String$ = "<br/>"
                        end if
                    else
                        String$ = chr$(13); chr$(10)
                    end if
                end if

                ' concatenate the message into one long string for easier display
                UsageMessage$ = UsageMessage$ + String$
            end if
        wend
    end if

    if ATLinitialized then
        hATL = DisplayATL("#m", UsageMessage$, hATL, AtlPosX, AtlPosY, AtlWidth, AtlHeight)
    else
        #m.info "!font courier_new 10"
        #m.info UsageMessage$
    end if

    #m.default "!setfocus"
    wait

[quit]
    ' event handler for closing the dialog
    gosub [SaveDefaults]
    close #m
    Close #atl
    end

    ' #################################
    ' #################################
    ' ##                             ##
    ' ## section for DATA definition ##
    ' ##                             ##
    ' #################################
    ' #################################

[UsageMessage]
    ' definition of the usage message
    '
    ' strings starting with "ATL:" are only valid for the ATL control
    ' strings starting with "STATIC:" are only valid for the static text control
    ' strings containing "\n" are replaced by new line characters or tags

    data "ATL:MSHTML:<html><head><title>Usage</title></head><body>"
    data "ATL:<center><h3>", "LB Help Search Add-On - Usage", "ATL:</h3></center>", "STATIC:\n", "STATIC:\n"
    data "ATL:<h4>", "Search Text:", "ATL:</h4>", "STATIC:\n", "STATIC:\n"
    data "ATL:<pre>"
    data "Text field ... enter text to search for", "\n"
    data "As Phrase .... results containing the entered words as is", "\n"
    data "All Words .... results containing all entered words", "\n"
    data "Any Word ..... results containing at least one of the entered words", "\n"
    data "ATL:</pre>"
    data "STATIC:\n"
    data "ATL:<h4>", "Search Source:", "ATL:</h4>", "STATIC:\n", "STATIC:\n"
    data "ATL:<pre>"
    data "Index Only ...... search only the title of the pages", "\n"
    data "Page Contents ... search the entire page content", "\n"
    data "Source Code ..... search only example code contained on the pages", "\n"
    data "ATL:</pre>"
    data "STATIC:\n"
    data "ATL:<h4>", "Buttons:", "ATL:</h4>", "STATIC:\n", "STATIC:\n"
    data "ATL:<pre>"
    data "Search ... start the search according to the above settings", "\n"
    data "Index .... display the initial page and load the list of all pages", "\n"
    data "Usage .... display this message", "\n"
    data "ATL:</pre>"
    data "STATIC:\n"
    data "ATL:<h4>", "Search Results:", "ATL:</h4>", "STATIC:\n", "STATIC:\n"
    data "ATL:<pre>"
    data "Initially ........ contains the list of all pages", "\n"
    data "After a Search ... contains the list of matching pages", "\n"
    data "ATL:</pre>"
    data "ATL:<h4>", "ATL:Printing can be done through the context menu of this control.", "ATL:</h4>"
    data "STATIC:\n"
    data "ATL:<h4>", "Settings and window layout are saved on close.", "ATL:</h4>"
    data "ATL:</body></html>"
    data "@END"

    ' ############################
    ' ############################
    ' ##                        ##
    ' ## section for procedures ##
    ' ##                        ##
    ' ############################
    ' ############################

    ' ##########
    ' #        #
    ' # GOSUBs #
    ' #        #
    ' ##########

[SaveDefaults]
    ' save dialog layout and current settings to the ini file
    #m.AsPhrase "value? AsPhrase$"
    #m.AllWords "value? AllWords$"
    #m.AnyWord  "value? AnyWord$"

    #m.IndexOnly    "value? IndexOnly$"
    #m.PageContents "value? PageContents$"
    #m.SourceCode   "value? SourceCode$"

    ' make sure the destination folder exists
    result = mkdir(AppDataFolder$; "\"; LbAppDataFolder$)

    ' save the settings only if the folder was created or already exists
    if result = 0 or result = 183 then
        call GetWindowRect "#m", UpperLeftX, UpperLeftY, WindowWidth, WindowHeight

        if UpperLeftX = 0 then UpperLeftX = 1
        if UpperLeftY = 0 then UpperLeftY = 1

        open AppDataFolder$; "\"; LbAppDataFolder$; "\"; IniFileName$ for output as #ini

        #ini "WindowWidth  = "; int(WindowWidth  / GetScreenScaleForDialog())
        #ini "WindowHeight = "; int(WindowHeight / GetScreenScaleForDialog())
        #ini "UpperLeftX   = "; UpperLeftX
        #ini "UpperLeftY   = "; UpperLeftY
        #ini "AsPhrase     = "; AsPhrase$
        #ini "AllWords     = "; AllWords$
        #ini "AnyWord      = "; AnyWord$
        #ini "IndexOnly    = "; IndexOnly$
        #ini "PageContents = "; PageContents$
        #ini "SourceCode   = "; SourceCode$

        close #ini
    else
        notice "Path does not exist"; chr$(13); "Unable to save settings!"
    end if
    return

[ReadDefaults]
    ' read dialog layout and last settings from the ini file
    files AppDataFolder$; "\"; LbAppDataFolder$, IniFileName$, FilesInfo$()

    ' only read if file exists
    if val(FilesInfo$(0,0)) > 0 then
        open AppDataFolder$; "\"; LbAppDataFolder$; "\"; IniFileName$ for input as #ini
        while not(eof(#ini))
            line input #ini, setting$

            Option$ = trim$(word$(setting$, 1, "="))
            Value$  = trim$(word$(setting$, 2, "="))

            select case Option$
                case "WindowWidth"
                WindowWidth = val(Value$)

                case "WindowHeight"
                WindowHeight = val(Value$)

                case "UpperLeftX"
                UpperLeftX = val(Value$)

                case "UpperLeftY"
                UpperLeftY = val(Value$)

                case "AsPhrase"
                AsPhrase$ = Value$

                case "AllWords"
                AllWords$ = Value$

                case "AnyWord"
                AnyWord$ = Value$

                case "IndexOnly"
                IndexOnly$ = Value$

                case "PageContents"
                PageContents$ = Value$

                case "SourceCode"
                SourceCode$ = Value$
            end select
        wend
        close #ini
    end if
    return

[CreateIndex]
    ' fill page index
    files HelpFileRoot$, HelpFilePattern$, FilesInfo$()

    FoundFiles = val(FilesInfo$(0,0))

    if FoundFiles = 0 then
        if ATLinitialized then
            Message$ = "MSHTML:<html><head></head><body><center><h1>Unable to locate Help Files!";_
                       "</h1></center></body></html>"
            hATL = DisplayATL("#m", Message$, hATL, AtlPosX, AtlPosY, AtlWidth, AtlHeight)
        else
            #m.info "Unable to locate Help Files!"
        end if
    else
        redim PageIndex$(FoundFiles,2)
        redim FoundPages$(FoundFiles)
        redim FileName$(FoundFiles)

        for i = 1 to FoundFiles
            FileName$ = HelpFileRoot$; FilesInfo$(i,0)

            open FileName$ for input as #f
            contents$ = input$(#f, min(512, lof(#f)))
            close #f

            ' parsing the tilte tag does not return unique results
            ' StartPos = instr(upper$(contents$), "<TITLE>") + 7
            ' EndPos   = instr(upper$(contents$), "</TITLE>, StartPos")

            ' parsing the first bold text does return unique results
            ' this returns the headline
            StartPos = instr(upper$(contents$), "<B>") + 3
            EndPos   = instr(upper$(contents$), "</B>", StartPos)

            PageTitle$ = mid$(contents$, StartPos, EndPos - StartPos)

            PageIndex$(i,1) = PageTitle$
            PageIndex$(i,2) = FileName$
        next

        ' sort page index
        sort PageIndex$(, 1, FoundFiles, 1

        ' fill list box array with page information
        for i = 1 to FoundFiles
            FoundPages$(i) = PageIndex$(i,1)
            FileName$(i) = PageIndex$(i,2)
        next

        if ATLinitialized then
            hATL = DisplayATL("#m", InitialPage$, hATL, AtlPosX, AtlPosY, AtlWidth, AtlHeight)
        else
            #m.info "Displaying Pages in your Browser!"
        end if

        call SetWindowText "#m", WindowTitle$; " - "; InitialTitle$; chr$(0)
    end if
    return

    ' ########
    ' #      #
    ' # SUBs #
    ' #      #
    ' ########

sub SetWindowPos hWndParent, PosX, PosY, Width, Height
    ' set window position and size with the ability to ignore one or the other
    '
    ' hWndParent ... handle of the control to position/resize
    ' PosX ......... horizontal position of the top left corner
    ' PosX ......... vertical position of the top left corner
    ' Width ........ new width of the control or window
    ' Height ....... new height of the control or window

    ' do not change the z-order and do not activate the window/control
    uFlags = _SWP_NOZORDER or _SWP_NOACTIVATE

    ' ignore the positional arguments, if both are -1
    if PosX  = -1 and PosY   = -1 then uFlags = uFlags or _SWP_NOMOVE

    ' ignore the size arguments, if both are -1
    if Width = -1 and Height = -1 then uFlags = uFlags or _SWP_NOSIZE

    calldll #user32, "SetWindowPos",_
        hWndParent as ulong,_
        _NULL      as ulong,_
        PosX       as long,_
        PosY       as long,_
        Width      as long,_
        Height     as long,_
        uFlags     as ulong,_
        result     as long
end sub

sub GetWindowRect Parent$, ByRef PosX, ByRef PosY, ByRef Width, ByRef Height
    ' get the size of the window or control
    '
    ' Parent$ ... LB handle of the control or window, for instance "#m" or "#m.cb"
    '
    ' on success fills the remaining 4 arguments with the
    ' size and position of the control/window

    hWndParent = hWnd(#Parent$)

    struct RECT,_
        left   as LONG,_
        top    as LONG,_
        right  as LONG,_
        bottom as LONG

    calldll #user32, "GetWindowRect",_
        hWndParent as ulong,_
        RECT       as struct,_
        result     as long

    if result <> 0 then
        PosX   = RECT.left.struct
        PosY   = RECT.top.struct
        Width  = RECT.right.struct  - RECT.left.struct
        Height = RECT.bottom.struct - RECT.top.struct
    end if
end sub

sub SetWindowText Parent$, Caption$
    ' change the caption of a control or window
    '
    ' Parent$ .... LB handle of the control or window, for instance "#m" or "#m.cb"
    ' Caption$ ... text for the new caption

    hWndParent = hWnd(#Parent$)

    calldll #user32, "SetWindowTextA",_
        hWndParent as ulong,_
        Caption$   as ptr,_
        result     as long
end sub

' #############
' #           #
' # FUNCTIONs #
' #           #
' #############

function GetClientRect(Parent$, ByRef Width, ByRef Height)
    ' get the size of a window
    '
    ' Parent$ .... LB handle of the window, for instance "#m"
    '
    ' on success 1 is returned and the arguments are filled with the values
    ' on failure 0 is returned

    hWndParent = hWnd(#Parent$)

    struct RECT,_
        left   as LONG,_
        top    as LONG,_
        right  as LONG,_
        bottom as LONG

    calldll #user32, "GetClientRect",_
        hWndParent as ulong,_
        RECT       as struct,_
        result     as long

    if result <> 0 then
        Width  = RECT.right.struct
        Height = RECT.bottom.struct

        GetClientRect = 1
    else
        GetClientRect = 0
    end if
end function

function GetDesktopRect(ByRef PosX, ByRef PosY, ByRef Width, ByRef Height)
    ' get the size and position of the desktop area not covered by any tool bars
    '
    ' on success 1 is returned and the arguments are filled with the values
    ' on failure 0 is returned

    struct RECT,_
        left   as LONG,_
        top    as LONG,_
        right  as LONG,_
        bottom as LONG

    uiAction = _SPI_GETWORKAREA

    calldll #user32, "SystemParametersInfoA",_
        uiAction as ulong,_
        uiParam  as ulong,_
        RECT     as struct,_
        fWinIni  as ulong,_
        result   as long

    if result <> 0 then
        PosX   = RECT.left.struct
        PosY   = RECT.top.struct
        Width  = RECT.right.struct  - RECT.left.struct
        Height = RECT.bottom.struct - RECT.top.struct

        GetDesktopRect = 1
    else
        GetDesktopRect = 0
    end if
end function

function CollectSourceCode$(FileName$)
    ' this function parses HTML pages for source code sections
    ' and returns a concatenated string of them

    ' source code in LBs help file is defined by the font "Courier New"
    StartCode$ = "<FONT FACE="; chr$(34); "COURIER NEW"; chr$(34); " SIZE="; chr$(34); "2"; chr$(34); ">"

    open FileName$ for input as #f
    contents$ = input$(#f, lof(#f))
    close #f

    StartPos = 0
    EndPos   = 1

    ' use an infinite loop for parsing
    while 1
        StartPos = instr(upper$(contents$), StartCode$, EndPos)

        ' break out of the loop, if there is nothing found
        if StartPos = 0 then exit while

        StartPos = StartPos + len(StartCode$)
        EndPos   = instr(upper$(contents$), "</FONT>", StartPos)

        CollectSourceCode$ = CollectSourceCode$; "<br/>"; mid$(contents$, StartPos, EndPos - StartPos)
    wend
end function

function BuildSearchPattern$(SearchString$, ContainerVar$, Operator$, CaseSensitive)
    ' return a string containing a conditional statement to be executed by the EVAL() function
    '
    ' SearchString$ ... string containing the search term
    ' ContainerVar$ ... variable containing the string to be searched
    ' Condition$ ...... boolean operator to concatenate multiple conditions (AND/OR/XOR)
    ' CaseSensitive ... flag to create a case sensitive contition or not
    '                   1 ... case sensitive
    '                   0 ... case insensitive
    count = 1

    ' build condition for first word of the search term
    if CaseSensitive then
        BuildSearchPattern$ = "instr("; ContainerVar$; ", ";_
                              chr$(34); word$(SearchString$, count); chr$(34); ") > 0"
    else
        BuildSearchPattern$ = "instr(upper$("; ContainerVar$; "), ";_
                              chr$(34); upper$(word$(SearchString$, count)); chr$(34); ") > 0"
    end if

    count = count + 1

    ' add remaining conditions separated by the operator
    while word$(SearchString$, count) <> ""
        if CaseSensitive then
            BuildSearchPattern$ = BuildSearchPattern$; " "; Operator$; " instr("; ContainerVar$; ", ";_
                                  chr$(34); word$(SearchString$, count); chr$(34); ") > 0"
        else
            BuildSearchPattern$ = BuildSearchPattern$; " "; Operator$; " instr(upper$("; ContainerVar$; "), ";_
                                  chr$(34); upper$(word$(SearchString$, count)); chr$(34); ") > 0"
        end if

        count = count + 1
    wend
end function

function DisplayATL(Parent$, File$, Handle, PosX, PosY, Width, Height)
    ' create an ATL control
    '
    ' Parent$ ... LB handle of the parent window, for instance "#m"
    ' File$ ..... Path of a file, HTML code as a string or an URL
    '             see http://lbpe.wikispaces.com/ATL+Tutorial for more
    ' Handle .... Windows handle of the last created ATL control
    '             usually the handle returned by the last call of this function
    '
    ' on success returns the handle of the created control

    if Handle then
        CallDLL #user32, "DestroyWindow", _
            Handle As ulong, _
            result As long
    end if

    hWndParent = hWnd(#Parent$)

    CallDLL #user32, "GetWindowLongA", _
        hWndParent     As ulong, _
        _GWL_HINSTANCE As long, _
        hInst          As ulong

    style   = _WS_CHILD or _WS_VISIBLE or _WS_VSCROLL or _WS_HSCROLL or _WS_BORDER
    exStyle = _WS_EX_CLIENTEDGE or _WS_EX_TOPMOST

    CallDLL #user32, "CreateWindowExA", _
        exStyle    As ulong, _
        "AtlAxWin" As ptr, _
        File$      As ptr, _
        style      As ulong, _
        PosX       As long, _
        PosY       As long, _
        Width      As long, _
        Height     As long, _
        hWndParent As ulong, _
        _NULL      As ulong, _
        hInst      As ulong, _
        _NULL      As ulong, _
        DisplayATL As ulong
end function

function GetFolder$(Path$)
    ' strip off the part after the last backslash of a file or folder path

    pos = 1
    GetFolder$ = Path$

    while pos > 0
        pos = instr(Path$, "\", pos)

        if pos > 0 then
            GetFolder$ = left$(Path$, pos)
            pos = pos + 1
        end if
    wend
end function

function GetPathTail$(Path$)
    ' get the part after the last backslash of a file or folder path

    if right$(Path$, 1) = "\" then
        GetPathTail$ = left$(Path$, len(Path$)-1)
    else
        GetPathTail$ = Path$
    end if

    for pos = len(GetPathTail$) to 1 step -1
        if mid$(GetPathTail$, pos, 1) = "\" then exit for
    next

    if pos > 1 then GetPathTail$ = mid$(GetPathTail$, pos + 1)
end function

function GetModuleFileName$()
    ' return the full path of the executable of the current process

    nSize = _MAX_PATH + 1
    lpFilename$ = space$(nSize); CHR$(0)

    calldll #kernel32, "GetModuleFileNameA",_
        hModule     as uLong,_
        lpFilename$ as ptr,_
        nSize       as uLong,_
        result      as uLong

    if result > 0 then GetModuleFileName$ = trim$(lpFilename$)
end function

function GetScreenScaleForDialog()
    ' get the scale to size controls inside a dialog type window
    '
    ' controls of dialog type windows are automatically scaled
    ' to match the users screen DPI settings
    '
    ' LB does not use all scales, only 100% and 125%

    calldll #user32, "GetDC",_
        0   as ulong,_   ' entire screen
        hDC as ulong

    nIndex = _LOGPIXELSX

    calldll #gdi32, "GetDeviceCaps",_
        hDC    as ulong,_
        nIndex as ulong,_
        dpi    as ulong

    calldll #user32, "ReleaseDC",_
        0      as ulong,_   ' entire screen
        hDC    as ulong,_
        result as ulong     ' 1 = success

    ScreenScale = dpi / 96

    ScreenScaleTmp = max(1, ScreenScale)
    GetScreenScaleForDialog = min(1.25, ScreenScaleTmp)
end function

function GetEnvironmentVariable$(lpName$)
    'get the value of an environment variable
    nSize = 1024

[Retry]
    lpBuffer$ = space$(nSize)

    calldll #kernel32, "GetEnvironmentVariableA", _
        lpName$   as ptr, _
        lpBuffer$ as ptr, _
        nSize     as ulong, _
        result    as ulong

    select case
        ' buffer too small
        case result > nSize
        nSize = result
        goto [Retry]

        ' variable found
        case result > 0
        GetEnvironmentVariable$ = trim$(lpBuffer$)
    end select
end function
sarmednafi
Posts: 36
Joined: Fri Apr 06, 2018 6:27 am

Re: Help Search program?

Post by sarmednafi »

Thank you, Richard,

It 's clear now.

All the best,
tsh73
Posts: 44
Joined: Fri Apr 06, 2018 7:58 pm

Re: Help Search program?

Post by tsh73 »

(my guess that)
It is OK with BASIC strings to contain chr(0)
but Windows uses C library to work with strings - where \0 is string terminator
So we got string printed up to first chr(0).
It is in the lore - I know it from somewhere.

As for using undocumented / incompatible features - if one program only in ONE language he rarely even thinks of using other ways beside first one that works.
EDIT and totally oblivious as to if it could fail and why (speaking of myself)

For example, LBB "x++" is incompatible with LB.
(EDIT-2 10.04,2018 Actually not x++ but x+=3)
But if one not using LB he would never know that.
Could programs be written without it? Yes.
Does not using it worths it? Depends on the goal.

EDIT
this one works the same in LB/LBB - only first part is visible

Code: Select all

a$="hello"+chr$(0)+"there"
notice a$
Last edited by tsh73 on Tue Apr 10, 2018 5:23 pm, edited 1 time in total.
guest
Site Admin
Posts: 192
Joined: Tue Apr 03, 2018 1:34 pm

Re: Help Search program?

Post by guest »

tsh73 wrote: Mon Apr 09, 2018 9:57 pm It is in the lore - I know it from somewhere.
It's not surprising that only the string before the NUL is visible - that is to be expected and both LB 4 and LBB behave the same way. What is more surprising is that the selection? command returns the whole string, including the NUL. It implies that selection? is returning the contents of the array, not the contents of the listbox, as it does in LBB.

If you were to modify the contents of the listbox using Windows API calls, resulting in it no longer corresponding to the array, I guess LB 4 would return the contents of the array even if what is displayed in the listbox is completely different!
As for using undocumented / incompatible features - if one program only in ONE language he rarely even thinks of using other ways beside first one that works.
That is poor quality coding; no professional programmer would work that way. If you rely on an undocumented feature, even if you use only the one language, there is no guarantee that it will work consistently, or that it will behave the same way after an upgrade. You refer to the "first way that works", but just because it worked five minutes ago it doesn't mean that it will necessarily work in five minutes time! You are taking a risk, always, when using any undocumented feature.

Sadly the documentation of Liberty BASIC is incomplete, so sometimes you have to use an undocumented feature simply because it has been omitted (the CHAR[N] structure type is a good example). But that doesn't mean it's OK to use other undocumented features just because they seem to work.

Richard.