autocomplete ist nur eine kleine simple Funktion ohne Vorschau
Fehlermeldungen willkommen
Code: Alles auswählen
; Windows 10 x64 - PB 5.60 - HJBremer
DeclareModule Combobox  
   Declare.i ComboboxAddItem(gadget, text$)   
   Declare.i ComboboxGadgetX(gadget, sp, ze, br, hh, flag = 0)  
   
   EnumerationBinary
      #Combobox_Time
      ;weitere Flags
   EndEnumeration      
   
EndDeclareModule
Module Combobox   
   EnableExplicit
      
   Structure COMBOBOXINFO
      cbSize.l
      rcItem.RECT 
      rcButton.RECT 
      stateButton.l
      hwndCombo.i
      hwndItem.i
      hwndList.i
   EndStructure 
   
   Structure mycombo
      pbnr.i
      pbid.i
      menu.i
      flag.i
      oldproc_gadget.i
      oldproc_listbox.i
      oldproc_editfield.i
      info.COMBOBOXINFO
   EndStructure
   
   Enumeration 10001    ;Menuitems für PopUpMenu ( Rightclick im Editfeld )
      #menuitem_delete
      #menuitem_copy 
      #menuitem_paste  
      #menuitem_notepad 
      #menuitem_loaditems
   EndEnumeration
   
   #propname_cbmem = "combobox2_cbmem"     ;für Callbacks
   #propname_lfdnr = "combobox2_lfdnr"     ;nur für ComboboxFilename
   
   Macro ComboboxFilename(pbnr)
      GetFilePart(#PB_Compiler_Filename, 1) + "_cb_" + GetProp_(GadgetID(pbnr), #propname_lfdnr)  + ".txt"
   EndMacro
   
   Procedure.i GetGadgetWindow(pbnr)
      ;ermittelt in welchem Fenster sich ein Gadget befindet, wichtig für BindEvent()
      ;Repeat muß sein, falls Gadget in einem Container steckt, sonst reicht nur GetProp_()
      ;von jassing - http://forums.purebasic.com/english/viewtopic.php?f=13&t=61255
      
      Protected window, pbid = GadgetID(pbnr)
      
      Repeat   
         window = GetProp_(GetParent_(pbid), "PB_WindowID")
         If window = 0
            pbid = GetParent_(pbid)
         Else
            ProcedureReturn window - 1
         EndIf
      Until IsWindow(window)
      
   EndProcedure
   
   Procedure.i ComboboxLoadItems(pbnr)
      ;schreibt die Daten einer Datei in die Comboboxliste
      
      Protected text$      
      Protected datnr = OpenFile(#PB_Any, ComboboxFilename(pbnr))
      
      If datnr         
         While Eof(datnr) = 0          
            text$ = ReadString(datnr)   
            SendMessage_(GadgetID(pbnr), #CB_ADDSTRING, 0, @text$)
         Wend   
         CloseFile(datnr)         
      EndIf
      
   EndProcedure
   
   Procedure.i ComboboxSaveItems(pbnr)
      ;schreibt die Daten einer Comboboxliste in eine Datei
      
      Protected text$, i   
      Protected count = CountGadgetItems(pbnr) - 1                   ; -1 weil ab null gezählt wird
      Protected datnr = CreateFile(#PB_Any, ComboboxFilename(pbnr))
      
      If datnr         
         For i = 0 To count            
            text$ = GetGadgetItemText(pbnr, i)            
            WriteStringN(datnr, text$)
         Next 
         CloseFile(datnr)         
      EndIf
      
   EndProcedure
   
   Procedure.i ComboboxAddItem(pbnr, text$)
      ;speichert einen neuen Eintrag am Ende der Datei und fügt diesen in die Liste ein      
      ;Hinweis: #CB_FINDSTRINGEXACT liefert nur 0 oder -1 wenn Combobox #CBS_SORT hat.
      
      If SendMessage_(GadgetID(pbnr), #CB_FINDSTRINGEXACT, -1, @text$) = #CB_ERR ; wenn -1 = nicht gefunden
         SendMessage_(GadgetID(pbnr), #CB_ADDSTRING, 0, @text$)
         
         Protected datnr = OpenFile(#PB_Any, ComboboxFilename(pbnr)) 
         
         If datnr
            FileSeek(datnr, Lof(datnr))
            WriteStringN(datnr, text$)
            CloseFile(datnr) 
         EndIf
      EndIf
      
   EndProcedure
   
   Procedure.i ComboBoxEventEditfield(hwnd, msg, wParam, lParam)
      ;Callback nur für Editfield eines Comboboxgadgets 
      
      Protected *mc.mycombo = GetProp_(hwnd, #propname_cbmem) 
      Protected oldproc = *mc\oldproc_editfield      
      Protected text$, lg, found
      Protected startpos, endpos                ;für #EM_GETSEL
      
      Static starttime, endtime, tripleflag     ;für Tripleclick Abfrage, muß Static sein
      
      Select msg            
         Case #WM_NCDESTROY: RemoveProp_(hwnd, #propname_cbmem)
            
         Case #WM_HELP
            text$ = "Return = speichern / TAB oder Mausclick = NICHT speichern" + #LF$
            text$ + "Rightclick in Editfeld = PopUpMenu" + #LF$
            text$ + "Rightclick in Liste = löschen / Item wird ins Clipboard kopiert" + #LF$ + #LF$
            text$ + "bei Textdrop wird an der Cursorspitze eingefügt" + #LF$ + #LF$
            
            MessageRequester("Info", text$)
            ProcedureReturn 0
         
         Case #WM_RBUTTONDOWN    ;Right click on ComboBox 
            DisplayPopupMenu(*mc\menu, hwnd)
            
         Case #WM_COMMAND        ;Menuitems vom PopUpMenu
            SendMessage_(hwnd, #EM_GETSEL, @startpos, @endpos) ;markierten Text ermitteln 
            Select wParam
               Case #menuitem_delete 
                  If startpos = endpos             ;nix markiert also alles löschen
                     SetGadgetText(*mc\pbnr, "")
                  Else                             ;sonst markierten Text mit Leertext ersetzen
                     text$ = ""
                     SendMessage_(hwnd, #EM_REPLACESEL, 0, @text$) 
                  EndIf
                  
               Case #menuitem_copy
                  text$ = GetGadgetText(*mc\pbnr)
                  If startpos <> endpos             ;nur markiertes kopieren, sonst alles
                     text$ = Mid(text$, startpos + 1, endpos - startpos)
                  EndIf
                  SetClipboardText(text$)
                  
               Case #menuitem_paste
                  text$ = GetClipboardText()
                  SendMessage_(hwnd, #EM_REPLACESEL, 0, @text$) 
                  
               Case #menuitem_notepad: RunProgram("notepad", ComboboxFilename(*mc\pbnr), "")
               Case #menuitem_loaditems: ClearGadgetItems(*mc\pbnr): ComboboxLoadItems(*mc\pbnr)   
            EndSelect
            
         Case #WM_KILLFOCUS   ;hier weitere Auswertungen der Eingabe z.B. für Time
            
            text$ = GetGadgetText(*mc\pbnr): lg = Len(text$)
            
            ;für Flag Abfrage immer Bool benutzen
            
            If Bool(*mc\flag & #Combobox_Time) ;Uhrzeit
               If text$
                  If ParseDate("%hh:%ii", text$) = -1
                     ;ändere falsche Zeit
                     Select lg
                        Case 1: text$ = "0" + text$ + ":00"
                        Case 2: text$ + ":00"
                        Case 3: text$ = Left(text$,2) + ":" + Right(text$,1) + "0"
                        Case 4: text$ = Left(text$,2) + ":" + Right(text$,2)
                     EndSelect
                     ReplaceString(text$, " ", "0", #PB_String_InPlace)
                     If ParseDate("%hh:%ii", text$) = -1
                        text$ = FormatDate("%hh:00", Date())  
                     EndIf
                  EndIf
                  ;setze und speicher geänderte Zeit
                  SetGadgetText(*mc\pbnr, text$) 
                  ComboboxAddItem(*mc\pbnr, text$)
               EndIf
            EndIf
            
         Case #WM_CHAR  ;Eingabecheck für ein Zeichen + Combobox Autocomplete 
            
            ;für Flag Abfrage immer Bool benutzen
            If Bool(*mc\flag & #Combobox_Time) ;nur Zeichen für Uhrzeit erlaubt
               Select wParam
                  Case 48 To 58        ;0-9 und :
                  Case 8, 13, 32       ;Backspace + Return + Space zulassen         
                  Default: wparam = 0
               EndSelect               
            EndIf
            
            ;Autocomplete nach einem Vorbild aus dem englischem Forum, nur etwas kleiner !
            SendMessage_(hwnd, #EM_GETSEL, @startpos, 0)               
            text$ = Left(GetGadgetText(*mc\pbnr), startpos) + Chr(wParam)  ; bisheriger Text + Eingabe
            found = SendMessage_(*mc\pbid, #CB_FINDSTRING, -1, @text$)     ; -1 = nicht gefunden               
            If found > -1           
               SetGadgetState(*mc\pbnr, found)                    ;PB markiert Text in der Liste + setzt Text ein 
               SendMessage_(hwnd, #EM_SETSEL, startpos + 1, -1)   ;nun Rest markieren, -1 ist endposi
               ProcedureReturn 0                                  ;ProcedureReturn 0 muß sein 
            EndIf
            
            ; hier beginnt Tripleclick Abfrage, kann so auch in einem StringGadget benutzt werden
            
         Case #WM_LBUTTONDBLCLK
            starttime = GetTickCount_()               ;PB markiert jetzt ein Wort bei #WM_LBUTTONDBLCLK            
                                             
         Case #WM_LBUTTONDOWN                         ;beim 3.Click entfernt PB Markierung wieder, das ist so !
            If starttime
               endtime = GetTickCount_() - starttime  ;Zeit zwischen DBLClk und 3.Click
               starttime = 0
               If endtime < GetDoubleClickTime_()     ;dann trippelclick flag setzen
                  tripleflag = 1                      ;auch vierfach wenn man schnell ist
               EndIf                                  ;aber das ist egal
            EndIf
            
         Case #WM_LBUTTONUP
            If tripleflag                             ;Pb ist fertig mit entfernen der Markierung,
               tripleflag = 0                         ;nun können wir die ganze Zeile markieren               
               SendMessage_(hwnd, #EM_SETSEL, 0, -1)  ; -1 ist endposi kann auch 1000 oder so sein 
            EndIf      
               
      EndSelect
      
      ProcedureReturn CallWindowProc_(oldproc, hwnd, msg, wParam, lParam)
   EndProcedure
   
   Procedure.i ComboBoxEventListbox(hwnd, msg, wParam, lParam)
      ;Callback nur für Listbox eines Comboboxgadgets für Item löschen
      
      Protected *mc.mycombo = GetProp_(hwnd, #propname_cbmem) 
      Protected item, oldproc = *mc\oldproc_listbox
      
      Select msg            
         Case #WM_NCDESTROY: RemoveProp_(hwnd, #propname_cbmem)
            
         Case #WM_RBUTTONDOWN    ;Right click on ComboBox-Listitem löscht Item  
            item = SendMessage_(hwnd, #LB_GETCURSEL, 0, 0)
            SetClipboardText(GetGadgetItemText(*mc\pbnr, item))   ;vorm Löschen ins Clipboard
            SendMessage_(hwnd, #LB_DELETESTRING, item, 0)         ;Löschen     
            ComboboxSaveItems(*mc\pbnr)                           ;alle Items speichern
      EndSelect
      
      ProcedureReturn CallWindowProc_(oldproc, hwnd, msg, wParam, lParam)
   EndProcedure
   
   Procedure.i ComboboxEvent(hwnd, msg, wParam, lParam)
      ;Callback nur für Comboboxgadget für speichern
      
      Protected *mc.mycombo = GetProp_(hwnd, #propname_cbmem) 
      Protected oldproc = *mc\oldproc_gadget
      Protected text$, ok 
      
      Select msg 
         Case #WM_NCDESTROY: 
            RemoveProp_(hwnd, #propname_cbmem)
            RemoveProp_(hwnd, #propname_lfdnr)
 
         Case 343         
            ;wahrscheinlich #CB_GETDROPPEDSTATE 
            ;wird ausgelöst durch Cursor up + down + ! Taste Return !
            If GetKeyState_(#VK_RETURN) > 1
               PostMessage_(hwnd, #WM_KEYDOWN, #VK_TAB, 0) 
               ;Eingabe speichern mit Return
               text$ = GetGadgetText(*mc\pbnr)
               If Len(text$)
                  ok = 1
                  If Bool(*mc\flag & #Combobox_Time)
                     If ParseDate("%hh:%ii", text$) = -1: ok = 0: EndIf
                  EndIf
                  If ok: ComboboxAddItem(*mc\pbnr, text$): EndIf
                EndIf 
            EndIf
            
      EndSelect
      
      ProcedureReturn CallWindowProc_(oldproc, hwnd, msg, wParam, lParam)
   EndProcedure
   
   Procedure.i ComboboxDropPosi(pbnr, x)
      ;ermittelt die Anzahl Zeichen vor der X Position
      Protected size.size, dc = GetDC_(GadgetID(pbnr))
      Protected fontid = GetGadgetFont(pbnr)
      Protected text$ = GetGadgetText(pbnr), lg = Len(text$), j
      
      SelectObject_(dc, fontid)
      For j = 1 To lg         
         GetTextExtentPoint32_(dc, Left(text$, j), j, size): ;Debug size\cx               
         If size\cx > x: Break: EndIf
      Next    
      
      ProcedureReturn j - 1
   EndProcedure 
   
   Procedure.i ComboboxDropEvent()
      ;fügt Text an Cursorposi im Editfeld ein 
      
      Protected pbnr = EventGadget()
      Protected startpos, text$
      Protected *mc.mycombo = GetProp_(GadgetID(pbnr), #propname_cbmem) 
      
      text$ = EventDropText()
      startpos = ComboboxDropPosi(pbnr, EventDropX() - 2)               ; Rand abziehen 2 oder 3     
      SendMessage_(*mc\info\hwndItem, #EM_SETSEL, startpos, startpos)            
      SendMessage_(*mc\info\hwndItem, #EM_REPLACESEL, 0, @text$) 
    
   EndProcedure
   
   Procedure.i ComboboxGadgetX(gadget, sp, ze, br, hh, flag = 0)
      ;erstellt ComboBoxGadget, subclassing und hinterlegt lfdnr für Filename      
      
      Static lfdnr     
      Protected nr, pbid, pbnr, window, *mc.mycombo = AllocateMemory(SizeOf(mycombo))   ;Mem erstellen 
      
      Protected pbflag = #PB_ComboBox_Editable|#CBS_SORT
      
      If Bool(flag & #PB_ComboBox_LowerCase): pbflag | #PB_ComboBox_LowerCase: EndIf 
      If Bool(flag & #PB_ComboBox_UpperCase): pbflag | #PB_ComboBox_UpperCase: EndIf 
      
      nr = ComboBoxGadget(gadget, sp, ze, br, hh, pbflag) 
      
      If gadget = #PB_Any
         pbid = GadgetID(nr)
         pbnr = nr
      Else
         pbid = nr
         pbnr = gadget
      EndIf
      
      SendMessage_(pbid, #CB_SETMINVISIBLE, 15, 0)    ;ab 15 Einträge Scrollbalken
            
      EnableGadgetDrop(pbnr, #PB_Drop_Text, #PB_Drag_Copy) 
      window = GetGadgetWindow(pbnr)  ;Combobox gehört zu welchem Fenster ?
                                      ;wird window weggelassen, gibt es für jedes Drop ein Aufruf der jeweiligen Procs
      BindEvent(#PB_Event_GadgetDrop, @ComboboxDropEvent(), window, pbnr)
      
      lfdnr + 1
      
      *mc\info\cbSize = SizeOf(COMBOBOXINFO): GetComboBoxInfo_(pbid, *mc\info)
      *mc\pbid = pbid
      *mc\pbnr = pbnr
      *mc\flag = flag
      *mc\oldproc_gadget = SetWindowLongPtr_(pbid, #GWL_WNDPROC, @ComboboxEvent())      
      *mc\oldproc_listbox = SetWindowLongPtr_(*mc\info\hwndList, #GWL_WNDPROC, @ComboBoxEventListbox())
      *mc\oldproc_editfield = SetWindowLongPtr_(*mc\info\hwndItem, #GWL_WNDPROC, @ComboBoxEventEditfield())
      
      *mc\menu = CreatePopupMenu(#PB_Any)          ;verhindert nerviges Windowsmenu
      MenuItem(#menuitem_delete, "Löschen")
      MenuItem(#menuitem_copy,   "Kopieren")
      MenuItem(#menuitem_paste,  "Einfügen")
      MenuBar()
      MenuItem(#menuitem_notepad, "Liste in Editor laden")
      MenuItem(#menuitem_loaditems, "Liste neu laden")
      
      SetProp_(pbid, #propname_cbmem, *mc)     
      SetProp_(pbid, #propname_lfdnr, lfdnr)  
      SetProp_(*mc\info\hwndList, #propname_cbmem, *mc) 
      SetProp_(*mc\info\hwndItem, #propname_cbmem, *mc) 
      
      ;Debug *mc\info\hwndCombo : Debug pbid    ;Kontrolle ob Structur korrect ist, Werte müssen gleich sein
      
      ComboboxLoadItems(pbnr)
      
      ProcedureReturn nr      
   EndProcedure
   
EndModule
UseModule Combobox
CompilerIf #PB_Compiler_IsMainFile
   
   Enumeration 10
      #mainwindow
      #combo1
      #combo2
      #combo3  
   EndEnumeration
      
   LoadFont(1, "Consolas", 11)
   SetGadgetFont(#PB_Default, FontID(1))
   
   OpenWindow(#mainwindow, 0, 0, 640, 300, "Demo", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
   
   Define sp = 20, ze = 20, br = 300, hh = 24
   
   ComboboxGadgetX(#combo1, sp, ze, br, hh) : ze + 30          
   ComboboxGadgetX(#combo2, sp, ze, br, hh, #PB_ComboBox_UpperCase) : ze + 30  
   ComboboxGadgetX(#combo3, sp, ze, br, hh, #Combobox_Time) : ze + 30
   
   ComboboxAddItem(#combo1, "Kugel") 
   ComboboxAddItem(#combo1, "Computer") 
   
   SendMessage_(GadgetID(#combo1), #CB_SETCUEBANNER, 0, @"F1 = Info")
   SendMessage_(GadgetID(#combo2), #CB_SETCUEBANNER, 0, @"Uppercase")
   SendMessage_(GadgetID(#combo3), #CB_SETCUEBANNER, 0, @"Time")
       
   Repeat
      Event = WaitWindowEvent()
      
      Select Event
         Case #PB_Event_CloseWindow
            Select EventWindow()
               Case #mainwindow: Break
                  
            EndSelect
            
         Case #PB_Event_Gadget
            Select EventGadget()
               Case #combo1
                  
            EndSelect
            
      EndSelect
   ForEver  
   
CompilerEndIf

