From 12a7256c291fbef761c85512e78b5a0f7d446e05 Mon Sep 17 00:00:00 2001 From: MaxED <j.maxed@gmail.com> Date: Wed, 10 Oct 2012 15:39:18 +0000 Subject: [PATCH] [WIP] Some more work on Properties Dock plugin... --- Build/Plugins/PropertiesDock.dll | Bin 14336 -> 33792 bytes Build/Plugins/PropertiesDock.pdb | Bin 36352 -> 0 bytes Source/Plugins/PropertiesDock/BuilderPlug.cs | 4 +- .../Controls/PropertiesDocker.Designer.cs | 90 +- .../Controls/PropertiesDocker.cs | 109 +- .../PropertiesDock/Data/MapElementsData.cs | 24 + .../PropertiesDock/Data/PropertyBag.cs | 979 ++++++++++++++++++ .../PropertiesDock/Info/IMapElementInfo.cs | 2 + .../Plugins/PropertiesDock/Info/ThingInfo.cs | 80 ++ .../Plugins/PropertiesDock/Info/VertexInfo.cs | 44 +- .../Properties/Resources.Designer.cs | 77 ++ .../PropertiesDock/Properties/Resources.resx | 127 +++ .../PropertiesDock/PropertiesDock.csproj | 18 + .../Plugins/PropertiesDock/Resources/Add.png | Bin 0 -> 491 bytes .../PropertiesDock/Resources/Remove.png | Bin 0 -> 1178 bytes 15 files changed, 1505 insertions(+), 49 deletions(-) delete mode 100644 Build/Plugins/PropertiesDock.pdb create mode 100644 Source/Plugins/PropertiesDock/Data/MapElementsData.cs create mode 100644 Source/Plugins/PropertiesDock/Data/PropertyBag.cs create mode 100644 Source/Plugins/PropertiesDock/Info/ThingInfo.cs create mode 100644 Source/Plugins/PropertiesDock/Properties/Resources.Designer.cs create mode 100644 Source/Plugins/PropertiesDock/Properties/Resources.resx create mode 100644 Source/Plugins/PropertiesDock/Resources/Add.png create mode 100644 Source/Plugins/PropertiesDock/Resources/Remove.png diff --git a/Build/Plugins/PropertiesDock.dll b/Build/Plugins/PropertiesDock.dll index b5cf4a461169dc7aedffa36be3973696d681488d..c21b458af23fac405c9014569555b96cb034be13 100644 GIT binary patch literal 33792 zcmeHwdwf*Ywf{Qj%qy>fnY>Am;iV%9A>ko{BIE@`c?1#=EM6QkLl{WrgqaBt5Sv&_ zrPV6+QPFCBwzXcZc&%DnD^+WY*H>++k9rkb>h;myYHhu(TJrm@wa=MH5@@}j`^WwL zes{vGv)9^duf5jVYd_DK!_xDvB!h??_zn&dJ%lTN76?2s=ts79+{0eFKj)e84{1xD z8Q;Dsnh5m7&CYnJJJ1n|#mr=2LnILIjRm5yK-2QJK(`r=)D#rtPO?O|HWMw;44Qm% zZ>wF~5z@!!X&#~rL2)VSvg>gT;CnN^tX%*XUFnUK=+9R=f-mU&F=)Z(iAKx6^wmNY z3;k(7(Yc(yndo$O#L&+{qI}RU0AJ<Myc6L8B3O|6cz`cXVMH~_$c`lN_MHI86I(64 zfypO;YHQ+&cn2`i?Pesnp)SHV<Ffz`sfkCrOfa&pbP!*bbuqpfp9Mq>8K@*{*{}Gp zZaO*XLbSqBAhs*sT1a$tj<EbgAg%`|6Xil!(QXae>WNac;cjT-pec}0SgVPS$n%lD zi&=83$Tj6;(hVs9CYuL^n1{;>&jwxJ1-%?b@MKhx^rDj8j892toH9HE4$CURxRg*C z=@ODrSZmN&=n1)8H;XSirscY(dKID@<~KMfl`RHMvw02kR?rReHbC<_X3=-C#8hcU z(xnS)bt*tvP*G25FbKuPNb!-%Q$8vUo_c~j=rOFE(~?;=LY~XYtIo=E5_di~=N09u zR)O9HIa9}5{#P^;TDtO7i?~g=$(2hk#prYCF7rlEiV~**=2tn2@~g-*h5d<!;=TYo zjnd%Mbj7w05MJVgnsgRoK$TN@C<jN|hH^PSWadh_lFQM<H8U@rUs3C%^Wi*@e3Yo= z49g4?f#WK4pZ*^x?Q@%T$V#He=y{4Q-)-(=6OL(x9-n8b-=pxvbnuxo08VzB7zeT# z7*EX1V7MWJp%M}pf4%`K24{hby_1r4RR$|p#&}|O2E!E@3<Y3d+&mo&<awU07Ln#0 z5R$OBqQI@Yr4e$Z5egkHpF`=**3JbR+cj4o92|UYtk2!M5F!e5d^xtk9$$`m21|bx zDkD-d<)ar?6gqv*#F;F{RYg7*_kns0ipZj3e4Wr?48Gid_{04eF=e&AMEB~{@F4B7 z8=;yW7kuDFYjD3~x;Y;;rTc`ECH=BQQEmDEC{1Fp$S$iBMkelqc72Aq02N9+2v38h zhv6{m*(r!+or+Kf@tiKhVUg>~t0MQ5v+M$@iKfi84MFRpIQ*gFR`gXi8fGJx=MfP8 zMSckKN3ndnY!mYHt0sEV!J4Z&nh|V9R*7^|M`_+`RUT<!blRW4HpZjc*5Tvv2P6*y z!D`tO=n7>~kA@J^VNKn98UvKl`Xex)aE1dG2?0Fpb8aUTwS8c+ik1l}8#CI`Fc+s8 z*hUlOA;U){+%Ki6SwulqRZiN<JW`>YGP4lIOm(>OUz_PFU6V^WDxR5m=(WlmWiR5B zW0{E_pVPLu7$XseBFo;<bjCSqD$lU5MeIvE;B3LgUe#vG9)rheSFY{~R^v(;Eq;zw zfZI5Lau}a%|2&VfdPYx0^H<mbW+WMN@SJJSDOvSNGzQ0Ur7A|UW5`hX6SDJpUa`j8 zpbXMn6vi43#eR}inJvq*O=V&}RCQ$(zyzd>wdzCg0FUWTuOqmG4JX~P8{-A$x83OQ z+@tL{DO7%7s<{-bnQ>AAJyl;Ymm%w-<p5@Dnu%pFPo|8oqw3&_Odg#u{&Bx#e@i{r zfCpiKu94@S>cC(z7W3{(l*!09+kk^3t&c$`Lk`9<TTW5p76?Kiw#;Q>CURl)y$u)X z?W^I?Oo8nX$YX>d!3Di-IgV^O@D`lMzP|wVVGIJFQZG}#sX3MjRa$Aj2Ug@&ot9Bs z^^ok+ysA=HM!s#HXlm5DX*cTPj~>*xepI;|A>vSTc^tH!=h2dCz>mX=Jw=AO3Ks2J z4R9G}CHA18$)R|%RjT6nrOq9pl(lAZO8+loyMt@MX0By$9>A8|48muafVD<i%Mw{} zm|ukkcd$Y(cW@n;lrp6wXC}^P;r|3cQ@(=uz*OZh;^U!sa6O2QI67WW;^LHLQ!3DY zSiMm>>t~o3aPbg8)(WDaYWWBwl`!XfT;>MwPc8QFavWnd!_qYpj%Z66n?~E3m>8H` z^jdZ1Xm-cn8O>;l8n?&L`aU&|am?+6&IWYGvK!ssp$EgLmYW+EU@6+4&qRF}rY?sW zVU5g<fV&aYS>!TBMoEWii%xK1^?MRDM%QqZn44Ix5>#$R8KEk8m8QVM`1fVxVQhC8 zi6lF9vv8Lh<`yJMgXrOi{WMR*2s{`Hz=Qoj5M352o)?|;D5}6DWyeexi!-|clMWtE zU1kjFl4Cl$G`n~%f|X@6^>P@g=au9G#mtji=&UOBJA*w)7LeOBxpYl_jwpL8fxMMD zc>Z|xwNZZOu@cNq74|%$EOVQ2@DP@}EL_3T8l$)>-&JBJK+3e0s`5!ME-q1)!x+_e zO%&|nX+CWm`ck%Sa9?f`9hGhCMS8exOuZZ&mTlXBTDEORGHn|>S!~<E92vHun;+LU z7OpBz+13Y=Wt+57$~J`!x2>?&L91XR$0g~`k|4%*7a7H}pK8bm#uAA-qqMd(j(N1l zaT&%1P4!a5b#B#YLs2j$^aOXY8W#ds<H@J518sc#IzBs69XkE1ZHI#=4cF97Rv(bg zWMXv9EtM{gtlG>heHV8z6FYXh*GLI{7k4p520K;@4#Py%%!H}TMyP5@rZB0e{6zKi zk6e$C5h<!`s*2Ql^y{`=MtmPz-V6cCLO;(DdC*1YIUaFV)n~-Nnaz`D^H9|~BaS|r zDIg<rz7c;pJ2OK%mIW-m!iay6&6AUo&I>Jl7c0YaKL&-T=pr7`#2#~ijaE22G81QC zDjeeo&$<6*+pEN-jjkT8yklOhJb<y9cl{WK5W>*!^lzY~RVd|!kqg1_Af`<=DruD% z?wTN`Nak2+aisine3|GKTq0nV81Aj$TH)Yv_#{ev4u<TsuRKR!^yLY|M{%D<J(xIs z?43)%_T^})44qgnjm;vfem4&6((iK73aLV_ASP|Jg<S>wYtR<dF*ZGxeICN&Gf><i z^R2lD_{n5oG_ERfd#=avK@uaZ&Rad0=D}F(aep~_%z-V-5t)EW%`c#cwNl_w-@H`t zy`20aCuOQ=7fpE57@C4Utk;{kV%@9GH_F)VFR=ukoYlyRMb-s&U%O1GynqB1ttmF= zQq@`3n8o?3yIc7jcE~>|Bfm_#De@7;(xuViUDahSiLdI>F3az#a<#}?3=Sa|loUFu zYI1yz>#++G2kE;~IzQiHuhYy0;x(V6bPW&dIqW!8_!Ax&Io7LSvy8n)H5O95EWoNb z71u&gQycnO@N(FMg1lDz5he!t=!q|by4#DAdg2OBV$MSHN=|YdSk%#JDtp4B7N)7( z4fVu6E><kbuW+(dl2>sO%VjWJ%}F-Y^5ZqY;XSU@w9;Iao#qPcG>f&;L5r2u9yiLo zRtaH@*NzB#k$2rNvWOl|mRd5%@%aen36NG_QXY63eoE|zDD(QXyz+6UCvITc*V453 zIVi<vGxju4V&0TS0=Za$2UY<jFd*leH>a~o%v%&9S}Oyj3tB2MdCa&{RpG5^)_5qR zLS`yY+{U7BPqPGcu66FvTA8`Tyd%x0M9Gw^oL9<cplk>$l6Em;B_;+(JFBuX%MY9% zPh_aYgusVzVW4KH5=xe3IwSfq7p8w#;#&}J-l^)47E>;ff!}folZU#5Y4IyE>SjW= zOPHcuGIOXloK<SOBrVEviORKH!iuC_!dS{Bj0|xJlj65zsKo^365gm~MFVG*+R?xW zT(WMcOC(yAOSseVNi??xlZU#5Y4Jxg>Sn@m9Kcy+wgb`yEe9|m6%$--+5wEE9KgsB z2QVrAT!s=%IF182E9C%2;DGaoI)Dd)aRqj-mtjG<qYN3gJ2J>T@{B+(9YQ{FuV;DX z<ypbbr1-lThA~0<`M7CW&C2wYRg9FF-&Tz#B64($8<wA0skCj3m6>-bUPemIe@-KT ztI@B<6=vzgO!28K&>DzDVC%S&EQOeu8g<w`<8o|wG8ViKE;sL1H8COZ*6^B`9&gU5 zN#s;woXQ;En3ytMA@`)^qII!xcO||9l6kM9b0elbAXx6WqAVks7+;%FH4_4RSjVEQ z+L>N%^(Tzy2O9(=4xlEs5#6RN_%Ct|eiawB0k%TLqR6Cjo$$oBqRQh<3-K=@GpL*% z7>$k{xYF+3!JDPH?95Bd`xT;wU<={lC6&WnErqCPvDg?fzboZSf<nVG0P6sa?}N}d zUi(~B0UkN=!g@ORfbjM+jn`1-gN&;(PTGnxvUUh!>w*J>UFBd?hiX_BuO&UP5cJ(l z)#G==y7<cgiY@UFB<;p--4Wzvn!byZ=J%vJkrR9vY4iIGmNNJOgB4tlRliPW+V!Vu zMzvEZQ{W>gbu!L7Si0gee+Uxad1STJ8C&H@atai?VJ_l2<t*;Re7rLINBGYE2l=ps zv*e+xTl-_%!?~`xh$v8dVTD$7%uw605~~dq*%S#>)noJ`j7Q>PG9SFdr&VVgz39{< zaWPqWfvP4My@;VSS5c})CYxfLG!U;X5jDf~o|<np!#QG}tU*s6iLX@gsT1Lh)<^LD zUD+$svQMbPe}NCtplpi&0kLeDk0P36+Y&q|aV0HOU83(ouwaKS+33bqIM7h5YTN!) z-43dO%mDhHUVb^63?~OA@jQciZgrK$>*QJ%KVPo9S*RX-jB64<sQcvk+;8<=W05Hx z7ai5u&DfM}sat8r?vuEzVLpz+l4+mD+5K6aZ5OdD$Mb8Thun^k9SR?jm*!UG;qE~2 zNtCw^#0zoehMrI3xs2D5-gM0HI(u1y3QV<eSwhY`n3fydjn;9QKY<*zc^XG9wT1i= zjI?rpirnB%fV`^lR_;}tyGux3D8ly@4hcqlql))$97mS^?8lMye+p$%`a9@6TYqOS zmr)vHgLDRNJUNq0@>B3%Q3kY=DE?h2p~&&?u|8Pa=avMY0X*5VBCjf+wF^EAnw+ek zqz9iv+G;NzXE6g{oW*FXnhR%_27L|miA+yC59WT(v^KOq18%;+;O7AORcrlv@E1ts z!j0x(rvDP4plZC|2)>9^o)YagUjpJbUuKS10C2y_2>uEQIo^8#N0-Ji9C)UW_XvVt zwezn>zJ3fh-_rS0G=v}DNtwPyolELy`ORNa>9b&TQBhG4n|X{TG-V}l^WPvXlgLcY zk(e@x4b0?qq(!Z^MP*Sx$_VYr9NLcEXn%+9+>LN_=w8QexD!|BZk}*4-Ulo+h$~Gw zFGHx;nNoX?sys!}y{_Gpq@a8EDN=BARza`J>kfXwvYE4>qu1$miRl?x7Y!*jG(V%L z*DaYTsY8p7l%HK|cKNT2Ty-{+tN{=V1Y>zDVP~bs62`3fWgd)Y%)@?8^Ds7?hkcgj zVJw5kqvUY=q<I({&cltC=3y*@$I^$}JI%w`a2{^UG!J9Lc{rNVJd6$J;ZRHSFows4 zg0D$8!{B==2na}&v+r}V-wKqyoGE2_-Nkgv56qpG$=DP;3IXHLq4dP-kiEM~1oHgn zPnTn&VID!gl;%6uLUNdIus&06BHV*iOt<owaU%m}=Y2kwZALJMuizWTW<9DTa=@1> zd{gY6r{*Ao=0g{_31eC{R4EGn7P8gQK7r!zW0>UUP^`P#Zz3=8?*O^3e&$wXWT2E7 z%!4er6SAfvR}7j!dOss_-*i0k5k|NVf+fEz9&%8RaGGz4%p@2!B~8r<L-?a7;>UuR zo_*>MKh$vDhi_L~Um_Xlu2~sLn7#3iNFtS583~0W@#;W#qQi`LMK@FjR!8EAs2Q6# zy{4A`ssoL^UCG{fWL_-Nn~aCLsspXP8@i$$=S2G2%`K7GybZHwhh}xmI<0QbjG2+z z)8|YaURiG}8SRePwYNo*I+U_Mt@Ta(k9B?t0rWp!Z`Pe1yUDV%DIVG$jdj*EM3dd2 zp6Wn0`M=`O+8G;XZJfQat}Z;QHZ&uo9Mm{<?GxeqzL3Xp#h=B{!BtOT{P8D$B}J$k zcbm82+6x^Qh?02tX^@rWPd;nD;=f%zeh`X^v&CA@;2cL&C>e6=Tz8HEFp9wo$kF$< zE?ZQPI|hA`3R)I7VI9d2+R$hZh6x(?!C(j%EwOS@1Ksm%<=a3Uo%KuWi4I(m_esc! zybD{J>f29W-f_V}{i7@1``e<Qy!4AVij3&7V;{Ubc*Fct`lF-29yT7B_rc(!?>zA1 z!Skmz9-48%2OZB1R>iJA_WFlUUy(F+j>`FD{r1<74gT?lg@d!_#Rp&C)k+VRKKx<M zp!+v}du;Igp{~Fqf4uhP!O1WFdGI@rbmr~*!iMmyBR$dACwoWLw%+_BJ@$6N-v+;Q z&t{|GXOB-A^@97foBM9~)8HNFPCfSAqqlmW*gROeZuQ6W2d7_pw>NA=1ABf}`{8|e zJuq1R(LvWQ%=S+neeb^42ESK&&9UbmaXb<D=+^017u-5fR^#%mkN^00^B0eMssEi> z-yZzYj;UAw(`_F<>iX$TyX%h)RtIqLPIz#2!MD7Zgx+c2>hAh$?%jhko^7drZ|$9f z#S6~b*|7QFZaw8oGqzq+{j-nf|M{}N3`RSjI9T??DW^^@sj2;F{%`MpZ}8AJFBx2} z{ra)bFTB0{%K6V79sJ|Fr}gCj`l?^+E3R}U^Jn(IK5_qz&P!kJzb@yi1HWF7(;GNV z-{78E?L7LqTC~|KlUgGETU)P3yHHE>vZi|)F1R@Fe{|68aQrs}9hE1W*CgA_jmhny zc!cW1CLZ~2=?-;bcngL5wjPR{iG5Wd+P)^aX6cf-9cFh;h<R#uboa2U=*;;$dO{sr zBK)j)XEZi%!XLi>=!8HtJa59evzFE_?P-i`iZ0$6kF@Pv*50vmOUIn>g!yL$&fGB< z0=gr~P+&)QS1d7i$Giz5cP`RQo;D#MEXghNCMc!WENu-mn(;_rR!!~Hj@r{|1E<y1 z)XkVVd-m+=!1UU>+G(}Zr`1iLS~q*{jOlZ0YXjD20&0tgH_lzzw9u*x*t`jwlF6RA z)240TzP)Dqj2biEIjwHaoH;CG`t+&bn40K|B||%=#u5{42~CkiM?Bh-#B>s1iJ=W< zZ*txQXsS%;?n#M_B`i0=VbgYmdZyLY)Uru7<I<%kX6^1yaVL^1BWZm{6r5=9>xoRW z=OeIB<f?k+HpU~Nq#18F%`V%Et((lGnb>6Z1RC3B1<sAe!shmba4%gtw<VTHhGHF& zmZo_VP`)M_4bMHTX-55N4Rh)mr`I>v)iu|hzOcTrdEw0F=?fcApD}Y`tzEXs?C9-| z#FBQ|aJuaD<Cf(>=<Y$yO_6wXTO_<NZgvO6;JMM`>T>$R<I1zjhL5jH?QzRagEG^& zSu>j1CLwk>nF?AUsj!d45bJRS_2Nrjn~4tbGc}6eaaEo7vD*CRiL1*(jh9}t|MD3< z`pMqsFTe5g6KmrWpSba?2i`3^W$PD5KYs42GX@XGK3mxTrz>(lNPP9+JKAkO)Smdu zgsM|7{?kYLy>E8s-8<!{?d$13o4(rh!+W%A8=n5!yE~VidGx!*xA(k!*_dl@OHTg& zix-_%N;e$+fxhOOeW%{ND`C|BbgSn0_A}!bY&+}ehUWF(JT&mTN4^=q{Yc@1bB^Tf zq-VbP_1mA;#`Vx^Yd`$(VD29e)nD-JzF5G~6@Ra1`4?Y%;0sIM3{3yH<%e$`d2#*6 z1)ux)_CudpwzHYLdunKGXgj+>lQ~)aZebfQux8cHo<5t|oYV#QJDhb)#6iRw9C2L7 z>9%AXON#_^ymtl>k6;s5wb7;pYO0*LXjMxS@^%34!rEzKLl-}fB_B+<+PS3<<mK>! z;UjGZKU~Q3@rC&EG>i2m;lP)$xyG8Ah$mPaknx&S_;|wRsTmu1nt?Bl#bk}oGBEjK z|E#`@GpD+`Xsfna%cU=CJ2V$PsND|uh`>XDxpWwGFa1q>3#SoNb)TL~({<)tpl=2| z8!(qjj2(bW98CYn!Ib$<PVaYe`Zh^#b+P1HH^Z&&&5-uJoLh3d^phNx{0`E1<|3D2 z8K9RM^WH+)SMs>_{QS27X9`RTyiDMY0`CL#)1UHR%=gp5g4+xH^nSrF0q1zfdUL7E z%bMKbz0&KV_r1RaoaJN98w76ju|AjicKJ%^1K&}fi+qKHeisD=HVX_3+%520fd>RW zEznW)+d>zW2|T%o%gz^Q7Og9Kmp)g-ZFOJKSBhNpg22Hd_Q~jC_E?p`d4MIfsd#0v zi+YPWcfZi@1a#3MP+W9a;Jd{vUoT;vu_Y{_2GB)qp!jKf$$2Gyx}#(t;N1dWEIC+` zpd|<rFC|OaOShG>haUoT(W9m8jn@G`qrC|p7nPTBT?+vH)K$iwe7NkUGB15x#{M5u z{+4!*HXX;lCA6aaC_J`W-~|G=3A_Q&OAmn4OCJh6a}1}~3A_lfg!YbMKkpy28R>hG z_R_PU`>C+v_5v5pt{C*^((($nYi$Kvx&>)3U0cC&ZUgkw>lIH{pv5b=l}jprSpc5O zwn`UWT)7T@{(2>Q`>9Hn^G+pu$r!ssf?Y*C_h}71YAl_Ke!*!yFq)R2PdM>ciZj;H z)B&s<9yyI}MrtQGd9OTQ%6$=-2Y(HMT?1?^<<LUGz5#4JN`?fx5113Valw87Y%+T1 z4jRB?cF#%PI6V2vy1Xt}fIchO?||{&Eu=wZT{{(hzl!dVyb53*V0S{J(R8XdLvvC! z-GjV!=$j7e(hHEjU!Ms0iojj2ZAkyc<AShHJh^}eb4~+%E@uJYVS&HO$;HO@-*cKk z`E6c5U}^qdzzO-xQzfuI|8k_43p`&a8zsFve;?>q<zEB%jr{$959i+m_+&my`#AqL zjV0fWbWXtmz>xO^z>VI+pqGtu;4Xj5s8Yc8Q9LG{KdKVx&63_GaJRq^`m#nBOZrOT z$@8%-MLy>5@o9#SzUC_d{Fbl6Xbr$Uw5M#l?n9{_g?V=C#lSALuzh+tu&XWX4jm7X z(mw+W&>qht`YFJEY++9UGw3x7`wcJ${ilV!2h2&M#}htJXk<)9)SPKydBEJXL$Eir zDaH)6+|?EqH;RGXYhe!w_Oyk$9H%4iEel)cm<P-;fopg}`<9~t*aa3g=vV~oLJM2# zT#87%E`_ZGcE5!kbe@MX{}(B2J+O6?luVDS6L;ZE!IaPP=~BVo%-Q4GiCN@Z7IvlU zB49tTu$u*Y-om~k*ipgW$a&0lF*yGs*dEWvu06miCUbpzJP!9|IFYJW7!|m$!a?p5 z3-h`6Bd*s8_NF$*?L%C4S$P5X&A={8Vc)=6R6dSuAd^l-xk9Q;VRzv?zE-d|wKLuS zf-$Sr$~znNl~7L#dkB606AN1j&B~~Nr&P#X>wW}J^#)Q{IqoZ+CD@zV1@6c3v|6i` z7j^#>{rp0~-q5zYp9OZMh27{rj9!1Qg+1(kg#z@v!g5}9zfR-nZ3{c*K8h3iPb|)W z=P=Gh#{^lIH?(@sQH&l{78dfngL(f9!MMe|T9DQV_M}J48KYIx4h!Qps-~}5SfS({ zR2WUlc?EeM9^B|jj2Yv!sWjKZrsupvHPmWhujrGsX|%(_=Am327sBUBXLpW|rYi*4 z&YWpl8oMl~oMzHdDaU%w)n-x86lQ)?<8r6byB5afPNUH{EaW^ccRD>Gn2N|bbT$s{ zxK;j`vsjx$OD!xXcbPVqS}p9P+;;5@N?KTT?p*Cmy3WGR$o0`Yx?QjVI!7=*-6I;H z4T7!X+hM?V<({X_qk|T9DX;}JX`0HrDmSDx(kFuLp__8cshPf6%Xxd~Zb(^34+?fI z-Jjd3Eu`NGHb6g+yu;$Q0s5(6FIm_tf*rB2-wXDZg=u*#vr$|+Kt+Nrv9PMVEvVsf z3k&7NwZ-&|g<S&dY<khcz6opz9ksBh^7^!8^b#);=?Qu-?-H$*M)S%Zw*Lh+te_)U zyAoaN(F?w%t)TxD>`C$!aGo3cWsaAV1S_(zDS};XVY3B$)xs7PEQS;eV^Z$Cf@RuD zs<yDMg1K57wOiOl1?^fpU1(uf6ug5cv}c~B%5nRxrgau}sNlQWYP!<Gjukwrt)Yi3 zEXV7kwRFV73I+4<Q8Bb$;C)hCOX~zvk#!zjr!dcQ?@rv6yVt_jcpnG$aEh~C`wYD# zn2OzX^eYSF*j-1jSs2IeKhaUao}|xtU)I)B*#cGj0q?K15Z!KJ-$!1AUb3*KynoO- z>8yI@RGMw3#e!YyIp+OH+e{ZIjA}-80-MxebB@zD)65h$P3xkT6gF9p(b^PNt#74R z3Y)1X>9Z;9OnnDklfs(y3+WdrY?(el@20Rd`W`B1v}^CsFQ;G%Gxe*fF@^2YZ=jA8 zcBy_VU6jJE*6*a7QrIo}z4XHrc8~rb9aWg;#!)}ee@LHL*gubY449`$YWIA9R55;g z5wNhIjw%OMZDEIzcaWMa>|Nv?q*e>lMqf-nqD>a&AN>xnq=ijH-eYu`g-u1?V|1N` z%^qD$kJG&twrF%Yupe01xyXBheqmvok@o~0v9R69`!W5+!uBBV$K+^MHe7+cC#llH zzJ|Of>12gr#C%Tw2`#Yl%6z}n578P6V?7U1kA<<Ghgcw1DjYpW^rtNBNv+2Brv7va zYxVtJ$1P%f40=*q=le*1fz;MlMcvQoN=xR)E|>9hy2rvC?mXic^r&D@pzu`sCH=dF z@k-}K8nuw6eh#(!uuH8J>?+SgzJT!(ZL+YZeJ2C^tcAVgs|I$dg?;2Z(|DO4vM}Ax z81HS1{U?Ih3V=t!v65~X#S{grg)%J#^(<+#g#L+#`T6N#jTD8UPO6luUF9;xmT-oY zP19A`>{5znc<pJrElt(Jn&?=obK9_F2hEpSnn#3oS(VHD_=T=FZOihMhS~HLLT?ke zN}%FA7ip!xLFWs7IECptMI=|*zEvpOg~BrN^vwy={TV!85Y9|}lqO26eUf{X@ZTWm z+XUVvP<c#o4!8Y2DfO^$s`OJx8`Am?%A~(AB9DrPOur3J8`!hh-d4HCqd2gm8Hw}6 zbi60v1f~9wTo-MF1UL3g$FE(<`Cm@kODe8DEyS`3kzWL8k5~UD5qMOhD>LTPX@lNH zu7j9Uqdy7!I0fzAt$LG!#eN-oIs>~W13Mu^Gj^LrpcDZMV}I$RyReh=(Vqp*$3D_W z*J0s>9k`<baF%ls;1$>>GF%Dx5%z@)*8?_VA2^==13S0z*iSu+bBqG)UCJqdJxe)# z?0Oy0fjvq&oh|g0l3we62WN=ev77D_I3RGZz<mPu3%o_(odOR4UZQ<Z(mxV-NZ`)^ z+YRiX^vCIKKp$*l_`c>T4jb=lYu#bvuUZ(tOaH5Om(dUSXMyt_dyu})@fE;X&Tj%< z;rthYj~e?#!hRZ8d@*S{>$y+t%8~R|`a<!u#(1QsX{TDbKKCzCD(ZgK*iQ$GFQPjA zJv8rL$a&Y810EmMTb#T*-Y1^fC!X0yykp%b9@|H}?`+X?N+vq?!Jo4PK8sxT-acaQ z?ZY1P9YEeq)<OQcjvOj38KbSxZyYs)*6IsN!j2F%l|&s|K{p+BvP;XMB_+HA<6YUE z)L-%?M^wMOWFO!afLry4O0IVF>2H)=?-<bkTyis@yYyzz%S&$u&vyMT$DK5;^njy9 zpH%t?U{z@`^gL7G+3tsst}oq*r>)r1H|U>ApLgulKX&~RGPjgoMEmp$OE1y(>6ezG zjp)kKcWA$U5BU2~@2iem^k+(c?U+RDnMv@#pB>fkyxUnVo_w3$FU@yOg2(*MTXbhx zt#hVu&J@lwh4W0|oGCo4VTY69X6HVATv<8o)midhy};cAO0et_ZLhvo()VM3uuq>^ zb`c$tHhP=Zlx=hN={w5$oj=n*U-l3*yb}Dk=)6zcsy|V-$N94ULK&AjQdSI0-!9up zZ=kI&(cTc-4+wl);QInKiOIi8I!Ds`^!##`a7y`2(5I>V4(I#O?JnnEp~HQkbeBKs z^cmarCy~Ce{Aoa5{k=hZ%O9t5i+*$Y&q2Qvlmo;(<3WEMGJjb9it_*|`cvhXXm8V- zfTtq&i2gQ}j(NlRKGlx7MANjh$Gqj7V{AgY-ne4SADs0D^RE!jefl@XeCS+@^j(fO z^asZL&H0A@>KKP>pZ*`<-=}{(2JtL=yL#eXoDX)5cD=0URFu26>JusguBcH{ak6Wx z!K>Jp^@fUSSD(Q>;#7?(14gJ~hU*qY_35s9@ysFOofP+`b6k7HmKNCuHPJxDw_QzW z*?V0r#CxF@;yuuh^vf$Aad9g@ff~PC@vq3`-B622YZBO}zgzLT>lQ<={H<$=L`%Cw z%bmugO5MFqC}E)-FgO|y7}F|?sR=!OoI5NL)h=)=%>jLd$XqKexK{hvHBMWrIoy-9 z??Kyf`i}s8bVy(hJzROYdn^6C@+x--{QCiqR331@jNF6n8u~sR1)Qjz<Eg>jndgyy zTqyr4@aIDL6}<!cZ-nxJz>i4>f1XwiSg6ebtk4=f9rS^g=QyNIbggh5(yCnh0k6_7 zbNxnRx}g`k7r706qWe<73c$U_Iqsjh?-ckMW4+O1>@qGl_8T`D-!UFG4jF$miXG*S zlN_fy&TuSntZ=M%^g4Dr`W>HlJmh%XQRY0yxyyN(^J-_FYoY5w*J0Nw?t1qv?z`OI zbL*Io2xHCZfUf{DZs5;>Q$r7CpB&s#$it~!KF;b2FdvM<$=GQ5x&LZcH_B{uZ3P_g z`~mO_o(}-85_prqyFLF2%3;sPfUkIPh)XMTrT{9Q3nab7`wS=_IatDsQ3X2Doudx{ z-ane<KO*qC(Jbdxfxi~Ye+c}WK;6fjlYPHJDaCWx<1+|%dl~=>{EHd7mjSK?9F3V& zhiy(k{xd9#Jy<<CU>i<gL3K!e8E87J#&0KaK8Rmn!ukThO87y?$seCNpCoWP&Lnk= z)C^~1w$^c~RSGx<rLkf}IgJu1q2nF_!`(P3)aeS$zZzW$sAF#6ll-p;yc#EfI$b63 z8l3-WbS<DxU&Xxw4ey0ucs=e7=(Jzp4Y+Th;rU*MH{o4iI^77U(aq3Er>_gV7555t zx{YRmemkH}--f0d-36%Acc7_G_tF`l901hmKAfTHi1hh@CGe7t^HTQN81YG^_~az< zNkDuuL3}a^-q7h}@k3DjFh%@O4L=z0Nd@3Kc)-BP$vD6ZV7oyZV6}nAr6vPzgrx>; zf{g}khIPL&{@qyW_`PG$afY+ed8_L^*I!))?h?17pVv~EpZ9PN4k3{G;0}Z^WYac} zKzqxg;SLeu=A3-+gC~44%g!*4V%4WUT%c^MdQTyzS0l|n;@J6|)}UmZxzr5zc16yj z*0|Xdi6^6x1fDI}QWNg#vPpdnp-yVV%LrTWkV2Dbb~p4!yYSp!O?HtKds|PWqtWc@ zigfU^8(C!gW<TBw(Xuqu)7%x2hZtfTO~R9dc+X!8C$r^7;$&6W+SS`hcyD5?Q`jWM z&tf#1v29#BPHCA5esIi|)W<4iA<f(H@I-waH^;5Yghi-}L$PodWZ4<*p$%OTvLE+I z`~#lIa17axMv{8Ojh1iN48K@dO{3Y}W5%EgS|Ngyvc}#-((GoNGCXBH9G7-!f?AeD z@g9zrGzm`%#PC2!Ty(WHA1=ds0;Oi58ShTeszf9X-zDQ_m+j(_t%Kk3fCwr7XE{9W z$EA4CGujbKB%2~#k<L&uLgC295Qw%m_}!BoVN}zB2W6-;l3d?3gefrsjrlAo2v1rR zNv;lc^+qTZ4zEuk@kqD1Et1CZoM@UWg;-oEe0eM-V0kP>txw{~i4DC;L{=({Q>~EM zVl^YSSPf*RM2Nl_*r$}!*cD17mW8?_Y<3#?v{LLiOG(Nid}?WSe`B3eoljz6c+6$T z`0+hvMS@6SW3%v2so~J5N{dTHBX_cqyW#OwYz{{`ZXwM|4Kew@9xftCLd8m@A|^Es zr9Jy!uO_<}WniDuzqS{bZ;xT5gg>$gL*1WIQGGlf>XXqc8bet@WguuY@tn7$S`rJR zJ0jt>=+20R^TUzLV_kg~+SV8A*c3NoU<jw1(C(=Tv|p;*)^uX*VE&b+sibEiEqJb- z5@3-;eK?FqGQ%rFvCarJc11#Q42O6uk;lG9vj<PCr-oTO4Ah{@SW7G%*|B^ht{7F5 zm@jxZvM%*WYB!as3Z~pWveH&%BXP0oYFN?ZW~Nj=E(iNW3_dPL$|c9;u+2RIgZSaN z`IduJvu8GWOCr_WEs0Ol)Tx#|v2BvO0ZW=FH$M9!HA2alz!W>2h0qv^VfMGDlZ<5B zCbPFI+=hoKLtThPHQi(j%Mw)I)6>=0xQW9n!GWe`dz;0Il_A8D)?HZTSj#1nb<YmM z8l^RapzQ&eE^r2Q^v2`X%7QR`EQ!V<;mAgU(%Y@HtfJbm96|UpStZkJD<P)WTg;NN zN?RUV9PQlHg>Q0+iPcC#B=D*)5|%;<k(5d1(oy_?L^8HTBO;Co#E2?Hy***99$*nV zo%o8!T3#Q~D#?MLHpU}~O_T_2i`d=_v1>&@7DZW<MY3$qqA9DgNzp`asEe!3BFhXM z?Z~9t?I+Ezn8PxN%kr{EEqeME#iQZ6R9t29+Up`=nf~`#W{k|T0Rgoz8tDqJZ^Z9w zw(#OmhAxcH9rk3Opr&Y|$4o>pN=1_@YE$vbLBwTvHJHpwa%j~tjT6&T6B3)0rF>l$ zHr*~UBa1wP7RAlpo(6LVb#73>E@~&R1WhKfUq~5(F}Hhz$&4KvghhH|J6;CPw4|cR zg2r;PwReHVBlQ}~HuwPh8}Q(<WxjBCUy8M)Y*rGqJeGD15OlR=k?qZ~uu3oQiNsP! zV;4Nxz>nc0Gu;=gewIP9x>yE91zct^iQP<s73G-WMly!`1olCc>Kn^roNSJD@-Q$` z37Eez+DWZgp+}a>Q}t}Dpvt0EcsO09Rz_56Br)}tpVSo+RDuh_i|kBkC7wVek>q7o zlJ{)X!i3CKOiW(vj^P!$ZNt!j`PG^(7U4-`9#ccIgS37_CZk50HC>LMV~5QmL((Hm zI*TG$ZA3fJ0mOh%yzhePN?q11Q#0&Y6H4P@R1OWb0UeeVvbPKg>cH^TX~z5P4Un1_ z!s(wgP(-aGW4x?SX6>h}L0c7JZ>H9P30bFPpq$%iSH=ELA(CS+%UM(DB*HR`%DmQc zO;yTlSp?=tXQef>Tax$qs!;0p8sb=vNBkNhv-%+<mYh;e5=_Y|kwP*IPuXcTc_dv) zmf}j)Of`qpFIA>$ut#dz7U~iM?14Qs;9=C<h950bOMQY@l#v8>w<PJtP*>Lm{4R%N zII!5zC#=-sP+}8?PaGp~C8Aft4t0o8zpBK%rcz5oF?8ZMVUm*^;Hk9L6-2+G{OmkC zUm9Ee^wO@#`lTVMUmA)8UPp2%do5-afCI3*#-FrcLnskpCs9wB@B@~}4hr!ap3DuK ziK89?Z_h9I`Cu205R;(rVm=aMo5Qj)sPF2+{F0DGSAu#m)^LYF;6NhAVwskBkTA*Y z3U~8tAB$|4t7#2ZYJ`SpHEf_bMnD-LM7s`4dF3-9K|?-k!4I4^nPGHGtBv4SZn!Ox zKHD`IYwgpj`a~kqy#Z^9XtFDk3Ocm1B4<R?@Jz%bhS=V?C^b@ccTXtRH#`G@7Vhmx z9~q=-<9$G!7g)o~w8!z&+wM?&%kV5K2GSYUaJW+akhZ1;4n!DY-MUTIUYKTAM-x#r zRl3Nc-e@?D+16v);$Qa;&sZ4kiVVxNs%?sfI%8NMMmsR|MmF?zc5+mtWhffjo)=Z= zZec0Q%M%EXXKD+AxOioxE3`vWiL^M&N-l@24biS>GQ;IopSD&C(8oHf0{aF`+{AHX zx3P6X)u`6dWDh&(s%*wAm)KUv)$e-26YY%ADw+J$_(PV8T$eg*q(zY=V?23T6vdjZ zi?dVXU7EZUo6KeW-=nmsm7NSS_KswzpXE4iDZ(53ZhN{S^&=rNd)7B^#flf+vbDj4 z&aAeTaEHAyOP5}Y7F!xhc5K2`%A*0%lo;toT0AU@nCOg{8CT1uV|tSsXUzl_Gnhqo zP~S+!pbzG549ZxH*<&M*TD%P!sg{=Ia=6?UNg~CJeA*dGO4x4<b*NR!(kNCPW<1P8 zh(!tQ$e>`MkDpw!dwFHi5#k;Esvew>V@MBS!-wImJsws2$0d<iXL6Ih2}>V7NaYyy zc-fo~WL48rJU?qKvU!ZQI-X3R?PiZ$+YpqziL#E0VWxFXE+S;(&8~obnJ2&()N#An zWv#swpB5m)IveJF9#?8JsR|OA<#=H#g95xYW4cm{!;6bGTf;fK)J`umlejCOj`1Vm z<{cdo8LdUrq1EsLGqe2|=cOje4<{pxN<A$JYZo<Q>sm+H+{Dosx2)wAjA_!ikch6w zy`c+}6zj@6hAf4Y3%MB}B7Fqj)|<fcKD;E<haZ|!J<gcScw4fs3vI{dD}vp6Lt5Et z(*!MqQ~^>Fd!10vCX9Tmc;(1aB)`>+f{W25CPK?zLD>#d`pNkh^Q008C6(103L|hk zMM7p82l@yo^kS*J0SgkSX*K%dNLP;?ZT8AWRw9{ORyFv<<z#&%CaDo>v?UR%JZAFt zOj*jBHep(6GPlPRg@Z3M)>Sv;ZHpZ}YRAV543Zx~i+Gii9dxETYFEp?SSW1uy-BkT zb9$r+FNcWnw1hJio5oW~HkVMaV!}-ltASe)FbE&Jxx-;=U&tdb&qmfNOrl+pB#1<i z8X09nqSj7US)O&|eh!C8!o)CmZXCx<eCB|;M#f+9d3pe~qA-CMVqie&YYoNW3{06_ zW+!Wlb;{C6B7p$}j#lo%2#b@J=6KwUTUjIalP13Z#URrVA*y2%d*>iMg!e$!{Kj`! zgo9@su`tD<#xPkKi7V`7!3v%?Hbx_1Sx~1oU*Sj-e!tEx;Rzh043FX%*LciMJ3vkU zk@zw*rtU{&X4-{QWz*%bW{co_t0Q7fJGL@en(@$P4Wnv8ZHVIs*xB>qkc_q@PY5H; zE7@y@jGKI_@$uh^*(>=^RXX)Hl|+UFa;~Adcy&uF7Q|RIV@VxFD#kSDT80L`lN+g5 zmfI%Y-4UQ3+_yIYBe*g@dH{2dT5VAxm|z3=DQ-aC@}YP-Y_5WK+|Q1phEC@31t7u3 zMM`lxA2UAc38N&HwgGm4*F>Ee^ztPl3-<t#zcDMv)+UN_tYJ#l*akd<kN|Bp%3_NE z3{$>cQvtu3gmq>ssBpLql-G>f*i+CyuO7TnkwC>sTsKf7B*l=+KI(#nirR|2PROjo z3%;`bhh>n+2y@;L?&&AsM&1x^{Iun`vXM8oQ?yIB<U(j3fsWWX;YZo(B`pDXE6ovr zPi-EwaA^l5q)WA98O2dRg&XiJ6~E#ookyj~EZ$|Xzngi>(;f>DQ?dVqr?wM~Z<SPb zTs0xD18*?rHo>bEN8ku&aP&Z4Ol+d!k)%<@a^%-bB&j%W1|CNAqAe$HfaOu~2iLY9 zboLHTPVnXS<~P?+)ljYu@NhhF@4-IPD#Ni#Q%B;(5g?>z@+nQo9l$dy0n)~+>ndor z1z+|}4D@ur&<s2UGy4?KIDP}d!_gSRcN<_A=wYPOCAgQVw$kvQTd6+P0&9Ax22YD& zD~xnQmi0CG-{*$+a=SG~&aEMSyH5hrdQn#OI&xbt|0&3P^AFKy?w{DQCHKnZ*IxDe zyIypb&nC}<JJ+o)pLt}j?(!Ngf4%`v;Olh+G!1}2arsYo0<+*uCe|^S%wQISa^zR~ z=WueKJD~aJjrG?v(#%LR5Xmn$f{+_jB3n?22VWiEN@V+MJubX(+2gMT0_Z}GV+GOu zwNOU)x{(cvhXpSM#p4Ah%CV*%XMm8$68tN`p?NB~41-GKK`tk?N@(E07w`B&r9rOL z5d;TJ<Zo?WzyX@y<F)jMMOr1a(*arWIb4C)KL@<4r1Dirt;Lru(ER5M3<-?50*2=C z=v-LW@!XfjWye-}`BLeRs>`V;+s&HkNNmNo7vDa7cRO7E{s9J`XRy~n<PU(3?^tgR zMELtJ$3OOxzki>)T;uS7Nq4a+5l=wZ{r&s>bMga@N>23Oh!)BX7&elFnZn<Hi|+Ay zTvj5-W0M>%ctCfF>0YR8XkITG02lupTpf&hJ$wa@gi{EWIh<fhWp2Q&NM3IqCxvLG zQ533p^HX#n!o(TiOlR1LD&e0~kWs`xN7}qz1xmd$hyVZr&(xx=32}i2LomVHWy;wz z;R=8M9hLr>{{A~FmCx>A@7~SLjA%mJqM4Co;sFLXI^p;J0|IY!5j*=v&%N{4f7W+6 zcdk&s>x9$6!SF_b-(|_Z{@)ug+u_#y`FLi}-~U(j4@4c${`u=Fb<V59(`?>Kp-fhk z$(%b&rDu8E4uq(`Tz8XE37_dGj#m%p{&|XF9v;s}n%jz{HM{W|&pEz<0*OE$^zg4h zup%4Za#jnMGIy@O1wq~dvVSxP{$~GD-9J#Cqme`Iof2xn&!)L+xpj{RD(YxCx6@y% zD^3x=f;pDvyItt49z{njwN5v8QtqqLiNG@ljvSbZGCIZqRuEp%b=~RK{WB|B$V{i( zs01NEzJXb~E1&&3Fb}AIpdO<`KK`Gae0;cl^7F;H1G69k6gbc8g$iDOt=Co!rJ7Nh ze=Uy*{(%*_0j=0SuuAhsbT<-fk;n;9v2Wmft&rSu^+y~5JT1s(jpd$Pi2r@VjZPuV zWBrpnC~GmMq^+{0t+HgSN)=f()?eoFud<4)g8JS{j3V69YMfY$>c+x!-OEP%2clwe zx7+Qg99!wpbKvUUN*zgMY_Bl&G2$O+$>%<n?;9BK4Se3IXuH`p9*cXgDkMXLZ$J$V z1J@|xe#L)>y4<OVcdN?*Zi;|Mx;m;6mj1n>*>_n18Eg>97>Y16<c!vI&)Adj<f)M> zVLn!k#&hQ4mudgN@;J2v!Tx9)&LpsZpnUwdmiAE)c{EZVG@6W3XcxVZpjIK;2NC0u zZlinog>VMKz>5cDeFKNQzJX_Q+-P#E5ZwE-T(q||=D=Z3I>8a(A2^)OeA&)KLS??p zaa@RprpwVJ1Bd+sFIyRhZ3cuf;=O)>Ml<YFSbLXH!!L?T)ZqNh#DQ5KZ@0qO^2w3E z^}wUsdW|A}m&4|APva1Q&TmT(!ZpWjV0ghlt_ed7=olmWZ*;kJjO1#p8F(Mw(#2hc z8hNw#F%T=REY--LI&Tg<c4r`fw|rqSd#Xm0W`{x@A-sQTD!*57>dYCTnN#P~%?eGO zIjbX#LzfLRXVuMC?SmI@@gH7Lg2ocLNZ=QQ_?)ESS=e^H<XjwyV6&GxD{9B1JlpYK zUC77&zpYtYtn=x@{rK)8TG`grcGk-?Cw;cOs_D^fdgo;?wazL;e$(7_jZNzs18ti^ z@t$>y&dYc_b$#3FWz*NSi9grzql<WqXI-}K>u`r)-Hy{wTPK^)A(=Hj;SE%Iy{IMN zEKUc~cxI|LgD*|TlO^${t}Z^QQV)VgBJyA@a|}+Vz=AaE|EB&g^8mjo2hUd%^=B*2 zQBH&&FpSQ8i}77^Gtm*%9hrnQzwTf)wPA0w8gFn|iBt>z2dZVjTL2fz|3-!1894rM z4C_SjTAjtZ%8&a&b>N#J78v>mz8a+R-iCL9yl-m3&SWEY1N@mJrP`6pJG6xCne4L$ zVW4{*3m{a3PP}(kCj`SKRSHin>{fp>vCD)HVDBiDwfUQ5AJQRmdLYl*VS(>NyBgM` z7K2Dyjygl&ogMG#`BR4-Q*G+Y^%jDkE#>`T99Q0drFOd~tgi;Uv@ZPdXEFI9X9;9< ziUi)J^<dA&p6J9033iimvXD&+;EhND{7r{Wb@D!vR?%m<RgS&L`q+M9Om^Y_uwDzq zugYs-?CW@6o3`%n`*WuFGrRmyuMYJozXwReE}i$(yyxBk3;JN?-zz7M{C@#lsor7N OiX)f*{rvyuf&T*&wDQOR literal 14336 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 MHGeSTcl5yj0Q9dfQ2+n{ diff --git a/Build/Plugins/PropertiesDock.pdb b/Build/Plugins/PropertiesDock.pdb deleted file mode 100644 index e8cbabed6a18cc795839e82d845ada6a3d5bf555..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36352 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%0DP8evpdjL`)=a%X;uHK2UxSVS#KYyc-lPE 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 diff --git a/Source/Plugins/PropertiesDock/BuilderPlug.cs b/Source/Plugins/PropertiesDock/BuilderPlug.cs index e2bc19ded..7790085aa 100644 --- a/Source/Plugins/PropertiesDock/BuilderPlug.cs +++ b/Source/Plugins/PropertiesDock/BuilderPlug.cs @@ -38,7 +38,9 @@ namespace CodeImp.DoomBuilder.PropertiesDock // This is called after a map has been successfully opened public override void OnMapOpenEnd() { - if(propertiesDocker == null) { + MapElementsData.Init(); + + if(propertiesDocker == null) { propertiesDocker = new PropertiesDocker(); docker = new Docker("propertiesdockerpanel", "Properties", propertiesDocker); General.Interface.AddDocker(docker); diff --git a/Source/Plugins/PropertiesDock/Controls/PropertiesDocker.Designer.cs b/Source/Plugins/PropertiesDock/Controls/PropertiesDocker.Designer.cs index a5c37c0c9..864477dc6 100644 --- a/Source/Plugins/PropertiesDock/Controls/PropertiesDocker.Designer.cs +++ b/Source/Plugins/PropertiesDock/Controls/PropertiesDocker.Designer.cs @@ -29,14 +29,18 @@ this.tabPage1 = new System.Windows.Forms.TabPage(); this.propertyGrid1 = new System.Windows.Forms.PropertyGrid(); this.tabPage2 = new System.Windows.Forms.TabPage(); - this.textBox1 = new System.Windows.Forms.TextBox(); this.propertyGrid2 = new System.Windows.Forms.PropertyGrid(); this.tabPage3 = new System.Windows.Forms.TabPage(); this.propertyGrid3 = new System.Windows.Forms.PropertyGrid(); + this.gbCustomFields = new System.Windows.Forms.GroupBox(); + this.cbFieldType = new System.Windows.Forms.ComboBox(); + this.bAddField = new System.Windows.Forms.Button(); + this.tbFieldName = new System.Windows.Forms.TextBox(); this.tabControl.SuspendLayout(); this.tabPage1.SuspendLayout(); this.tabPage2.SuspendLayout(); this.tabPage3.SuspendLayout(); + this.gbCustomFields.SuspendLayout(); this.SuspendLayout(); // // tabControl @@ -48,10 +52,10 @@ this.tabControl.Controls.Add(this.tabPage2); this.tabControl.Controls.Add(this.tabPage3); this.tabControl.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); - this.tabControl.Location = new System.Drawing.Point(3, 3); + this.tabControl.Location = new System.Drawing.Point(3, 56); this.tabControl.Name = "tabControl"; this.tabControl.SelectedIndex = 0; - this.tabControl.Size = new System.Drawing.Size(266, 288); + this.tabControl.Size = new System.Drawing.Size(266, 341); this.tabControl.TabIndex = 0; // // tabPage1 @@ -60,7 +64,7 @@ this.tabPage1.Location = new System.Drawing.Point(4, 23); this.tabPage1.Name = "tabPage1"; this.tabPage1.Padding = new System.Windows.Forms.Padding(3); - this.tabPage1.Size = new System.Drawing.Size(258, 261); + this.tabPage1.Size = new System.Drawing.Size(258, 314); this.tabPage1.TabIndex = 0; this.tabPage1.Text = "tabPage1"; this.tabPage1.UseVisualStyleBackColor = true; @@ -73,7 +77,7 @@ this.propertyGrid1.HelpVisible = false; this.propertyGrid1.Location = new System.Drawing.Point(6, 6); this.propertyGrid1.Name = "propertyGrid1"; - this.propertyGrid1.Size = new System.Drawing.Size(246, 249); + this.propertyGrid1.Size = new System.Drawing.Size(246, 302); this.propertyGrid1.TabIndex = 0; this.propertyGrid1.PropertyValueChanged += new System.Windows.Forms.PropertyValueChangedEventHandler(this.propertyGrid1_PropertyValueChanged); // @@ -83,21 +87,11 @@ this.tabPage2.Location = new System.Drawing.Point(4, 23); this.tabPage2.Name = "tabPage2"; this.tabPage2.Padding = new System.Windows.Forms.Padding(3); - this.tabPage2.Size = new System.Drawing.Size(258, 261); + this.tabPage2.Size = new System.Drawing.Size(258, 308); this.tabPage2.TabIndex = 1; this.tabPage2.Text = "tabPage2"; this.tabPage2.UseVisualStyleBackColor = true; // - // textBox1 - // - this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.textBox1.Location = new System.Drawing.Point(3, 293); - this.textBox1.Multiline = true; - this.textBox1.Name = "textBox1"; - this.textBox1.Size = new System.Drawing.Size(266, 104); - this.textBox1.TabIndex = 1; - // // propertyGrid2 // this.propertyGrid2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) @@ -106,7 +100,7 @@ this.propertyGrid2.HelpVisible = false; this.propertyGrid2.Location = new System.Drawing.Point(6, 6); this.propertyGrid2.Name = "propertyGrid2"; - this.propertyGrid2.Size = new System.Drawing.Size(246, 249); + this.propertyGrid2.Size = new System.Drawing.Size(246, 296); this.propertyGrid2.TabIndex = 1; this.propertyGrid2.PropertyValueChanged += new System.Windows.Forms.PropertyValueChangedEventHandler(this.propertyGrid2_PropertyValueChanged); // @@ -115,7 +109,7 @@ this.tabPage3.Controls.Add(this.propertyGrid3); this.tabPage3.Location = new System.Drawing.Point(4, 23); this.tabPage3.Name = "tabPage3"; - this.tabPage3.Size = new System.Drawing.Size(258, 261); + this.tabPage3.Size = new System.Drawing.Size(258, 308); this.tabPage3.TabIndex = 2; this.tabPage3.Text = "tabPage3"; this.tabPage3.UseVisualStyleBackColor = true; @@ -128,15 +122,61 @@ this.propertyGrid3.HelpVisible = false; this.propertyGrid3.Location = new System.Drawing.Point(6, 6); this.propertyGrid3.Name = "propertyGrid3"; - this.propertyGrid3.Size = new System.Drawing.Size(246, 249); + this.propertyGrid3.Size = new System.Drawing.Size(246, 296); this.propertyGrid3.TabIndex = 1; this.propertyGrid3.PropertyValueChanged += new System.Windows.Forms.PropertyValueChangedEventHandler(this.propertyGrid3_PropertyValueChanged); // + // gbCustomFields + // + this.gbCustomFields.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.gbCustomFields.Controls.Add(this.tbFieldName); + this.gbCustomFields.Controls.Add(this.bAddField); + this.gbCustomFields.Controls.Add(this.cbFieldType); + this.gbCustomFields.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); + this.gbCustomFields.Location = new System.Drawing.Point(3, 3); + this.gbCustomFields.Name = "gbCustomFields"; + this.gbCustomFields.Size = new System.Drawing.Size(266, 47); + this.gbCustomFields.TabIndex = 1; + this.gbCustomFields.TabStop = false; + this.gbCustomFields.Text = "Add custom field:"; + // + // cbFieldType + // + this.cbFieldType.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.cbFieldType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbFieldType.FormattingEnabled = true; + this.cbFieldType.Location = new System.Drawing.Point(148, 19); + this.cbFieldType.Name = "cbFieldType"; + this.cbFieldType.Size = new System.Drawing.Size(80, 22); + this.cbFieldType.TabIndex = 0; + // + // bAddField + // + this.bAddField.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.bAddField.Image = global::CodeImp.DoomBuilder.PropertiesDock.Properties.Resources.Add; + this.bAddField.Location = new System.Drawing.Point(232, 19); + this.bAddField.Name = "bAddField"; + this.bAddField.Size = new System.Drawing.Size(28, 23); + this.bAddField.TabIndex = 1; + this.bAddField.UseVisualStyleBackColor = true; + this.bAddField.Click += new System.EventHandler(this.bAddField_Click); + // + // tbFieldName + // + this.tbFieldName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tbFieldName.Location = new System.Drawing.Point(10, 20); + this.tbFieldName.Name = "tbFieldName"; + this.tbFieldName.Size = new System.Drawing.Size(134, 20); + this.tbFieldName.TabIndex = 3; + // // PropertiesDocker // this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; - this.Controls.Add(this.textBox1); + this.Controls.Add(this.gbCustomFields); this.Controls.Add(this.tabControl); this.Font = new System.Drawing.Font("Arial Narrow", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); this.Name = "PropertiesDocker"; @@ -145,8 +185,9 @@ this.tabPage1.ResumeLayout(false); this.tabPage2.ResumeLayout(false); this.tabPage3.ResumeLayout(false); + this.gbCustomFields.ResumeLayout(false); + this.gbCustomFields.PerformLayout(); this.ResumeLayout(false); - this.PerformLayout(); } @@ -155,10 +196,13 @@ private System.Windows.Forms.TabControl tabControl; private System.Windows.Forms.TabPage tabPage1; private System.Windows.Forms.TabPage tabPage2; - private System.Windows.Forms.PropertyGrid propertyGrid1; - private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.PropertyGrid propertyGrid1; private System.Windows.Forms.PropertyGrid propertyGrid2; private System.Windows.Forms.TabPage tabPage3; private System.Windows.Forms.PropertyGrid propertyGrid3; + private System.Windows.Forms.GroupBox gbCustomFields; + private System.Windows.Forms.Button bAddField; + private System.Windows.Forms.ComboBox cbFieldType; + private System.Windows.Forms.TextBox tbFieldName; } } diff --git a/Source/Plugins/PropertiesDock/Controls/PropertiesDocker.cs b/Source/Plugins/PropertiesDock/Controls/PropertiesDocker.cs index 7bac3006c..3b7486db0 100644 --- a/Source/Plugins/PropertiesDock/Controls/PropertiesDocker.cs +++ b/Source/Plugins/PropertiesDock/Controls/PropertiesDocker.cs @@ -16,12 +16,24 @@ namespace CodeImp.DoomBuilder.PropertiesDock private TabPage page3; private string currentMode; + private static PropertiesDocker me; public PropertiesDocker() { InitializeComponent(); + me = this; page2 = tabControl.TabPages[1]; page3 = tabControl.TabPages[2]; + + if (!General.Map.UDMF) { + gbCustomFields.Visible = false; + tabControl.Top = 3; + } else { + //todo: add "Delete field" button + + //todo: sort this out... + //cbFieldType.Items.AddRange(General.Types.GetCustomUseAttributes()); + } } //SHOW HIGHLIGHT INFO @@ -34,12 +46,13 @@ namespace CodeImp.DoomBuilder.PropertiesDock } public void ShowThingInfo(Thing t) { - + propertyGrid1.SelectedObject = new ThingInfo(t); + viewThings(1, t.Index, true); } public void ShowVertexInfo(Vertex v) { propertyGrid1.SelectedObject = new VertexInfo(v); - viewVertices(true); + viewVertices(1, v.Index, true); } public void OnHighlightLost() { @@ -47,9 +60,28 @@ namespace CodeImp.DoomBuilder.PropertiesDock } //SHOW SELECTION INFO + private void showSelectedThingsInfo() { + //anything selected? + List<Thing> things = (List<Thing>)General.Map.Map.GetSelectedThings(true); + + if (things.Count > 0) { + ThingInfo[] infos = new ThingInfo[things.Count]; + int i = 0; + + foreach (Thing t in things) { + infos[i++] = new ThingInfo(t); + } + + propertyGrid1.SelectedObjects = infos; + viewThings(things.Count, things.Count == 1 ? things[0].Index : -1, true); + } else { + viewThings(-1, -1, false); + } + } + private void showSelectedVerticesInfo() { //anything selected? - ICollection<Vertex> verts = General.Map.Map.GetSelectedVertices(true); + List<Vertex> verts = (List<Vertex>)General.Map.Map.GetSelectedVertices(true); if (verts.Count > 0) { VertexInfo[] infos = new VertexInfo[verts.Count]; @@ -60,30 +92,52 @@ namespace CodeImp.DoomBuilder.PropertiesDock } propertyGrid1.SelectedObjects = infos; - viewVertices(true); + viewVertices(verts.Count, verts.Count == 1 ? verts[0].Index : -1, true); } else { - //propertyGrid1.SelectedObjects = null; - viewVertices(false); + viewVertices(-1, -1, false); } } //PANELS UPDATE - private void viewVertices(bool enabled) { - propertyGrid1.Enabled = enabled; - tabControl.TabPages[0].Text = "Vertex:"; + private void viewVertices(int count, int index, bool enabled) { + updateTabs(enabled, false); + + if(count != -1) + tabControl.TabPages[0].Text = count > 1 ? count + " vertices:" : "Vertex "+index+":"; + } - if (tabControl.TabPages.Count > 1) { - tabControl.TabPages.Remove(page2); - tabControl.TabPages.Remove(page3); + private void viewThings(int count, int index, bool enabled) { + updateTabs(enabled, false); + + if (count != -1) + tabControl.TabPages[0].Text = count > 1 ? count + " things:" : "Thing " + index + ":"; + } + + private void updateTabs(bool enabled, bool showAllTabs) { + if (showAllTabs) { + if (tabControl.TabPages.Count == 1) { + tabControl.TabPages.Add(page2); + tabControl.TabPages.Add(page3); + } + propertyGrid2.Enabled = enabled; + propertyGrid3.Enabled = enabled; + + } else { + if (tabControl.TabPages.Count == 3) { + tabControl.TabPages.Remove(page2); + tabControl.TabPages.Remove(page3); + } } + propertyGrid1.Enabled = enabled; } //util public void ChangeEditMode(string name) { - textBox1.AppendText("Mode Changed to " + name + Environment.NewLine); + //textBox1.AppendText("Mode Changed to " + name + Environment.NewLine); if (name == "ThingsMode") { currentMode = name; + showSelectedThingsInfo(); } else if (name == "SectorsMode") { currentMode = name; @@ -105,6 +159,12 @@ namespace CodeImp.DoomBuilder.PropertiesDock ChangeEditMode(currentMode); } + public static void Refresh() { + me.propertyGrid1.Refresh(); + me.propertyGrid2.Refresh(); + me.propertyGrid3.Refresh(); + } + private void saveChanges() { if (currentMode == "ThingsMode" && propertyGrid1.Enabled) { applyThingChanges(); @@ -120,7 +180,7 @@ namespace CodeImp.DoomBuilder.PropertiesDock } private void applyThingChanges() { - throw new NotImplementedException(); + //throw new NotImplementedException(); } private void applySectorChanges() { @@ -168,5 +228,26 @@ namespace CodeImp.DoomBuilder.PropertiesDock private void propertyGrid3_PropertyValueChanged(object s, PropertyValueChangedEventArgs e) { saveChanges(); } + + private void bAddField_Click(object sender, EventArgs e) { + PropertyGrid g = null; + + if (tbFieldName.Text.Length > 0) { + if (tabControl.SelectedIndex == 0) { + g = propertyGrid1; + } else if (tabControl.SelectedIndex == 1) { + g = propertyGrid2; + } else if (tabControl.SelectedIndex == 2) { + g = propertyGrid3; + } + } + + if (g != null && g.Enabled && g.SelectedObjects.Length > 0) { + foreach (object o in g.SelectedObjects) { + //todo: set correct type + ((IMapElementInfo)o).AddCustomProperty(tbFieldName.Text, typeof(string)); + } + } + } } } diff --git a/Source/Plugins/PropertiesDock/Data/MapElementsData.cs b/Source/Plugins/PropertiesDock/Data/MapElementsData.cs new file mode 100644 index 000000000..08ae54687 --- /dev/null +++ b/Source/Plugins/PropertiesDock/Data/MapElementsData.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Text; + +using CodeImp.DoomBuilder.Config; + +namespace CodeImp.DoomBuilder.PropertiesDock { + public static class MapElementsData { + + public static Dictionary<int, string> ThingTypeDescriptions { get { return thingTypeDescriptions;}} + private static Dictionary<int, string> thingTypeDescriptions; + + public static void Init() { + //thing types + thingTypeDescriptions = new Dictionary<int, string>(); + + foreach (ThingCategory tc in General.Map.Data.ThingCategories) { + foreach (ThingTypeInfo ti in tc.Things) { + thingTypeDescriptions.Add(ti.Index, ti.Title); + } + } + } + } +} diff --git a/Source/Plugins/PropertiesDock/Data/PropertyBag.cs b/Source/Plugins/PropertiesDock/Data/PropertyBag.cs new file mode 100644 index 000000000..0bf7ce037 --- /dev/null +++ b/Source/Plugins/PropertiesDock/Data/PropertyBag.cs @@ -0,0 +1,979 @@ +/******************************************************************** + * + * PropertyBag.cs + * -------------- + * Copyright (C) 2002 Tony Allowatt + * Last Update: 12/14/2002 + * + * THE SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS", WITHOUT WARRANTY + * OF ANY KIND, EXPRESS OR IMPLIED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF THIS + * SOFTWARE. + * + * Public types defined in this file: + * ---------------------------------- + * namespace Flobbster.Windows.Forms + * class PropertySpec + * class PropertySpecEventArgs + * delegate PropertySpecEventHandler + * class PropertyBag + * class PropertyBag.PropertySpecCollection + * class PropertyTable + * + ********************************************************************/ + +using System; +using System.Collections; +using System.ComponentModel; +using System.Drawing.Design; + +namespace CodeImp.DoomBuilder.PropertiesDock { + /// <summary> + /// Represents a single property in a PropertySpec. + /// </summary> + public class PropertySpec { + private Attribute[] attributes; + private string category; + private object defaultValue; + private string description; + private string editor; + private string name; + private string type; + private string typeConverter; + private object currentValue; //mxd + + /// <summary> + /// Initializes a new instance of the PropertySpec class. + /// </summary> + /// <param name="name">The name of the property displayed in the property grid.</param> + /// <param name="type">The fully qualified name of the type of the property.</param> + public PropertySpec(string name, string type) : this(name, type, null, null, null) { } + + /// <summary> + /// Initializes a new instance of the PropertySpec class. + /// </summary> + /// <param name="name">The name of the property displayed in the property grid.</param> + /// <param name="type">A Type that represents the type of the property.</param> + public PropertySpec(string name, Type type) : + this(name, type.AssemblyQualifiedName, null, null, null) { } + + /// <summary> + /// Initializes a new instance of the PropertySpec class. + /// </summary> + /// <param name="name">The name of the property displayed in the property grid.</param> + /// <param name="type">The fully qualified name of the type of the property.</param> + /// <param name="category">The category under which the property is displayed in the + /// property grid.</param> + public PropertySpec(string name, string type, string category) : this(name, type, category, null, null) { } + + /// <summary> + /// Initializes a new instance of the PropertySpec class. + /// </summary> + /// <param name="name">The name of the property displayed in the property grid.</param> + /// <param name="type">A Type that represents the type of the property.</param> + /// <param name="category"></param> + public PropertySpec(string name, Type type, string category) : + this(name, type.AssemblyQualifiedName, category, null, null) { } + + /// <summary> + /// Initializes a new instance of the PropertySpec class. + /// </summary> + /// <param name="name">The name of the property displayed in the property grid.</param> + /// <param name="type">The fully qualified name of the type of the property.</param> + /// <param name="category">The category under which the property is displayed in the + /// property grid.</param> + /// <param name="description">A string that is displayed in the help area of the + /// property grid.</param> + public PropertySpec(string name, string type, string category, string description) : + this(name, type, category, description, null) { } + + /// <summary> + /// Initializes a new instance of the PropertySpec class. + /// </summary> + /// <param name="name">The name of the property displayed in the property grid.</param> + /// <param name="type">A Type that represents the type of the property.</param> + /// <param name="category">The category under which the property is displayed in the + /// property grid.</param> + /// <param name="description">A string that is displayed in the help area of the + /// property grid.</param> + public PropertySpec(string name, Type type, string category, string description) : + this(name, type.AssemblyQualifiedName, category, description, null) { } + + /// <summary> + /// Initializes a new instance of the PropertySpec class. + /// </summary> + /// <param name="name">The name of the property displayed in the property grid.</param> + /// <param name="type">The fully qualified name of the type of the property.</param> + /// <param name="category">The category under which the property is displayed in the + /// property grid.</param> + /// <param name="description">A string that is displayed in the help area of the + /// property grid.</param> + /// <param name="defaultValue">The default value of the property, or null if there is + /// no default value.</param> + public PropertySpec(string name, string type, string category, string description, object defaultValue) { + this.name = name; + this.type = type; + this.category = category; + this.description = description; + this.defaultValue = defaultValue; + this.attributes = null; + this.currentValue = defaultValue; //mxd + } + + /// <summary> + /// Initializes a new instance of the PropertySpec class. + /// </summary> + /// <param name="name">The name of the property displayed in the property grid.</param> + /// <param name="type">A Type that represents the type of the property.</param> + /// <param name="category">The category under which the property is displayed in the + /// property grid.</param> + /// <param name="description">A string that is displayed in the help area of the + /// property grid.</param> + /// <param name="defaultValue">The default value of the property, or null if there is + /// no default value.</param> + public PropertySpec(string name, Type type, string category, string description, object defaultValue) : + this(name, type.AssemblyQualifiedName, category, description, defaultValue) { } + + /// <summary> + /// Initializes a new instance of the PropertySpec class. + /// </summary> + /// <param name="name">The name of the property displayed in the property grid.</param> + /// <param name="type">The fully qualified name of the type of the property.</param> + /// <param name="category">The category under which the property is displayed in the + /// property grid.</param> + /// <param name="description">A string that is displayed in the help area of the + /// property grid.</param> + /// <param name="defaultValue">The default value of the property, or null if there is + /// no default value.</param> + /// <param name="editor">The fully qualified name of the type of the editor for this + /// property. This type must derive from UITypeEditor.</param> + /// <param name="typeConverter">The fully qualified name of the type of the type + /// converter for this property. This type must derive from TypeConverter.</param> + public PropertySpec(string name, string type, string category, string description, object defaultValue, + string editor, string typeConverter) + : this(name, type, category, description, defaultValue) { + this.editor = editor; + this.typeConverter = typeConverter; + } + + /// <summary> + /// Initializes a new instance of the PropertySpec class. + /// </summary> + /// <param name="name">The name of the property displayed in the property grid.</param> + /// <param name="type">A Type that represents the type of the property.</param> + /// <param name="category">The category under which the property is displayed in the + /// property grid.</param> + /// <param name="description">A string that is displayed in the help area of the + /// property grid.</param> + /// <param name="defaultValue">The default value of the property, or null if there is + /// no default value.</param> + /// <param name="editor">The fully qualified name of the type of the editor for this + /// property. This type must derive from UITypeEditor.</param> + /// <param name="typeConverter">The fully qualified name of the type of the type + /// converter for this property. This type must derive from TypeConverter.</param> + public PropertySpec(string name, Type type, string category, string description, object defaultValue, + string editor, string typeConverter) : + this(name, type.AssemblyQualifiedName, category, description, defaultValue, editor, typeConverter) { } + + /// <summary> + /// Initializes a new instance of the PropertySpec class. + /// </summary> + /// <param name="name">The name of the property displayed in the property grid.</param> + /// <param name="type">The fully qualified name of the type of the property.</param> + /// <param name="category">The category under which the property is displayed in the + /// property grid.</param> + /// <param name="description">A string that is displayed in the help area of the + /// property grid.</param> + /// <param name="defaultValue">The default value of the property, or null if there is + /// no default value.</param> + /// <param name="editor">The Type that represents the type of the editor for this + /// property. This type must derive from UITypeEditor.</param> + /// <param name="typeConverter">The fully qualified name of the type of the type + /// converter for this property. This type must derive from TypeConverter.</param> + public PropertySpec(string name, string type, string category, string description, object defaultValue, + Type editor, string typeConverter) : + this(name, type, category, description, defaultValue, editor.AssemblyQualifiedName, + typeConverter) { } + + /// <summary> + /// Initializes a new instance of the PropertySpec class. + /// </summary> + /// <param name="name">The name of the property displayed in the property grid.</param> + /// <param name="type">A Type that represents the type of the property.</param> + /// <param name="category">The category under which the property is displayed in the + /// property grid.</param> + /// <param name="description">A string that is displayed in the help area of the + /// property grid.</param> + /// <param name="defaultValue">The default value of the property, or null if there is + /// no default value.</param> + /// <param name="editor">The Type that represents the type of the editor for this + /// property. This type must derive from UITypeEditor.</param> + /// <param name="typeConverter">The fully qualified name of the type of the type + /// converter for this property. This type must derive from TypeConverter.</param> + public PropertySpec(string name, Type type, string category, string description, object defaultValue, + Type editor, string typeConverter) : + this(name, type.AssemblyQualifiedName, category, description, defaultValue, + editor.AssemblyQualifiedName, typeConverter) { } + + /// <summary> + /// Initializes a new instance of the PropertySpec class. + /// </summary> + /// <param name="name">The name of the property displayed in the property grid.</param> + /// <param name="type">The fully qualified name of the type of the property.</param> + /// <param name="category">The category under which the property is displayed in the + /// property grid.</param> + /// <param name="description">A string that is displayed in the help area of the + /// property grid.</param> + /// <param name="defaultValue">The default value of the property, or null if there is + /// no default value.</param> + /// <param name="editor">The fully qualified name of the type of the editor for this + /// property. This type must derive from UITypeEditor.</param> + /// <param name="typeConverter">The Type that represents the type of the type + /// converter for this property. This type must derive from TypeConverter.</param> + public PropertySpec(string name, string type, string category, string description, object defaultValue, + string editor, Type typeConverter) : + this(name, type, category, description, defaultValue, editor, typeConverter.AssemblyQualifiedName) { } + + /// <summary> + /// Initializes a new instance of the PropertySpec class. + /// </summary> + /// <param name="name">The name of the property displayed in the property grid.</param> + /// <param name="type">A Type that represents the type of the property.</param> + /// <param name="category">The category under which the property is displayed in the + /// property grid.</param> + /// <param name="description">A string that is displayed in the help area of the + /// property grid.</param> + /// <param name="defaultValue">The default value of the property, or null if there is + /// no default value.</param> + /// <param name="editor">The fully qualified name of the type of the editor for this + /// property. This type must derive from UITypeEditor.</param> + /// <param name="typeConverter">The Type that represents the type of the type + /// converter for this property. This type must derive from TypeConverter.</param> + public PropertySpec(string name, Type type, string category, string description, object defaultValue, + string editor, Type typeConverter) : + this(name, type.AssemblyQualifiedName, category, description, defaultValue, editor, + typeConverter.AssemblyQualifiedName) { } + + /// <summary> + /// Initializes a new instance of the PropertySpec class. + /// </summary> + /// <param name="name">The name of the property displayed in the property grid.</param> + /// <param name="type">The fully qualified name of the type of the property.</param> + /// <param name="category">The category under which the property is displayed in the + /// property grid.</param> + /// <param name="description">A string that is displayed in the help area of the + /// property grid.</param> + /// <param name="defaultValue">The default value of the property, or null if there is + /// no default value.</param> + /// <param name="editor">The Type that represents the type of the editor for this + /// property. This type must derive from UITypeEditor.</param> + /// <param name="typeConverter">The Type that represents the type of the type + /// converter for this property. This type must derive from TypeConverter.</param> + public PropertySpec(string name, string type, string category, string description, object defaultValue, + Type editor, Type typeConverter) : + this(name, type, category, description, defaultValue, editor.AssemblyQualifiedName, + typeConverter.AssemblyQualifiedName) { } + + /// <summary> + /// Initializes a new instance of the PropertySpec class. + /// </summary> + /// <param name="name">The name of the property displayed in the property grid.</param> + /// <param name="type">A Type that represents the type of the property.</param> + /// <param name="category">The category under which the property is displayed in the + /// property grid.</param> + /// <param name="description">A string that is displayed in the help area of the + /// property grid.</param> + /// <param name="defaultValue">The default value of the property, or null if there is + /// no default value.</param> + /// <param name="editor">The Type that represents the type of the editor for this + /// property. This type must derive from UITypeEditor.</param> + /// <param name="typeConverter">The Type that represents the type of the type + /// converter for this property. This type must derive from TypeConverter.</param> + public PropertySpec(string name, Type type, string category, string description, object defaultValue, + Type editor, Type typeConverter) : + this(name, type.AssemblyQualifiedName, category, description, defaultValue, + editor.AssemblyQualifiedName, typeConverter.AssemblyQualifiedName) { } + + /// <summary> + /// Gets or sets a collection of additional Attributes for this property. This can + /// be used to specify attributes beyond those supported intrinsically by the + /// PropertySpec class, such as ReadOnly and Browsable. + /// </summary> + public Attribute[] Attributes { + get { return attributes; } + set { attributes = value; } + } + + /// <summary> + /// Gets or sets the category name of this property. + /// </summary> + public string Category { + get { return category; } + set { category = value; } + } + + /// <summary> + /// Gets or sets the fully qualified name of the type converter + /// type for this property. + /// </summary> + public string ConverterTypeName { + get { return typeConverter; } + set { typeConverter = value; } + } + + /// <summary> + /// Gets or sets the default value of this property. + /// </summary> + public object DefaultValue { + get { return defaultValue; } + set { defaultValue = value; } + } + + //mxd + /// <summary> + /// Gets or sets the value of this property. + /// </summary> + public object Value { + get { return currentValue; } + set { currentValue = value; } + } + + /// <summary> + /// Gets or sets the help text description of this property. + /// </summary> + public string Description { + get { return description; } + set { description = value; } + } + + /// <summary> + /// Gets or sets the fully qualified name of the editor type for + /// this property. + /// </summary> + public string EditorTypeName { + get { return editor; } + set { editor = value; } + } + + /// <summary> + /// Gets or sets the name of this property. + /// </summary> + public string Name { + get { return name; } + set { name = value; } + } + + /// <summary> + /// Gets or sets the fully qualfied name of the type of this + /// property. + /// </summary> + public string TypeName { + get { return type; } + set { type = value; } + } + } + + /// <summary> + /// Provides data for the GetValue and SetValue events of the PropertyBag class. + /// </summary> + public class PropertySpecEventArgs : EventArgs { + private PropertySpec property; + private object val; + + /// <summary> + /// Initializes a new instance of the PropertySpecEventArgs class. + /// </summary> + /// <param name="property">The PropertySpec that represents the property whose + /// value is being requested or set.</param> + /// <param name="val">The current value of the property.</param> + public PropertySpecEventArgs(PropertySpec property, object val) { + this.property = property; + this.val = val; + } + + /// <summary> + /// Gets the PropertySpec that represents the property whose value is being + /// requested or set. + /// </summary> + public PropertySpec Property { + get { return property; } + } + + /// <summary> + /// Gets or sets the current value of the property. + /// </summary> + public object Value { + get { return val; } + set { val = value; } + } + } + + /// <summary> + /// Represents the method that will handle the GetValue and SetValue events of the + /// PropertyBag class. + /// </summary> + public delegate void PropertySpecEventHandler(object sender, PropertySpecEventArgs e); + + /// <summary> + /// Represents a collection of custom properties that can be selected into a + /// PropertyGrid to provide functionality beyond that of the simple reflection + /// normally used to query an object's properties. + /// </summary> + public class PropertyBag : ICustomTypeDescriptor { + #region PropertySpecCollection class definition + /// <summary> + /// Encapsulates a collection of PropertySpec objects. + /// </summary> + [Serializable] + public class PropertySpecCollection : IList { + private ArrayList innerArray; + + /// <summary> + /// Initializes a new instance of the PropertySpecCollection class. + /// </summary> + public PropertySpecCollection() { + innerArray = new ArrayList(); + } + + /// <summary> + /// Gets the number of elements in the PropertySpecCollection. + /// </summary> + /// <value> + /// The number of elements contained in the PropertySpecCollection. + /// </value> + public int Count { + get { return innerArray.Count; } + } + + /// <summary> + /// Gets a value indicating whether the PropertySpecCollection has a fixed size. + /// </summary> + /// <value> + /// true if the PropertySpecCollection has a fixed size; otherwise, false. + /// </value> + public bool IsFixedSize { + get { return false; } + } + + /// <summary> + /// Gets a value indicating whether the PropertySpecCollection is read-only. + /// </summary> + public bool IsReadOnly { + get { return false; } + } + + /// <summary> + /// Gets a value indicating whether access to the collection is synchronized (thread-safe). + /// </summary> + /// <value> + /// true if access to the PropertySpecCollection is synchronized (thread-safe); otherwise, false. + /// </value> + public bool IsSynchronized { + get { return false; } + } + + /// <summary> + /// Gets an object that can be used to synchronize access to the collection. + /// </summary> + /// <value> + /// An object that can be used to synchronize access to the collection. + /// </value> + object ICollection.SyncRoot { + get { return null; } + } + + /// <summary> + /// Gets or sets the element at the specified index. + /// In C#, this property is the indexer for the PropertySpecCollection class. + /// </summary> + /// <param name="index">The zero-based index of the element to get or set.</param> + /// <value> + /// The element at the specified index. + /// </value> + public PropertySpec this[int index] { + get { return (PropertySpec)innerArray[index]; } + set { innerArray[index] = value; } + } + + /// <summary> + /// Adds a PropertySpec to the end of the PropertySpecCollection. + /// </summary> + /// <param name="value">The PropertySpec to be added to the end of the PropertySpecCollection.</param> + /// <returns>The PropertySpecCollection index at which the value has been added.</returns> + public int Add(PropertySpec value) { + int index = innerArray.Add(value); + + return index; + } + + /// <summary> + /// Adds the elements of an array of PropertySpec objects to the end of the PropertySpecCollection. + /// </summary> + /// <param name="array">The PropertySpec array whose elements should be added to the end of the + /// PropertySpecCollection.</param> + public void AddRange(PropertySpec[] array) { + innerArray.AddRange(array); + } + + /// <summary> + /// Removes all elements from the PropertySpecCollection. + /// </summary> + public void Clear() { + innerArray.Clear(); + } + + /// <summary> + /// Determines whether a PropertySpec is in the PropertySpecCollection. + /// </summary> + /// <param name="item">The PropertySpec to locate in the PropertySpecCollection. The element to locate + /// can be a null reference (Nothing in Visual Basic).</param> + /// <returns>true if item is found in the PropertySpecCollection; otherwise, false.</returns> + public bool Contains(PropertySpec item) { + return innerArray.Contains(item); + } + + /// <summary> + /// Determines whether a PropertySpec with the specified name is in the PropertySpecCollection. + /// </summary> + /// <param name="name">The name of the PropertySpec to locate in the PropertySpecCollection.</param> + /// <returns>true if item is found in the PropertySpecCollection; otherwise, false.</returns> + public bool Contains(string name) { + foreach (PropertySpec spec in innerArray) + if (spec.Name == name) + return true; + + return false; + } + + /// <summary> + /// Copies the entire PropertySpecCollection to a compatible one-dimensional Array, starting at the + /// beginning of the target array. + /// </summary> + /// <param name="array">The one-dimensional Array that is the destination of the elements copied + /// from PropertySpecCollection. The Array must have zero-based indexing.</param> + public void CopyTo(PropertySpec[] array) { + innerArray.CopyTo(array); + } + + /// <summary> + /// Copies the PropertySpecCollection or a portion of it to a one-dimensional array. + /// </summary> + /// <param name="array">The one-dimensional Array that is the destination of the elements copied + /// from the collection.</param> + /// <param name="index">The zero-based index in array at which copying begins.</param> + public void CopyTo(PropertySpec[] array, int index) { + innerArray.CopyTo(array, index); + } + + /// <summary> + /// Returns an enumerator that can iterate through the PropertySpecCollection. + /// </summary> + /// <returns>An IEnumerator for the entire PropertySpecCollection.</returns> + public IEnumerator GetEnumerator() { + return innerArray.GetEnumerator(); + } + + /// <summary> + /// Searches for the specified PropertySpec and returns the zero-based index of the first + /// occurrence within the entire PropertySpecCollection. + /// </summary> + /// <param name="value">The PropertySpec to locate in the PropertySpecCollection.</param> + /// <returns>The zero-based index of the first occurrence of value within the entire PropertySpecCollection, + /// if found; otherwise, -1.</returns> + public int IndexOf(PropertySpec value) { + return innerArray.IndexOf(value); + } + + /// <summary> + /// Searches for the PropertySpec with the specified name and returns the zero-based index of + /// the first occurrence within the entire PropertySpecCollection. + /// </summary> + /// <param name="name">The name of the PropertySpec to locate in the PropertySpecCollection.</param> + /// <returns>The zero-based index of the first occurrence of value within the entire PropertySpecCollection, + /// if found; otherwise, -1.</returns> + public int IndexOf(string name) { + int i = 0; + + foreach (PropertySpec spec in innerArray) { + if (spec.Name == name) + return i; + + i++; + } + + return -1; + } + + /// <summary> + /// Inserts a PropertySpec object into the PropertySpecCollection at the specified index. + /// </summary> + /// <param name="index">The zero-based index at which value should be inserted.</param> + /// <param name="value">The PropertySpec to insert.</param> + public void Insert(int index, PropertySpec value) { + innerArray.Insert(index, value); + } + + /// <summary> + /// Removes the first occurrence of a specific object from the PropertySpecCollection. + /// </summary> + /// <param name="obj">The PropertySpec to remove from the PropertySpecCollection.</param> + public void Remove(PropertySpec obj) { + innerArray.Remove(obj); + } + + /// <summary> + /// Removes the property with the specified name from the PropertySpecCollection. + /// </summary> + /// <param name="name">The name of the PropertySpec to remove from the PropertySpecCollection.</param> + public void Remove(string name) { + int index = IndexOf(name); + RemoveAt(index); + } + + /// <summary> + /// Removes the object at the specified index of the PropertySpecCollection. + /// </summary> + /// <param name="index">The zero-based index of the element to remove.</param> + public void RemoveAt(int index) { + innerArray.RemoveAt(index); + } + + /// <summary> + /// Copies the elements of the PropertySpecCollection to a new PropertySpec array. + /// </summary> + /// <returns>A PropertySpec array containing copies of the elements of the PropertySpecCollection.</returns> + public PropertySpec[] ToArray() { + return (PropertySpec[])innerArray.ToArray(typeof(PropertySpec)); + } + + #region Explicit interface implementations for ICollection and IList + /// <summary> + /// This member supports the .NET Framework infrastructure and is not intended to be used directly from your code. + /// </summary> + void ICollection.CopyTo(Array array, int index) { + CopyTo((PropertySpec[])array, index); + } + + /// <summary> + /// This member supports the .NET Framework infrastructure and is not intended to be used directly from your code. + /// </summary> + int IList.Add(object value) { + return Add((PropertySpec)value); + } + + /// <summary> + /// This member supports the .NET Framework infrastructure and is not intended to be used directly from your code. + /// </summary> + bool IList.Contains(object obj) { + return Contains((PropertySpec)obj); + } + + /// <summary> + /// This member supports the .NET Framework infrastructure and is not intended to be used directly from your code. + /// </summary> + object IList.this[int index] { + get { + return ((PropertySpecCollection)this)[index]; + } + set { + ((PropertySpecCollection)this)[index] = (PropertySpec)value; + } + } + + /// <summary> + /// This member supports the .NET Framework infrastructure and is not intended to be used directly from your code. + /// </summary> + int IList.IndexOf(object obj) { + return IndexOf((PropertySpec)obj); + } + + /// <summary> + /// This member supports the .NET Framework infrastructure and is not intended to be used directly from your code. + /// </summary> + void IList.Insert(int index, object value) { + Insert(index, (PropertySpec)value); + } + + /// <summary> + /// This member supports the .NET Framework infrastructure and is not intended to be used directly from your code. + /// </summary> + void IList.Remove(object value) { + Remove((PropertySpec)value); + } + #endregion + } + #endregion + #region PropertySpecDescriptor class definition + private class PropertySpecDescriptor : PropertyDescriptor { + private PropertyBag bag; + private PropertySpec item; + + public PropertySpecDescriptor(PropertySpec item, PropertyBag bag, string name, Attribute[] attrs) : + base(name, attrs) { + this.bag = bag; + this.item = item; + } + + public override Type ComponentType { + get { return item.GetType(); } + } + + public override bool IsReadOnly { + get { return (Attributes.Matches(ReadOnlyAttribute.Yes)); } + } + + public override Type PropertyType { + get { return Type.GetType(item.TypeName); } + } + + public override bool CanResetValue(object component) { + if (item.DefaultValue == null) + return false; + else + return !this.GetValue(component).Equals(item.DefaultValue); + } + + public override object GetValue(object component) { + // Have the property bag raise an event to get the current value + // of the property. + + PropertySpecEventArgs e = new PropertySpecEventArgs(item, null); + bag.OnGetValue(e); + return e.Value; + } + + public override void ResetValue(object component) { + SetValue(component, item.DefaultValue); + } + + public override void SetValue(object component, object value) { + // Have the property bag raise an event to set the current value + // of the property. + + PropertySpecEventArgs e = new PropertySpecEventArgs(item, value); + bag.OnSetValue(e); + } + + public override bool ShouldSerializeValue(object component) { + object val = this.GetValue(component); + + if (item.DefaultValue == null && val == null) + return false; + else if (item.DefaultValue != null && val == null)//mxd + return true; + else + return !val.Equals(item.DefaultValue); + } + } + #endregion + + private string defaultProperty; + protected PropertySpecCollection properties; //mxd + + /// <summary> + /// Initializes a new instance of the PropertyBag class. + /// </summary> + public PropertyBag() { + defaultProperty = null; + properties = new PropertySpecCollection(); + } + + /// <summary> + /// Gets or sets the name of the default property in the collection. + /// </summary> + public string DefaultProperty { + get { return defaultProperty; } + set { defaultProperty = value; } + } + + /// <summary> + /// Gets the collection of properties contained within this PropertyBag. + /// </summary> + public PropertySpecCollection Properties { + get { return properties; } + } + + /// <summary> + /// Occurs when a PropertyGrid requests the value of a property. + /// </summary> + public event PropertySpecEventHandler GetValue; + + /// <summary> + /// Occurs when the user changes the value of a property in a PropertyGrid. + /// </summary> + public event PropertySpecEventHandler SetValue; + + /// <summary> + /// Raises the GetValue event. + /// </summary> + /// <param name="e">A PropertySpecEventArgs that contains the event data.</param> + protected virtual void OnGetValue(PropertySpecEventArgs e) { + if (GetValue != null) + GetValue(this, e); + } + + /// <summary> + /// Raises the SetValue event. + /// </summary> + /// <param name="e">A PropertySpecEventArgs that contains the event data.</param> + protected virtual void OnSetValue(PropertySpecEventArgs e) { + //mxd + e.Property.Value = e.Value; + + if (SetValue != null) + SetValue(this, e); + } + + #region ICustomTypeDescriptor explicit interface definitions + // Most of the functions required by the ICustomTypeDescriptor are + // merely pssed on to the default TypeDescriptor for this type, + // which will do something appropriate. The exceptions are noted + // below. + AttributeCollection ICustomTypeDescriptor.GetAttributes() { + return TypeDescriptor.GetAttributes(this, true); + } + + string ICustomTypeDescriptor.GetClassName() { + return TypeDescriptor.GetClassName(this, true); + } + + string ICustomTypeDescriptor.GetComponentName() { + return TypeDescriptor.GetComponentName(this, true); + } + + TypeConverter ICustomTypeDescriptor.GetConverter() { + return TypeDescriptor.GetConverter(this, true); + } + + EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() { + return TypeDescriptor.GetDefaultEvent(this, true); + } + + PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() { + // This function searches the property list for the property + // with the same name as the DefaultProperty specified, and + // returns a property descriptor for it. If no property is + // found that matches DefaultProperty, a null reference is + // returned instead. + + PropertySpec propertySpec = null; + if (defaultProperty != null) { + int index = properties.IndexOf(defaultProperty); + propertySpec = properties[index]; + } + + if (propertySpec != null) + return new PropertySpecDescriptor(propertySpec, this, propertySpec.Name, null); + else + return null; + } + + object ICustomTypeDescriptor.GetEditor(Type editorBaseType) { + return TypeDescriptor.GetEditor(this, editorBaseType, true); + } + + EventDescriptorCollection ICustomTypeDescriptor.GetEvents() { + return TypeDescriptor.GetEvents(this, true); + } + + EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) { + return TypeDescriptor.GetEvents(this, attributes, true); + } + + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() { + return ((ICustomTypeDescriptor)this).GetProperties(new Attribute[0]); + } + + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) { + // Rather than passing this function on to the default TypeDescriptor, + // which would return the actual properties of PropertyBag, I construct + // a list here that contains property descriptors for the elements of the + // Properties list in the bag. + + ArrayList props = new ArrayList(); + + foreach (PropertySpec property in properties) { + ArrayList attrs = new ArrayList(); + + // If a category, description, editor, or type converter are specified + // in the PropertySpec, create attributes to define that relationship. + if (property.Category != null) + attrs.Add(new CategoryAttribute(property.Category)); + + if (property.Description != null) + attrs.Add(new DescriptionAttribute(property.Description)); + + if (property.EditorTypeName != null) + attrs.Add(new EditorAttribute(property.EditorTypeName, typeof(UITypeEditor))); + + if (property.ConverterTypeName != null) + attrs.Add(new TypeConverterAttribute(property.ConverterTypeName)); + + // Additionally, append the custom attributes associated with the + // PropertySpec, if any. + if (property.Attributes != null) + attrs.AddRange(property.Attributes); + + Attribute[] attrArray = (Attribute[])attrs.ToArray(typeof(Attribute)); + + // Create a new property descriptor for the property item, and add + // it to the list. + PropertySpecDescriptor pd = new PropertySpecDescriptor(property, + this, property.Name, attrArray); + props.Add(pd); + } + + // Convert the list of PropertyDescriptors to a collection that the + // ICustomTypeDescriptor can use, and return it. + PropertyDescriptor[] propArray = (PropertyDescriptor[])props.ToArray( + typeof(PropertyDescriptor)); + return new PropertyDescriptorCollection(propArray); + } + + object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) { + return this; + } + #endregion + } + + /// <summary> + /// An extension of PropertyBag that manages a table of property values, in + /// addition to firing events when property values are requested or set. + /// </summary> + public class PropertyTable : PropertyBag { + private Hashtable propValues; + + /// <summary> + /// Initializes a new instance of the PropertyTable class. + /// </summary> + public PropertyTable() { + propValues = new Hashtable(); + } + + /// <summary> + /// Gets or sets the value of the property with the specified name. + /// <p>In C#, this property is the indexer of the PropertyTable class.</p> + /// </summary> + public object this[string key] { + get { return propValues[key]; } + set { propValues[key] = value; } + } + + /// <summary> + /// This member overrides PropertyBag.OnGetValue. + /// </summary> + protected override void OnGetValue(PropertySpecEventArgs e) { + e.Value = propValues[e.Property.Name]; + base.OnGetValue(e); + } + + /// <summary> + /// This member overrides PropertyBag.OnSetValue. + /// </summary> + protected override void OnSetValue(PropertySpecEventArgs e) { + propValues[e.Property.Name] = e.Value; + base.OnSetValue(e); + } + } +} diff --git a/Source/Plugins/PropertiesDock/Info/IMapElementInfo.cs b/Source/Plugins/PropertiesDock/Info/IMapElementInfo.cs index c7e6bd423..624ca45a8 100644 --- a/Source/Plugins/PropertiesDock/Info/IMapElementInfo.cs +++ b/Source/Plugins/PropertiesDock/Info/IMapElementInfo.cs @@ -6,5 +6,7 @@ using System.Text; namespace CodeImp.DoomBuilder.PropertiesDock { interface IMapElementInfo { void ApplyChanges(); + void AddCustomProperty(string name, Type type); + void RemoveCustomProperty(string name); } } diff --git a/Source/Plugins/PropertiesDock/Info/ThingInfo.cs b/Source/Plugins/PropertiesDock/Info/ThingInfo.cs new file mode 100644 index 000000000..f51c1a085 --- /dev/null +++ b/Source/Plugins/PropertiesDock/Info/ThingInfo.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using System.Globalization; + +namespace CodeImp.DoomBuilder.PropertiesDock { + public class ThingInfo : IMapElementInfo { + + [TypeConverterAttribute(typeof(ThingTypeConverter)), CategoryAttribute("General"), DefaultValueAttribute(0)] + public int Type { get { return type; } set { type = value; } } + private int type; + + private Thing thing; + + public ThingInfo(Thing t) { + thing = t; + type = t.Type; + } + + public void ApplyChanges() { + + } + + public void AddCustomProperty(string name, Type type) { + //properties.Add(new PropertySpec(name + ":", value.GetType(), "Custom properties:")); + } + + public void RemoveCustomProperty(string name) { + /*string n = name.ToUpperInvariant().Trim(); + foreach (PropertySpec ps in properties) { + string cn = ps.Name.ToUpperInvariant(); + if (cn.IndexOf(n) == 0 && cn.Length == n.Length + 1) { + properties.Remove(name); + return; + } + }*/ + } + } + + public class ThingTypeConverter : TypeConverter { + + public override bool CanConvertTo(ITypeDescriptorContext context, System.Type destinationType) { + if (destinationType == typeof(int)) + return true; + return base.CanConvertTo(context, destinationType); + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, System.Type destinationType) { + if (destinationType == typeof(System.String) && value is int) { + int type = (int)value; + if (MapElementsData.ThingTypeDescriptions.ContainsKey(type)) { + return type + " - " + MapElementsData.ThingTypeDescriptions[type]; + } + + return type + " - Unknown Thing"; + } + return base.ConvertTo(context, culture, value, destinationType); + } + + public override bool CanConvertFrom(ITypeDescriptorContext context, System.Type sourceType) { + if (sourceType == typeof(string)) + return true; + return base.CanConvertFrom(context, sourceType); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { + if (value is string) { + int type = 0; + if (!int.TryParse((string)value, out type)) { + //throw new ArgumentException("'" + (string)value + "' is not a valid Thing type"); + General.ShowErrorMessage("'" + (string)value + "' is not a valid Thing type", System.Windows.Forms.MessageBoxButtons.OK); + } + return type; + } + return base.ConvertFrom(context, culture, value); + } + } +} diff --git a/Source/Plugins/PropertiesDock/Info/VertexInfo.cs b/Source/Plugins/PropertiesDock/Info/VertexInfo.cs index e1165ed18..623facbe9 100644 --- a/Source/Plugins/PropertiesDock/Info/VertexInfo.cs +++ b/Source/Plugins/PropertiesDock/Info/VertexInfo.cs @@ -6,29 +6,51 @@ using System.Text; using CodeImp.DoomBuilder.Map; namespace CodeImp.DoomBuilder.PropertiesDock { - public class VertexInfo : IMapElementInfo { - [CategoryAttribute("Position"), DefaultValueAttribute(0f)] - public float X { get { return x; } set { x = value; } } - private float x; + + public class VertexInfo : PropertyBag, IMapElementInfo { + /*[CategoryAttribute("Position"), DefaultValueAttribute(0f)] + public float X { get { return x; } set { x = value; } }*/ + //private float x; - [CategoryAttribute("Position"), DefaultValueAttribute(0f)] - public float Y { get { return y; } set { y = value; } } - private float y; + /*[CategoryAttribute("Position"), DefaultValueAttribute(0f)] + public float Y { get { return y; } set { y = value; } }*/ + //private float y; + + //public PropertyBag Properties { get { return properties; } } + //private PropertyBag properties; private Vertex vertex; - public VertexInfo(Vertex v) { + public VertexInfo(Vertex v) : base() { vertex = v; - x = v.Position.x; - y = v.Position.y; + //x = v.Position.x; + //y = v.Position.y; + //properties = new PropertyBag(); + properties.Add(new PropertySpec("X:", typeof(float), "Position:", null, v.Position.x)); + properties.Add(new PropertySpec("Y:", typeof(float), "Position:", null, v.Position.y)); } public void ApplyChanges() { float min = (float)General.Map.FormatInterface.MinCoordinate; float max = (float)General.Map.FormatInterface.MaxCoordinate; - vertex.Move(new CodeImp.DoomBuilder.Geometry.Vector2D(General.Clamp(x, min, max), General.Clamp(y, min, max))); + vertex.Move(new CodeImp.DoomBuilder.Geometry.Vector2D(General.Clamp((float)properties[0].Value, min, max), General.Clamp((float)properties[1].Value, min, max))); //todo: add custom fields support } + + public void AddCustomProperty(string name, Type type) { + properties.Add(new PropertySpec(name + ":", type, "Custom properties:")); + } + + public void RemoveCustomProperty(string name){ + string n = name.ToUpperInvariant().Trim(); + foreach (PropertySpec ps in properties) { + string cn = ps.Name.ToUpperInvariant(); + if (cn.IndexOf(n) == 0 && cn.Length == n.Length + 1) { + properties.Remove(name); + return; + } + } + } } } diff --git a/Source/Plugins/PropertiesDock/Properties/Resources.Designer.cs b/Source/Plugins/PropertiesDock/Properties/Resources.Designer.cs new file mode 100644 index 000000000..6fc6cbb8a --- /dev/null +++ b/Source/Plugins/PropertiesDock/Properties/Resources.Designer.cs @@ -0,0 +1,77 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// Ðтот код Ñоздан программой. +// ИÑполнÑÐµÐ¼Ð°Ñ Ð²ÐµÑ€ÑиÑ:2.0.50727.4927 +// +// Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² Ñтом файле могут привеÑти к неправильной работе и будут потерÑны в Ñлучае +// повторной генерации кода. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace CodeImp.DoomBuilder.PropertiesDock.Properties { + using System; + + + /// <summary> + /// КлаÑÑ Ñ€ÐµÑурÑа Ñо Ñтрогой типизацией Ð´Ð»Ñ Ð¿Ð¾Ð¸Ñка локализованных Ñтрок и Ñ‚.д. + /// </summary> + // Ðтот клаÑÑ Ñоздан автоматичеÑки клаÑÑом StronglyTypedResourceBuilder + // Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ такого ÑредÑтва, как ResGen или Visual Studio. + // Чтобы добавить или удалить член, измените файл .ResX и Ñнова запуÑтите ResGen + // Ñ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð¾Ð¼ /str или переÑтройте Ñвой проект VS. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// <summary> + /// Возвращает кÑшированный ÑкземплÑÑ€ ResourceManager, иÑпользованный Ñтим клаÑÑом. + /// </summary> + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CodeImp.DoomBuilder.PropertiesDock.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// <summary> + /// ПерезапиÑывает ÑвойÑтво CurrentUICulture текущего потока Ð´Ð»Ñ Ð²Ñех + /// обращений к реÑурÑу Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ Ñтого клаÑÑа реÑурÑа Ñо Ñтрогой типизацией. + /// </summary> + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static System.Drawing.Bitmap Add { + get { + object obj = ResourceManager.GetObject("Add", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap Remove { + get { + object obj = ResourceManager.GetObject("Remove", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/Source/Plugins/PropertiesDock/Properties/Resources.resx b/Source/Plugins/PropertiesDock/Properties/Resources.resx new file mode 100644 index 000000000..00a8973d7 --- /dev/null +++ b/Source/Plugins/PropertiesDock/Properties/Resources.resx @@ -0,0 +1,127 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> + <data name="Add" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\Add.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="Remove" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\Remove.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> +</root> \ No newline at end of file diff --git a/Source/Plugins/PropertiesDock/PropertiesDock.csproj b/Source/Plugins/PropertiesDock/PropertiesDock.csproj index 34291d181..e88d90f62 100644 --- a/Source/Plugins/PropertiesDock/PropertiesDock.csproj +++ b/Source/Plugins/PropertiesDock/PropertiesDock.csproj @@ -50,10 +50,18 @@ <Compile Include="Controls\PropertiesDocker.Designer.cs"> <DependentUpon>PropertiesDocker.cs</DependentUpon> </Compile> + <Compile Include="Data\MapElementsData.cs" /> + <Compile Include="Data\PropertyBag.cs" /> <Compile Include="Data\VertexPosition.cs" /> <Compile Include="Info\IMapElementInfo.cs" /> + <Compile Include="Info\ThingInfo.cs" /> <Compile Include="Info\VertexInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="Properties\Resources.Designer.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>Resources.resx</DependentUpon> + </Compile> </ItemGroup> <ItemGroup> <ProjectReference Include="..\..\Core\Builder.csproj"> @@ -66,6 +74,16 @@ <EmbeddedResource Include="Controls\PropertiesDocker.resx"> <DependentUpon>PropertiesDocker.cs</DependentUpon> </EmbeddedResource> + <EmbeddedResource Include="Properties\Resources.resx"> + <Generator>ResXFileCodeGenerator</Generator> + <LastGenOutput>Resources.Designer.cs</LastGenOutput> + </EmbeddedResource> + </ItemGroup> + <ItemGroup> + <None Include="Resources\Add.png" /> + </ItemGroup> + <ItemGroup> + <None Include="Resources\Remove.png" /> </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <!-- To modify your build process, add your task inside one of the targets below and uncomment it. diff --git a/Source/Plugins/PropertiesDock/Resources/Add.png b/Source/Plugins/PropertiesDock/Resources/Add.png new file mode 100644 index 0000000000000000000000000000000000000000..79f062aa94e5c5cff258bb52c2ff79fa1387b6c2 GIT binary patch literal 491 zcmV<H0Tlj;P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00059Nkl<Zc-muR zzykV@@cl;@E42B<h)pdcE(0=5pEEpSn9U%^kjgOQv?S>Us4F=#eEIs4L4Z$|;p*+3 zq#K|jZ_V)a+ZzTxUP*?V_YRO|K<^=e|BABu3_pK;X5i)&Ww?F+Btx<N2kZuP?GyeF zGl~(&f`y*Ej55RTzdso`+4&gmKDdTrBTP-1(_2Qy_Fdxt3+(TK0}KX00k`hH6~o`Z ze;AmUm>GhVj$_DAIH<%>>GqP5v3Z;He<gWYhF|~RgUyDxf)T|<$hLymY)oPdcOTwk zsP%fv$XLHo@xPK1FT<aIUyxM7bTdN5pvHhh;XeaAlL*7@yRR7<{qHj}R<BY0kLo!_ z21cM}dF5YF+y65>xXTVv@Sg$6Ees5;A-5T^hHUu?t^bN@A0Ql{`UkfK7&;=Zp{Q-$ zq9<GJbLlZw14@?Y{a4g@3AP;s?%j}J=!v;Rd^#vxVDw*E`ytrtAO&}?%QN)HpCjIY z{COt-Ei5_U{1eB2FicE3L$m>+Km%T(3jz%g@JMF=3S=??@t+-YfBwT}fH)8f05LZZ h^P$Pzg3`~B1p%Q@rV#+WrRx9y002ovPDHLkV1j^q;v)b6 literal 0 HcmV?d00001 diff --git a/Source/Plugins/PropertiesDock/Resources/Remove.png b/Source/Plugins/PropertiesDock/Resources/Remove.png new file mode 100644 index 0000000000000000000000000000000000000000..9f5eda7d5d79583d6d7d07b53ddb94181ae3b861 GIT binary patch literal 1178 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+m=!WZB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxKsVXI%s|1+P|wiV z#N6CmN5ROz&_Lh7NZ-&%*U;R`*vQJjKmiJrfVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8EkR}&8R-I5=oVMzl_XZ^<`pZ$OmImpPA<wUD9OyvQvjKmn3P{y zVygsnDZ~r81#n~YilM;-3^4tQ!~%UoJp+)JU<!SG@hi>Eg{v+u2}(t{7puX=A(aKG z`a!A1`K3k4z=%sz23b{L<y@4SSdw29lAoUg3&ntp{F40QjQj!x=U`KX@XWlF{PJS3 zYF}R~&%EN2#JuEGPZwJypnko~%oHm#7h^{=Crd+TBS%+5Lsvr!H%DhzHxpMQH)jiD z6E_2xUYGpj(%jU%5}4i;gkB?@dO-;xw*Y9fOKMSOS!#+~QGTuh*uz$tINf65hSNN# z-W1$!F~F%;ALtl;P_!aNGfW7Wc0f#cq6Ko`$v!m?nAVGciQ1#(o(Tg3qkyN2V@SoV zq(A@v+cT>+2;OYC9MzN1_j2q1o*N>`Q`sdM4lOrnyfX8D0#BOrQO=W#z09;73}z|S zeDYBUpR<QgWt+CsoG-umnGdT)vRyv<|DYdRqJXpt+qC3{6|Aov`I<F*dj1!8Jylp_ zzu)mi%XNXOMva!V=S?f#)*hMgSzd@wqmjc*g>lu7>*iMkQkt{kK1i*TJj}2|(at7f zan7k}`Wmww8(3o}?o^vl@tt=yV}hm0ffl**4;X&`<kxOv)w$Nfa$SbIL{Ypig}q4P zgunu+iX^AbbOGK&#{_I<GBzdF7__ZwUCO+HF~K#$<gk!Xo1(|34@<0OGwx2yX;V0z pyo6bxS1$C&6G_dp=NK5-7&?zTvHbhDe<P^0@O1TaS?83{1OP1pih=+D literal 0 HcmV?d00001 -- GitLab