; ################################ ; Custom TreeView Gadget ; PureBasic 5.24LTS and 5.46 LTS ; Windows, Linux, MacOS ; (w) 09/2015-07/2018 by V. Schmid ; ################################ EnableExplicit UsePNGImageDecoder() DeclareModule TreeView #collapsed = 0 #open = 1 #parent_root = 0 ; flags for create() function #flags_multiSelect = 1 #flags_autoSize = 2 #flags_offerSearch = 4 #flags_checkBoxes = 8 #SearchMenuHookID = 1999 ; may conflict with other source code :-( Structure ct_customizing fontId.i ; ID of the font to use foregroundColor.i backgroundColor.i selForegroundColor.i selBackgroundColor.i lineColor.i headerForegroundColor.i headerBackroundColor.i EndStructure Structure ct_column text.s ; the column text content imageId.i ; imageId to use (0 = no image) itemDataStr.s ; individual item data itemDataPtr.i ; individual item data EndStructure Structure ct_header text.s ; column header text position.i ; header position (x) width.i ; header width maxWidth.i ; maximum entry width itemDataStr.s ; individual item data itemDataPtr.i ; individual item data EndStructure Structure ct_entry text.s ; entry text sortValue.s ; dummy entry for column sort status.i ; #collapsed / #open imageId.i ; imageId to use (0 = no image) itemDataStr.s ; individual item data itemDataPtr.i ; individual item data selected.i ; #True or #False if selected checked.i ; #True or #False if checked (only if #flags_checkBoxes is used) hidden.i ; #True or #False if hidden or not x.i ; x-position in canvas or 0 (invisible) y.i ; y-position in canvas or 0 (invisible) checkboxLeft.i ; x-position of checkbox (if given, otherwise 0. Width is *main\lineHeight because of quadratic!) *parent.ct_entry ; pointer to parent node of type ct_entry List columns.ct_column() ; list op potential additional columns List childs.ct_entry() ; list of possible tree child nodes EndStructure Structure ct_main x.i ; gadget x-position y.i ; gadget y-position width.i ; gadget width height.i ; gadget height gadgetId.i ; canvas gadget id hScrollId.i ; hScroll gadget id vScrollId.i ; vScroll gadget id scrollX.i ; scroll position X (0=top) scrollY.i ; scroll position Y (0=left) flags.i ; several flags treeHeight.i ; internal tree height calculation treeWidth.i ; internal tree width calculation lineHeight.i ; height of one line regarding config (font) lineCount.i ; number of lines (respecting collapsed as 1) headerCount.i ; Number of used columns searchMode.i ; #true or #false if some search is currently active searchFieldId.i ; gadget id of search input text field (0 = none) customization.ct_customizing ; optical customization data Array header.ct_header(50) ; column information List childs.ct_entry() ; list or root items (incl. childs) EndStructure Declare.i redraw(*tree.ct_main) Declare.i clear(*tree.ct_main) Declare.i create(x.i, y.i, width.i, height.i, flags.i = 0) Declare.i remove(*tree.ct_main) Declare.i customize(*tree.ct_main, *config.ct_customizing) Declare.i addItem(*treeId.ct_main, *parent.ct_entry, text.s, imageId.i = 0, itemData.s = "") Declare sort(*treeId.ct_main, Mode.i = #PB_Sort_Ascending | #PB_Sort_NoCase) Declare sortColumn(*treeId.ct_main, columnId.i, Mode.i = #PB_Sort_Ascending | #PB_Sort_NoCase) Declare.i getSelected(*treeId.ct_main, List entries.ct_entry()) Declare.i resizeHeader(*treeId.ct_main, columnId.i, width.i) Declare.i setIcon(*entry.ct_entry, columnId.i, imageId.i) Declare.i resizeTree(*treeId.ct_main, x.i, y.i, width.i, height.i) Declare.i mouseOnHeader(*treeId.ct_main, x.i = #PB_Ignore, y.i = #PB_Ignore) Declare.i eventHandler(*treeId.ct_main) Declare unselectAll(List tree.ct_entry()) Declare.i getByItemDataStr(*treeId.ct_main, search.s, List entries.ct_entry()) Declare.i getByItemDataPtr(*treeId.ct_main, search.i, List entries.ct_entry()) EndDeclareModule Module TreeView #tree_fx_icon_margin = 5 ; General margin between icons and text in tree #tree_fx_col_margin = 5 ; Margin for column header text (line to text) #tree_fx_sel_margin = 3 ; +- offset for selecting the column line with the mouse #tree_fx_linecolor = $999999 #tree_fx_searchBack = $BBFFFFFF #tree_fx_searchFront = $BB444444 #tree_fx_defaultColWidth = 200 ; default column width #tree_fx_indentFactor = 1.5 ; indent factor (<1 less, >1 more, best 1.5) #tree_fx_scrollBarSize = 20 ; scrollbar size #tree_fx_lineMargin = 3 ; left line margin Declare.i _cust_tree_eventHandler() Declare.i _cust_tree_searchHandler() Declare.i _cust_tree_update_search(List tree.ct_entry(), search.s, depth.i = 0) Global NewList cust_trees.ct_main() Global *currentSelected.ct_entry ; pointer to last selected item (0 if nothing) ; catch all needed images Global ct_image_collapsed.i = CatchImage(#PB_Any, ?tree_collapsed_image) Global ct_image_expanded.i = CatchImage(#PB_Any, ?tree_expanded_image) Global ct_image_search.i = CatchImage(#PB_Any, ?tree_search_image) Global ct_image_checked.i = CatchImage(#PB_Any, ?tree_checked_image) Global ct_image_unchecked.i = CatchImage(#PB_Any, ?tree_unchecked_image) ;{ Image data for expanded and collapsed image ; Generated using regify GIT repo includes/tools/bin2dataSection.pb DataSection tree_expanded_image: Data.q $0A1A0A0D474E5089,$524448490D000000,$3800000038000000,$3B86A80000000608,$494273040000001E Data.q $64087C0808080854,$4144490F04000088,$54484F9AED816854,$A1A5A94A7FC61451,$126108463698B5A0 Data.q $8D5B337230228CAD,$236C22E85CED1656,$3B44288276893B6A,$E0657219B648DD03,$FA08B3B55C2EBFA6 Data.q $37BCE6149A541103,$BDCE3997BC79CE2D,$C22E0F98919BEE6F,$F73BBEEF7DF39CF3,$A0A82A0A85EE7B9E Data.q $12E071EE284AA89C,$C0A3815A069C09D0,$0EF00BE04FFFFD61,$098F819602BC0D78,$01580860168271E9 Data.q $F01EBFD7CDA7203C,$EA78D81E79179B5B,$34C0520DF6B1A43B,$06401F81F50889B0,$D408D01B40D20618 Data.q $A8C9B56967D46B6A,$7C255331A9BFE04F,$E098A98C03507F8F,$788C5D40BD022D0B,$9536023848BBEA75 Data.q $AB03B42B5E81C6A3,$BF1870E8016BC384,$4B3955745F066343,$EE80D78943605E82,$BD9CAF872B2EB904 Data.q $2F91640830017209,$B31CA84D24340737,$709060890D5599CA,$224607B245248CC8,$1F3DE907B25FCEFC Data.q $21AA0302DC1D7018,$1CFF8C8EDAE1C38B,$D96882313B72F9E5,$3E60CC8353C4C1C0,$5D9C25982FDB9BF2 Data.q $A35ADE90C6425A8B,$394D101A298A1334,$2493B308A42C6344,$627ACC26E8B88297,$14382D0F48D33C65 Data.q $C75B69C6880B1AE5,$AE2866DC3C67A416,$1F4132C4DC6881D1,$70357DA1A2FF3E42,$AD545EE436AB71C0 Data.q $CC7C5B6C80F68643,$71CB6077C0739AD6,$575AABD085D8D75B,$F6C87AACD90CDE8C,$70C33081705764C5 Data.q $4F56A9D7F01A2EDD,$98CDC3F21F4417B5,$9F0B19A0AA1E2B18,$327C2C8F58D1987C,$1E3549E3133EE688 Data.q $48B22194813820B6,$E02FC6353A2E2299,$C4D279A230DF85A4,$3BB8A5B4C1E90978,$E02A9EB5788CF7EA Data.q $BF569BE96BB8C6A9,$24B3684320E31325,$5F4B35EFA83E07A9,$6A6B3EC27C0727D4,$8DE91D620AB8CC23 Data.q $1EF44AEB80124B46,$B80E4FABD4536179,$626EBCD111B78311,$5510636ABE548DBC,$5C6FC2DC6C95CF01 Data.q $8B5145FEBE625E7D,$4105E62E3136DE68,$EC0D5A74990F2ED6,$B4E4C7CE03D6D55B,$C80780688662C17A Data.q $D9BE579DB0379B70,$B1E4506886ACF822,$BB39D61148A2649D,$2D1811DC22EBB0F6,$882B045846D079D2 Data.q $189D89976DF7A425,$26AABBFEECAB38D5,$71AD2B922EDE3D2C,$A86EFD0B3889968C,$FFCBBFDB3D20DE03 Data.q $2E48A8702DE795FB,$6AA4D9454FBE85AB,$229A067D634570F9,$C0755C0F99F49BCF,$B26D9AA94658D193 Data.q $3C889DDE904F085D,$275BE20ECB41509B,$C833842A5DB760DB,$E485B6D29C05BDF4,$812EDEB7C41D679C Data.q $F15F21EE10B786FD,$58EC51DF701CB6B1,$2BE0168B25D75E1B,$DC41CC701BEF3EE1,$B70A79D6D9A2C968 Data.q $1CB79E87849BA745,$5D9C71534E8A0CC5,$DC0BEA79220F0D97,$AC5904365D15F79C,$10AE510388E10C83 Data.q $D7EC6C63447D7E14,$FFB0F74A8E1CFDD2,$4BE1C9D014825F0E,$0690076259F1E880,$E09129AAB328CE08 Data.q $4483E60898FB8F45,$E09473D8475CB93B,$D084BBA50F403B08,$7CCB0F9C1097DFCD,$7E41A80491AFAFF8 Data.q $170CA51A045F4917,$ABEB92A92148A281,$344ED72AF994535C,$9E32F09F3C65F7B3,$C4EB3C65DBBE791F Data.q $A0A82B29D72A1F39,$5595DB7307F14482,$00000000C183AD43,$826042AE444E4549 EndDataSection DataSection tree_collapsed_image: Data.q $0A1A0A0D474E5089,$524448490D000000,$3800000038000000,$3B86A80000000608,$494273040000001E Data.q $64087C0808080854,$414449C603000088,$9C684FDAED816854,$D44DA28FF1C71845,$809112F687AB044A Data.q $97314114593D60D4,$F60F378A29E0BB5C,$26F1137A9A12F120,$E31173429BC41694,$8737F8BD8AA73165 Data.q $82203FC1515B9082,$9987AEC9B454369A,$FBC99DE4C4B37797,$7DD859785FB2C26E,$CF33BCCECCF7E79F Data.q $E48388C46232F33C,$0A78F3C09EFC868E,$FBF8DEE10F12638F,$716F86B11F8EFC2D,$C31C2D4F1434BF15 Data.q $3D3ABC6E85B1563C,$F17DF3D3ABC6FF9F,$8E0383C79DB456DB,$81943612E38E577D,$2614C5980B1B44DF Data.q $BC6CDDF889AF1630,$8C5C0DFB0A6D1DA7,$E9831F38238C0FBE,$FC33C61682B03D0A,$C0D808E515DB478D Data.q $394D7127281AD462,$E8E9FFA35D3196BC,$B42073516BD1E8B3,$5A834CC75F0A2370,$A834CF3513A35133 Data.q $38CBB9E85362CE05,$9B9A8A58D460B4DA,$E7321CAB38468631,$DAFD1EC585D9129A,$AB5E0EBDFE12D91B Data.q $C6A9BC9FC50279DB,$D3075DFF18527AE1,$C350DCB10A2CFD73,$33C23AC1984473C4,$5AFBAECE52585877 Data.q $284C41CB672BCE42,$9D73175D9CAA3955,$9DD8242F2766571C,$B328CC09A2B9B3C6,$97D63092E78CAC9E Data.q $04FE147AC4EE6C13,$78C42CB02708E68F,$496CCFF098F0D531,$FCE9CA3FC2EE50CB,$C42D3DE1B9A3BC6F Data.q $5C02179F413121B8,$A5CA07E06DEC1449,$ACDD70F422BC57E5,$D468D5A314F98D44,$F83F61206DEB144A Data.q $F44E9E0DF45B7285,$E0D55AB7A8AD17B9,$FA0AEFA537EDD084,$949F84BF54D8481B,$85C60EA751CBBEFB Data.q $B4818B9CFAAAB758,$787DC1606A76C433,$B89BDFB317CDD84B,$5739F4769FA74327,$10C85A70C20BE6E5 Data.q $B948F8930581ADDA,$B2F568662F1EFFA3,$7895F372A85D7D10,$3C16069550866CF3,$D0CC9E2AF29EFB67 Data.q $2293DF6215DAC424,$6120694A10CA9843,$F7E89329BF157D56,$DAE33294EEA270F1,$2114489D81E17593 Data.q $DB68DDE00F0581BD,$22733CF543789FC5,$F950B6F9B957AE6A,$3C1606FC920C4B1A,$ED15D7E7CF954F86 Data.q $C4D0FFF76F9884C6,$873B59DAC5175BC2,$457771D781FE8861,$7A0C8A3D7A21A4FB,$17213DEC430AE992 Data.q $AE649A57DA2A614E,$F989144CB50700C4,$C76B109343227867,$909AE0CF4EC4CB0E,$61498BDB14CA9D5E Data.q $16107EF7585CFFA1,$1094F3E9977C19FA,$6EEE84E79B955AF3,$BCA2E3F90AEB5532,$AA52B3E8ED3F782A Data.q $3B9DF543FB648836,$52FA5DBBD4DB2724,$24EA3494257FE539,$F19FDE1BFD44BB79,$75BC36B323CE66B2 Data.q $6BF9E16DE16E2C95,$BFA745F90592EC82,$134552478C7DC684,$72986CBDB13A2EC9,$B22608E4CF1A7482 Data.q $08645BF0A121B2EF,$2FC28FB5DBF0B187,$36AB8FFB4BF74BDB,$052F87E433DD2B6A,$0EA0FCEFD67C7D43 Data.q $5BCA7A37E8F5CF40,$BF73C4CC192F611C,$2FC4319C17611D01,$4305246BF50C1421,$572AFB432F5022FD Data.q $F9935A978CBE753F,$A231188E53AF465F,$75D0893D9003FE51,$49000000006B1E9F,$826042AE444E45 EndDataSection DataSection tree_search_image: Data.q $0A1A0A0D474E5089,$524448490D000000,$3000000030000000,$F902570000000608,$414449DE0A000087 Data.q $554C0999EDDA7854,$17104105EFC715E9,$7143701501415144,$11BA87510DC05417,$4C49D26A8546828B Data.q $4D38D27692649DA6,$B13A624E92666D33,$DF715D4A4BAD30D3,$7140541516C75C70,$86E2EE2880B8A8AD Data.q $C7DCCBBCDCEFEBC0,$E64D333470F2F77B,$FDDFBDDEF7DE5E4B,$5577CE73FF2CFFCE,$3F00B43FA87CFFE5 Data.q $29DAED76A6E77001,$7C6D369B4A111111,$83FDFDFDC2424242,$3C414DBC5A6A6A6A,$6BB5D56679EB3D3D Data.q $EAF4F67B3D9F4AD7,$B3629555554AABD5,$94AD56AB401CDED9,$4C0E0703A986C361,$9D5D5D5258080808 Data.q $3C1E0F76EDF6FB78,$432AC9E4EAEAEB50,$1FB5CE9BDB1F656E,$956DB6DB6A1E1E1E,$98079BEC17A0A0A0 Data.q $2A2A2E6ECF67B3AC,$4BA5D006CD2013AA,$5151813198CC6517,$5A241E14A0F15F51,$D0D740118B0D3ED2 Data.q $A55ADADA9FEB98DB,$3E6C6C6C4AEEF77B,$70C0BC2C2C2CC896,$9349949D4EA7401F,$D37733D9ECF44526 Data.q $DB822AB6D13D4DA6,$64EEB47DF755D237,$BF5EBE4B9105E301,$4663F2793C9B6D7E,$9C205721C141414A Data.q $4E4E4952D96CB400,$DAED6C7D1D1D10EE,$8B70D799AE0891B5,$E67DFF5866CEBECD,$CCA2A4AE0C04D7A6 Data.q $4B0783C1FC51B8C7,$87C3E1801393BB1A,$E9F346C94CA6532B,$729A0DD69AF5F5F5,$FC6DAEE68F746E7B Data.q $27C529292928401C,$8D70F87C3D889C4E,$2B7B7B78001DB031,$A9FE5D30C3696969,$D859A36EEC3CA753 Data.q $FB2FFC36F07AABE0,$CBCBCDB0FBB55601,$C7EEE80B9A6AC54B,$5CE5D33C5ACD06EF,$162BC360E8743ABE Data.q $0D71DCEE7715D8A1,$FA7A7A4A1E8F47A0,$168B45FFAFD7EBD7,$109ADCC4D2DC1A2D,$F4FADBDBEDF6A562 Data.q $6290600B2102F3E9,$ED003CCB53E72F04,$C1959595519EE1F5,$77000980D65F2F97,$252524651738C85C Data.q $6D410C78FC7E3C9D,$CA7984F4BA000973,$8C960015DDDEEF76,$8643219417938221,$F3399CCE2FC840DC Data.q $95F7FBFDC1ED822F,$2D0960F76B9FCFE7,$8EF77BBD5E8F4087,$1B8DC6E8FD0D0D0F,$93366CD8BC5E2E93 Data.q $75BADFD4462311EF,$21200970AA37142B,$EC763ADC2B082121,$B887ED78E6B8AA98,$030302EB7DBEDF4B Data.q $53E494BC5C5C52FF,$5868BAED43E1F0FD,$65128577C5BE515C,$FFBFDFED51A81808,$740E0703ACE8A0CF Data.q $6BB5D5B43B90BA31,$359ACA7838383557,$984C011A401B726B,$BFF7869EAD1A2130,$F06DBC210B91B893 Data.q $73B9C676F835C1E0,$8CDA6A969696D6E7,$ABD5EDF5FA148C8C,$358DE10023BE10D7,$7BBDDEEEA7E3C36E Data.q $0A6EE2804851F71B,$01E96176E5821CBE,$A6C8E6485C774962,$F676767E6030180E,$8833EE0019DFD93F Data.q $0CD90664B86B249B,$F3402CF8F8F9B057,$6C930B0B095E68D9,$4F2793EDF81881DD,$4C82402974BA5E16 Data.q $F0025AB003EE2F34,$AFF8A38800FE2020,$EDE46B082ED80D66,$8C3BABB8BBABB5DA,$7A132000A7E7E7E4 Data.q $192E0008A005655F,$12D8B8E5E365C103,$C5C4A7EBF5FA5504,$8EED7ADD6EB425C5,$186F2465C5823B1D Data.q $75BFE19B26EE26B5,$652C9494979F0994,$A116365C1BE49D7D,$3158D724BD1E85BE,$758CE8002C5E5892 Data.q $F3A298400399D6EB,$7AE02C90B93BCFE7,$AD92C7C5A0FAE025,$DEEF772DB9A317C9,$069862356B3A7EBD Data.q $0A2CEE773A01141A,$13626C15FE8A12F8,$F7DBEDF7CB19E3D6,$CD490E7AC573163E,$3420C8D000E642EE Data.q $B53ED1240F4000E2,$C947F3D9ECF6EFC9,$E502502C5B5A5086,$A93F28415BBC2286,$239C412A62046D6A Data.q $F429C828D46A36AE,$1D716EB79E5E5E72,$0C9F8955CA39E374,$DFE300085004B4F6,$8A8A8ABAB9A20F9A Data.q $CCE6503603773470,$178BC9F36E09F399,$39E43D658CEF942F,$CDF52B8ABF5A31B2,$24AE1E1E1D7FCFCF Data.q $A919261621EE2626,$36062556A95AD5D5,$57D7D7C00E60D86C,$2F8F8F8E3F6340B3,$33C41835CA07EB81 Data.q $DD6DB6DB74A62626,$B82088FF5DBA9BED,$7FB7DBEDE4D3B88E,$CDCC3F333333EB23,$003D6AC86242FDCD Data.q $000D484002980B34,$50500E520310E8F4,$29BCDE6F73ECC1AD,$3C9E4E977DC913EE,$36DDFC7366B7E969 Data.q $4963E410CFCCE673,$CBCBCA13A9A9A97C,$7D218CB9959597F7,$71B8D8CDB240312E,$8F983800428025A3 Data.q $287E1632D4C52E34,$2B95E26F379BCE1B,$F76DDD7EBF5EF657,$FEFD68DC58A10E31,$2890A2ACC8D3FBFD Data.q $65A12174B616212C,$1A8B5820025A6400,$0B33F800A8003227,$C9DCEE773464923F,$C016C6F1AC6EFA70 Data.q $A15E68A9C79439A2,$C9A6939E8D07EFE6,$902CD00000C202CC,$222E7E000E639393,$AB69B4DA685C9CC0 Data.q $7F763231A285CBA1,$600F51BCCEC28634,$2CD66F379B65A177,$911C8078ECF67B3A,$0154031242E56D6C Data.q $8380B10A00A800D6,$8FFE09F4230324F9,$4DD3126F5D646B91,$120EE69E6A503528,$773B9DF3F7B7429A Data.q $7FD01064BFF5926E,$80042E275CB799A5,$D80B06F9A4004B4C,$1A2BD4300B4D260B,$B090927BD1646EFA Data.q $E6B17B12E8D097C8,$B410B3B15CD9BF70,$A8CF82C0F59B0559,$AA96814F5D7DA49D,$F0E7A1C15E86DA97 Data.q $01268CC06157F7F7,$C8040406332760F0,$919124DFDAD1F7F5,$3B55B82540484280,$950161CB9E37B32B Data.q $9E4CB2391C8FAFEC,$BADD6E80F8FC7E33,$0093C54D368CA195,$9E14002166B2EEA0,$3FFD633FE1BA7D3E Data.q $42B1A232FF8CC663,$A3D1E73FDCB4A50D,$98C87A1E1694D947,$2EF022A4C832F3D5,$242436B95899894A Data.q $EAFBFA74297FFC44,$0164A902FB26EAEA,$700045059442CD60,$FD1E8F467FA905A9,$F472B542E7F1B21B Data.q $1CB61E0CD0E87431,$5AC7365D699AFA8D,$E161E686276F8652,$A9D4EA7EB25A496A,$05A4D001D388485B Data.q $32405B981152A001,$21F2ECA94452EC31,$4AC4BEC763B18562,$948A05C9BFDE5049,$928CF98440F1F815 Data.q $CFDDA2AC55010939,$865BCC9569CA367D,$3A87FA0AF4027FC0,$683419F1A4322BC9,$52266B8ADD5A1F90 Data.q $C802A00090B25B8E,$6C7C285000252CB0,$97CBE5E0F4AEEC36,$F3D493752F9408C9,$E3C081156E0A6814 Data.q $2BC026F3EE08BBA1,$6E147991438F2169,$C037A438CDAEF5AF,$4130712B76687E32,$4CCA0A43890D9840 Data.q $4D9A6F602C4E62CB,$D0D53E48198470CF,$7AE7F0AB05F407DA,$B7E2B3446DA9385D,$7E863FBD6DA250C2 Data.q $72D63D614507BAE5,$0127702068BB0100,$1C8E47500002D2E1,$1595003F4A8D5E69,$BA0A09421E4D6411 Data.q $929FF334439B8933,$2B2B2BEB6685BE84,$6B4440252C5EA6B5,$03E7D8375AB8EBE2,$28282F4C0C4B1E36 Data.q $2FC820E9521E30A8,$84B76892D96CB75B,$DD67EC2A76741C83,$3E1F6E0010A000D0,$A3E32F1A858D87C9 Data.q $F3E3946B18BEB0DF,$BF2FBCF29EDF0875,$5897A0C51C78D013,$44858917F9FCFE64,$C1C40096570AB13A Data.q $EC3404D70D56AB55,$96F311214763B1D5,$1A7008D171B8DC72,$E8A750E80C73B8ED,$44A496A66B420BBD Data.q $C12D08AAFBE1B566,$02E20100D22797A9,$D21046E55EC49024,$F0F11178BC5E2D38,$0A98C41C83866535 Data.q $E63D4DD73D24F980,$EEA93922DD07CB21,$88F7661C6E6CDF68,$D2262020582C17B6,$994A2A2A225834D8 Data.q $1F595959A7FA7D3E,$A3BCAD61CAF57ABF,$AB1F5D4683DE2480,$74371979449E06E0,$9937816571563B35 Data.q $350B85C2C404C58B,$482156993945C410,$8D7C7068723623DF,$0B5CF582480B2A94,$9F4E12D83EBA9125 Data.q $995CAE57DFD66B35,$9EC50005DE41AC69,$0275DA5A5A4404E0,$316479205DB81FF4,$B20E63448BD8FBC2 Data.q $6A7EC62E5C49DB21,$5939393CF8400CE2,$3538B87AB325DC3B,$7E80E5E56FDA3633,$A8221F2769630C20 Data.q $30B0B0FA9B1A005E,$F318579DE88BE4C9,$98754D5C253B5389,$1963FEC34BA7A44D,$30B1B0B4C5E9E9E9 Data.q $856200277FF18D64,$D5EAC705ED38B8B8,$21723BCA234523AB,$EA08B18B9A74CF17,$F47A3F8BDDEEF7C7 Data.q $75E41D4FA7D3C968,$65257433A9CAD0D9,$9FA0D949E8962E41,$920B3B321713BD94,$D50A55B53C220A77 Data.q $8ED30F1B3F6921C1,$A2BE25881E747EDC,$DE5D6F4E547791DE,$CB424F2C90F7D0C5,$F2C817D81265E127 Data.q $3C29DC4494B7EF2E,$F20FC8122E255C3C,$CD7C6B6CA622759A,$EB7ED731B80B94DA,$FFDEC92F564959AF Data.q $AF54B366D82CAF61,$542954B70D989A52,$86CB21F60F00BD27,$6D6774F29A3051B3,$56848AAB893D4633 Data.q $BC12B4763953F7CA,$3F0027E3CC7E0059,$F9A95119981FF8F4,$49000000005B3975,$826042AE444E45 EndDataSection DataSection tree_checked_image: Data.q $0A1A0A0D474E5089,$524448490D000000,$3800000038000000,$3B86A80000000608,$494273040000001E Data.q $64087C0808080854,$4144490405000088,$23484D5AED816854,$20D4624C157D1849,$411045D58744449A Data.q $8F51C43D06187F04,$1E0941CFE6E62B2E,$8BCDE20F07A0F01C,$7A345C3FC1E3820A,$927829EEA222A0F0 Data.q $B087B08C141B20F1,$62626310AAC20820,$BA49B6C9261EDF30,$A683EBAED38EE9D3,$ABDFD7AABEABBBAA Data.q $DE3BC7780AABAFEE,$F801BF0067C15FF1,$004742908003C013,$A80046005C005FC0,$9C6C3D8019FB12E4 Data.q $935C12879CC52938,$F003B8011CA54F92,$3123468D1F72548B,$C0091A3461F55EC6,$004FDC804C022260 Data.q $0088818C646000FE,$C8D5D5D511A34600,$EFC164D0FA96CECE,$9797970CECECE3F7,$00AFC008DF4E0000 Data.q $18895C3DFADAC4F6,$D1B366C818306463,$27A7A78D2923E3E3,$31881830646CD9B2,$A0F084F851572B16 Data.q $9D391E5E5E518C66,$5274E9C1C579A54E,$08624FC92C5E5E5E,$282C2C2F4067057E,$2FB10B0B0B1785CD Data.q $EA08471E0862AF9D,$E0F34A7CF9F27AF5,$25CF5EBD23E7CF85,$10F90DF7C225CC78,$4E9D3A02A2A2A94E Data.q $66376EDC3B4943CC,$465BCFBFBFB16767,$E97B82A2A2A1D3A7,$A0F6116E2E595F03,$9060C133C4566CD9 Data.q $ADADABE3A3EBEBEA,$9B359612AEAEAE8D,$8EC88972DE0F5CCD,$3A389393930A5E02,$AAA0ECECEF9E8A3A Data.q $1FB1699B9B9B0AAA,$9C9C9E0F094860F1,$EEEFB0FDE5AB5690,$8BC0814283CDA6EE,$083D3D3D11F1F1E0 Data.q $E59E8882E0D10204,$A24EAF7B68870E1D,$BDE53D3D38636363,$D12EAEAE8393939F,$2F40FC27DC9232F6 Data.q $C0026262631F1F1E,$7C7C7E6E022C10F7,$1ABE6DA26565651C,$20F4F4F47EFDFA89,$A111259788020C18 Data.q $C9497CBE5F05A5A5,$CE28E8E8E891ABEE,$62E6E6E7EF79CFCF,$55E0C53799E96969,$B4F4F4C43C3C3C10 Data.q $94949429A9A99960,$7AF5E831F15B6CA4,$3BC428508BDBDBD1,$255AB5615B5B5AEE,$F0E0C7C5F0464FD9 Data.q $01BC74572E5C30F0,$B8B0183060F1FECF,$DBB512A2BED928B8,$33332C179F9F98ED,$286DC922A2A28333 Data.q $D6AD583BBBBBD126,$58318C1E5F62A7A8,$476ACA767674162C,$E2E2E20E0E0E8931,$0BCE46491C237BE2 Data.q $9DBB273737300A0A,$3730F9F3E1050464,$7EFEFE854A950737,$B7B7B7AA8A666664,$B3B332C165E5E587 Data.q $8827BA724C9930B3,$D9B321E3C7B6C1A4,$EBC99B9B9B800D1C,$A9AFAFAFA40D26F5,$BBBBAC967048A8A8 Data.q $464ED8285CE0A53B,$353DFDFDE0FFE130,$4B6922A427463636,$CF1D1D1D92524B4B,$F6E98B8B8BE6C5EA Data.q $9A9060C1823956F6,$488549173AE29A9A,$B1B58F95C46C6C6E,$E304B939129FBFBF,$26C44E93DBB76A24 Data.q $264C9915D5D5EB92,$9393297D7D7D7AC1,$B48B972E83CE304B,$F6D723C107AF2D5A,$B4A8C632595E76F6 Data.q $8904F2B76EDC94B4,$08D1A348B6B6B688,$51ABABABAE470412,$E0E8A952A822252F,$DF7904B9391240E0 Data.q $9B366AE4C99211A2,$80C0C0C77CB393A3,$25AACE0A09727264,$5172F2F2F9AA64F2,$16A2C56504A9179F Data.q $3468D0EBEBEB058B,$797960646467EA92,$8821BA7259595919,$B54928FDC9290464,$9314A63458C60D5A Data.q $8C834343421A1A1F,$CAA9BF13CA474BEE,$2CBF7EFC92B2B2B5,$41183D49A243A669,$61AB56A83C855CAC Data.q $979AC56AD5A0D9B3,$094AE5713A4EB48C,$D4119191F9228D26,$6B20BA70EA49D5D5,$69B5B5B5AE48426D Data.q $6F7C136BE1FD445F,$A815959583468D57,$08F821366897EAD5,$32483C3C3D29C206,$C28DBDBDB058B16C Data.q $8D9D9D90C63042C2,$B249555554159595,$71CAF8063E988427,$0E402040D005B691,$CA28D26E6E6E8387 Data.q $E3245BF40175217F,$03CA96B6B6B078F1,$1A9A9A83468D067D,$B58421B64BE7E7E4,$6FA5EE1E3C7835B5 Data.q $80109BF378B9657C,$0811BD7FE48DB237,$BC6DCA1BF8023E00,$2B67810B9EB83A13,$7C5C78CDE9BC1897 Data.q $7DA608254A76FC46,$C3978EF1DE3BE53B,$66BF783B4452BC3F,$4E45490000000096,$826042AE44 EndDataSection DataSection tree_unchecked_image: Data.q $0A1A0A0D474E5089,$524448490D000000,$3800000038000000,$3B86A80000000608,$494273040000001E Data.q $64087C0808080854,$414449FE01000088,$E36ABD9AED816854,$A30826D9BF851440,$9BA54ED810CA6842 Data.q $CDA4BBECB00F3690,$9615E8429154A45B,$5B40B3B6134EB6AD,$DB240ADDB5713484,$51CB1418646DDCE0 Data.q $0CD1860F9ECFE8EC,$C1715246748E70D2,$F81EE06B858CB162,$3C096D4F6BC01403,$6350E5F02DC04F03 Data.q $52F462E2E55F815F,$23F8E7DB996EBF19,$B9AFB6F80FF009E7,$B4DD06A528B5AD4B,$9876A0336CDC02D6 Data.q $8885295A037E00BB,$99329D3A66B5A000,$2C58B10769BDF64C,$0C0073E7CC66CD98,$EB5D347E077C026D Data.q $7DF7C4A528917967,$CDF495AB5648A289,$7DF7C48A2896BD7A,$CA2B88DAE3554A51,$4924F3CF1294AD4D Data.q $F1124921BD7DBE92,$3BC189AAF26AAF3C,$6A5EDF61861D0D8C,$626BB9D2FAB0C309,$F3C95D75D06371F0 Data.q $AE2E79E4B51F6FBC,$DF75234D1F069AEB,$1C70082097B2F390,$0871C705E94DC9A7,$067507373A9CC082 Data.q $8A86DCD90D5ECACF,$D0C05FF0707333C6,$DE45A2A274A0CF79,$88D8146047EBB833,$CEC7446C45A2A276 Data.q $88D8E8707BB41BC9,$8EC7446C45A2A276,$B446C45A2A27688D,$6C763A23622D1513,$9DA23622D1513B44 Data.q $827DA88F939168A8,$CEA7A3325FE4E743,$B4AE8A28A5ECB2E0,$19D41CD2E3454DEC,$817569A6957B297C Data.q $539A9A6905145141,$20E6C3E0CEA0E62F,$E7DA416E38E232CB,$0F5399965931C710,$1C02117D1C9AE775 Data.q $5C01021749C91949,$AB576EF0AFE97F02,$51C26AE52D6DB6D6,$B29CB6F35C99E317,$58B1694E5E9F830F Data.q $597EF578E73FE3DA,$49000000004FA3D9,$826042AE444E45 EndDataSection ;} ; Determine the system DPI factor to scale the content with. ; Returns a float value that is used to multiply the buttons dimensions with. ; Windows: GetDeviceCaps() ; Linux: gdk_screen_get_resolution() ; MacOS: Cocoa deviceDescription/NSDeviceResolution Procedure.f _cust_tree_DetermineSystemDPIFactor() Static LastFactor.f = 0 If LastFactor.f = 0 LastFactor.f = 1 ; init CompilerSelect #PB_Compiler_OS CompilerCase #PB_OS_Windows ; get DPI on Windows (default 96 DPI) Protected hDC.i = GetWindowDC_(GetDesktopWindow_()) Protected lDPI.i = GetDeviceCaps_(hDC.i, #LOGPIXELSX ) ; DPI ReleaseDC_(GetDesktopWindow_(), hDC.i) If lDPI.i > 0 LastFactor.f = lDPI / 96; 96 DPI is Windows default for 100% EndIf CompilerCase #PB_OS_MacOS ; Get DPI on Mac (default 72 DPI) Protected dpi.NSSize Protected screen.i = CocoaMessage(0, 0, "NSScreen mainScreen") Protected description.i = CocoaMessage(0, screen, "deviceDescription") CocoaMessage(@dpi, CocoaMessage(0, description, "objectForKey:$", @"NSDeviceResolution"), "sizeValue") If dpi\width > 0 LastFactor.f = dpi\width / 72; 72 DPI is MacOS default for 100% EndIf ; sadly, with High Sierra Apple started to report the DPI to the apps. We had to stop it :( LastFactor.f = 1 CompilerCase #PB_OS_Linux LastFactor.f = 1 CompilerEndSelect EndIf ProcedureReturn LastFactor.f EndProcedure ; Makes the cursor in the window (parent of given Gadget) to a hand ; Works both windows and linux Procedure.b _cust_tree_cursorSplit(GadgetID.i, Standard.i = #False) CompilerSelect #PB_Compiler_OS CompilerCase #PB_OS_Windows ; WINDOWS Static *cursor If *cursor = 0 *cursor = LoadCursor_(0, #IDC_SIZEWE) EndIf SetCursor_(*cursor) CompilerCase #PB_OS_Linux ; LINUX If Standard.i = #False Static *cursor.GdkCursor If *cursor = 0 *cursor = gdk_cursor_new_(#GDK_SB_H_DOUBLE_ARROW); EndIf Else *cursor = #Null EndIf gdk_window_set_cursor_(gtk_widget_get_parent_window_(GadgetID(GadgetID.i)), *cursor) CompilerCase #PB_OS_MacOS ; MAC ; write me! CompilerEndSelect EndProcedure ; Determine the needed pixel height for a given fontId Procedure.i _cust_tree_getLineHeight(fontId.i) Protected i.i = CreateImage(#PB_Any, 50, 50) StartDrawing(ImageOutput(i.i)) DrawingFont(FontID(fontId.i)) Protected h.i = TextHeight("|´_") + 2 StopDrawing() FreeImage(i.i) ProcedureReturn h.i EndProcedure ; go through all the items to find out which is covered by given coordinates (eg click or hover) Procedure.i _cust_tree_getItem(*tree.ct_main, List tree.ct_entry(), x.i, y.i) Protected *Res.ct_entry ForEach tree() If y.i >= tree()\y And y.i <= tree()\y + *tree\lineHeight And tree()\y <> -1 And tree()\x <> -1 ProcedureReturn tree() EndIf If ListSize(tree()\childs()) > 0 *Res = _cust_tree_getItem(*tree, tree()\childs(), x.i, y.i) If *Res <> 0: Break: EndIf EndIf Next ProcedureReturn *Res EndProcedure Procedure.i _cust_tree_redraw_header(*main.ct_main) Protected c.i Protected textWidth.i = 0 Protected widthOffset.i = 0 Protected left.i = *main\scrollX With *main BackColor(\customization\headerBackroundColor) For c.i = 0 To \headerCount Box(\header(c.i)\position + left.i, 0, \header(c.i)\width - 1, \lineHeight, \customization\headerBackroundColor) textWidth = DrawText(\header(c.i)\position + #tree_fx_col_margin + left.i, 1, \header(c.i)\text, \customization\headerForegroundColor) Line(\header(c.i)\position + \header(c.i)\width + left.i - 2, 0, 1, \height, \customization\lineColor) Line(\header(c.i)\position + \header(c.i)\width + left.i - 1, 0, 1, \lineHeight, \customization\backgroundColor) textWidth.i = textWidth.i - widthOffset.i If *main\header(c.i)\maxWidth < textWidth *main\header(c.i)\maxWidth = textWidth + #tree_fx_col_margin EndIf widthOffset.i = widthOffset.i + *main\header(c.i)\width Next Protected rightMostX.i = \header(\headerCount)\position + \header(\headerCount)\width + 1 Protected rightFillW.i = \width - rightMostX.i - left.i Box(rightMostX.i + left.i, 0, rightFillW.i, \lineHeight, \customization\headerBackroundColor) ; blank rest of column Box(rightMostX.i + left.i, \lineHeight + 1, rightFillW.i, \height, \customization\backgroundColor) ; horizontal line Line(0, \lineHeight, \width, 1, \customization\lineColor) Line(0, 0, \width, 1, \customization\backgroundColor) ; addition ky Keya from PB forum \treeWidth = rightMostX.i + 1 EndWith EndProcedure ; draw (optional) additional columns behind a tree entry Procedure.i _cust_tree_redraw_columns(*main.ct_main, List columns.ct_column(), *entry.ct_entry) Protected textWidth.i = 0 Protected left.i = *main\scrollX Protected y.i = *main\treeHeight - *main\scrollY With *main If \headerCount < 1 ProcedureReturn ; no additional columns EndIf DrawingMode(#PB_2DDrawing_Default) ; clean background If *entry\selected = #True Box(\header(1)\position + left.i - #tree_fx_lineMargin, y.i, \width + #tree_fx_lineMargin, \lineHeight, \customization\selBackgroundColor) Else Box(\header(1)\position + left.i - #tree_fx_lineMargin, y.i, \width + #tree_fx_lineMargin, \lineHeight, \customization\backgroundColor) EndIf If ListSize(columns()) < 1 ; just clean (no real column data in this row available) ProcedureReturn EndIf ; draw real columns Protected c.i = 1 Protected offX.i = 0 Protected widthOffset.i = \header(0)\width ForEach columns() offX.i = left.i If IsImage(columns()\imageId) offX.i = *main\lineHeight + #tree_fx_icon_margin + left.i EndIf If *entry\selected = #True ; selected Box(\header(c.i)\position + left.i - #tree_fx_lineMargin, y.i, \header(c.i)\width + #tree_fx_lineMargin, \lineHeight, \customization\selBackgroundColor) textWidth.i = DrawText(\header(c.i)\position + #tree_fx_col_margin + offX.i, y.i, columns()\text, \customization\selForegroundColor, \customization\selBackgroundColor) Else ; not selected Box(\header(c.i)\position + left.i - #tree_fx_lineMargin, y.i, \header(c.i)\width + #tree_fx_lineMargin, \lineHeight, \customization\backgroundColor) textWidth.i = DrawText(\header(c.i)\position + #tree_fx_col_margin + offX.i, y.i, columns()\text, \customization\foregroundColor, \customization\backgroundColor) EndIf If IsImage(columns()\imageId) ; draw optional image DrawingMode(#PB_2DDrawing_AlphaBlend) DrawImage(ImageID(columns()\imageId), \header(c.i)\position + #tree_fx_col_margin + left.i, y.i, \lineHeight, \lineHeight) DrawingMode(#PB_2DDrawing_Default) EndIf textWidth.i = textWidth.i - widthOffset.i If textWidth > \header(c.i)\maxWidth \header(c.i)\maxWidth = textWidth + #tree_fx_col_margin EndIf widthOffset.i = widthOffset.i + \header(c.i)\width c.i = c.i + 1 Next EndWith EndProcedure ; redraw the content of a custom tree node (recursively calling child nodes) Procedure.i _cust_tree_redraw_item(List tree.ct_entry(), level.i, *main.ct_main) Static headerHeight.i = 0 Protected textWidth.i = 0 If level.i = 0 ; let's see what is needed EndIf With *main Protected indentWidth.i = \lineHeight * #tree_fx_indentFactor + #tree_fx_icon_margin ; square images ForEach tree() If tree()\hidden = #True ; skip hidden entry and remove from ; available click targets (coordinates) tree()\x = -1 tree()\y = -1 Continue EndIf Protected xEntry.i = level.i * indentWidth.i + \scrollX ; x start Protected yEntry.i = \treeHeight - \scrollY ; y start Protected imgO.i = 0 If yEntry >= -*main\lineHeight And yEntry < *main\height ; visible entry (need to draw) tree()\x = xEntry.i tree()\y = yEntry.i tree()\checkboxLeft = 0 ; default If ListSize(tree()\childs()) > 0 ; draw open/close images DrawingMode(#PB_2DDrawing_AlphaBlend) If tree()\status = #open DrawImage(ImageID(ct_image_expanded), xEntry.i + #tree_fx_icon_margin, yEntry.i, \lineHeight, \lineHeight) Else DrawImage(ImageID(ct_image_collapsed), xEntry.i + #tree_fx_icon_margin, yEntry.i, \lineHeight, \lineHeight) EndIf xEntry.i = xEntry.i + \lineHeight + #tree_fx_icon_margin + #tree_fx_icon_margin Else xEntry.i = xEntry.i + #tree_fx_icon_margin EndIf If Bool(\flags & #flags_checkBoxes) ; draw checkboxes DrawingMode(#PB_2DDrawing_AlphaBlend) imgO.i = *main\lineHeight + #tree_fx_icon_margin If tree()\checked DrawImage(ImageID(ct_image_checked), xEntry.i, yEntry.i, \lineHeight, \lineHeight) Else DrawImage(ImageID(ct_image_unchecked), xEntry.i, yEntry.i, \lineHeight, \lineHeight) EndIf tree()\checkboxLeft = xEntry.i xEntry.i = xEntry.i + \lineHeight + #tree_fx_icon_margin EndIf If tree()\selected = #True ; draw background selection DrawingMode(#PB_2DDrawing_Default) Box(xEntry.i - #tree_fx_sel_margin, yEntry.i, \header(0)\width, \lineHeight, \customization\selBackgroundColor) EndIf If tree()\imageId <> 0 ; draw entry image DrawingMode(#PB_2DDrawing_AlphaBlend) imgO.i = *main\lineHeight + #tree_fx_icon_margin DrawImage(ImageID(tree()\imageId), xEntry.i, yEntry.i, \lineHeight, \lineHeight) xEntry.i = xEntry.i + \lineHeight + #tree_fx_icon_margin EndIf DrawingMode(#PB_2DDrawing_Default) ; draw node text If tree()\selected = #True textWidth = DrawText(xEntry.i, yEntry.i, tree()\text, \customization\selForegroundColor, \customization\selBackgroundColor) Else textWidth = DrawText(xEntry.i, yEntry.i, tree()\text, \customization\foregroundColor, \customization\backgroundColor) EndIf If textWidth > *main\header(0)\maxWidth *main\header(0)\maxWidth = textWidth + #tree_fx_lineMargin EndIf ; draw additional columns _cust_tree_redraw_columns(*main, tree()\columns(), tree()) Else ; invisible entry tree()\x = -1 tree()\y = -1 EndIf \treeHeight = \treeHeight + \lineHeight \lineCount = \lineCount + 1 If ListSize(tree()\childs()) > 0 If tree()\status = #open ; draw entries _cust_tree_redraw_item(tree()\childs(), level.i + 1, *main) ; draw line Protected lineX.i = level.i * indentWidth.i + \lineHeight / 2 + \scrollX + #tree_fx_icon_margin Protected lineH.i = \treeHeight - yEntry.i - \scrollY - \lineHeight * 1.5 Line(lineX.i, yEntry.i + \lineHeight, 1, lineH.i, #tree_fx_linecolor) Line(lineX.i, yEntry.i + \lineHeight + lineH.i, \lineHeight / 2 + #tree_fx_icon_margin, 1, #tree_fx_linecolor) Else ForEach tree()\childs() tree()\childs()\x = -1 tree()\childs()\y = -1 Next EndIf EndIf Next If level = 0 ; finalize drawing If *main\headerCount > 0 _cust_tree_redraw_header(*main) EndIf EndIf EndWith EndProcedure ; Draw all needed things for search function ; If it returns #true, the calling function kmust redraw again (eg search turned off) Procedure.i _cust_tree_redraw_search(*main.ct_main) Protected size.i = *main\lineHeight Protected x.i = *main\width - #tree_fx_scrollBarSize - size.i - 3 DrawingMode(#PB_2DDrawing_AlphaBlend | #PB_2DDrawing_Outlined) ; Box(x.i, 0, size.i, size.i, #tree_fx_searchFront) DrawImage(ImageID(ct_image_search.i), x.i, 0, size.i, size.i) ; If *main\searchMode = #True ; enable search field If *main\searchFieldId = 0 *main\searchFieldId = StringGadget(#PB_Any, *main\x + x.i - 100, *main\y + 2, 100, size.i, "", #PB_String_BorderLess) If IsFont(*main\customization\fontId) SetGadgetFont(*main\searchFieldId, FontID(*main\customization\fontId)) EndIf BindGadgetEvent(*main\searchFieldId, @_cust_tree_searchHandler(), #PB_All) ; search gadget SetActiveGadget(*main\searchFieldId) ; make active and trigger Focus event! EndIf Else ; disable search field If *main\searchFieldId <> 0 UnbindGadgetEvent(*main\searchFieldId, @_cust_tree_searchHandler()) FreeGadget(*main\searchFieldId) *main\searchFieldId = 0 _cust_tree_update_search(*main\childs(), ""); show all entries ProcedureReturn #True EndIf EndIf ProcedureReturn #False EndProcedure ; redraw the given custom tree Procedure.i redraw(*tree.ct_main) ; ensure correct column size for first column Protected x.i, colWidthCheckSum.i = 0 If *tree\headerCount < 1 ; set maximum width of first column to tree width *tree\header(0)\width = *tree\width ; no header to display *tree\treeHeight = 0 Else ; set and verify column sizes for auto-sizing If Bool(*tree\flags & #flags_autoSize) For x.i = 0 To *tree\headerCount If *tree\header(x.i)\maxWidth > 0 *tree\header(x.i)\width = *tree\header(x.i)\maxWidth + #tree_fx_lineMargin If x.i > 0 *tree\header(x.i)\position = *tree\header(x.i-1)\position + *tree\header(x.i-1)\width EndIf colWidthCheckSum.i = colWidthCheckSum.i + *tree\header(x.i)\maxWidth EndIf Next EndIf ; display some header *tree\treeHeight = *tree\lineHeight EndIf ; start redrawing *tree\treeWidth = 0 *tree\lineCount = 0 StartDrawing(CanvasOutput(*tree\gadgetId)) If IsFont(*tree\customization\fontId) DrawingFont(FontID(*tree\customization\fontId)) EndIf With *tree ; clean area Box(0,0, \width, \height, \customization\backgroundColor) ; draw root items and their childs _cust_tree_redraw_item(\childs(), 0, *tree) ; draw search gadget If Bool(*tree\flags & #flags_offerSearch) _cust_tree_redraw_search(*tree) EndIf EndWith StopDrawing() ; check if redraw is needed (because of auto-size) Protected colWidthCheckSum2.i = 0 If Bool(*tree\flags & #flags_autoSize) For x.i = 0 To *tree\headerCount If *tree\header(x.i)\maxWidth > 0 *tree\header(x.i)\width = *tree\header(x.i)\maxWidth If x.i > 0 *tree\header(x.i)\position = *tree\header(x.i-1)\position + *tree\header(x.i-1)\width EndIf colWidthCheckSum2.i = colWidthCheckSum2.i + *tree\header(x.i)\maxWidth EndIf Next If colWidthCheckSum.i <> colWidthCheckSum2.i ; Width was changed during last draw. ; Redraw needed to adapt the new sizes. redraw(*tree) EndIf EndIf ; update scrollbar gadget(s) position (now I know the maximum height) ; update vertical scrollbar SetGadgetAttribute(*tree\vScrollId, #PB_ScrollBar_Maximum, *tree\lineCount) SetGadgetAttribute(*tree\vScrollId, #PB_ScrollBar_Minimum, 0) SetGadgetAttribute(*tree\vScrollId, #PB_ScrollBar_PageLength, Round(*tree\height / *tree\lineHeight, #PB_Round_Down)-1) ; update horizontal scrollbar Protected scrollWidth = *tree\treeWidth - *tree\width + #tree_fx_scrollBarSize scrollWidth = (1 + Round(scrollWidth / 20, #PB_Round_Up)) * 20 SetGadgetAttribute(*tree\hScrollId, #PB_ScrollBar_Maximum, scrollWidth) SetGadgetAttribute(*tree\hScrollId, #PB_ScrollBar_Minimum, 0) SetGadgetAttribute(*tree\hScrollId, #PB_ScrollBar_PageLength, 20) If IsGadget(*tree\searchFieldId) ResizeGadget(*tree\searchFieldId, #PB_Ignore, #PB_Ignore, #PB_Ignore, #PB_Ignore) EndIf SetGadgetState(*tree\vScrollId, *tree\scrollY / *tree\lineHeight) EndProcedure Procedure.i clear(*tree.ct_main) ClearList(*tree\childs()) Protected x.i For x.i = 0 To *tree\headerCount *tree\header(x.i)\itemDataPtr = 0 *tree\header(x.i)\itemDataStr = "" *tree\header(x.i)\maxWidth = 0 *tree\header(x.i)\text = "Column " + Str(c.i) *tree\header(x.i)\width = #tree_fx_defaultColWidth *tree\header(x.i)\position = x.i * #tree_fx_defaultColWidth Next *tree\header(0)\text = "" SetGadgetState(*tree\hScrollId, 0) SetGadgetState(*tree\vScrollId, 0) *tree\scrollX = 0 *tree\scrollY = 0 EndProcedure ; remove all selections recursively Procedure _cust_tree_reset_selection(List tree.ct_entry()) ForEach tree() tree()\selected = #False If ListSize(tree()\childs()) > 0 _cust_tree_reset_selection(tree()\childs()) EndIf Next EndProcedure ; Check if coordinates are above a column resizer. ; Returns -1 if not, otherwise the column id to resize. Procedure.i _cust_tree_checkColumnSizer(*main.ct_main, x.i, y.i) If y.i > *main\lineHeight Or *main\headerCount < 1 ; definitely no column sizer! ProcedureReturn -1 EndIf If Bool(*main\flags & #flags_autoSize) ; no column sizer in auto-size mode ProcedureReturn -1 EndIf Protected p.i = 0 Protected c.i = 0 For c.i = 0 To *main\headerCount p.i = *main\header(c.i)\position + *main\header(c.i)\width + *main\scrollX If Abs(x.i - p.i) < #tree_fx_sel_margin ProcedureReturn c.i EndIf Next ProcedureReturn -1 EndProcedure ; Go through all tree and hide all non matching items (need redraw later!) ; Matched items will get opened (no longer collapsed) ; Returns #True if we had a match below! Procedure.i _cust_tree_update_search(List tree.ct_entry(), search.s, depth.i = 0) Protected match.i = #False Protected rMatch.i = #False Protected compare.s = "" ForEach tree() ; verify for childs compare.s = tree()\text ForEach tree()\columns() ; add column values to search criteria compare.s + " " + tree()\columns()\text Next If ListSize(tree()\childs()) > 0 ; recursively search childs rMatch.i = _cust_tree_update_search(tree()\childs(), search.s, depth.i + 1) tree()\hidden = Bool(Not rMatch.i) If (rMatch.i = #True And search.s <> "") match.i = #True tree()\status = #open ; do not collapse results! EndIf ; Check for item itself If FindString(compare.s, search.s, 1, #PB_String_NoCase) Or search.s = "" tree()\hidden = #False match.i = #True Else tree()\x = -1 tree()\y = -1 EndIf Else ; verify main entry If FindString(compare.s, search.s, 1, #PB_String_NoCase) Or search.s = "" tree()\hidden = #False match.i = #True Else tree()\hidden = #True tree()\x = -1 tree()\y = -1 EndIf EndIf Next ProcedureReturn match.i EndProcedure ; Ensure visibility during scrolling! Procedure.i _cust_tree_ensure_visibility(*main.ct_main, *currentSelected.ct_entry) With *main Protected headerHeight.i = 0 If \headerCount > 0: headerHeight.i = \lineHeight: EndIf If *currentSelected\y < headerHeight.i + (2 * \lineHeight) \scrollY = \scrollY - \lineHeight If \scrollY < 0: \scrollY = 0: EndIf EndIf If *currentSelected\y > \height - #tree_fx_scrollBarSize - \lineHeight - (2 * \lineHeight) If \scrollY + \height < \treeHeight + \lineHeight \scrollY = \scrollY + \lineHeight EndIf EndIf EndWith EndProcedure ; Handles the events of the search StringGadget Procedure.i _cust_tree_searchHandler() Protected gadId.i = EventGadget() Protected gadEvt.i = EventType() Protected *tree.ct_main = 0 ForEach cust_trees() If cust_trees()\searchFieldId = gadId.i *tree = cust_trees() Break EndIf Next If *tree = 0: ProcedureReturn: EndIf ; something was typed into the search field If gadEvt.i = #PB_EventType_Change _cust_tree_update_search(*tree\childs(), GetGadgetText(*tree\searchFieldId)) *tree\scrollX = 0 *tree\scrollY = 0 ; redraw tree redraw(*tree) EndIf ; set or remove keyboard shortct to check ESC key Protected win.i = EventWindow() If IsWindow(win.i) If gadEvt.i = #PB_EventType_Focus AddKeyboardShortcut(win.i, #PB_Shortcut_Escape, #SearchMenuHookID) EndIf If gadEvt.i = #PB_EventType_LostFocus RemoveKeyboardShortcut(win.i, #PB_Shortcut_Escape) EndIf EndIf EndProcedure ; Call this in your event loop to enable search field to close on ESC key Procedure.i eventHandler(*main.ct_main) If EventMenu() = #SearchMenuHookID If *main\searchMode = #True *main\searchMode = #False redraw(*main) redraw(*main) EndIf EndIf EndProcedure ; Handle keystrokes in Canvas gadget (move, select) Procedure.i _cust_tree_keyHandler(gadId.i, *main.ct_main) Protected triggerEvent.i = #False Select GetGadgetAttribute(gadId.i, #PB_Canvas_Key) Case #PB_Shortcut_Up If *currentSelected\parent <> #parent_root ; sub-tree ForEach *currentSelected\parent\childs() If *currentSelected\parent\childs() = *currentSelected *currentSelected\selected = #False If PreviousElement(*currentSelected\parent\childs()) *currentSelected\parent\childs()\selected = #True triggerEvent.i = #True *currentSelected = *currentSelected\parent\childs() If ListSize(*currentSelected\childs()) > 0 And *currentSelected\status = #open *currentSelected\parent\childs()\selected = #False LastElement(*currentSelected\childs()) *currentSelected = *currentSelected\childs() *currentSelected\selected = #True EndIf Else ; use parent node (if exists) If *currentSelected\parent <> 0 *currentSelected = *currentSelected\parent *currentSelected\selected = #True triggerEvent.i = #True EndIf EndIf Break EndIf Next Else ; root-tree ForEach *main\childs() If *main\childs() = *currentSelected If PreviousElement(*main\childs()) *currentSelected\selected = #False *currentSelected = *main\childs() *currentSelected\selected = #True triggerEvent.i = #True If ListSize(*currentSelected\childs()) > 0 And *currentSelected\status = #open *currentSelected\selected = #False LastElement(*currentSelected\childs()) *currentSelected = *currentSelected\childs() *currentSelected\selected = #True EndIf EndIf Break EndIf Next EndIf ; redraw tree _cust_tree_ensure_visibility(*main, *currentSelected) redraw(*main) Case #PB_Shortcut_Down If ListSize(*currentSelected\childs()) > 0 And *currentSelected\status = #open ; dig into childs *currentSelected\selected = #False FirstElement(*currentSelected\childs()) *currentSelected = *currentSelected\childs() *currentSelected\selected = #True triggerEvent.i = #True Else ; try next in current list If *currentSelected\parent <> #parent_root ; sub-tree ForEach *currentSelected\parent\childs() If *currentSelected\parent\childs() = *currentSelected *currentSelected\selected = #False If NextElement(*currentSelected\parent\childs()) *currentSelected = *currentSelected\parent\childs() *currentSelected\selected = #True triggerEvent.i = #True Else ; use parent node list (if exists) If *currentSelected\parent\parent <> #parent_root ForEach *currentSelected\parent\parent\childs() If *currentSelected\parent\parent\childs() = *currentSelected\parent NextElement(*currentSelected\parent\parent\childs()) *currentSelected = *currentSelected\parent\parent\childs() *currentSelected\selected = #True triggerEvent.i = #True Break EndIf Next Else ; jump to root ForEach *main\childs() If *main\childs() = *currentSelected\parent NextElement(*main\childs()) *currentSelected = *main\childs() *currentSelected\selected = #True triggerEvent.i = #True Break EndIf Next EndIf EndIf Break EndIf Next Else ; root-tree ForEach *main\childs() If *main\childs() = *currentSelected If NextElement(*main\childs()) *currentSelected\selected = #False *currentSelected = *main\childs() *currentSelected\selected = #True triggerEvent.i = #True EndIf Break EndIf Next EndIf EndIf ; redraw tree _cust_tree_ensure_visibility(*main, *currentSelected) redraw(*main) Case #PB_Shortcut_Space If Bool(*main\flags & #flags_checkBoxes) *currentSelected\checked = Bool(Not *currentSelected\checked) triggerEvent.i = #True ; redraw tree redraw(*main) EndIf Case #PB_Shortcut_Left If ListSize(*currentSelected\childs()) > 0 And *currentSelected\status = #open *currentSelected\status = #collapsed triggerEvent.i = #True ; redraw tree redraw(*main) EndIf Case #PB_Shortcut_Right If ListSize(*currentSelected\childs()) > 0 And *currentSelected\status = #collapsed *currentSelected\status = #open triggerEvent.i = #True ; redraw tree redraw(*main) EndIf EndSelect If triggerEvent.i = #True ; trigger some final change event to allow applications to react on this PostEvent(#PB_Event_Gadget, EventWindow(), EventGadget(), #PB_EventType_Change) EndIf EndProcedure ; event handler for canvas gadget events Procedure.i _cust_tree_eventHandler() Static selColumnId.i = 0 Static resizing.b = #False Static resizingCol.i = -1 Static cursorIsChanged.i = #False Protected gadId.i = EventGadget() Protected gadEvt.i = EventType() Protected *tree.ct_main = 0 ForEach cust_trees() If cust_trees()\gadgetId = gadId.i *tree = cust_trees() Break EndIf Next If *tree = 0: ProcedureReturn: EndIf ; handle events If gadEvt.i = #PB_EventType_LeftClick ; get clicked entry Protected mouseX.i = GetGadgetAttribute(gadId.i, #PB_Canvas_MouseX) Protected mouseY.i = GetGadgetAttribute(gadId.i, #PB_Canvas_MouseY) Protected dontSelect.i = #False If Bool(*tree\flags & #flags_offerSearch) ; verify if mouse was clicked on search button Protected size.i = *tree\lineHeight Protected x.i = *tree\width - #tree_fx_scrollBarSize - size.i If mouseX.i > x.i And mouseX.i < x.i + size.i If mouseY.i > 0 And mouseY.i < size.i *tree\searchMode = Bool(Not *tree\searchMode) ; redraw tree redraw(*tree) dontSelect.i = #True EndIf EndIf EndIf Protected *Selected.ct_entry = _cust_tree_getItem(*tree, *tree\childs(), mouseX.i, mouseY.i) If *Selected <> 0 And dontSelect.i = #False ; some entry was clicked If GetGadgetAttribute(gadId.i, #PB_Canvas_Modifiers) = #PB_Canvas_Control And Bool(*tree\flags & #flags_multiSelect) ; CTRL *Selected\selected = Bool(Not *Selected\selected) ElseIf GetGadgetAttribute(gadId.i, #PB_Canvas_Modifiers) = #PB_Canvas_Shift And Bool(*tree\flags & #flags_multiSelect) ; SHIFT (not supported as usual) *Selected\selected = Bool(Not *Selected\selected) Else ; nothing, ordinary click _cust_tree_reset_selection(*tree\childs()) *Selected\selected = #True EndIf If *Selected\selected = #True *currentSelected = *Selected Else *currentSelected = 0 EndIf If Bool(*tree\flags & #flags_checkBoxes) And mouseX.i > *Selected\checkboxLeft And mousex.i < *Selected\checkboxLeft + *tree\lineHeight ; checkbox was clicked *Selected\checked = Bool(Not *Selected\checked) Else ; open close clicked? If ListSize(*Selected\childs()) > 0 And mouseX.i < *Selected\x + *tree\lineHeight + #tree_fx_icon_margin ; open/close If *Selected\status = #open *Selected\status = #collapsed Else *Selected\status = #open EndIf EndIf EndIf EndIf ; redraw tree redraw(*tree) EndIf If gadEvt.i = #PB_EventType_MouseMove selColumnId.i = _cust_tree_checkColumnSizer(*tree, GetGadgetAttribute(gadId.i, #PB_Canvas_MouseX), GetGadgetAttribute(gadId.i, #PB_Canvas_MouseY)) If selColumnId.i > -1 _cust_tree_cursorSplit(gadId.i) cursorIsChanged.i = #True Else If cursorIsChanged.i = #True _cust_tree_cursorSplit(gadId.i, #True) cursorIsChanged.i = #False EndIf EndIf Protected c.i If resizing.b = #True And resizingCol.i > -1 ; move selected column width *tree\header(resizingCol)\width = GetGadgetAttribute(gadId.i, #PB_Canvas_MouseX) - *tree\header(resizingCol)\position - *tree\scrollX If *tree\header(resizingCol)\width < *tree\lineHeight *tree\header(resizingCol)\width = *tree\lineHeight EndIf ; re-pos the other columns For c.i = resizingCol+1 To *tree\headerCount *tree\header(c.i)\position = *tree\header(c.i-1)\position + *tree\header(c.i-1)\width Next ; redraw tree redraw(*tree) EndIf EndIf If gadEvt.i = #PB_EventType_LeftButtonDown And selColumnId.i > -1 resizing.b = #True resizingCol.i = selColumnId.i EndIf If gadEvt.i = #PB_EventType_LeftButtonUp resizing.b = #False resizingCol.i = -1 EndIf If gadEvt.i = #PB_EventType_KeyDown ; Keystrokes from Canvas to get using GetGadgetAttribute(gadId.i, #PB_Canvas_Key) If *currentSelected <> 0 ; use key handler _cust_tree_keyHandler(gadId.i, *tree) EndIf EndIf If gadEvt.i = #PB_EventType_MouseWheel ; Mouse wheel scrolling *tree\scrollY = *tree\scrollY - (GetGadgetAttribute(gadId.i, #PB_Canvas_WheelDelta) * *tree\lineHeight) If *tree\scrollY > *tree\treeHeight - *tree\height + #tree_fx_scrollBarSize *tree\scrollY = *tree\treeHeight - *tree\height + #tree_fx_scrollBarSize EndIf If *tree\scrollY < 0: *tree\scrollY = 0: EndIf SetGadgetState(*tree\vScrollId, *tree\scrollY / *tree\lineHeight) ; redraw tree redraw(*tree) EndIf If gadEvt.i = #PB_EventType_SizeItem *tree\width = GadgetWidth(gadId) + #tree_fx_scrollBarSize *tree\height = GadgetHeight(gadId) + #tree_fx_scrollBarSize ; redraw tree redraw(*tree) EndIf EndProcedure ; event handler for scroll gadget events Procedure.i _cust_tree_scrollEventHandler() Protected gadId.i = EventGadget() Protected gadEvt.i = EventType() Protected *tree.ct_main = 0 ForEach cust_trees() If cust_trees()\vScrollId = gadId.i Or cust_trees()\hScrollId = gadId.i *tree = cust_trees() Break EndIf Next If *tree = 0: ProcedureReturn: EndIf If gadId.i = *tree\vScrollId *tree\scrollY = GetGadgetState(gadId.i) * *tree\lineHeight EndIf If gadId.i = *tree\hScrollId *tree\scrollX = -GetGadgetState(gadId.i) EndIf If gadEvt.i = #PB_EventType_LeftClick Or gadEvt.i = #PB_EventType_RightClick ; make the canvas the active gadget SetActiveGadget(*tree\gadgetId) EndIf ; redraw tree redraw(*tree) EndProcedure Procedure.i resizeTree(*treeId.ct_main, x.i, y.i, width.i, height.i) With *treeId If x.i = \x And y.i = \y And width.i = \width And height.i = \height ; Do nothing if size has not changed! ; Do not all the work and redraw in this case. ProcedureReturn EndIf \x = x.i \y = y.i \width = width.i \height = height.i ResizeGadget(\gadgetId, x.i, y.i, width.i - #tree_fx_scrollBarSize, height.i - #tree_fx_scrollBarSize) ResizeGadget(\vScrollId, x.i + width.i - #tree_fx_scrollBarSize, y.i, #tree_fx_scrollBarSize, height.i - #tree_fx_scrollBarSize) ResizeGadget(\hScrollId, x.i, y.i + height.i - #tree_fx_scrollBarSize, width.i - #tree_fx_scrollBarSize, #tree_fx_scrollBarSize) redraw(*treeId) EndWith EndProcedure ; release all ressources reserved for this treeview Procedure.i remove(*tree.ct_main) ForEach cust_trees() If cust_trees() = *tree ; delete ressources FreeGadget(*tree\vScrollId) FreeGadget(*tree\hScrollId) If IsGadget(*tree\searchFieldId) FreeGadget(*tree\searchFieldId) EndIf FreeGadget(*tree\gadgetId) DeleteElement(cust_trees()) Break EndIf Next EndProcedure ; create a new custom tree gadget Procedure.i create(x.i, y.i, width.i, height.i, flags.i = 0) Protected treeId.i = AddElement(cust_trees()) Protected c.i ; Prepare tree and fill tree structure with some working defaults With cust_trees() \x = x.i \y = y.i \width = width.i \height = height.i \scrollX = 0 \scrollY = 0 \flags = flags.i \customization\backgroundColor = RGB(255,255,255) \customization\foregroundColor = RGB(60,60,60) \customization\selBackgroundColor = \customization\foregroundColor \customization\selForegroundColor = \customization\backgroundColor \customization\headerForegroundColor = \customization\foregroundColor \customization\headerBackroundColor = RGB(230,230,230) \customization\lineColor = RGB(180,180,180) \lineHeight = 20 ; some default \gadgetId = CanvasGadget(#PB_Any, x.i, y.i, width.i - #tree_fx_scrollBarSize, height.i - #tree_fx_scrollBarSize, #PB_Canvas_Border | #PB_Canvas_Keyboard) \vScrollId = ScrollBarGadget(#PB_Any, x.i + width.i - #tree_fx_scrollBarSize, y.i, #tree_fx_scrollBarSize, height.i - #tree_fx_scrollBarSize, 0, 1, 1, #PB_ScrollBar_Vertical) \hScrollId = ScrollBarGadget(#PB_Any, x.i, y.i + height.i - #tree_fx_scrollBarSize, width.i - #tree_fx_scrollBarSize, #tree_fx_scrollBarSize, 0, 1, 1) ; set some default headers For c.i = 0 To ArraySize(\header()) \header(c.i)\text = "Column " + Str(c.i) \header(c.i)\position = c.i * #tree_fx_defaultColWidth \header(c.i)\width = #tree_fx_defaultColWidth Next \header(0)\text = "" ; initial tree part ; bind gadget events BindGadgetEvent(\gadgetId, @_cust_tree_eventHandler(), #PB_All) ; Canvas BindGadgetEvent(\hScrollId, @_cust_tree_scrollEventHandler(), #PB_All) ; Scroll X BindGadgetEvent(\vScrollId, @_cust_tree_scrollEventHandler(), #PB_All) ; Scroll Y EndWith ProcedureReturn treeId.i EndProcedure ; customize the given custom tree gadget Procedure.i customize(*tree.ct_main, *config.ct_customizing) With *tree\customization If *config\fontId <> #PB_Ignore \fontId = *config\fontId *tree\lineHeight = _cust_tree_getLineHeight(*config\fontId) EndIf If *config\foregroundColor <> #PB_Ignore \foregroundColor = *config\foregroundColor EndIf If *config\backgroundColor <> #PB_Ignore \backgroundColor = *config\backgroundColor EndIf If *config\selForegroundColor <> #PB_Ignore \selForegroundColor = *config\backgroundColor EndIf If *config\selBackgroundColor <> #PB_Ignore \selBackgroundColor = *config\foregroundColor EndIf If *config\selForegroundColor <> #PB_Ignore \selForegroundColor = *config\selForegroundColor EndIf If *config\selBackgroundColor <> #PB_Ignore \selBackgroundColor = *config\selBackgroundColor EndIf If *config\headerForegroundColor <> #PB_Ignore \headerForegroundColor = *config\headerForegroundColor EndIf If *config\headerBackroundColor <> #PB_Ignore \headerBackroundColor = *config\headerBackroundColor EndIf If *config\lineColor <> #PB_Ignore \lineColor = *config\lineColor EndIf EndWith EndProcedure ; add a new item to custom tree gadget Procedure.i addItem(*treeId.ct_main, *parent.ct_entry, text.s, imageId.i = 0, itemData.s = "") Protected x.i Protected *originalParent = *parent If *parent = #parent_root *parent = AddElement(*treeId\childs()) Else *parent = AddElement(*parent\childs()) EndIf *parent\status = #open ; initial open *parent\imageId = imageId.i *parent\itemDataStr = itemData.s *parent\parent = *originalParent If FindString(text.s, #TAB$) < 1; Or 1=1 ; only simple entry *parent\text = text.s Else ; additional text *parent\text = StringField(text.s, 1, #TAB$) Protected headCnt.i = CountString(text.s, #TAB$) + 1 For x.i = 2 To headCnt.i AddElement(*parent\columns()) *parent\columns()\text = StringField(text.s, x.i, #TAB$) Next If *treeId\headerCount < headCnt.i - 1: *treeId\headerCount = headCnt.i - 1: EndIf EndIf ProcedureReturn *parent EndProcedure ; removes all selection flags for the given entry and all childs Procedure unselectAll(List tree.ct_entry()) ForEach tree() tree()\selected = #False If ListSize(tree()\childs()) > 0 unselectAll(tree()\childs()) EndIf Next EndProcedure ; internal sort function (recursive) Procedure.i _cust_tree_sort(List tree.ct_entry(), Mode.i) SortStructuredList(tree(), Mode.i, OffsetOf(ct_entry\text), #PB_String) ForEach tree() If ListSize(tree()\childs()) > 0 _cust_tree_sort(tree()\childs(), Mode.i) EndIf Next EndProcedure ; sort all entries in the threeview incl. redraw Procedure sort(*treeId.ct_main, Mode.i = #PB_Sort_Ascending | #PB_Sort_NoCase) _cust_tree_sort(*treeId\childs(), Mode.i) redraw(*treeId) EndProcedure ; internal column sort function (recursive) Procedure.i _cust_tree_column_sort_prepare(List tree.ct_entry(), columnId.i) If SelectElement(tree()\columns(), columnId.i) tree()\sortValue = tree()\columns()\text Else tree()\sortValue = "nö" EndIf ForEach tree() If SelectElement(tree()\columns(), columnId.i) tree()\sortValue = tree()\columns()\text Else tree()\sortValue = "nö" EndIf If ListSize(tree()\childs()) > 0 _cust_tree_column_sort_prepare(tree()\childs(), columnId.i) EndIf Next EndProcedure ; internal column sort function (recursive) Procedure.i _cust_tree_column_sort(List tree.ct_entry(), Mode.i) SortStructuredList(tree(), Mode.i, OffsetOf(ct_entry\sortValue), #PB_String) ForEach tree() If ListSize(tree()\childs()) > 0 _cust_tree_column_sort(tree()\childs(), Mode.i) EndIf Next EndProcedure ; sort all entries in the threeview by column incl. redraw Procedure sortColumn(*treeId.ct_main, columnId.i, Mode.i = #PB_Sort_Ascending | #PB_Sort_NoCase) If columnId.i < 0 ; no sort ProcedureReturn EndIf If columnId.i = 0 ; standard sort _cust_tree_sort(*treeId\childs(), Mode.i) Else ; column sort _cust_tree_column_sort_prepare(*treeId\childs(), columnId.i - 1) _cust_tree_column_sort(*treeId\childs(), Mode.i) EndIf redraw(*treeId) EndProcedure ; internal selected search function (recursive) Procedure.i _cust_tree_getSelected(List tree.ct_entry(), List entries.ct_entry()) ForEach tree() If tree()\selected = #True AddElement(entries()) entries() = tree() EndIf If ListSize(tree()\childs()) > 0 _cust_tree_getSelected(tree()\childs(), entries()) EndIf Next EndProcedure ; returns all selected entries in submitted entries LinkedList of type ct_entry. Procedure.i getSelected(*treeId.ct_main, List entries.ct_entry()) ClearList(entries()) _cust_tree_getSelected(*treeId\childs(), entries()) EndProcedure ; internal ItemDataStr search function (recursive) Procedure.i _cust_tree_getByItemDataStr(List tree.ct_entry(), search.s, List entries.ct_entry()) ForEach tree() If tree()\itemDataStr = search.s AddElement(entries()) entries() = tree() EndIf If ListSize(tree()\childs()) > 0 _cust_tree_getByItemDataStr(tree()\childs(), search.s, entries()) EndIf Next EndProcedure ; returns all found entries in submitted entries LinkedList of type ct_entry. ; it searches in itemDataStr (case sensitive!) Procedure.i getByItemDataStr(*treeId.ct_main, search.s, List entries.ct_entry()) ClearList(entries()) _cust_tree_getByItemDataStr(*treeId\childs(), search.s, entries()) EndProcedure ; internal ItemDataPtr search function (recursive) Procedure.i _cust_tree_getByItemDataPtr(List tree.ct_entry(), search.i, List entries.ct_entry()) ForEach tree() If tree()\itemDataPtr = search.i AddElement(entries()) entries() = tree() EndIf If ListSize(tree()\childs()) > 0 _cust_tree_getByItemDataPtr(tree()\childs(), search.i, entries()) EndIf Next EndProcedure ; returns all found entries in submitted entries LinkedList of type ct_entry. ; it searches in itemDataPtr Procedure.i getByItemDataPtr(*treeId.ct_main, search.i, List entries.ct_entry()) ClearList(entries()) _cust_tree_getByItemDataPtr(*treeId\childs(), search.i, entries()) EndProcedure ; Resize a given header. ; You need to call redraw() in order to see the changes. Procedure.i resizeHeader(*treeId.ct_main, columnId.i, width.i) If columnId.i < 0 Or columnId.i > *treeId\headerCount ProcedureReturn #False EndIf Protected c.i *treeId\header(columnId.i)\width = width.i ; re-pos the other columns For c.i = columnId+1 To *treeId\headerCount *treeId\header(c.i)\position = *treeId\header(c.i-1)\position + *treeId\header(c.i-1)\width Next ProcedureReturn #True EndProcedure Procedure.i setIcon(*entry.ct_entry, columnId.i, imageId.i) If columnId.i < 1 ; main entry *entry\imageId = imageId.i Else If SelectElement(*entry\columns(), columnId.i - 1) <> 0 *entry\columns()\imageId = imageId.i Else Debug "Column " + Str(columnId.i) + " does not exist for entry '" + *entry\text + "'" EndIf EndIf EndProcedure ; returns the column Id the cursor is currently on ; -1 is "not over a header" Procedure.i mouseOnHeader(*treeId.ct_main, x.i = #PB_Ignore, y.i = #PB_Ignore) If x.i = #PB_Ignore x.i = GetGadgetAttribute(*treeId\gadgetId, #PB_Canvas_MouseX) EndIf If y.i = #PB_Ignore y.i = GetGadgetAttribute(*treeId\gadgetId, #PB_Canvas_MouseY) EndIf If y.i > *treeId\lineHeight Or *treeId\headerCount < 1 ; definitely no column header! ProcedureReturn -1 EndIf Protected p.i = 0 Protected c.i = 0 For c.i = 0 To *treeId\headerCount p.i = *treeId\header(c.i)\position + *treeId\header(c.i)\width + *treeId\scrollX If x.i < p.i ProcedureReturn c.i EndIf Next ProcedureReturn -1 EndProcedure EndModule CompilerIf #PB_Compiler_IsMainFile ; ####################################################################### ; TEST ;{ ####################################################################### ;{ Generated using regify GIT repo includes/tools/bin2dataSection.pb DataSection folderyellow_png: Data.q $0A1A0A0D474E5089,$524448490D000000,$3000000030000000,$F902570000000608,$4752730100000087 Data.q $0000E91CCEAE0042,$FF0044474B620600,$93A7BDA0FF00FF00,$7359487009000000,$C3140000C3140000 Data.q $000000424D701501,$04DB07454D497407,$E5DE7EDB000B0B16,$54414449B8070000,$575C8F4999EDDE68 Data.q $3ABAA1BEE77FC715,$445890B1831019C4,$04A26C3624604688,$43E4B0592C483163,$C22241B22C160904 Data.q $31002C166B023E7B,$98C0885020589158,$50F6EC83B638A090,$78B0F7BA6F0D7774,$AA2C11B9EA77BEF7 Data.q $7ABD5552A4FC2465,$3C14F73FFCCF7FF7,$F57D52F78F5C9EB9,$FD7D7F0FA79FEEB1,$DBC680AFE738AFCB Data.q $FFD0501DAFE24401,$BECC5E4273210016,$4902A71FF7EAF227,$2F67E82F93FF7E42,$F01E0D0EFF5F077F Data.q $51D414E0668F3380,$BF7FBB88E0F961CE,$3C27FE46FD2EA7CE,$93EA55C5EA4B3610,$7756887C28B880BF Data.q $3F98CCD6501CA151,$2F57EB7D5B4419F3,$D01DF1273E2C93AA,$184C1E8DB40459F8,$B8DD579367C6500D Data.q $4AB3E2F1BD0AEB76,$1BFE7CAF8B172D59,$9B0D9B00A5BEBC6F,$885C424146F87BC5,$AAE86D01AC3924D9 Data.q $C5908B8FE1FB3C6B,$17DC317824A369FB,$183358651033F749,$E421C541442C634A,$45B5411455165108 Data.q $37873F3BD5F3315C,$AC9CD0DA640A5BE6,$4AAD47B99B89BE08,$25B543200BA2BD18,$66FD742103E08C84 Data.q $0EA02A0A2126BD1A,$CAF48A032C219068,$0057B45497A22DD8,$CB9CA28444798A8D,$A1C84247BDCB5B83 Data.q $090C2409F51AAB34,$0777479931E7BFDB,$D54D15EBA8EF04F5,$CDBD08077C1034D2,$BB4B26028D1485E8 Data.q $0292A342C5338BF8,$C511154045F7A3A2,$7BEDE372DFB7B089,$A8371CD2A880E68F,$626E80B4610A137A Data.q $AAA841085704C458,$47158B4735272735,$7D43AA094E842747,$CBEF199C9788BA10,$B6EAD170404A1EA0 Data.q $FD8CB2C94851A8BE,$42A94EAEEF9B92FD,$8BA50C7F9288FA3F,$AC667CB459B0930B,$E1AAF59EF3A1401F Data.q $072AFE5E2E13FDDE,$9D0A53E9F4097E2F,$876114EBF2674E4D,$21684803BC37FBBC,$F06D7ADF7395DF54 Data.q $B8C49D5E3969BD40,$C3709460D0156E4B,$59E37AC77DE28A86,$1D53AFA5E0E23FDC,$C72D98227AD119FE Data.q $7EF1F21549DA4F89,$964B40218B614538,$BC007E776FF373EF,$BE5E0E6DFABF5F96,$12C7FCDC87C3C3C2 Data.q $BA49BD8231710047,$66C95411C1A6D2ED,$4B5668D7E2FC5C7B,$05128C9332541B54,$AC1A5FBC59F85175 Data.q $863A73EC9B36D8C2,$381CFB0EB7BB9E73,$DB95ECFE6F0AF978,$BA72B5DAA4BD2F17,$5DA1A5D464754F4B Data.q $8F45D446DEAFC03F,$091F3C5A3E39B231,$72A24DFC65102871,$36723863A703C4F5,$2EF7C3E3E24C6EEB Data.q $53AD5AD74B7FF774,$64C2F592FB8CE9D5,$572D63F14A4FAC93,$0A9388D2D246520F,$3EC30FC0C3843143 Data.q $51DB2D59444319FF,$020C239D41B2F581,$4C6A564AF333C110,$2B1EAD62DC91874C,$60E133A8B0DAC007 Data.q $99431AFFBAC19231,$20BAF7A0547E75D1,$6571A99B33B00C1A,$FCA2DD0DE26E810A,$1A68D0F5A0F0C3B3 Data.q $DC6B42B58D71F01E,$8AC98B293157932E,$0C85E48C0B3C371A,$690494A8A2A028C1,$A802D005CBA30023 Data.q $011AB022F607A80C,$A30CEE828C37934B,$9B192E4424A5570F,$6A9903D31A443A39,$F1376629D3A99D22 Data.q $6AD63061187844D9,$24458C888B118876,$9D9F6D8F7DB41F1F,$72E26168C4CEC989,$BA312EF6EA6A3E4A Data.q $D0769D88842B6301,$6C95A2EA37526554,$08CAAF8F2B58C015,$99AA18367B313339,$3CA6D61170860F8D Data.q $3BA365826C589116,$84CDA5FA4F51CA87,$56C609B2308EFCEC,$0128433323CAC40E,$309BDF789DA74FD8 Data.q $62CB607C332E65B0,$2275FBAC615D8EE2,$F04215501AA039DB,$6F34951C634F99DB,$E16393F3D25AAB0D Data.q $D68F436754386159,$3A6E488B481C407B,$253380E69655D4C3,$927E721373DE609B,$B067608D48C0BBA0 Data.q $8EBF7EDA82A94652,$6CBCB498F253738B,$EF5C4CE0BB76CB11,$E2DB10868C1CC30A,$D2C5AD8B33B40C81 Data.q $53E83ED955899563,$118577D184DD611A,$2751440784C87F0C,$3221958E4CD1D0E6,$6C20D6C342AA6367 Data.q $6D558CC1186EDD88,$F56D4F222CF11817,$A67B2BDF238564C2,$28DAA2553E686713,$6656AA4FEE971855 Data.q $C798C0BBDEB6C708,$3C665C1BCC52ACA3,$FBE1E9F8378FE370,$60D3BB4CA51D3F78,$F56D9FB5EE30A848 Data.q $F77833C8E2863DD8,$C73FCEE78151BDFA,$B329DCEF873B9F0B,$32CD9A9E6194EED0,$AF46059D6E1D9823 Data.q $3CB6B3BDDC665E50,$AF71A80F23EF04A0,$0A89CCA9564D589A,$57CD4D000118B0A3,$681CC042F04639D8 Data.q $72756BA3AEF8F782,$6D6243484FB628F0,$D872A6B4386DDB8C,$1D1C257A30AECB03,$734FC9FF3E2619DA Data.q $948D37AD7413F970,$C2FEEA6B58D6A84D,$76EF9F6CD793B81C,$BFBC66516287D188,$787CB02BD5BC06B8 Data.q $849B5EA6867AAE9B,$62ABF23F6C6186C9,$F1ED2669C213F025,$4B8488C2BB4E4266,$F47B707939A7F883 Data.q $EAD27CE4538D3880,$06126A99E49478C6,$77DF3333E44C33B6,$5EFF86A93B1B118D,$D306825B415894F2 Data.q $249CB03EDDE3A6BE,$3B57899D91693162,$CD040625DD164B4D,$B40D34038929ACA2,$78C561699D0EED8D Data.q $DD22E9328B293666,$D6F8A9FBC7A46219,$7F14BA1152736558,$2701EFB3DB112185,$D70D4F3663611193 Data.q $10E28619D96FD4DA,$2E8CA6738E7A84F1,$9C17F9CD64EF168E,$55E08BDE2E24E00F,$CB5836D8DC4506AC Data.q $5853CEC66B88BDF8,$D8E94890FB532145,$0B53F78A3814BE9C,$8C24328CB58A9B9F,$FAC26C16E90F651A Data.q $B801730FC5FEBF0F,$96DA9739966F9174,$63F68845FF8B8219,$FEF78657A4FDA730,$BFA3FA8D3EF81BB3 Data.q $FDD9869FF87173D3,$40545145DBFC6704,$19FE9393BE915055,$BCBB12A0E77D2080,$7F572F5BA9CCB6B4 Data.q $46BD42A078FEA6F9,$906F1C29504FE0F6,$1EDEC1915F0BEB3C,$7F6566463DDC1994,$EB7B967ECFFFB872 Data.q $63FDC35AA42B2C0E,$A1AAA405D211CEE4,$959D0C8A26BFFA6E,$907C2E0CFEE8E650,$47C2E0CB2007ECF0 Data.q $8E7CC1DC829FE7D1,$5D0665A25FB22DD6,$9B9C8F18C78AAA80,$C45C801FCF0773A3,$FAE4FF51FC9E87C3 Data.q $78C25AC505FEBFBF,$00000000F7457727,$826042AE444E4549 EndDataSection ;} If OpenWindow(0, 10, 10, 800, 480, "Custom TreeGadget", #PB_Window_SystemMenu) ; PREPARE SOME CUSTOMIZING Define FolderGadget.i = CatchImage(#PB_Any, ?folderyellow_png) Define Config.TreeView::ct_customizing With Config \fontId = LoadFont(#PB_Any, "Arial", 10) \foregroundColor = RGB(60,60,60) \backgroundColor = RGB(250,250,250) \selForegroundColor = RGB(255,255,255) \selBackgroundColor = RGB(20,20,255) \lineColor = #PB_Ignore \headerBackroundColor = #PB_Ignore \headerForegroundColor = #PB_Ignore EndWith ;{ CREATE TREEGADGET #1 Define *Tree1.TreeView::ct_main = TreeView::create(10, 10, 500, 460, TreeView::#flags_multiSelect | TreeView::#flags_offerSearch | TreeView::#flags_checkBoxes ) TreeView::customize(*Tree1, Config) ; two column Define *root1.TreeView::ct_entry = TreeView::addItem(*Tree1, TreeView::#parent_root, "B Root1"+#TAB$+"col1"+#TAB$+"col2", FolderGadget.i) Define *root2.TreeView::ct_entry = TreeView::addItem(*Tree1, TreeView::#parent_root, "A Root2"+#TAB$+"", FolderGadget.i) Define *root3.TreeView::ct_entry = TreeView::addItem(*Tree1, TreeView::#parent_root, "C Root3"+#TAB$+"", FolderGadget.i) Define *root4.TreeView::ct_entry = TreeView::addItem(*Tree1, TreeView::#parent_root, "C Root4"+#TAB$+"", FolderGadget.i) Define *root5.TreeView::ct_entry = TreeView::addItem(*Tree1, TreeView::#parent_root, "C Root5"+#TAB$+"", 0) Define *entry1.TreeView::ct_entry = TreeView::addItem(*Tree1, *root1, "Z Sub1 of root 1"+#TAB$+"col1"+#TAB$+"col2", FolderGadget.i) Define *entry2.TreeView::ct_entry = TreeView::addItem(*Tree1, *root1, "B Sub2 of root 1"+#TAB$+"col1"+#TAB$+"col2 dsabhdbhuds bhudsa bhudsabhudsa", FolderGadget.i) Define *entry3.TreeView::ct_entry = TreeView::addItem(*Tree1, *root2, "B Very much entries here for test...", FolderGadget.i) Define *entry4.TreeView::ct_entry = TreeView::addItem(*Tree1, *root2, "C Sub2 of root 2", FolderGadget.i) Define *entry5.TreeView::ct_entry = TreeView::addItem(*Tree1, *root2, "A Sub3 of root 2"+#TAB$+"col1", FolderGadget.i) Define *entry6.TreeView::ct_entry = TreeView::addItem(*Tree1, *root2, "Sub4 of root 2", FolderGadget.i) Define *entry7.TreeView::ct_entry = TreeView::addItem(*Tree1, *entry4, "Sub1 of Entry 4 Very much entries here for test... Lala"+#TAB$+"col1"+#TAB$+"col2", FolderGadget.i) TreeView::setIcon(*entry1, 1, FolderGadget.i) TreeView::setIcon(*root2, 1, FolderGadget.i) *Tree1\header(0)\text = "Header 0" *Tree1\header(1)\text = "Header 1" *Tree1\header(2)\text = "Header 2" Define x.i For x.i = 1 To 1000 TreeView::addItem(*Tree1, *entry3, "File Entry " + Str(x.i) + #TAB$ + Str(Random(9999,1000)) + " B", FolderGadget.i) Next *entry3\status = TreeView::#collapsed ; collapse one entry TreeView::sort(*Tree1) ; sort all entries ;} ;{ CREATE TREEGADGET #2 Define *Tree2.TreeView::ct_main = TreeView::create(520, 10, 260, 460) With Config \fontId = LoadFont(#PB_Any, "Courier New", 14) \foregroundColor = RGB(40,40,70) \backgroundColor = RGB(200,200,225) \selForegroundColor = RGB(255,0,0) \selBackgroundColor = RGB(255,255,0) EndWith TreeView::customize(*Tree2, Config) Define *root21.TreeView::ct_entry = TreeView::addItem(*Tree2, TreeView::#parent_root, "Entry 1", FolderGadget.i) Define *root22.TreeView::ct_entry = TreeView::addItem(*Tree2, TreeView::#parent_root, "Entry 2", FolderGadget.i) For x.i = 1 To 20 TreeView::addItem(*Tree2, *root21, "Sub "+Str(x), FolderGadget.i) Next ;} TreeView::redraw(*Tree1) TreeView::redraw(*Tree2) Define Event.i, Quit.i Repeat Event = WaitWindowEvent() TreeView::eventHandler(*Tree1); to enable search closing by ESC key TreeView::eventHandler(*Tree2); to enable search closing by ESC key If Event = #PB_Event_CloseWindow ; If the user has pressed on the window close button Quit = 1 EndIf If Event = #PB_Event_Gadget If EventGadget() = *Tree1\gadgetId If EventType() = #PB_EventType_LeftClick ; Optional example for column sort (just uncomment next line) ; TreeView::sortColumn(*Tree1, TreeView::mouseOnHeader(*Tree1)) EndIf EndIf EndIf Until Quit = 1 ; cleanup! TreeView::remove(*Tree1) TreeView::remove(*Tree2) EndIf ;} CompilerEndIf