Comboboxgadget mit autosave, autocomplete, Drop an Cursorpos
Verfasst: 02.03.2018 15:38
Drop an Cursorpos meint nicht die Caretposi sondern die Mausposi
autocomplete ist nur eine kleine simple Funktion ohne Vorschau
Fehlermeldungen willkommen
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