Re: Editierbares ListIconGadget
Verfasst: 07.04.2016 10:03
Vor 8 Jahren hab ich gerade noch mit Windows gearbeitet, kurz danach nicht mehr. Ich hab keine Ahnung mehr, wie das per Windows-API geht.
Code: Alles auswählen
Case #WM_MOUSEMOVE
If *li\hedit
UpdateWindow_(*li\hedit)
EndIf
Code: Alles auswählen
Case #WM_MOUSEMOVE
If *li\hedit
InvalidateRect_(*li\hedit, 0, 1)
UpdateWindow_(*li\hedit)
EndIf
Code: Alles auswählen
EnableExplicit
Prototype ListIconGadget_Request(GadgetID.l, row.l, column.l, msg.l, *out, *userdata)
Enumeration
;Msg
#ListIconGadget_Click
#ListIconGadget_EditChange
#ListIconGadget_EditEscape
;Return für *out
#ListIconGadget_NoOpen
#ListIconGadget_Edit
#ListIconGadget_List
EndEnumeration
;Hier kann man maximal 32768 angeben. Mehr Zeichen unterstützt das Edit Control nicht.
#ListIconGadget_MaxChars = 1024
Structure ListIconGadget_S
id.l
*hnd
msg.l[4]
direct.l
*oldCB
*hedit
item.l
subitem.l
type.l
r.RECT
*edit_oldCb
*requestCB.ListIconGadget_Request
*userdata
EndStructure
;Der Callback für das ListIconGadget
Procedure ListIconGadget_CB(hwnd.l, Msg.l, wParam.l, lParam.l)
Protected *li.ListIconGadget_S = GetWindowLong_(hwnd, #GWL_USERDATA)
Protected hit.LVHITTESTINFO, itemdata.LVITEM, open.l, *out, *c.Character, char.c, *begin
Protected minpos.l, maxpos.l, cpp.l, delta.l, i.l, length.l, type.l, client.RECT
If hwnd = *li\hnd
Select Msg
Case #WM_MOUSEMOVE
If *li\hedit And *li\type = #ListIconGadget_List
InvalidateRect_(*li\hedit, 0, 1)
UpdateWindow_(*li\hedit)
EndIf
Case *li\msg[0], *li\msg[1], *li\msg[2], *li\msg[3]
;Case #WM_LBUTTONDOWN, #WM_LBUTTONDBLCLK
;Mauskoordinaten relativ zum Gadget
hit\pt\x = lparam & $FFFF
hit\pt\y = (lparam >> 16) & $FFFF
;Zeile und Spalte ermitteln
SendMessage_(hwnd, #LVM_SUBITEMHITTEST, 0, hit)
If hit\iItem >= 0
If *li\requestCB
type = *li\requestCB(*li\id, hit\iItem, hit\iSubitem, #ListIconGadget_Click, @*out, *li\userdata)
Else
type = #ListIconGadget_Edit
EndIf
;Koordinaten des Eintrags errechnen
*li\r\left = #LVIR_BOUNDS
*li\r\top = hit\iSubItem
SendMessage_(hwnd, #LVM_GETSUBITEMRECT, hit\iItem, *li\r)
If hit\iSubItem = 0 ;Bei erster Spalte muss die Breite korrigiert werden
*li\r\left - 2
*li\r\right = *li\r\left + SendMessage_(hwnd, #LVM_GETCOLUMNWIDTH, 0, 0) + 2
EndIf
;Text des Eintrags auslesen
itemdata\iSubItem = hit\iSubItem
itemdata\pszText = AllocateMemory((#ListIconGadget_MaxChars + 1) * SizeOf(Character))
itemdata\cchTextMax = #ListIconGadget_MaxChars
SendMessage_(hwnd, #LVM_GETITEMTEXT, hit\iItem, itemdata)
;Falls schon Edit Control/ListBox besteht, dann schließen und Änderungen übernehmen
If *li\hedit
Select *li\type
Case #ListIconGadget_Edit
SendMessage_(*li\hedit, #WM_KILLFOCUS, #VK_RETURN, 0)
Case #ListIconGadget_List
SendMessage_(*li\hedit, #WM_KILLFOCUS, #VK_ESCAPE, 0)
EndSelect
If Not *li\direct : ProcedureReturn 0 : EndIf
EndIf
Select type
Case #ListIconGadget_NoOpen
ProcedureReturn 0
;do nothing
Case #ListIconGadget_Edit ;{
*li\type = type
*li\r\left + 1
*li\r\bottom - 1
;neues StringGadget erstellen
*li\hedit = CreateWindow_("edit", 0, #ES_AUTOHSCROLL | #WS_CHILD | #ES_LEFT, *li\r\left, *li\r\top, *li\r\right - *li\r\left - 2, *li\r\bottom - *li\r\top, *li\hnd, 0, GetWindowLong_(*li\hnd, #GWL_HINSTANCE), 0)
If *li\hedit
;Text zuweisen
SendMessage_(*li\hedit, #WM_SETFONT, SendMessage_(hwnd, #WM_GETFONT, 0, 0), 0)
SendMessage_(*li\hedit, #EM_REPLACESEL, #False, itemdata\pszText)
*li\item = hit\iItem
*li\subitem = hit\iSubItem
SetWindowLong_(*li\hedit, #GWL_USERDATA, *li)
*li\edit_oldCB = SetWindowLong_(*li\hedit, #GWL_WNDPROC, @ListIconGadget_CB())
ShowWindow_(*li\hedit, #SW_SHOW)
SetFocus_(*li\hedit)
SetWindowLong_(*li\hedit, #GWL_WNDPROC, @ListIconGadget_CB()) ; Farbanpassung initiieren
EndIf
If itemdata\pszText : FreeMemory(itemdata\pszText) : EndIf
ProcedureReturn 0 ;}
Case #ListIconGadget_List ;{
*li\type = type
*li\r\left + 1
*li\r\bottom = *li\r\top + (*li\r\bottom - *li\r\top) * 6 - 1
GetClientRect_(hwnd, client)
If *li\r\bottom > client\bottom
*li\r\top - (*li\r\bottom - client\bottom)
*li\r\bottom = client\bottom
EndIf
;neue ListBox erstellen
*li\hedit = CreateWindow_("listbox", 0, #WS_VSCROLL | #WS_CHILD | #WS_BORDER, *li\r\left, *li\r\top, *li\r\right - *li\r\left - 2, *li\r\bottom - *li\r\top, *li\hnd, 0, GetWindowLong_(*li\hnd, #GWL_HINSTANCE), 0)
If *li\hedit
SendMessage_(*li\hedit, #WM_SETFONT, SendMessage_(hwnd, #WM_GETFONT, 0, 0), 0)
*li\item = hit\iItem
*li\subitem = hit\iSubItem
SetWindowLong_(*li\hedit, #GWL_USERDATA, *li)
*li\edit_oldCB = SetWindowLong_(*li\hedit, #GWL_WNDPROC, @ListIconGadget_CB())
ShowWindow_(*li\hedit, #SW_SHOW)
SetFocus_(*li\hedit)
If *out
*c = *out
char = *c\c
*c + SizeOf(Character)
*begin = *c
i = 0
Repeat
If *c\c = char
If *c = *begin
Break
Else
*c\c = 0
SendMessage_(*li\hedit, #LB_ADDSTRING, 0, *begin)
*begin = *c + SizeOf(Character)
i + 1
EndIf
EndIf
*c + SizeOf(Character)
ForEver
FreeMemory(*out)
SendMessage_(*li\hedit, #LB_SELECTSTRING, -1, itemdata\pszText)
EndIf
SetWindowLong_(*li\hedit, #GWL_WNDPROC, @ListIconGadget_CB()) ; Farbanpassung initiieren
EndIf
If itemdata\pszText : FreeMemory(itemdata\pszText) : EndIf
ProcedureReturn 0 ;}
EndSelect
EndIf
Case #WM_PAINT ;{
If *li\hedit
;Koordinaten des Eintrags errechnen
*li\r\left = #LVIR_BOUNDS
*li\r\top = *li\subitem
SendMessage_(hwnd, #LVM_GETSUBITEMRECT, *li\item, *li\r)
If *li\subitem = 0 ;Bei erster Spalte muss die Breite korrigiert werden
*li\r\left - 2
*li\r\right = *li\r\left + SendMessage_(hwnd, #LVM_GETCOLUMNWIDTH, 0, 0) + 2
EndIf
Select *li\type
Case #ListIconGadget_Edit
*li\r\left + 1
*li\r\bottom - 1
MoveWindow_(*li\hedit, *li\r\left, *li\r\top, *li\r\right - *li\r\left - 2, *li\r\bottom - *li\r\top, 0)
Case #ListIconGadget_List
*li\r\left + 1
*li\r\bottom = *li\r\top + (*li\r\bottom - *li\r\top) * 6 - 1
GetClientRect_(hwnd, client)
If *li\r\bottom > client\bottom
*li\r\top - (*li\r\bottom - client\bottom)
*li\r\bottom = client\bottom
EndIf
MoveWindow_(*li\hedit, *li\r\left, *li\r\top, *li\r\right - *li\r\left - 2, *li\r\bottom - *li\r\top, 0)
EndSelect
InvalidateRect_(*li\hedit, 0, 0)
;Komplette erste Spalte neuzeichnen
client\top = 0
client\left = *li\r\left
client\bottom = *li\r\bottom
client\right = *li\r\right
InvalidateRect_(hwnd, client, 0)
EndIf ;}
;Aufpassen, dass das Edit Control nicht nach oben oder unten aus dem Fenster rutscht, links und rechts ist egal (vorerst)
Case #WM_VSCROLL ;{
If *li\hedit
cpp = SendMessage_(hwnd, #LVM_GETCOUNTPERPAGE, 0, 0)
minpos = SendMessage_(hwnd, #LVM_GETTOPINDEX, 0, 0)
maxpos = minpos + cpp
Select wParam & $FFFF
Case #SB_THUMBPOSITION, #SB_THUMBTRACK
minpos = (wParam >> 16) & $FFFF
If *li\item < minpos Or *li\item >= minpos + cpp : ProcedureReturn 0: EndIf
Case #SB_LINEDOWN : If *li\item < minpos + 1 : ProcedureReturn 0 : EndIf
Case #SB_LINEUP : If *li\item >= maxpos - 1 : ProcedureReturn 0 : EndIf
Case #SB_PAGEDOWN : If *li\item < minpos + cpp : ProcedureReturn 0 : EndIf
Case #SB_PAGEUP : If *li\item >= maxpos - cpp : ProcedureReturn 0 : EndIf
Default : ProcedureReturn 0
EndSelect
EndIf ;}
;Wenn jemand rumscrollen will, lass das ja nicht zu :)
Case #WM_MOUSEWHEEL ;{
If *li\hedit
; delta = ((wParam >> 16) & $FFFF)
; If delta > $7FFF : delta = -$10000 + delta : EndIf
; minpos = SendMessage_(hwnd, #LVM_GETTOPINDEX, 0, 0)
; maxpos = minpos + cpp
ProcedureReturn 0
EndIf ;}
;Hintergrundfarbe von ListBox und Edit ändern..
Case #WM_CTLCOLOREDIT, #WM_CTLCOLORLISTBOX
Select lParam
Case *li\hedit
; SetTextColor_(wParam, $00000FF)
SetBkColor_(wParam, RGB(255, 255, 200))
ProcedureReturn CreateSolidBrush_(RGB(255, 255, 200))
EndSelect
EndSelect
ProcedureReturn CallWindowProc_(*li\oldCB, hwnd, Msg, wParam, lParam)
;***********************************************
;**** EVENT HANDLING FOR THE OTHER CONTROLS ****
;***********************************************
ElseIf hwnd = *li\hedit
Select *li\type
Case #ListIconGadget_Edit ;{
;***********************************************
;***************** EDIT CONTROL ****************
;***********************************************
Select Msg
Case #WM_KILLFOCUS
;Text aus dem Stringgadget nehmen
itemdata\pszText = AllocateMemory((#ListIconGadget_MaxChars + 1) * SizeOf(Character))
PokeW(itemdata\pszText, #ListIconGadget_MaxChars)
SendMessage_(hwnd, #EM_GETLINE, 0, itemdata\pszText)
If PeekW(itemdata\pszText) = #ListIconGadget_MaxChars : PokeW(itemdata\pszText, 0) : EndIf
;Text in ListIconGadget schreiben und Speicher wieder freigeben
itemdata\iSubItem = *li\subitem
SendMessage_(*li\hnd, #LVM_SETITEMTEXT, *li\item, itemdata)
FreeMemory(itemdata\pszText)
;StringGadget schließen
DestroyWindow_(hwnd)
*li\hedit = 0
If *li\requestCB
*li\requestCB(*li\id, *li\item, *li\subitem, #ListIconGadget_EditChange, 0, *li\userdata)
EndIf
Case #WM_KEYDOWN
Select wParam
Case #VK_RETURN
;Text aus dem Stringgadget nehmen
itemdata\pszText = AllocateMemory((#ListIconGadget_MaxChars + 1) * SizeOf(Character))
PokeW(itemdata\pszText, #ListIconGadget_MaxChars)
SendMessage_(hwnd, #EM_GETLINE, 0, itemdata\pszText)
If PeekW(itemdata\pszText) = #ListIconGadget_MaxChars : PokeW(itemdata\pszText, 0) : EndIf
;Text in ListIconGadget schreiben und Speicher wieder freigeben
itemdata\iSubItem = *li\subitem
SendMessage_(*li\hnd, #LVM_SETITEMTEXT, *li\item, itemdata)
FreeMemory(itemdata\pszText)
;StringGadget schließen
DestroyWindow_(hwnd)
*li\hedit = 0
If *li\requestCB
*li\requestCB(*li\id, *li\item, *li\subitem, #ListIconGadget_EditChange, 0, *li\userdata)
EndIf
Case #VK_ESCAPE ;Wenn Escape gedrückt wurde
;StringGadget schließen
DestroyWindow_(hwnd)
*li\hedit = 0
If *li\requestCB
*li\requestCB(*li\id, *li\item, *li\subitem, #ListIconGadget_EditEscape, 0, *li\userdata)
EndIf
EndSelect
EndSelect
ProcedureReturn CallWindowProc_(*li\edit_oldCB, hwnd, Msg, wParam, lParam)
;}
Case #ListIconGadget_List ;{
;***********************************************
;******************* LIST BOX ******************
;***********************************************
Select msg
Case #WM_KILLFOCUS
*li\hedit = 0
DestroyWindow_(hwnd)
If *li\requestCB
*li\requestCB(*li\id, *li\item, *li\subitem, #ListIconGadget_EditEscape, 0, *li\userdata)
EndIf
Case #WM_LBUTTONDBLCLK
i = SendMessage_(hwnd, #LB_GETCURSEL, 0, 0)
If i >= 0
itemdata\cchTextMax = SendMessage_(hwnd, #LB_GETTEXTLEN, i, 0)
itemdata\pszText = AllocateMemory((itemdata\cchTextMax + 1) * SizeOf(Character))
SendMessage_(hwnd, #LB_GETTEXT, i, itemdata\pszText)
;Text in ListIconGadget schreiben und Speicher wieder freigeben
itemdata\iSubItem = *li\subitem
SendMessage_(*li\hnd, #LVM_SETITEMTEXT, *li\item, itemdata)
FreeMemory(itemdata\pszText)
*li\hedit = 0
DestroyWindow_(hwnd)
If *li\requestCB
*li\requestCB(*li\id, *li\item, *li\subitem, #ListIconGadget_EditChange, i, *li\userdata)
EndIf
EndIf
Case #WM_KEYDOWN
If wParam = #VK_ESCAPE
*li\hedit = 0
DestroyWindow_(hwnd)
If *li\requestCB
*li\requestCB(*li\id, *li\item, *li\subitem, #ListIconGadget_EditEscape, 0, *li\userdata)
EndIf
EndIf
EndSelect
ProcedureReturn CallWindowProc_(*li\edit_oldCB, hwnd, Msg, wParam, lParam)
;}
EndSelect
EndIf
EndProcedure
;Die Procedure zum Initialisieren von allem oben drüber
Procedure ListIconGadget_SetEditable(*li.ListIconGadget_S, *requestCB = 0, *userdata = 0)
*li\hnd = GadgetID(*li\id)
SetWindowLong_(*li\hnd, #GWL_USERDATA, *li)
*li\oldCB = SetWindowLong_(*li\hnd, #GWL_WNDPROC, @ListIconGadget_CB())
*li\requestCB = *requestCB
*li\userdata = *userdata
EndProcedure
Code: Alles auswählen
XIncludeFile "LIG_inc.pbi"
Procedure SetGadgetItemIcon(iGadgetNr.i, iRow.i, iCol.i, iIcon.i, iSet.i)
Protected lvi.lv_item
lvi\mask = #LVIF_IMAGE
lvi\iItem = iRow ; row number for change
lvi\iSubItem = iCol ; 2nd subitem
lvi\pszText = #Null ; text to change to
If iSet
lvi\iImage = iIcon ; Icon setzen
Else
lvi\iImage = -1 ; Icon löschen
EndIf
SendMessage_(GadgetID(iGadgetNr), #LVM_SETITEM, 0, @lvi)
EndProcedure
;Der Callback macht folgendes:
;Bei Klick in die erste Spalte, klappt eine Auswahlliste aus
;Bei Klick in die zweite und dritte Spalte kann editiert werden
;Bei Klick in die dritte Spalte wechselt diese zwischen Ja und Nein
;Wenn man in Spalte 2 und 3 nichts eingegeben hat, dann wird '(nichts)' hineingeschrieben
Procedure requestEdit(GadgetID.l, row.l, column.l, msg.l, *out, *userdata)
Protected *pout.Long = *out
Select msg
Case #ListIconGadget_Click
If column = 0
*pout\l = AllocateMemory(1024)
If *pout\l
;Zuerst das Trennzeichen, dann die Elemente und dann zweimal das Trennzeichen
If row = 2
PokeS(*pout\l, ";Eins;Zwei;oder;Drei;oder;Vier;oder;Fünf;;")
Else
PokeS(*pout\l, ";Hallo;das;ist;die;Auswahlliste;für;die;" + Str(row) + ". Zeile;;")
EndIf
;*pout\l wird danach wieder mit FreeMemory() freigegeben
ProcedureReturn #ListIconGadget_List
EndIf
ProcedureReturn #ListIconGadget_NoOpen
ElseIf column = 3
If GetGadgetItemText(GadgetID, row, column) = "Ja"
SetGadgetItemText(GadgetID, row, "Nein", column)
Else
SetGadgetItemText(GadgetID, row, "Ja", column)
EndIf
ProcedureReturn #ListIconGadget_NoOpen
EndIf
ProcedureReturn #ListIconGadget_Edit
Case #ListIconGadget_EditChange
If GetGadgetItemText(GadgetID, row, column) = ""
SetGadgetItemText(GadgetID, row, "(nichts)", column)
EndIf
Debug "Zeile " + Str(row) + ", Spalte " + Str(column) + " geändert: '" + GetGadgetItemText(GadgetID, row, column) + "'"
Case #ListIconGadget_EditEscape
Debug "Zeile " + Str(row) + ", Spalte " + Str(column) + " NICHT geändert: '" + GetGadgetItemText(GadgetID, row, column) + "'"
EndSelect
EndProcedure
Define li.ListIconGadget_S, i.l, s.s
Define hImlImages.l, iStyle.l
If OpenWindow(0, 0, 0, 600, 500, "bla", #PB_Window_ScreenCentered | #PB_Window_SystemMenu)
; If CreateGadgetList(WindowID(0))
li\id = ListIconGadget(#PB_Any, 100, 100, 400, 300, "Column0", 125, #PB_ListIcon_FullRowSelect | #PB_ListIcon_GridLines)
AddGadgetColumn(li\id, 1, "Column1", 75)
AddGadgetColumn(li\id, 2, "Column2", 75)
AddGadgetColumn(li\id, 3, "Column3", 75)
For i = 0 To 100
AddGadgetItem(li\id, i, "Row" + Str(i) + Chr(10) + "C1" + Chr(10) + "C2" + Chr(10) + "X")
Next
; Eine Imagelist aufbauen
CatchImage(0, ?Test)
hImlImages = ImageList_Create_(16, 16, #ILC_COLOR32 | #ILC_MASK, 0, 8) ; | #ILC_MASK
ImageList_AddMasked_(hImlImages, ImageID(0), #CLR_NONE)
; Das "PB-eigenen" Image ist jetzt nicht mehr nötig, weil es in die ImageList kopiert wurde
FreeImage(0)
; Die Imagelist dem ListIconGadget zuordnen
SendMessage_(GadgetID(li\id), #LVM_SETIMAGELIST, #LVSIL_SMALL, hImlImages)
; Jetzt dem ListIconGadget sagen, daß es auch in SubItems Icons anzeigel soll/darf
iStyle = SendMessage_(GadgetID(li\id), #LVM_GETEXTENDEDLISTVIEWSTYLE , 0, 0) | #LVS_EX_SUBITEMIMAGES
SendMessage_(GadgetID(li\id), #LVM_SETEXTENDEDLISTVIEWSTYLE , 0, iStyle)
; SendMessage_(GadgetID(li\id), #LVM_SETTEXTBKCOLOR , 0, #White)
; SendMessage_(GadgetID(li\id), #LVM_SETBKCOLOR, 0, #White)
;Die blöden, per default gesetzen Icons in Spalte 0 entfernen
SetGadgetItemIcon(li\id, 0, 0, 0, #False)
SetGadgetItemIcon(li\id, 1, 0, 0, #False)
SetGadgetItemIcon(li\id, 2, 0, 0, #False)
;Jetzt die Icons nachträglich in die Spalten setzen
SetGadgetItemIcon(li\id, 0, 1, 2, #True)
SetGadgetItemIcon(li\id, 1, 1, 2, #True)
SetGadgetItemIcon(li\id, 2, 1, 2, #True)
SetGadgetItemIcon(li\id, 0, 2, 1, #True)
SetGadgetItemIcon(li\id, 1, 2, 1, #True)
SetGadgetItemIcon(li\id, 2, 2, 0, #True)
SetGadgetItemIcon(li\id, 0, 3, 2, #True)
SetGadgetItemIcon(li\id, 1, 3, 2, #True)
SetGadgetItemIcon(li\id, 2, 3, 2, #True)
;Messages, bei denen reagiert werden soll
li\msg[0] = #WM_LBUTTONDOWN
li\msg[1] = #WM_LBUTTONDBLCLK
;Steht 'direct' auf #True, kann man von einem Edit direkt ins andere klicken
li\direct = #True
ListIconGadget_SetEditable(li, @requestEdit(), 2)
Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
; EndIf
EndIf
End
DataSection
; Dies ist ein 16 Farben BMP Image mit 48 x 16 Pixeln Ausmaße (also quasi drei 16 x 16 Pixel große Images nebeneinander)
Test:
; IncludeBinary "test.bmp"
Data.l $01F64D42,$00000000,$00760000,$00280000,$00300000,$00100000,$00010000,$00000004,$01800000,$00000000
Data.l $00000000,$00000000,$00000000,$02040000,$FEFC00FC,$02FC00FC,$00000004,$FC820000,$3A00002D,$3C0000FF
Data.l $00000091,$5500007C,$01000001,$02000000,$00000000,$E9910000,$43B10061,$003700AC,$007E0080,$1111007C
Data.l $11111111,$11111111,$11111111,$11111111,$11111111,$11111111,$11111111,$11111111,$11111111,$11111111
Data.l $11111111,$11111111,$11111111,$10111111,$10111101,$11111101,$11111111,$11111111,$11222211,$11111111
Data.l $00111100,$11111111,$11111111,$11111111,$21222212,$11111111,$01000010,$11111111,$11111111,$11111111
Data.l $22111122,$11111111,$11000011,$11111111,$11881811,$12111111,$12111121,$11111121,$11111111,$11111111
Data.l $81888811,$11111111,$11211211,$11111111,$11011011,$11111111,$88888818,$11111111,$11211211,$11111111
Data.l $11011011,$11111111,$88888818,$11111111,$11211211,$11111111,$11011011,$11111111,$88888818,$12111111
Data.l $12111121,$10111121,$10111101,$11111101,$81888811,$22111111,$22111122,$00111122,$00111100,$11111100
Data.l $11881811,$22111111,$22111122,$00111122,$00111100,$11111100,$11111111,$12111111,$12111121,$10111121
Data.l $10111101,$11111101,$11111111,$11111111,$11111111,$11111111,$11111111,$11111111,$11111111,$11111111
Data.l $11111111,$11111111,$11111111,$11111111,$11111111
Data.b $11,$11
EndDataSection