Welcome, Guest
You have to register before you can post on our site.

Username
  

Password
  





Search Forums

(Advanced Search)

Forum Statistics
» Members: 123
» Latest member: Derick O'Brien
» Forum threads: 96
» Forum posts: 764

Full Statistics

Latest Threads
Arduino users news
Forum: This and that - friendly chat
Last Post: Jules Marchildon
Yesterday, 01:49 AM
» Replies: 2
» Views: 208
How to run PB progs in Li...
Forum: PowerBASIC for Windows
Last Post: Rod Macia
20.10.2025, 09:06 PM
» Replies: 6
» Views: 289
Having problems with pbus...
Forum: This and that - friendly chat
Last Post: Stuart McLachlan
20.10.2025, 05:57 AM
» Replies: 1
» Views: 143
Very Simple Round Gauge 0...
Forum: Source Code Library
Last Post: Jules Marchildon
18.10.2025, 02:17 AM
» Replies: 6
» Views: 650
The Future
Forum: Suggestions and discussion about PUMP
Last Post: Dale Yarker
12.10.2025, 07:34 AM
» Replies: 84
» Views: 10,305
READ$/Data Slow
Forum: PowerBASIC for Windows
Last Post: Brent F Boshart
11.10.2025, 09:13 PM
» Replies: 4
» Views: 284
7zip alternatives ?
Forum: This and that - friendly chat
Last Post: Eric Pearson
08.10.2025, 04:07 PM
» Replies: 4
» Views: 296
Doubly Linked String/WStr...
Forum: Source Code Library
Last Post: Stanley Durham
05.10.2025, 10:19 AM
» Replies: 2
» Views: 223
DOCX and XLSX Viewer
Forum: PowerBASIC for Windows
Last Post: Dale Yarker
05.10.2025, 05:05 AM
» Replies: 9
» Views: 961
Very Simple Round Gauge D...
Forum: Programming
Last Post: Jules Marchildon
03.10.2025, 03:06 AM
» Replies: 5
» Views: 566

 
  gbLocator (Discussion)
Posted by: Gary Beene - 07.09.2025, 07:49 AM - Forum: PowerBASIC for Windows - Replies (2)

This thread is for discussion of gbLocator - a file and folder search utility for blind and low vision users. I posted it a few days ago in the Source Code forum.

Same links ...
Web Site: https://newvisionconcepts.com/gblocator/gblocator.htm
Installation File:   https://garybeene.com/files/gblocator_setup.exe
Source Code File:   https://garybeene.com/files/gblocator.zip

There's even a short video:  https://garybeene.com/files/gblocator.mp4

ver 4.1 is now available - these new features:


1. F1 shows Settings Shortcuts and F2 shows Actions Shortcuts.
2. When Ctrl-F1 is used to start a browser, the BigX toolbar is shown - literally a big "X" over the small browser "x". (optional)
3. Folders now have "(f)" in front of them in Column 1 and when spoken, is spoken as "folder"
4. When opening a file/folder externally (browser, default client, or File Explorer), user can choose between opening the BigX toolbar or Windows Narrator (or neither)
5. A variety of changes to the text spoken when an action is taken - generally more informative but sometimes shorter
6. The Ctrl+Mouse fontsize change now works over the RE and the LV
7. When Expanding a folder, the folder name is now NOT shown in the RE.  Having that long a visible search string was annoying. 
8. As searches are made, the last 5 search terms are kept. To see them, press the up/down arrow keys when focus is on the RE (for my low vision users, I avoid dropdown combobox solutions).
9. Previews of image files (I don't think this was in the previous version)

Howdy, Mike!

Thanks for that post.  I'm glad to hear it was ok at 1920x1080.  I tried the newest version on 3840x2160 and it seems to draw correctly.  If you would, please try the latest version on your 3840x2160 again and let me know if it look ok for you too.

In the latest version, I can't repeat the problem you mentioned, about ESC. As you noted, ESC is supposed to stop talking and exit only if talking is stopped.


Howdy, Pierre!

Yes, with David's encouragement, I put up a thread about gbLocator on the Everything forum!
I was pretty excited about the possibility of getting responses there, but it's been 5 days and I've had zero responses!  Bummer that!  Huh  I will have to be patient!

Test. I don't seem to have figured out how to make a reply that carries the name of the person making the reply.  This time, I pressed "New Reply" across the top of the thread.

Print this item

  New Announcements?
Posted by: Stuart McLachlan - 06.09.2025, 04:43 AM - Forum: Suggestions and discussion about PUMP - No Replies

The Portal sidebar shows something like this:

Last visit: Today, 12:05
Since then, there have been:
» 1 new announcement
» 1 new thread
» 1 new post

View New Posts
View Today's Posts

What are "announcements" and where do we find them?

Print this item

  WMIC removed in WIn11 25H2
Posted by: Stuart McLachlan - 06.09.2025, 04:23 AM - Forum: Programming - No Replies

There are currently a number of threads in gbThreads that contain a Shell to WMIC  (Windows Management Instrumentation Command-line), including some from the last few years.  

If you are currently using an application that Shells to WMIC, it will not work in Win11 25H2 (coming very soon).  It is being removed (along with PowerShell 2.0).

Print this item

  Very Fast WString /Object Hash Table (Comments)
Posted by: Stanley Durham - 05.09.2025, 03:20 AM - Forum: Programming - Replies (7)

One thing I learned from this, for absolute speed, you can’t use normal WString comparison. Also, Equal is faster than Comparison if that’s all you need, which is the case with a hash table. Comparison requires two comparisons to determine if it’s less than or greater than. For absolute speed, you can’t use; If A$ = B$ Then.
In the Equal callback, testing the lengths first immediately eliminates some values. Passing a pointer and the length to the callback eliminates that overhead in the callback.
I could get this faster, about 0.095 seconds to find 1,000,000 keys, by writing the code inline instead of using callbacks, but the complexity isn’t worth the benefit.


Code:
Very Fast WString /Object Hash Table

Print this item

  Very Fast WString /Object Hash Table
Posted by: Stanley Durham - 05.09.2025, 02:29 AM - Forum: Source Code Library - No Replies

find 100,000 random keys = 0.004 seconds
find 1,000,000 random keys = 0.112 seconds
find 5,000,000 random keys = 0.900 seconds
 
A unique key, WString, is used to store and retrieve a payload value, Object.
Key is case sensitive.
Property Set Capacity(ByVal value As Long), about number of expected items.
 
Can Store/Restore To/From File. To use that you need to provide callbacks. One to pack the object into a String and another to restore the object from a String.
Declare Function ObjectStoreCallback(o As IUnknown) As String
Declare Function ObjectRestoreCallback(ByRef stored As String) As IUnknown
When you implement the callbacks, use the actual “Interface” instead of “IUnknown.”
 
INC has no dependencies.

Code:
'ObjectHash.inc
'Public domain, use at own risk. SDurham
#If Not %Def(%OHash250808)
    %OHash250808 = 1

    Declare Function ObjectStoreCallback(o As IUnknown) As String
        'store object's data in a String and return the String
        'when you implement the callback, replace IUnknown with the actual interface
    Declare Function ObjectRestoreCallback(ByRef stored As String) As IUnknown
        'restore object's data and return new instance
        'when you implement the callback, replace IUnknown with the actual interface

    Type WStrT
        mem As Word Ptr
        count As Long
    End Type
    Type OHashNodeT
        key As WStrT
        value As Long
        next As OHashNodeT Ptr
    End Type
    Type StrBuildT
        mem As Long
        count As Long
        max As Long
    End Type

    Class OHashC

        'Key/Value WString/Object Hash Table
        'value stored and retrieved using unique key
        'key case sensitive

        Instance mCapacity As Long
        Instance mCount As Long
        Instance mArr() As Long
        Instance mIndex As Long
        Instance mNode As OHashNodeT Ptr
        Instance mStore As Long
        Instance mRestore As Long

        Class Method Create()
            mCapacity = 30 'default capacity
            ReDim mArr(0 To mCapacity - 1)
        End Method
        Class Method Destroy()
            Me.ClearMe()
            Erase mArr()
        End Method

        Interface OHashI : Inherit IUnknown

            Property Set Capacity(ByVal value As Long)
                'about number of expected items : hash table must be empty
                If mCount = 0 And value > 0 Then
                    mCapacity = value
                    ReDim mArr(0 To mCapacity - 1)
                End If
            End Property

            Method Storage(ByVal storeCB As Long, ByVal restoreCB As Long)
                'store and restore callbacks must be set to call Stor/Restore methods
                If storeCB And restoreCB Then
                    mStore = storeCB : mRestore = restoreCB
                End If
            End Method

            Method Clear()
                'empty container
                Me.ClearMe()
            End Method

            Property Get Count() As Long
                'get item count
                Property = mCount
            End Property

            Method Set(ByRef key As WString, value As IUnknown)
                'add key and associated value : key added if not in hash table
                Local index, keymem, keylen As Long
                Local node As OHashNodeT Ptr
                If IsObject(value) Then
                    keymem = StrPtr(key)
                    keylen = Len(key)
                    index = WStrHashIndex(keymem, keylen, mCapacity)
                    node = mArr(index)
                    While node
                        If WStrEqual(keymem, keylen, @node.key.mem, @node.key.count) Then
                            @node.value = Me.FreeObj(@node.value)
                            @node.value = Me.LongFromObj(value)
                            Exit Method
                        End If
                        node = @node.next
                    Wend
                    'key not in hash table
                    node = MemAlloc(SizeOf(@node))
                    If node Then
                        WStrSet @node.key, key
                        @node.value = Me.LongFromObj(value)
                        @node.next = mArr(index)
                        mArr(index) = node
                        Incr mCount
                    End If
                End If
            End Method

            Method Get(ByRef key As WString) As IUnknown
                'get key's associated value : null if key not in hash table
                Local o As IUnknown
                Local node As OHashNodeT Ptr
                node = Me.Contains(key)
                If node Then
                    o = Me.ObjFromLong(@node.value)
                    Method = o
                End If
            End Method

            Method Contains(ByRef key As WString) As Long
                'true/false if key in hash table
                Local keymem, keylen As Long
                Local node As OHashNodeT Ptr
                If mCount Then
                    keymem = StrPtr(key)
                    keylen = Len(key)
                    node = mArr(WStrHashIndex(keymem, keylen, mCapacity))
                    While node
                        If WStrEqual(keymem, keylen, @node.key.mem, @node.key.count) Then
                            Method = node : Exit Method
                        End If
                        node = @node.next
                    Wend
                End If
            End Method

            Method Delete(ByRef key As WString)
                'delete key and associated value
                Local index, keymem, keylen As Long
                Local node, prev As OHashNodeT Ptr
                If mCount Then
                    keymem = StrPtr(key)
                    keylen = Len(key)
                    index = WStrHashIndex(keymem, keylen, mCapacity)
                    node = mArr(index)
                    While node
                        If WStrEqual(keymem, keylen, @node.key.mem, @node.key.count) Then
                            If prev Then
                                @prev.next = @node.next
                            Else
                                mArr(index) = @node.next
                            End If
                            node = Me.FreeNode(node)
                            Decr MCount
                            Exit Method
                        End If
                        node = @node.next
                    Wend
                End If
            End Method

            Method Start()
                'reset cursor to start
                mIndex = -1 : mNode = 0
            End Method

            Method Each() As Long
                'move cursor to next key in hash table : true/false success
                Register i As Long
                If mCount Then
                    If mNode And @mNode.next Then
                        mNode = @mNode.next
                        Method = 1 : Exit Method
                    Else
                        mNode = 0
                        For i = mIndex + 1 To mCapacity - 1
                            If mArr(i) Then
                                mIndex = i
                                mNode = mArr(i)
                                Method = 1 : Exit Method
                            End If
                        Next i
                    End If
                End If
                mIndex = -1 : mNode = 0
            End Method

            Method Key() As WString
                'get key at cursor position : null if cursor invalid
                If mNode Then Method = WStrGet(@mNode.key)
            End Method

            Method Value() As IUnknown
                'get value at cursor position : null if cursor invalid
                If mNode Then Method = Me.ObjFromLong(@mNode.value)
            End Method

            Method Store() As String
                'store container as String
                Local key, s As String
                Local value As IUnknown
                Local sb As StrBuildT
                If mCount And mStore Then
                    StrBuildPush sb, Mkl$(mCount)
                    Me.Start()
                    While Me.Each()
                        key = ChrToUtf8$(Me.Key())
                        value = Me.Value()
                        If IsObject(value) Then
                            Call Dword mStore Using ObjectStoreCallback(value) To s
                            StrBuildPush sb, Mkl$(Len(key))
                            StrBuildPush sb, key
                            StrBuildPush sb, Mkl$(Len(s))
                            StrBuildPush sb, s
                        End If
                    Wend
                    Method = StrBuildPop(sb)
                End If
            End Method

            Method Restore(ByRef stored As String)
                'restore container from String
                Register i As Long
                Local items, bytes As Long
                Local key, s As String
                Local o As IUnknown
                Local p As Long Ptr
                Me.ClearMe()
                If mRestore And Len(stored) Then
                    p = StrPtr(stored)
                    items = @p : Incr p
                    For i = 1 To items
                        bytes = @p : Incr p
                        key = Peek$(p, bytes) : p += bytes
                        bytes = @p : Incr p
                        s = Peek$(p, bytes) : p += bytes
                        Call Dword mRestore Using ObjectRestoreCallback(s) To o
                        Me.Set(Utf8ToChr$(key), o)
                    Next i
                End If
            End Method

            Method FileStore(ByVal file As WString)
                'store container to File
                If Len(file) And mStore Then Me.FilePut(file, Me.Store())
            End Method

            Method FileRestore(ByVal file As WString)
                'restore container from File
                If IsFile(file) And mRestore Then Me.Restore(Me.FileGet(file))
            End Method

        End Interface

        Class Method ClearMe()
            Register i As Long
            Local node As OHashNodeT Ptr
            For i = 0 To mCapacity - 1
                While mArr(i)
                    node = mArr(i)
                    mArr(i) = @node.next
                    Me.FreeNode(node)
                Wend
            Next i
            mCount = 0
        End Method

        Class Method FreeNode(ByVal node As OHashNodeT Ptr) As Long
            If node Then
                WStrFinal @node.key
                @node.value = Me.FreeObj(@node.value)
                MemFree(node)
            End If
        End Method

        Class Method LongFromObj(o As IUnknown) As Long
            If IsObject(o) Then
                o.AddRef()
                Method = Peek(Long, VarPtr(o))
            End If
        End Method

        Class Method ObjFromLong(ByVal h As Long) As IUnknown
            Local o As IUnknown
            If h Then
                Poke Long, VarPtr(o), h
                o.AddRef()
                Method = o
            End If
        End Method

        Class Method FreeObj(ByVal h As Long) As Long
            Local o As IUnknown
            If h Then
                Poke Long, VarPtr(o), h
                o = Nothing
            End If
        End Method


        Class Method FilePut(ByVal file As WString, ByRef value As String)
            Local f As Long
            If Len(file) = 0 Then Exit Method
            f = FreeFile
            Open file For Binary As f
            SetEof f
            Put$ f, value
            Close f
        End Method

        Class Method FileGet(ByVal file As WString) As String
            Local f As Long, value As String
            If IsFalse IsFile(file) Then Exit Method
            f = FreeFile
            Open file For Binary As f
            Get$ f, Lof(f), value
            Close f
            Method = value
        End Method

    End Class
#EndIf '%OHash250808

#If Not %Def(%WStr250808)
    %WStr250808 = 1
    '----------------------------------------------------------------------
    'WString Container = Word Array
    'Public domain, use at own risk. SDurham
    '----------------------------------------------------------------------
    %WrdSize = 2
    Type WStrT
        mem As Word Ptr
        count As Long
    End Type
    '----------------------------------------------------------------------
    Sub WStrFinal(t As WStrT)
        'call before variable gores out of scope to free memory
        WStrClear t
    End Sub
    '----------------------------------------------------------------------
    Sub WStrClear(t As WStrT)
        'empty container
        t.mem = MemFree(t.mem)
        t.count = 0
    End Sub
    '----------------------------------------------------------------------
    Function WStrCount(t As WStrT) As Long
        'get character count
        Function = t.count
    End Function
    '----------------------------------------------------------------------
    Function WStrGet(t As WStrT) As WString
        'get value
        If t.mem Then Function = Peek$$(t.mem, t.count)
    End Function
    '----------------------------------------------------------------------
    Macro WStrGetM(t) = Peek$$(t.mem, t.count)
    '----------------------------------------------------------------------
    Sub WStrSet(t As WStrT, ByRef value As WString)
        'set value
        Local characters As Long
        t.mem = MemFree(t.mem)
        t.count = 0
        characters = Len(value)
        If characters Then
            t.mem = MemAlloc(characters * %WrdSize)
            If t.mem Then
                Memory Copy StrPtr(value), t.mem, characters * %WrdSize
                t.count = characters
            End If
        End If
    End Sub
    '----------------------------------------------------------------------
#EndIf '%WStr250808

#If Not %Def(%WStrHash250808)
    %WStrHash250808 = 1
    '----------------------------------------------------------------------
    'WString Hash Index
    'Public domain, use at own risk. SDurham
    '----------------------------------------------------------------------
    Function WStrHashIndex(ByVal keymem As Word Ptr, ByVal keylen As Long, ByVal capacity As Long) As Long
        'get key's hash index position
        Register i As Long
        Register total As Long
        Local temp As Long
        If keymem Then
            For i = 0 To keylen - 1
                temp += @keymem[i] + total
                Shift Left total, 8
                total += temp
            Next i
            Function = Abs(total Mod capacity)
        End If
    End Function
    '----------------------------------------------------------------------
#EndIf '%WStrHash250808

#If Not %Def(%WStrEqual250808)
    %WStrEqual250808 = 1
    '----------------------------------------------------------------------
    Function WStrEqual(ByVal memA As Word Ptr, ByVal lenA As Long, ByVal memB As Word Ptr, ByVal lenB As Long) As Long
        'true/false if two strings equal
        Register i As Long
        If memA And memB And lenA = lenB Then
            For i = 0 To lenA - 1
                If @memA[i] <> @memB[i] Then Exit Function
            Next i
            Function = 1
        End If
    End Function
    '----------------------------------------------------------------------
#EndIf '%WStrEqual250808

#If Not %Def(%StrBuild250811)
    %StrBuild250811 = 1
    '----------------------------------------------------------------------
    'String Builder
    'Public domain, use at own risk. SDurham
    '----------------------------------------------------------------------
    %BytSize = 1
    %StrBuildBufferMax = 5000000
    '----------------------------------------------------------------------
    Type StrBuildT
        mem As Long
        count As Long
        max As Long
    End Type
    '----------------------------------------------------------------------
    Sub StrBuildPush(t As StrBuildT, ByVal value As String)
        'append value
        Local strlen, currentcount, buffer, newmax As Long
        strlen = Len(value)
        If strlen Then
            If strlen > t.max - t.count Then
                currentcount = t.count : t.count = 0 : t.max = 0
                buffer = Max&(1, 2 * currentcount)
                buffer = Min&(buffer, %StrBuildBufferMax)
                newmax = currentcount + buffer + strlen
                t.mem = MemReAlloc(t.mem, newmax * %BytSize)
                If t.mem = 0 Then Exit Sub
                t.count = currentcount : t.max = newmax
            End If
            Memory Copy StrPtr(value), t.mem + (t.count * %BytSize), strlen * %BytSize
            t.count += strlen
        End If
    End Sub
    '----------------------------------------------------------------------
    Function StrBuildPop(t As StrBuildT) As String
        'get whole string : free memory
        If t.mem And t.count Then Function = Peek$(t.mem, t.count)
        t.mem = MemFree(t.mem)
        t.count = 0
        t.max = 0
    End Function
    '----------------------------------------------------------------------
#EndIf '%StrBuild250811

#If Not %Def(%Memory230925)
    %Memory230925 = 1
    '----------------------------------------------------------------------
    'Memory Allocation
    'Public domain, use at own risk. SDurham
    '----------------------------------------------------------------------
    Declare Function GlobalAlloc Lib "Kernel32.dll" Alias "GlobalAlloc" (ByVal uFlags As Dword, ByVal dwBytes As Dword) As Dword
    Declare Function GlobalReAlloc Lib "Kernel32.dll" Alias "GlobalReAlloc" (ByVal hMem As Dword, ByVal dwBytes As Dword, ByVal uFlags As Dword) As Dword
    Declare Function GlobalFree Lib "Kernel32.dll" Alias "GlobalFree" (ByVal hMem As Dword) As Dword
    %MEMFIXED = &H0000 : %MEMMOVEABLE = &H0002 : %MEMZEROINIT = &H0040 : %MEMGPTR = (%MEMZEROINIT Or %MEMFIXED)
    '----------------------------------------------------------------------
    Function MemAlloc(ByVal bytes As Long) As Long
        'allocate memory
        If bytes Then Function = GlobalAlloc(ByVal %MEMGPTR, ByVal bytes)
    End Function
    '----------------------------------------------------------------------
    Function MemReAlloc(ByVal hMem As Long, ByVal bytes As Long) As Long
        'reallocate new size
        If hMem And bytes Then
            Function = GlobalReAlloc(ByVal hMem, ByVal bytes, ByVal %MEMMOVEABLE Or %MEMZEROINIT)
        ElseIf bytes Then
            Function = GlobalAlloc(ByVal %MEMGPTR, ByVal bytes)
        ElseIf hMem Then
            Function = GlobalFree(ByVal hMem)
        End If
    End Function
    '----------------------------------------------------------------------
    Function MemFree(ByVal hMem As Long) As Long
        'free memory
        If hMem Then GlobalFree(ByVal hMem)
    End Function
    '----------------------------------------------------------------------
#EndIf '%Memory230925

Code:
'ObjectHash.bas
#Option LargeMem32
#Dim All
#Compile Exe
#Include Once "WIN32API.INC"
#Include Once "..\ObjectHash.inc"

%TextBox = 101
%BtnID = 102
Global gDlg As Long
Global gBuild As IStringBuilderW
Sub SS(ByVal value As WString)
    gBuild.Add(value + $CrLf)
End Sub

Class TestC
    Instance mX As Long
    Interface TestI : Inherit IUnknown
        Property Get X() As Long
            Property = mX
        End Property
        Property Set X(ByVal value As Long)
            mX = value
        End Property
        Property Get Str() As WString
            Property = Format$(mX)
        End Property
    End Interface
End Class

Function TestStore(o As TestI) As String
    'store callback
    'store object's data in a String and return the String
    'when you implement the callback, replace IUnknown with the actual interface
    If IsObject(o) Then Function = Mkl$(o.X)
End Function

Function TestRestore(ByRef stored As String) As TestI
    'restore callback
    'restore object's data and return new instance
    'when you implement the callback, replace IUnknown with the actual interface
    Local o As TestI : o = Class "TestC"
    o.X = Cvl(stored)
    Function = o
End Function

Sub SampleCode()
    Register i As Long
    Local hash As OHashI : hash = Class "OHashC"
    Local o As TestI : o = Class "TestC"
    Local stored As String
    Local a() As WString
    Local d As Double
    Local testcount As Long : testcount = 100000 'can change

    Randomize
    MousePtr 11
    Control Set Text gDlg, %TextBox, ""

    SS ""
    SS "Property Set Capacity(ByVal value As Long)"
    hash.capacity = testcount

    SS ""
    SS "Method Storage(ByVal storeCB As Long, ByVal restoreCB As Long)"
    hash.Storage(CodePtr(TestStore), CodePtr(TestRestore))

    SS ""
    SS "Method Add(ByRef key As WString, value As IUnknown) As Long"
    SS "add key/value items"
    For i = 65 To 69
        o = Class "TestC" : o.X = i
        hash.Set(Chr$$(i), o)
    Next i
    hash.Start()
    While hash.Each()
        o = hash.Value()
        If IsObject(o) Then SS "key = " + $Dq + hash.Key() + $Dq + " | value = " + o.Str
    Wend

    SS ""
    SS "change value for key C"
    o = Class "TestC" : o.X = 4444
    hash.Set("C", o)
    hash.Start()
    While hash.Each()
        o = hash.Value()
        If IsObject(o) Then SS "key = " + $Dq + hash.Key() + $Dq + " | value = " + o.Str
    Wend

    SS ""
    SS "Method Get(ByRef key As WString) As IUnknown"
    SS "get object to change value for key C"
    o = hash.Get("C") : If IsObject(o) Then o.X = 999999999
    hash.Start()
    While hash.Each()
        o = hash.Value()
        If IsObject(o) Then SS "key = " + $Dq + hash.Key() + $Dq + " | value = " + o.Str
    Wend

    SS ""
    SS "Method Contains(ByRef key As WString)"
    SS "hash table contains key B = " + Format$(hash.Contains("B"))
    SS "hash table contains key E = " + Format$(hash.Contains("E"))
    SS "hash table contains key ZZZ = " + Format$(hash.Contains("ZZZ"))

    SS ""
    SS "Method Delete(ByRef key As WString)"
    SS "delete key C"
    hash.Delete("C")
    hash.Start()
    While hash.Each()
        o = hash.Value()
        If IsObject(o) Then SS "key = " + $Dq + hash.Key() + $Dq + " | value = " + o.Str
    Wend

    SS ""
    SS "Method Store() As String"
    stored = hash.Store()
    SS "Method Restore(ByRef stored As String)"
    hash.Restore(stored)
    SS "Method FileStore(ByVal file As String)"
    hash.FileStore("Stored.hash")
    SS "Method FileRestore(ByVal file As String)"
    hash.FileRestore("Stored.hash")
    Kill "Stored.hash"
    hash.Start()
    While hash.Each()
        o = hash.Value()
        If IsObject(o) Then SS "key = " + $Dq + hash.Key() + $Dq + " | value = " + o.Str
    Wend

    ReDim a(1 To testcount)
    For i = 1 To testcount
        a(i) = RandomString()
    Next i

    SS ""
    SS "add "+Format$(testcount,"#,")+" random key/value items"
    hash.Clear()
    d = Timer
    For i = 1 To testcount
        o = Class "TestC" : o.X = i
        hash.Set(a(i), o)
    Next i
    SS "Time = " + Format$(Timer - d, "000.000")
    SS "Count = " + Format$(hash.Count, "#,") + " duplicate keys not allowed"

    SS ""
    SS "find "+Format$(testcount,"#,")+" random keys"
    d = Timer
    For i = 1 To testcount
        If hash.Contains(a(i)) = 0 Then
            ? "hash table fail" : Exit For
        End If
    Next i
    SS "Time = " + Format$(Timer - d, "000.000")

    SS ""
    SS ""
    Control Set Text gDlg, %TextBox, gBuild.String
    gBuild.Clear
    MousePtr 1
End Sub

Function RandomString() As String
    Register i As Long
    Local s As String
    For i = 1 To Rnd(5, 8)
        Select Case As Const Rnd(1, 2)
            Case 1 : s += Chr$(Rnd(65, 90))
            Case 2 : s += Chr$(Rnd(97, 122))
        End Select
    Next i
    Function = s
End Function

Function PBMain()
    gBuild = Class "StringBuilderW"
    Dialog Default Font "courier new", 12, 0, 0
    Dialog New 0, "Sample Code", 0, 0, 0, 0, %WS_Caption Or %WS_ClipSiblings Or %WS_MinimizeBox Or %WS_SysMenu Or %WS_ThickFrame, %WS_Ex_AppWindow To gDlg
    Control Add TextBox, gDlg, %TextBox, "", 0, 0, 0, 0, %ES_AutoHScroll Or %ES_AutoVScroll Or %ES_MultiLine Or %ES_NoHideSel Or %ES_WantReturn Or %WS_HScroll Or %WS_VScroll Or %WS_Border, 0
    Control Add Button,  gDlg, %BtnID, "Run", 275, 220, 60, 15, %BS_Flat, 0
    SendMessageW(GetDlgItem(gDlg, %TextBox), %EM_SETLIMITTEXT, 4000000, 0)
    Dialog Show Modeless gDlg, Call DlgCB
    While IsWin(gDlg)
        Dialog DoEvents
    Wend
End Function

CallBack Function DlgCB()
    Select Case As Long Cb.Msg
        Case %WM_InitDialog
            WM_InitDialog()
        Case %WM_Size
            WM_Size()
        Case %WM_Command
            Select Case As Long Cb.Ctl
                Case %BtnID : If Cb.CtlMsg = %BN_Clicked Then SampleCode()
            End Select
    End Select
End Function

Sub WM_InitDialog()
    Local clientW, clientH As Long
    Desktop Get Client To clientW, clientH
    clientW /= 7
    clientH /= 4
    Dialog Set Loc gDlg, clientW / 2, clientH / 7
    Dialog Set Size gDlg, clientW, clientH
End Sub

Sub WM_Size()
    Local clientW, clientH As Long
    Local marg As Long
    Local buttonW, buttonH As Long
    Local txtWidth, txtHeight As Long
    Local fromLeft, fromBottom As Long
    Dialog Get Client gDlg To clientW, clientH
    marg = 1 : buttonW = 30 : buttonH = 10
    fromLeft = clientW - marg - marg - buttonW
    fromBottom = clientH - marg - marg - buttonH
    Control Set Size gDlg, %BtnID, buttonW, buttonH
    Control Set Loc gDlg, %BtnID, fromLeft, fromBottom
    txtWidth = clientW - marg - marg
    txtHeight = clientH - marg - buttonH - marg - marg
    Control Set Size gDlg, %TextBox, txtWidth, txtHeight
    Control Set Loc gDlg, %TextBox, marg, marg
End Sub

comments 
Very Fast WString /Object Hash Table (Comments)

Print this item

  gbNotes (Discussion)
Posted by: Gary Beene - 03.09.2025, 10:05 PM - Forum: PowerBASIC for Windows - Replies (6)

I was going to wait about posting gbNotes until I added more features, but I've gotten distracted and probably won't touch it again for a while.  But since I use it daily I thought it ought to be good enough to share.

Link to Source Code Forum

The "database" is just a text file that uses "++++" as a delimiter between entries.

The first line of each entry is just a user name.  Any number of lines can added after that.

On the left is a search term editor, a list of the most search terms and a list of the most recent search matches.  Click on a match to see and edit the content.  Ctrl+D will insert a date line into the editor, below which you can enter new content.

Each day, it does a backup of the CRM.txt file into a "backup" folder.

You can also view/edit/search the entire CRM.txt file

There's not much more to it.  Simple, but good enough to let me track my contacts.

[Image: gbnotes.png]

Print this item

  gbNotes
Posted by: Gary Beene - 03.09.2025, 09:54 PM - Forum: Source Code Library - No Replies

A while back I asked about CRM app recommendations. Not having found something as simple as I wanted, I wrote my own and have been using it the last couple of months. 

I still have a list of changes I want to make to it, but since I've been using for a while now, I guess it's worth offering up to anyone who wants it.

Discussion:  
Source Code:  https://garybeene.com/files/gbnotes.zip

[Image: gbnotes.png]


Code:
'Compilable Example:  Requires JOSE includes.
#Compile Exe  "gbnotes.exe"
#Dim All

#Debug Error On
#Debug Display On

%Unicode = 1
#Include "Win32API.inc"

$Ver = "1.0"
$Delim = "++++"

#Resource Icon xlogo, "icons\n.ico"
#Resource Icon xsave, "icons\save.ico"
#Resource Icon xnew, "icons\new.ico"
#Resource Icon xdelete, "icons\delete.ico"
#Resource Icon xsearch, "icons\search.ico"
#Resource Icon xall, "icons\all.ico"
#Resource Icon xsaveall, "icons\saveall.ico"
#Resource Icon xplus, "icons\listplus.ico"
#Resource Icon xup, "icons\searchup.ico"
#Resource Icon xdown, "icons\searchdown.ico"
#Resource Icon xexit, "icons\exit.ico"

%MultiLineREStyle_Wrap    = %WS_Child Or %WS_ClipSiblings Or %WS_Visible Or %ES_MultiLine Or %WS_VScroll Or _
                            %ES_AutoVScroll Or %ES_WantReturn Or %ES_NoHideSel Or %WS_TabStop Or %ES_SaveSel

Enum Equates Singular
   IDC_ComboBox = 500
   IDC_ListView
   IDC_RichEdit
   IDC_Toolbar
   IDC_StatusBar
   IDT_SaveCFN
   IDT_New
   IDT_Delete
   IDT_Search
   IDT_InsertDelimiter
   IDT_ShowAll
   IDT_AllContacts
   IDT_SaveAll
'   IDT_Sort
   IDT_Exit
   IDT_Mute
   IDT_InsertDate
   IDT_SetFocusRE
   IDT_SearchUP
   IDT_SearchDown
   IDT_NextREDown
   IDT_PrevREUp

   IDC_InputTextBox
   IDC_InputOk
   IDC_InputCancel

End Enum

Global hDlg, hRichEdit, hListView, hComboBox, hList, hDlgI As Dword
Global D() As String, REText$, SearchTerm$, SearchTerms$
Global RESearchTerm As WStringZ * 100
Global SearchDirection, BackupNumber, CFN, iSkip, Maximized, Minimized, Mute As Long

Function PBMain() As Long

   Dialog Default Font "Arial Black", 16,1
   Dialog New Pixels, 0, "gbNotes " + $Ver,,,800,800, %WS_OverlappedWindow To hDlg
   Dialog Set Icon hDlg, "xlogo"

   Control Add ComboBox, hDlg, %IDC_ComboBox,, 10,10,400,200, %CBS_Simple Or %CBS_NoIntegralHeight Or %WS_VScroll Or %WS_TabStop, %WS_Ex_ClientEdge
   Control Handle hDlg, %IDC_ComboBox To hComboBox

   Control Add ListView, hDlg, %IDC_ListView, "",10,40,400,400, %WS_TabStop Or %LVS_Report Or %LVS_ShowSelAlways Or %LVS_SingleSel, %WS_Ex_ClientEdge
   ListView Insert Column hDlg, %IDC_ListView, 1,"All Contacts",400,0
   ListView Insert Column hDlg, %IDC_ListView, 2,"Position",150,0
   Control Handle hDlg, %IDC_ListView To hListView

   LoadLibrary("msftedit.dll")
   Control Add "RichEdit50W", hDlg, %IDC_RichEdit, "", 410,10,400,400, %MultiLineREStyle_Wrap, %WS_Ex_ClientEdge
   Control Handle hDlg, %IDC_RichEdit To hRichEdit
   SendMessage hDlg, %EM_SetEventMask, 0, %ENM_SelChange
   SendMessage hRichEdit, %EM_SetEventMask, 0, %ENM_SelChange Or %ENM_Link
   SendMessage hRichEdit, %EM_AUTOURLDETECT, %True, 0

   Control Add Toolbar, hDlg, %IDC_Toolbar, "", 0,0,0,0,%CCS_NoMoveY
   ImageList New Icon 32,32,32,10 To hList
   ImageList Add Icon hList, "xsearch"  '1
   ImageList Add Icon hList, "xsave"    '2
   ImageList Add Icon hList, "xnew"     '3
   ImageList Add Icon hList, "xdelete"  '4
   ImageList Add Icon hList, "xall"     '5
   ImageList Add Icon hList, "xsaveall" '6
   ImageList Add Icon hList, "xplus"    '7
   ImageList Add Icon hList, "xexit"    '8
   ImageList Add Icon hList, "xup"      '9
   ImageList Add Icon hList, "xdown"    '10

   Toolbar Set ImageList hDlg, %IDC_Toolbar, hList, 0
   Toolbar Add Button hDlg, %IDC_Toolbar, 8, %IDT_Exit, %TbStyle_Button, "Exit"
   Toolbar Add Button hDlg, %IDC_Toolbar, 5, %IDT_AllContacts, %TbStyle_Button, "All"
   Toolbar Add Button hDlg, %IDC_Toolbar, 2, %IDT_SaveCFN, %TbStyle_Button, "Save"
   Toolbar Add Button hDlg, %IDC_Toolbar, 3, %IDT_New, %TbStyle_Button, "New"
   Toolbar Add Button hDlg, %IDC_Toolbar, 4, %IDT_Delete, %TbStyle_Button, "Delete"

   Toolbar Add Separator hDlg, %IDC_Toolbar, 50

   Toolbar Add Button hDlg, %IDC_Toolbar, 5, %IDT_ShowAll, %TbStyle_Button, "Show All"
   Toolbar Add Separator hDlg, %IDC_Toolbar, 10
   Toolbar Add Button hDlg, %IDC_Toolbar, 6, %IDT_SaveAll, %TbStyle_Button, "Save All"
   Toolbar Add Button hDlg, %IDC_Toolbar, 10, %IDT_SearchUp, %TbStyle_Button, "Up"
   Toolbar Add Button hDlg, %IDC_Toolbar, 9, %IDT_SearchDown, %TbStyle_Button, "Down"

   Control Add Statusbar, hDlg, %IDC_StatusBar, "Welcome to gbNotes!", 0,0,0,90

   Dialog Show Modal hDlg Call DlgProc
End Function

CallBack Function DlgProc() As Long
   Local pNMLV As NMListView Ptr, iRow As Long, temp$
   Select Case Cb.Msg
      Case %WM_InitDialog
         If IsFalse IsFolder("backup") Then MkDir "backup"
         Settings_INI "get"
         BuildAcceleratorTable

         LoadCRMFile       'create D() from CRM.txt
         DisplayCRMArray   'put D() into LV

         LoadComboBox
         Control Set Text hDlg, %IDC_ComboBox, SearchTerm$

         If Maximized Then Dialog Show State hDlg, %SW_Maximize  'restore Maximized state if necessary
         If Minimized Then Dialog Show State hDlg, %SW_Minimize       'restore Maximized state if necessary

         Control Set Text hDlg, %IDC_RichEdit, D(CFN)
         ListView Select hDlg, %IDC_ListView, CFN
         ListView_SetItemState hListView, CFN-1, %LVIS_Focused, %LVIS_Focused  '<--- keyboard synchronizing code
         ListView Visible hDlg, %IDC_ListView, CFN

      Case %WM_Size : Resize

      Case %WM_Command
         Select Case Cb.Ctl
'            Case %IdCancel : Dialog End hDlg
            Case %IdOk 'Enter Key
               If GetParent(GetFocus) = hComboBox Then
                  sBeep
                  Control Get Text hDlg, %IDC_ComboBox To SearchTerm$
                  Search
                  AddSearchTerm
               End If
            Case %IDC_ComboBox
               Select Case Cb.CtlMsg
                  Case %CBN_SelChange
                     'get post-selection text
                     Control Send hDlg, %IDC_ComboBox, %CB_GetCurSel, 0, 0 To iRow
                     ComboBox Get Text hDlg, %IDC_ComboBox, iRow+1 To SearchTerm$
                     Dialog Set Text hDlg, "SearchTerm = " + SearchTerm$  + "   " + Time$
                     Search
               End Select
            Case %IDT_Exit       : sBeep : Dialog End hDlg
            Case %IDT_SaveCFN    : sBeep : SaveCFN          : ST Trim$(Parse$(D(CFN),$CrLf,1)) + " saved"
            Case %IDT_New        : NewCFN                   : ST "New Contact"
            Case %IDT_Delete     : sBeep : DeleteCFN        : ST ""
            Case %IDT_Search     : sBeep : Control Get Text hDlg, %IDC_ComboBox To SearchTerm$ : Search  'ST
            Case %IDT_InsertDate : sBeep : InsertDate       : ST ""
            Case %IDT_InsertDelimiter : sBeep : InsertDelimiter : ST ""
            Case %IDT_ShowAll    : sBeep : ShowAll          : ST "All CRM.txt Loaded in Editor"
            Case %IDT_SaveAll    : sBeep : SaveAll          : ST "Editor Content saved to crm.txt"
'            Case %IDT_Sort       : sBeep : SortData
            Case %IDT_Mute       : WinBeep(275,150) : Mute Xor=1 : ST "Mute is " + IIf$(Mute,"ON","OFF")
            Case %IDT_SetFocusRE : SetFocus hRichEdit
            Case %IDT_SearchDown     : SearchDirection = 1 : FindRE : ST ""
            Case %IDT_SearchUp       : SearchDirection = 0 : FindRE
            Case %IDT_NextREDown     : sBeep : SearchDirection = 1 :    SearchText RESearchTerm, %FR_Down
            Case %IDT_AllContacts    : DisplayCRMArray

         End Select
      Case %WM_Notify
         Select Case Cb.NmId
            Case %IDC_RichEdit
               Select Case Cb.NmCode
                  Case %EN_Link : OpenLink(Cb.LParam)
               End Select
            Case %IDC_ListView
               Select Case Cb.NmCode
                  Case %LVN_ItemChanged  '%NM_Click
                     pNMLV = Cb.LParam
                     If (@pNMLV.uChanged And %LVIF_STATE) = %LVIF_STATE Then  ' if state has changed
                         If (@pNMLV.unewstate And %LVIS_SELECTED) = %LVIS_SELECTED Then
                            ListView Get Select hDlg, %IDC_ListView To iRow
                            ListView Get Text hDlg, %IDC_Listview, iRow, 2 To temp$
                            CFN = Val(temp$)
                            Control Set Text hDlg, %IDC_RichEdit, D(CFN)
                            sBeep
                         End If
                     End If
               End Select
         End Select
      Case %WM_Destroy
         SaveBackup
         Settings_INI "save"

   End Select
End Function

Sub Resize
   Local x,y,w,h,wc,hc,wt,ht As Long
   Control Get Size hDlg, %IDC_Toolbar To wt,ht
   Dialog Get Client hDlg To w,h

   Control Set Loc hDlg, %IDC_ComboBox, 10,ht+10
   Control Get Size hDlg, %IDC_ComboBox To wc,hc

   Control Set Loc hDlg, %IDC_ListView, 10,ht+hc+20
   Control Set Size hDlg, %IDC_ListView, 400, h-ht-hc-30-40

   Control Set Loc hDlg, %IDC_RichEdit, wc+15,ht+10
   Control Set Size hDlg, %IDC_RichEdit, w-wc-25,h-ht-30-30

End Sub

Function OpenLink(ByVal lpLink As Dword) As Long
   Local enlinkPtr As ENLINK Ptr, linkText As String, iReturn As Long
   Control Get Text hDlg, %IDC_RichEdit To REText$
   enlinkPtr  = lpLink
   If @enLinkPtr.Msg = %WM_LButtonUp Then
      LinkText = Mid$(REText$,@enLinkPtr.chrg.cpMin+1 To @enLinkPtr.chrg.cpMax)
      LinkText = Remove$(LinkText, Any $Cr+$Lf+$Spc)
      iReturn  = ShellExecute(hDlg, "Open", (LinkText), $Nul, $Nul, %SW_ShowNormal)
   End If
End Function

Sub LoadComboBox
   Local temp$, i As Long
   Open "searchterms.txt" For Binary As #1 : Get$ #1, Lof(1), SearchTerms$ : Close #1
   If Len(SearchTerms$) = 0 Then SearchTerms$ = "Texas" + $CrLf + "Papua New Guinea"
   SearchTerms$ = Trim$(SearchTerms$, Any $CrLf + $Spc)
   SearchTerm$ = Parse$(SearchTerms$,$CrLf,1)

   ComboBox Reset hDlg, %IDC_ComboBox
   Control Set Text hDlg, %IDC_ComboBox, SearchTerm$

   For i = 1 To Min(5,ParseCount(searchterms$,$CrLf))
      ComboBox Insert hDlg, %IDC_ComboBox, i, Parse$(SearchTerms$,$CrLf,i)
   Next i
End Sub

Sub LoadCRMFile
   Local tmp$, temp$, i,iCount As Long
   Open "crm.txt" For Binary As #1 : Get$ #1, Lof(1), temp$ : Close #1
   If Len(Trim$(temp$)) = 0 Then
      temp$ = "Gary Beene" + $CrLf + "Dallas, Texas" + $CrLf + $Delim + $CrLf + "Stuart McLachlan" + $CrLf + "Papua New Guinea"
      Open "crm.txt" For Output As #1 : Print #1, temp$; : Close #1
   End If
   iCount = ParseCount(temp$, $Delim)
   ReDim D(1 To iCount)
   Parse temp$, D(), $Delim

   For i = 1 To iCount
      D(i) = Trim$(D(i), Any $CrLf + $Spc)
      tmp$ = Parse$(D(i),$CrLf,1)
      If Trim$(tmp$) = "" Then ? "Data Issue:" + $CrLf + D(i-1)
   Next i

   ST "Records: " + Format$(iCount,"##,###")
End Sub

Sub DisplayCRMArray
   Local temp$, i As Long
   ListView Reset hDlg, %IDC_ListView
   For i = 1 To UBound(D)
      D(i) = Trim$(D(i))
      ListView Insert Item hDlg, %IDC_ListView, i,0,Parse$(D(i),$CrLf,1)
      ListView Set Text hDlg, %IDC_ListView, i,2,Format$(i)
   Next i
End Sub

Sub Search
   Local temp$, i,iCount As Long

   ListView Reset hDlg, %IDC_ListView
   For i = 1 To UBound(D)
      If InStr(LCase$(D(i)), LCase$(SearchTerm$)) Then
         Incr iCount
         ListView Insert Item hDlg, %IDC_ListView, iCount, 0, Parse$(D(i),$CrLf,1)
         ListView Set Text hDlg, %IDC_ListView, iCount, 2, Format$(i)
      End If
   Next i

   ListView Select hDlg, %IDC_ListView, 1
   ListView_SetItemState hListView, 0, %LVIS_Focused, %LVIS_Focused  '<--- keyboard synchronizing code
   ListView Set Header hDlg, %IDC_ListView, 1, "Matches: " + Format$(iCount) + "   " + $Dq + SearchTerm$ + $Dq
End Sub

Sub Settings_INI(Task$)
   Local x,y,w,h, tempz, INIFileName As WStringZ * %Max_Path, WinPla As WindowPlacement

   'set ini filename
   INIFileName = Exe.Path$ + Exe.Name$ + ".ini"    'get INI file name

   Select Case Task$
      Case "get"
         'get dialog width/height from INI file and use to set Dialog size
         GetPrivateProfileString "All", "Width", "800", w, %Max_Path, INIFileName
         GetPrivateProfileString "All", "Height", "800", h, %Max_Path, INIFileName
         Dialog Set Size hDlg,Val(w), Val(h)   'width/height

         'get dialog top/left from INI file and use to set Dialog location
         Getprivateprofilestring "All", "Left", "0", x, %Max_Path, INIFileName
         Getprivateprofilestring "All", "Top", "0",  y,  %Max_Path, INIFileName
         If IsFile(INIFileName) Then Dialog Set Loc hDlg, Val(x), Val(y)   'left/top but only once INIFileName exists

         'get value for string variables
         GetPrivateProfileString "All", "RESearchTerm", "TWC", RESearchTerm, %Max_Path, INIFileName

         'get value for numeric variables
         Getprivateprofilestring "All", "Minimized", "0", tempz, %Max_Path, INIFileName   : Minimized = Val(tempz)
         Getprivateprofilestring "All", "Maximized", "0", tempz, %Max_Path, INIFileName   : Maximized = Val(tempz)
         Getprivateprofilestring "All", "CFN", "1",       tempz, %Max_Path, INIFileName   : CFN = Val(tempz)
         Getprivateprofilestring "All", "Mute", "0",      tempz, %Max_Path, INIFileName   : Mute = Val(tempz)
         Getprivateprofilestring "All", "BackupNumber", "0",      tempz, %Max_Path, INIFileName   : BackupNumber = Val(tempz)

      Case "save"
         If IsFile(INIFileName) Then Kill INIFileName    'clear the INI file Name to remove residual entries before saving
         WinPla.Length = SizeOf(WinPla)
         GetWindowPlacement hDlg, WinPla
         WritePrivateProfileString "All", "Left", Str$(WinPla.rcNormalPosition.nLeft), INIFileName
         WritePrivateProfileString "All", "Top", Str$(WinPla.rcNormalPosition.nTop), INIFileName
         WritePrivateProfileString "All", "Width", Str$(WinPla.rcNormalPosition.nRight - WinPla.rcNormalPosition.nLeft), INIFileName
         WritePrivateProfileString "All", "Height", Str$(WinPla.rcNormalPosition.nBottom - WinPla.rcNormalPosition.nTop), INIFileName

         'save string variables
         WritePrivateProfileString "All", "RESearchTerm", RESearchTerm, INIFileName

         'save numeric variables
         Minimized = IsIconic(hDlg)
         Maximized = IsZoomed(hDlg)
         WritePrivateProfileString "All", "Minimized", Str$(Minimized), INIFileName
         WritePrivateProfileString "All", "Maximized", Str$(Maximized), INIFileName
         WritePrivateProfileString "All", "CFN", Str$(CFN), INIFileName
         WritePrivateProfileString "All", "Mute", Str$(Mute), INIFileName
         WritePrivateProfileString "All", "BackupNumber", Str$(BackupNumber), INIFileName

   End Select
End Sub

Sub SaveCFN
   Local temp$
   Control Get Text hDlg, %IDC_RichEdit To temp$

   If InStr(temp$, $Delim) Then ? "Cannot Save!" : Exit Sub

   D(CFN) = temp$
   Open "crm.txt" For Output As #1 : Print #1, Join$(D(), $CrLf + $Delim + $CrLf); : Close #1
   ListView Set Text hDlg, %IDC_ListView, CFN, 1, Parse$(D(CFN),$CrLf,1)
   SetFocus hRichEdit
End Sub

Sub ShowAll
   Control Set Text hDlg, %IDC_RichEdit, Join$(D(),$CrLf + $Delim + $CrLf)
   SendMessage hRichEdit, %EM_SetSel, 0, 0
   SetFocus hRichEdit
End Sub

Sub SaveAll
   Local temp$
   If MsgBox("Update All CRM Data?", %MB_YesNo Or %MB_IconQuestion Or %MB_TaskModal, "Update CRM") = %IdYes Then
      Control Get Text hDlg, %IDC_RichEdit To temp$
      temp$ = Trim$(temp$, Any $CrLf + " +")
      Open "crm.txt" For Output As #1
      Print #1, temp$;
      Close #1

      LoadCRMFile       'create D() from CRM.txt
      DisplayCRMArray   'put D() into LV

   End If
End Sub

Sub sBeep
   If Mute Then Exit Sub
   WinBeep(275,150)
End Sub

Sub BuildAcceleratorTable
   Local ac() As ACCELAPI, hAccelerator As Dword, c As Long  ' for keyboard accelator table values
   Dim ac(10)
   ac(c).fvirt = %FVIRTKEY Or %FCONTROL : ac(c).key   = %VK_Delete : ac(c).cmd = %IDT_Delete          : Incr c
   ac(c).fvirt = %FVIRTKEY Or %FCONTROL : ac(c).key   = %VK_D : ac(c).cmd = %IDT_InsertDate        : Incr c
   ac(c).fvirt = %FVIRTKEY Or %FCONTROL : ac(c).key   = %VK_E : ac(c).cmd = %IDT_ShowAll           : Incr c
   ac(c).fvirt = %FVIRTKEY Or %FCONTROL : ac(c).key   = %VK_F : ac(c).cmd = %IDT_SearchDown        : Incr c
   ac(c).fvirt = %FVIRTKEY Or %FCONTROL : ac(c).key   = %VK_I : ac(c).cmd = %IDT_InsertDelimiter   : Incr c
   ac(c).fvirt = %FVIRTKEY Or %FCONTROL : ac(c).key   = %VK_R : ac(c).cmd = %IDT_SetFocusRE        : Incr c
   ac(c).fvirt = %FVIRTKEY Or %FCONTROL : ac(c).key   = %VK_M : ac(c).cmd = %IDT_Mute              : Incr c
   ac(c).fvirt = %FVIRTKEY Or %FCONTROL : ac(c).key   = %VK_N : ac(c).cmd = %IDT_NextREDown        : Incr c
   ac(c).fvirt = %FVIRTKEY Or %FCONTROL : ac(c).key   = %VK_S : ac(c).cmd = %IDT_SaveCFN           : Incr c
   ac(c).fvirt = %FVIRTKEY Or %FCONTROL Or %FSHIFT : ac(c).key = %VK_F : ac(c).cmd = %IDT_SearchUp : Incr c
   ac(c).fvirt = %FVIRTKEY Or %FCONTROL Or %FSHIFT : ac(c).key = %VK_S : ac(c).cmd = %IDT_SaveAll  : Incr c
   Accel Attach hDlg, AC() To hAccelerator
End Sub

Sub InsertDelimiter
   Clipboard Reset
   Clipboard Set Text $CrLf + $Delim + $CrLf + $CrLf
   SendMessage hRichEdit, %WM_Paste, 0, 0
End Sub

Sub InsertDate
   Clipboard Reset
   Clipboard Set Text $CrLf + "Discussion: " + Date$ + "  " + Time$ + "  ---------" + $CrLf + $CrLf
   SendMessage hRichEdit, %WM_Paste, 0, 0
End Sub

'Sub SortData
'   Array Sort D(1)
'   Open "crm.txt" For Output As #1 : Print #1, Join$(D(), $CrLf + $Delim + $CrLf); : Close #1
'   LoadCRMFile       'create D() from CRM.txt
'   DisplayCRMArray   'put D() into LV
'End Sub

Sub AddSearchTerm
   Local i As Long

   'If SearchTerm alread in use, do nothing
   For i = 1 To Min(5,ParseCount(SearchTerms$,$CrLf))
      If LCase$(Parse$(SearchTerms$,$CrLf,i)) = LCase$(SearchTerm$) Then Exit Sub
   Next i

   'If new search term, add it top of the list of SearchTerms$
   SearchTerms$ = SearchTerm$ + $CrLf + SearchTerms$
   SearchTerms$ = Trim$(SearchTerms$, Any $CrLf + $Spc)
   Open "searchterms.txt" For Output As #1 : Print #1, SearchTerms$; : Close #1

   LoadComboBox
End Sub

Sub SaveBackup
   Local temp$
   temp$ = Time$
   Replace  ":" With "." In temp$
   Incr BackupNumber
   Open "backup\crm_" + Format$(BackupNumber) + "_" + Date$ + "_" + temp$ + ".txt" For Output As #1
   Print #1, Join$(D(), $CrLf + $Delim + $CrLf); : Close #1
End Sub

Sub FindRE
   GetRESearchTerm
   SearchText RESearchTerm, %FR_Down
End Sub

Function SearchText(ByVal sTextToSearchFor As WString, SearchDirection As Long) As Long
Local  FindTextText         As FINDTEXTEX
Local  NextMatch, SelStart, SelEnd As Long

SendMessage(hRichEdit, %EM_GETSEL, VarPtr(SelStart), VarPtr(SelEnd))
FindTextText.lpStrText = StrPtr(sTextToSearchFor)

If SearchDirection = 1 Then
   FindTextText.chrg.cpMin = SelEnd + 1 'Search from current position
   FindTextText.chrg.cpMax = -1         '- till the end
Else '%FR_UP
   FindTextText.chrg.cpMin = SelStart   'Search from current position
   FindTextText.chrg.cpMax = 0          '- up to the start
End If

NextMatch = SendMessage(hRichEdit, %EM_FINDTEXTEX, SearchDirection, VarPtr(FindTextText)) 'Return next match or -1 for no more

If NextMatch = -1 Then WinBeep(250,300) :Exit Function
SendMessage(hRichEdit, %EM_SETSEL, FindTextText.chrgText.cpMin, FindTextText.chrgText.cpMax)

End Function

Sub GetRESearchTerm
   Dialog New Pixels, 0, "Search RE",,,200,80, %WS_SysMenu To hDlgI
   Dialog Set Icon hDlgI, "xsearch"
   Control Add TextBox, hDlgI, %IDC_InputTextBox,RESearchTerm, 20,10,160,30
   Control Add Button, hDlgI, %IDC_InputOk, "Ok", 20,50,50,25
   Control Add Button, hDlgI, %IDC_InputCancel, "Cancel", 90,50,90,25
   RESearchTerm = ""
   Dialog Show Modal hdlgI Call hDlgIProc
End Sub

CallBack Function hDlgIProc() As Long
   Select Case Cb.Msg
      Case %WM_Command
         Select Case Cb.Ctl
            Case %IdOk    , %IDC_InputOk     : Control Get Text hDlgI, %IDC_InputTextBox To RESearchTerm : Dialog End hDlgI
            Case %IdCancel, %IDC_InputCancel : Dialog End hDlgI
         End Select
   End Select
End Function

Sub NewCFN
   ReDim Preserve D(1 To UBound(D)+1)
   D(UBound(D)) = "New Entry"
   CFN = UBound(D)

   ListView Insert Item hDlg, %IDC_ListView, CFN, 0, "New Entry " + $CrLf + "Date: " + Date$
   ListView Set Text hDlg, %IDC_ListView, CFN, 2, Format$(CFN)

   ListView Select hDlg, %IDC_ListView, CFN
   ListView_SetItemState hListView, CFN-1, %LVIS_Focused, %LVIS_Focused  '<--- keyboard synchronizing code
   ListView Visible hDlg, %IDC_ListView, CFN

   Control Set Text hDlg, %IDC_RichEdit, D(CFN)
   SetFocus hRichEdit
End Sub

Sub DeleteCFN

   If GetFocus = hListView Then
      'list of all item
      If UBound(D) = 1 Then D(1) = "New Entry" : Exit Sub
      ListView Delete Item hDlg, %IDC_ListView, CFN

      Array Delete D(CFN)
      ReDim Preserve D(UBound(D)-1)
      If CFN > UBound(D) Then CFN = CFN - 1

      Open "crm.txt" For Output As #1 : Print #1, Join$(D(), $CrLf + $Delim + $CrLf); : Close #1

      LoadCRMFile       'create D() from CRM.txt
      DisplayCRMArray   'put D() into LV

   Else
      ? "Delete Allowed Only on Main List"
   End If

End Sub

Sub ST(temp$)
   Statusbar Set Text hDlg, %IDC_StatusBar, 1, 0, temp$
End Sub



Attached Files Thumbnail(s)
   
Print this item

  The Future
Posted by: George Bleck - 03.09.2025, 07:28 PM - Forum: Suggestions and discussion about PUMP - Replies (84)

I'd like to start by thanking Albert for hosting our community's new home. This platform has been crucial in bringing our group together and facilitating knowledge-sharing, which would be difficult to achieve without it.

That being said, I think we're still vulnerable to disruptions if something happens to Albert or the current setup. To address this, I propose that we establish a committee to oversee the creation of a more permanent and stable home for our community. This would involve securing reliable hosting, maintenance, and data management, including the ability to export our databases if needed.

To ensure the committee represents our diverse international community, I suggest we aim for broad representation of at least 3-5 members. I know we could nominate some individuals who have demonstrated long-term commitment, but it's essential that potential committee members volunteer and are voted in to confirm their willingness and community acceptance to take on this responsibility.

I'm personally willing to contribute financially to partially support a hosted platform managed by an elected committee, even if I'm not part of the committee myself. I could (and should) not do it alone though, so I expect other donations to help meet the appropriate level of support we would look to achieve.

Establishing bylaws, rules, and other governing documents would be an important next step, which I believe the committee should handle once it's formed.

Thoughts on this proposal?

Print this item

Photo gbLocator
Posted by: Gary Beene - 03.09.2025, 07:28 AM - Forum: Source Code Library - Replies (4)

Howdy!

I've continued working on gbLocator, which provides a user interface to Everything for blind and low vision users.  I'm up to v4.2.

Web Site: https://newvisionconcepts.com/gblocator/gblocator.htm
Installation File:   https://garybeene.com/files/gblocator_setup.exe
Source Code File:   https://garybeene.com/files/gblocator.zip

You'll recall that I spoke with David (Everything author) and he encouraged me to post something on his forum. I've just done that and hope to get some feedback from folks on his forum.

Here are gbLocator images - one with the toolbar showing (low vision users) and one with the toolbar hidden (blind users).

   

... does this forum allow inline images or just thumbnails that are links to the full size image?

Print this item

  gbThreads
Posted by: Gary Beene - 03.09.2025, 03:01 AM - Forum: PowerBASIC for Windows - Replies (14)

With the guidance from the other thread on accessing the forum, I was able to update my gbThreads files through today.

These links contain just the file bigthread.htm. Use it to replace the one you already have.

https://garybeene.com/files/bigthread.zip   140MB

https://garybeene.com/files/bigthread.7z   90MB

I had to manually walk through the forums to get a list of threads that have changed since the last update so please let me know if I missed anything!

Print this item